summaryrefslogtreecommitdiffstats
path: root/libgimp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--libgimp/COPYING165
-rw-r--r--libgimp/Makefile.am488
-rw-r--r--libgimp/Makefile.in1936
-rw-r--r--libgimp/gimp.c2588
-rw-r--r--libgimp/gimp.def939
-rw-r--r--libgimp/gimp.h372
-rw-r--r--libgimp/gimp_pdb.c263
-rw-r--r--libgimp/gimp_pdb.h46
-rw-r--r--libgimp/gimp_pdb_headers.h87
-rw-r--r--libgimp/gimpaspectpreview.c483
-rw-r--r--libgimp/gimpaspectpreview.h75
-rw-r--r--libgimp/gimpbrush_pdb.c864
-rw-r--r--libgimp/gimpbrush_pdb.h82
-rw-r--r--libgimp/gimpbrushes.c94
-rw-r--r--libgimp/gimpbrushes.h45
-rw-r--r--libgimp/gimpbrushes_pdb.c256
-rw-r--r--libgimp/gimpbrushes_pdb.h59
-rw-r--r--libgimp/gimpbrushmenu.c168
-rw-r--r--libgimp/gimpbrushmenu.h55
-rw-r--r--libgimp/gimpbrushselect.c240
-rw-r--r--libgimp/gimpbrushselect.h54
-rw-r--r--libgimp/gimpbrushselect_pdb.c149
-rw-r--r--libgimp/gimpbrushselect_pdb.h51
-rw-r--r--libgimp/gimpbrushselectbutton.c932
-rw-r--r--libgimp/gimpbrushselectbutton.h94
-rw-r--r--libgimp/gimpbuffer_pdb.c276
-rw-r--r--libgimp/gimpbuffer_pdb.h48
-rw-r--r--libgimp/gimpchannel.c61
-rw-r--r--libgimp/gimpchannel.h44
-rw-r--r--libgimp/gimpchannel_pdb.c396
-rw-r--r--libgimp/gimpchannel_pdb.h64
-rw-r--r--libgimp/gimpcolor_pdb.c611
-rw-r--r--libgimp/gimpcolor_pdb.h111
-rw-r--r--libgimp/gimpcontext_pdb.c3812
-rw-r--r--libgimp/gimpcontext_pdb.h161
-rw-r--r--libgimp/gimpdebug_pdb.c105
-rw-r--r--libgimp/gimpdebug_pdb.h41
-rw-r--r--libgimp/gimpdisplay_pdb.c237
-rw-r--r--libgimp/gimpdisplay_pdb.h46
-rw-r--r--libgimp/gimpdrawable.c822
-rw-r--r--libgimp/gimpdrawable.h142
-rw-r--r--libgimp/gimpdrawable_pdb.c1043
-rw-r--r--libgimp/gimpdrawable_pdb.h113
-rw-r--r--libgimp/gimpdrawablecolor_pdb.c780
-rw-r--r--libgimp/gimpdrawablecolor_pdb.h110
-rw-r--r--libgimp/gimpdrawableedit_pdb.c320
-rw-r--r--libgimp/gimpdrawableedit_pdb.h60
-rw-r--r--libgimp/gimpdrawablepreview.c757
-rw-r--r--libgimp/gimpdrawablepreview.h93
-rw-r--r--libgimp/gimpdrawabletransform_pdb.c917
-rw-r--r--libgimp/gimpdrawabletransform_pdb.h205
-rw-r--r--libgimp/gimpdynamics_pdb.c113
-rw-r--r--libgimp/gimpdynamics_pdb.h42
-rw-r--r--libgimp/gimpedit.c67
-rw-r--r--libgimp/gimpedit.h41
-rw-r--r--libgimp/gimpedit_pdb.c781
-rw-r--r--libgimp/gimpedit_pdb.h102
-rw-r--r--libgimp/gimpenums.c541
-rw-r--r--libgimp/gimpenums.c.tail205
-rw-r--r--libgimp/gimpenums.h170
-rw-r--r--libgimp/gimpexport.c1157
-rw-r--r--libgimp/gimpexport.h93
-rw-r--r--libgimp/gimpfileops_pdb.c550
-rw-r--r--libgimp/gimpfileops_pdb.h74
-rw-r--r--libgimp/gimpfloatingsel_pdb.c227
-rw-r--r--libgimp/gimpfloatingsel_pdb.h50
-rw-r--r--libgimp/gimpfontmenu.c142
-rw-r--r--libgimp/gimpfontmenu.h49
-rw-r--r--libgimp/gimpfonts_pdb.c109
-rw-r--r--libgimp/gimpfonts_pdb.h42
-rw-r--r--libgimp/gimpfontselect.c208
-rw-r--r--libgimp/gimpfontselect.h45
-rw-r--r--libgimp/gimpfontselect_pdb.c131
-rw-r--r--libgimp/gimpfontselect_pdb.h45
-rw-r--r--libgimp/gimpfontselectbutton.c471
-rw-r--r--libgimp/gimpfontselectbutton.h79
-rw-r--r--libgimp/gimpgimprc.c60
-rw-r--r--libgimp/gimpgimprc.h38
-rw-r--r--libgimp/gimpgimprc_pdb.c330
-rw-r--r--libgimp/gimpgimprc_pdb.h52
-rw-r--r--libgimp/gimpgradient_pdb.c1320
-rw-r--r--libgimp/gimpgradient_pdb.h139
-rw-r--r--libgimp/gimpgradientmenu.c148
-rw-r--r--libgimp/gimpgradientmenu.h49
-rw-r--r--libgimp/gimpgradients.c53
-rw-r--r--libgimp/gimpgradients.h39
-rw-r--r--libgimp/gimpgradients_pdb.c237
-rw-r--r--libgimp/gimpgradients_pdb.h55
-rw-r--r--libgimp/gimpgradientselect.c225
-rw-r--r--libgimp/gimpgradientselect.h48
-rw-r--r--libgimp/gimpgradientselect_pdb.c134
-rw-r--r--libgimp/gimpgradientselect_pdb.h46
-rw-r--r--libgimp/gimpgradientselectbutton.c628
-rw-r--r--libgimp/gimpgradientselectbutton.h81
-rw-r--r--libgimp/gimphelp_pdb.c72
-rw-r--r--libgimp/gimphelp_pdb.h41
-rw-r--r--libgimp/gimpimage.c520
-rw-r--r--libgimp/gimpimage.h116
-rw-r--r--libgimp/gimpimage_pdb.c3223
-rw-r--r--libgimp/gimpimage_pdb.h214
-rw-r--r--libgimp/gimpimagecolorprofile.c173
-rw-r--r--libgimp/gimpimagecolorprofile.h47
-rw-r--r--libgimp/gimpimagecolorprofile_pdb.c292
-rw-r--r--libgimp/gimpimagecolorprofile_pdb.h57
-rw-r--r--libgimp/gimpimagecombobox.c272
-rw-r--r--libgimp/gimpimagecombobox.h51
-rw-r--r--libgimp/gimpimageconvert_pdb.c233
-rw-r--r--libgimp/gimpimageconvert_pdb.h54
-rw-r--r--libgimp/gimpimagegrid_pdb.c408
-rw-r--r--libgimp/gimpimagegrid_pdb.h62
-rw-r--r--libgimp/gimpimageguides_pdb.c243
-rw-r--r--libgimp/gimpimageguides_pdb.h51
-rw-r--r--libgimp/gimpimagemetadata.c1204
-rw-r--r--libgimp/gimpimagemetadata.h61
-rw-r--r--libgimp/gimpimagesamplepoints_pdb.c192
-rw-r--r--libgimp/gimpimagesamplepoints_pdb.h49
-rw-r--r--libgimp/gimpimageselect_pdb.c417
-rw-r--r--libgimp/gimpimageselect_pdb.h75
-rw-r--r--libgimp/gimpimagetransform_pdb.c303
-rw-r--r--libgimp/gimpimagetransform_pdb.h62
-rw-r--r--libgimp/gimpimageundo_pdb.c276
-rw-r--r--libgimp/gimpimageundo_pdb.h46
-rw-r--r--libgimp/gimpitem_pdb.c1152
-rw-r--r--libgimp/gimpitem_pdb.h85
-rw-r--r--libgimp/gimpitemcombobox.c589
-rw-r--r--libgimp/gimpitemcombobox.h77
-rw-r--r--libgimp/gimpitemtransform_pdb.c697
-rw-r--r--libgimp/gimpitemtransform_pdb.h96
-rw-r--r--libgimp/gimplayer.c415
-rw-r--r--libgimp/gimplayer.h63
-rw-r--r--libgimp/gimplayer_pdb.c1354
-rw-r--r--libgimp/gimplayer_pdb.h115
-rw-r--r--libgimp/gimpmenu.c509
-rw-r--r--libgimp/gimpmenu.h68
-rw-r--r--libgimp/gimpmessage_pdb.c126
-rw-r--r--libgimp/gimpmessage_pdb.h42
-rw-r--r--libgimp/gimppainttools_pdb.c775
-rw-r--r--libgimp/gimppainttools_pdb.h109
-rw-r--r--libgimp/gimppalette.c130
-rw-r--r--libgimp/gimppalette.h47
-rw-r--r--libgimp/gimppalette_pdb.c591
-rw-r--r--libgimp/gimppalette_pdb.h70
-rw-r--r--libgimp/gimppalettemenu.c148
-rw-r--r--libgimp/gimppalettemenu.h49
-rw-r--r--libgimp/gimppalettes.c38
-rw-r--r--libgimp/gimppalettes.h37
-rw-r--r--libgimp/gimppalettes_pdb.c178
-rw-r--r--libgimp/gimppalettes_pdb.h49
-rw-r--r--libgimp/gimppaletteselect.c212
-rw-r--r--libgimp/gimppaletteselect.h45
-rw-r--r--libgimp/gimppaletteselect_pdb.c131
-rw-r--r--libgimp/gimppaletteselect_pdb.h45
-rw-r--r--libgimp/gimppaletteselectbutton.c488
-rw-r--r--libgimp/gimppaletteselectbutton.h79
-rw-r--r--libgimp/gimppaths_pdb.c560
-rw-r--r--libgimp/gimppaths_pdb.h99
-rw-r--r--libgimp/gimppattern_pdb.c147
-rw-r--r--libgimp/gimppattern_pdb.h49
-rw-r--r--libgimp/gimppatternmenu.c151
-rw-r--r--libgimp/gimppatternmenu.h49
-rw-r--r--libgimp/gimppatterns.c40
-rw-r--r--libgimp/gimppatterns.h37
-rw-r--r--libgimp/gimppatterns_pdb.c194
-rw-r--r--libgimp/gimppatterns_pdb.h52
-rw-r--r--libgimp/gimppatternselect.c230
-rw-r--r--libgimp/gimppatternselect.h49
-rw-r--r--libgimp/gimppatternselect_pdb.c131
-rw-r--r--libgimp/gimppatternselect_pdb.h45
-rw-r--r--libgimp/gimppatternselectbutton.c730
-rw-r--r--libgimp/gimppatternselectbutton.h83
-rw-r--r--libgimp/gimppixbuf.c302
-rw-r--r--libgimp/gimppixbuf.h70
-rw-r--r--libgimp/gimppixelfetcher.c334
-rw-r--r--libgimp/gimppixelfetcher.h76
-rw-r--r--libgimp/gimppixelrgn.c965
-rw-r--r--libgimp/gimppixelrgn.h122
-rw-r--r--libgimp/gimpplugin.c71
-rw-r--r--libgimp/gimpplugin.h44
-rw-r--r--libgimp/gimpplugin_pdb.c357
-rw-r--r--libgimp/gimpplugin_pdb.h55
-rw-r--r--libgimp/gimpprocbrowserdialog.c546
-rw-r--r--libgimp/gimpprocbrowserdialog.h82
-rw-r--r--libgimp/gimpproceduraldb.c173
-rw-r--r--libgimp/gimpproceduraldb.h53
-rw-r--r--libgimp/gimpproceduraldb_pdb.c501
-rw-r--r--libgimp/gimpproceduraldb_pdb.h77
-rw-r--r--libgimp/gimpprocview.c339
-rw-r--r--libgimp/gimpprocview.h50
-rw-r--r--libgimp/gimpprogress.c452
-rw-r--r--libgimp/gimpprogress.h94
-rw-r--r--libgimp/gimpprogress_pdb.c324
-rw-r--r--libgimp/gimpprogress_pdb.h49
-rw-r--r--libgimp/gimpprogressbar.c230
-rw-r--r--libgimp/gimpprogressbar.h69
-rw-r--r--libgimp/gimpregioniterator.c397
-rw-r--r--libgimp/gimpregioniterator.h95
-rw-r--r--libgimp/gimpselectbutton.c95
-rw-r--r--libgimp/gimpselectbutton.h76
-rw-r--r--libgimp/gimpselection.c66
-rw-r--r--libgimp/gimpselection.h45
-rw-r--r--libgimp/gimpselection_pdb.c650
-rw-r--r--libgimp/gimpselection_pdb.h76
-rw-r--r--libgimp/gimpselectiontools_pdb.c461
-rw-r--r--libgimp/gimpselectiontools_pdb.h123
-rw-r--r--libgimp/gimptextlayer_pdb.c1127
-rw-r--r--libgimp/gimptextlayer_pdb.h97
-rw-r--r--libgimp/gimptexttool_pdb.c324
-rw-r--r--libgimp/gimptexttool_pdb.h91
-rw-r--r--libgimp/gimptile.c433
-rw-r--r--libgimp/gimptile.h73
-rw-r--r--libgimp/gimptilebackendplugin.c313
-rw-r--r--libgimp/gimptilebackendplugin.h59
-rw-r--r--libgimp/gimptransformtools_pdb.c287
-rw-r--r--libgimp/gimptransformtools_pdb.h79
-rw-r--r--libgimp/gimptypes.h82
-rw-r--r--libgimp/gimpui.c505
-rw-r--r--libgimp/gimpui.def90
-rw-r--r--libgimp/gimpui.h72
-rw-r--r--libgimp/gimpuimarshal.c261
-rw-r--r--libgimp/gimpuimarshal.h48
-rw-r--r--libgimp/gimpuimarshal.list28
-rw-r--r--libgimp/gimpuitypes.h53
-rw-r--r--libgimp/gimpunit_pdb.c427
-rw-r--r--libgimp/gimpunit_pdb.h58
-rw-r--r--libgimp/gimpunitcache.c236
-rw-r--r--libgimp/gimpunitcache.h52
-rw-r--r--libgimp/gimpvectors.c271
-rw-r--r--libgimp/gimpvectors.h73
-rw-r--r--libgimp/gimpvectors_pdb.c1191
-rw-r--r--libgimp/gimpvectors_pdb.h151
-rw-r--r--libgimp/gimpzoompreview.c1013
-rw-r--r--libgimp/gimpzoompreview.h95
-rw-r--r--libgimp/libgimp-intl.h45
-rw-r--r--libgimp/stdplugins-intl.h42
-rw-r--r--libgimpbase/Makefile.am264
-rw-r--r--libgimpbase/Makefile.in1660
-rw-r--r--libgimpbase/gimpbase-private.c93
-rw-r--r--libgimpbase/gimpbase-private.h69
-rw-r--r--libgimpbase/gimpbase.def244
-rw-r--r--libgimpbase/gimpbase.h46
-rw-r--r--libgimpbase/gimpbaseenums.c2045
-rw-r--r--libgimpbase/gimpbaseenums.h1558
-rw-r--r--libgimpbase/gimpbasetypes.c244
-rw-r--r--libgimpbase/gimpbasetypes.h113
-rw-r--r--libgimpbase/gimpchecks.c73
-rw-r--r--libgimpbase/gimpchecks.h68
-rw-r--r--libgimpbase/gimpcompatenums.c581
-rw-r--r--libgimpbase/gimpcompatenums.h252
-rw-r--r--libgimpbase/gimpcpuaccel.c531
-rw-r--r--libgimpbase/gimpcpuaccel.h76
-rw-r--r--libgimpbase/gimpdatafiles.c225
-rw-r--r--libgimpbase/gimpdatafiles.h72
-rw-r--r--libgimpbase/gimpenv.c1277
-rw-r--r--libgimpbase/gimpenv.h96
-rw-r--r--libgimpbase/gimplimits.h97
-rw-r--r--libgimpbase/gimpmemsize.c288
-rw-r--r--libgimpbase/gimpmemsize.h68
-rw-r--r--libgimpbase/gimpmetadata.c1796
-rw-r--r--libgimpbase/gimpmetadata.h154
-rw-r--r--libgimpbase/gimpparam.h66
-rw-r--r--libgimpbase/gimpparasite.c352
-rw-r--r--libgimpbase/gimpparasite.h113
-rw-r--r--libgimpbase/gimpparasiteio.c204
-rw-r--r--libgimpbase/gimpparasiteio.h92
-rw-r--r--libgimpbase/gimpprotocol.c1934
-rw-r--r--libgimpbase/gimpprotocol.h245
-rw-r--r--libgimpbase/gimprectangle.c133
-rw-r--r--libgimpbase/gimprectangle.h60
-rw-r--r--libgimpbase/gimpreloc.c435
-rw-r--r--libgimpbase/gimpreloc.h46
-rw-r--r--libgimpbase/gimpsignal.c110
-rw-r--r--libgimpbase/gimpsignal.h48
-rw-r--r--libgimpbase/gimpunit.c755
-rw-r--r--libgimpbase/gimpunit.h110
-rw-r--r--libgimpbase/gimputils.c1603
-rw-r--r--libgimpbase/gimputils.h87
-rw-r--r--libgimpbase/gimpvaluearray.c589
-rw-r--r--libgimpbase/gimpvaluearray.h104
-rw-r--r--libgimpbase/gimpversion.h70
-rw-r--r--libgimpbase/gimpwin32-io.h99
-rw-r--r--libgimpbase/gimpwire.c695
-rw-r--r--libgimpbase/gimpwire.h146
-rw-r--r--libgimpbase/test-cpu-accel.c48
-rw-r--r--libgimpcolor/Makefile.am148
-rw-r--r--libgimpcolor/Makefile.in1523
-rw-r--r--libgimpcolor/gimpadaptivesupersample.c394
-rw-r--r--libgimpcolor/gimpadaptivesupersample.h50
-rw-r--r--libgimpcolor/gimpbilinear.c313
-rw-r--r--libgimpcolor/gimpbilinear.h63
-rw-r--r--libgimpcolor/gimpcairo.c210
-rw-r--r--libgimpcolor/gimpcairo.h159
-rw-r--r--libgimpcolor/gimpcmyk.c233
-rw-r--r--libgimpcolor/gimpcmyk.h78
-rw-r--r--libgimpcolor/gimpcolor.def128
-rw-r--r--libgimpcolor/gimpcolor.h41
-rw-r--r--libgimpcolor/gimpcolormanaged.c151
-rw-r--r--libgimpcolor/gimpcolormanaged.h82
-rw-r--r--libgimpcolor/gimpcolorprofile.c1789
-rw-r--r--libgimpcolor/gimpcolorprofile.h128
-rw-r--r--libgimpcolor/gimpcolorspace.c1103
-rw-r--r--libgimpcolor/gimpcolorspace.h121
-rw-r--r--libgimpcolor/gimpcolortransform.c596
-rw-r--r--libgimpcolor/gimpcolortransform.h127
-rw-r--r--libgimpcolor/gimpcolortypes.h126
-rw-r--r--libgimpcolor/gimphsl.c93
-rw-r--r--libgimpcolor/gimphsl.h49
-rw-r--r--libgimpcolor/gimphsv.c107
-rw-r--r--libgimpcolor/gimphsv.h54
-rw-r--r--libgimpcolor/gimppixbuf.c152
-rw-r--r--libgimpcolor/gimppixbuf.h43
-rw-r--r--libgimpcolor/gimprgb-parse.c651
-rw-r--r--libgimpcolor/gimprgb.c846
-rw-r--r--libgimpcolor/gimprgb.h233
-rw-r--r--libgimpcolor/test-color-parser.c119
-rw-r--r--libgimpconfig/Makefile.am163
-rw-r--r--libgimpconfig/Makefile.in1179
-rw-r--r--libgimpconfig/gimpcolorconfig.c1087
-rw-r--r--libgimpconfig/gimpcolorconfig.h110
-rw-r--r--libgimpconfig/gimpconfig-deserialize.c1019
-rw-r--r--libgimpconfig/gimpconfig-deserialize.h44
-rw-r--r--libgimpconfig/gimpconfig-error.c51
-rw-r--r--libgimpconfig/gimpconfig-error.h59
-rw-r--r--libgimpconfig/gimpconfig-iface.c859
-rw-r--r--libgimpconfig/gimpconfig-iface.h141
-rw-r--r--libgimpconfig/gimpconfig-params.h240
-rw-r--r--libgimpconfig/gimpconfig-path.c720
-rw-r--r--libgimpconfig/gimpconfig-path.h108
-rw-r--r--libgimpconfig/gimpconfig-serialize.c576
-rw-r--r--libgimpconfig/gimpconfig-serialize.h52
-rw-r--r--libgimpconfig/gimpconfig-utils.c482
-rw-r--r--libgimpconfig/gimpconfig-utils.h52
-rw-r--r--libgimpconfig/gimpconfig.def94
-rw-r--r--libgimpconfig/gimpconfig.h40
-rw-r--r--libgimpconfig/gimpconfigenums.c79
-rw-r--r--libgimpconfig/gimpconfigenums.h68
-rw-r--r--libgimpconfig/gimpconfigtypes.h38
-rw-r--r--libgimpconfig/gimpconfigwriter.c799
-rw-r--r--libgimpconfig/gimpconfigwriter.h74
-rw-r--r--libgimpconfig/gimpscanner.c871
-rw-r--r--libgimpconfig/gimpscanner.h67
-rw-r--r--libgimpmath/Makefile.am93
-rw-r--r--libgimpmath/Makefile.in1071
-rw-r--r--libgimpmath/gimpmath.def78
-rw-r--r--libgimpmath/gimpmath.h153
-rw-r--r--libgimpmath/gimpmathtypes.h114
-rw-r--r--libgimpmath/gimpmatrix.c1080
-rw-r--r--libgimpmath/gimpmatrix.h157
-rw-r--r--libgimpmath/gimpmd5.c53
-rw-r--r--libgimpmath/gimpmd5.h26
-rw-r--r--libgimpmath/gimpvector.c1129
-rw-r--r--libgimpmath/gimpvector.h160
-rw-r--r--libgimpmodule/Makefile.am87
-rw-r--r--libgimpmodule/Makefile.in1070
-rw-r--r--libgimpmodule/gimpmodule.c536
-rw-r--r--libgimpmodule/gimpmodule.def18
-rw-r--r--libgimpmodule/gimpmodule.h250
-rw-r--r--libgimpmodule/gimpmoduledb.c508
-rw-r--r--libgimpmodule/gimpmoduledb.h84
-rw-r--r--libgimpmodule/gimpmoduletypes.h47
-rw-r--r--libgimpthumb/Makefile.am137
-rw-r--r--libgimpthumb/Makefile.in1145
-rw-r--r--libgimpthumb/gimp-thumbnail-list.c251
-rw-r--r--libgimpthumb/gimpthumb-enums.c75
-rw-r--r--libgimpthumb/gimpthumb-enums.h118
-rw-r--r--libgimpthumb/gimpthumb-error.c52
-rw-r--r--libgimpthumb/gimpthumb-error.h64
-rw-r--r--libgimpthumb/gimpthumb-types.h38
-rw-r--r--libgimpthumb/gimpthumb-utils.c870
-rw-r--r--libgimpthumb/gimpthumb-utils.h73
-rw-r--r--libgimpthumb/gimpthumb.def32
-rw-r--r--libgimpthumb/gimpthumb.h38
-rw-r--r--libgimpthumb/gimpthumbnail.c1400
-rw-r--r--libgimpthumb/gimpthumbnail.h135
-rw-r--r--libgimpwidgets/Makefile.am418
-rw-r--r--libgimpwidgets/Makefile.in1988
-rw-r--r--libgimpwidgets/gimp3migration.c262
-rw-r--r--libgimpwidgets/gimp3migration.h77
-rw-r--r--libgimpwidgets/gimpbrowser.c396
-rw-r--r--libgimpwidgets/gimpbrowser.h95
-rw-r--r--libgimpwidgets/gimpbusybox.c236
-rw-r--r--libgimpwidgets/gimpbusybox.h76
-rw-r--r--libgimpwidgets/gimpbutton.c165
-rw-r--r--libgimpwidgets/gimpbutton.h77
-rw-r--r--libgimpwidgets/gimpcairo-utils.c202
-rw-r--r--libgimpwidgets/gimpcairo-utils.h36
-rw-r--r--libgimpwidgets/gimpcellrenderercolor.c332
-rw-r--r--libgimpwidgets/gimpcellrenderercolor.h71
-rw-r--r--libgimpwidgets/gimpcellrenderertoggle.c576
-rw-r--r--libgimpwidgets/gimpcellrenderertoggle.h78
-rw-r--r--libgimpwidgets/gimpchainbutton.c554
-rw-r--r--libgimpwidgets/gimpchainbutton.h92
-rw-r--r--libgimpwidgets/gimpcolorarea.c956
-rw-r--r--libgimpwidgets/gimpcolorarea.h100
-rw-r--r--libgimpwidgets/gimpcolorbutton.c1014
-rw-r--r--libgimpwidgets/gimpcolorbutton.h114
-rw-r--r--libgimpwidgets/gimpcolordisplay.c563
-rw-r--r--libgimpwidgets/gimpcolordisplay.h142
-rw-r--r--libgimpwidgets/gimpcolordisplaystack.c412
-rw-r--r--libgimpwidgets/gimpcolordisplaystack.h104
-rw-r--r--libgimpwidgets/gimpcolorhexentry.c324
-rw-r--r--libgimpwidgets/gimpcolorhexentry.h75
-rw-r--r--libgimpwidgets/gimpcolornotebook.c547
-rw-r--r--libgimpwidgets/gimpcolornotebook.h78
-rw-r--r--libgimpwidgets/gimpcolorprofilechooserdialog.c355
-rw-r--r--libgimpwidgets/gimpcolorprofilechooserdialog.h67
-rw-r--r--libgimpwidgets/gimpcolorprofilecombobox.c628
-rw-r--r--libgimpwidgets/gimpcolorprofilecombobox.h90
-rw-r--r--libgimpwidgets/gimpcolorprofilestore-private.h52
-rw-r--r--libgimpwidgets/gimpcolorprofilestore.c794
-rw-r--r--libgimpwidgets/gimpcolorprofilestore.h76
-rw-r--r--libgimpwidgets/gimpcolorprofileview.c209
-rw-r--r--libgimpwidgets/gimpcolorprofileview.h68
-rw-r--r--libgimpwidgets/gimpcolorscale.c1133
-rw-r--r--libgimpwidgets/gimpcolorscale.h88
-rw-r--r--libgimpwidgets/gimpcolorscales.c925
-rw-r--r--libgimpwidgets/gimpcolorscales.h49
-rw-r--r--libgimpwidgets/gimpcolorselect.c2006
-rw-r--r--libgimpwidgets/gimpcolorselect.h41
-rw-r--r--libgimpwidgets/gimpcolorselection.c697
-rw-r--r--libgimpwidgets/gimpcolorselection.h106
-rw-r--r--libgimpwidgets/gimpcolorselector.c638
-rw-r--r--libgimpwidgets/gimpcolorselector.h177
-rw-r--r--libgimpwidgets/gimpcontroller.c263
-rw-r--r--libgimpwidgets/gimpcontroller.h183
-rw-r--r--libgimpwidgets/gimpdialog.c689
-rw-r--r--libgimpwidgets/gimpdialog.h95
-rw-r--r--libgimpwidgets/gimpeevl.c666
-rw-r--r--libgimpwidgets/gimpeevl.h99
-rw-r--r--libgimpwidgets/gimpenumcombobox.c229
-rw-r--r--libgimpwidgets/gimpenumcombobox.h74
-rw-r--r--libgimpwidgets/gimpenumlabel.c218
-rw-r--r--libgimpwidgets/gimpenumlabel.h66
-rw-r--r--libgimpwidgets/gimpenumstore.c397
-rw-r--r--libgimpwidgets/gimpenumstore.h85
-rw-r--r--libgimpwidgets/gimpenumwidgets.c536
-rw-r--r--libgimpwidgets/gimpenumwidgets.h97
-rw-r--r--libgimpwidgets/gimpfileentry.c499
-rw-r--r--libgimpwidgets/gimpfileentry.h91
-rw-r--r--libgimpwidgets/gimpframe.c372
-rw-r--r--libgimpwidgets/gimpframe.h67
-rw-r--r--libgimpwidgets/gimphelpui.c563
-rw-r--r--libgimpwidgets/gimphelpui.h76
-rw-r--r--libgimpwidgets/gimphintbox.c246
-rw-r--r--libgimpwidgets/gimphintbox.h75
-rw-r--r--libgimpwidgets/gimpicons.c651
-rw-r--r--libgimpwidgets/gimpicons.h680
-rw-r--r--libgimpwidgets/gimpintcombobox.c978
-rw-r--r--libgimpwidgets/gimpintcombobox.h117
-rw-r--r--libgimpwidgets/gimpintstore.c351
-rw-r--r--libgimpwidgets/gimpintstore.h104
-rw-r--r--libgimpwidgets/gimpmemsizeentry.c320
-rw-r--r--libgimpwidgets/gimpmemsizeentry.h84
-rw-r--r--libgimpwidgets/gimpnumberpairentry.c1301
-rw-r--r--libgimpwidgets/gimpnumberpairentry.h104
-rw-r--r--libgimpwidgets/gimpoffsetarea.c506
-rw-r--r--libgimpwidgets/gimpoffsetarea.h91
-rw-r--r--libgimpwidgets/gimpoldwidgets.c650
-rw-r--r--libgimpwidgets/gimpoldwidgets.h122
-rw-r--r--libgimpwidgets/gimppageselector.c1327
-rw-r--r--libgimpwidgets/gimppageselector.h105
-rw-r--r--libgimpwidgets/gimppatheditor.c876
-rw-r--r--libgimpwidgets/gimppatheditor.h105
-rw-r--r--libgimpwidgets/gimppickbutton-default.c368
-rw-r--r--libgimpwidgets/gimppickbutton-default.h25
-rw-r--r--libgimpwidgets/gimppickbutton-kwin.c112
-rw-r--r--libgimpwidgets/gimppickbutton-kwin.h25
-rw-r--r--libgimpwidgets/gimppickbutton-quartz.c485
-rw-r--r--libgimpwidgets/gimppickbutton-quartz.h25
-rw-r--r--libgimpwidgets/gimppickbutton-win32.c1135
-rw-r--r--libgimpwidgets/gimppickbutton-win32.h23
-rw-r--r--libgimpwidgets/gimppickbutton-xdg.c152
-rw-r--r--libgimpwidgets/gimppickbutton-xdg.h25
-rw-r--r--libgimpwidgets/gimppickbutton.c188
-rw-r--r--libgimpwidgets/gimppickbutton.h69
-rw-r--r--libgimpwidgets/gimppixmap.c179
-rw-r--r--libgimpwidgets/gimppixmap.h78
-rw-r--r--libgimpwidgets/gimppreview.c851
-rw-r--r--libgimpwidgets/gimppreview.h149
-rw-r--r--libgimpwidgets/gimppreviewarea.c1956
-rw-r--r--libgimpwidgets/gimppreviewarea.h133
-rw-r--r--libgimpwidgets/gimppropwidgets.c4352
-rw-r--r--libgimpwidgets/gimppropwidgets.h243
-rw-r--r--libgimpwidgets/gimpquerybox.c699
-rw-r--r--libgimpwidgets/gimpquerybox.h180
-rw-r--r--libgimpwidgets/gimpruler.c1465
-rw-r--r--libgimpwidgets/gimpruler.h81
-rw-r--r--libgimpwidgets/gimpscaleentry.c479
-rw-r--r--libgimpwidgets/gimpscaleentry.h125
-rw-r--r--libgimpwidgets/gimpscrolledpreview.c917
-rw-r--r--libgimpwidgets/gimpscrolledpreview.h90
-rw-r--r--libgimpwidgets/gimpsizeentry.c1594
-rw-r--r--libgimpwidgets/gimpsizeentry.h155
-rw-r--r--libgimpwidgets/gimpspinbutton.c385
-rw-r--r--libgimpwidgets/gimpspinbutton.h90
-rw-r--r--libgimpwidgets/gimpstringcombobox.c363
-rw-r--r--libgimpwidgets/gimpstringcombobox.h74
-rw-r--r--libgimpwidgets/gimpunitcombobox.c218
-rw-r--r--libgimpwidgets/gimpunitcombobox.h71
-rw-r--r--libgimpwidgets/gimpunitmenu.c633
-rw-r--r--libgimpwidgets/gimpunitmenu.h105
-rw-r--r--libgimpwidgets/gimpunitstore.c937
-rw-r--r--libgimpwidgets/gimpunitstore.h113
-rw-r--r--libgimpwidgets/gimpwidgets-error.c40
-rw-r--r--libgimpwidgets/gimpwidgets-error.h58
-rw-r--r--libgimpwidgets/gimpwidgets-private.c106
-rw-r--r--libgimpwidgets/gimpwidgets-private.h47
-rw-r--r--libgimpwidgets/gimpwidgets.c997
-rw-r--r--libgimpwidgets/gimpwidgets.def470
-rw-r--r--libgimpwidgets/gimpwidgets.h256
-rw-r--r--libgimpwidgets/gimpwidgetsenums.c313
-rw-r--r--libgimpwidgets/gimpwidgetsenums.h239
-rw-r--r--libgimpwidgets/gimpwidgetsmarshal.c433
-rw-r--r--libgimpwidgets/gimpwidgetsmarshal.h90
-rw-r--r--libgimpwidgets/gimpwidgetsmarshal.list35
-rw-r--r--libgimpwidgets/gimpwidgetstypes.h110
-rw-r--r--libgimpwidgets/gimpwidgetsutils.c921
-rw-r--r--libgimpwidgets/gimpwidgetsutils.h66
-rw-r--r--libgimpwidgets/gimpzoommodel.c698
-rw-r--r--libgimpwidgets/gimpzoommodel.h89
-rw-r--r--libgimpwidgets/test-eevl.c196
-rw-r--r--libgimpwidgets/test-preview-area.c240
521 files changed, 173966 insertions, 0 deletions
diff --git a/libgimp/COPYING b/libgimp/COPYING
new file mode 100644
index 0000000..804fcb1
--- /dev/null
+++ b/libgimp/COPYING
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/libgimp/Makefile.am b/libgimp/Makefile.am
new file mode 100644
index 0000000..6e0a9e2
--- /dev/null
+++ b/libgimp/Makefile.am
@@ -0,0 +1,488 @@
+## Process this file with automake to produce Makefile.in
+
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpmodule = $(top_builddir)/libgimpmodule/libgimpmodule-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = ./libgimp-$(GIMP_API_VERSION).la
+
+if PLATFORM_WIN32
+no_undefined = -no-undefined
+endif
+
+if PLATFORM_OSX
+xobjective_c = "-xobjective-c"
+xobjective_cxx = "-xobjective-c++"
+xnone = "-xnone"
+framework_cocoa = -framework Cocoa
+endif
+
+if OS_WIN32
+if HAVE_EXCHNDL
+exchndl = -lexchndl
+endif
+
+gimp_def = gimp.def
+gimpui_def = gimpui.def
+libgimp_export_symbols = -export-symbols $(srcdir)/gimp.def
+libgimpui_export_symbols = -export-symbols $(srcdir)/gimpui.def
+
+install-libtool-import-lib:
+ $(INSTALL) .libs/libgimp-$(GIMP_API_VERSION).dll.a $(DESTDIR)$(libdir)
+ $(INSTALL) .libs/libgimpui-$(GIMP_API_VERSION).dll.a $(DESTDIR)$(libdir)
+ $(INSTALL) $(srcdir)/gimp.def $(DESTDIR)$(libdir)
+ $(INSTALL) $(srcdir)/gimpui.def $(DESTDIR)$(libdir)
+
+uninstall-libtool-import-lib:
+ -rm $(DESTDIR)$(libdir)/libgimp-$(GIMP_API_VERSION).dll.a
+ -rm $(DESTDIR)$(libdir)/libgimpui-$(GIMP_API_VERSION).dll.a
+ -rm $(DESTDIR)$(libdir)/gimp.def
+ -rm $(DESTDIR)$(libdir)/gimpui.def
+else
+install-libtool-import-lib:
+uninstall-libtool-import-lib:
+endif
+
+if MS_LIB_AVAILABLE
+noinst_DATA = gimp-$(GIMP_API_VERSION).lib gimpui-$(GIMP_API_VERSION).lib
+
+install-ms-lib:
+ $(INSTALL) gimp-$(GIMP_API_VERSION).lib $(DESTDIR)$(libdir)
+ $(INSTALL) gimpui-$(GIMP_API_VERSION).lib $(DESTDIR)$(libdir)
+
+uninstall-ms-lib:
+ -rm $(DESTDIR)$(libdir)/gimp-$(GIMP_API_VERSION).lib
+ -rm $(DESTDIR)$(libdir)/gimpui-$(GIMP_API_VERSION).lib
+
+gimp-@GIMP_API_VERSION@.lib: gimp.def
+ lib -name:libgimp-$(GIMP_API_VERSION)-@LT_CURRENT_MINUS_AGE@.dll -def:gimp.def -out:$@
+
+gimpui-@GIMP_API_VERSION@.lib: gimpui.def
+ lib -name:libgimpui-$(GIMP_API_VERSION)-@LT_CURRENT_MINUS_AGE@.dll -def:gimpui.def -out:$@
+
+else
+install-ms-lib:
+uninstall-ms-lib:
+endif
+
+gimpincludedir = $(includedir)/gimp-$(GIMP_API_VERSION)/libgimp
+
+AM_CPPFLAGS = \
+ -DGIMPDIR=\""$(gimpdir)"\" \
+ -DGIMP_USER_VERSION=\"$(GIMP_USER_VERSION)\" \
+ -DG_LOG_DOMAIN=\"LibGimp\" \
+ -DGIMP_COMPILATION \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(GEXIV2_CFLAGS) \
+ -I$(includedir)
+
+AM_CFLAGS = \
+ $(xobjective_c)
+
+AM_CXXFLAGS = \
+ $(xobjective_cxx)
+
+AM_LDFLAGS = \
+ $(xnone)
+
+lib_LTLIBRARIES = libgimp-@GIMP_API_VERSION@.la libgimpui-@GIMP_API_VERSION@.la
+
+PDB_WRAPPERS_C = \
+ gimp_pdb.c \
+ gimpbrush_pdb.c \
+ gimpbrushes_pdb.c \
+ gimpbrushselect_pdb.c \
+ gimpbuffer_pdb.c \
+ gimpchannel_pdb.c \
+ gimpcolor_pdb.c \
+ gimpcontext_pdb.c \
+ gimpdebug_pdb.c \
+ gimpdisplay_pdb.c \
+ gimpdrawable_pdb.c \
+ gimpdrawablecolor_pdb.c \
+ gimpdrawableedit_pdb.c \
+ gimpdrawabletransform_pdb.c \
+ gimpdynamics_pdb.c \
+ gimpedit_pdb.c \
+ gimpfileops_pdb.c \
+ gimpfloatingsel_pdb.c \
+ gimpfonts_pdb.c \
+ gimpfontselect_pdb.c \
+ gimpgimprc_pdb.c \
+ gimpgradient_pdb.c \
+ gimpgradients_pdb.c \
+ gimpgradientselect_pdb.c \
+ gimphelp_pdb.c \
+ gimpimage_pdb.c \
+ gimpimagecolorprofile_pdb.c \
+ gimpimageconvert_pdb.c \
+ gimpimagegrid_pdb.c \
+ gimpimageguides_pdb.c \
+ gimpimagesamplepoints_pdb.c \
+ gimpimageselect_pdb.c \
+ gimpimagetransform_pdb.c \
+ gimpimageundo_pdb.c \
+ gimpitem_pdb.c \
+ gimpitemtransform_pdb.c \
+ gimplayer_pdb.c \
+ gimpmessage_pdb.c \
+ gimppainttools_pdb.c \
+ gimppalette_pdb.c \
+ gimppalettes_pdb.c \
+ gimppaletteselect_pdb.c \
+ gimppaths_pdb.c \
+ gimppattern_pdb.c \
+ gimppatterns_pdb.c \
+ gimppatternselect_pdb.c \
+ gimpplugin_pdb.c \
+ gimpproceduraldb_pdb.c \
+ gimpprogress_pdb.c \
+ gimpselection_pdb.c \
+ gimpselectiontools_pdb.c \
+ gimptextlayer_pdb.c \
+ gimptexttool_pdb.c \
+ gimptransformtools_pdb.c \
+ gimpunit_pdb.c \
+ gimpvectors_pdb.c
+
+PDB_WRAPPERS_H = \
+ gimp_pdb_headers.h \
+ gimp_pdb.h \
+ gimpbrush_pdb.h \
+ gimpbrushes_pdb.h \
+ gimpbrushselect_pdb.h \
+ gimpbuffer_pdb.h \
+ gimpchannel_pdb.h \
+ gimpcolor_pdb.h \
+ gimpcontext_pdb.h \
+ gimpdebug_pdb.h \
+ gimpdisplay_pdb.h \
+ gimpdrawable_pdb.h \
+ gimpdrawablecolor_pdb.h \
+ gimpdrawableedit_pdb.h \
+ gimpdrawabletransform_pdb.h \
+ gimpdynamics_pdb.h \
+ gimpedit_pdb.h \
+ gimpfileops_pdb.h \
+ gimpfloatingsel_pdb.h \
+ gimpfonts_pdb.h \
+ gimpfontselect_pdb.h \
+ gimpgimprc_pdb.h \
+ gimpgradient_pdb.h \
+ gimpgradients_pdb.h \
+ gimpgradientselect_pdb.h \
+ gimphelp_pdb.h \
+ gimpimage_pdb.h \
+ gimpimagecolorprofile_pdb.h \
+ gimpimageconvert_pdb.h \
+ gimpimagegrid_pdb.h \
+ gimpimageguides_pdb.h \
+ gimpimagesamplepoints_pdb.h \
+ gimpimageselect_pdb.h \
+ gimpimagetransform_pdb.h \
+ gimpimageundo_pdb.h \
+ gimpitem_pdb.h \
+ gimpitemtransform_pdb.h \
+ gimplayer_pdb.h \
+ gimpmessage_pdb.h \
+ gimppainttools_pdb.h \
+ gimppalette_pdb.h \
+ gimppalettes_pdb.h \
+ gimppaletteselect_pdb.h \
+ gimppaths_pdb.h \
+ gimppattern_pdb.h \
+ gimppatterns_pdb.h \
+ gimppatternselect_pdb.h \
+ gimpplugin_pdb.h \
+ gimpproceduraldb_pdb.h \
+ gimpprogress_pdb.h \
+ gimpselection_pdb.h \
+ gimpselectiontools_pdb.h \
+ gimptextlayer_pdb.h \
+ gimptexttool_pdb.h \
+ gimptransformtools_pdb.h \
+ gimpunit_pdb.h \
+ gimpvectors_pdb.h
+
+libgimp_sources = \
+ gimp.c \
+ gimp.h \
+ gimptypes.h \
+ gimpenums.h \
+ ${PDB_WRAPPERS_C} \
+ ${PDB_WRAPPERS_H} \
+ gimpbrushes.c \
+ gimpbrushes.h \
+ gimpbrushselect.c \
+ gimpbrushselect.h \
+ gimpchannel.c \
+ gimpchannel.h \
+ gimpdrawable.c \
+ gimpdrawable.h \
+ gimpedit.c \
+ gimpedit.h \
+ gimpfontselect.c \
+ gimpfontselect.h \
+ gimpgimprc.c \
+ gimpgimprc.h \
+ gimpgradients.c \
+ gimpgradients.h \
+ gimpgradientselect.c \
+ gimpgradientselect.h \
+ gimpimage.c \
+ gimpimage.h \
+ gimpimagecolorprofile.c \
+ gimpimagecolorprofile.h \
+ gimplayer.c \
+ gimplayer.h \
+ gimppalette.c \
+ gimppalette.h \
+ gimppalettes.c \
+ gimppalettes.h \
+ gimppaletteselect.c \
+ gimppaletteselect.h \
+ gimppatterns.c \
+ gimppatterns.h \
+ gimppatternselect.c \
+ gimppatternselect.h \
+ gimppixbuf.c \
+ gimppixbuf.h \
+ gimppixelfetcher.c \
+ gimppixelfetcher.h \
+ gimppixelrgn.c \
+ gimppixelrgn.h \
+ gimpplugin.c \
+ gimpplugin.h \
+ gimpproceduraldb.c \
+ gimpproceduraldb.h \
+ gimpprogress.c \
+ gimpprogress.h \
+ gimpregioniterator.c \
+ gimpregioniterator.h \
+ gimpselection.c \
+ gimpselection.h \
+ gimptile.c \
+ gimptile.h \
+ gimptilebackendplugin.c \
+ gimptilebackendplugin.h \
+ gimpunitcache.c \
+ gimpunitcache.h \
+ gimpvectors.c \
+ gimpvectors.h \
+ stdplugins-intl.h \
+ libgimp-intl.h
+
+libgimp_built_sources = \
+ gimpenums.c
+
+libgimp_extra_sources = gimpenums.c.tail
+
+libgimp_@GIMP_API_VERSION@_la_SOURCES = \
+ $(libgimp_built_sources) \
+ $(libgimp_sources)
+
+libgimpui_sources = \
+ gimpui.c \
+ gimpui.h \
+ gimpuitypes.h \
+ gimpaspectpreview.c \
+ gimpaspectpreview.h \
+ gimpbrushmenu.c \
+ gimpbrushmenu.h \
+ gimpbrushselectbutton.c \
+ gimpbrushselectbutton.h \
+ gimpdrawablepreview.c \
+ gimpdrawablepreview.h \
+ gimpexport.c \
+ gimpexport.h \
+ gimpfontmenu.c \
+ gimpfontmenu.h \
+ gimpfontselectbutton.c \
+ gimpfontselectbutton.h \
+ gimpgradientmenu.c \
+ gimpgradientmenu.h \
+ gimpgradientselectbutton.c \
+ gimpgradientselectbutton.h \
+ gimpimagecombobox.c \
+ gimpimagecombobox.h \
+ gimpimagemetadata.c \
+ gimpimagemetadata.h \
+ gimpitemcombobox.c \
+ gimpitemcombobox.h \
+ gimpmenu.c \
+ gimpmenu.h \
+ gimppalettemenu.c \
+ gimppalettemenu.h \
+ gimppaletteselectbutton.c \
+ gimppaletteselectbutton.h \
+ gimppatternmenu.c \
+ gimppatternmenu.h \
+ gimppatternselectbutton.c \
+ gimppatternselectbutton.h \
+ gimpprocbrowserdialog.c \
+ gimpprocbrowserdialog.h \
+ gimpprocview.c \
+ gimpprocview.h \
+ gimpprogressbar.c \
+ gimpprogressbar.h \
+ gimpselectbutton.c \
+ gimpselectbutton.h \
+ gimpzoompreview.c \
+ gimpzoompreview.h
+
+libgimpui_built_sources = \
+ gimpuimarshal.c \
+ gimpuimarshal.h
+
+libgimpui_extra_sources = gimpuimarshal.list
+
+libgimpui_@GIMP_API_VERSION@_la_SOURCES = \
+ $(libgimpui_built_sources) \
+ $(libgimpui_sources)
+
+gimpinclude_HEADERS = \
+ gimp.h \
+ gimptypes.h \
+ gimpenums.h \
+ ${PDB_WRAPPERS_H} \
+ gimpbrushes.h \
+ gimpbrushselect.h \
+ gimpchannel.h \
+ gimpdrawable.h \
+ gimpedit.h \
+ gimpfontselect.h \
+ gimpgimprc.h \
+ gimpgradients.h \
+ gimpgradientselect.h \
+ gimpimage.h \
+ gimpimagecolorprofile.h \
+ gimplayer.h \
+ gimppalette.h \
+ gimppalettes.h \
+ gimppaletteselect.h \
+ gimppatterns.h \
+ gimppatternselect.h \
+ gimppixelfetcher.h \
+ gimppixelrgn.h \
+ gimpplugin.h \
+ gimpproceduraldb.h \
+ gimpprogress.h \
+ gimpregioniterator.h \
+ gimpselection.h \
+ gimptile.h \
+ gimpvectors.h \
+ \
+ gimpui.h \
+ gimpuitypes.h \
+ gimpaspectpreview.h \
+ gimpbrushmenu.h \
+ gimpbrushselectbutton.h \
+ gimpdrawablepreview.h \
+ gimpexport.h \
+ gimpfontmenu.h \
+ gimpfontselectbutton.h \
+ gimpgradientmenu.h \
+ gimpgradientselectbutton.h \
+ gimpimagecombobox.h \
+ gimpimagemetadata.h \
+ gimpitemcombobox.h \
+ gimpmenu.h \
+ gimppalettemenu.h \
+ gimppaletteselectbutton.h \
+ gimppatternmenu.h \
+ gimppatternselectbutton.h \
+ gimppixbuf.h \
+ gimpprocbrowserdialog.h \
+ gimpprocview.h \
+ gimpprogressbar.h \
+ gimpselectbutton.h \
+ gimpzoompreview.h
+
+libgimp_@GIMP_API_VERSION@_la_LDFLAGS = \
+ -version-info $(LT_VERSION_INFO) \
+ $(no_undefined) \
+ $(libgimp_export_symbols) \
+ $(framework_cocoa) \
+ $(xnone)
+
+EXTRA_libgimp_@GIMP_API_VERSION@_la_DEPENDENCIES = $(gimp_def)
+
+libgimp_@GIMP_API_VERSION@_la_LIBADD = \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(exchndl) \
+ $(CAIRO_LIBS) \
+ $(GEGL_LIBS) \
+ $(GDK_PIXBUF_LIBS) \
+ $(RT_LIBS)
+
+libgimpui_@GIMP_API_VERSION@_la_LDFLAGS = \
+ -version-info $(LT_VERSION_INFO) \
+ $(no_undefined) \
+ $(libgimpui_export_symbols) \
+ $(framework_cocoa) \
+ $(xnone)
+
+EXTRA_libgimpui_@GIMP_API_VERSION@_la_DEPENDENCIES = $(gimpui_def)
+
+libgimpui_@GIMP_API_VERSION@_la_LIBADD = \
+ $(libgimp) \
+ $(libgimpwidgets) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(libgimpmodule) \
+ $(GEGL_LIBS) \
+ $(GTK_LIBS) \
+ $(GEXIV2_LIBS) \
+ $(RT_LIBS)
+
+BUILT_SOURCES = \
+ $(libgimp_built_sources) \
+ $(libgimpui_built_sources)
+
+EXTRA_DIST = \
+ COPYING \
+ gimp.def \
+ gimpui.def \
+ $(libgimp_extra_sources) \
+ $(libgimpui_extra_sources)
+
+install-data-local: install-ms-lib install-libtool-import-lib
+
+uninstall-local: uninstall-ms-lib uninstall-libtool-import-lib
+
+#
+# rules to generate built sources
+#
+# setup autogeneration dependencies
+gen_sources = xgen-cec xgen-umh xgen-umc
+CLEANFILES = $(gen_sources)
+
+gimpenums.c: $(srcdir)/gimpenums.h $(srcdir)/gimpenums.c.tail $(GIMP_MKENUMS) Makefile.am
+ $(AM_V_GEN) $(GIMP_MKENUMS) \
+ --fhead "#include \"config.h\"\n#include <gio/gio.h>\n#include <gegl.h>\n#undef GIMP_DISABLE_DEPRECATED\n#include \"libgimpbase/gimpbase.h\"\n#include \"libgimpbase/gimpbase-private.h\"\n#include \"libgimpconfig/gimpconfigenums.h\"\n#include \"gimpenums.h\"" \
+ --fprod "\n/* enumerations from \"@filename@\" */" \
+ --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \
+ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \
+ --vtail " { 0, NULL, NULL }\n };\n" \
+ --dhead " static const Gimp@Type@Desc descs[] =\n {" \
+ --dprod " { @VALUENAME@, @valuedesc@, @valuehelp@ },@if ('@valueabbrev@' ne 'NULL')@\n /* Translators: this is an abbreviated version of @valueudesc@.\n Keep it short. */\n { @VALUENAME@, @valueabbrev@, NULL },@endif@" \
+ --dtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n {\n type = g_@type@_register_static (\"@EnumName@\", values);\n gimp_type_set_translation_domain (type, GETTEXT_PACKAGE \"-libgimp\");\n gimp_type_set_translation_context (type, \"@enumnick@\");\n gimp_@type@_set_value_descriptions (type, descs);\n }\n\n return type;\n}\n" \
+ $< > xgen-cec \
+ && cat $(srcdir)/gimpenums.c.tail >> xgen-cec \
+ && cp xgen-cec $(@F) \
+ && rm -f xgen-cec
+
+gimpuimarshal.h: $(srcdir)/gimpuimarshal.list
+ $(AM_V_GEN) $(GLIB_GENMARSHAL) --prefix=_gimpui_marshal $(srcdir)/gimpuimarshal.list --header >> xgen-umh \
+ && (cmp -s xgen-umh $(@F) || cp xgen-umh $(@F)) \
+ && rm -f xgen-umh xgen-umh~
+
+gimpuimarshal.c: gimpuimarshal.h
+ $(AM_V_GEN) $(GLIB_GENMARSHAL) --prefix=_gimpui_marshal $(srcdir)/gimpuimarshal.list --header --body >> xgen-umc \
+ && cp xgen-umc $(@F) \
+ && rm -f xgen-umc xgen-umc~
diff --git a/libgimp/Makefile.in b/libgimp/Makefile.in
new file mode 100644
index 0000000..c34a1cf
--- /dev/null
+++ b/libgimp/Makefile.in
@@ -0,0 +1,1936 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = libgimp
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(gimpinclude_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(gimpincludedir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libgimp_@GIMP_API_VERSION@_la_DEPENDENCIES = $(libgimpconfig) \
+ $(libgimpcolor) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am__objects_1 = gimpenums.lo
+am__objects_2 = gimp_pdb.lo gimpbrush_pdb.lo gimpbrushes_pdb.lo \
+ gimpbrushselect_pdb.lo gimpbuffer_pdb.lo gimpchannel_pdb.lo \
+ gimpcolor_pdb.lo gimpcontext_pdb.lo gimpdebug_pdb.lo \
+ gimpdisplay_pdb.lo gimpdrawable_pdb.lo \
+ gimpdrawablecolor_pdb.lo gimpdrawableedit_pdb.lo \
+ gimpdrawabletransform_pdb.lo gimpdynamics_pdb.lo \
+ gimpedit_pdb.lo gimpfileops_pdb.lo gimpfloatingsel_pdb.lo \
+ gimpfonts_pdb.lo gimpfontselect_pdb.lo gimpgimprc_pdb.lo \
+ gimpgradient_pdb.lo gimpgradients_pdb.lo \
+ gimpgradientselect_pdb.lo gimphelp_pdb.lo gimpimage_pdb.lo \
+ gimpimagecolorprofile_pdb.lo gimpimageconvert_pdb.lo \
+ gimpimagegrid_pdb.lo gimpimageguides_pdb.lo \
+ gimpimagesamplepoints_pdb.lo gimpimageselect_pdb.lo \
+ gimpimagetransform_pdb.lo gimpimageundo_pdb.lo gimpitem_pdb.lo \
+ gimpitemtransform_pdb.lo gimplayer_pdb.lo gimpmessage_pdb.lo \
+ gimppainttools_pdb.lo gimppalette_pdb.lo gimppalettes_pdb.lo \
+ gimppaletteselect_pdb.lo gimppaths_pdb.lo gimppattern_pdb.lo \
+ gimppatterns_pdb.lo gimppatternselect_pdb.lo gimpplugin_pdb.lo \
+ gimpproceduraldb_pdb.lo gimpprogress_pdb.lo \
+ gimpselection_pdb.lo gimpselectiontools_pdb.lo \
+ gimptextlayer_pdb.lo gimptexttool_pdb.lo \
+ gimptransformtools_pdb.lo gimpunit_pdb.lo gimpvectors_pdb.lo
+am__objects_3 =
+am__objects_4 = gimp.lo $(am__objects_2) $(am__objects_3) \
+ gimpbrushes.lo gimpbrushselect.lo gimpchannel.lo \
+ gimpdrawable.lo gimpedit.lo gimpfontselect.lo gimpgimprc.lo \
+ gimpgradients.lo gimpgradientselect.lo gimpimage.lo \
+ gimpimagecolorprofile.lo gimplayer.lo gimppalette.lo \
+ gimppalettes.lo gimppaletteselect.lo gimppatterns.lo \
+ gimppatternselect.lo gimppixbuf.lo gimppixelfetcher.lo \
+ gimppixelrgn.lo gimpplugin.lo gimpproceduraldb.lo \
+ gimpprogress.lo gimpregioniterator.lo gimpselection.lo \
+ gimptile.lo gimptilebackendplugin.lo gimpunitcache.lo \
+ gimpvectors.lo
+am_libgimp_@GIMP_API_VERSION@_la_OBJECTS = $(am__objects_1) \
+ $(am__objects_4)
+libgimp_@GIMP_API_VERSION@_la_OBJECTS = \
+ $(am_libgimp_@GIMP_API_VERSION@_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libgimp_@GIMP_API_VERSION@_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) \
+ $(libgimp_@GIMP_API_VERSION@_la_LDFLAGS) $(LDFLAGS) -o $@
+libgimpui_@GIMP_API_VERSION@_la_DEPENDENCIES = $(libgimp) \
+ $(libgimpwidgets) $(libgimpcolor) $(libgimpbase) \
+ $(libgimpmodule) $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am__objects_5 = gimpuimarshal.lo
+am__objects_6 = gimpui.lo gimpaspectpreview.lo gimpbrushmenu.lo \
+ gimpbrushselectbutton.lo gimpdrawablepreview.lo gimpexport.lo \
+ gimpfontmenu.lo gimpfontselectbutton.lo gimpgradientmenu.lo \
+ gimpgradientselectbutton.lo gimpimagecombobox.lo \
+ gimpimagemetadata.lo gimpitemcombobox.lo gimpmenu.lo \
+ gimppalettemenu.lo gimppaletteselectbutton.lo \
+ gimppatternmenu.lo gimppatternselectbutton.lo \
+ gimpprocbrowserdialog.lo gimpprocview.lo gimpprogressbar.lo \
+ gimpselectbutton.lo gimpzoompreview.lo
+am_libgimpui_@GIMP_API_VERSION@_la_OBJECTS = $(am__objects_5) \
+ $(am__objects_6)
+libgimpui_@GIMP_API_VERSION@_la_OBJECTS = \
+ $(am_libgimpui_@GIMP_API_VERSION@_la_OBJECTS)
+libgimpui_@GIMP_API_VERSION@_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) \
+ $(libgimpui_@GIMP_API_VERSION@_la_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/gimp.Plo ./$(DEPDIR)/gimp_pdb.Plo \
+ ./$(DEPDIR)/gimpaspectpreview.Plo \
+ ./$(DEPDIR)/gimpbrush_pdb.Plo ./$(DEPDIR)/gimpbrushes.Plo \
+ ./$(DEPDIR)/gimpbrushes_pdb.Plo ./$(DEPDIR)/gimpbrushmenu.Plo \
+ ./$(DEPDIR)/gimpbrushselect.Plo \
+ ./$(DEPDIR)/gimpbrushselect_pdb.Plo \
+ ./$(DEPDIR)/gimpbrushselectbutton.Plo \
+ ./$(DEPDIR)/gimpbuffer_pdb.Plo ./$(DEPDIR)/gimpchannel.Plo \
+ ./$(DEPDIR)/gimpchannel_pdb.Plo ./$(DEPDIR)/gimpcolor_pdb.Plo \
+ ./$(DEPDIR)/gimpcontext_pdb.Plo ./$(DEPDIR)/gimpdebug_pdb.Plo \
+ ./$(DEPDIR)/gimpdisplay_pdb.Plo ./$(DEPDIR)/gimpdrawable.Plo \
+ ./$(DEPDIR)/gimpdrawable_pdb.Plo \
+ ./$(DEPDIR)/gimpdrawablecolor_pdb.Plo \
+ ./$(DEPDIR)/gimpdrawableedit_pdb.Plo \
+ ./$(DEPDIR)/gimpdrawablepreview.Plo \
+ ./$(DEPDIR)/gimpdrawabletransform_pdb.Plo \
+ ./$(DEPDIR)/gimpdynamics_pdb.Plo ./$(DEPDIR)/gimpedit.Plo \
+ ./$(DEPDIR)/gimpedit_pdb.Plo ./$(DEPDIR)/gimpenums.Plo \
+ ./$(DEPDIR)/gimpexport.Plo ./$(DEPDIR)/gimpfileops_pdb.Plo \
+ ./$(DEPDIR)/gimpfloatingsel_pdb.Plo \
+ ./$(DEPDIR)/gimpfontmenu.Plo ./$(DEPDIR)/gimpfonts_pdb.Plo \
+ ./$(DEPDIR)/gimpfontselect.Plo \
+ ./$(DEPDIR)/gimpfontselect_pdb.Plo \
+ ./$(DEPDIR)/gimpfontselectbutton.Plo \
+ ./$(DEPDIR)/gimpgimprc.Plo ./$(DEPDIR)/gimpgimprc_pdb.Plo \
+ ./$(DEPDIR)/gimpgradient_pdb.Plo \
+ ./$(DEPDIR)/gimpgradientmenu.Plo ./$(DEPDIR)/gimpgradients.Plo \
+ ./$(DEPDIR)/gimpgradients_pdb.Plo \
+ ./$(DEPDIR)/gimpgradientselect.Plo \
+ ./$(DEPDIR)/gimpgradientselect_pdb.Plo \
+ ./$(DEPDIR)/gimpgradientselectbutton.Plo \
+ ./$(DEPDIR)/gimphelp_pdb.Plo ./$(DEPDIR)/gimpimage.Plo \
+ ./$(DEPDIR)/gimpimage_pdb.Plo \
+ ./$(DEPDIR)/gimpimagecolorprofile.Plo \
+ ./$(DEPDIR)/gimpimagecolorprofile_pdb.Plo \
+ ./$(DEPDIR)/gimpimagecombobox.Plo \
+ ./$(DEPDIR)/gimpimageconvert_pdb.Plo \
+ ./$(DEPDIR)/gimpimagegrid_pdb.Plo \
+ ./$(DEPDIR)/gimpimageguides_pdb.Plo \
+ ./$(DEPDIR)/gimpimagemetadata.Plo \
+ ./$(DEPDIR)/gimpimagesamplepoints_pdb.Plo \
+ ./$(DEPDIR)/gimpimageselect_pdb.Plo \
+ ./$(DEPDIR)/gimpimagetransform_pdb.Plo \
+ ./$(DEPDIR)/gimpimageundo_pdb.Plo ./$(DEPDIR)/gimpitem_pdb.Plo \
+ ./$(DEPDIR)/gimpitemcombobox.Plo \
+ ./$(DEPDIR)/gimpitemtransform_pdb.Plo \
+ ./$(DEPDIR)/gimplayer.Plo ./$(DEPDIR)/gimplayer_pdb.Plo \
+ ./$(DEPDIR)/gimpmenu.Plo ./$(DEPDIR)/gimpmessage_pdb.Plo \
+ ./$(DEPDIR)/gimppainttools_pdb.Plo ./$(DEPDIR)/gimppalette.Plo \
+ ./$(DEPDIR)/gimppalette_pdb.Plo \
+ ./$(DEPDIR)/gimppalettemenu.Plo ./$(DEPDIR)/gimppalettes.Plo \
+ ./$(DEPDIR)/gimppalettes_pdb.Plo \
+ ./$(DEPDIR)/gimppaletteselect.Plo \
+ ./$(DEPDIR)/gimppaletteselect_pdb.Plo \
+ ./$(DEPDIR)/gimppaletteselectbutton.Plo \
+ ./$(DEPDIR)/gimppaths_pdb.Plo ./$(DEPDIR)/gimppattern_pdb.Plo \
+ ./$(DEPDIR)/gimppatternmenu.Plo ./$(DEPDIR)/gimppatterns.Plo \
+ ./$(DEPDIR)/gimppatterns_pdb.Plo \
+ ./$(DEPDIR)/gimppatternselect.Plo \
+ ./$(DEPDIR)/gimppatternselect_pdb.Plo \
+ ./$(DEPDIR)/gimppatternselectbutton.Plo \
+ ./$(DEPDIR)/gimppixbuf.Plo ./$(DEPDIR)/gimppixelfetcher.Plo \
+ ./$(DEPDIR)/gimppixelrgn.Plo ./$(DEPDIR)/gimpplugin.Plo \
+ ./$(DEPDIR)/gimpplugin_pdb.Plo \
+ ./$(DEPDIR)/gimpprocbrowserdialog.Plo \
+ ./$(DEPDIR)/gimpproceduraldb.Plo \
+ ./$(DEPDIR)/gimpproceduraldb_pdb.Plo \
+ ./$(DEPDIR)/gimpprocview.Plo ./$(DEPDIR)/gimpprogress.Plo \
+ ./$(DEPDIR)/gimpprogress_pdb.Plo \
+ ./$(DEPDIR)/gimpprogressbar.Plo \
+ ./$(DEPDIR)/gimpregioniterator.Plo \
+ ./$(DEPDIR)/gimpselectbutton.Plo ./$(DEPDIR)/gimpselection.Plo \
+ ./$(DEPDIR)/gimpselection_pdb.Plo \
+ ./$(DEPDIR)/gimpselectiontools_pdb.Plo \
+ ./$(DEPDIR)/gimptextlayer_pdb.Plo \
+ ./$(DEPDIR)/gimptexttool_pdb.Plo ./$(DEPDIR)/gimptile.Plo \
+ ./$(DEPDIR)/gimptilebackendplugin.Plo \
+ ./$(DEPDIR)/gimptransformtools_pdb.Plo ./$(DEPDIR)/gimpui.Plo \
+ ./$(DEPDIR)/gimpuimarshal.Plo ./$(DEPDIR)/gimpunit_pdb.Plo \
+ ./$(DEPDIR)/gimpunitcache.Plo ./$(DEPDIR)/gimpvectors.Plo \
+ ./$(DEPDIR)/gimpvectors_pdb.Plo \
+ ./$(DEPDIR)/gimpzoompreview.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libgimp_@GIMP_API_VERSION@_la_SOURCES) \
+ $(libgimpui_@GIMP_API_VERSION@_la_SOURCES)
+DIST_SOURCES = $(libgimp_@GIMP_API_VERSION@_la_SOURCES) \
+ $(libgimpui_@GIMP_API_VERSION@_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+DATA = $(noinst_DATA)
+HEADERS = $(gimpinclude_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp COPYING
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpmodule = $(top_builddir)/libgimpmodule/libgimpmodule-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = ./libgimp-$(GIMP_API_VERSION).la
+@PLATFORM_WIN32_TRUE@no_undefined = -no-undefined
+@PLATFORM_OSX_TRUE@xobjective_c = "-xobjective-c"
+@PLATFORM_OSX_TRUE@xobjective_cxx = "-xobjective-c++"
+@PLATFORM_OSX_TRUE@xnone = "-xnone"
+@PLATFORM_OSX_TRUE@framework_cocoa = -framework Cocoa
+@HAVE_EXCHNDL_TRUE@@OS_WIN32_TRUE@exchndl = -lexchndl
+@OS_WIN32_TRUE@gimp_def = gimp.def
+@OS_WIN32_TRUE@gimpui_def = gimpui.def
+@OS_WIN32_TRUE@libgimp_export_symbols = -export-symbols $(srcdir)/gimp.def
+@OS_WIN32_TRUE@libgimpui_export_symbols = -export-symbols $(srcdir)/gimpui.def
+@MS_LIB_AVAILABLE_TRUE@noinst_DATA = gimp-$(GIMP_API_VERSION).lib gimpui-$(GIMP_API_VERSION).lib
+gimpincludedir = $(includedir)/gimp-$(GIMP_API_VERSION)/libgimp
+AM_CPPFLAGS = \
+ -DGIMPDIR=\""$(gimpdir)"\" \
+ -DGIMP_USER_VERSION=\"$(GIMP_USER_VERSION)\" \
+ -DG_LOG_DOMAIN=\"LibGimp\" \
+ -DGIMP_COMPILATION \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(GEXIV2_CFLAGS) \
+ -I$(includedir)
+
+AM_CFLAGS = \
+ $(xobjective_c)
+
+AM_CXXFLAGS = \
+ $(xobjective_cxx)
+
+AM_LDFLAGS = \
+ $(xnone)
+
+lib_LTLIBRARIES = libgimp-@GIMP_API_VERSION@.la libgimpui-@GIMP_API_VERSION@.la
+PDB_WRAPPERS_C = \
+ gimp_pdb.c \
+ gimpbrush_pdb.c \
+ gimpbrushes_pdb.c \
+ gimpbrushselect_pdb.c \
+ gimpbuffer_pdb.c \
+ gimpchannel_pdb.c \
+ gimpcolor_pdb.c \
+ gimpcontext_pdb.c \
+ gimpdebug_pdb.c \
+ gimpdisplay_pdb.c \
+ gimpdrawable_pdb.c \
+ gimpdrawablecolor_pdb.c \
+ gimpdrawableedit_pdb.c \
+ gimpdrawabletransform_pdb.c \
+ gimpdynamics_pdb.c \
+ gimpedit_pdb.c \
+ gimpfileops_pdb.c \
+ gimpfloatingsel_pdb.c \
+ gimpfonts_pdb.c \
+ gimpfontselect_pdb.c \
+ gimpgimprc_pdb.c \
+ gimpgradient_pdb.c \
+ gimpgradients_pdb.c \
+ gimpgradientselect_pdb.c \
+ gimphelp_pdb.c \
+ gimpimage_pdb.c \
+ gimpimagecolorprofile_pdb.c \
+ gimpimageconvert_pdb.c \
+ gimpimagegrid_pdb.c \
+ gimpimageguides_pdb.c \
+ gimpimagesamplepoints_pdb.c \
+ gimpimageselect_pdb.c \
+ gimpimagetransform_pdb.c \
+ gimpimageundo_pdb.c \
+ gimpitem_pdb.c \
+ gimpitemtransform_pdb.c \
+ gimplayer_pdb.c \
+ gimpmessage_pdb.c \
+ gimppainttools_pdb.c \
+ gimppalette_pdb.c \
+ gimppalettes_pdb.c \
+ gimppaletteselect_pdb.c \
+ gimppaths_pdb.c \
+ gimppattern_pdb.c \
+ gimppatterns_pdb.c \
+ gimppatternselect_pdb.c \
+ gimpplugin_pdb.c \
+ gimpproceduraldb_pdb.c \
+ gimpprogress_pdb.c \
+ gimpselection_pdb.c \
+ gimpselectiontools_pdb.c \
+ gimptextlayer_pdb.c \
+ gimptexttool_pdb.c \
+ gimptransformtools_pdb.c \
+ gimpunit_pdb.c \
+ gimpvectors_pdb.c
+
+PDB_WRAPPERS_H = \
+ gimp_pdb_headers.h \
+ gimp_pdb.h \
+ gimpbrush_pdb.h \
+ gimpbrushes_pdb.h \
+ gimpbrushselect_pdb.h \
+ gimpbuffer_pdb.h \
+ gimpchannel_pdb.h \
+ gimpcolor_pdb.h \
+ gimpcontext_pdb.h \
+ gimpdebug_pdb.h \
+ gimpdisplay_pdb.h \
+ gimpdrawable_pdb.h \
+ gimpdrawablecolor_pdb.h \
+ gimpdrawableedit_pdb.h \
+ gimpdrawabletransform_pdb.h \
+ gimpdynamics_pdb.h \
+ gimpedit_pdb.h \
+ gimpfileops_pdb.h \
+ gimpfloatingsel_pdb.h \
+ gimpfonts_pdb.h \
+ gimpfontselect_pdb.h \
+ gimpgimprc_pdb.h \
+ gimpgradient_pdb.h \
+ gimpgradients_pdb.h \
+ gimpgradientselect_pdb.h \
+ gimphelp_pdb.h \
+ gimpimage_pdb.h \
+ gimpimagecolorprofile_pdb.h \
+ gimpimageconvert_pdb.h \
+ gimpimagegrid_pdb.h \
+ gimpimageguides_pdb.h \
+ gimpimagesamplepoints_pdb.h \
+ gimpimageselect_pdb.h \
+ gimpimagetransform_pdb.h \
+ gimpimageundo_pdb.h \
+ gimpitem_pdb.h \
+ gimpitemtransform_pdb.h \
+ gimplayer_pdb.h \
+ gimpmessage_pdb.h \
+ gimppainttools_pdb.h \
+ gimppalette_pdb.h \
+ gimppalettes_pdb.h \
+ gimppaletteselect_pdb.h \
+ gimppaths_pdb.h \
+ gimppattern_pdb.h \
+ gimppatterns_pdb.h \
+ gimppatternselect_pdb.h \
+ gimpplugin_pdb.h \
+ gimpproceduraldb_pdb.h \
+ gimpprogress_pdb.h \
+ gimpselection_pdb.h \
+ gimpselectiontools_pdb.h \
+ gimptextlayer_pdb.h \
+ gimptexttool_pdb.h \
+ gimptransformtools_pdb.h \
+ gimpunit_pdb.h \
+ gimpvectors_pdb.h
+
+libgimp_sources = \
+ gimp.c \
+ gimp.h \
+ gimptypes.h \
+ gimpenums.h \
+ ${PDB_WRAPPERS_C} \
+ ${PDB_WRAPPERS_H} \
+ gimpbrushes.c \
+ gimpbrushes.h \
+ gimpbrushselect.c \
+ gimpbrushselect.h \
+ gimpchannel.c \
+ gimpchannel.h \
+ gimpdrawable.c \
+ gimpdrawable.h \
+ gimpedit.c \
+ gimpedit.h \
+ gimpfontselect.c \
+ gimpfontselect.h \
+ gimpgimprc.c \
+ gimpgimprc.h \
+ gimpgradients.c \
+ gimpgradients.h \
+ gimpgradientselect.c \
+ gimpgradientselect.h \
+ gimpimage.c \
+ gimpimage.h \
+ gimpimagecolorprofile.c \
+ gimpimagecolorprofile.h \
+ gimplayer.c \
+ gimplayer.h \
+ gimppalette.c \
+ gimppalette.h \
+ gimppalettes.c \
+ gimppalettes.h \
+ gimppaletteselect.c \
+ gimppaletteselect.h \
+ gimppatterns.c \
+ gimppatterns.h \
+ gimppatternselect.c \
+ gimppatternselect.h \
+ gimppixbuf.c \
+ gimppixbuf.h \
+ gimppixelfetcher.c \
+ gimppixelfetcher.h \
+ gimppixelrgn.c \
+ gimppixelrgn.h \
+ gimpplugin.c \
+ gimpplugin.h \
+ gimpproceduraldb.c \
+ gimpproceduraldb.h \
+ gimpprogress.c \
+ gimpprogress.h \
+ gimpregioniterator.c \
+ gimpregioniterator.h \
+ gimpselection.c \
+ gimpselection.h \
+ gimptile.c \
+ gimptile.h \
+ gimptilebackendplugin.c \
+ gimptilebackendplugin.h \
+ gimpunitcache.c \
+ gimpunitcache.h \
+ gimpvectors.c \
+ gimpvectors.h \
+ stdplugins-intl.h \
+ libgimp-intl.h
+
+libgimp_built_sources = \
+ gimpenums.c
+
+libgimp_extra_sources = gimpenums.c.tail
+libgimp_@GIMP_API_VERSION@_la_SOURCES = \
+ $(libgimp_built_sources) \
+ $(libgimp_sources)
+
+libgimpui_sources = \
+ gimpui.c \
+ gimpui.h \
+ gimpuitypes.h \
+ gimpaspectpreview.c \
+ gimpaspectpreview.h \
+ gimpbrushmenu.c \
+ gimpbrushmenu.h \
+ gimpbrushselectbutton.c \
+ gimpbrushselectbutton.h \
+ gimpdrawablepreview.c \
+ gimpdrawablepreview.h \
+ gimpexport.c \
+ gimpexport.h \
+ gimpfontmenu.c \
+ gimpfontmenu.h \
+ gimpfontselectbutton.c \
+ gimpfontselectbutton.h \
+ gimpgradientmenu.c \
+ gimpgradientmenu.h \
+ gimpgradientselectbutton.c \
+ gimpgradientselectbutton.h \
+ gimpimagecombobox.c \
+ gimpimagecombobox.h \
+ gimpimagemetadata.c \
+ gimpimagemetadata.h \
+ gimpitemcombobox.c \
+ gimpitemcombobox.h \
+ gimpmenu.c \
+ gimpmenu.h \
+ gimppalettemenu.c \
+ gimppalettemenu.h \
+ gimppaletteselectbutton.c \
+ gimppaletteselectbutton.h \
+ gimppatternmenu.c \
+ gimppatternmenu.h \
+ gimppatternselectbutton.c \
+ gimppatternselectbutton.h \
+ gimpprocbrowserdialog.c \
+ gimpprocbrowserdialog.h \
+ gimpprocview.c \
+ gimpprocview.h \
+ gimpprogressbar.c \
+ gimpprogressbar.h \
+ gimpselectbutton.c \
+ gimpselectbutton.h \
+ gimpzoompreview.c \
+ gimpzoompreview.h
+
+libgimpui_built_sources = \
+ gimpuimarshal.c \
+ gimpuimarshal.h
+
+libgimpui_extra_sources = gimpuimarshal.list
+libgimpui_@GIMP_API_VERSION@_la_SOURCES = \
+ $(libgimpui_built_sources) \
+ $(libgimpui_sources)
+
+gimpinclude_HEADERS = \
+ gimp.h \
+ gimptypes.h \
+ gimpenums.h \
+ ${PDB_WRAPPERS_H} \
+ gimpbrushes.h \
+ gimpbrushselect.h \
+ gimpchannel.h \
+ gimpdrawable.h \
+ gimpedit.h \
+ gimpfontselect.h \
+ gimpgimprc.h \
+ gimpgradients.h \
+ gimpgradientselect.h \
+ gimpimage.h \
+ gimpimagecolorprofile.h \
+ gimplayer.h \
+ gimppalette.h \
+ gimppalettes.h \
+ gimppaletteselect.h \
+ gimppatterns.h \
+ gimppatternselect.h \
+ gimppixelfetcher.h \
+ gimppixelrgn.h \
+ gimpplugin.h \
+ gimpproceduraldb.h \
+ gimpprogress.h \
+ gimpregioniterator.h \
+ gimpselection.h \
+ gimptile.h \
+ gimpvectors.h \
+ \
+ gimpui.h \
+ gimpuitypes.h \
+ gimpaspectpreview.h \
+ gimpbrushmenu.h \
+ gimpbrushselectbutton.h \
+ gimpdrawablepreview.h \
+ gimpexport.h \
+ gimpfontmenu.h \
+ gimpfontselectbutton.h \
+ gimpgradientmenu.h \
+ gimpgradientselectbutton.h \
+ gimpimagecombobox.h \
+ gimpimagemetadata.h \
+ gimpitemcombobox.h \
+ gimpmenu.h \
+ gimppalettemenu.h \
+ gimppaletteselectbutton.h \
+ gimppatternmenu.h \
+ gimppatternselectbutton.h \
+ gimppixbuf.h \
+ gimpprocbrowserdialog.h \
+ gimpprocview.h \
+ gimpprogressbar.h \
+ gimpselectbutton.h \
+ gimpzoompreview.h
+
+libgimp_@GIMP_API_VERSION@_la_LDFLAGS = \
+ -version-info $(LT_VERSION_INFO) \
+ $(no_undefined) \
+ $(libgimp_export_symbols) \
+ $(framework_cocoa) \
+ $(xnone)
+
+EXTRA_libgimp_@GIMP_API_VERSION@_la_DEPENDENCIES = $(gimp_def)
+libgimp_@GIMP_API_VERSION@_la_LIBADD = \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(exchndl) \
+ $(CAIRO_LIBS) \
+ $(GEGL_LIBS) \
+ $(GDK_PIXBUF_LIBS) \
+ $(RT_LIBS)
+
+libgimpui_@GIMP_API_VERSION@_la_LDFLAGS = \
+ -version-info $(LT_VERSION_INFO) \
+ $(no_undefined) \
+ $(libgimpui_export_symbols) \
+ $(framework_cocoa) \
+ $(xnone)
+
+EXTRA_libgimpui_@GIMP_API_VERSION@_la_DEPENDENCIES = $(gimpui_def)
+libgimpui_@GIMP_API_VERSION@_la_LIBADD = \
+ $(libgimp) \
+ $(libgimpwidgets) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(libgimpmodule) \
+ $(GEGL_LIBS) \
+ $(GTK_LIBS) \
+ $(GEXIV2_LIBS) \
+ $(RT_LIBS)
+
+BUILT_SOURCES = \
+ $(libgimp_built_sources) \
+ $(libgimpui_built_sources)
+
+EXTRA_DIST = \
+ COPYING \
+ gimp.def \
+ gimpui.def \
+ $(libgimp_extra_sources) \
+ $(libgimpui_extra_sources)
+
+
+#
+# rules to generate built sources
+#
+# setup autogeneration dependencies
+gen_sources = xgen-cec xgen-umh xgen-umc
+CLEANFILES = $(gen_sources)
+all: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu libgimp/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu libgimp/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libgimp-@GIMP_API_VERSION@.la: $(libgimp_@GIMP_API_VERSION@_la_OBJECTS) $(libgimp_@GIMP_API_VERSION@_la_DEPENDENCIES) $(EXTRA_libgimp_@GIMP_API_VERSION@_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libgimp_@GIMP_API_VERSION@_la_LINK) -rpath $(libdir) $(libgimp_@GIMP_API_VERSION@_la_OBJECTS) $(libgimp_@GIMP_API_VERSION@_la_LIBADD) $(LIBS)
+
+libgimpui-@GIMP_API_VERSION@.la: $(libgimpui_@GIMP_API_VERSION@_la_OBJECTS) $(libgimpui_@GIMP_API_VERSION@_la_DEPENDENCIES) $(EXTRA_libgimpui_@GIMP_API_VERSION@_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libgimpui_@GIMP_API_VERSION@_la_LINK) -rpath $(libdir) $(libgimpui_@GIMP_API_VERSION@_la_OBJECTS) $(libgimpui_@GIMP_API_VERSION@_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpaspectpreview.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbrush_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbrushes.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbrushes_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbrushmenu.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbrushselect.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbrushselect_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbrushselectbutton.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbuffer_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpchannel.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpchannel_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcolor_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcontext_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdebug_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdisplay_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdrawable.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdrawable_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdrawablecolor_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdrawableedit_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdrawablepreview.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdrawabletransform_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdynamics_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpedit.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpedit_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpenums.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpexport.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpfileops_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpfloatingsel_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpfontmenu.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpfonts_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpfontselect.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpfontselect_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpfontselectbutton.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpgimprc.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpgimprc_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpgradient_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpgradientmenu.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpgradients.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpgradients_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpgradientselect.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpgradientselect_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpgradientselectbutton.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimphelp_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimage_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimagecolorprofile.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimagecolorprofile_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimagecombobox.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimageconvert_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimagegrid_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimageguides_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimagemetadata.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimagesamplepoints_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimageselect_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimagetransform_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpimageundo_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpitem_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpitemcombobox.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpitemtransform_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimplayer.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimplayer_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpmenu.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpmessage_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppainttools_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppalette.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppalette_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppalettemenu.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppalettes.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppalettes_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppaletteselect.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppaletteselect_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppaletteselectbutton.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppaths_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppattern_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppatternmenu.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppatterns.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppatterns_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppatternselect.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppatternselect_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppatternselectbutton.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppixbuf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppixelfetcher.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppixelrgn.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpplugin.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpplugin_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpprocbrowserdialog.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpproceduraldb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpproceduraldb_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpprocview.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpprogress.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpprogress_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpprogressbar.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpregioniterator.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpselectbutton.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpselection.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpselection_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpselectiontools_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptextlayer_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptexttool_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptile.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptilebackendplugin.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptransformtools_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpui.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpuimarshal.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpunit_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpunitcache.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpvectors.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpvectors_pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpzoompreview.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-gimpincludeHEADERS: $(gimpinclude_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(gimpinclude_HEADERS)'; test -n "$(gimpincludedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(gimpincludedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(gimpincludedir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(gimpincludedir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(gimpincludedir)" || exit $$?; \
+ done
+
+uninstall-gimpincludeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(gimpinclude_HEADERS)'; test -n "$(gimpincludedir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(gimpincludedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) check-am
+all-am: Makefile $(LTLIBRARIES) $(DATA) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(gimpincludedir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-am
+install-exec: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/gimp.Plo
+ -rm -f ./$(DEPDIR)/gimp_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpaspectpreview.Plo
+ -rm -f ./$(DEPDIR)/gimpbrush_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpbrushes.Plo
+ -rm -f ./$(DEPDIR)/gimpbrushes_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpbrushmenu.Plo
+ -rm -f ./$(DEPDIR)/gimpbrushselect.Plo
+ -rm -f ./$(DEPDIR)/gimpbrushselect_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpbrushselectbutton.Plo
+ -rm -f ./$(DEPDIR)/gimpbuffer_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpchannel.Plo
+ -rm -f ./$(DEPDIR)/gimpchannel_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpcolor_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpcontext_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpdebug_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpdisplay_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpdrawable.Plo
+ -rm -f ./$(DEPDIR)/gimpdrawable_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpdrawablecolor_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpdrawableedit_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpdrawablepreview.Plo
+ -rm -f ./$(DEPDIR)/gimpdrawabletransform_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpdynamics_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpedit.Plo
+ -rm -f ./$(DEPDIR)/gimpedit_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpenums.Plo
+ -rm -f ./$(DEPDIR)/gimpexport.Plo
+ -rm -f ./$(DEPDIR)/gimpfileops_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpfloatingsel_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpfontmenu.Plo
+ -rm -f ./$(DEPDIR)/gimpfonts_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpfontselect.Plo
+ -rm -f ./$(DEPDIR)/gimpfontselect_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpfontselectbutton.Plo
+ -rm -f ./$(DEPDIR)/gimpgimprc.Plo
+ -rm -f ./$(DEPDIR)/gimpgimprc_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpgradient_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpgradientmenu.Plo
+ -rm -f ./$(DEPDIR)/gimpgradients.Plo
+ -rm -f ./$(DEPDIR)/gimpgradients_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpgradientselect.Plo
+ -rm -f ./$(DEPDIR)/gimpgradientselect_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpgradientselectbutton.Plo
+ -rm -f ./$(DEPDIR)/gimphelp_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpimage.Plo
+ -rm -f ./$(DEPDIR)/gimpimage_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpimagecolorprofile.Plo
+ -rm -f ./$(DEPDIR)/gimpimagecolorprofile_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpimagecombobox.Plo
+ -rm -f ./$(DEPDIR)/gimpimageconvert_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpimagegrid_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpimageguides_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpimagemetadata.Plo
+ -rm -f ./$(DEPDIR)/gimpimagesamplepoints_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpimageselect_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpimagetransform_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpimageundo_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpitem_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpitemcombobox.Plo
+ -rm -f ./$(DEPDIR)/gimpitemtransform_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimplayer.Plo
+ -rm -f ./$(DEPDIR)/gimplayer_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpmenu.Plo
+ -rm -f ./$(DEPDIR)/gimpmessage_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimppainttools_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimppalette.Plo
+ -rm -f ./$(DEPDIR)/gimppalette_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimppalettemenu.Plo
+ -rm -f ./$(DEPDIR)/gimppalettes.Plo
+ -rm -f ./$(DEPDIR)/gimppalettes_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimppaletteselect.Plo
+ -rm -f ./$(DEPDIR)/gimppaletteselect_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimppaletteselectbutton.Plo
+ -rm -f ./$(DEPDIR)/gimppaths_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimppattern_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimppatternmenu.Plo
+ -rm -f ./$(DEPDIR)/gimppatterns.Plo
+ -rm -f ./$(DEPDIR)/gimppatterns_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimppatternselect.Plo
+ -rm -f ./$(DEPDIR)/gimppatternselect_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimppatternselectbutton.Plo
+ -rm -f ./$(DEPDIR)/gimppixbuf.Plo
+ -rm -f ./$(DEPDIR)/gimppixelfetcher.Plo
+ -rm -f ./$(DEPDIR)/gimppixelrgn.Plo
+ -rm -f ./$(DEPDIR)/gimpplugin.Plo
+ -rm -f ./$(DEPDIR)/gimpplugin_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpprocbrowserdialog.Plo
+ -rm -f ./$(DEPDIR)/gimpproceduraldb.Plo
+ -rm -f ./$(DEPDIR)/gimpproceduraldb_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpprocview.Plo
+ -rm -f ./$(DEPDIR)/gimpprogress.Plo
+ -rm -f ./$(DEPDIR)/gimpprogress_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpprogressbar.Plo
+ -rm -f ./$(DEPDIR)/gimpregioniterator.Plo
+ -rm -f ./$(DEPDIR)/gimpselectbutton.Plo
+ -rm -f ./$(DEPDIR)/gimpselection.Plo
+ -rm -f ./$(DEPDIR)/gimpselection_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpselectiontools_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimptextlayer_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimptexttool_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimptile.Plo
+ -rm -f ./$(DEPDIR)/gimptilebackendplugin.Plo
+ -rm -f ./$(DEPDIR)/gimptransformtools_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpui.Plo
+ -rm -f ./$(DEPDIR)/gimpuimarshal.Plo
+ -rm -f ./$(DEPDIR)/gimpunit_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpunitcache.Plo
+ -rm -f ./$(DEPDIR)/gimpvectors.Plo
+ -rm -f ./$(DEPDIR)/gimpvectors_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpzoompreview.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-data-local install-gimpincludeHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/gimp.Plo
+ -rm -f ./$(DEPDIR)/gimp_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpaspectpreview.Plo
+ -rm -f ./$(DEPDIR)/gimpbrush_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpbrushes.Plo
+ -rm -f ./$(DEPDIR)/gimpbrushes_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpbrushmenu.Plo
+ -rm -f ./$(DEPDIR)/gimpbrushselect.Plo
+ -rm -f ./$(DEPDIR)/gimpbrushselect_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpbrushselectbutton.Plo
+ -rm -f ./$(DEPDIR)/gimpbuffer_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpchannel.Plo
+ -rm -f ./$(DEPDIR)/gimpchannel_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpcolor_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpcontext_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpdebug_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpdisplay_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpdrawable.Plo
+ -rm -f ./$(DEPDIR)/gimpdrawable_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpdrawablecolor_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpdrawableedit_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpdrawablepreview.Plo
+ -rm -f ./$(DEPDIR)/gimpdrawabletransform_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpdynamics_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpedit.Plo
+ -rm -f ./$(DEPDIR)/gimpedit_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpenums.Plo
+ -rm -f ./$(DEPDIR)/gimpexport.Plo
+ -rm -f ./$(DEPDIR)/gimpfileops_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpfloatingsel_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpfontmenu.Plo
+ -rm -f ./$(DEPDIR)/gimpfonts_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpfontselect.Plo
+ -rm -f ./$(DEPDIR)/gimpfontselect_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpfontselectbutton.Plo
+ -rm -f ./$(DEPDIR)/gimpgimprc.Plo
+ -rm -f ./$(DEPDIR)/gimpgimprc_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpgradient_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpgradientmenu.Plo
+ -rm -f ./$(DEPDIR)/gimpgradients.Plo
+ -rm -f ./$(DEPDIR)/gimpgradients_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpgradientselect.Plo
+ -rm -f ./$(DEPDIR)/gimpgradientselect_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpgradientselectbutton.Plo
+ -rm -f ./$(DEPDIR)/gimphelp_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpimage.Plo
+ -rm -f ./$(DEPDIR)/gimpimage_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpimagecolorprofile.Plo
+ -rm -f ./$(DEPDIR)/gimpimagecolorprofile_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpimagecombobox.Plo
+ -rm -f ./$(DEPDIR)/gimpimageconvert_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpimagegrid_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpimageguides_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpimagemetadata.Plo
+ -rm -f ./$(DEPDIR)/gimpimagesamplepoints_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpimageselect_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpimagetransform_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpimageundo_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpitem_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpitemcombobox.Plo
+ -rm -f ./$(DEPDIR)/gimpitemtransform_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimplayer.Plo
+ -rm -f ./$(DEPDIR)/gimplayer_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpmenu.Plo
+ -rm -f ./$(DEPDIR)/gimpmessage_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimppainttools_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimppalette.Plo
+ -rm -f ./$(DEPDIR)/gimppalette_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimppalettemenu.Plo
+ -rm -f ./$(DEPDIR)/gimppalettes.Plo
+ -rm -f ./$(DEPDIR)/gimppalettes_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimppaletteselect.Plo
+ -rm -f ./$(DEPDIR)/gimppaletteselect_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimppaletteselectbutton.Plo
+ -rm -f ./$(DEPDIR)/gimppaths_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimppattern_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimppatternmenu.Plo
+ -rm -f ./$(DEPDIR)/gimppatterns.Plo
+ -rm -f ./$(DEPDIR)/gimppatterns_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimppatternselect.Plo
+ -rm -f ./$(DEPDIR)/gimppatternselect_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimppatternselectbutton.Plo
+ -rm -f ./$(DEPDIR)/gimppixbuf.Plo
+ -rm -f ./$(DEPDIR)/gimppixelfetcher.Plo
+ -rm -f ./$(DEPDIR)/gimppixelrgn.Plo
+ -rm -f ./$(DEPDIR)/gimpplugin.Plo
+ -rm -f ./$(DEPDIR)/gimpplugin_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpprocbrowserdialog.Plo
+ -rm -f ./$(DEPDIR)/gimpproceduraldb.Plo
+ -rm -f ./$(DEPDIR)/gimpproceduraldb_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpprocview.Plo
+ -rm -f ./$(DEPDIR)/gimpprogress.Plo
+ -rm -f ./$(DEPDIR)/gimpprogress_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpprogressbar.Plo
+ -rm -f ./$(DEPDIR)/gimpregioniterator.Plo
+ -rm -f ./$(DEPDIR)/gimpselectbutton.Plo
+ -rm -f ./$(DEPDIR)/gimpselection.Plo
+ -rm -f ./$(DEPDIR)/gimpselection_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpselectiontools_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimptextlayer_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimptexttool_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimptile.Plo
+ -rm -f ./$(DEPDIR)/gimptilebackendplugin.Plo
+ -rm -f ./$(DEPDIR)/gimptransformtools_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpui.Plo
+ -rm -f ./$(DEPDIR)/gimpuimarshal.Plo
+ -rm -f ./$(DEPDIR)/gimpunit_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpunitcache.Plo
+ -rm -f ./$(DEPDIR)/gimpvectors.Plo
+ -rm -f ./$(DEPDIR)/gimpvectors_pdb.Plo
+ -rm -f ./$(DEPDIR)/gimpzoompreview.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-gimpincludeHEADERS uninstall-libLTLIBRARIES \
+ uninstall-local
+
+.MAKE: all check install install-am install-exec install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libLTLIBRARIES clean-libtool cscopelist-am \
+ ctags ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-data-local install-dvi install-dvi-am \
+ install-exec install-exec-am install-gimpincludeHEADERS \
+ install-html install-html-am install-info install-info-am \
+ install-libLTLIBRARIES install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am \
+ uninstall-gimpincludeHEADERS uninstall-libLTLIBRARIES \
+ uninstall-local
+
+.PRECIOUS: Makefile
+
+
+@OS_WIN32_TRUE@install-libtool-import-lib:
+@OS_WIN32_TRUE@ $(INSTALL) .libs/libgimp-$(GIMP_API_VERSION).dll.a $(DESTDIR)$(libdir)
+@OS_WIN32_TRUE@ $(INSTALL) .libs/libgimpui-$(GIMP_API_VERSION).dll.a $(DESTDIR)$(libdir)
+@OS_WIN32_TRUE@ $(INSTALL) $(srcdir)/gimp.def $(DESTDIR)$(libdir)
+@OS_WIN32_TRUE@ $(INSTALL) $(srcdir)/gimpui.def $(DESTDIR)$(libdir)
+
+@OS_WIN32_TRUE@uninstall-libtool-import-lib:
+@OS_WIN32_TRUE@ -rm $(DESTDIR)$(libdir)/libgimp-$(GIMP_API_VERSION).dll.a
+@OS_WIN32_TRUE@ -rm $(DESTDIR)$(libdir)/libgimpui-$(GIMP_API_VERSION).dll.a
+@OS_WIN32_TRUE@ -rm $(DESTDIR)$(libdir)/gimp.def
+@OS_WIN32_TRUE@ -rm $(DESTDIR)$(libdir)/gimpui.def
+@OS_WIN32_FALSE@install-libtool-import-lib:
+@OS_WIN32_FALSE@uninstall-libtool-import-lib:
+
+@MS_LIB_AVAILABLE_TRUE@install-ms-lib:
+@MS_LIB_AVAILABLE_TRUE@ $(INSTALL) gimp-$(GIMP_API_VERSION).lib $(DESTDIR)$(libdir)
+@MS_LIB_AVAILABLE_TRUE@ $(INSTALL) gimpui-$(GIMP_API_VERSION).lib $(DESTDIR)$(libdir)
+
+@MS_LIB_AVAILABLE_TRUE@uninstall-ms-lib:
+@MS_LIB_AVAILABLE_TRUE@ -rm $(DESTDIR)$(libdir)/gimp-$(GIMP_API_VERSION).lib
+@MS_LIB_AVAILABLE_TRUE@ -rm $(DESTDIR)$(libdir)/gimpui-$(GIMP_API_VERSION).lib
+
+@MS_LIB_AVAILABLE_TRUE@gimp-@GIMP_API_VERSION@.lib: gimp.def
+@MS_LIB_AVAILABLE_TRUE@ lib -name:libgimp-$(GIMP_API_VERSION)-@LT_CURRENT_MINUS_AGE@.dll -def:gimp.def -out:$@
+
+@MS_LIB_AVAILABLE_TRUE@gimpui-@GIMP_API_VERSION@.lib: gimpui.def
+@MS_LIB_AVAILABLE_TRUE@ lib -name:libgimpui-$(GIMP_API_VERSION)-@LT_CURRENT_MINUS_AGE@.dll -def:gimpui.def -out:$@
+
+@MS_LIB_AVAILABLE_FALSE@install-ms-lib:
+@MS_LIB_AVAILABLE_FALSE@uninstall-ms-lib:
+
+install-data-local: install-ms-lib install-libtool-import-lib
+
+uninstall-local: uninstall-ms-lib uninstall-libtool-import-lib
+
+gimpenums.c: $(srcdir)/gimpenums.h $(srcdir)/gimpenums.c.tail $(GIMP_MKENUMS) Makefile.am
+ $(AM_V_GEN) $(GIMP_MKENUMS) \
+ --fhead "#include \"config.h\"\n#include <gio/gio.h>\n#include <gegl.h>\n#undef GIMP_DISABLE_DEPRECATED\n#include \"libgimpbase/gimpbase.h\"\n#include \"libgimpbase/gimpbase-private.h\"\n#include \"libgimpconfig/gimpconfigenums.h\"\n#include \"gimpenums.h\"" \
+ --fprod "\n/* enumerations from \"@filename@\" */" \
+ --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \
+ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \
+ --vtail " { 0, NULL, NULL }\n };\n" \
+ --dhead " static const Gimp@Type@Desc descs[] =\n {" \
+ --dprod " { @VALUENAME@, @valuedesc@, @valuehelp@ },@if ('@valueabbrev@' ne 'NULL')@\n /* Translators: this is an abbreviated version of @valueudesc@.\n Keep it short. */\n { @VALUENAME@, @valueabbrev@, NULL },@endif@" \
+ --dtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n {\n type = g_@type@_register_static (\"@EnumName@\", values);\n gimp_type_set_translation_domain (type, GETTEXT_PACKAGE \"-libgimp\");\n gimp_type_set_translation_context (type, \"@enumnick@\");\n gimp_@type@_set_value_descriptions (type, descs);\n }\n\n return type;\n}\n" \
+ $< > xgen-cec \
+ && cat $(srcdir)/gimpenums.c.tail >> xgen-cec \
+ && cp xgen-cec $(@F) \
+ && rm -f xgen-cec
+
+gimpuimarshal.h: $(srcdir)/gimpuimarshal.list
+ $(AM_V_GEN) $(GLIB_GENMARSHAL) --prefix=_gimpui_marshal $(srcdir)/gimpuimarshal.list --header >> xgen-umh \
+ && (cmp -s xgen-umh $(@F) || cp xgen-umh $(@F)) \
+ && rm -f xgen-umh xgen-umh~
+
+gimpuimarshal.c: gimpuimarshal.h
+ $(AM_V_GEN) $(GLIB_GENMARSHAL) --prefix=_gimpui_marshal $(srcdir)/gimpuimarshal.list --header --body >> xgen-umc \
+ && cp xgen-umc $(@F) \
+ && rm -f xgen-umc xgen-umc~
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libgimp/gimp.c b/libgimp/gimp.c
new file mode 100644
index 0000000..f0734ff
--- /dev/null
+++ b/libgimp/gimp.c
@@ -0,0 +1,2588 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimp.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#define _GNU_SOURCE /* for the sigaction stuff */
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifndef WAIT_ANY
+#define WAIT_ANY -1
+#endif
+
+#include <gtk/gtk.h> /* need GDK_WINDOWING_FOO defines */
+
+#ifndef G_OS_WIN32
+#include "libgimpbase/gimpsignal.h"
+
+#else
+
+#ifdef HAVE_EXCHNDL
+#include <time.h>
+#include <exchndl.h>
+#endif
+
+#include <signal.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#if defined(USE_SYSV_SHM)
+
+#ifdef HAVE_IPC_H
+#include <sys/ipc.h>
+#endif
+
+#ifdef HAVE_SHM_H
+#include <sys/shm.h>
+#endif
+
+#elif defined(USE_POSIX_SHM)
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#endif /* USE_POSIX_SHM */
+
+#ifdef GDK_WINDOWING_QUARTZ
+#include <Cocoa/Cocoa.h>
+#endif
+
+#if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
+# ifdef STRICT
+# undef STRICT
+# endif
+# define STRICT
+
+# ifdef _WIN32_WINNT
+# undef _WIN32_WINNT
+# endif
+# define _WIN32_WINNT 0x0601
+
+# include <windows.h>
+# include <tlhelp32.h>
+# undef RGB
+# define USE_WIN32_SHM 1
+#endif
+
+#include <locale.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpbase/gimpbase-private.h"
+#include "libgimpbase/gimpprotocol.h"
+#include "libgimpbase/gimpwire.h"
+
+#include "gimp.h"
+#include "gimpunitcache.h"
+
+#include "libgimp-intl.h"
+
+
+/**
+ * SECTION: gimp
+ * @title: Gimp
+ * @short_description: Main functions needed for building a GIMP plug-in.
+ * This header includes all other GIMP Library headers.
+ *
+ * Main functions needed for building a GIMP plug-in. This header
+ * includes all other GIMP Library headers.
+ **/
+
+
+#define TILE_MAP_SIZE (_tile_width * _tile_height * 32)
+
+#define ERRMSG_SHM_FAILED "Could not attach to gimp shared memory segment"
+
+/* Maybe this should go in a public header if we add other things to it */
+typedef enum
+{
+ GIMP_DEBUG_PID = 1 << 0,
+ GIMP_DEBUG_FATAL_WARNINGS = 1 << 1,
+ GIMP_DEBUG_QUERY = 1 << 2,
+ GIMP_DEBUG_INIT = 1 << 3,
+ GIMP_DEBUG_RUN = 1 << 4,
+ GIMP_DEBUG_QUIT = 1 << 5,
+
+ GIMP_DEBUG_DEFAULT = (GIMP_DEBUG_RUN | GIMP_DEBUG_FATAL_WARNINGS)
+} GimpDebugFlag;
+
+#define WRITE_BUFFER_SIZE 1024
+
+void gimp_read_expect_msg (GimpWireMessage *msg,
+ gint type);
+
+
+static void gimp_close (void);
+static void gimp_debug_stop (void);
+static void gimp_message_func (const gchar *log_domain,
+ GLogLevelFlags log_level,
+ const gchar *message,
+ gpointer data);
+static void gimp_fatal_func (const gchar *log_domain,
+ GLogLevelFlags flags,
+ const gchar *message,
+ gpointer data);
+#ifdef G_OS_WIN32
+#ifdef HAVE_EXCHNDL
+static LONG WINAPI gimp_plugin_sigfatal_handler (PEXCEPTION_POINTERS pExceptionInfo);
+#endif
+#else
+static void gimp_plugin_sigfatal_handler (gint sig_num);
+#endif
+static gboolean gimp_plugin_io_error_handler (GIOChannel *channel,
+ GIOCondition cond,
+ gpointer data);
+static gboolean gimp_write (GIOChannel *channel,
+ const guint8 *buf,
+ gulong count,
+ gpointer user_data);
+static gboolean gimp_flush (GIOChannel *channel,
+ gpointer user_data);
+static void gimp_loop (void);
+static void gimp_config (GPConfig *config);
+static void gimp_proc_run (GPProcRun *proc_run);
+static void gimp_temp_proc_run (GPProcRun *proc_run);
+static void gimp_process_message (GimpWireMessage *msg);
+static void gimp_single_message (void);
+static gboolean gimp_extension_read (GIOChannel *channel,
+ GIOCondition condition,
+ gpointer data);
+
+static void gimp_set_pdb_error (const GimpParam *return_vals,
+ gint n_return_vals);
+
+
+#if defined G_OS_WIN32 && defined HAVE_EXCHNDL
+static LPTOP_LEVEL_EXCEPTION_FILTER _prevExceptionFilter = NULL;
+static gchar *plug_in_backtrace_path = NULL;
+#endif
+
+static GIOChannel *_readchannel = NULL;
+GIOChannel *_writechannel = NULL;
+
+#ifdef USE_WIN32_SHM
+static HANDLE shm_handle;
+#endif
+
+static gint _tile_width = -1;
+static gint _tile_height = -1;
+static gint _shm_ID = -1;
+static guchar *_shm_addr = NULL;
+static gboolean _show_tool_tips = TRUE;
+static gboolean _show_help_button = TRUE;
+static gboolean _export_profile = FALSE;
+static gboolean _export_exif = FALSE;
+static gboolean _export_xmp = FALSE;
+static gboolean _export_iptc = FALSE;
+static GimpCheckSize _check_size = GIMP_CHECK_SIZE_MEDIUM_CHECKS;
+static GimpCheckType _check_type = GIMP_CHECK_TYPE_GRAY_CHECKS;
+static gint _min_colors = 144;
+static gint _gdisp_ID = -1;
+static gchar *_wm_class = NULL;
+static gchar *_display_name = NULL;
+static gint _monitor_number = 0;
+static guint32 _timestamp = 0;
+static gchar *_icon_theme_dir = NULL;
+static const gchar *progname = NULL;
+
+static gchar write_buffer[WRITE_BUFFER_SIZE];
+static gulong write_buffer_index = 0;
+
+static GimpStackTraceMode stack_trace_mode = GIMP_STACK_TRACE_NEVER;
+
+static GHashTable *temp_proc_ht = NULL;
+
+static guint gimp_debug_flags = 0;
+
+static const GDebugKey gimp_debug_keys[] =
+{
+ { "pid", GIMP_DEBUG_PID },
+ { "fatal-warnings", GIMP_DEBUG_FATAL_WARNINGS },
+ { "fw", GIMP_DEBUG_FATAL_WARNINGS },
+ { "query", GIMP_DEBUG_QUERY },
+ { "init", GIMP_DEBUG_INIT },
+ { "run", GIMP_DEBUG_RUN },
+ { "quit", GIMP_DEBUG_QUIT },
+ { "on", GIMP_DEBUG_DEFAULT }
+};
+
+static GimpPlugInInfo PLUG_IN_INFO;
+
+
+static GimpPDBStatusType pdb_error_status = GIMP_PDB_SUCCESS;
+static gchar *pdb_error_message = NULL;
+
+
+/**
+ * gimp_main:
+ * @info: the PLUG_IN_INFO structure
+ * @argc: the number of arguments
+ * @argv: the arguments
+ *
+ * The main procedure that must be called with the PLUG_IN_INFO structure
+ * and the 'argc' and 'argv' that are passed to "main".
+ *
+ * Returns: an exit status as defined by the C library,
+ * on success EXIT_SUCCESS.
+ **/
+gint
+gimp_main (const GimpPlugInInfo *info,
+ gint argc,
+ gchar *argv[])
+{
+ enum
+ {
+ ARG_PROGNAME,
+ ARG_GIMP,
+ ARG_READ_FD,
+ ARG_WRITE_FD,
+ ARG_MODE,
+ ARG_STACK_TRACE_MODE,
+
+ N_ARGS
+ };
+
+ gchar *basename;
+ const gchar *env_string;
+ gchar *debug_string;
+
+#ifdef G_OS_WIN32
+ gint i, j, k;
+
+ /* Reduce risks */
+ {
+ typedef BOOL (WINAPI *t_SetDllDirectoryA) (LPCSTR lpPathName);
+ t_SetDllDirectoryA p_SetDllDirectoryA;
+
+ p_SetDllDirectoryA = (t_SetDllDirectoryA) GetProcAddress (GetModuleHandle ("kernel32.dll"),
+ "SetDllDirectoryA");
+ if (p_SetDllDirectoryA)
+ (*p_SetDllDirectoryA) ("");
+ }
+
+ /* On Windows, set DLL search path to $INSTALLDIR/bin so that GEGL
+ file operations can find their respective file library DLLs (such
+ as jasper, etc.) without needing to set external PATH. */
+ {
+ const gchar *install_dir;
+ gchar *bin_dir;
+ LPWSTR w_bin_dir;
+ int n;
+
+ w_bin_dir = NULL;
+ install_dir = gimp_installation_directory ();
+ bin_dir = g_build_filename (install_dir, "bin", NULL);
+
+ n = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS,
+ bin_dir, -1, NULL, 0);
+ if (n == 0)
+ goto out;
+
+ w_bin_dir = g_malloc_n (n + 1, sizeof (wchar_t));
+ n = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS,
+ bin_dir, -1,
+ w_bin_dir, (n + 1) * sizeof (wchar_t));
+ if (n == 0)
+ goto out;
+
+ SetDllDirectoryW (w_bin_dir);
+
+ out:
+ if (w_bin_dir)
+ g_free (w_bin_dir);
+ g_free (bin_dir);
+ }
+
+#ifdef HAVE_EXCHNDL
+ /* Use Dr. Mingw (dumps backtrace on crash) if it is available. */
+ {
+ time_t t;
+ gchar *filename;
+ gchar *dir;
+
+ /* This has to be the non-roaming directory (i.e., the local
+ directory) as backtraces correspond to the binaries on this
+ system. */
+ dir = g_build_filename (g_get_user_data_dir (),
+ GIMPDIR, GIMP_USER_VERSION, "CrashLog",
+ NULL);
+ /* Ensure the path exists. */
+ g_mkdir_with_parents (dir, 0700);
+
+ time (&t);
+ filename = g_strdup_printf ("%s-crash-%" G_GUINT64_FORMAT ".txt",
+ g_get_prgname(), t);
+ plug_in_backtrace_path = g_build_filename (dir, filename, NULL);
+ g_free (filename);
+ g_free (dir);
+
+ /* Similar to core crash handling in app/signals.c, the order here
+ * is very important!
+ */
+ if (! _prevExceptionFilter)
+ _prevExceptionFilter = SetUnhandledExceptionFilter (gimp_plugin_sigfatal_handler);
+
+ ExcHndlInit ();
+ ExcHndlSetLogFileNameA (plug_in_backtrace_path);
+ }
+#endif
+
+#ifndef _WIN64
+ {
+ typedef BOOL (WINAPI *t_SetProcessDEPPolicy) (DWORD dwFlags);
+ t_SetProcessDEPPolicy p_SetProcessDEPPolicy;
+
+ p_SetProcessDEPPolicy = GetProcAddress (GetModuleHandle ("kernel32.dll"),
+ "SetProcessDEPPolicy");
+ if (p_SetProcessDEPPolicy)
+ (*p_SetProcessDEPPolicy) (PROCESS_DEP_ENABLE|PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION);
+ }
+#endif
+
+ /* Group all our windows together on the taskbar */
+ {
+ typedef HRESULT (WINAPI *t_SetCurrentProcessExplicitAppUserModelID) (PCWSTR lpPathName);
+ t_SetCurrentProcessExplicitAppUserModelID p_SetCurrentProcessExplicitAppUserModelID;
+
+ p_SetCurrentProcessExplicitAppUserModelID = (t_SetCurrentProcessExplicitAppUserModelID) GetProcAddress (GetModuleHandle ("shell32.dll"),
+ "SetCurrentProcessExplicitAppUserModelID");
+ if (p_SetCurrentProcessExplicitAppUserModelID)
+ (*p_SetCurrentProcessExplicitAppUserModelID) (L"gimp.GimpApplication");
+ }
+
+ /* Check for exe file name with spaces in the path having been split up
+ * by buggy NT C runtime, or something. I don't know why this happens
+ * on NT (including w2k), but not on w95/98.
+ */
+
+ for (i = 1; i < argc; i++)
+ {
+ k = strlen (argv[i]);
+
+ if (k > 10)
+ {
+ if (g_ascii_strcasecmp (argv[i] + k - 4, ".exe") == 0)
+ {
+ /* Found the end of the executable name, most probably.
+ * Splice the parts of the name back together.
+ */
+ GString *s;
+
+ s = g_string_new (argv[ARG_PROGNAME]);
+
+ for (j = 1; j <= i; j++)
+ {
+ s = g_string_append_c (s, ' ');
+ s = g_string_append (s, argv[j]);
+ }
+
+ argv[ARG_PROGNAME] = s->str;
+
+ /* Move rest of argv down */
+ for (j = 1; j < argc - i; j++)
+ argv[j] = argv[j + i];
+
+ argv[argc - i] = NULL;
+ argc -= i;
+
+ break;
+ }
+ }
+ }
+#endif
+
+ g_assert (info != NULL);
+
+ PLUG_IN_INFO = *info;
+
+ if ((argc != N_ARGS) || (strcmp (argv[ARG_GIMP], "-gimp") != 0))
+ {
+ g_printerr ("%s is a GIMP plug-in and must be run by GIMP to be used\n",
+ argv[ARG_PROGNAME]);
+ return 1;
+ }
+
+ gimp_env_init (TRUE);
+
+ progname = argv[ARG_PROGNAME];
+
+ basename = g_path_get_basename (progname);
+
+ g_set_prgname (basename);
+
+ env_string = g_getenv ("GIMP_PLUGIN_DEBUG");
+
+ if (env_string)
+ {
+ const gchar *debug_messages;
+
+ debug_string = strchr (env_string, ',');
+
+ if (debug_string)
+ {
+ gint len = debug_string - env_string;
+
+ if ((strlen (basename) == len) &&
+ (strncmp (basename, env_string, len) == 0))
+ {
+ gimp_debug_flags =
+ g_parse_debug_string (debug_string + 1,
+ gimp_debug_keys,
+ G_N_ELEMENTS (gimp_debug_keys));
+ }
+ }
+ else if (strcmp (env_string, basename) == 0)
+ {
+ gimp_debug_flags = GIMP_DEBUG_DEFAULT;
+ }
+
+ /* make debug output visible by setting G_MESSAGES_DEBUG */
+ debug_messages = g_getenv ("G_MESSAGES_DEBUG");
+
+ if (debug_messages)
+ {
+ gchar *tmp = g_strconcat (debug_messages, ",LibGimp", NULL);
+ g_setenv ("G_MESSAGES_DEBUG", tmp, TRUE);
+ g_free (tmp);
+ }
+ else
+ {
+ g_setenv ("G_MESSAGES_DEBUG", "LibGimp", TRUE);
+ }
+ }
+
+ g_free (basename);
+
+ stack_trace_mode = (GimpStackTraceMode) CLAMP (atoi (argv[ARG_STACK_TRACE_MODE]),
+ GIMP_STACK_TRACE_NEVER,
+ GIMP_STACK_TRACE_ALWAYS);
+
+#ifndef G_OS_WIN32
+ /* No use catching these on Win32, the user won't get any meaningful
+ * stack trace from glib anyhow. It's better to let Windows inform
+ * about the program error, and offer debugging if the plug-in
+ * has been built with MSVC, and the user has MSVC installed.
+ */
+ gimp_signal_private (SIGHUP, gimp_plugin_sigfatal_handler, 0);
+ gimp_signal_private (SIGINT, gimp_plugin_sigfatal_handler, 0);
+ gimp_signal_private (SIGQUIT, gimp_plugin_sigfatal_handler, 0);
+ gimp_signal_private (SIGTERM, gimp_plugin_sigfatal_handler, 0);
+
+ gimp_signal_private (SIGABRT, gimp_plugin_sigfatal_handler, 0);
+ gimp_signal_private (SIGBUS, gimp_plugin_sigfatal_handler, 0);
+ gimp_signal_private (SIGSEGV, gimp_plugin_sigfatal_handler, 0);
+ gimp_signal_private (SIGFPE, gimp_plugin_sigfatal_handler, 0);
+
+ /* Ignore SIGPIPE from crashing Gimp */
+ gimp_signal_private (SIGPIPE, SIG_IGN, 0);
+
+ /* Restart syscalls interrupted by SIGCHLD */
+ gimp_signal_private (SIGCHLD, SIG_DFL, SA_RESTART);
+#endif
+
+#ifdef G_OS_WIN32
+ _readchannel = g_io_channel_win32_new_fd (atoi (argv[ARG_READ_FD]));
+ _writechannel = g_io_channel_win32_new_fd (atoi (argv[ARG_WRITE_FD]));
+#else
+ _readchannel = g_io_channel_unix_new (atoi (argv[ARG_READ_FD]));
+ _writechannel = g_io_channel_unix_new (atoi (argv[ARG_WRITE_FD]));
+#endif
+
+ g_io_channel_set_encoding (_readchannel, NULL, NULL);
+ g_io_channel_set_encoding (_writechannel, NULL, NULL);
+
+ g_io_channel_set_buffered (_readchannel, FALSE);
+ g_io_channel_set_buffered (_writechannel, FALSE);
+
+ g_io_channel_set_close_on_unref (_readchannel, TRUE);
+ g_io_channel_set_close_on_unref (_writechannel, TRUE);
+
+ gp_init ();
+
+ gimp_wire_set_writer (gimp_write);
+ gimp_wire_set_flusher (gimp_flush);
+
+ gimp_enums_init ();
+
+ /* initialize units */
+ {
+ GimpUnitVtable vtable;
+
+ vtable.unit_get_number_of_units = _gimp_unit_cache_get_number_of_units;
+ vtable.unit_get_number_of_built_in_units =
+ _gimp_unit_cache_get_number_of_built_in_units;
+ vtable.unit_new = _gimp_unit_cache_new;
+ vtable.unit_get_deletion_flag = _gimp_unit_cache_get_deletion_flag;
+ vtable.unit_set_deletion_flag = _gimp_unit_cache_set_deletion_flag;
+ vtable.unit_get_factor = _gimp_unit_cache_get_factor;
+ vtable.unit_get_digits = _gimp_unit_cache_get_digits;
+ vtable.unit_get_identifier = _gimp_unit_cache_get_identifier;
+ vtable.unit_get_symbol = _gimp_unit_cache_get_symbol;
+ vtable.unit_get_abbreviation = _gimp_unit_cache_get_abbreviation;
+ vtable.unit_get_singular = _gimp_unit_cache_get_singular;
+ vtable.unit_get_plural = _gimp_unit_cache_get_plural;
+
+ gimp_base_init (&vtable);
+ }
+
+ /* initialize i18n support */
+
+ setlocale (LC_ALL, "");
+
+ bindtextdomain (GETTEXT_PACKAGE"-libgimp", gimp_locale_directory ());
+#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (GETTEXT_PACKAGE"-libgimp", "UTF-8");
+#endif
+
+
+ /* set handler both for the "LibGimp" and "" domains */
+ {
+ const gchar * const log_domains[] =
+ {
+ "LibGimp",
+ "LibGimpBase",
+ "LibGimpColor",
+ "LibGimpConfig",
+ "LibGimpMath",
+ "LibGimpModule",
+ "LibGimpThumb",
+ "LibGimpWidgets"
+ };
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS (log_domains); i++)
+ g_log_set_handler (log_domains[i],
+ G_LOG_LEVEL_MESSAGE,
+ gimp_message_func,
+ NULL);
+
+ g_log_set_handler (NULL,
+ G_LOG_LEVEL_MESSAGE,
+ gimp_message_func,
+ NULL);
+ }
+
+ if (gimp_debug_flags & GIMP_DEBUG_FATAL_WARNINGS)
+ {
+ GLogLevelFlags fatal_mask;
+
+ fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
+ fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL;
+ g_log_set_always_fatal (fatal_mask);
+
+ g_log_set_handler (NULL,
+ G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL |
+ G_LOG_LEVEL_ERROR | G_LOG_FLAG_FATAL,
+ gimp_fatal_func, NULL);
+ }
+ else
+ {
+ g_log_set_handler (NULL,
+ G_LOG_LEVEL_ERROR | G_LOG_FLAG_FATAL,
+ gimp_fatal_func, NULL);
+ }
+
+ if (strcmp (argv[ARG_MODE], "-query") == 0)
+ {
+ if (PLUG_IN_INFO.init_proc)
+ gp_has_init_write (_writechannel, NULL);
+
+ if (gimp_debug_flags & GIMP_DEBUG_QUERY)
+ gimp_debug_stop ();
+
+ if (PLUG_IN_INFO.query_proc)
+ (* PLUG_IN_INFO.query_proc) ();
+
+ gimp_close ();
+
+ return EXIT_SUCCESS;
+ }
+
+ if (strcmp (argv[ARG_MODE], "-init") == 0)
+ {
+ if (gimp_debug_flags & GIMP_DEBUG_INIT)
+ gimp_debug_stop ();
+
+ if (PLUG_IN_INFO.init_proc)
+ (* PLUG_IN_INFO.init_proc) ();
+
+ gimp_close ();
+
+ return EXIT_SUCCESS;
+ }
+
+ if (gimp_debug_flags & GIMP_DEBUG_RUN)
+ gimp_debug_stop ();
+ else if (gimp_debug_flags & GIMP_DEBUG_PID)
+ g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Here I am!");
+
+ temp_proc_ht = g_hash_table_new (g_str_hash, g_str_equal);
+
+ g_io_add_watch (_readchannel,
+ G_IO_ERR | G_IO_HUP,
+ gimp_plugin_io_error_handler,
+ NULL);
+
+ gimp_loop ();
+
+ return EXIT_SUCCESS;
+}
+
+/**
+ * gimp_quit:
+ *
+ * Forcefully causes the GIMP library to exit and close down its
+ * connection to main gimp application. This function never returns.
+ **/
+void
+gimp_quit (void)
+{
+ gimp_close ();
+
+#if defined G_OS_WIN32 && defined HAVE_EXCHNDL
+ if (plug_in_backtrace_path)
+ g_free (plug_in_backtrace_path);
+#endif
+
+ exit (EXIT_SUCCESS);
+}
+
+/**
+ * gimp_install_procedure:
+ * @name: the procedure's name.
+ * @blurb: a short text describing what the procedure does.
+ * @help: the help text for the procedure (usually considerably
+ * longer than @blurb).
+ * @author: the procedure's author(s).
+ * @copyright: the procedure's copyright.
+ * @date: the date the procedure was added.
+ * @menu_label: the label to use for the procedure's menu entry,
+ * or #NULL if the procedure has no menu entry.
+ * @image_types: the drawable types the procedure can handle.
+ * @type: the type of the procedure.
+ * @n_params: the number of parameters the procedure takes.
+ * @n_return_vals: the number of return values the procedure returns.
+ * @params: the procedure's parameters.
+ * @return_vals: the procedure's return values.
+ *
+ * Installs a new procedure with the PDB (procedural database).
+ *
+ * Call this function from within your plug-in's query() function for
+ * each procedure your plug-in implements.
+ *
+ * The @name parameter is mandatory and should be unique, or it will
+ * overwrite an already existing procedure (overwrite procedures only
+ * if you know what you're doing).
+ *
+ * The @blurb, @help, @author, @copyright and @date parameters are
+ * optional but then you shouldn't write procedures without proper
+ * documentation, should you.
+ *
+ * @menu_label defines the label that should be used for the
+ * procedure's menu entry. The position where to register in the menu
+ * hierarchy is chosen using gimp_plugin_menu_register(). This
+ * function also still accepts the old (pre-2.2) way of registering a
+ * menu entry and takes a string in the form
+ * "&lt;Domain&gt;/Path/To/My/Menu"
+ * (e.g. "&lt;Image&gt;/Filters/Render/Useless").
+ *
+ * Note that registering a full (pre-2.2-style) menu path is
+ * deprecated and will cause a failure in GIMP 3.0 and newer.
+ *
+ * It is possible to register a procedure only for keyboard-shortcut
+ * activation by passing a @menu_label to gimp_install_procedure() but
+ * not registering any menu path with gimp_plugin_menu_register(). In
+ * this case, the given @menu_label will only be used as the
+ * procedure's user-visible name in the keyboard shortcut editor.
+ *
+ * @image_types is a comma separated list of image types, or actually
+ * drawable types, that this procedure can deal with. Wildcards are
+ * possible here, so you could say "RGB*" instead of "RGB, RGBA" or
+ * "*" for all image types. If the procedure doesn't need an image to
+ * run, use the empty string.
+ *
+ * @type must be one of %GIMP_PLUGIN or %GIMP_EXTENSION. Note that
+ * temporary procedures must be installed using
+ * gimp_install_temp_proc().
+ *
+ * NOTE: Unlike the GIMP 1.2 API, %GIMP_EXTENSION no longer means
+ * that the procedure's menu prefix is &lt;Toolbox&gt;, but that
+ * it will install temporary procedures. Therefore, the GIMP core
+ * will wait until the %GIMP_EXTENSION procedure has called
+ * gimp_extension_ack(), which means that the procedure has done
+ * its initialization, installed its temporary procedures and is
+ * ready to run.
+ *
+ * <emphasis>Not calling gimp_extension_ack() from a %GIMP_EXTENSION
+ * procedure will cause the GIMP core to lock up.</emphasis>
+ *
+ * Additionally, a %GIMP_EXTENSION procedure with no parameters
+ * (@n_params == 0 and @params == #NULL) is an "automatic" extension
+ * that will be automatically started on each GIMP startup.
+ **/
+void
+gimp_install_procedure (const gchar *name,
+ const gchar *blurb,
+ const gchar *help,
+ const gchar *author,
+ const gchar *copyright,
+ const gchar *date,
+ const gchar *menu_label,
+ const gchar *image_types,
+ GimpPDBProcType type,
+ gint n_params,
+ gint n_return_vals,
+ const GimpParamDef *params,
+ const GimpParamDef *return_vals)
+{
+ GPProcInstall proc_install;
+
+ g_return_if_fail (name != NULL);
+ g_return_if_fail (type != GIMP_INTERNAL);
+ g_return_if_fail ((n_params == 0 && params == NULL) ||
+ (n_params > 0 && params != NULL));
+ g_return_if_fail ((n_return_vals == 0 && return_vals == NULL) ||
+ (n_return_vals > 0 && return_vals != NULL));
+
+ proc_install.name = (gchar *) name;
+ proc_install.blurb = (gchar *) blurb;
+ proc_install.help = (gchar *) help;
+ proc_install.author = (gchar *) author;
+ proc_install.copyright = (gchar *) copyright;
+ proc_install.date = (gchar *) date;
+ proc_install.menu_path = (gchar *) menu_label;
+ proc_install.image_types = (gchar *) image_types;
+ proc_install.type = type;
+ proc_install.nparams = n_params;
+ proc_install.nreturn_vals = n_return_vals;
+ proc_install.params = (GPParamDef *) params;
+ proc_install.return_vals = (GPParamDef *) return_vals;
+
+ if (! gp_proc_install_write (_writechannel, &proc_install, NULL))
+ gimp_quit ();
+}
+
+/**
+ * gimp_install_temp_proc:
+ * @name: the procedure's name.
+ * @blurb: a short text describing what the procedure does.
+ * @help: the help text for the procedure (usually considerably
+ * longer than @blurb).
+ * @author: the procedure's author(s).
+ * @copyright: the procedure's copyright.
+ * @date: the date the procedure was added.
+ * @menu_label: the procedure's menu label, or #NULL if the procedure has
+ * no menu entry.
+ * @image_types: the drawable types the procedure can handle.
+ * @type: the type of the procedure.
+ * @n_params: the number of parameters the procedure takes.
+ * @n_return_vals: the number of return values the procedure returns.
+ * @params: the procedure's parameters.
+ * @return_vals: the procedure's return values.
+ * @run_proc: the function to call for executing the procedure.
+ *
+ * Installs a new temporary procedure with the PDB (procedural database).
+ *
+ * A temporary procedure is a procedure which is only available while
+ * one of your plug-in's "real" procedures is running.
+ *
+ * See gimp_install_procedure() for most details.
+ *
+ * @type <emphasis>must</emphasis> be %GIMP_TEMPORARY or the function
+ * will fail.
+ *
+ * @run_proc is the function which will be called to execute the
+ * procedure.
+ *
+ * NOTE: Normally, plug-in communication is triggered by the plug-in
+ * and the GIMP core only responds to the plug-in's requests. You must
+ * explicitly enable receiving of temporary procedure run requests
+ * using either gimp_extension_enable() or
+ * gimp_extension_process(). See this functions' documentation for
+ * details.
+ **/
+void
+gimp_install_temp_proc (const gchar *name,
+ const gchar *blurb,
+ const gchar *help,
+ const gchar *author,
+ const gchar *copyright,
+ const gchar *date,
+ const gchar *menu_label,
+ const gchar *image_types,
+ GimpPDBProcType type,
+ gint n_params,
+ gint n_return_vals,
+ const GimpParamDef *params,
+ const GimpParamDef *return_vals,
+ GimpRunProc run_proc)
+{
+ g_return_if_fail (name != NULL);
+ g_return_if_fail ((n_params == 0 && params == NULL) ||
+ (n_params > 0 && params != NULL));
+ g_return_if_fail ((n_return_vals == 0 && return_vals == NULL) ||
+ (n_return_vals > 0 && return_vals != NULL));
+ g_return_if_fail (type == GIMP_TEMPORARY);
+ g_return_if_fail (run_proc != NULL);
+
+ gimp_install_procedure (name,
+ blurb, help,
+ author, copyright, date,
+ menu_label,
+ image_types,
+ type,
+ n_params, n_return_vals,
+ params, return_vals);
+
+ /* Insert the temp proc run function into the hash table */
+ g_hash_table_insert (temp_proc_ht, g_strdup (name), (gpointer) run_proc);
+}
+
+/**
+ * gimp_uninstall_temp_proc:
+ * @name: the procedure's name
+ *
+ * Uninstalls a temporary procedure which has previously been
+ * installed using gimp_install_temp_proc().
+ **/
+void
+gimp_uninstall_temp_proc (const gchar *name)
+{
+ GPProcUninstall proc_uninstall;
+ gpointer hash_name;
+ gboolean found;
+
+ g_return_if_fail (name != NULL);
+
+ proc_uninstall.name = (gchar *) name;
+
+ if (! gp_proc_uninstall_write (_writechannel, &proc_uninstall, NULL))
+ gimp_quit ();
+
+ found = g_hash_table_lookup_extended (temp_proc_ht, name, &hash_name, NULL);
+ if (found)
+ {
+ g_hash_table_remove (temp_proc_ht, (gpointer) name);
+ g_free (hash_name);
+ }
+}
+
+/**
+ * gimp_run_procedure:
+ * @name: the name of the procedure to run
+ * @n_return_vals: return location for the number of return values
+ * @...: list of procedure parameters
+ *
+ * This function calls a GIMP procedure and returns its return values.
+ *
+ * The procedure's parameters are given by a va_list in the format
+ * (type, value, type, value) and must be terminated by %GIMP_PDB_END.
+ *
+ * This function converts the va_list of parameters into an array and
+ * passes them to gimp_run_procedure2(). Please look there for further
+ * information.
+ *
+ * Return value: the procedure's return values unless there was an error,
+ * in which case the zero-th return value will be the error status, and
+ * the first return value will be a string detailing the error.
+ **/
+GimpParam *
+gimp_run_procedure (const gchar *name,
+ gint *n_return_vals,
+ ...)
+{
+ GimpPDBArgType param_type;
+ GimpParam *return_vals;
+ GimpParam *params = NULL;
+ gint n_params = 0;
+ va_list args;
+ gint i;
+
+ g_return_val_if_fail (name != NULL, NULL);
+ g_return_val_if_fail (n_return_vals != NULL, NULL);
+
+ va_start (args, n_return_vals);
+ param_type = va_arg (args, GimpPDBArgType);
+
+ while (param_type != GIMP_PDB_END)
+ {
+ switch (param_type)
+ {
+ case GIMP_PDB_INT32:
+ case GIMP_PDB_DISPLAY:
+ case GIMP_PDB_IMAGE:
+ case GIMP_PDB_ITEM:
+ case GIMP_PDB_LAYER:
+ case GIMP_PDB_CHANNEL:
+ case GIMP_PDB_DRAWABLE:
+ case GIMP_PDB_SELECTION:
+ case GIMP_PDB_VECTORS:
+ case GIMP_PDB_STATUS:
+ (void) va_arg (args, gint);
+ break;
+ case GIMP_PDB_INT16:
+ (void) va_arg (args, gint);
+ break;
+ case GIMP_PDB_INT8:
+ (void) va_arg (args, gint);
+ break;
+ case GIMP_PDB_FLOAT:
+ (void) va_arg (args, gdouble);
+ break;
+ case GIMP_PDB_STRING:
+ (void) va_arg (args, gchar *);
+ break;
+ case GIMP_PDB_INT32ARRAY:
+ (void) va_arg (args, gint32 *);
+ break;
+ case GIMP_PDB_INT16ARRAY:
+ (void) va_arg (args, gint16 *);
+ break;
+ case GIMP_PDB_INT8ARRAY:
+ (void) va_arg (args, gint8 *);
+ break;
+ case GIMP_PDB_FLOATARRAY:
+ (void) va_arg (args, gdouble *);
+ break;
+ case GIMP_PDB_STRINGARRAY:
+ (void) va_arg (args, gchar **);
+ break;
+ case GIMP_PDB_COLOR:
+ case GIMP_PDB_COLORARRAY:
+ (void) va_arg (args, GimpRGB *);
+ break;
+ case GIMP_PDB_PARASITE:
+ (void) va_arg (args, GimpParasite *);
+ break;
+ case GIMP_PDB_END:
+ break;
+ }
+
+ n_params++;
+
+ param_type = va_arg (args, GimpPDBArgType);
+ }
+
+ va_end (args);
+
+ params = g_new0 (GimpParam, n_params);
+
+ va_start (args, n_return_vals);
+
+ for (i = 0; i < n_params; i++)
+ {
+ params[i].type = va_arg (args, GimpPDBArgType);
+
+ switch (params[i].type)
+ {
+ case GIMP_PDB_INT32:
+ params[i].data.d_int32 = (gint32) va_arg (args, gint);
+ break;
+ case GIMP_PDB_INT16:
+ params[i].data.d_int16 = (gint16) va_arg (args, gint);
+ break;
+ case GIMP_PDB_INT8:
+ params[i].data.d_int8 = (guint8) va_arg (args, gint);
+ break;
+ case GIMP_PDB_FLOAT:
+ params[i].data.d_float = (gdouble) va_arg (args, gdouble);
+ break;
+ case GIMP_PDB_STRING:
+ params[i].data.d_string = va_arg (args, gchar *);
+ break;
+ case GIMP_PDB_INT32ARRAY:
+ params[i].data.d_int32array = va_arg (args, gint32 *);
+ break;
+ case GIMP_PDB_INT16ARRAY:
+ params[i].data.d_int16array = va_arg (args, gint16 *);
+ break;
+ case GIMP_PDB_INT8ARRAY:
+ params[i].data.d_int8array = va_arg (args, guint8 *);
+ break;
+ case GIMP_PDB_FLOATARRAY:
+ params[i].data.d_floatarray = va_arg (args, gdouble *);
+ break;
+ case GIMP_PDB_STRINGARRAY:
+ params[i].data.d_stringarray = va_arg (args, gchar **);
+ break;
+ case GIMP_PDB_COLOR:
+ params[i].data.d_color = *va_arg (args, GimpRGB *);
+ break;
+ case GIMP_PDB_ITEM:
+ params[i].data.d_item = va_arg (args, gint32);
+ break;
+ case GIMP_PDB_DISPLAY:
+ params[i].data.d_display = va_arg (args, gint32);
+ break;
+ case GIMP_PDB_IMAGE:
+ params[i].data.d_image = va_arg (args, gint32);
+ break;
+ case GIMP_PDB_LAYER:
+ params[i].data.d_layer = va_arg (args, gint32);
+ break;
+ case GIMP_PDB_CHANNEL:
+ params[i].data.d_channel = va_arg (args, gint32);
+ break;
+ case GIMP_PDB_DRAWABLE:
+ params[i].data.d_drawable = va_arg (args, gint32);
+ break;
+ case GIMP_PDB_SELECTION:
+ params[i].data.d_selection = va_arg (args, gint32);
+ break;
+ case GIMP_PDB_COLORARRAY:
+ params[i].data.d_colorarray = va_arg (args, GimpRGB *);
+ break;
+ case GIMP_PDB_VECTORS:
+ params[i].data.d_vectors = va_arg (args, gint32);
+ break;
+ case GIMP_PDB_PARASITE:
+ {
+ GimpParasite *parasite = va_arg (args, GimpParasite *);
+
+ if (parasite == NULL)
+ {
+ params[i].data.d_parasite.name = NULL;
+ params[i].data.d_parasite.data = NULL;
+ }
+ else
+ {
+ params[i].data.d_parasite.name = parasite->name;
+ params[i].data.d_parasite.flags = parasite->flags;
+ params[i].data.d_parasite.size = parasite->size;
+ params[i].data.d_parasite.data = parasite->data;
+ }
+ }
+ break;
+ case GIMP_PDB_STATUS:
+ params[i].data.d_status = va_arg (args, gint32);
+ break;
+ case GIMP_PDB_END:
+ break;
+ }
+ }
+
+ va_end (args);
+
+ return_vals = gimp_run_procedure2 (name, n_return_vals, n_params, params);
+
+ g_free (params);
+
+ return return_vals;
+}
+
+void
+gimp_read_expect_msg (GimpWireMessage *msg,
+ gint type)
+{
+ while (TRUE)
+ {
+ if (! gimp_wire_read_msg (_readchannel, msg, NULL))
+ gimp_quit ();
+
+ if (msg->type == type)
+ return; /* up to the caller to call wire_destroy() */
+
+ if (msg->type == GP_TEMP_PROC_RUN || msg->type == GP_QUIT)
+ {
+ gimp_process_message (msg);
+ }
+ else
+ {
+ g_error ("unexpected message: %d", msg->type);
+ }
+
+ gimp_wire_destroy (msg);
+ }
+}
+
+/**
+ * gimp_run_procedure2:
+ * @name: the name of the procedure to run
+ * @n_return_vals: return location for the number of return values
+ * @n_params: the number of parameters the procedure takes.
+ * @params: the procedure's parameters array.
+ *
+ * This function calls a GIMP procedure and returns its return values.
+ * To get more information about the available procedures and the
+ * parameters they expect, please have a look at the Procedure Browser
+ * as found in the Xtns menu in GIMP's toolbox.
+ *
+ * As soon as you don't need the return values any longer, you should
+ * free them using gimp_destroy_params().
+ *
+ * Return value: the procedure's return values unless there was an error,
+ * in which case the zero-th return value will be the error status, and
+ * if there are two values returned, the other return value will be a
+ * string detailing the error.
+ **/
+GimpParam *
+gimp_run_procedure2 (const gchar *name,
+ gint *n_return_vals,
+ gint n_params,
+ const GimpParam *params)
+{
+ GPProcRun proc_run;
+ GPProcReturn *proc_return;
+ GimpWireMessage msg;
+ GimpParam *return_vals;
+
+ g_return_val_if_fail (name != NULL, NULL);
+ g_return_val_if_fail (n_return_vals != NULL, NULL);
+
+ proc_run.name = (gchar *) name;
+ proc_run.nparams = n_params;
+ proc_run.params = (GPParam *) params;
+
+ if (! gp_proc_run_write (_writechannel, &proc_run, NULL))
+ gimp_quit ();
+
+ gimp_read_expect_msg (&msg, GP_PROC_RETURN);
+
+ proc_return = msg.data;
+
+ *n_return_vals = proc_return->nparams;
+ return_vals = (GimpParam *) proc_return->params;
+
+ proc_return->nparams = 0;
+ proc_return->params = NULL;
+
+ gimp_wire_destroy (&msg);
+
+ gimp_set_pdb_error (return_vals, *n_return_vals);
+
+ return return_vals;
+}
+
+/**
+ * gimp_destroy_params:
+ * @params: the #GimpParam array to destroy
+ * @n_params: the number of elements in the array
+ *
+ * Destroys a #GimpParam array as returned by gimp_run_procedure() or
+ * gimp_run_procedure2().
+ **/
+void
+gimp_destroy_params (GimpParam *params,
+ gint n_params)
+{
+ gp_params_destroy ((GPParam *) params, n_params);
+}
+
+/**
+ * gimp_destroy_paramdefs:
+ * @paramdefs: the #GimpParamDef array to destroy
+ * @n_params: the number of elements in the array
+ *
+ * Destroys a #GimpParamDef array as returned by
+ * gimp_procedural_db_proc_info().
+ **/
+void
+gimp_destroy_paramdefs (GimpParamDef *paramdefs,
+ gint n_params)
+{
+ while (n_params--)
+ {
+ g_free (paramdefs[n_params].name);
+ g_free (paramdefs[n_params].description);
+ }
+
+ g_free (paramdefs);
+}
+
+/**
+ * gimp_get_pdb_error:
+ *
+ * Retrieves the error message from the last procedure call.
+ *
+ * If a procedure call fails, then it might pass an error message with
+ * the return values. Plug-ins that are using the libgimp C wrappers
+ * don't access the procedure return values directly. Thus libgimp
+ * stores the error message and makes it available with this
+ * function. The next procedure call unsets the error message again.
+ *
+ * The returned string is owned by libgimp and must not be freed or
+ * modified.
+ *
+ * Return value: the error message
+ *
+ * Since: 2.6
+ **/
+const gchar *
+gimp_get_pdb_error (void)
+{
+ if (pdb_error_message && strlen (pdb_error_message))
+ return pdb_error_message;
+
+ switch (pdb_error_status)
+ {
+ case GIMP_PDB_SUCCESS:
+ /* procedure executed successfully */
+ return _("success");
+
+ case GIMP_PDB_EXECUTION_ERROR:
+ /* procedure execution failed */
+ return _("execution error");
+
+ case GIMP_PDB_CALLING_ERROR:
+ /* procedure called incorrectly */
+ return _("calling error");
+
+ case GIMP_PDB_CANCEL:
+ /* procedure execution cancelled */
+ return _("cancelled");
+
+ default:
+ return "invalid return status";
+ }
+}
+
+/**
+ * gimp_get_pdb_status:
+ *
+ * Retrieves the status from the last procedure call.
+ *
+ * Return value: the #GimpPDBStatusType.
+ *
+ * Since: 2.10
+ **/
+GimpPDBStatusType
+gimp_get_pdb_status (void)
+{
+ return pdb_error_status;
+}
+
+/**
+ * gimp_tile_width:
+ *
+ * Returns the tile width GIMP is using.
+ *
+ * This is a constant value given at plug-in configuration time.
+ *
+ * Return value: the tile_width
+ **/
+guint
+gimp_tile_width (void)
+{
+ return _tile_width;
+}
+
+/**
+ * gimp_tile_height:
+ *
+ * Returns the tile height GIMP is using.
+ *
+ * This is a constant value given at plug-in configuration time.
+ *
+ * Return value: the tile_height
+ **/
+guint
+gimp_tile_height (void)
+{
+ return _tile_height;
+}
+
+/**
+ * gimp_shm_ID:
+ *
+ * Returns the shared memory ID used for passing tile data between the
+ * GIMP core and the plug-in.
+ *
+ * This is a constant value given at plug-in configuration time.
+ *
+ * Return value: the shared memory ID
+ **/
+gint
+gimp_shm_ID (void)
+{
+ return _shm_ID;
+}
+
+/**
+ * gimp_shm_addr:
+ *
+ * Returns the address of the shared memory segment used for passing
+ * tile data between the GIMP core and the plug-in.
+ *
+ * This is a constant value given at plug-in configuration time.
+ *
+ * Return value: the shared memory address
+ **/
+guchar *
+gimp_shm_addr (void)
+{
+ return _shm_addr;
+}
+
+/**
+ * gimp_gamma:
+ *
+ * Returns the global gamma value GIMP and all its plug-ins should
+ * use.
+ *
+ * This is a constant value.
+ *
+ * NOTE: This function will always return 2.2, the gamma value for
+ * sRGB. If you need the actual gamma value of a drawable, look at its
+ * format.
+ *
+ * See also: gimp_drawable_get_format().
+ *
+ * @Deprecated: 2.8.4
+ *
+ * Return value: the gamma value
+ **/
+gdouble
+gimp_gamma (void)
+{
+ return 2.2;
+}
+
+/**
+ * gimp_install_cmap:
+ *
+ * Returns whether or not the plug-in should allocate an own colormap
+ * when running on an 8 bit display. See also: gimp_min_colors().
+ *
+ * This is a constant value given at plug-in configuration time.
+ *
+ * @Deprecated: 2.8
+ *
+ * Return value: the install_cmap boolean
+ **/
+gboolean
+gimp_install_cmap (void)
+{
+ return FALSE;
+}
+
+/**
+ * gimp_min_colors:
+ *
+ * Returns the minimum number of colors to use when allocating an own
+ * colormap on 8 bit displays.
+ *
+ * This is a constant value given at plug-in configuration time.
+ *
+ * See also: gimp_install_cmap()
+ *
+ * @Deprecated: 2.8
+ *
+ * Return value: the minimum number of colors to allocate
+ **/
+gint
+gimp_min_colors (void)
+{
+ return _min_colors;
+}
+
+/**
+ * gimp_show_tool_tips:
+ *
+ * Returns whether or not the plug-in should show tool-tips.
+ *
+ * This is a constant value given at plug-in configuration time.
+ *
+ * Return value: the show_tool_tips boolean
+ **/
+gboolean
+gimp_show_tool_tips (void)
+{
+ return _show_tool_tips;
+}
+
+/**
+ * gimp_show_help_button:
+ *
+ * Returns whether or not GimpDialog should automatically add a help
+ * button if help_func and help_id are given.
+ *
+ * This is a constant value given at plug-in configuration time.
+ *
+ * Return value: the show_help_button boolean
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_show_help_button (void)
+{
+ return _show_help_button;
+}
+
+/**
+ * gimp_export_color_profile:
+ *
+ * Returns whether file plug-ins should default to exporting the
+ * image's color profile.
+ *
+ * Return value: TRUE if preferences are set to export the color profile.
+ *
+ * Since: 2.10.4
+ **/
+gboolean
+gimp_export_color_profile (void)
+{
+ return _export_profile;
+}
+
+/**
+ * gimp_export_exif:
+ *
+ * Returns whether file plug-ins should default to exporting Exif
+ * metadata, according preferences (original settings is #FALSE since
+ * metadata can contain sensitive information).
+ *
+ * Return value: TRUE if preferences are set to export Exif.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_export_exif (void)
+{
+ return _export_exif;
+}
+
+/**
+ * gimp_export_xmp:
+ *
+ * Returns whether file plug-ins should default to exporting XMP
+ * metadata, according preferences (original settings is #FALSE since
+ * metadata can contain sensitive information).
+ *
+ * Return value: TRUE if preferences are set to export XMP.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_export_xmp (void)
+{
+ return _export_xmp;
+}
+
+/**
+ * gimp_export_iptc:
+ *
+ * Returns whether file plug-ins should default to exporting IPTC
+ * metadata, according preferences (original settings is #FALSE since
+ * metadata can contain sensitive information).
+ *
+ * Return value: TRUE if preferences are set to export IPTC.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_export_iptc (void)
+{
+ return _export_iptc;
+}
+
+/**
+ * gimp_check_size:
+ *
+ * Returns the size of the checkerboard to be used in previews.
+ *
+ * This is a constant value given at plug-in configuration time.
+ *
+ * Return value: the check_size value
+ *
+ * Since: 2.2
+ **/
+GimpCheckSize
+gimp_check_size (void)
+{
+ return _check_size;
+}
+
+/**
+ * gimp_check_type:
+ *
+ * Returns the type of the checkerboard to be used in previews.
+ *
+ * This is a constant value given at plug-in configuration time.
+ *
+ * Return value: the check_type value
+ *
+ * Since: 2.2
+ **/
+GimpCheckType
+gimp_check_type (void)
+{
+ return _check_type;
+}
+
+/**
+ * gimp_default_display:
+ *
+ * Returns the default display ID. This corresponds to the display the
+ * running procedure's menu entry was invoked from.
+ *
+ * This is a constant value given at plug-in configuration time.
+ *
+ * Return value: the default display ID
+ **/
+gint32
+gimp_default_display (void)
+{
+ return _gdisp_ID;
+}
+
+/**
+ * gimp_wm_class:
+ *
+ * Returns the window manager class to be used for plug-in windows.
+ *
+ * This is a constant value given at plug-in configuration time.
+ *
+ * Return value: the window manager class
+ **/
+const gchar *
+gimp_wm_class (void)
+{
+ return _wm_class;
+}
+
+/**
+ * gimp_display_name:
+ *
+ * Returns the display to be used for plug-in windows.
+ *
+ * This is a constant value given at plug-in configuration time.
+ * Will return #NULL if GIMP has been started with no GUI, either
+ * via "--no-interface" flag, or a console build.
+ *
+ * Return value: the display name
+ **/
+const gchar *
+gimp_display_name (void)
+{
+ return _display_name;
+}
+
+/**
+ * gimp_monitor_number:
+ *
+ * Returns the monitor number to be used for plug-in windows.
+ *
+ * This is a constant value given at plug-in configuration time.
+ *
+ * Return value: the monitor number
+ **/
+gint
+gimp_monitor_number (void)
+{
+ return _monitor_number;
+}
+
+/**
+ * gimp_user_time:
+ *
+ * Returns the timestamp of the user interaction that should be set on
+ * the plug-in window. This is handled transparently, plug-in authors
+ * do not have to care about it.
+ *
+ * This is a constant value given at plug-in configuration time.
+ *
+ * Return value: timestamp for plug-in window
+ *
+ * Since: 2.6
+ **/
+guint32
+gimp_user_time (void)
+{
+ return _timestamp;
+}
+
+/**
+ * gimp_get_icon_theme_dir:
+ *
+ * Returns the directory of the current icon theme.
+ *
+ * This is a constant value given at plug-in configuration time.
+ *
+ * Return value: the icon theme directory
+ *
+ * Since: 2.10.4
+ **/
+const gchar *
+gimp_icon_theme_dir (void)
+{
+ return _icon_theme_dir;
+}
+
+/**
+ * gimp_get_progname:
+ *
+ * Returns the plug-in's executable name.
+ *
+ * Return value: the executable name
+ **/
+const gchar *
+gimp_get_progname (void)
+{
+ return progname;
+}
+
+/**
+ * gimp_extension_ack:
+ *
+ * Notify the main GIMP application that the extension has been properly
+ * initialized and is ready to run.
+ *
+ * This function <emphasis>must</emphasis> be called from every
+ * procedure that was registered as #GIMP_EXTENSION.
+ *
+ * Subsequently, extensions can process temporary procedure run
+ * requests using either gimp_extension_enable() or
+ * gimp_extension_process().
+ *
+ * See also: gimp_install_procedure(), gimp_install_temp_proc()
+ **/
+void
+gimp_extension_ack (void)
+{
+ if (! gp_extension_ack_write (_writechannel, NULL))
+ gimp_quit ();
+}
+
+/**
+ * gimp_extension_enable:
+ *
+ * Enables asynchronous processing of messages from the main GIMP
+ * application.
+ *
+ * Normally, a plug-in is not called by GIMP except for the call to
+ * the procedure it implements. All subsequent communication is
+ * triggered by the plug-in and all messages sent from GIMP to the
+ * plug-in are just answers to requests the plug-in made.
+ *
+ * If the plug-in however registered temporary procedures using
+ * gimp_install_temp_proc(), it needs to be able to receive requests
+ * to execute them. Usually this will be done by running
+ * gimp_extension_process() in an endless loop.
+ *
+ * If the plug-in cannot use gimp_extension_process(), i.e. if it has
+ * a GUI and is hanging around in a #GMainLoop, it must call
+ * gimp_extension_enable().
+ *
+ * Note that the plug-in does not need to be a #GIMP_EXTENSION to
+ * register temporary procedures.
+ *
+ * See also: gimp_install_procedure(), gimp_install_temp_proc()
+ **/
+void
+gimp_extension_enable (void)
+{
+ static gboolean callback_added = FALSE;
+
+ if (! callback_added)
+ {
+ g_io_add_watch (_readchannel, G_IO_IN | G_IO_PRI, gimp_extension_read,
+ NULL);
+
+ callback_added = TRUE;
+ }
+}
+
+/**
+ * gimp_extension_process:
+ * @timeout: The timeout (in ms) to use for the select() call.
+ *
+ * Processes one message sent by GIMP and returns.
+ *
+ * Call this function in an endless loop after calling
+ * gimp_extension_ack() to process requests for running temporary
+ * procedures.
+ *
+ * See gimp_extension_enable() for an asynchronous way of doing the
+ * same if running an endless loop is not an option.
+ *
+ * See also: gimp_install_procedure(), gimp_install_temp_proc()
+ **/
+void
+gimp_extension_process (guint timeout)
+{
+#ifndef G_OS_WIN32
+ gint select_val;
+
+ do
+ {
+ fd_set readfds;
+ struct timeval tv;
+ struct timeval *tvp;
+
+ if (timeout)
+ {
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = (timeout % 1000) * 1000;
+ tvp = &tv;
+ }
+ else
+ tvp = NULL;
+
+ FD_ZERO (&readfds);
+ FD_SET (g_io_channel_unix_get_fd (_readchannel), &readfds);
+
+ if ((select_val = select (FD_SETSIZE, &readfds, NULL, NULL, tvp)) > 0)
+ {
+ gimp_single_message ();
+ }
+ else if (select_val == -1 && errno != EINTR)
+ {
+ perror ("gimp_extension_process");
+ gimp_quit ();
+ }
+ }
+ while (select_val == -1 && errno == EINTR);
+#else
+ /* Zero means infinite wait for us, but g_poll and
+ * g_io_channel_win32_poll use -1 to indicate
+ * infinite wait.
+ */
+ GPollFD pollfd;
+
+ if (timeout == 0)
+ timeout = -1;
+
+ g_io_channel_win32_make_pollfd (_readchannel, G_IO_IN, &pollfd);
+
+ if (g_io_channel_win32_poll (&pollfd, 1, timeout) == 1)
+ gimp_single_message ();
+#endif
+}
+
+/**
+ * gimp_parasite_find:
+ * @name: The name of the parasite to find.
+ *
+ * Deprecated: Use gimp_get_parasite() instead.
+ *
+ * Returns: The found parasite.
+ **/
+GimpParasite *
+gimp_parasite_find (const gchar *name)
+{
+ return gimp_get_parasite (name);
+}
+
+/**
+ * gimp_parasite_attach:
+ * @parasite: The parasite to attach.
+ *
+ * Deprecated: Use gimp_attach_parasite() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_parasite_attach (const GimpParasite *parasite)
+{
+ return gimp_attach_parasite (parasite);
+}
+
+/**
+ * gimp_parasite_detach:
+ * @name: The name of the parasite to detach.
+ *
+ * Deprecated: Use gimp_detach_parasite() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_parasite_detach (const gchar *name)
+{
+ return gimp_detach_parasite (name);
+}
+
+/**
+ * gimp_parasite_list:
+ * @num_parasites: The number of attached parasites.
+ * @parasites: The names of currently attached parasites.
+ *
+ * Deprecated: Use gimp_get_parasite_list() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_parasite_list (gint *num_parasites,
+ gchar ***parasites)
+{
+ *parasites = gimp_get_parasite_list (num_parasites);
+
+ return *parasites != NULL;
+}
+
+/**
+ * gimp_attach_new_parasite:
+ * @name: the name of the #GimpParasite to create and attach.
+ * @flags: the flags set on the #GimpParasite.
+ * @size: the size of the parasite data in bytes.
+ * @data: a pointer to the data attached with the #GimpParasite.
+ *
+ * Convenience function that creates a parasite and attaches it
+ * to GIMP.
+ *
+ * Deprecated: Use gimp_attach_parasite() instead.
+ *
+ * Return value: TRUE on successful creation and attachment of
+ * the new parasite.
+ *
+ * See Also: gimp_attach_parasite()
+ */
+gboolean
+gimp_attach_new_parasite (const gchar *name,
+ gint flags,
+ gint size,
+ gconstpointer data)
+{
+ GimpParasite *parasite = gimp_parasite_new (name, flags, size, data);
+ gboolean success;
+
+ success = gimp_attach_parasite (parasite);
+
+ gimp_parasite_free (parasite);
+
+ return success;
+}
+
+
+/* private functions */
+
+static void
+gimp_close (void)
+{
+ if (gimp_debug_flags & GIMP_DEBUG_QUIT)
+ gimp_debug_stop ();
+
+ if (PLUG_IN_INFO.quit_proc)
+ (* PLUG_IN_INFO.quit_proc) ();
+
+#if defined(USE_SYSV_SHM)
+
+ if ((_shm_ID != -1) && _shm_addr)
+ shmdt ((char *) _shm_addr);
+
+#elif defined(USE_WIN32_SHM)
+
+ if (shm_handle)
+ CloseHandle (shm_handle);
+
+#elif defined(USE_POSIX_SHM)
+
+ if ((_shm_ID != -1) && (_shm_addr != MAP_FAILED))
+ munmap (_shm_addr, TILE_MAP_SIZE);
+
+#endif
+
+ gp_quit_write (_writechannel, NULL);
+}
+
+static void
+gimp_debug_stop (void)
+{
+#ifndef G_OS_WIN32
+
+ g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Waiting for debugger...");
+ raise (SIGSTOP);
+
+#else
+
+ HANDLE hThreadSnap = NULL;
+ THREADENTRY32 te32 = { 0 };
+ pid_t opid = getpid ();
+
+ g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
+ "Debugging (restart externally): %ld",
+ (long int) opid);
+
+ hThreadSnap = CreateToolhelp32Snapshot (TH32CS_SNAPTHREAD, 0);
+ if (hThreadSnap == INVALID_HANDLE_VALUE)
+ {
+ g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
+ "error getting threadsnap - debugging impossible");
+ return;
+ }
+
+ te32.dwSize = sizeof (THREADENTRY32);
+
+ if (Thread32First (hThreadSnap, &te32))
+ {
+ do
+ {
+ if (te32.th32OwnerProcessID == opid)
+ {
+ HANDLE hThread = OpenThread (THREAD_SUSPEND_RESUME, FALSE,
+ te32.th32ThreadID);
+ SuspendThread (hThread);
+ CloseHandle (hThread);
+ }
+ }
+ while (Thread32Next (hThreadSnap, &te32));
+ }
+ else
+ {
+ g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "error getting threads");
+ }
+
+ CloseHandle (hThreadSnap);
+
+#endif
+}
+
+static void
+gimp_message_func (const gchar *log_domain,
+ GLogLevelFlags log_level,
+ const gchar *message,
+ gpointer data)
+{
+ gimp_message (message);
+}
+
+static void
+gimp_fatal_func (const gchar *log_domain,
+ GLogLevelFlags flags,
+ const gchar *message,
+ gpointer data)
+{
+ const gchar *level;
+
+ switch (flags & G_LOG_LEVEL_MASK)
+ {
+ case G_LOG_LEVEL_WARNING:
+ level = "WARNING";
+ break;
+ case G_LOG_LEVEL_CRITICAL:
+ level = "CRITICAL";
+ break;
+ case G_LOG_LEVEL_ERROR:
+ level = "ERROR";
+ break;
+ default:
+ level = "FATAL";
+ break;
+ }
+
+ g_printerr ("%s: %s: %s\n",
+ progname, level, message);
+
+#ifndef G_OS_WIN32
+ switch (stack_trace_mode)
+ {
+ case GIMP_STACK_TRACE_NEVER:
+ break;
+
+ case GIMP_STACK_TRACE_QUERY:
+ {
+ sigset_t sigset;
+
+ sigemptyset (&sigset);
+ sigprocmask (SIG_SETMASK, &sigset, NULL);
+ gimp_stack_trace_query (progname);
+ }
+ break;
+
+ case GIMP_STACK_TRACE_ALWAYS:
+ {
+ sigset_t sigset;
+
+ sigemptyset (&sigset);
+ sigprocmask (SIG_SETMASK, &sigset, NULL);
+ gimp_stack_trace_print (progname, stdout, NULL);
+ }
+ break;
+ }
+#endif
+
+ /* Do not end with gimp_quit().
+ * We want the plug-in to continue its normal crash course, otherwise
+ * we won't get the "Plug-in crashed" error in GIMP.
+ */
+ exit (EXIT_FAILURE);
+}
+
+#ifdef G_OS_WIN32
+
+#ifdef HAVE_EXCHNDL
+static LONG WINAPI
+gimp_plugin_sigfatal_handler (PEXCEPTION_POINTERS pExceptionInfo)
+{
+ g_printerr ("%s: fatal error\n", progname);
+
+ SetUnhandledExceptionFilter (_prevExceptionFilter);
+
+ /* For simplicity, do not make a difference between QUERY and ALWAYS
+ * on Windows (at least not for now).
+ */
+ if (stack_trace_mode != GIMP_STACK_TRACE_NEVER &&
+ g_file_test (plug_in_backtrace_path, G_FILE_TEST_IS_REGULAR))
+ {
+ FILE *stream;
+ guchar buffer[256];
+ size_t read_len;
+
+ stream = fopen (plug_in_backtrace_path, "r");
+ do
+ {
+ /* Just read and output directly the file content. */
+ read_len = fread (buffer, 1, sizeof (buffer) - 1, stream);
+ buffer[read_len] = '\0';
+ g_printerr ("%s", buffer);
+ }
+ while (read_len);
+ fclose (stream);
+ }
+
+ if (_prevExceptionFilter && _prevExceptionFilter != gimp_plugin_sigfatal_handler)
+ return _prevExceptionFilter (pExceptionInfo);
+ else
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+#endif
+
+#else
+static void
+gimp_plugin_sigfatal_handler (gint sig_num)
+{
+ switch (sig_num)
+ {
+ case SIGHUP:
+ case SIGINT:
+ case SIGQUIT:
+ case SIGTERM:
+ g_printerr ("%s terminated: %s\n", progname, g_strsignal (sig_num));
+ break;
+
+ case SIGABRT:
+ case SIGBUS:
+ case SIGSEGV:
+ case SIGFPE:
+ case SIGPIPE:
+ default:
+ g_printerr ("%s: fatal error: %s\n", progname, g_strsignal (sig_num));
+ switch (stack_trace_mode)
+ {
+ case GIMP_STACK_TRACE_NEVER:
+ break;
+
+ case GIMP_STACK_TRACE_QUERY:
+ {
+ sigset_t sigset;
+
+ sigemptyset (&sigset);
+ sigprocmask (SIG_SETMASK, &sigset, NULL);
+ gimp_stack_trace_query (progname);
+ }
+ break;
+
+ case GIMP_STACK_TRACE_ALWAYS:
+ {
+ sigset_t sigset;
+
+ sigemptyset (&sigset);
+ sigprocmask (SIG_SETMASK, &sigset, NULL);
+ gimp_stack_trace_print (progname, stdout, NULL);
+ }
+ break;
+ }
+ break;
+ }
+
+ /* Do not end with gimp_quit().
+ * We want the plug-in to continue its normal crash course, otherwise
+ * we won't get the "Plug-in crashed" error in GIMP.
+ */
+ exit (EXIT_FAILURE);
+}
+#endif
+
+static gboolean
+gimp_plugin_io_error_handler (GIOChannel *channel,
+ GIOCondition cond,
+ gpointer data)
+{
+ g_printerr ("%s: fatal error: GIMP crashed\n", progname);
+ gimp_quit ();
+
+ /* never reached */
+ return TRUE;
+}
+
+static gboolean
+gimp_write (GIOChannel *channel,
+ const guint8 *buf,
+ gulong count,
+ gpointer user_data)
+{
+ gulong bytes;
+
+ while (count > 0)
+ {
+ if ((write_buffer_index + count) >= WRITE_BUFFER_SIZE)
+ {
+ bytes = WRITE_BUFFER_SIZE - write_buffer_index;
+ memcpy (&write_buffer[write_buffer_index], buf, bytes);
+ write_buffer_index += bytes;
+ if (! gimp_wire_flush (channel, NULL))
+ return FALSE;
+ }
+ else
+ {
+ bytes = count;
+ memcpy (&write_buffer[write_buffer_index], buf, bytes);
+ write_buffer_index += bytes;
+ }
+
+ buf += bytes;
+ count -= bytes;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gimp_flush (GIOChannel *channel,
+ gpointer user_data)
+{
+ GIOStatus status;
+ GError *error = NULL;
+ gsize count;
+ gsize bytes;
+
+ if (write_buffer_index > 0)
+ {
+ count = 0;
+ while (count != write_buffer_index)
+ {
+ do
+ {
+ bytes = 0;
+ status = g_io_channel_write_chars (channel,
+ &write_buffer[count],
+ (write_buffer_index - count),
+ &bytes,
+ &error);
+ }
+ while (status == G_IO_STATUS_AGAIN);
+
+ if (status != G_IO_STATUS_NORMAL)
+ {
+ if (error)
+ {
+ g_warning ("%s: gimp_flush(): error: %s",
+ g_get_prgname (), error->message);
+ g_error_free (error);
+ }
+ else
+ {
+ g_warning ("%s: gimp_flush(): error", g_get_prgname ());
+ }
+
+ return FALSE;
+ }
+
+ count += bytes;
+ }
+
+ write_buffer_index = 0;
+ }
+
+ return TRUE;
+}
+
+static void
+gimp_loop (void)
+{
+ GimpWireMessage msg;
+
+ while (TRUE)
+ {
+ if (! gimp_wire_read_msg (_readchannel, &msg, NULL))
+ {
+ gimp_close ();
+ return;
+ }
+
+ switch (msg.type)
+ {
+ case GP_QUIT:
+ gimp_wire_destroy (&msg);
+ gimp_close ();
+ return;
+
+ case GP_CONFIG:
+ gimp_config (msg.data);
+ break;
+
+ case GP_TILE_REQ:
+ case GP_TILE_ACK:
+ case GP_TILE_DATA:
+ g_warning ("unexpected tile message received (should not happen)");
+ break;
+
+ case GP_PROC_RUN:
+ gimp_proc_run (msg.data);
+ gimp_wire_destroy (&msg);
+ gimp_close ();
+ return;
+
+ case GP_PROC_RETURN:
+ g_warning ("unexpected proc return message received (should not happen)");
+ break;
+
+ case GP_TEMP_PROC_RUN:
+ g_warning ("unexpected temp proc run message received (should not happen");
+ break;
+
+ case GP_TEMP_PROC_RETURN:
+ g_warning ("unexpected temp proc return message received (should not happen");
+ break;
+
+ case GP_PROC_INSTALL:
+ g_warning ("unexpected proc install message received (should not happen)");
+ break;
+
+ case GP_HAS_INIT:
+ g_warning ("unexpected has init message received (should not happen)");
+ break;
+ }
+
+ gimp_wire_destroy (&msg);
+ }
+}
+
+static void
+gimp_config (GPConfig *config)
+{
+ GFile *file;
+ gchar *path;
+
+ if (config->version < GIMP_PROTOCOL_VERSION)
+ {
+ g_message ("Could not execute plug-in \"%s\"\n(%s)\n"
+ "because GIMP is using an older version of the "
+ "plug-in protocol.",
+ gimp_filename_to_utf8 (g_get_prgname ()),
+ gimp_filename_to_utf8 (progname));
+ gimp_quit ();
+ }
+ else if (config->version > GIMP_PROTOCOL_VERSION)
+ {
+ g_message ("Could not execute plug-in \"%s\"\n(%s)\n"
+ "because it uses an obsolete version of the "
+ "plug-in protocol.",
+ gimp_filename_to_utf8 (g_get_prgname ()),
+ gimp_filename_to_utf8 (progname));
+ gimp_quit ();
+ }
+
+ _tile_width = config->tile_width;
+ _tile_height = config->tile_height;
+ _shm_ID = config->shm_ID;
+ _check_size = config->check_size;
+ _check_type = config->check_type;
+ _show_tool_tips = config->show_tooltips ? TRUE : FALSE;
+ _show_help_button = config->show_help_button ? TRUE : FALSE;
+ _export_profile = config->export_profile ? TRUE : FALSE;
+ _export_exif = config->export_exif ? TRUE : FALSE;
+ _export_xmp = config->export_xmp ? TRUE : FALSE;
+ _export_iptc = config->export_iptc ? TRUE : FALSE;
+ _min_colors = config->min_colors;
+ _gdisp_ID = config->gdisp_ID;
+ _wm_class = g_strdup (config->wm_class);
+ _display_name = g_strdup (config->display_name);
+ _monitor_number = config->monitor_number;
+ _timestamp = config->timestamp;
+ _icon_theme_dir = g_strdup (config->icon_theme_dir);
+
+ if (config->app_name)
+ g_set_application_name (config->app_name);
+
+ gimp_cpu_accel_set_use (config->use_cpu_accel);
+
+ file = gimp_file_new_for_config_path (config->swap_path, NULL);
+ path = g_file_get_path (file);
+
+ g_object_set (gegl_config (),
+ "tile-cache-size", config->tile_cache_size,
+ "swap", path,
+ "swap-compression", config->swap_compression,
+ "threads", (gint) config->num_processors,
+ "use-opencl", config->use_opencl,
+ "application-license", "GPL3",
+ NULL);
+
+ g_free (path);
+ g_object_unref (file);
+
+ if (_shm_ID != -1)
+ {
+#if defined(USE_SYSV_SHM)
+
+ /* Use SysV shared memory mechanisms for transferring tile data. */
+
+ _shm_addr = (guchar *) shmat (_shm_ID, NULL, 0);
+
+ if (_shm_addr == (guchar *) -1)
+ {
+ g_error ("shmat() failed: %s\n" ERRMSG_SHM_FAILED,
+ g_strerror (errno));
+ }
+
+#elif defined(USE_WIN32_SHM)
+
+ /* Use Win32 shared memory mechanisms for transferring tile data. */
+
+ gchar fileMapName[128];
+
+ /* From the id, derive the file map name */
+ g_snprintf (fileMapName, sizeof (fileMapName), "GIMP%d.SHM", _shm_ID);
+
+ /* Open the file mapping */
+ shm_handle = OpenFileMapping (FILE_MAP_ALL_ACCESS,
+ 0, fileMapName);
+ if (shm_handle)
+ {
+ /* Map the shared memory into our address space for use */
+ _shm_addr = (guchar *) MapViewOfFile (shm_handle,
+ FILE_MAP_ALL_ACCESS,
+ 0, 0, TILE_MAP_SIZE);
+
+ /* Verify that we mapped our view */
+ if (!_shm_addr)
+ {
+ g_error ("MapViewOfFile error: %lu... " ERRMSG_SHM_FAILED,
+ GetLastError ());
+ }
+ }
+ else
+ {
+ g_error ("OpenFileMapping error: %lu... " ERRMSG_SHM_FAILED,
+ GetLastError ());
+ }
+
+#elif defined(USE_POSIX_SHM)
+
+ /* Use POSIX shared memory mechanisms for transferring tile data. */
+
+ gchar map_file[32];
+ gint shm_fd;
+
+ /* From the id, derive the file map name */
+ g_snprintf (map_file, sizeof (map_file), "/gimp-shm-%d", _shm_ID);
+
+ /* Open the file mapping */
+ shm_fd = shm_open (map_file, O_RDWR, 0600);
+
+ if (shm_fd != -1)
+ {
+ /* Map the shared memory into our address space for use */
+ _shm_addr = (guchar *) mmap (NULL, TILE_MAP_SIZE,
+ PROT_READ | PROT_WRITE, MAP_SHARED,
+ shm_fd, 0);
+
+ /* Verify that we mapped our view */
+ if (_shm_addr == MAP_FAILED)
+ {
+ g_error ("mmap() failed: %s\n" ERRMSG_SHM_FAILED,
+ g_strerror (errno));
+ }
+
+ close (shm_fd);
+ }
+ else
+ {
+ g_error ("shm_open() failed: %s\n" ERRMSG_SHM_FAILED,
+ g_strerror (errno));
+ }
+
+#endif
+ }
+}
+
+static void
+gimp_proc_run (GPProcRun *proc_run)
+{
+ if (PLUG_IN_INFO.run_proc)
+ {
+ GPProcReturn proc_return;
+ GimpParam *return_vals;
+ gint n_return_vals;
+
+ (* PLUG_IN_INFO.run_proc) (proc_run->name,
+ proc_run->nparams,
+ (GimpParam *) proc_run->params,
+ &n_return_vals, &return_vals);
+
+ proc_return.name = proc_run->name;
+ proc_return.nparams = n_return_vals;
+ proc_return.params = (GPParam *) return_vals;
+
+ if (! gp_proc_return_write (_writechannel, &proc_return, NULL))
+ gimp_quit ();
+ }
+}
+
+static void
+gimp_temp_proc_run (GPProcRun *proc_run)
+{
+ GimpRunProc run_proc = g_hash_table_lookup (temp_proc_ht, proc_run->name);
+
+ if (run_proc)
+ {
+ GPProcReturn proc_return;
+ GimpParam *return_vals;
+ gint n_return_vals;
+
+#ifdef GDK_WINDOWING_QUARTZ
+ if (proc_run->params &&
+ proc_run->params[0].data.d_int32 == GIMP_RUN_INTERACTIVE)
+ {
+ [NSApp activateIgnoringOtherApps: YES];
+ }
+#endif
+
+ (* run_proc) (proc_run->name,
+ proc_run->nparams,
+ (GimpParam *) proc_run->params,
+ &n_return_vals, &return_vals);
+
+ proc_return.name = proc_run->name;
+ proc_return.nparams = n_return_vals;
+ proc_return.params = (GPParam *) return_vals;
+
+ if (! gp_temp_proc_return_write (_writechannel, &proc_return, NULL))
+ gimp_quit ();
+ }
+}
+
+static void
+gimp_process_message (GimpWireMessage *msg)
+{
+ switch (msg->type)
+ {
+ case GP_QUIT:
+ gimp_quit ();
+ break;
+ case GP_CONFIG:
+ gimp_config (msg->data);
+ break;
+ case GP_TILE_REQ:
+ case GP_TILE_ACK:
+ case GP_TILE_DATA:
+ g_warning ("unexpected tile message received (should not happen)");
+ break;
+ case GP_PROC_RUN:
+ g_warning ("unexpected proc run message received (should not happen)");
+ break;
+ case GP_PROC_RETURN:
+ g_warning ("unexpected proc return message received (should not happen)");
+ break;
+ case GP_TEMP_PROC_RUN:
+ gimp_temp_proc_run (msg->data);
+ break;
+ case GP_TEMP_PROC_RETURN:
+ g_warning ("unexpected temp proc return message received (should not happen)");
+ break;
+ case GP_PROC_INSTALL:
+ g_warning ("unexpected proc install message received (should not happen)");
+ break;
+ case GP_HAS_INIT:
+ g_warning ("unexpected has init message received (should not happen)");
+ break;
+ }
+}
+
+static void
+gimp_single_message (void)
+{
+ GimpWireMessage msg;
+
+ /* Run a temp function */
+ if (! gimp_wire_read_msg (_readchannel, &msg, NULL))
+ gimp_quit ();
+
+ gimp_process_message (&msg);
+
+ gimp_wire_destroy (&msg);
+}
+
+static gboolean
+gimp_extension_read (GIOChannel *channel,
+ GIOCondition condition,
+ gpointer data)
+{
+ gimp_single_message ();
+
+ return TRUE;
+}
+
+static void
+gimp_set_pdb_error (const GimpParam *return_vals,
+ gint n_return_vals)
+{
+ if (pdb_error_message)
+ {
+ g_free (pdb_error_message);
+ pdb_error_message = NULL;
+ }
+
+ pdb_error_status = return_vals[0].data.d_status;
+
+ switch (pdb_error_status)
+ {
+ case GIMP_PDB_SUCCESS:
+ case GIMP_PDB_PASS_THROUGH:
+ break;
+
+ case GIMP_PDB_EXECUTION_ERROR:
+ case GIMP_PDB_CALLING_ERROR:
+ case GIMP_PDB_CANCEL:
+ if (n_return_vals > 1 && return_vals[1].type == GIMP_PDB_STRING)
+ {
+ pdb_error_message = g_strdup (return_vals[1].data.d_string);
+ }
+ break;
+ }
+}
diff --git a/libgimp/gimp.def b/libgimp/gimp.def
new file mode 100644
index 0000000..b65b730
--- /dev/null
+++ b/libgimp/gimp.def
@@ -0,0 +1,939 @@
+EXPORTS
+ gimp_airbrush
+ gimp_airbrush_default
+ gimp_attach_new_parasite
+ gimp_attach_parasite
+ gimp_brightness_contrast
+ gimp_brush_application_mode_get_type
+ gimp_brush_delete
+ gimp_brush_duplicate
+ gimp_brush_get_angle
+ gimp_brush_get_aspect_ratio
+ gimp_brush_get_hardness
+ gimp_brush_get_info
+ gimp_brush_get_pixels
+ gimp_brush_get_radius
+ gimp_brush_get_shape
+ gimp_brush_get_spacing
+ gimp_brush_get_spikes
+ gimp_brush_is_editable
+ gimp_brush_is_generated
+ gimp_brush_new
+ gimp_brush_rename
+ gimp_brush_select_destroy
+ gimp_brush_select_new
+ gimp_brush_set_angle
+ gimp_brush_set_aspect_ratio
+ gimp_brush_set_hardness
+ gimp_brush_set_radius
+ gimp_brush_set_shape
+ gimp_brush_set_spacing
+ gimp_brush_set_spikes
+ gimp_brushes_close_popup
+ gimp_brushes_get_brush
+ gimp_brushes_get_brush_data
+ gimp_brushes_get_list
+ gimp_brushes_get_opacity
+ gimp_brushes_get_paint_mode
+ gimp_brushes_get_spacing
+ gimp_brushes_popup
+ gimp_brushes_refresh
+ gimp_brushes_set_brush
+ gimp_brushes_set_opacity
+ gimp_brushes_set_paint_mode
+ gimp_brushes_set_popup
+ gimp_brushes_set_spacing
+ gimp_buffer_delete
+ gimp_buffer_get_bytes
+ gimp_buffer_get_height
+ gimp_buffer_get_image_type
+ gimp_buffer_get_width
+ gimp_buffer_rename
+ gimp_buffers_get_list
+ gimp_by_color_select
+ gimp_by_color_select_full
+ gimp_channel_combine_masks
+ gimp_channel_copy
+ gimp_channel_get_color
+ gimp_channel_get_opacity
+ gimp_channel_get_show_masked
+ gimp_channel_new
+ gimp_channel_new_from_component
+ gimp_channel_set_color
+ gimp_channel_set_opacity
+ gimp_channel_set_show_masked
+ gimp_check_size
+ gimp_check_type
+ gimp_clone
+ gimp_clone_default
+ gimp_color_balance
+ gimp_colorize
+ gimp_context_get_antialias
+ gimp_context_get_background
+ gimp_context_get_brush
+ gimp_context_get_brush_angle
+ gimp_context_get_brush_aspect_ratio
+ gimp_context_get_brush_force
+ gimp_context_get_brush_hardness
+ gimp_context_get_brush_size
+ gimp_context_get_brush_spacing
+ gimp_context_get_diagonal_neighbors
+ gimp_context_get_distance_metric
+ gimp_context_get_dynamics
+ gimp_context_get_feather
+ gimp_context_get_feather_radius
+ gimp_context_get_font
+ gimp_context_get_foreground
+ gimp_context_get_gradient
+ gimp_context_get_gradient_blend_color_space
+ gimp_context_get_gradient_repeat_mode
+ gimp_context_get_gradient_reverse
+ gimp_context_get_ink_angle
+ gimp_context_get_ink_blob_angle
+ gimp_context_get_ink_blob_aspect_ratio
+ gimp_context_get_ink_blob_type
+ gimp_context_get_ink_size
+ gimp_context_get_ink_size_sensitivity
+ gimp_context_get_ink_speed_sensitivity
+ gimp_context_get_ink_tilt_sensitivity
+ gimp_context_get_interpolation
+ gimp_context_get_line_cap_style
+ gimp_context_get_line_dash_offset
+ gimp_context_get_line_dash_pattern
+ gimp_context_get_line_join_style
+ gimp_context_get_line_miter_limit
+ gimp_context_get_line_width
+ gimp_context_get_line_width_unit
+ gimp_context_get_mypaint_brush
+ gimp_context_get_opacity
+ gimp_context_get_paint_method
+ gimp_context_get_paint_mode
+ gimp_context_get_palette
+ gimp_context_get_pattern
+ gimp_context_get_sample_criterion
+ gimp_context_get_sample_merged
+ gimp_context_get_sample_threshold
+ gimp_context_get_sample_threshold_int
+ gimp_context_get_sample_transparent
+ gimp_context_get_stroke_method
+ gimp_context_get_transform_direction
+ gimp_context_get_transform_recursion
+ gimp_context_get_transform_resize
+ gimp_context_list_paint_methods
+ gimp_context_pop
+ gimp_context_push
+ gimp_context_set_antialias
+ gimp_context_set_background
+ gimp_context_set_brush
+ gimp_context_set_brush_angle
+ gimp_context_set_brush_aspect_ratio
+ gimp_context_set_brush_default_hardness
+ gimp_context_set_brush_default_size
+ gimp_context_set_brush_default_spacing
+ gimp_context_set_brush_force
+ gimp_context_set_brush_hardness
+ gimp_context_set_brush_size
+ gimp_context_set_brush_spacing
+ gimp_context_set_default_colors
+ gimp_context_set_defaults
+ gimp_context_set_diagonal_neighbors
+ gimp_context_set_distance_metric
+ gimp_context_set_dynamics
+ gimp_context_set_feather
+ gimp_context_set_feather_radius
+ gimp_context_set_font
+ gimp_context_set_foreground
+ gimp_context_set_gradient
+ gimp_context_set_gradient_blend_color_space
+ gimp_context_set_gradient_fg_bg_hsv_ccw
+ gimp_context_set_gradient_fg_bg_hsv_cw
+ gimp_context_set_gradient_fg_bg_rgb
+ gimp_context_set_gradient_fg_transparent
+ gimp_context_set_gradient_repeat_mode
+ gimp_context_set_gradient_reverse
+ gimp_context_set_ink_angle
+ gimp_context_set_ink_blob_angle
+ gimp_context_set_ink_blob_aspect_ratio
+ gimp_context_set_ink_blob_type
+ gimp_context_set_ink_size
+ gimp_context_set_ink_size_sensitivity
+ gimp_context_set_ink_speed_sensitivity
+ gimp_context_set_ink_tilt_sensitivity
+ gimp_context_set_interpolation
+ gimp_context_set_line_cap_style
+ gimp_context_set_line_dash_offset
+ gimp_context_set_line_dash_pattern
+ gimp_context_set_line_join_style
+ gimp_context_set_line_miter_limit
+ gimp_context_set_line_width
+ gimp_context_set_line_width_unit
+ gimp_context_set_mypaint_brush
+ gimp_context_set_opacity
+ gimp_context_set_paint_method
+ gimp_context_set_paint_mode
+ gimp_context_set_palette
+ gimp_context_set_pattern
+ gimp_context_set_sample_criterion
+ gimp_context_set_sample_merged
+ gimp_context_set_sample_threshold
+ gimp_context_set_sample_threshold_int
+ gimp_context_set_sample_transparent
+ gimp_context_set_stroke_method
+ gimp_context_set_transform_direction
+ gimp_context_set_transform_recursion
+ gimp_context_set_transform_resize
+ gimp_context_swap_colors
+ gimp_convert_dither_type_get_type
+ gimp_convolve
+ gimp_convolve_default
+ gimp_curves_explicit
+ gimp_curves_spline
+ gimp_debug_timer_end
+ gimp_debug_timer_start
+ gimp_default_display
+ gimp_desaturate
+ gimp_desaturate_full
+ gimp_destroy_paramdefs
+ gimp_destroy_params
+ gimp_detach_parasite
+ gimp_display_delete
+ gimp_display_get_window_handle
+ gimp_display_is_valid
+ gimp_display_name
+ gimp_display_new
+ gimp_displays_flush
+ gimp_displays_reconnect
+ gimp_dodgeburn
+ gimp_dodgeburn_default
+ gimp_drawable_attach_new_parasite
+ gimp_drawable_bpp
+ gimp_drawable_brightness_contrast
+ gimp_drawable_color_balance
+ gimp_drawable_colorize_hsl
+ gimp_drawable_curves_explicit
+ gimp_drawable_curves_spline
+ gimp_drawable_delete
+ gimp_drawable_desaturate
+ gimp_drawable_detach
+ gimp_drawable_edit_bucket_fill
+ gimp_drawable_edit_clear
+ gimp_drawable_edit_fill
+ gimp_drawable_edit_gradient_fill
+ gimp_drawable_edit_stroke_item
+ gimp_drawable_edit_stroke_selection
+ gimp_drawable_equalize
+ gimp_drawable_extract_component
+ gimp_drawable_fill
+ gimp_drawable_flush
+ gimp_drawable_foreground_extract
+ gimp_drawable_free_shadow
+ gimp_drawable_get
+ gimp_drawable_get_buffer
+ gimp_drawable_get_color_uchar
+ gimp_drawable_get_format
+ gimp_drawable_get_image
+ gimp_drawable_get_linked
+ gimp_drawable_get_name
+ gimp_drawable_get_pixel
+ gimp_drawable_get_shadow_buffer
+ gimp_drawable_get_sub_thumbnail
+ gimp_drawable_get_sub_thumbnail_data
+ gimp_drawable_get_tattoo
+ gimp_drawable_get_thumbnail
+ gimp_drawable_get_thumbnail_data
+ gimp_drawable_get_thumbnail_format
+ gimp_drawable_get_tile
+ gimp_drawable_get_tile2
+ gimp_drawable_get_visible
+ gimp_drawable_has_alpha
+ gimp_drawable_height
+ gimp_drawable_histogram
+ gimp_drawable_hue_saturation
+ gimp_drawable_invert
+ gimp_drawable_is_channel
+ gimp_drawable_is_gray
+ gimp_drawable_is_indexed
+ gimp_drawable_is_layer
+ gimp_drawable_is_layer_mask
+ gimp_drawable_is_rgb
+ gimp_drawable_is_text_layer
+ gimp_drawable_is_valid
+ gimp_drawable_levels
+ gimp_drawable_levels_stretch
+ gimp_drawable_mask_bounds
+ gimp_drawable_mask_intersect
+ gimp_drawable_merge_shadow
+ gimp_drawable_offset
+ gimp_drawable_offsets
+ gimp_drawable_parasite_attach
+ gimp_drawable_parasite_detach
+ gimp_drawable_parasite_find
+ gimp_drawable_parasite_list
+ gimp_drawable_posterize
+ gimp_drawable_set_image
+ gimp_drawable_set_linked
+ gimp_drawable_set_name
+ gimp_drawable_set_pixel
+ gimp_drawable_set_tattoo
+ gimp_drawable_set_visible
+ gimp_drawable_shadows_highlights
+ gimp_drawable_threshold
+ gimp_drawable_transform_2d
+ gimp_drawable_transform_2d_default
+ gimp_drawable_transform_flip
+ gimp_drawable_transform_flip_default
+ gimp_drawable_transform_flip_simple
+ gimp_drawable_transform_matrix
+ gimp_drawable_transform_matrix_default
+ gimp_drawable_transform_perspective
+ gimp_drawable_transform_perspective_default
+ gimp_drawable_transform_rotate
+ gimp_drawable_transform_rotate_default
+ gimp_drawable_transform_rotate_simple
+ gimp_drawable_transform_scale
+ gimp_drawable_transform_scale_default
+ gimp_drawable_transform_shear
+ gimp_drawable_transform_shear_default
+ gimp_drawable_type
+ gimp_drawable_type_with_alpha
+ gimp_drawable_update
+ gimp_drawable_width
+ gimp_dynamics_get_list
+ gimp_dynamics_refresh
+ gimp_edit_blend
+ gimp_edit_bucket_fill
+ gimp_edit_bucket_fill_full
+ gimp_edit_clear
+ gimp_edit_copy
+ gimp_edit_copy_visible
+ gimp_edit_cut
+ gimp_edit_fill
+ gimp_edit_named_copy
+ gimp_edit_named_copy_visible
+ gimp_edit_named_cut
+ gimp_edit_named_paste
+ gimp_edit_named_paste_as_new
+ gimp_edit_named_paste_as_new_image
+ gimp_edit_paste
+ gimp_edit_paste_as_new
+ gimp_edit_paste_as_new_image
+ gimp_edit_stroke
+ gimp_edit_stroke_vectors
+ gimp_ellipse_select
+ gimp_enums_get_type_names
+ gimp_enums_init
+ gimp_equalize
+ gimp_eraser
+ gimp_eraser_default
+ gimp_export_color_profile
+ gimp_export_exif
+ gimp_export_iptc
+ gimp_export_xmp
+ gimp_extension_ack
+ gimp_extension_enable
+ gimp_extension_process
+ gimp_file_load
+ gimp_file_load_layer
+ gimp_file_load_layers
+ gimp_file_save
+ gimp_file_save_thumbnail
+ gimp_flip
+ gimp_floating_sel_anchor
+ gimp_floating_sel_attach
+ gimp_floating_sel_relax
+ gimp_floating_sel_remove
+ gimp_floating_sel_rigor
+ gimp_floating_sel_to_layer
+ gimp_font_select_destroy
+ gimp_font_select_new
+ gimp_fonts_close_popup
+ gimp_fonts_get_list
+ gimp_fonts_popup
+ gimp_fonts_refresh
+ gimp_fonts_set_popup
+ gimp_free_select
+ gimp_fuzzy_select
+ gimp_fuzzy_select_full
+ gimp_gamma
+ gimp_get_color_configuration
+ gimp_get_default_comment
+ gimp_get_default_unit
+ gimp_get_icon_theme_dir
+ gimp_get_module_load_inhibit
+ gimp_get_monitor_resolution
+ gimp_get_parasite
+ gimp_get_parasite_list
+ gimp_get_path_by_tattoo
+ gimp_get_pdb_error
+ gimp_get_pdb_status
+ gimp_get_progname
+ gimp_get_theme_dir
+ gimp_getpid
+ gimp_gimprc_query
+ gimp_gimprc_set
+ gimp_gradient_delete
+ gimp_gradient_duplicate
+ gimp_gradient_get_custom_samples
+ gimp_gradient_get_number_of_segments
+ gimp_gradient_get_uniform_samples
+ gimp_gradient_is_editable
+ gimp_gradient_new
+ gimp_gradient_rename
+ gimp_gradient_segment_get_blending_function
+ gimp_gradient_segment_get_coloring_type
+ gimp_gradient_segment_get_left_color
+ gimp_gradient_segment_get_left_pos
+ gimp_gradient_segment_get_middle_pos
+ gimp_gradient_segment_get_right_color
+ gimp_gradient_segment_get_right_pos
+ gimp_gradient_segment_range_blend_colors
+ gimp_gradient_segment_range_blend_opacity
+ gimp_gradient_segment_range_delete
+ gimp_gradient_segment_range_flip
+ gimp_gradient_segment_range_move
+ gimp_gradient_segment_range_redistribute_handles
+ gimp_gradient_segment_range_replicate
+ gimp_gradient_segment_range_set_blending_function
+ gimp_gradient_segment_range_set_coloring_type
+ gimp_gradient_segment_range_split_midpoint
+ gimp_gradient_segment_range_split_uniform
+ gimp_gradient_segment_set_left_color
+ gimp_gradient_segment_set_left_pos
+ gimp_gradient_segment_set_middle_pos
+ gimp_gradient_segment_set_right_color
+ gimp_gradient_segment_set_right_pos
+ gimp_gradient_select_destroy
+ gimp_gradient_select_new
+ gimp_gradients_close_popup
+ gimp_gradients_get_gradient
+ gimp_gradients_get_gradient_data
+ gimp_gradients_get_list
+ gimp_gradients_popup
+ gimp_gradients_refresh
+ gimp_gradients_sample_custom
+ gimp_gradients_sample_uniform
+ gimp_gradients_set_gradient
+ gimp_gradients_set_popup
+ gimp_heal
+ gimp_heal_default
+ gimp_help
+ gimp_histogram
+ gimp_histogram_channel_get_type
+ gimp_hue_saturation
+ gimp_icon_theme_dir
+ gimp_image_add_channel
+ gimp_image_add_hguide
+ gimp_image_add_layer
+ gimp_image_add_sample_point
+ gimp_image_add_vectors
+ gimp_image_add_vguide
+ gimp_image_attach_new_parasite
+ gimp_image_attach_parasite
+ gimp_image_base_type
+ gimp_image_clean_all
+ gimp_image_convert_color_profile
+ gimp_image_convert_color_profile_from_file
+ gimp_image_convert_grayscale
+ gimp_image_convert_indexed
+ gimp_image_convert_precision
+ gimp_image_convert_rgb
+ gimp_image_convert_set_dither_matrix
+ gimp_image_crop
+ gimp_image_delete
+ gimp_image_delete_guide
+ gimp_image_delete_sample_point
+ gimp_image_detach_parasite
+ gimp_image_duplicate
+ gimp_image_find_next_guide
+ gimp_image_find_next_sample_point
+ gimp_image_flatten
+ gimp_image_flip
+ gimp_image_floating_sel_attached_to
+ gimp_image_free_shadow
+ gimp_image_freeze_channels
+ gimp_image_freeze_layers
+ gimp_image_freeze_vectors
+ gimp_image_get_active_channel
+ gimp_image_get_active_drawable
+ gimp_image_get_active_layer
+ gimp_image_get_active_vectors
+ gimp_image_get_channel_by_name
+ gimp_image_get_channel_by_tattoo
+ gimp_image_get_channel_position
+ gimp_image_get_channels
+ gimp_image_get_cmap
+ gimp_image_get_color_profile
+ gimp_image_get_colormap
+ gimp_image_get_component_active
+ gimp_image_get_component_visible
+ gimp_image_get_default_new_layer_mode
+ gimp_image_get_effective_color_profile
+ gimp_image_get_exported_uri
+ gimp_image_get_filename
+ gimp_image_get_floating_sel
+ gimp_image_get_guide_orientation
+ gimp_image_get_guide_position
+ gimp_image_get_imported_uri
+ gimp_image_get_item_position
+ gimp_image_get_layer_by_name
+ gimp_image_get_layer_by_tattoo
+ gimp_image_get_layer_position
+ gimp_image_get_layers
+ gimp_image_get_metadata
+ gimp_image_get_name
+ gimp_image_get_parasite
+ gimp_image_get_parasite_list
+ gimp_image_get_precision
+ gimp_image_get_resolution
+ gimp_image_get_sample_point_position
+ gimp_image_get_selection
+ gimp_image_get_tattoo_state
+ gimp_image_get_thumbnail
+ gimp_image_get_thumbnail_data
+ gimp_image_get_unit
+ gimp_image_get_uri
+ gimp_image_get_vectors
+ gimp_image_get_vectors_by_name
+ gimp_image_get_vectors_by_tattoo
+ gimp_image_get_vectors_position
+ gimp_image_get_xcf_uri
+ gimp_image_grid_get_background_color
+ gimp_image_grid_get_foreground_color
+ gimp_image_grid_get_offset
+ gimp_image_grid_get_spacing
+ gimp_image_grid_get_style
+ gimp_image_grid_set_background_color
+ gimp_image_grid_set_foreground_color
+ gimp_image_grid_set_offset
+ gimp_image_grid_set_spacing
+ gimp_image_grid_set_style
+ gimp_image_height
+ gimp_image_insert_channel
+ gimp_image_insert_layer
+ gimp_image_insert_vectors
+ gimp_image_is_dirty
+ gimp_image_is_valid
+ gimp_image_list
+ gimp_image_lower_channel
+ gimp_image_lower_item
+ gimp_image_lower_item_to_bottom
+ gimp_image_lower_layer
+ gimp_image_lower_layer_to_bottom
+ gimp_image_lower_vectors
+ gimp_image_lower_vectors_to_bottom
+ gimp_image_merge_down
+ gimp_image_merge_layer_group
+ gimp_image_merge_visible_layers
+ gimp_image_new
+ gimp_image_new_with_precision
+ gimp_image_parasite_attach
+ gimp_image_parasite_detach
+ gimp_image_parasite_find
+ gimp_image_parasite_list
+ gimp_image_pick_color
+ gimp_image_pick_correlate_layer
+ gimp_image_raise_channel
+ gimp_image_raise_item
+ gimp_image_raise_item_to_top
+ gimp_image_raise_layer
+ gimp_image_raise_layer_to_top
+ gimp_image_raise_vectors
+ gimp_image_raise_vectors_to_top
+ gimp_image_remove_channel
+ gimp_image_remove_layer
+ gimp_image_remove_vectors
+ gimp_image_reorder_item
+ gimp_image_resize
+ gimp_image_resize_to_layers
+ gimp_image_rotate
+ gimp_image_scale
+ gimp_image_scale_full
+ gimp_image_select_color
+ gimp_image_select_contiguous_color
+ gimp_image_select_ellipse
+ gimp_image_select_item
+ gimp_image_select_polygon
+ gimp_image_select_rectangle
+ gimp_image_select_round_rectangle
+ gimp_image_set_active_channel
+ gimp_image_set_active_layer
+ gimp_image_set_active_vectors
+ gimp_image_set_cmap
+ gimp_image_set_color_profile
+ gimp_image_set_color_profile_from_file
+ gimp_image_set_colormap
+ gimp_image_set_component_active
+ gimp_image_set_component_visible
+ gimp_image_set_filename
+ gimp_image_set_metadata
+ gimp_image_set_resolution
+ gimp_image_set_tattoo_state
+ gimp_image_set_unit
+ gimp_image_thaw_channels
+ gimp_image_thaw_layers
+ gimp_image_thaw_vectors
+ gimp_image_undo_disable
+ gimp_image_undo_enable
+ gimp_image_undo_freeze
+ gimp_image_undo_group_end
+ gimp_image_undo_group_start
+ gimp_image_undo_is_enabled
+ gimp_image_undo_thaw
+ gimp_image_unset_active_channel
+ gimp_image_width
+ gimp_install_cmap
+ gimp_install_procedure
+ gimp_install_temp_proc
+ gimp_invert
+ gimp_item_attach_parasite
+ gimp_item_delete
+ gimp_item_detach_parasite
+ gimp_item_get_children
+ gimp_item_get_color_tag
+ gimp_item_get_expanded
+ gimp_item_get_image
+ gimp_item_get_linked
+ gimp_item_get_lock_content
+ gimp_item_get_lock_position
+ gimp_item_get_name
+ gimp_item_get_parasite
+ gimp_item_get_parasite_list
+ gimp_item_get_parent
+ gimp_item_get_tattoo
+ gimp_item_get_visible
+ gimp_item_is_channel
+ gimp_item_is_drawable
+ gimp_item_is_group
+ gimp_item_is_layer
+ gimp_item_is_layer_mask
+ gimp_item_is_selection
+ gimp_item_is_text_layer
+ gimp_item_is_valid
+ gimp_item_is_vectors
+ gimp_item_set_color_tag
+ gimp_item_set_expanded
+ gimp_item_set_linked
+ gimp_item_set_lock_content
+ gimp_item_set_lock_position
+ gimp_item_set_name
+ gimp_item_set_tattoo
+ gimp_item_set_visible
+ gimp_item_transform_2d
+ gimp_item_transform_flip
+ gimp_item_transform_flip_simple
+ gimp_item_transform_matrix
+ gimp_item_transform_perspective
+ gimp_item_transform_rotate
+ gimp_item_transform_rotate_simple
+ gimp_item_transform_scale
+ gimp_item_transform_shear
+ gimp_item_transform_translate
+ gimp_layer_add_alpha
+ gimp_layer_add_mask
+ gimp_layer_color_space_get_type
+ gimp_layer_composite_mode_get_type
+ gimp_layer_copy
+ gimp_layer_create_mask
+ gimp_layer_flatten
+ gimp_layer_from_mask
+ gimp_layer_get_apply_mask
+ gimp_layer_get_blend_space
+ gimp_layer_get_composite_mode
+ gimp_layer_get_composite_space
+ gimp_layer_get_edit_mask
+ gimp_layer_get_lock_alpha
+ gimp_layer_get_mask
+ gimp_layer_get_mode
+ gimp_layer_get_opacity
+ gimp_layer_get_preserve_trans
+ gimp_layer_get_show_mask
+ gimp_layer_group_new
+ gimp_layer_is_floating_sel
+ gimp_layer_mode_get_type
+ gimp_layer_new
+ gimp_layer_new_from_drawable
+ gimp_layer_new_from_pixbuf
+ gimp_layer_new_from_surface
+ gimp_layer_new_from_visible
+ gimp_layer_remove_mask
+ gimp_layer_resize
+ gimp_layer_resize_to_image_size
+ gimp_layer_scale
+ gimp_layer_scale_full
+ gimp_layer_set_apply_mask
+ gimp_layer_set_blend_space
+ gimp_layer_set_composite_mode
+ gimp_layer_set_composite_space
+ gimp_layer_set_edit_mask
+ gimp_layer_set_lock_alpha
+ gimp_layer_set_mode
+ gimp_layer_set_offsets
+ gimp_layer_set_opacity
+ gimp_layer_set_preserve_trans
+ gimp_layer_set_show_mask
+ gimp_layer_translate
+ gimp_levels
+ gimp_levels_auto
+ gimp_levels_stretch
+ gimp_main
+ gimp_message
+ gimp_message_get_handler
+ gimp_message_set_handler
+ gimp_min_colors
+ gimp_monitor_number
+ gimp_paintbrush
+ gimp_paintbrush_default
+ gimp_palette_add_entry
+ gimp_palette_delete
+ gimp_palette_delete_entry
+ gimp_palette_duplicate
+ gimp_palette_entry_get_color
+ gimp_palette_entry_get_name
+ gimp_palette_entry_set_color
+ gimp_palette_entry_set_name
+ gimp_palette_get_background
+ gimp_palette_get_colors
+ gimp_palette_get_columns
+ gimp_palette_get_foreground
+ gimp_palette_get_info
+ gimp_palette_is_editable
+ gimp_palette_new
+ gimp_palette_rename
+ gimp_palette_select_destroy
+ gimp_palette_select_new
+ gimp_palette_set_background
+ gimp_palette_set_columns
+ gimp_palette_set_default_colors
+ gimp_palette_set_foreground
+ gimp_palette_swap_colors
+ gimp_palettes_close_popup
+ gimp_palettes_get_list
+ gimp_palettes_get_palette
+ gimp_palettes_get_palette_entry
+ gimp_palettes_popup
+ gimp_palettes_refresh
+ gimp_palettes_set_palette
+ gimp_palettes_set_popup
+ gimp_parasite_attach
+ gimp_parasite_detach
+ gimp_parasite_find
+ gimp_parasite_list
+ gimp_path_delete
+ gimp_path_get_current
+ gimp_path_get_locked
+ gimp_path_get_point_at_dist
+ gimp_path_get_points
+ gimp_path_get_tattoo
+ gimp_path_import
+ gimp_path_list
+ gimp_path_set_current
+ gimp_path_set_locked
+ gimp_path_set_points
+ gimp_path_set_tattoo
+ gimp_path_stroke_current
+ gimp_path_to_selection
+ gimp_pattern_get_info
+ gimp_pattern_get_pixels
+ gimp_pattern_select_destroy
+ gimp_pattern_select_new
+ gimp_patterns_close_popup
+ gimp_patterns_get_list
+ gimp_patterns_get_pattern
+ gimp_patterns_get_pattern_data
+ gimp_patterns_popup
+ gimp_patterns_refresh
+ gimp_patterns_set_pattern
+ gimp_patterns_set_popup
+ gimp_pencil
+ gimp_perspective
+ gimp_pixel_fetcher_destroy
+ gimp_pixel_fetcher_get_pixel
+ gimp_pixel_fetcher_new
+ gimp_pixel_fetcher_put_pixel
+ gimp_pixel_fetcher_set_bg_color
+ gimp_pixel_fetcher_set_edge_mode
+ gimp_pixel_rgn_get_col
+ gimp_pixel_rgn_get_pixel
+ gimp_pixel_rgn_get_rect
+ gimp_pixel_rgn_get_row
+ gimp_pixel_rgn_init
+ gimp_pixel_rgn_resize
+ gimp_pixel_rgn_set_col
+ gimp_pixel_rgn_set_pixel
+ gimp_pixel_rgn_set_rect
+ gimp_pixel_rgn_set_row
+ gimp_pixel_rgns_process
+ gimp_pixel_rgns_register
+ gimp_pixel_rgns_register2
+ gimp_plug_in_error_quark
+ gimp_plugin_domain_register
+ gimp_plugin_enable_precision
+ gimp_plugin_get_pdb_error_handler
+ gimp_plugin_help_register
+ gimp_plugin_icon_register
+ gimp_plugin_menu_branch_register
+ gimp_plugin_menu_register
+ gimp_plugin_precision_enabled
+ gimp_plugin_set_pdb_error_handler
+ gimp_posterize
+ gimp_procedural_db_dump
+ gimp_procedural_db_get_data
+ gimp_procedural_db_get_data_size
+ gimp_procedural_db_proc_arg
+ gimp_procedural_db_proc_exists
+ gimp_procedural_db_proc_info
+ gimp_procedural_db_proc_val
+ gimp_procedural_db_query
+ gimp_procedural_db_set_data
+ gimp_procedural_db_temp_name
+ gimp_progress_cancel
+ gimp_progress_end
+ gimp_progress_get_window_handle
+ gimp_progress_init
+ gimp_progress_init_printf
+ gimp_progress_install
+ gimp_progress_install_vtable
+ gimp_progress_pulse
+ gimp_progress_set_text
+ gimp_progress_set_text_printf
+ gimp_progress_uninstall
+ gimp_progress_update
+ gimp_quit
+ gimp_read_expect_msg
+ gimp_rect_select
+ gimp_register_file_handler_mime
+ gimp_register_file_handler_priority
+ gimp_register_file_handler_raw
+ gimp_register_file_handler_uri
+ gimp_register_load_handler
+ gimp_register_magic_load_handler
+ gimp_register_save_handler
+ gimp_register_thumbnail_loader
+ gimp_rgn_iterate1
+ gimp_rgn_iterate2
+ gimp_rgn_iterator_dest
+ gimp_rgn_iterator_free
+ gimp_rgn_iterator_new
+ gimp_rgn_iterator_src
+ gimp_rgn_iterator_src_dest
+ gimp_rotate
+ gimp_round_rect_select
+ gimp_run_procedure
+ gimp_run_procedure2
+ gimp_scale
+ gimp_selection_all
+ gimp_selection_border
+ gimp_selection_bounds
+ gimp_selection_clear
+ gimp_selection_combine
+ gimp_selection_feather
+ gimp_selection_float
+ gimp_selection_flood
+ gimp_selection_grow
+ gimp_selection_invert
+ gimp_selection_is_empty
+ gimp_selection_layer_alpha
+ gimp_selection_load
+ gimp_selection_none
+ gimp_selection_save
+ gimp_selection_sharpen
+ gimp_selection_shrink
+ gimp_selection_translate
+ gimp_selection_value
+ gimp_shear
+ gimp_shm_ID
+ gimp_shm_addr
+ gimp_show_help_button
+ gimp_show_tool_tips
+ gimp_smudge
+ gimp_smudge_default
+ gimp_temp_name
+ gimp_text
+ gimp_text_fontname
+ gimp_text_get_extents
+ gimp_text_get_extents_fontname
+ gimp_text_layer_get_antialias
+ gimp_text_layer_get_base_direction
+ gimp_text_layer_get_color
+ gimp_text_layer_get_font
+ gimp_text_layer_get_font_size
+ gimp_text_layer_get_hint_style
+ gimp_text_layer_get_hinting
+ gimp_text_layer_get_indent
+ gimp_text_layer_get_justification
+ gimp_text_layer_get_kerning
+ gimp_text_layer_get_language
+ gimp_text_layer_get_letter_spacing
+ gimp_text_layer_get_line_spacing
+ gimp_text_layer_get_markup
+ gimp_text_layer_get_text
+ gimp_text_layer_new
+ gimp_text_layer_resize
+ gimp_text_layer_set_antialias
+ gimp_text_layer_set_base_direction
+ gimp_text_layer_set_color
+ gimp_text_layer_set_font
+ gimp_text_layer_set_font_size
+ gimp_text_layer_set_hint_style
+ gimp_text_layer_set_hinting
+ gimp_text_layer_set_indent
+ gimp_text_layer_set_justification
+ gimp_text_layer_set_kerning
+ gimp_text_layer_set_language
+ gimp_text_layer_set_letter_spacing
+ gimp_text_layer_set_line_spacing
+ gimp_text_layer_set_text
+ gimp_threshold
+ gimp_tile_cache_ntiles
+ gimp_tile_cache_size
+ gimp_tile_flush
+ gimp_tile_height
+ gimp_tile_ref
+ gimp_tile_ref_zero
+ gimp_tile_unref
+ gimp_tile_width
+ gimp_transform_2d
+ gimp_uninstall_temp_proc
+ gimp_user_time
+ gimp_vectors_bezier_stroke_conicto
+ gimp_vectors_bezier_stroke_cubicto
+ gimp_vectors_bezier_stroke_lineto
+ gimp_vectors_bezier_stroke_new_ellipse
+ gimp_vectors_bezier_stroke_new_moveto
+ gimp_vectors_copy
+ gimp_vectors_export_to_file
+ gimp_vectors_export_to_string
+ gimp_vectors_get_image
+ gimp_vectors_get_linked
+ gimp_vectors_get_name
+ gimp_vectors_get_strokes
+ gimp_vectors_get_tattoo
+ gimp_vectors_get_visible
+ gimp_vectors_import_from_file
+ gimp_vectors_import_from_string
+ gimp_vectors_is_valid
+ gimp_vectors_new
+ gimp_vectors_new_from_text_layer
+ gimp_vectors_parasite_attach
+ gimp_vectors_parasite_detach
+ gimp_vectors_parasite_find
+ gimp_vectors_parasite_list
+ gimp_vectors_remove_stroke
+ gimp_vectors_set_linked
+ gimp_vectors_set_name
+ gimp_vectors_set_tattoo
+ gimp_vectors_set_visible
+ gimp_vectors_stroke_close
+ gimp_vectors_stroke_flip
+ gimp_vectors_stroke_flip_free
+ gimp_vectors_stroke_get_length
+ gimp_vectors_stroke_get_point_at_dist
+ gimp_vectors_stroke_get_points
+ gimp_vectors_stroke_interpolate
+ gimp_vectors_stroke_new_from_points
+ gimp_vectors_stroke_rotate
+ gimp_vectors_stroke_scale
+ gimp_vectors_stroke_translate
+ gimp_vectors_to_selection
+ gimp_version
+ gimp_wm_class
diff --git a/libgimp/gimp.h b/libgimp/gimp.h
new file mode 100644
index 0000000..992974e
--- /dev/null
+++ b/libgimp/gimp.h
@@ -0,0 +1,372 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimp.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_H__
+#define __GIMP_H__
+
+#include <cairo.h>
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include <libgimpbase/gimpbase.h>
+#include <libgimpcolor/gimpcolor.h>
+#include <libgimpconfig/gimpconfig.h>
+#include <libgimpmath/gimpmath.h>
+
+#define __GIMP_H_INSIDE__
+
+#include <libgimp/gimpenums.h>
+#include <libgimp/gimptypes.h>
+
+#include <libgimp/gimpbrushes.h>
+#include <libgimp/gimpbrushselect.h>
+#include <libgimp/gimpchannel.h>
+#include <libgimp/gimpdrawable.h>
+#include <libgimp/gimpedit.h>
+#include <libgimp/gimpfontselect.h>
+#include <libgimp/gimpgimprc.h>
+#include <libgimp/gimpgradients.h>
+#include <libgimp/gimpgradientselect.h>
+#include <libgimp/gimpimage.h>
+#include <libgimp/gimpimagecolorprofile.h>
+#include <libgimp/gimplayer.h>
+#include <libgimp/gimppalette.h>
+#include <libgimp/gimppalettes.h>
+#include <libgimp/gimppaletteselect.h>
+#include <libgimp/gimppatterns.h>
+#include <libgimp/gimppatternselect.h>
+#include <libgimp/gimppixbuf.h>
+#include <libgimp/gimppixelfetcher.h>
+#include <libgimp/gimppixelrgn.h>
+#include <libgimp/gimpplugin.h>
+#include <libgimp/gimpproceduraldb.h>
+#include <libgimp/gimpprogress.h>
+#include <libgimp/gimpregioniterator.h>
+#include <libgimp/gimpselection.h>
+#include <libgimp/gimptile.h>
+#include <libgimp/gimpvectors.h>
+
+#include <libgimp/gimp_pdb_headers.h>
+
+#undef __GIMP_H_INSIDE__
+
+#ifdef G_OS_WIN32
+#include <stdlib.h> /* For __argc and __argv */
+#endif
+
+G_BEGIN_DECLS
+
+
+#define gimp_get_data gimp_procedural_db_get_data
+#define gimp_get_data_size gimp_procedural_db_get_data_size
+#define gimp_set_data gimp_procedural_db_set_data
+
+
+typedef void (* GimpInitProc) (void);
+typedef void (* GimpQuitProc) (void);
+typedef void (* GimpQueryProc) (void);
+typedef void (* GimpRunProc) (const gchar *name,
+ gint n_params,
+ const GimpParam *param,
+ gint *n_return_vals,
+ GimpParam **return_vals);
+
+
+/**
+ * GimpPlugInInfo:
+ * @init_proc: called when the gimp application initially starts up
+ * @quit_proc: called when the gimp application exits
+ * @query_proc: called by gimp so that the plug-in can inform the
+ * gimp of what it does. (ie. installing a procedure database
+ * procedure).
+ * @run_proc: called to run a procedure the plug-in installed in the
+ * procedure database.
+ **/
+struct _GimpPlugInInfo
+{
+ GimpInitProc init_proc;
+ GimpQuitProc quit_proc;
+ GimpQueryProc query_proc;
+ GimpRunProc run_proc;
+};
+
+struct _GimpParamDef
+{
+ GimpPDBArgType type;
+ gchar *name;
+ gchar *description;
+};
+
+struct _GimpParamRegion
+{
+ gint32 x;
+ gint32 y;
+ gint32 width;
+ gint32 height;
+};
+
+union _GimpParamData
+{
+ gint32 d_int32;
+ gint16 d_int16;
+ guint8 d_int8;
+ gdouble d_float;
+ gchar *d_string;
+ gint32 *d_int32array;
+ gint16 *d_int16array;
+ guint8 *d_int8array;
+ gdouble *d_floatarray;
+ gchar **d_stringarray;
+ GimpRGB *d_colorarray;
+ GimpRGB d_color;
+ GimpParamRegion d_region; /* deprecated */
+ gint32 d_display;
+ gint32 d_image;
+ gint32 d_item;
+ gint32 d_layer;
+ gint32 d_layer_mask;
+ gint32 d_channel;
+ gint32 d_drawable;
+ gint32 d_selection;
+ gint32 d_boundary;
+ gint32 d_path; /* deprecated */
+ gint32 d_vectors;
+ gint32 d_unit;
+ GimpParasite d_parasite;
+ gint32 d_tattoo;
+ GimpPDBStatusType d_status;
+};
+
+struct _GimpParam
+{
+ GimpPDBArgType type;
+ GimpParamData data;
+};
+
+
+
+/**
+ * MAIN:
+ *
+ * A macro that expands to the appropriate main() function for the
+ * platform being compiled for.
+ *
+ * To use this macro, simply place a line that contains just the code
+ * MAIN() at the toplevel of your file. No semicolon should be used.
+ **/
+
+#ifdef G_OS_WIN32
+
+/* Define WinMain() because plug-ins are built as GUI applications. Also
+ * define a main() in case some plug-in still is built as a console
+ * application.
+ */
+# ifdef __GNUC__
+# ifndef _stdcall
+# define _stdcall __attribute__((stdcall))
+# endif
+# endif
+
+# define MAIN() \
+ struct HINSTANCE__; \
+ \
+ int _stdcall \
+ WinMain (struct HINSTANCE__ *hInstance, \
+ struct HINSTANCE__ *hPrevInstance, \
+ char *lpszCmdLine, \
+ int nCmdShow); \
+ \
+ int _stdcall \
+ WinMain (struct HINSTANCE__ *hInstance, \
+ struct HINSTANCE__ *hPrevInstance, \
+ char *lpszCmdLine, \
+ int nCmdShow) \
+ { \
+ return gimp_main (&PLUG_IN_INFO, __argc, __argv); \
+ } \
+ \
+ int \
+ main (int argc, char *argv[]) \
+ { \
+ /* Use __argc and __argv here, too, as they work \
+ * better with mingw-w64. \
+ */ \
+ return gimp_main (&PLUG_IN_INFO, __argc, __argv); \
+ }
+#else
+# define MAIN() \
+ int \
+ main (int argc, char *argv[]) \
+ { \
+ return gimp_main (&PLUG_IN_INFO, argc, argv); \
+ }
+#endif
+
+
+/* The main procedure that must be called with the PLUG_IN_INFO structure
+ * and the 'argc' and 'argv' that are passed to "main".
+ */
+gint gimp_main (const GimpPlugInInfo *info,
+ gint argc,
+ gchar *argv[]);
+
+/* Forcefully causes the gimp library to exit and
+ * close down its connection to main gimp application.
+ */
+void gimp_quit (void) G_GNUC_NORETURN;
+
+
+/* Install a procedure in the procedure database.
+ */
+void gimp_install_procedure (const gchar *name,
+ const gchar *blurb,
+ const gchar *help,
+ const gchar *author,
+ const gchar *copyright,
+ const gchar *date,
+ const gchar *menu_label,
+ const gchar *image_types,
+ GimpPDBProcType type,
+ gint n_params,
+ gint n_return_vals,
+ const GimpParamDef *params,
+ const GimpParamDef *return_vals);
+
+/* Install a temporary procedure in the procedure database.
+ */
+void gimp_install_temp_proc (const gchar *name,
+ const gchar *blurb,
+ const gchar *help,
+ const gchar *author,
+ const gchar *copyright,
+ const gchar *date,
+ const gchar *menu_label,
+ const gchar *image_types,
+ GimpPDBProcType type,
+ gint n_params,
+ gint n_return_vals,
+ const GimpParamDef *params,
+ const GimpParamDef *return_vals,
+ GimpRunProc run_proc);
+
+/* Uninstall a temporary procedure
+ */
+void gimp_uninstall_temp_proc (const gchar *name);
+
+/* Notify the main GIMP application that the extension is ready to run
+ */
+void gimp_extension_ack (void);
+
+/* Enable asynchronous processing of temp_procs
+ */
+void gimp_extension_enable (void);
+
+/* Process one temp_proc and return
+ */
+void gimp_extension_process (guint timeout);
+
+/* Run a procedure in the procedure database. The parameters are
+ * specified via the variable length argument list. The return
+ * values are returned in the 'GimpParam*' array.
+ */
+GimpParam * gimp_run_procedure (const gchar *name,
+ gint *n_return_vals,
+ ...);
+
+/* Run a procedure in the procedure database. The parameters are
+ * specified as an array of GimpParam. The return
+ * values are returned in the 'GimpParam*' array.
+ */
+GimpParam * gimp_run_procedure2 (const gchar *name,
+ gint *n_return_vals,
+ gint n_params,
+ const GimpParam *params);
+
+/* Destroy the an array of parameters. This is useful for
+ * destroying the return values returned by a call to
+ * 'gimp_run_procedure'.
+ */
+void gimp_destroy_params (GimpParam *params,
+ gint n_params);
+
+/* Destroy the an array of GimpParamDef's. This is useful for
+ * destroying the return values returned by a call to
+ * 'gimp_procedural_db_proc_info'.
+ */
+void gimp_destroy_paramdefs (GimpParamDef *paramdefs,
+ gint n_params);
+
+/* Retrieve the error message for the last procedure call.
+ */
+const gchar * gimp_get_pdb_error (void);
+
+/* Retrieve the return status for the last procedure call.
+ */
+GimpPDBStatusType gimp_get_pdb_status (void);
+
+/* Return various constants given by the GIMP core at plug-in config time.
+ */
+guint gimp_tile_width (void) G_GNUC_CONST;
+guint gimp_tile_height (void) G_GNUC_CONST;
+gint gimp_shm_ID (void) G_GNUC_CONST;
+guchar * gimp_shm_addr (void) G_GNUC_CONST;
+gboolean gimp_show_tool_tips (void) G_GNUC_CONST;
+gboolean gimp_show_help_button (void) G_GNUC_CONST;
+gboolean gimp_export_color_profile (void) G_GNUC_CONST;
+gboolean gimp_export_exif (void) G_GNUC_CONST;
+gboolean gimp_export_xmp (void) G_GNUC_CONST;
+gboolean gimp_export_iptc (void) G_GNUC_CONST;
+GimpCheckSize gimp_check_size (void) G_GNUC_CONST;
+GimpCheckType gimp_check_type (void) G_GNUC_CONST;
+gint32 gimp_default_display (void) G_GNUC_CONST;
+const gchar * gimp_wm_class (void) G_GNUC_CONST;
+const gchar * gimp_display_name (void) G_GNUC_CONST;
+gint gimp_monitor_number (void) G_GNUC_CONST;
+guint32 gimp_user_time (void) G_GNUC_CONST;
+const gchar * gimp_icon_theme_dir (void) G_GNUC_CONST;
+
+const gchar * gimp_get_progname (void) G_GNUC_CONST;
+
+GIMP_DEPRECATED
+gdouble gimp_gamma (void) G_GNUC_CONST;
+GIMP_DEPRECATED
+gboolean gimp_install_cmap (void) G_GNUC_CONST;
+GIMP_DEPRECATED
+gint gimp_min_colors (void) G_GNUC_CONST;
+
+GIMP_DEPRECATED_FOR(gimp_get_parasite)
+GimpParasite * gimp_parasite_find (const gchar *name);
+GIMP_DEPRECATED_FOR(gimp_attach_parasite)
+gboolean gimp_parasite_attach (const GimpParasite *parasite);
+GIMP_DEPRECATED_FOR(gimp_detach_parasite)
+gboolean gimp_parasite_detach (const gchar *name);
+GIMP_DEPRECATED_FOR(gimp_get_parasite_list)
+gboolean gimp_parasite_list (gint *num_parasites,
+ gchar ***parasites);
+GIMP_DEPRECATED_FOR(gimp_attach_parasite)
+gboolean gimp_attach_new_parasite (const gchar *name,
+ gint flags,
+ gint size,
+ gconstpointer data);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_H__ */
diff --git a/libgimp/gimp_pdb.c b/libgimp/gimp_pdb.c
new file mode 100644
index 0000000..b041d76
--- /dev/null
+++ b/libgimp/gimp_pdb.c
@@ -0,0 +1,263 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimp_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimp
+ * @title: gimp
+ * @short_description: Miscellaneous procedures
+ *
+ * Miscellaneous procedures not fitting in any category.
+ **/
+
+
+/**
+ * gimp_version:
+ *
+ * Returns the host GIMP version.
+ *
+ * This procedure returns the version number of the currently running
+ * GIMP.
+ *
+ * Returns: GIMP version number.
+ **/
+gchar *
+gimp_version (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *version = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-version",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ version = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return version;
+}
+
+/**
+ * gimp_getpid:
+ *
+ * Returns the PID of the host GIMP process.
+ *
+ * This procedure returns the process ID of the currently running GIMP.
+ *
+ * Returns: The PID.
+ *
+ * Since: 2.4
+ **/
+gint
+gimp_getpid (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint pid = 0;
+
+ return_vals = gimp_run_procedure ("gimp-getpid",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ pid = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return pid;
+}
+
+/**
+ * gimp_attach_parasite:
+ * @parasite: The parasite to attach.
+ *
+ * Add a global parasite.
+ *
+ * This procedure attaches a global parasite. It has no return values.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_attach_parasite (const GimpParasite *parasite)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-attach-parasite",
+ &nreturn_vals,
+ GIMP_PDB_PARASITE, parasite,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_detach_parasite:
+ * @name: The name of the parasite to detach.
+ *
+ * Removes a global parasite.
+ *
+ * This procedure detaches a global parasite from. It has no return
+ * values.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_detach_parasite (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-detach-parasite",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_get_parasite:
+ * @name: The name of the parasite to find.
+ *
+ * Look up a global parasite.
+ *
+ * Finds and returns the global parasite that was previously attached.
+ *
+ * Returns: The found parasite.
+ *
+ * Since: 2.8
+ **/
+GimpParasite *
+gimp_get_parasite (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpParasite *parasite = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-get-parasite",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ parasite = gimp_parasite_copy (&return_vals[1].data.d_parasite);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return parasite;
+}
+
+/**
+ * gimp_get_parasite_list:
+ * @num_parasites: The number of attached parasites.
+ *
+ * List all parasites.
+ *
+ * Returns a list of all currently attached global parasites.
+ *
+ * Returns: The names of currently attached parasites. The returned
+ * value must be freed with g_strfreev().
+ *
+ * Since: 2.8
+ **/
+gchar **
+gimp_get_parasite_list (gint *num_parasites)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar **parasites = NULL;
+ gint i;
+
+ return_vals = gimp_run_procedure ("gimp-get-parasite-list",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ *num_parasites = 0;
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ *num_parasites = return_vals[1].data.d_int32;
+ if (*num_parasites > 0)
+ {
+ parasites = g_new0 (gchar *, *num_parasites + 1);
+ for (i = 0; i < *num_parasites; i++)
+ parasites[i] = g_strdup (return_vals[2].data.d_stringarray[i]);
+ }
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return parasites;
+}
+
+/**
+ * gimp_temp_name:
+ * @extension: The extension the file will have.
+ *
+ * Generates a unique filename.
+ *
+ * Generates a unique filename using the temp path supplied in the
+ * user's gimprc.
+ *
+ * Returns: The new temp filename.
+ **/
+gchar *
+gimp_temp_name (const gchar *extension)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-temp-name",
+ &nreturn_vals,
+ GIMP_PDB_STRING, extension,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ name = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return name;
+}
diff --git a/libgimp/gimp_pdb.h b/libgimp/gimp_pdb.h
new file mode 100644
index 0000000..25ef652
--- /dev/null
+++ b/libgimp/gimp_pdb.h
@@ -0,0 +1,46 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimp_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_GIMP_PDB_H__
+#define __GIMP_GIMP_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gchar* gimp_version (void);
+gint gimp_getpid (void);
+gboolean gimp_attach_parasite (const GimpParasite *parasite);
+gboolean gimp_detach_parasite (const gchar *name);
+GimpParasite* gimp_get_parasite (const gchar *name);
+gchar** gimp_get_parasite_list (gint *num_parasites);
+gchar* gimp_temp_name (const gchar *extension);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_GIMP_PDB_H__ */
diff --git a/libgimp/gimp_pdb_headers.h b/libgimp/gimp_pdb_headers.h
new file mode 100644
index 0000000..82113d8
--- /dev/null
+++ b/libgimp/gimp_pdb_headers.h
@@ -0,0 +1,87 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimp_pdb_headers.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PDB_HEADERS_H__
+#define __GIMP_PDB_HEADERS_H__
+
+#include <libgimp/gimp_pdb.h>
+#include <libgimp/gimpbrush_pdb.h>
+#include <libgimp/gimpbrushes_pdb.h>
+#include <libgimp/gimpbrushselect_pdb.h>
+#include <libgimp/gimpbuffer_pdb.h>
+#include <libgimp/gimpchannel_pdb.h>
+#include <libgimp/gimpcolor_pdb.h>
+#include <libgimp/gimpcontext_pdb.h>
+#include <libgimp/gimpdebug_pdb.h>
+#include <libgimp/gimpdisplay_pdb.h>
+#include <libgimp/gimpdrawable_pdb.h>
+#include <libgimp/gimpdrawablecolor_pdb.h>
+#include <libgimp/gimpdrawableedit_pdb.h>
+#include <libgimp/gimpdrawabletransform_pdb.h>
+#include <libgimp/gimpdynamics_pdb.h>
+#include <libgimp/gimpedit_pdb.h>
+#include <libgimp/gimpfileops_pdb.h>
+#include <libgimp/gimpfloatingsel_pdb.h>
+#include <libgimp/gimpfonts_pdb.h>
+#include <libgimp/gimpfontselect_pdb.h>
+#include <libgimp/gimpgimprc_pdb.h>
+#include <libgimp/gimpgradient_pdb.h>
+#include <libgimp/gimpgradients_pdb.h>
+#include <libgimp/gimpgradientselect_pdb.h>
+#include <libgimp/gimphelp_pdb.h>
+#include <libgimp/gimpimage_pdb.h>
+#include <libgimp/gimpimagecolorprofile_pdb.h>
+#include <libgimp/gimpimageconvert_pdb.h>
+#include <libgimp/gimpimagegrid_pdb.h>
+#include <libgimp/gimpimageguides_pdb.h>
+#include <libgimp/gimpimagesamplepoints_pdb.h>
+#include <libgimp/gimpimageselect_pdb.h>
+#include <libgimp/gimpimagetransform_pdb.h>
+#include <libgimp/gimpimageundo_pdb.h>
+#include <libgimp/gimpitem_pdb.h>
+#include <libgimp/gimpitemtransform_pdb.h>
+#include <libgimp/gimplayer_pdb.h>
+#include <libgimp/gimpmessage_pdb.h>
+#include <libgimp/gimppainttools_pdb.h>
+#include <libgimp/gimppalette_pdb.h>
+#include <libgimp/gimppalettes_pdb.h>
+#include <libgimp/gimppaletteselect_pdb.h>
+#include <libgimp/gimppaths_pdb.h>
+#include <libgimp/gimppattern_pdb.h>
+#include <libgimp/gimppatterns_pdb.h>
+#include <libgimp/gimppatternselect_pdb.h>
+#include <libgimp/gimpplugin_pdb.h>
+#include <libgimp/gimpproceduraldb_pdb.h>
+#include <libgimp/gimpprogress_pdb.h>
+#include <libgimp/gimpselection_pdb.h>
+#include <libgimp/gimpselectiontools_pdb.h>
+#include <libgimp/gimptextlayer_pdb.h>
+#include <libgimp/gimptexttool_pdb.h>
+#include <libgimp/gimptransformtools_pdb.h>
+#include <libgimp/gimpunit_pdb.h>
+#include <libgimp/gimpvectors_pdb.h>
+
+#endif /* __GIMP_PDB_HEADERS_H__ */
diff --git a/libgimp/gimpaspectpreview.c b/libgimp/gimpaspectpreview.c
new file mode 100644
index 0000000..6177746
--- /dev/null
+++ b/libgimp/gimpaspectpreview.c
@@ -0,0 +1,483 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpaspectpreview.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "gimpuitypes.h"
+
+#include "gimp.h"
+
+#include "libgimp-intl.h"
+
+#include "gimpaspectpreview.h"
+
+
+/**
+ * SECTION: gimpaspectpreview
+ * @title: GimpAspectPreview
+ * @short_description: A widget providing a preview with fixed aspect ratio.
+ *
+ * A widget providing a preview with fixed aspect ratio.
+ **/
+
+
+enum
+{
+ PROP_0,
+ PROP_DRAWABLE,
+ PROP_DRAWABLE_ID
+};
+
+typedef struct
+{
+ gint32 drawable_ID;
+} GimpAspectPreviewPrivate;
+
+typedef struct
+{
+ gboolean update;
+} PreviewSettings;
+
+
+#define GIMP_ASPECT_PREVIEW_GET_PRIVATE(obj) \
+ ((GimpAspectPreviewPrivate *) gimp_aspect_preview_get_instance_private ((GimpAspectPreview *) (preview)))
+
+static void gimp_aspect_preview_constructed (GObject *object);
+static void gimp_aspect_preview_dispose (GObject *object);
+static void gimp_aspect_preview_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_aspect_preview_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_aspect_preview_style_set (GtkWidget *widget,
+ GtkStyle *prev_style);
+static void gimp_aspect_preview_draw (GimpPreview *preview);
+static void gimp_aspect_preview_draw_buffer (GimpPreview *preview,
+ const guchar *buffer,
+ gint rowstride);
+static void gimp_aspect_preview_transform (GimpPreview *preview,
+ gint src_x,
+ gint src_y,
+ gint *dest_x,
+ gint *dest_y);
+static void gimp_aspect_preview_untransform (GimpPreview *preview,
+ gint src_x,
+ gint src_y,
+ gint *dest_x,
+ gint *dest_y);
+
+static void gimp_aspect_preview_set_drawable (GimpAspectPreview *preview,
+ GimpDrawable *drawable);
+static void gimp_aspect_preview_set_drawable_id
+ (GimpAspectPreview *preview,
+ gint32 drawable_ID);
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpAspectPreview, gimp_aspect_preview,
+ GIMP_TYPE_PREVIEW)
+
+#define parent_class gimp_aspect_preview_parent_class
+
+static gint gimp_aspect_preview_counter = 0;
+
+
+static void
+gimp_aspect_preview_class_init (GimpAspectPreviewClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GimpPreviewClass *preview_class = GIMP_PREVIEW_CLASS (klass);
+
+ object_class->constructed = gimp_aspect_preview_constructed;
+ object_class->dispose = gimp_aspect_preview_dispose;
+ object_class->get_property = gimp_aspect_preview_get_property;
+ object_class->set_property = gimp_aspect_preview_set_property;
+
+ widget_class->style_set = gimp_aspect_preview_style_set;
+
+ preview_class->draw = gimp_aspect_preview_draw;
+ preview_class->draw_buffer = gimp_aspect_preview_draw_buffer;
+ preview_class->transform = gimp_aspect_preview_transform;
+ preview_class->untransform = gimp_aspect_preview_untransform;
+
+ /**
+ * GimpAspectPreview:drawable:
+ *
+ * Deprecated: use the drawable-id property instead.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class, PROP_DRAWABLE,
+ g_param_spec_pointer ("drawable",
+ "Drawable",
+ "Deprecated: use the drawable-id property instead",
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * GimpAspectPreview:drawable-id:
+ *
+ * The drawable the #GimpAspectPreview is attached to.
+ *
+ * Since: 2.10
+ */
+ g_object_class_install_property (object_class, PROP_DRAWABLE_ID,
+ g_param_spec_int ("drawable-id",
+ "Drawable ID",
+ "The drawable this preview is attached to",
+ -1, G_MAXINT, -1,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+gimp_aspect_preview_init (GimpAspectPreview *preview)
+{
+ g_object_set (GIMP_PREVIEW (preview)->area,
+ "check-size", gimp_check_size (),
+ "check-type", gimp_check_type (),
+ NULL);
+}
+
+static void
+gimp_aspect_preview_constructed (GObject *object)
+{
+ gchar *data_name;
+ PreviewSettings settings;
+
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ data_name = g_strdup_printf ("%s-aspect-preview-%d",
+ g_get_prgname (),
+ gimp_aspect_preview_counter++);
+
+ if (gimp_get_data (data_name, &settings))
+ {
+ gimp_preview_set_update (GIMP_PREVIEW (object), settings.update);
+ }
+
+ g_object_set_data_full (object, "gimp-aspect-preview-data-name",
+ data_name, (GDestroyNotify) g_free);
+}
+
+static void
+gimp_aspect_preview_dispose (GObject *object)
+{
+ const gchar *data_name = g_object_get_data (G_OBJECT (object),
+ "gimp-aspect-preview-data-name");
+
+ if (data_name)
+ {
+ GimpPreview *preview = GIMP_PREVIEW (object);
+ PreviewSettings settings;
+
+ settings.update = gimp_preview_get_update (preview);
+
+ gimp_set_data (data_name, &settings, sizeof (PreviewSettings));
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gimp_aspect_preview_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpAspectPreview *preview = GIMP_ASPECT_PREVIEW (object);
+ GimpAspectPreviewPrivate *priv = GIMP_ASPECT_PREVIEW_GET_PRIVATE (preview);
+
+ switch (property_id)
+ {
+ case PROP_DRAWABLE:
+ g_value_set_pointer (value, preview->drawable);
+ break;
+
+ case PROP_DRAWABLE_ID:
+ g_value_set_int (value, priv->drawable_ID);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_aspect_preview_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpAspectPreview *preview = GIMP_ASPECT_PREVIEW (object);
+ GimpAspectPreviewPrivate *priv = GIMP_ASPECT_PREVIEW_GET_PRIVATE (preview);
+
+ switch (property_id)
+ {
+ case PROP_DRAWABLE:
+ g_return_if_fail (priv->drawable_ID < 1);
+ if (g_value_get_pointer (value))
+ gimp_aspect_preview_set_drawable (preview,
+ g_value_get_pointer (value));
+ break;
+
+ case PROP_DRAWABLE_ID:
+ gimp_aspect_preview_set_drawable_id (preview,
+ g_value_get_int (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_aspect_preview_style_set (GtkWidget *widget,
+ GtkStyle *prev_style)
+{
+ GimpPreview *preview = GIMP_PREVIEW (widget);
+ GimpAspectPreviewPrivate *priv = GIMP_ASPECT_PREVIEW_GET_PRIVATE (preview);
+ gint width;
+ gint height;
+ gint size;
+
+ if (GTK_WIDGET_CLASS (parent_class)->style_set)
+ GTK_WIDGET_CLASS (parent_class)->style_set (widget, prev_style);
+
+ gtk_widget_style_get (widget,
+ "size", &size,
+ NULL);
+
+ width = gimp_drawable_width (priv->drawable_ID);
+ height = gimp_drawable_height (priv->drawable_ID);
+
+ if (width > height)
+ {
+ preview->width = MIN (width, size);
+ preview->height = (height * preview->width) / width;
+ }
+ else
+ {
+ preview->height = MIN (height, size);
+ preview->width = (width * preview->height) / height;
+ }
+
+ gtk_widget_set_size_request (preview->area,
+ preview->width, preview->height);
+}
+
+
+static void
+gimp_aspect_preview_draw (GimpPreview *preview)
+{
+ g_return_if_fail (GIMP_IS_ASPECT_PREVIEW (preview));
+
+ gimp_preview_area_fill (GIMP_PREVIEW_AREA (preview->area),
+ 0, 0,
+ preview->width,
+ preview->height,
+ 0, 0, 0);
+}
+
+static void
+gimp_aspect_preview_draw_buffer (GimpPreview *preview,
+ const guchar *buffer,
+ gint rowstride)
+{
+ GimpAspectPreviewPrivate *priv = GIMP_ASPECT_PREVIEW_GET_PRIVATE (preview);
+ gint32 image_ID;
+
+ image_ID = gimp_item_get_image (priv->drawable_ID);
+
+ if (gimp_selection_is_empty (image_ID))
+ {
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview->area),
+ 0, 0,
+ preview->width, preview->height,
+ gimp_drawable_type (priv->drawable_ID),
+ buffer,
+ rowstride);
+ }
+ else
+ {
+ guchar *sel;
+ guchar *src;
+ gint selection_ID;
+ gint width, height;
+ gint bpp;
+
+ selection_ID = gimp_image_get_selection (image_ID);
+
+ width = preview->width;
+ height = preview->height;
+
+ src = gimp_drawable_get_thumbnail_data (priv->drawable_ID,
+ &width, &height, &bpp);
+ sel = gimp_drawable_get_thumbnail_data (selection_ID,
+ &width, &height, &bpp);
+
+ gimp_preview_area_mask (GIMP_PREVIEW_AREA (preview->area),
+ 0, 0, preview->width, preview->height,
+ gimp_drawable_type (priv->drawable_ID),
+ src, width * gimp_drawable_bpp (priv->drawable_ID),
+ buffer, rowstride,
+ sel, width);
+
+ g_free (sel);
+ g_free (src);
+ }
+}
+
+static void
+gimp_aspect_preview_transform (GimpPreview *preview,
+ gint src_x,
+ gint src_y,
+ gint *dest_x,
+ gint *dest_y)
+{
+ GimpAspectPreviewPrivate *priv = GIMP_ASPECT_PREVIEW_GET_PRIVATE (preview);
+
+ *dest_x = (gdouble) src_x * preview->width / gimp_drawable_width (priv->drawable_ID);
+ *dest_y = (gdouble) src_y * preview->height / gimp_drawable_height (priv->drawable_ID);
+}
+
+static void
+gimp_aspect_preview_untransform (GimpPreview *preview,
+ gint src_x,
+ gint src_y,
+ gint *dest_x,
+ gint *dest_y)
+{
+ GimpAspectPreviewPrivate *priv = GIMP_ASPECT_PREVIEW_GET_PRIVATE (preview);
+
+ *dest_x = (gdouble) src_x * gimp_drawable_width (priv->drawable_ID) / preview->width;
+ *dest_y = (gdouble) src_y * gimp_drawable_height (priv->drawable_ID) / preview->height;
+}
+
+static void
+gimp_aspect_preview_set_drawable (GimpAspectPreview *preview,
+ GimpDrawable *drawable)
+{
+ GimpAspectPreviewPrivate *priv = GIMP_ASPECT_PREVIEW_GET_PRIVATE (preview);
+
+ g_return_if_fail (preview->drawable == NULL);
+ g_return_if_fail (priv->drawable_ID < 1);
+
+ preview->drawable = drawable;
+
+ gimp_aspect_preview_set_drawable_id (preview, drawable->drawable_id);
+}
+
+static void
+gimp_aspect_preview_set_drawable_id (GimpAspectPreview *preview,
+ gint32 drawable_ID)
+{
+ GimpAspectPreviewPrivate *priv = GIMP_ASPECT_PREVIEW_GET_PRIVATE (preview);
+ gint d_width;
+ gint d_height;
+ gint width;
+ gint height;
+
+ g_return_if_fail (priv->drawable_ID < 1);
+
+ priv->drawable_ID = drawable_ID;
+
+ d_width = gimp_drawable_width (priv->drawable_ID);
+ d_height = gimp_drawable_height (priv->drawable_ID);
+
+ if (d_width > d_height)
+ {
+ width = MIN (d_width, 512);
+ height = (d_height * width) / d_width;
+ }
+ else
+ {
+ height = MIN (d_height, 512);
+ width = (d_width * height) / d_height;
+ }
+
+ gimp_preview_set_bounds (GIMP_PREVIEW (preview), 0, 0, width, height);
+
+ if (height > 0)
+ g_object_set (GIMP_PREVIEW (preview)->frame,
+ "ratio",
+ (gdouble) d_width / (gdouble) d_height,
+ NULL);
+}
+
+/**
+ * gimp_aspect_preview_new_from_drawable_id:
+ * @drawable_ID: a drawable ID
+ *
+ * Creates a new #GimpAspectPreview widget for @drawable_ID. See also
+ * gimp_drawable_preview_new_from_drawable_id().
+ *
+ * Since: 2.10
+ *
+ * Returns: a new #GimpAspectPreview.
+ **/
+GtkWidget *
+gimp_aspect_preview_new_from_drawable_id (gint32 drawable_ID)
+{
+ g_return_val_if_fail (gimp_item_is_valid (drawable_ID), NULL);
+ g_return_val_if_fail (gimp_item_is_drawable (drawable_ID), NULL);
+
+ return g_object_new (GIMP_TYPE_ASPECT_PREVIEW,
+ "drawable-id", drawable_ID,
+ NULL);
+}
+/**
+ * gimp_aspect_preview_new:
+ * @drawable: a #GimpDrawable
+ * @toggle: unused
+ *
+ * Creates a new #GimpAspectPreview widget for @drawable. See also
+ * gimp_drawable_preview_new().
+ *
+ * In GIMP 2.2 the @toggle parameter was provided to conviently access
+ * the state of the "Preview" check-button. This is not any longer
+ * necessary as the preview itself now stores this state, as well as
+ * the scroll offset.
+ *
+ * Since: 2.2
+ *
+ * Returns: a new #GimpAspectPreview.
+ **/
+GtkWidget *
+gimp_aspect_preview_new (GimpDrawable *drawable,
+ gboolean *toggle)
+{
+ g_return_val_if_fail (drawable != NULL, NULL);
+
+ return g_object_new (GIMP_TYPE_ASPECT_PREVIEW,
+ "drawable", drawable,
+ NULL);
+}
diff --git a/libgimp/gimpaspectpreview.h b/libgimp/gimpaspectpreview.h
new file mode 100644
index 0000000..6e95f0d
--- /dev/null
+++ b/libgimp/gimpaspectpreview.h
@@ -0,0 +1,75 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpaspectpreview.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_UI_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimpui.h> can be included directly."
+#endif
+
+#ifndef __GIMP_ASPECT_PREVIEW_H__
+#define __GIMP_ASPECT_PREVIEW_H__
+
+G_BEGIN_DECLS
+
+
+/* For information look into the C source or the html documentation */
+
+
+#define GIMP_TYPE_ASPECT_PREVIEW (gimp_aspect_preview_get_type ())
+#define GIMP_ASPECT_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_ASPECT_PREVIEW, GimpAspectPreview))
+#define GIMP_ASPECT_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_ASPECT_PREVIEW, GimpAspectPreviewClass))
+#define GIMP_IS_ASPECT_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_ASPECT_PREVIEW))
+#define GIMP_IS_ASPECT_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_ASPECT_PREVIEW))
+#define GIMP_ASPECT_PREVIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_ASPECT_PREVIEW, GimpAspectPreviewClass))
+
+
+typedef struct _GimpAspectPreviewClass GimpAspectPreviewClass;
+
+struct _GimpAspectPreview
+{
+ GimpPreview parent_instance;
+
+ /*< private >*/
+ GimpDrawable *drawable;
+};
+
+struct _GimpAspectPreviewClass
+{
+ GimpPreviewClass parent_class;
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_aspect_preview_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_aspect_preview_new_from_drawable_id (gint32 drawable_ID);
+
+GIMP_DEPRECATED_FOR(gimp_aspect_preview_new_from_drawable_id)
+GtkWidget * gimp_aspect_preview_new (GimpDrawable *drawable,
+ gboolean *toggle);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_ASPECT_PREVIEW_H__ */
diff --git a/libgimp/gimpbrush_pdb.c b/libgimp/gimpbrush_pdb.c
new file mode 100644
index 0000000..6cb5bc7
--- /dev/null
+++ b/libgimp/gimpbrush_pdb.c
@@ -0,0 +1,864 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpbrush_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpbrush
+ * @title: gimpbrush
+ * @short_description: Functions operating on a single brush.
+ *
+ * Functions operating on a single brush.
+ **/
+
+
+/**
+ * gimp_brush_new:
+ * @name: The requested name of the new brush.
+ *
+ * Creates a new brush.
+ *
+ * This procedure creates a new, uninitialized brush.
+ *
+ * Returns: The actual new brush name. The returned value must be freed
+ * with g_free().
+ *
+ * Since: 2.2
+ **/
+gchar *
+gimp_brush_new (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *actual_name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-brush-new",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ actual_name = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return actual_name;
+}
+
+/**
+ * gimp_brush_duplicate:
+ * @name: The brush name.
+ *
+ * Duplicates a brush.
+ *
+ * This procedure creates an identical brush by a different name.
+ *
+ * Returns: The name of the brush's copy. The returned value must be
+ * freed with g_free().
+ *
+ * Since: 2.2
+ **/
+gchar *
+gimp_brush_duplicate (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *copy_name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-brush-duplicate",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ copy_name = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return copy_name;
+}
+
+/**
+ * gimp_brush_is_generated:
+ * @name: The brush name.
+ *
+ * Tests if brush is generated.
+ *
+ * Returns TRUE if this brush is parametric, FALSE for other types.
+ *
+ * Returns: TRUE if the brush is generated.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_brush_is_generated (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean generated = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-brush-is-generated",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ generated = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return generated;
+}
+
+/**
+ * gimp_brush_rename:
+ * @name: The brush name.
+ * @new_name: The new name of the brush.
+ *
+ * Renames a brush.
+ *
+ * This procedure renames a brush.
+ *
+ * Returns: The actual new name of the brush. The returned value must
+ * be freed with g_free().
+ *
+ * Since: 2.2
+ **/
+gchar *
+gimp_brush_rename (const gchar *name,
+ const gchar *new_name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *actual_name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-brush-rename",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_STRING, new_name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ actual_name = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return actual_name;
+}
+
+/**
+ * gimp_brush_delete:
+ * @name: The brush name.
+ *
+ * Deletes a brush.
+ *
+ * This procedure deletes a brush.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_brush_delete (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-brush-delete",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_brush_is_editable:
+ * @name: The brush name.
+ *
+ * Tests if brush can be edited.
+ *
+ * Returns TRUE if you have permission to change the brush.
+ *
+ * Returns: TRUE if the brush can be edited.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_brush_is_editable (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean editable = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-brush-is-editable",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ editable = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return editable;
+}
+
+/**
+ * gimp_brush_get_info:
+ * @name: The brush name.
+ * @width: The brush width.
+ * @height: The brush height.
+ * @mask_bpp: The brush mask bpp.
+ * @color_bpp: The brush color bpp.
+ *
+ * Retrieves information about the specified brush.
+ *
+ * This procedure retrieves information about the specified brush:
+ * brush extents (width and height), color depth and mask depth.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_brush_get_info (const gchar *name,
+ gint *width,
+ gint *height,
+ gint *mask_bpp,
+ gint *color_bpp)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-brush-get-info",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ *width = 0;
+ *height = 0;
+ *mask_bpp = 0;
+ *color_bpp = 0;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *width = return_vals[1].data.d_int32;
+ *height = return_vals[2].data.d_int32;
+ *mask_bpp = return_vals[3].data.d_int32;
+ *color_bpp = return_vals[4].data.d_int32;
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_brush_get_pixels:
+ * @name: The brush name.
+ * @width: The brush width.
+ * @height: The brush height.
+ * @mask_bpp: The brush mask bpp.
+ * @num_mask_bytes: Length of brush mask data.
+ * @mask_bytes: The brush mask data.
+ * @color_bpp: The brush color bpp.
+ * @num_color_bytes: Length of brush color data.
+ * @color_bytes: The brush color data.
+ *
+ * Retrieves information about the specified brush.
+ *
+ * This procedure retrieves information about the specified brush. This
+ * includes the brush extents (width and height) and its pixels data.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_brush_get_pixels (const gchar *name,
+ gint *width,
+ gint *height,
+ gint *mask_bpp,
+ gint *num_mask_bytes,
+ guint8 **mask_bytes,
+ gint *color_bpp,
+ gint *num_color_bytes,
+ guint8 **color_bytes)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-brush-get-pixels",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ *width = 0;
+ *height = 0;
+ *mask_bpp = 0;
+ *num_mask_bytes = 0;
+ *mask_bytes = NULL;
+ *color_bpp = 0;
+ *num_color_bytes = 0;
+ *color_bytes = NULL;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *width = return_vals[1].data.d_int32;
+ *height = return_vals[2].data.d_int32;
+ *mask_bpp = return_vals[3].data.d_int32;
+ *num_mask_bytes = return_vals[4].data.d_int32;
+ *mask_bytes = g_new (guint8, *num_mask_bytes);
+ memcpy (*mask_bytes,
+ return_vals[5].data.d_int8array,
+ *num_mask_bytes * sizeof (guint8));
+ *color_bpp = return_vals[6].data.d_int32;
+ *num_color_bytes = return_vals[7].data.d_int32;
+ *color_bytes = g_new (guint8, *num_color_bytes);
+ memcpy (*color_bytes,
+ return_vals[8].data.d_int8array,
+ *num_color_bytes * sizeof (guint8));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_brush_get_spacing:
+ * @name: The brush name.
+ * @spacing: The brush spacing.
+ *
+ * Gets the brush spacing.
+ *
+ * This procedure returns the spacing setting for the specified brush.
+ * The return value is an integer between 0 and 1000 which represents
+ * percentage of the maximum of the width and height of the mask.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_brush_get_spacing (const gchar *name,
+ gint *spacing)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-brush-get-spacing",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ *spacing = 0;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ *spacing = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_brush_set_spacing:
+ * @name: The brush name.
+ * @spacing: The brush spacing.
+ *
+ * Sets the brush spacing.
+ *
+ * This procedure modifies the spacing setting for the specified brush.
+ * The value should be a integer between 0 and 1000.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_brush_set_spacing (const gchar *name,
+ gint spacing)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-brush-set-spacing",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, spacing,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_brush_get_shape:
+ * @name: The brush name.
+ *
+ * Gets the shape of a generated brush.
+ *
+ * This procedure gets the shape value for a generated brush. If called
+ * for any other type of brush, it does not succeed. The current
+ * possibilities are Circle (GIMP_BRUSH_GENERATED_CIRCLE), Square
+ * (GIMP_BRUSH_GENERATED_SQUARE), and Diamond
+ * (GIMP_BRUSH_GENERATED_DIAMOND). Other shapes are likely to be added
+ * in the future.
+ *
+ * Returns: The brush shape.
+ *
+ * Since: 2.4
+ **/
+GimpBrushGeneratedShape
+gimp_brush_get_shape (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpBrushGeneratedShape shape = 0;
+
+ return_vals = gimp_run_procedure ("gimp-brush-get-shape",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ shape = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return shape;
+}
+
+/**
+ * gimp_brush_set_shape:
+ * @name: The brush name.
+ * @shape_in: The brush shape.
+ *
+ * Sets the shape of a generated brush.
+ *
+ * This procedure sets the shape value for a generated brush. If called
+ * for any other type of brush, it does not succeed. The current
+ * possibilities are Circle (GIMP_BRUSH_GENERATED_CIRCLE), Square
+ * (GIMP_BRUSH_GENERATED_SQUARE), and Diamond
+ * (GIMP_BRUSH_GENERATED_DIAMOND). Other shapes are likely to be added
+ * in the future.
+ *
+ * Returns: The brush shape actually assigned.
+ *
+ * Since: 2.4
+ **/
+GimpBrushGeneratedShape
+gimp_brush_set_shape (const gchar *name,
+ GimpBrushGeneratedShape shape_in)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpBrushGeneratedShape shape_out = 0;
+
+ return_vals = gimp_run_procedure ("gimp-brush-set-shape",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, shape_in,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ shape_out = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return shape_out;
+}
+
+/**
+ * gimp_brush_get_radius:
+ * @name: The brush name.
+ *
+ * Gets the radius of a generated brush.
+ *
+ * This procedure gets the radius value for a generated brush. If
+ * called for any other type of brush, it does not succeed.
+ *
+ * Returns: The radius of the brush in pixels.
+ *
+ * Since: 2.4
+ **/
+gdouble
+gimp_brush_get_radius (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble radius = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-brush-get-radius",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ radius = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return radius;
+}
+
+/**
+ * gimp_brush_set_radius:
+ * @name: The brush name.
+ * @radius_in: The desired brush radius in pixel.
+ *
+ * Sets the radius of a generated brush.
+ *
+ * This procedure sets the radius for a generated brush. If called for
+ * any other type of brush, it does not succeed.
+ *
+ * Returns: The brush radius actually assigned.
+ *
+ * Since: 2.4
+ **/
+gdouble
+gimp_brush_set_radius (const gchar *name,
+ gdouble radius_in)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble radius_out = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-brush-set-radius",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_FLOAT, radius_in,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ radius_out = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return radius_out;
+}
+
+/**
+ * gimp_brush_get_spikes:
+ * @name: The brush name.
+ *
+ * Gets the number of spikes for a generated brush.
+ *
+ * This procedure gets the number of spikes for a generated brush. If
+ * called for any other type of brush, it does not succeed.
+ *
+ * Returns: The number of spikes on the brush.
+ *
+ * Since: 2.4
+ **/
+gint
+gimp_brush_get_spikes (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint spikes = 0;
+
+ return_vals = gimp_run_procedure ("gimp-brush-get-spikes",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ spikes = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return spikes;
+}
+
+/**
+ * gimp_brush_set_spikes:
+ * @name: The brush name.
+ * @spikes_in: The desired number of spikes.
+ *
+ * Sets the number of spikes for a generated brush.
+ *
+ * This procedure sets the number of spikes for a generated brush. If
+ * called for any other type of brush, it does not succeed.
+ *
+ * Returns: The number of spikes actually assigned.
+ *
+ * Since: 2.4
+ **/
+gint
+gimp_brush_set_spikes (const gchar *name,
+ gint spikes_in)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint spikes_out = 0;
+
+ return_vals = gimp_run_procedure ("gimp-brush-set-spikes",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, spikes_in,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ spikes_out = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return spikes_out;
+}
+
+/**
+ * gimp_brush_get_hardness:
+ * @name: The brush name.
+ *
+ * Gets the hardness of a generated brush.
+ *
+ * This procedure gets the hardness of a generated brush. The hardness
+ * of a brush is the amount its intensity fades at the outside edge, as
+ * a float between 0.0 and 1.0. If called for any other type of brush,
+ * the function does not succeed.
+ *
+ * Returns: The hardness of the brush.
+ *
+ * Since: 2.4
+ **/
+gdouble
+gimp_brush_get_hardness (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble hardness = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-brush-get-hardness",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ hardness = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return hardness;
+}
+
+/**
+ * gimp_brush_set_hardness:
+ * @name: The brush name.
+ * @hardness_in: The desired brush hardness.
+ *
+ * Sets the hardness of a generated brush.
+ *
+ * This procedure sets the hardness for a generated brush. If called
+ * for any other type of brush, it does not succeed. The value should
+ * be a float between 0.0 and 1.0.
+ *
+ * Returns: The brush hardness actually assigned.
+ *
+ * Since: 2.4
+ **/
+gdouble
+gimp_brush_set_hardness (const gchar *name,
+ gdouble hardness_in)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble hardness_out = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-brush-set-hardness",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_FLOAT, hardness_in,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ hardness_out = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return hardness_out;
+}
+
+/**
+ * gimp_brush_get_aspect_ratio:
+ * @name: The brush name.
+ *
+ * Gets the aspect ratio of a generated brush.
+ *
+ * This procedure gets the aspect ratio of a generated brush. If called
+ * for any other type of brush, it does not succeed. The return value
+ * is a float between 0.0 and 1000.0.
+ *
+ * Returns: The aspect ratio of the brush.
+ *
+ * Since: 2.4
+ **/
+gdouble
+gimp_brush_get_aspect_ratio (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble aspect_ratio = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-brush-get-aspect-ratio",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ aspect_ratio = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return aspect_ratio;
+}
+
+/**
+ * gimp_brush_set_aspect_ratio:
+ * @name: The brush name.
+ * @aspect_ratio_in: The desired brush aspect ratio.
+ *
+ * Sets the aspect ratio of a generated brush.
+ *
+ * This procedure sets the aspect ratio for a generated brush. If
+ * called for any other type of brush, it does not succeed. The value
+ * should be a float between 0.0 and 1000.0.
+ *
+ * Returns: The brush aspect ratio actually assigned.
+ *
+ * Since: 2.4
+ **/
+gdouble
+gimp_brush_set_aspect_ratio (const gchar *name,
+ gdouble aspect_ratio_in)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble aspect_ratio_out = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-brush-set-aspect-ratio",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_FLOAT, aspect_ratio_in,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ aspect_ratio_out = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return aspect_ratio_out;
+}
+
+/**
+ * gimp_brush_get_angle:
+ * @name: The brush name.
+ *
+ * Gets the rotation angle of a generated brush.
+ *
+ * This procedure gets the angle of rotation for a generated brush. If
+ * called for any other type of brush, it does not succeed.
+ *
+ * Returns: The rotation angle of the brush in degree.
+ *
+ * Since: 2.4
+ **/
+gdouble
+gimp_brush_get_angle (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble angle = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-brush-get-angle",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ angle = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return angle;
+}
+
+/**
+ * gimp_brush_set_angle:
+ * @name: The brush name.
+ * @angle_in: The desired brush rotation angle in degree.
+ *
+ * Sets the rotation angle of a generated brush.
+ *
+ * This procedure sets the rotation angle for a generated brush. If
+ * called for any other type of brush, it does not succeed.
+ *
+ * Returns: The brush rotation angle actually assigned.
+ *
+ * Since: 2.4
+ **/
+gdouble
+gimp_brush_set_angle (const gchar *name,
+ gdouble angle_in)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble angle_out = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-brush-set-angle",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_FLOAT, angle_in,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ angle_out = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return angle_out;
+}
diff --git a/libgimp/gimpbrush_pdb.h b/libgimp/gimpbrush_pdb.h
new file mode 100644
index 0000000..f56c568
--- /dev/null
+++ b/libgimp/gimpbrush_pdb.h
@@ -0,0 +1,82 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpbrush_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_BRUSH_PDB_H__
+#define __GIMP_BRUSH_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gchar* gimp_brush_new (const gchar *name);
+gchar* gimp_brush_duplicate (const gchar *name);
+gboolean gimp_brush_is_generated (const gchar *name);
+gchar* gimp_brush_rename (const gchar *name,
+ const gchar *new_name);
+gboolean gimp_brush_delete (const gchar *name);
+gboolean gimp_brush_is_editable (const gchar *name);
+gboolean gimp_brush_get_info (const gchar *name,
+ gint *width,
+ gint *height,
+ gint *mask_bpp,
+ gint *color_bpp);
+gboolean gimp_brush_get_pixels (const gchar *name,
+ gint *width,
+ gint *height,
+ gint *mask_bpp,
+ gint *num_mask_bytes,
+ guint8 **mask_bytes,
+ gint *color_bpp,
+ gint *num_color_bytes,
+ guint8 **color_bytes);
+gboolean gimp_brush_get_spacing (const gchar *name,
+ gint *spacing);
+gboolean gimp_brush_set_spacing (const gchar *name,
+ gint spacing);
+GimpBrushGeneratedShape gimp_brush_get_shape (const gchar *name);
+GimpBrushGeneratedShape gimp_brush_set_shape (const gchar *name,
+ GimpBrushGeneratedShape shape_in);
+gdouble gimp_brush_get_radius (const gchar *name);
+gdouble gimp_brush_set_radius (const gchar *name,
+ gdouble radius_in);
+gint gimp_brush_get_spikes (const gchar *name);
+gint gimp_brush_set_spikes (const gchar *name,
+ gint spikes_in);
+gdouble gimp_brush_get_hardness (const gchar *name);
+gdouble gimp_brush_set_hardness (const gchar *name,
+ gdouble hardness_in);
+gdouble gimp_brush_get_aspect_ratio (const gchar *name);
+gdouble gimp_brush_set_aspect_ratio (const gchar *name,
+ gdouble aspect_ratio_in);
+gdouble gimp_brush_get_angle (const gchar *name);
+gdouble gimp_brush_set_angle (const gchar *name,
+ gdouble angle_in);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_BRUSH_PDB_H__ */
diff --git a/libgimp/gimpbrushes.c b/libgimp/gimpbrushes.c
new file mode 100644
index 0000000..c8fdff7
--- /dev/null
+++ b/libgimp/gimpbrushes.c
@@ -0,0 +1,94 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpbrushes.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gimp.h"
+#include "gimpbrushes.h"
+
+/**
+ * gimp_brushes_get_opacity:
+ *
+ * This procedure is deprecated! Use gimp_context_get_opacity() instead.
+ *
+ * Returns: The brush opacity.
+ */
+gdouble
+gimp_brushes_get_opacity (void)
+{
+ return gimp_context_get_opacity ();
+}
+
+/**
+ * gimp_brushes_set_opacity:
+ * @opacity: The brush opacity.
+ *
+ * This procedure is deprecated! Use gimp_context_set_opacity() instead.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+gimp_brushes_set_opacity (gdouble opacity)
+{
+ return gimp_context_set_opacity (opacity);
+}
+
+/**
+ * gimp_brushes_get_paint_mode:
+ *
+ * This procedure is deprecated! Use gimp_context_get_paint_mode() instead.
+ *
+ * Returns: The paint mode.
+ */
+GimpLayerMode
+gimp_brushes_get_paint_mode (void)
+{
+ return gimp_context_get_paint_mode ();
+}
+
+/**
+ * gimp_brushes_set_paint_mode:
+ * @paint_mode: The paint mode.
+ *
+ * This procedure is deprecated! Use gimp_context_set_paint_mode() instead.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+gimp_brushes_set_paint_mode (GimpLayerMode paint_mode)
+{
+ return gimp_context_set_paint_mode (paint_mode);
+}
+
+/**
+ * gimp_brushes_set_brush:
+ * @name: The brush name.
+ *
+ * This procedure is deprecated! Use gimp_context_set_brush() instead.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+gimp_brushes_set_brush (const gchar *name)
+{
+ return gimp_context_set_brush (name);
+}
diff --git a/libgimp/gimpbrushes.h b/libgimp/gimpbrushes.h
new file mode 100644
index 0000000..aaa3701
--- /dev/null
+++ b/libgimp/gimpbrushes.h
@@ -0,0 +1,45 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpbrushes.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_BRUSHES_H__
+#define __GIMP_BRUSHES_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+GIMP_DEPRECATED_FOR(gimp_context_get_opacity)
+gdouble gimp_brushes_get_opacity (void);
+GIMP_DEPRECATED_FOR(gimp_context_set_opacity)
+gboolean gimp_brushes_set_opacity (gdouble opacity);
+GIMP_DEPRECATED_FOR(gimp_context_get_paint_mode)
+GimpLayerMode gimp_brushes_get_paint_mode (void);
+GIMP_DEPRECATED_FOR(gimp_context_set_paint_mode)
+gboolean gimp_brushes_set_paint_mode (GimpLayerMode paint_mode);
+GIMP_DEPRECATED_FOR(gimp_context_set_brush)
+gboolean gimp_brushes_set_brush (const gchar *name);
+
+G_END_DECLS
+
+#endif /* __GIMP_BRUSHES_H__ */
diff --git a/libgimp/gimpbrushes_pdb.c b/libgimp/gimpbrushes_pdb.c
new file mode 100644
index 0000000..594504b
--- /dev/null
+++ b/libgimp/gimpbrushes_pdb.c
@@ -0,0 +1,256 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpbrushes_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpbrushes
+ * @title: gimpbrushes
+ * @short_description: Functions for manipulating brushes.
+ *
+ * Functions related to getting and setting brushes.
+ **/
+
+
+/**
+ * gimp_brushes_refresh:
+ *
+ * Refresh current brushes. This function always succeeds.
+ *
+ * This procedure retrieves all brushes currently in the user's brush
+ * path and updates the brush dialogs accordingly.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_brushes_refresh (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-brushes-refresh",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_brushes_get_list:
+ * @filter: An optional regular expression used to filter the list.
+ * @num_brushes: The number of brushes in the brush list.
+ *
+ * Retrieve a complete listing of the available brushes.
+ *
+ * This procedure returns a complete listing of available GIMP brushes.
+ * Each name returned can be used as input to the
+ * gimp_context_set_brush() procedure.
+ *
+ * Returns: The list of brush names. The returned value must be freed
+ * with g_strfreev().
+ **/
+gchar **
+gimp_brushes_get_list (const gchar *filter,
+ gint *num_brushes)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar **brush_list = NULL;
+ gint i;
+
+ return_vals = gimp_run_procedure ("gimp-brushes-get-list",
+ &nreturn_vals,
+ GIMP_PDB_STRING, filter,
+ GIMP_PDB_END);
+
+ *num_brushes = 0;
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ *num_brushes = return_vals[1].data.d_int32;
+ if (*num_brushes > 0)
+ {
+ brush_list = g_new0 (gchar *, *num_brushes + 1);
+ for (i = 0; i < *num_brushes; i++)
+ brush_list[i] = g_strdup (return_vals[2].data.d_stringarray[i]);
+ }
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return brush_list;
+}
+
+/**
+ * gimp_brushes_get_brush:
+ * @width: The brush width.
+ * @height: The brush height.
+ * @spacing: The brush spacing.
+ *
+ * Deprecated: Use gimp_context_get_brush() instead.
+ *
+ * Returns: The brush name.
+ **/
+gchar *
+gimp_brushes_get_brush (gint *width,
+ gint *height,
+ gint *spacing)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-brushes-get-brush",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ name = g_strdup (return_vals[1].data.d_string);
+ *width = return_vals[2].data.d_int32;
+ *height = return_vals[3].data.d_int32;
+ *spacing = return_vals[4].data.d_int32;
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return name;
+}
+
+/**
+ * gimp_brushes_get_spacing:
+ *
+ * Deprecated: Use gimp_brush_get_spacing() instead.
+ *
+ * Returns: The brush spacing.
+ **/
+gint
+gimp_brushes_get_spacing (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint spacing = 0;
+
+ return_vals = gimp_run_procedure ("gimp-brushes-get-spacing",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ spacing = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return spacing;
+}
+
+/**
+ * gimp_brushes_set_spacing:
+ * @spacing: The brush spacing.
+ *
+ * Deprecated: Use gimp_brush_set_spacing() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_brushes_set_spacing (gint spacing)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-brushes-set-spacing",
+ &nreturn_vals,
+ GIMP_PDB_INT32, spacing,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_brushes_get_brush_data:
+ * @name: The brush name (\"\" means current active brush).
+ * @opacity: The brush opacity.
+ * @spacing: The brush spacing.
+ * @paint_mode: The paint mode.
+ * @width: The brush width.
+ * @height: The brush height.
+ * @length: Length of brush mask data.
+ * @mask_data: The brush mask data.
+ *
+ * Deprecated: Use gimp_brush_get_pixels() instead.
+ *
+ * Returns: The brush name.
+ **/
+gchar *
+gimp_brushes_get_brush_data (const gchar *name,
+ gdouble *opacity,
+ gint *spacing,
+ GimpLayerMode *paint_mode,
+ gint *width,
+ gint *height,
+ gint *length,
+ guint8 **mask_data)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *actual_name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-brushes-get-brush-data",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ *length = 0;
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ actual_name = g_strdup (return_vals[1].data.d_string);
+ *opacity = return_vals[2].data.d_float;
+ *spacing = return_vals[3].data.d_int32;
+ *paint_mode = return_vals[4].data.d_int32;
+ *width = return_vals[5].data.d_int32;
+ *height = return_vals[6].data.d_int32;
+ *length = return_vals[7].data.d_int32;
+ *mask_data = g_new (guint8, *length);
+ memcpy (*mask_data,
+ return_vals[8].data.d_int8array,
+ *length * sizeof (guint8));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return actual_name;
+}
diff --git a/libgimp/gimpbrushes_pdb.h b/libgimp/gimpbrushes_pdb.h
new file mode 100644
index 0000000..8faaee9
--- /dev/null
+++ b/libgimp/gimpbrushes_pdb.h
@@ -0,0 +1,59 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpbrushes_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_BRUSHES_PDB_H__
+#define __GIMP_BRUSHES_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_brushes_refresh (void);
+gchar** gimp_brushes_get_list (const gchar *filter,
+ gint *num_brushes);
+GIMP_DEPRECATED_FOR(gimp_context_get_brush)
+gchar* gimp_brushes_get_brush (gint *width,
+ gint *height,
+ gint *spacing);
+GIMP_DEPRECATED_FOR(gimp_brush_get_spacing)
+gint gimp_brushes_get_spacing (void);
+GIMP_DEPRECATED_FOR(gimp_brush_set_spacing)
+gboolean gimp_brushes_set_spacing (gint spacing);
+GIMP_DEPRECATED_FOR(gimp_brush_get_pixels)
+gchar* gimp_brushes_get_brush_data (const gchar *name,
+ gdouble *opacity,
+ gint *spacing,
+ GimpLayerMode *paint_mode,
+ gint *width,
+ gint *height,
+ gint *length,
+ guint8 **mask_data);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_BRUSHES_PDB_H__ */
diff --git a/libgimp/gimpbrushmenu.c b/libgimp/gimpbrushmenu.c
new file mode 100644
index 0000000..43dde0b
--- /dev/null
+++ b/libgimp/gimpbrushmenu.c
@@ -0,0 +1,168 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpbrushmenu.c
+ * Copyright (C) 1998 Andy Thomas
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "gimp.h"
+
+#include "gimpuitypes.h"
+#include "gimpbrushmenu.h"
+#include "gimpbrushselectbutton.h"
+
+
+/**
+ * SECTION: gimpbrushmenu
+ * @title: gimpbrushmenu
+ * @short_description: A widget for selecting brushes.
+ *
+ * A widget for selecting brushes.
+ **/
+
+
+typedef struct
+{
+ GimpRunBrushCallback callback;
+ gpointer data;
+} CompatCallbackData;
+
+
+static void compat_callback (GimpBrushSelectButton *brush_button,
+ const gchar *brush_name,
+ gdouble opacity,
+ gint spacing,
+ GimpLayerMode paint_mode,
+ gint width,
+ gint height,
+ const guchar *mask_data,
+ gboolean dialog_closing,
+ CompatCallbackData *data);
+static void compat_callback_data_free (CompatCallbackData *data);
+
+
+/**
+ * gimp_brush_select_widget_new:
+ * @title: Title of the dialog to use or %NULL to use the default title.
+ * @brush_name: Initial brush name or %NULL to use current selection.
+ * @opacity: Initial opacity. -1 means to use current opacity.
+ * @spacing: Initial spacing. -1 means to use current spacing.
+ * @paint_mode: Initial paint mode. -1 means to use current paint mode.
+ * @callback: A function to call when the selected brush changes.
+ * @data: A pointer to arbitrary data to be used in the call to @callback.
+ *
+ * Creates a new #GtkWidget that completely controls the selection of
+ * a #GimpBrush. This widget is suitable for placement in a table in
+ * a plug-in dialog.
+ *
+ * Returns: A #GtkWidget that you can use in your UI.
+ */
+GtkWidget *
+gimp_brush_select_widget_new (const gchar *title,
+ const gchar *brush_name,
+ gdouble opacity,
+ gint spacing,
+ GimpLayerMode paint_mode,
+ GimpRunBrushCallback callback,
+ gpointer data)
+{
+ GtkWidget *brush_button;
+ CompatCallbackData *compat_data;
+
+ g_return_val_if_fail (callback != NULL, NULL);
+
+ brush_button = gimp_brush_select_button_new (title, brush_name,
+ opacity, spacing, paint_mode);
+
+ compat_data = g_slice_new (CompatCallbackData);
+
+ compat_data->callback = callback;
+ compat_data->data = data;
+
+ g_signal_connect_data (brush_button, "brush-set",
+ G_CALLBACK (compat_callback),
+ compat_data,
+ (GClosureNotify) compat_callback_data_free, 0);
+
+ return brush_button;
+}
+
+/**
+ * gimp_brush_select_widget_close:
+ * @widget: A brush select widget.
+ *
+ * Closes the popup window associated with @widget.
+ */
+void
+gimp_brush_select_widget_close (GtkWidget *widget)
+{
+ g_return_if_fail (widget != NULL);
+
+ gimp_select_button_close_popup (GIMP_SELECT_BUTTON (widget));
+}
+
+/**
+ * gimp_brush_select_widget_set:
+ * @widget: A brush select widget.
+ * @brush_name: Brush name to set; %NULL means no change.
+ * @opacity: Opacity to set. -1.0 means no change.
+ * @spacing: Spacing to set. -1 means no change.
+ * @paint_mode: Paint mode to set. -1 means no change.
+ *
+ * Sets the current brush and other values for the brush select
+ * widget. Calls the callback function if one was supplied in the
+ * call to gimp_brush_select_widget_new().
+ */
+void
+gimp_brush_select_widget_set (GtkWidget *widget,
+ const gchar *brush_name,
+ gdouble opacity,
+ gint spacing,
+ GimpLayerMode paint_mode)
+{
+ g_return_if_fail (widget != NULL);
+
+ gimp_brush_select_button_set_brush (GIMP_BRUSH_SELECT_BUTTON (widget),
+ brush_name, opacity, spacing, paint_mode);
+}
+
+
+static void
+compat_callback (GimpBrushSelectButton *brush_button,
+ const gchar *brush_name,
+ gdouble opacity,
+ gint spacing,
+ GimpLayerMode paint_mode,
+ gint width,
+ gint height,
+ const guchar *mask_data,
+ gboolean dialog_closing,
+ CompatCallbackData *data)
+{
+ data->callback (brush_name, opacity, spacing, paint_mode,
+ width, height, mask_data, dialog_closing, data->data);
+}
+
+static void
+compat_callback_data_free (CompatCallbackData *data)
+{
+ g_slice_free (CompatCallbackData, data);
+}
diff --git a/libgimp/gimpbrushmenu.h b/libgimp/gimpbrushmenu.h
new file mode 100644
index 0000000..64d9565
--- /dev/null
+++ b/libgimp/gimpbrushmenu.h
@@ -0,0 +1,55 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpbrushmenu.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_UI_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimpui.h> can be included directly."
+#endif
+
+#ifndef __GIMP_BRUSH_MENU_H__
+#define __GIMP_BRUSH_MENU_H__
+
+/* These functions are deprecated and should not be used in newly
+ * written code.
+ */
+
+G_BEGIN_DECLS
+
+GIMP_DEPRECATED_FOR(gimp_brush_select_button_new)
+GtkWidget * gimp_brush_select_widget_new (const gchar *title,
+ const gchar *brush_name,
+ gdouble opacity,
+ gint spacing,
+ GimpLayerMode paint_mode,
+ GimpRunBrushCallback callback,
+ gpointer data);
+
+GIMP_DEPRECATED_FOR(gimp_select_button_close_popup)
+void gimp_brush_select_widget_close (GtkWidget *widget);
+GIMP_DEPRECATED_FOR(gimp_brush_select_button_set_brush)
+void gimp_brush_select_widget_set (GtkWidget *widget,
+ const gchar *brush_name,
+ gdouble opacity,
+ gint spacing,
+ GimpLayerMode paint_mode);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_BRUSH_MENU_H__ */
diff --git a/libgimp/gimpbrushselect.c b/libgimp/gimpbrushselect.c
new file mode 100644
index 0000000..fb17659
--- /dev/null
+++ b/libgimp/gimpbrushselect.c
@@ -0,0 +1,240 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpbrushselect.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+typedef struct
+{
+ gchar *brush_callback;
+ guint idle_id;
+ gchar *brush_name;
+ gdouble opacity;
+ gint spacing;
+ gint paint_mode;
+ gint width;
+ gint height;
+ guchar *brush_mask_data;
+ GimpRunBrushCallback callback;
+ gboolean closing;
+ gpointer data;
+} GimpBrushData;
+
+
+/* local function prototypes */
+
+static void gimp_brush_data_free (GimpBrushData *data);
+
+static void gimp_temp_brush_run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+static gboolean gimp_temp_brush_run_idle (GimpBrushData *brush_data);
+
+
+/* private variables */
+
+static GHashTable *gimp_brush_select_ht = NULL;
+
+
+/* public functions */
+
+const gchar *
+gimp_brush_select_new (const gchar *title,
+ const gchar *brush_name,
+ gdouble opacity,
+ gint spacing,
+ GimpLayerMode paint_mode,
+ GimpRunBrushCallback callback,
+ gpointer data)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_STRING, "str", "String" },
+ { GIMP_PDB_FLOAT, "opacity", "Opacity" },
+ { GIMP_PDB_INT32, "spacing", "Spacing" },
+ { GIMP_PDB_INT32, "paint-mode", "Paint mode" },
+ { GIMP_PDB_INT32, "mask-width", "Brush width" },
+ { GIMP_PDB_INT32, "mask-height", "Brush height" },
+ { GIMP_PDB_INT32, "mask-len", "Length of brush mask data" },
+ { GIMP_PDB_INT8ARRAY, "mask-data", "The brush mask data" },
+ { GIMP_PDB_INT32, "dialog-status", "If the dialog was closing "
+ "[0 = No, 1 = Yes]" }
+ };
+
+ gchar *brush_callback = gimp_procedural_db_temp_name ();
+
+ gimp_install_temp_proc (brush_callback,
+ "Temporary brush popup callback procedure",
+ "",
+ "",
+ "",
+ "",
+ NULL,
+ "",
+ GIMP_TEMPORARY,
+ G_N_ELEMENTS (args), 0,
+ args, NULL,
+ gimp_temp_brush_run);
+
+ if (gimp_brushes_popup (brush_callback, title, brush_name,
+ opacity, spacing, paint_mode))
+ {
+ GimpBrushData *brush_data;
+
+ gimp_extension_enable (); /* Allow callbacks to be watched */
+
+ /* Now add to hash table so we can find it again */
+ if (! gimp_brush_select_ht)
+ {
+ gimp_brush_select_ht =
+ g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free,
+ (GDestroyNotify) gimp_brush_data_free);
+ }
+
+ brush_data = g_slice_new0 (GimpBrushData);
+
+ brush_data->brush_callback = brush_callback;
+ brush_data->callback = callback;
+ brush_data->data = data;
+
+ g_hash_table_insert (gimp_brush_select_ht, brush_callback, brush_data);
+
+ return brush_callback;
+ }
+
+ gimp_uninstall_temp_proc (brush_callback);
+ g_free (brush_callback);
+
+ return NULL;
+}
+
+void
+gimp_brush_select_destroy (const gchar *brush_callback)
+{
+ GimpBrushData *brush_data;
+
+ g_return_if_fail (brush_callback != NULL);
+ g_return_if_fail (gimp_brush_select_ht != NULL);
+
+ brush_data = g_hash_table_lookup (gimp_brush_select_ht, brush_callback);
+
+ if (! brush_data)
+ {
+ g_warning ("Can't find internal brush data");
+ return;
+ }
+
+ if (brush_data->idle_id)
+ g_source_remove (brush_data->idle_id);
+
+ g_free (brush_data->brush_name);
+ g_free (brush_data->brush_mask_data);
+
+ if (brush_data->brush_callback)
+ gimp_brushes_close_popup (brush_data->brush_callback);
+
+ gimp_uninstall_temp_proc (brush_callback);
+
+ g_hash_table_remove (gimp_brush_select_ht, brush_callback);
+}
+
+
+/* private functions */
+
+static void
+gimp_brush_data_free (GimpBrushData *data)
+{
+ g_slice_free (GimpBrushData, data);
+}
+
+static void
+gimp_temp_brush_run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpBrushData *brush_data;
+
+ brush_data = g_hash_table_lookup (gimp_brush_select_ht, name);
+
+ if (! brush_data)
+ {
+ g_warning ("Can't find internal brush data");
+ }
+ else
+ {
+ g_free (brush_data->brush_name);
+ g_free (brush_data->brush_mask_data);
+
+ brush_data->brush_name = g_strdup (param[0].data.d_string);
+ brush_data->opacity = param[1].data.d_float;
+ brush_data->spacing = param[2].data.d_int32;
+ brush_data->paint_mode = param[3].data.d_int32;
+ brush_data->width = param[4].data.d_int32;
+ brush_data->height = param[5].data.d_int32;
+ brush_data->brush_mask_data = g_memdup (param[7].data.d_int8array,
+ param[6].data.d_int32);
+ brush_data->closing = param[8].data.d_int32;
+
+ if (! brush_data->idle_id)
+ brush_data->idle_id = g_idle_add ((GSourceFunc) gimp_temp_brush_run_idle,
+ brush_data);
+ }
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_SUCCESS;
+}
+
+static gboolean
+gimp_temp_brush_run_idle (GimpBrushData *brush_data)
+{
+ brush_data->idle_id = 0;
+
+ if (brush_data->callback)
+ brush_data->callback (brush_data->brush_name,
+ brush_data->opacity,
+ brush_data->spacing,
+ brush_data->paint_mode,
+ brush_data->width,
+ brush_data->height,
+ brush_data->brush_mask_data,
+ brush_data->closing,
+ brush_data->data);
+
+ if (brush_data->closing)
+ {
+ gchar *brush_callback = brush_data->brush_callback;
+
+ brush_data->brush_callback = NULL;
+ gimp_brush_select_destroy (brush_callback);
+ }
+
+ return FALSE;
+}
diff --git a/libgimp/gimpbrushselect.h b/libgimp/gimpbrushselect.h
new file mode 100644
index 0000000..ee66d05
--- /dev/null
+++ b/libgimp/gimpbrushselect.h
@@ -0,0 +1,54 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpbrushselect.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_BRUSH_SELECT_H__
+#define __GIMP_BRUSH_SELECT_H__
+
+G_BEGIN_DECLS
+
+
+typedef void (* GimpRunBrushCallback) (const gchar *brush_name,
+ gdouble opacity,
+ gint spacing,
+ GimpLayerMode paint_mode,
+ gint width,
+ gint height,
+ const guchar *mask_data,
+ gboolean dialog_closing,
+ gpointer user_data);
+
+
+const gchar * gimp_brush_select_new (const gchar *title,
+ const gchar *brush_name,
+ gdouble opacity,
+ gint spacing,
+ GimpLayerMode paint_mode,
+ GimpRunBrushCallback callback,
+ gpointer data);
+void gimp_brush_select_destroy (const gchar *brush_callback);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_BRUSH_SELECT_H__ */
diff --git a/libgimp/gimpbrushselect_pdb.c b/libgimp/gimpbrushselect_pdb.c
new file mode 100644
index 0000000..9a813f1
--- /dev/null
+++ b/libgimp/gimpbrushselect_pdb.c
@@ -0,0 +1,149 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpbrushselect_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpbrushselect
+ * @title: gimpbrushselect
+ * @short_description: Functions providing a brush selection dialog.
+ *
+ * Functions providing a brush selection dialog.
+ **/
+
+
+/**
+ * gimp_brushes_popup:
+ * @brush_callback: The callback PDB proc to call when brush selection is made.
+ * @popup_title: Title of the brush selection dialog.
+ * @initial_brush: The name of the brush to set as the first selected.
+ * @opacity: The initial opacity of the brush.
+ * @spacing: The initial spacing of the brush (if < 0 then use brush default spacing).
+ * @paint_mode: The initial paint mode.
+ *
+ * Invokes the Gimp brush selection.
+ *
+ * This procedure opens the brush selection dialog.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_brushes_popup (const gchar *brush_callback,
+ const gchar *popup_title,
+ const gchar *initial_brush,
+ gdouble opacity,
+ gint spacing,
+ GimpLayerMode paint_mode)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-brushes-popup",
+ &nreturn_vals,
+ GIMP_PDB_STRING, brush_callback,
+ GIMP_PDB_STRING, popup_title,
+ GIMP_PDB_STRING, initial_brush,
+ GIMP_PDB_FLOAT, opacity,
+ GIMP_PDB_INT32, spacing,
+ GIMP_PDB_INT32, paint_mode,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_brushes_close_popup:
+ * @brush_callback: The name of the callback registered for this pop-up.
+ *
+ * Close the brush selection dialog.
+ *
+ * This procedure closes an opened brush selection dialog.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_brushes_close_popup (const gchar *brush_callback)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-brushes-close-popup",
+ &nreturn_vals,
+ GIMP_PDB_STRING, brush_callback,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_brushes_set_popup:
+ * @brush_callback: The name of the callback registered for this pop-up.
+ * @brush_name: The name of the brush to set as selected.
+ * @opacity: The initial opacity of the brush.
+ * @spacing: The initial spacing of the brush (if < 0 then use brush default spacing).
+ * @paint_mode: The initial paint mode.
+ *
+ * Sets the current brush in a brush selection dialog.
+ *
+ * Sets the current brush in a brush selection dialog.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_brushes_set_popup (const gchar *brush_callback,
+ const gchar *brush_name,
+ gdouble opacity,
+ gint spacing,
+ GimpLayerMode paint_mode)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-brushes-set-popup",
+ &nreturn_vals,
+ GIMP_PDB_STRING, brush_callback,
+ GIMP_PDB_STRING, brush_name,
+ GIMP_PDB_FLOAT, opacity,
+ GIMP_PDB_INT32, spacing,
+ GIMP_PDB_INT32, paint_mode,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimpbrushselect_pdb.h b/libgimp/gimpbrushselect_pdb.h
new file mode 100644
index 0000000..ae401f2
--- /dev/null
+++ b/libgimp/gimpbrushselect_pdb.h
@@ -0,0 +1,51 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpbrushselect_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_BRUSH_SELECT_PDB_H__
+#define __GIMP_BRUSH_SELECT_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_brushes_popup (const gchar *brush_callback,
+ const gchar *popup_title,
+ const gchar *initial_brush,
+ gdouble opacity,
+ gint spacing,
+ GimpLayerMode paint_mode);
+gboolean gimp_brushes_close_popup (const gchar *brush_callback);
+gboolean gimp_brushes_set_popup (const gchar *brush_callback,
+ const gchar *brush_name,
+ gdouble opacity,
+ gint spacing,
+ GimpLayerMode paint_mode);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_BRUSH_SELECT_PDB_H__ */
diff --git a/libgimp/gimpbrushselectbutton.c b/libgimp/gimpbrushselectbutton.c
new file mode 100644
index 0000000..0dc3d54
--- /dev/null
+++ b/libgimp/gimpbrushselectbutton.c
@@ -0,0 +1,932 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpbrushselectbutton.c
+ * Copyright (C) 1998 Andy Thomas
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "gimp.h"
+
+#include "gimpuitypes.h"
+#include "gimpbrushselectbutton.h"
+#include "gimpuimarshal.h"
+
+#include "libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpbrushselectbutton
+ * @title: gimpbrushselectbutton
+ * @short_description: A button that pops up a brush selection dialog.
+ *
+ * A button that pops up a brush selection dialog.
+ **/
+
+
+#define CELL_SIZE 20
+
+
+#define GIMP_BRUSH_SELECT_BUTTON_GET_PRIVATE(obj) ((GimpBrushSelectButtonPrivate *) gimp_brush_select_button_get_instance_private ((GimpBrushSelectButton *) (obj)))
+
+typedef struct _GimpBrushSelectButtonPrivate GimpBrushSelectButtonPrivate;
+
+struct _GimpBrushSelectButtonPrivate
+{
+ gchar *title;
+
+ gchar *brush_name; /* Local copy */
+ gdouble opacity;
+ gint spacing;
+ GimpLayerMode paint_mode;
+ gint width;
+ gint height;
+ guchar *mask_data; /* local copy */
+
+ GtkWidget *inside;
+ GtkWidget *preview;
+ GtkWidget *popup;
+};
+
+enum
+{
+ BRUSH_SET,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_TITLE,
+ PROP_BRUSH_NAME,
+ PROP_BRUSH_OPACITY,
+ PROP_BRUSH_SPACING,
+ PROP_BRUSH_PAINT_MODE
+};
+
+
+/* local function prototypes */
+
+static void gimp_brush_select_button_finalize (GObject *object);
+
+static void gimp_brush_select_button_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_brush_select_button_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_brush_select_button_clicked (GimpBrushSelectButton *button);
+
+static void gimp_brush_select_button_callback (const gchar *brush_name,
+ gdouble opacity,
+ gint spacing,
+ GimpLayerMode paint_mode,
+ gint width,
+ gint height,
+ const guchar *mask_data,
+ gboolean dialog_closing,
+ gpointer user_data);
+
+static void gimp_brush_select_preview_resize (GimpBrushSelectButton *button);
+static gboolean gimp_brush_select_preview_events (GtkWidget *widget,
+ GdkEvent *event,
+ GimpBrushSelectButton *button);
+static void gimp_brush_select_preview_draw (GimpPreviewArea *area,
+ gint x,
+ gint y,
+ gint width,
+ gint height,
+ const guchar *mask_data,
+ gint rowstride);
+static void gimp_brush_select_preview_update (GtkWidget *preview,
+ gint brush_width,
+ gint brush_height,
+ const guchar *mask_data);
+
+static void gimp_brush_select_button_open_popup (GimpBrushSelectButton *button,
+ gint x,
+ gint y);
+static void gimp_brush_select_button_close_popup (GimpBrushSelectButton *button);
+
+static void gimp_brush_select_drag_data_received (GimpBrushSelectButton *button,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time);
+
+static GtkWidget * gimp_brush_select_button_create_inside (GimpBrushSelectButton *button);
+
+
+static const GtkTargetEntry target = { "application/x-gimp-brush-name", 0 };
+
+static guint brush_button_signals[LAST_SIGNAL] = { 0 };
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpBrushSelectButton, gimp_brush_select_button,
+ GIMP_TYPE_SELECT_BUTTON)
+
+
+static void
+gimp_brush_select_button_class_init (GimpBrushSelectButtonClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpSelectButtonClass *select_button_class = GIMP_SELECT_BUTTON_CLASS (klass);
+
+ object_class->finalize = gimp_brush_select_button_finalize;
+ object_class->set_property = gimp_brush_select_button_set_property;
+ object_class->get_property = gimp_brush_select_button_get_property;
+
+ select_button_class->select_destroy = gimp_brush_select_destroy;
+
+ klass->brush_set = NULL;
+
+ /**
+ * GimpBrushSelectButton:title:
+ *
+ * The title to be used for the brush selection popup dialog.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class, PROP_TITLE,
+ g_param_spec_string ("title",
+ "Title",
+ "The title to be used for the brush selection popup dialog",
+ _("Brush Selection"),
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * GimpBrushSelectButton:brush-name:
+ *
+ * The name of the currently selected brush.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class, PROP_BRUSH_NAME,
+ g_param_spec_string ("brush-name",
+ "Brush name",
+ "The name of the currently selected brush",
+ NULL,
+ GIMP_PARAM_READWRITE));
+
+ /**
+ * GimpBrushSelectButton:opacity:
+ *
+ * The opacity of the currently selected brush.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class, PROP_BRUSH_OPACITY,
+ g_param_spec_double ("brush-opacity",
+ "Brush opacity",
+ "The opacity of the currently selected brush",
+ -1.0, 100.0, -1.0,
+ GIMP_PARAM_READWRITE));
+
+ /**
+ * GimpBrushSelectButton:spacing:
+ *
+ * The spacing of the currently selected brush.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class, PROP_BRUSH_SPACING,
+ g_param_spec_int ("brush-spacing",
+ "Brush spacing",
+ "The spacing of the currently selected brush",
+ -G_MAXINT, 1000, -1,
+ GIMP_PARAM_READWRITE));
+
+ /**
+ * GimpBrushSelectButton:paint-mode:
+ *
+ * The paint mode.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class, PROP_BRUSH_PAINT_MODE,
+ g_param_spec_int ("brush-paint-mode",
+ "Brush paint mode",
+ "The paint mode of the currently selected brush",
+ -1, GIMP_LAYER_MODE_LUMINANCE,
+ -1,
+ GIMP_PARAM_READWRITE));
+
+ /**
+ * GimpBrushSelectButton::brush-set:
+ * @widget: the object which received the signal.
+ * @brush_name: the name of the currently selected brush.
+ * @opacity: opacity of the brush
+ * @spacing: spacing of the brush
+ * @paint_mode: paint mode of the brush
+ * @width: width of the brush
+ * @height: height of the brush
+ * @mask_data: brush mask data
+ * @dialog_closing: whether the dialog was closed or not.
+ *
+ * The ::brush-set signal is emitted when the user selects a brush.
+ *
+ * Since: 2.4
+ */
+ brush_button_signals[BRUSH_SET] =
+ g_signal_new ("brush-set",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpBrushSelectButtonClass, brush_set),
+ NULL, NULL,
+ _gimpui_marshal_VOID__STRING_DOUBLE_INT_INT_INT_INT_POINTER_BOOLEAN,
+ G_TYPE_NONE, 8,
+ G_TYPE_STRING,
+ G_TYPE_DOUBLE,
+ G_TYPE_INT,
+ G_TYPE_INT,
+ G_TYPE_INT,
+ G_TYPE_INT,
+ G_TYPE_POINTER,
+ G_TYPE_BOOLEAN);
+}
+
+static void
+gimp_brush_select_button_init (GimpBrushSelectButton *button)
+{
+ GimpBrushSelectButtonPrivate *priv;
+ gint mask_bpp;
+ gint mask_data_size;
+ gint color_bpp;
+ gint color_data_size;
+ guint8 *color_data;
+
+ priv = GIMP_BRUSH_SELECT_BUTTON_GET_PRIVATE (button);
+
+ priv->brush_name = gimp_context_get_brush ();
+ gimp_brush_get_pixels (priv->brush_name,
+ &priv->width,
+ &priv->height,
+ &mask_bpp,
+ &mask_data_size,
+ &priv->mask_data,
+ &color_bpp,
+ &color_data_size,
+ &color_data);
+
+ if (color_data)
+ g_free (color_data);
+
+ priv->inside = gimp_brush_select_button_create_inside (button);
+ gtk_container_add (GTK_CONTAINER (button), priv->inside);
+
+ priv->popup = NULL;
+}
+
+/**
+ * gimp_brush_select_button_new:
+ * @title: Title of the dialog to use or %NULL means to use the default
+ * title.
+ * @brush_name: Initial brush name or %NULL to use current selection.
+ * @opacity: Initial opacity. -1 means to use current opacity.
+ * @spacing: Initial spacing. -1 means to use current spacing.
+ * @paint_mode: Initial paint mode. -1 means to use current paint mode.
+ *
+ * Creates a new #GtkWidget that completely controls the selection of
+ * a brush. This widget is suitable for placement in a table in a
+ * plug-in dialog.
+ *
+ * Returns: A #GtkWidget that you can use in your UI.
+ *
+ * Since: 2.4
+ */
+GtkWidget *
+gimp_brush_select_button_new (const gchar *title,
+ const gchar *brush_name,
+ gdouble opacity,
+ gint spacing,
+ GimpLayerMode paint_mode)
+{
+ GtkWidget *button;
+
+ if (title)
+ button = g_object_new (GIMP_TYPE_BRUSH_SELECT_BUTTON,
+ "title", title,
+ "brush-name", brush_name,
+ "brush-opacity", opacity,
+ "brush-spacing", spacing,
+ "brush-paint-mode", paint_mode,
+ NULL);
+ else
+ button = g_object_new (GIMP_TYPE_BRUSH_SELECT_BUTTON,
+ "brush-name", brush_name,
+ "brush-opacity", opacity,
+ "brush-spacing", spacing,
+ "brush-paint-mode", paint_mode,
+ NULL);
+
+ return button;
+}
+
+/**
+ * gimp_brush_select_button_get_brush:
+ * @button: A #GimpBrushSelectButton
+ * @opacity: Opacity of the selected brush.
+ * @spacing: Spacing of the selected brush.
+ * @paint_mode: Paint mode of the selected brush.
+ *
+ * Retrieves the properties of currently selected brush.
+ *
+ * Returns: an internal copy of the brush name which must not be freed.
+ *
+ * Since: 2.4
+ */
+const gchar *
+gimp_brush_select_button_get_brush (GimpBrushSelectButton *button,
+ gdouble *opacity,
+ gint *spacing,
+ GimpLayerMode *paint_mode)
+{
+ GimpBrushSelectButtonPrivate *priv;
+
+ g_return_val_if_fail (GIMP_IS_BRUSH_SELECT_BUTTON (button), NULL);
+
+ priv = GIMP_BRUSH_SELECT_BUTTON_GET_PRIVATE (button);
+
+ if (opacity)
+ *opacity = priv->opacity;
+
+ if (spacing)
+ *spacing = priv->spacing;
+
+ if (paint_mode)
+ *paint_mode = priv->paint_mode;
+
+ return priv->brush_name;
+}
+
+/**
+ * gimp_brush_select_button_set_brush:
+ * @button: A #GimpBrushSelectButton
+ * @brush_name: Brush name to set; %NULL means no change.
+ * @opacity: Opacity to set. -1.0 means no change.
+ * @spacing: Spacing to set. -1 means no change.
+ * @paint_mode: Paint mode to set. -1 means no change.
+ *
+ * Sets the current brush and other values for the brush select
+ * button.
+ *
+ * Since: 2.4
+ */
+void
+gimp_brush_select_button_set_brush (GimpBrushSelectButton *button,
+ const gchar *brush_name,
+ gdouble opacity,
+ gint spacing,
+ GimpLayerMode paint_mode)
+{
+ GimpSelectButton *select_button;
+
+ g_return_if_fail (GIMP_IS_BRUSH_SELECT_BUTTON (button));
+
+ select_button = GIMP_SELECT_BUTTON (button);
+
+ if (select_button->temp_callback)
+ {
+ gimp_brushes_set_popup (select_button->temp_callback, brush_name,
+ opacity, spacing, paint_mode);
+ }
+ else
+ {
+ gchar *name;
+ gint width;
+ gint height;
+ gint bytes;
+ gint mask_data_size;
+ guint8 *mask_data;
+ gint color_bpp;
+ gint color_data_size;
+ guint8 *color_data;
+
+ if (brush_name && *brush_name)
+ name = g_strdup (brush_name);
+ else
+ name = gimp_context_get_brush ();
+
+ if (gimp_brush_get_pixels (name,
+ &width,
+ &height,
+ &bytes,
+ &mask_data_size,
+ &mask_data,
+ &color_bpp,
+ &color_data_size,
+ &color_data))
+ {
+ if (color_data)
+ g_free (color_data);
+
+ if (opacity < 0.0)
+ opacity = gimp_context_get_opacity ();
+
+ if (spacing == -1)
+ gimp_brush_get_spacing (name, &spacing);
+
+ if (paint_mode == -1)
+ paint_mode = gimp_context_get_paint_mode ();
+
+ gimp_brush_select_button_callback (name,
+ opacity, spacing, paint_mode,
+ width, height, mask_data,
+ FALSE, button);
+
+ g_free (mask_data);
+ }
+
+ g_free (name);
+ }
+}
+
+
+/* private functions */
+
+static void
+gimp_brush_select_button_finalize (GObject *object)
+{
+ GimpBrushSelectButtonPrivate *priv;
+
+ priv = GIMP_BRUSH_SELECT_BUTTON_GET_PRIVATE (object);
+
+ g_clear_pointer (&priv->brush_name, g_free);
+ g_clear_pointer (&priv->mask_data, g_free);
+ g_clear_pointer (&priv->title, g_free);
+
+ G_OBJECT_CLASS (gimp_brush_select_button_parent_class)->finalize (object);
+}
+
+static void
+gimp_brush_select_button_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpBrushSelectButton *button = GIMP_BRUSH_SELECT_BUTTON (object);
+ GimpBrushSelectButtonPrivate *priv;
+ gdouble opacity;
+ gint32 spacing;
+ gint32 paint_mode;
+
+ priv = GIMP_BRUSH_SELECT_BUTTON_GET_PRIVATE (button);
+
+ switch (property_id)
+ {
+ case PROP_TITLE:
+ priv->title = g_value_dup_string (value);
+ break;
+ case PROP_BRUSH_NAME:
+ gimp_brush_select_button_set_brush (button,
+ g_value_get_string (value),
+ -1.0, -1, -1);
+ break;
+ case PROP_BRUSH_OPACITY:
+ opacity = g_value_get_double (value);
+ if (opacity >= 0.0)
+ priv->opacity = opacity;
+ break;
+ case PROP_BRUSH_SPACING:
+ spacing = g_value_get_int (value);
+ if (spacing != -1)
+ priv->spacing = spacing;
+ break;
+ case PROP_BRUSH_PAINT_MODE:
+ paint_mode = g_value_get_int (value);
+ if (paint_mode != -1)
+ priv->paint_mode = paint_mode;
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_brush_select_button_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpBrushSelectButton *button = GIMP_BRUSH_SELECT_BUTTON (object);
+ GimpBrushSelectButtonPrivate *priv;
+
+ priv = GIMP_BRUSH_SELECT_BUTTON_GET_PRIVATE (button);
+
+ switch (property_id)
+ {
+ case PROP_TITLE:
+ g_value_set_string (value, priv->title);
+ break;
+ case PROP_BRUSH_NAME:
+ g_value_set_string (value, priv->brush_name);
+ break;
+ case PROP_BRUSH_OPACITY:
+ g_value_set_double (value, priv->opacity);
+ break;
+ case PROP_BRUSH_SPACING:
+ g_value_set_int (value, priv->spacing);
+ break;
+ case PROP_BRUSH_PAINT_MODE:
+ g_value_set_int (value, priv->paint_mode);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_brush_select_button_callback (const gchar *name,
+ gdouble opacity,
+ gint spacing,
+ GimpLayerMode paint_mode,
+ gint width,
+ gint height,
+ const guchar *mask_data,
+ gboolean dialog_closing,
+ gpointer data)
+{
+ GimpBrushSelectButton *button;
+ GimpBrushSelectButtonPrivate *priv;
+ GimpSelectButton *select_button;
+
+ button = GIMP_BRUSH_SELECT_BUTTON (data);
+
+ priv = GIMP_BRUSH_SELECT_BUTTON_GET_PRIVATE (button);
+ select_button = GIMP_SELECT_BUTTON (button);
+
+ g_free (priv->brush_name);
+ g_free (priv->mask_data);
+
+ priv->brush_name = g_strdup (name);
+ priv->width = width;
+ priv->height = height;
+ priv->mask_data = g_memdup (mask_data, width * height);
+ priv->opacity = opacity;
+ priv->spacing = spacing;
+ priv->paint_mode = paint_mode;
+
+ gimp_brush_select_preview_update (priv->preview,
+ width, height, mask_data);
+
+ if (dialog_closing)
+ select_button->temp_callback = NULL;
+
+ g_signal_emit (button, brush_button_signals[BRUSH_SET], 0,
+ name, opacity, spacing, paint_mode, width, height, mask_data,
+ dialog_closing);
+ g_object_notify (G_OBJECT (button), "brush-name");
+}
+
+static void
+gimp_brush_select_button_clicked (GimpBrushSelectButton *button)
+{
+ GimpBrushSelectButtonPrivate *priv;
+ GimpSelectButton *select_button;
+
+ priv = GIMP_BRUSH_SELECT_BUTTON_GET_PRIVATE (button);
+ select_button = GIMP_SELECT_BUTTON (button);
+
+ if (select_button->temp_callback)
+ {
+ /* calling gimp_brushes_set_popup() raises the dialog */
+ gimp_brushes_set_popup (select_button->temp_callback,
+ priv->brush_name,
+ priv->opacity,
+ priv->spacing,
+ priv->paint_mode);
+ }
+ else
+ {
+ select_button->temp_callback =
+ gimp_brush_select_new (priv->title, priv->brush_name,
+ priv->opacity, priv->spacing, priv->paint_mode,
+ gimp_brush_select_button_callback,
+ button);
+ }
+}
+
+static void
+gimp_brush_select_preview_resize (GimpBrushSelectButton *button)
+{
+ GimpBrushSelectButtonPrivate *priv;
+
+ priv = GIMP_BRUSH_SELECT_BUTTON_GET_PRIVATE (button);
+
+ if (priv->width > 0 && priv->height > 0)
+ gimp_brush_select_preview_update (priv->preview,
+ priv->width,
+ priv->height,
+ priv->mask_data);
+}
+
+static gboolean
+gimp_brush_select_preview_events (GtkWidget *widget,
+ GdkEvent *event,
+ GimpBrushSelectButton *button)
+{
+ GimpBrushSelectButtonPrivate *priv;
+ GdkEventButton *bevent;
+
+ priv = GIMP_BRUSH_SELECT_BUTTON_GET_PRIVATE (button);
+
+ if (priv->mask_data)
+ {
+ switch (event->type)
+ {
+ case GDK_BUTTON_PRESS:
+ bevent = (GdkEventButton *) event;
+
+ if (bevent->button == 1)
+ {
+ gtk_grab_add (widget);
+ gimp_brush_select_button_open_popup (button,
+ bevent->x, bevent->y);
+ }
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ bevent = (GdkEventButton *) event;
+
+ if (bevent->button == 1)
+ {
+ gtk_grab_remove (widget);
+ gimp_brush_select_button_close_popup (button);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+gimp_brush_select_preview_draw (GimpPreviewArea *area,
+ gint x,
+ gint y,
+ gint width,
+ gint height,
+ const guchar *mask_data,
+ gint rowstride)
+{
+ const guchar *src;
+ guchar *dest;
+ guchar *buf;
+ gint i, j;
+
+ buf = g_new (guchar, width * height);
+
+ src = mask_data;
+ dest = buf;
+
+ for (j = 0; j < height; j++)
+ {
+ const guchar *s = src;
+
+ for (i = 0; i < width; i++, s++, dest++)
+ *dest = 255 - *s;
+
+ src += rowstride;
+ }
+
+ gimp_preview_area_draw (area,
+ x, y, width, height,
+ GIMP_GRAY_IMAGE,
+ buf,
+ width);
+
+ g_free (buf);
+}
+
+static void
+gimp_brush_select_preview_update (GtkWidget *preview,
+ gint brush_width,
+ gint brush_height,
+ const guchar *mask_data)
+{
+ GimpPreviewArea *area = GIMP_PREVIEW_AREA (preview);
+ GtkAllocation allocation;
+ gint x, y;
+ gint width, height;
+
+ gtk_widget_get_allocation (preview, &allocation);
+
+ width = MIN (brush_width, allocation.width);
+ height = MIN (brush_height, allocation.height);
+
+ x = ((allocation.width - width) / 2);
+ y = ((allocation.height - height) / 2);
+
+ if (x || y)
+ gimp_preview_area_fill (area,
+ 0, 0,
+ allocation.width,
+ allocation.height,
+ 0xFF, 0xFF, 0xFF);
+
+ gimp_brush_select_preview_draw (area,
+ x, y, width, height,
+ mask_data, brush_width);
+}
+
+static void
+gimp_brush_select_button_open_popup (GimpBrushSelectButton *button,
+ gint x,
+ gint y)
+{
+ GimpBrushSelectButtonPrivate *priv;
+ GtkWidget *frame;
+ GtkWidget *preview;
+ GdkScreen *screen;
+ gint x_org;
+ gint y_org;
+ gint scr_w;
+ gint scr_h;
+
+ priv = GIMP_BRUSH_SELECT_BUTTON_GET_PRIVATE (button);
+
+ if (priv->popup)
+ gimp_brush_select_button_close_popup (button);
+
+ if (priv->width <= CELL_SIZE && priv->height <= CELL_SIZE)
+ return;
+
+ screen = gtk_widget_get_screen (GTK_WIDGET (button));
+
+ priv->popup = gtk_window_new (GTK_WINDOW_POPUP);
+ gtk_window_set_type_hint (GTK_WINDOW (priv->popup), GDK_WINDOW_TYPE_HINT_DND);
+ gtk_window_set_screen (GTK_WINDOW (priv->popup), screen);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
+ gtk_container_add (GTK_CONTAINER (priv->popup), frame);
+ gtk_widget_show (frame);
+
+ preview = gimp_preview_area_new ();
+ gtk_widget_set_size_request (preview, priv->width, priv->height);
+ gtk_container_add (GTK_CONTAINER (frame), preview);
+ gtk_widget_show (preview);
+
+ /* decide where to put the popup */
+ gdk_window_get_origin (gtk_widget_get_window (priv->preview),
+ &x_org, &y_org);
+
+ scr_w = gdk_screen_get_width (screen);
+ scr_h = gdk_screen_get_height (screen);
+
+ x = x_org + x - (priv->width / 2);
+ y = y_org + y - (priv->height / 2);
+ x = (x < 0) ? 0 : x;
+ y = (y < 0) ? 0 : y;
+ x = (x + priv->width > scr_w) ? scr_w - priv->width : x;
+ y = (y + priv->height > scr_h) ? scr_h - priv->height : y;
+
+ gtk_window_move (GTK_WINDOW (priv->popup), x, y);
+
+ gtk_widget_show (priv->popup);
+
+ /* Draw the brush */
+ gimp_brush_select_preview_draw (GIMP_PREVIEW_AREA (preview),
+ 0, 0, priv->width, priv->height,
+ priv->mask_data, priv->width);
+}
+
+static void
+gimp_brush_select_button_close_popup (GimpBrushSelectButton *button)
+{
+ GimpBrushSelectButtonPrivate *priv;
+
+ priv = GIMP_BRUSH_SELECT_BUTTON_GET_PRIVATE (button);
+
+ if (priv->popup)
+ {
+ gtk_widget_destroy (priv->popup);
+ priv->popup = NULL;
+ }
+}
+
+static void
+gimp_brush_select_drag_data_received (GimpBrushSelectButton *button,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time)
+{
+ gint length = gtk_selection_data_get_length (selection);
+ gchar *str;
+
+ if (gtk_selection_data_get_format (selection) != 8 || length < 1)
+ {
+ g_warning ("%s: received invalid brush data", G_STRFUNC);
+ return;
+ }
+
+ str = g_strndup ((const gchar *) gtk_selection_data_get_data (selection),
+ length);
+
+ if (g_utf8_validate (str, -1, NULL))
+ {
+ gint pid;
+ gpointer unused;
+ gint name_offset = 0;
+
+ if (sscanf (str, "%i:%p:%n", &pid, &unused, &name_offset) >= 2 &&
+ pid == gimp_getpid () && name_offset > 0)
+ {
+ gchar *name = str + name_offset;
+
+ gimp_brush_select_button_set_brush (button, name, -1.0, -1, -1);
+ }
+ }
+
+ g_free (str);
+}
+
+static GtkWidget *
+gimp_brush_select_button_create_inside (GimpBrushSelectButton *brush_button)
+{
+ GtkWidget *hbox;
+ GtkWidget *frame;
+ GtkWidget *button;
+ GimpBrushSelectButtonPrivate *priv;
+
+ priv = GIMP_BRUSH_SELECT_BUTTON_GET_PRIVATE (brush_button);
+
+ gtk_widget_push_composite_child ();
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
+
+ priv->preview = gimp_preview_area_new ();
+ gtk_widget_add_events (priv->preview,
+ GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
+ gtk_widget_set_size_request (priv->preview, CELL_SIZE, CELL_SIZE);
+ gtk_container_add (GTK_CONTAINER (frame), priv->preview);
+
+ g_signal_connect_swapped (priv->preview, "size-allocate",
+ G_CALLBACK (gimp_brush_select_preview_resize),
+ brush_button);
+ g_signal_connect (priv->preview, "event",
+ G_CALLBACK (gimp_brush_select_preview_events),
+ brush_button);
+
+ gtk_drag_dest_set (GTK_WIDGET (priv->preview),
+ GTK_DEST_DEFAULT_HIGHLIGHT |
+ GTK_DEST_DEFAULT_MOTION |
+ GTK_DEST_DEFAULT_DROP,
+ &target, 1,
+ GDK_ACTION_COPY);
+
+ g_signal_connect_swapped (priv->preview, "drag-data-received",
+ G_CALLBACK (gimp_brush_select_drag_data_received),
+ brush_button);
+
+ button = gtk_button_new_with_mnemonic (_("_Browse..."));
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (gimp_brush_select_button_clicked),
+ brush_button);
+
+ gtk_widget_show_all (hbox);
+
+ gtk_widget_pop_composite_child ();
+
+ return hbox;
+}
diff --git a/libgimp/gimpbrushselectbutton.h b/libgimp/gimpbrushselectbutton.h
new file mode 100644
index 0000000..8bc3a28
--- /dev/null
+++ b/libgimp/gimpbrushselectbutton.h
@@ -0,0 +1,94 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpbrushselectbutton.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_UI_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimpui.h> can be included directly."
+#endif
+
+#ifndef __GIMP_BRUSH_SELECT_BUTTON_H__
+#define __GIMP_BRUSH_SELECT_BUTTON_H__
+
+#include <libgimp/gimpselectbutton.h>
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+#define GIMP_TYPE_BRUSH_SELECT_BUTTON (gimp_brush_select_button_get_type ())
+#define GIMP_BRUSH_SELECT_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_BRUSH_SELECT_BUTTON, GimpBrushSelectButton))
+#define GIMP_BRUSH_SELECT_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_BRUSH_SELECT_BUTTON, GimpBrushSelectButtonClass))
+#define GIMP_IS_BRUSH_SELECT_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_BRUSH_SELECT_BUTTON))
+#define GIMP_IS_BRUSH_SELECT_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_BRUSH_SELECT_BUTTON))
+#define GIMP_BRUSH_SELECT_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_BRUSH_SELECT_BUTTON, GimpBrushSelectButtonClass))
+
+
+typedef struct _GimpBrushSelectButtonClass GimpBrushSelectButtonClass;
+
+struct _GimpBrushSelectButton
+{
+ GimpSelectButton parent_instance;
+};
+
+struct _GimpBrushSelectButtonClass
+{
+ GimpSelectButtonClass parent_class;
+
+ /* brush_set signal is emitted when brush is chosen */
+ void (* brush_set) (GimpBrushSelectButton *button,
+ const gchar *brush_name,
+ gdouble opacity,
+ gint spacing,
+ GimpLayerMode paint_mode,
+ gint width,
+ gint height,
+ const guchar *mask_data,
+ gboolean dialog_closing);
+
+ /* Padding for future expansion */
+ void (*_gimp_reserved1) (void);
+ void (*_gimp_reserved2) (void);
+ void (*_gimp_reserved3) (void);
+ void (*_gimp_reserved4) (void);
+};
+
+
+GType gimp_brush_select_button_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_brush_select_button_new (const gchar *title,
+ const gchar *brush_name,
+ gdouble opacity,
+ gint spacing,
+ GimpLayerMode paint_mode);
+
+const gchar * gimp_brush_select_button_get_brush (GimpBrushSelectButton *button,
+ gdouble *opacity,
+ gint *spacing,
+ GimpLayerMode *paint_mode);
+void gimp_brush_select_button_set_brush (GimpBrushSelectButton *button,
+ const gchar *brush_name,
+ gdouble opacity,
+ gint spacing,
+ GimpLayerMode paint_mode);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_BRUSH_SELECT_BUTTON_H__ */
diff --git a/libgimp/gimpbuffer_pdb.c b/libgimp/gimpbuffer_pdb.c
new file mode 100644
index 0000000..afe8ee0
--- /dev/null
+++ b/libgimp/gimpbuffer_pdb.c
@@ -0,0 +1,276 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpbuffer_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpbuffer
+ * @title: gimpbuffer
+ * @short_description: Functions for manipulating cut buffers.
+ *
+ * Functions related to named cut buffers.
+ **/
+
+
+/**
+ * gimp_buffers_get_list:
+ * @filter: An optional regular expression used to filter the list.
+ * @num_buffers: The number of buffers.
+ *
+ * Retrieve a complete listing of the available buffers.
+ *
+ * This procedure returns a complete listing of available named
+ * buffers.
+ *
+ * Returns: The list of buffer names. The returned value must be freed
+ * with g_strfreev().
+ *
+ * Since: 2.4
+ **/
+gchar **
+gimp_buffers_get_list (const gchar *filter,
+ gint *num_buffers)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar **buffer_list = NULL;
+ gint i;
+
+ return_vals = gimp_run_procedure ("gimp-buffers-get-list",
+ &nreturn_vals,
+ GIMP_PDB_STRING, filter,
+ GIMP_PDB_END);
+
+ *num_buffers = 0;
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ *num_buffers = return_vals[1].data.d_int32;
+ if (*num_buffers > 0)
+ {
+ buffer_list = g_new0 (gchar *, *num_buffers + 1);
+ for (i = 0; i < *num_buffers; i++)
+ buffer_list[i] = g_strdup (return_vals[2].data.d_stringarray[i]);
+ }
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return buffer_list;
+}
+
+/**
+ * gimp_buffer_rename:
+ * @buffer_name: The buffer name.
+ * @new_name: The buffer's new name.
+ *
+ * Renames a named buffer.
+ *
+ * This procedure renames a named buffer.
+ *
+ * Returns: The real name given to the buffer.
+ *
+ * Since: 2.4
+ **/
+gchar *
+gimp_buffer_rename (const gchar *buffer_name,
+ const gchar *new_name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *real_name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-buffer-rename",
+ &nreturn_vals,
+ GIMP_PDB_STRING, buffer_name,
+ GIMP_PDB_STRING, new_name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ real_name = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return real_name;
+}
+
+/**
+ * gimp_buffer_delete:
+ * @buffer_name: The buffer name.
+ *
+ * Deletes a named buffer.
+ *
+ * This procedure deletes a named buffer.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_buffer_delete (const gchar *buffer_name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-buffer-delete",
+ &nreturn_vals,
+ GIMP_PDB_STRING, buffer_name,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_buffer_get_width:
+ * @buffer_name: The buffer name.
+ *
+ * Retrieves the specified buffer's width.
+ *
+ * This procedure retrieves the specified named buffer's width.
+ *
+ * Returns: The buffer width.
+ *
+ * Since: 2.4
+ **/
+gint
+gimp_buffer_get_width (const gchar *buffer_name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint width = 0;
+
+ return_vals = gimp_run_procedure ("gimp-buffer-get-width",
+ &nreturn_vals,
+ GIMP_PDB_STRING, buffer_name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ width = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return width;
+}
+
+/**
+ * gimp_buffer_get_height:
+ * @buffer_name: The buffer name.
+ *
+ * Retrieves the specified buffer's height.
+ *
+ * This procedure retrieves the specified named buffer's height.
+ *
+ * Returns: The buffer height.
+ *
+ * Since: 2.4
+ **/
+gint
+gimp_buffer_get_height (const gchar *buffer_name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint height = 0;
+
+ return_vals = gimp_run_procedure ("gimp-buffer-get-height",
+ &nreturn_vals,
+ GIMP_PDB_STRING, buffer_name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ height = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return height;
+}
+
+/**
+ * gimp_buffer_get_bytes:
+ * @buffer_name: The buffer name.
+ *
+ * Retrieves the specified buffer's bytes.
+ *
+ * This procedure retrieves the specified named buffer's bytes.
+ *
+ * Returns: The buffer bpp.
+ *
+ * Since: 2.4
+ **/
+gint
+gimp_buffer_get_bytes (const gchar *buffer_name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint bytes = 0;
+
+ return_vals = gimp_run_procedure ("gimp-buffer-get-bytes",
+ &nreturn_vals,
+ GIMP_PDB_STRING, buffer_name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ bytes = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return bytes;
+}
+
+/**
+ * gimp_buffer_get_image_type:
+ * @buffer_name: The buffer name.
+ *
+ * Retrieves the specified buffer's image type.
+ *
+ * This procedure retrieves the specified named buffer's image type.
+ *
+ * Returns: The buffer image type.
+ *
+ * Since: 2.4
+ **/
+GimpImageBaseType
+gimp_buffer_get_image_type (const gchar *buffer_name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpImageBaseType image_type = 0;
+
+ return_vals = gimp_run_procedure ("gimp-buffer-get-image-type",
+ &nreturn_vals,
+ GIMP_PDB_STRING, buffer_name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ image_type = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return image_type;
+}
diff --git a/libgimp/gimpbuffer_pdb.h b/libgimp/gimpbuffer_pdb.h
new file mode 100644
index 0000000..83b9196
--- /dev/null
+++ b/libgimp/gimpbuffer_pdb.h
@@ -0,0 +1,48 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpbuffer_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_BUFFER_PDB_H__
+#define __GIMP_BUFFER_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gchar** gimp_buffers_get_list (const gchar *filter,
+ gint *num_buffers);
+gchar* gimp_buffer_rename (const gchar *buffer_name,
+ const gchar *new_name);
+gboolean gimp_buffer_delete (const gchar *buffer_name);
+gint gimp_buffer_get_width (const gchar *buffer_name);
+gint gimp_buffer_get_height (const gchar *buffer_name);
+gint gimp_buffer_get_bytes (const gchar *buffer_name);
+GimpImageBaseType gimp_buffer_get_image_type (const gchar *buffer_name);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_BUFFER_PDB_H__ */
diff --git a/libgimp/gimpchannel.c b/libgimp/gimpchannel.c
new file mode 100644
index 0000000..00a45f2
--- /dev/null
+++ b/libgimp/gimpchannel.c
@@ -0,0 +1,61 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpchannel.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * gimp_channel_new:
+ * @image_ID: The image to which to add the channel.
+ * @name: The channel name.
+ * @width: The channel width.
+ * @height: The channel height.
+ * @opacity: The channel opacity.
+ * @color: The channel compositing color.
+ *
+ * Create a new channel.
+ *
+ * This procedure creates a new channel with the specified width and
+ * height. Name, opacity, and color are also supplied parameters. The
+ * new channel still needs to be added to the image, as this is not
+ * automatic. Add the new channel with the gimp_image_insert_channel()
+ * command. Other attributes such as channel show masked, should be
+ * set with explicit procedure calls. The channel's contents are
+ * undefined initially.
+ *
+ * Returns: The newly created channel.
+ */
+gint32
+gimp_channel_new (gint32 image_ID,
+ const gchar *name,
+ guint width,
+ guint height,
+ gdouble opacity,
+ const GimpRGB *color)
+{
+ return _gimp_channel_new (image_ID,
+ width,
+ height,
+ name,
+ opacity,
+ color);
+}
diff --git a/libgimp/gimpchannel.h b/libgimp/gimpchannel.h
new file mode 100644
index 0000000..47b141b
--- /dev/null
+++ b/libgimp/gimpchannel.h
@@ -0,0 +1,44 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpchannel.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_CHANNEL_H__
+#define __GIMP_CHANNEL_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gint32 gimp_channel_new (gint32 image_ID,
+ const gchar *name,
+ guint width,
+ guint height,
+ gdouble opacity,
+ const GimpRGB *color);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_CHANNEL_H__ */
+
diff --git a/libgimp/gimpchannel_pdb.c b/libgimp/gimpchannel_pdb.c
new file mode 100644
index 0000000..a1b1a85
--- /dev/null
+++ b/libgimp/gimpchannel_pdb.c
@@ -0,0 +1,396 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpchannel_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpchannel
+ * @title: gimpchannel
+ * @short_description: Functions for manipulating channels.
+ *
+ * Functions for manipulating channels.
+ **/
+
+
+/**
+ * _gimp_channel_new:
+ * @image_ID: The image to which to add the channel.
+ * @width: The channel width.
+ * @height: The channel height.
+ * @name: The channel name.
+ * @opacity: The channel opacity.
+ * @color: The channel compositing color.
+ *
+ * Create a new channel.
+ *
+ * This procedure creates a new channel with the specified width,
+ * height, name, opacity and color.
+ * The new channel still needs to be added to the image, as this is not
+ * automatic. Add the new channel with gimp_image_insert_channel().
+ * Other attributes, such as channel visibility, should be set with
+ * explicit procedure calls.
+ * The channel's contents are undefined initially.
+ *
+ * Returns: The newly created channel.
+ **/
+gint32
+_gimp_channel_new (gint32 image_ID,
+ gint width,
+ gint height,
+ const gchar *name,
+ gdouble opacity,
+ const GimpRGB *color)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 channel_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-channel-new",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, width,
+ GIMP_PDB_INT32, height,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_FLOAT, opacity,
+ GIMP_PDB_COLOR, color,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ channel_ID = return_vals[1].data.d_channel;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return channel_ID;
+}
+
+/**
+ * gimp_channel_new_from_component:
+ * @image_ID: The image to which to add the channel.
+ * @component: The image component.
+ * @name: The channel name.
+ *
+ * Create a new channel from a color component
+ *
+ * This procedure creates a new channel from a color component.
+ * The new channel still needs to be added to the image, as this is not
+ * automatic. Add the new channel with gimp_image_insert_channel().
+ * Other attributes, such as channel visibility, should be set with
+ * explicit procedure calls.
+ *
+ * Returns: The newly created channel.
+ *
+ * Since: 2.4
+ **/
+gint32
+gimp_channel_new_from_component (gint32 image_ID,
+ GimpChannelType component,
+ const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 channel_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-channel-new-from-component",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, component,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ channel_ID = return_vals[1].data.d_channel;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return channel_ID;
+}
+
+/**
+ * gimp_channel_copy:
+ * @channel_ID: The channel to copy.
+ *
+ * Copy a channel.
+ *
+ * This procedure copies the specified channel and returns the copy.
+ * The new channel still needs to be added to the image, as this is not
+ * automatic. Add the new channel with gimp_image_insert_channel().
+ *
+ * Returns: The newly copied channel.
+ **/
+gint32
+gimp_channel_copy (gint32 channel_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 channel_copy_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-channel-copy",
+ &nreturn_vals,
+ GIMP_PDB_CHANNEL, channel_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ channel_copy_ID = return_vals[1].data.d_channel;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return channel_copy_ID;
+}
+
+/**
+ * gimp_channel_combine_masks:
+ * @channel1_ID: The channel1.
+ * @channel2_ID: The channel2.
+ * @operation: The selection operation.
+ * @offx: x offset between upper left corner of channels: (second - first).
+ * @offy: y offset between upper left corner of channels: (second - first).
+ *
+ * Combine two channel masks.
+ *
+ * This procedure combines two channel masks. The result is stored in
+ * the first channel.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_channel_combine_masks (gint32 channel1_ID,
+ gint32 channel2_ID,
+ GimpChannelOps operation,
+ gint offx,
+ gint offy)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-channel-combine-masks",
+ &nreturn_vals,
+ GIMP_PDB_CHANNEL, channel1_ID,
+ GIMP_PDB_CHANNEL, channel2_ID,
+ GIMP_PDB_INT32, operation,
+ GIMP_PDB_INT32, offx,
+ GIMP_PDB_INT32, offy,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_channel_get_show_masked:
+ * @channel_ID: The channel.
+ *
+ * Get the composite method of the specified channel.
+ *
+ * This procedure returns the specified channel's composite method. If
+ * it is TRUE, then the channel is composited with the image so that
+ * masked regions are shown. Otherwise, selected regions are shown.
+ *
+ * Returns: The channel composite method.
+ **/
+gboolean
+gimp_channel_get_show_masked (gint32 channel_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean show_masked = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-channel-get-show-masked",
+ &nreturn_vals,
+ GIMP_PDB_CHANNEL, channel_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ show_masked = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return show_masked;
+}
+
+/**
+ * gimp_channel_set_show_masked:
+ * @channel_ID: The channel.
+ * @show_masked: The new channel composite method.
+ *
+ * Set the composite method of the specified channel.
+ *
+ * This procedure sets the specified channel's composite method. If it
+ * is TRUE, then the channel is composited with the image so that
+ * masked regions are shown. Otherwise, selected regions are shown.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_channel_set_show_masked (gint32 channel_ID,
+ gboolean show_masked)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-channel-set-show-masked",
+ &nreturn_vals,
+ GIMP_PDB_CHANNEL, channel_ID,
+ GIMP_PDB_INT32, show_masked,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_channel_get_opacity:
+ * @channel_ID: The channel.
+ *
+ * Get the opacity of the specified channel.
+ *
+ * This procedure returns the specified channel's opacity.
+ *
+ * Returns: The channel opacity.
+ **/
+gdouble
+gimp_channel_get_opacity (gint32 channel_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble opacity = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-channel-get-opacity",
+ &nreturn_vals,
+ GIMP_PDB_CHANNEL, channel_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ opacity = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return opacity;
+}
+
+/**
+ * gimp_channel_set_opacity:
+ * @channel_ID: The channel.
+ * @opacity: The new channel opacity.
+ *
+ * Set the opacity of the specified channel.
+ *
+ * This procedure sets the specified channel's opacity.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_channel_set_opacity (gint32 channel_ID,
+ gdouble opacity)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-channel-set-opacity",
+ &nreturn_vals,
+ GIMP_PDB_CHANNEL, channel_ID,
+ GIMP_PDB_FLOAT, opacity,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_channel_get_color:
+ * @channel_ID: The channel.
+ * @color: The channel compositing color.
+ *
+ * Get the compositing color of the specified channel.
+ *
+ * This procedure returns the specified channel's compositing color.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_channel_get_color (gint32 channel_ID,
+ GimpRGB *color)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-channel-get-color",
+ &nreturn_vals,
+ GIMP_PDB_CHANNEL, channel_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ *color = return_vals[1].data.d_color;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_channel_set_color:
+ * @channel_ID: The channel.
+ * @color: The new channel compositing color.
+ *
+ * Set the compositing color of the specified channel.
+ *
+ * This procedure sets the specified channel's compositing color.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_channel_set_color (gint32 channel_ID,
+ const GimpRGB *color)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-channel-set-color",
+ &nreturn_vals,
+ GIMP_PDB_CHANNEL, channel_ID,
+ GIMP_PDB_COLOR, color,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimpchannel_pdb.h b/libgimp/gimpchannel_pdb.h
new file mode 100644
index 0000000..84fa4af
--- /dev/null
+++ b/libgimp/gimpchannel_pdb.h
@@ -0,0 +1,64 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpchannel_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_CHANNEL_PDB_H__
+#define __GIMP_CHANNEL_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+G_GNUC_INTERNAL gint32 _gimp_channel_new (gint32 image_ID,
+ gint width,
+ gint height,
+ const gchar *name,
+ gdouble opacity,
+ const GimpRGB *color);
+gint32 gimp_channel_new_from_component (gint32 image_ID,
+ GimpChannelType component,
+ const gchar *name);
+gint32 gimp_channel_copy (gint32 channel_ID);
+gboolean gimp_channel_combine_masks (gint32 channel1_ID,
+ gint32 channel2_ID,
+ GimpChannelOps operation,
+ gint offx,
+ gint offy);
+gboolean gimp_channel_get_show_masked (gint32 channel_ID);
+gboolean gimp_channel_set_show_masked (gint32 channel_ID,
+ gboolean show_masked);
+gdouble gimp_channel_get_opacity (gint32 channel_ID);
+gboolean gimp_channel_set_opacity (gint32 channel_ID,
+ gdouble opacity);
+gboolean gimp_channel_get_color (gint32 channel_ID,
+ GimpRGB *color);
+gboolean gimp_channel_set_color (gint32 channel_ID,
+ const GimpRGB *color);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_CHANNEL_PDB_H__ */
diff --git a/libgimp/gimpcolor_pdb.c b/libgimp/gimpcolor_pdb.c
new file mode 100644
index 0000000..aea8572
--- /dev/null
+++ b/libgimp/gimpcolor_pdb.c
@@ -0,0 +1,611 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpcolor_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpcolor
+ * @title: gimpcolor
+ * @short_description: Functions for manipulating color.
+ *
+ * Functions for manipulating color, including curves and histograms.
+ **/
+
+
+/**
+ * gimp_brightness_contrast:
+ * @drawable_ID: The drawable.
+ * @brightness: Brightness adjustment.
+ * @contrast: Contrast adjustment.
+ *
+ * Deprecated: Use gimp_drawable_brightness_contrast() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_brightness_contrast (gint32 drawable_ID,
+ gint brightness,
+ gint contrast)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-brightness-contrast",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, brightness,
+ GIMP_PDB_INT32, contrast,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_levels:
+ * @drawable_ID: The drawable.
+ * @channel: The channel to modify.
+ * @low_input: Intensity of lowest input.
+ * @high_input: Intensity of highest input.
+ * @gamma: Gamma adjustment factor.
+ * @low_output: Intensity of lowest output.
+ * @high_output: Intensity of highest output.
+ *
+ * Deprecated: Use gimp_drawable_levels() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_levels (gint32 drawable_ID,
+ GimpHistogramChannel channel,
+ gint low_input,
+ gint high_input,
+ gdouble gamma,
+ gint low_output,
+ gint high_output)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-levels",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, channel,
+ GIMP_PDB_INT32, low_input,
+ GIMP_PDB_INT32, high_input,
+ GIMP_PDB_FLOAT, gamma,
+ GIMP_PDB_INT32, low_output,
+ GIMP_PDB_INT32, high_output,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_levels_auto:
+ * @drawable_ID: The drawable.
+ *
+ * Deprecated: Use gimp_drawable_levels_stretch() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_levels_auto (gint32 drawable_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-levels-auto",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_levels_stretch:
+ * @drawable_ID: The drawable.
+ *
+ * Deprecated: Use gimp_drawable_levels_stretch() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_levels_stretch (gint32 drawable_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-levels-stretch",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_posterize:
+ * @drawable_ID: The drawable.
+ * @levels: Levels of posterization.
+ *
+ * Deprecated: Use gimp_drawable_posterize() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_posterize (gint32 drawable_ID,
+ gint levels)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-posterize",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, levels,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_desaturate:
+ * @drawable_ID: The drawable.
+ *
+ * Deprecated: Use gimp_drawable_desaturate() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_desaturate (gint32 drawable_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-desaturate",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_desaturate_full:
+ * @drawable_ID: The drawable.
+ * @desaturate_mode: The formula to use to desaturate.
+ *
+ * Deprecated: Use gimp_drawable_desaturate() instead.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_desaturate_full (gint32 drawable_ID,
+ GimpDesaturateMode desaturate_mode)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-desaturate-full",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, desaturate_mode,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_equalize:
+ * @drawable_ID: The drawable.
+ * @mask_only: Equalization option.
+ *
+ * Deprecated: Use gimp_drawable_equalize() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_equalize (gint32 drawable_ID,
+ gboolean mask_only)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-equalize",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, mask_only,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_invert:
+ * @drawable_ID: The drawable.
+ *
+ * Deprecated: Use gimp_drawable_invert() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_invert (gint32 drawable_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-invert",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_curves_spline:
+ * @drawable_ID: The drawable.
+ * @channel: The channel to modify.
+ * @num_points: The number of values in the control point array.
+ * @control_pts: The spline control points: { cp1.x, cp1.y, cp2.x, cp2.y, ... }.
+ *
+ * Deprecated: Use gimp_drawable_curves_spline() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_curves_spline (gint32 drawable_ID,
+ GimpHistogramChannel channel,
+ gint num_points,
+ const guint8 *control_pts)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-curves-spline",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, channel,
+ GIMP_PDB_INT32, num_points,
+ GIMP_PDB_INT8ARRAY, control_pts,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_curves_explicit:
+ * @drawable_ID: The drawable.
+ * @channel: The channel to modify.
+ * @num_bytes: The number of bytes in the new curve (always 256).
+ * @curve: The explicit curve.
+ *
+ * Deprecated: Use gimp_drawable_curves_explicit() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_curves_explicit (gint32 drawable_ID,
+ GimpHistogramChannel channel,
+ gint num_bytes,
+ const guint8 *curve)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-curves-explicit",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, channel,
+ GIMP_PDB_INT32, num_bytes,
+ GIMP_PDB_INT8ARRAY, curve,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_color_balance:
+ * @drawable_ID: The drawable.
+ * @transfer_mode: Transfer mode.
+ * @preserve_lum: Preserve luminosity values at each pixel.
+ * @cyan_red: Cyan-Red color balance.
+ * @magenta_green: Magenta-Green color balance.
+ * @yellow_blue: Yellow-Blue color balance.
+ *
+ * Modify the color balance of the specified drawable.
+ *
+ * Modify the color balance of the specified drawable. There are three
+ * axis which can be modified: cyan-red, magenta-green, and
+ * yellow-blue. Negative values increase the amount of the former,
+ * positive values increase the amount of the latter. Color balance can
+ * be controlled with the 'transfer_mode' setting, which allows
+ * shadows, mid-tones, and highlights in an image to be affected
+ * differently. The 'preserve-lum' parameter, if TRUE, ensures that the
+ * luminosity of each pixel remains fixed.
+ *
+ * Deprecated: Use gimp_drawable_color_color_balance() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_color_balance (gint32 drawable_ID,
+ GimpTransferMode transfer_mode,
+ gboolean preserve_lum,
+ gdouble cyan_red,
+ gdouble magenta_green,
+ gdouble yellow_blue)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-color-balance",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, transfer_mode,
+ GIMP_PDB_INT32, preserve_lum,
+ GIMP_PDB_FLOAT, cyan_red,
+ GIMP_PDB_FLOAT, magenta_green,
+ GIMP_PDB_FLOAT, yellow_blue,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_colorize:
+ * @drawable_ID: The drawable.
+ * @hue: Hue in degrees.
+ * @saturation: Saturation in percent.
+ * @lightness: Lightness in percent.
+ *
+ * Deprecated: Use gimp_drawable_colorize_hsl() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_colorize (gint32 drawable_ID,
+ gdouble hue,
+ gdouble saturation,
+ gdouble lightness)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-colorize",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_FLOAT, hue,
+ GIMP_PDB_FLOAT, saturation,
+ GIMP_PDB_FLOAT, lightness,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_histogram:
+ * @drawable_ID: The drawable.
+ * @channel: The channel to modify.
+ * @start_range: Start of the intensity measurement range.
+ * @end_range: End of the intensity measurement range.
+ * @mean: Mean intensity value.
+ * @std_dev: Standard deviation of intensity values.
+ * @median: Median intensity value.
+ * @pixels: Alpha-weighted pixel count for entire image.
+ * @count: Alpha-weighted pixel count for range.
+ * @percentile: Percentile that range falls under.
+ *
+ * Deprecated: Use gimp_drawable_histogram() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_histogram (gint32 drawable_ID,
+ GimpHistogramChannel channel,
+ gint start_range,
+ gint end_range,
+ gdouble *mean,
+ gdouble *std_dev,
+ gdouble *median,
+ gdouble *pixels,
+ gdouble *count,
+ gdouble *percentile)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-histogram",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, channel,
+ GIMP_PDB_INT32, start_range,
+ GIMP_PDB_INT32, end_range,
+ GIMP_PDB_END);
+
+ *mean = 0.0;
+ *std_dev = 0.0;
+ *median = 0.0;
+ *pixels = 0.0;
+ *count = 0.0;
+ *percentile = 0.0;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *mean = return_vals[1].data.d_float;
+ *std_dev = return_vals[2].data.d_float;
+ *median = return_vals[3].data.d_float;
+ *pixels = return_vals[4].data.d_float;
+ *count = return_vals[5].data.d_float;
+ *percentile = return_vals[6].data.d_float;
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_hue_saturation:
+ * @drawable_ID: The drawable.
+ * @hue_range: Range of affected hues.
+ * @hue_offset: Hue offset in degrees.
+ * @lightness: Lightness modification.
+ * @saturation: Saturation modification.
+ *
+ * Deprecated: Use gimp_drawable_hue_saturation() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_hue_saturation (gint32 drawable_ID,
+ GimpHueRange hue_range,
+ gdouble hue_offset,
+ gdouble lightness,
+ gdouble saturation)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-hue-saturation",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, hue_range,
+ GIMP_PDB_FLOAT, hue_offset,
+ GIMP_PDB_FLOAT, lightness,
+ GIMP_PDB_FLOAT, saturation,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_threshold:
+ * @drawable_ID: The drawable.
+ * @low_threshold: The low threshold value.
+ * @high_threshold: The high threshold value.
+ *
+ * Deprecated: Use gimp_drawable_threshold() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_threshold (gint32 drawable_ID,
+ gint low_threshold,
+ gint high_threshold)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-threshold",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, low_threshold,
+ GIMP_PDB_INT32, high_threshold,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimpcolor_pdb.h b/libgimp/gimpcolor_pdb.h
new file mode 100644
index 0000000..ef53d9e
--- /dev/null
+++ b/libgimp/gimpcolor_pdb.h
@@ -0,0 +1,111 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpcolor_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_COLOR_PDB_H__
+#define __GIMP_COLOR_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+GIMP_DEPRECATED_FOR(gimp_drawable_brightness_contrast)
+gboolean gimp_brightness_contrast (gint32 drawable_ID,
+ gint brightness,
+ gint contrast);
+GIMP_DEPRECATED_FOR(gimp_drawable_levels)
+gboolean gimp_levels (gint32 drawable_ID,
+ GimpHistogramChannel channel,
+ gint low_input,
+ gint high_input,
+ gdouble gamma,
+ gint low_output,
+ gint high_output);
+GIMP_DEPRECATED_FOR(gimp_drawable_levels_stretch)
+gboolean gimp_levels_auto (gint32 drawable_ID);
+GIMP_DEPRECATED_FOR(gimp_drawable_levels_stretch)
+gboolean gimp_levels_stretch (gint32 drawable_ID);
+GIMP_DEPRECATED_FOR(gimp_drawable_posterize)
+gboolean gimp_posterize (gint32 drawable_ID,
+ gint levels);
+GIMP_DEPRECATED_FOR(gimp_drawable_desaturate)
+gboolean gimp_desaturate (gint32 drawable_ID);
+GIMP_DEPRECATED_FOR(gimp_drawable_desaturate)
+gboolean gimp_desaturate_full (gint32 drawable_ID,
+ GimpDesaturateMode desaturate_mode);
+GIMP_DEPRECATED_FOR(gimp_drawable_equalize)
+gboolean gimp_equalize (gint32 drawable_ID,
+ gboolean mask_only);
+GIMP_DEPRECATED_FOR(gimp_drawable_invert)
+gboolean gimp_invert (gint32 drawable_ID);
+GIMP_DEPRECATED_FOR(gimp_drawable_curves_spline)
+gboolean gimp_curves_spline (gint32 drawable_ID,
+ GimpHistogramChannel channel,
+ gint num_points,
+ const guint8 *control_pts);
+GIMP_DEPRECATED_FOR(gimp_drawable_curves_explicit)
+gboolean gimp_curves_explicit (gint32 drawable_ID,
+ GimpHistogramChannel channel,
+ gint num_bytes,
+ const guint8 *curve);
+GIMP_DEPRECATED_FOR(gimp_drawable_color_color_balance)
+gboolean gimp_color_balance (gint32 drawable_ID,
+ GimpTransferMode transfer_mode,
+ gboolean preserve_lum,
+ gdouble cyan_red,
+ gdouble magenta_green,
+ gdouble yellow_blue);
+GIMP_DEPRECATED_FOR(gimp_drawable_colorize_hsl)
+gboolean gimp_colorize (gint32 drawable_ID,
+ gdouble hue,
+ gdouble saturation,
+ gdouble lightness);
+GIMP_DEPRECATED_FOR(gimp_drawable_histogram)
+gboolean gimp_histogram (gint32 drawable_ID,
+ GimpHistogramChannel channel,
+ gint start_range,
+ gint end_range,
+ gdouble *mean,
+ gdouble *std_dev,
+ gdouble *median,
+ gdouble *pixels,
+ gdouble *count,
+ gdouble *percentile);
+GIMP_DEPRECATED_FOR(gimp_drawable_hue_saturation)
+gboolean gimp_hue_saturation (gint32 drawable_ID,
+ GimpHueRange hue_range,
+ gdouble hue_offset,
+ gdouble lightness,
+ gdouble saturation);
+GIMP_DEPRECATED_FOR(gimp_drawable_threshold)
+gboolean gimp_threshold (gint32 drawable_ID,
+ gint low_threshold,
+ gint high_threshold);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_COLOR_PDB_H__ */
diff --git a/libgimp/gimpcontext_pdb.c b/libgimp/gimpcontext_pdb.c
new file mode 100644
index 0000000..92f71e2
--- /dev/null
+++ b/libgimp/gimpcontext_pdb.c
@@ -0,0 +1,3812 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpcontext_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpcontext
+ * @title: gimpcontext
+ * @short_description: Functions to manipulate a plug-in's context.
+ *
+ * Functions to manipulate a plug-in's context.
+ **/
+
+
+/**
+ * gimp_context_push:
+ *
+ * Pushes a context to the top of the plug-in's context stack.
+ *
+ * This procedure creates a new context by copying the current context.
+ * This copy becomes the new current context for the calling plug-in
+ * until it is popped again using gimp_context_pop().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_context_push (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-push",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_pop:
+ *
+ * Pops the topmost context from the plug-in's context stack.
+ *
+ * This procedure removes the topmost context from the plug-in's
+ * context stack. The context that was active before the corresponding
+ * call to gimp_context_push() becomes the new current context of the
+ * plug-in.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_context_pop (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-pop",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_set_defaults:
+ *
+ * Reset context settings to their default values.
+ *
+ * This procedure resets context settings used by various procedures to
+ * their default value. This procedure will usually be called after a
+ * context push so that a script which calls procedures affected by
+ * context settings will not be affected by changes in the global
+ * context.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_set_defaults (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-defaults",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_list_paint_methods:
+ * @num_paint_methods: The number of the available paint methods.
+ * @paint_methods: The names of the available paint methods.
+ *
+ * Lists the available paint methods.
+ *
+ * This procedure lists the names of the available paint methods. Any
+ * of the results can be used for gimp_context_set_paint_method().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_context_list_paint_methods (gint *num_paint_methods,
+ gchar ***paint_methods)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+ gint i;
+
+ return_vals = gimp_run_procedure ("gimp-context-list-paint-methods",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ *num_paint_methods = 0;
+ *paint_methods = NULL;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *num_paint_methods = return_vals[1].data.d_int32;
+ if (*num_paint_methods > 0)
+ {
+ *paint_methods = g_new0 (gchar *, *num_paint_methods + 1);
+ for (i = 0; i < *num_paint_methods; i++)
+ (*paint_methods)[i] = g_strdup (return_vals[2].data.d_stringarray[i]);
+ }
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_paint_method:
+ *
+ * Retrieve the currently active paint method.
+ *
+ * This procedure returns the name of the currently active paint
+ * method.
+ *
+ * Returns: The name of the active paint method.
+ *
+ * Since: 2.4
+ **/
+gchar *
+gimp_context_get_paint_method (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-paint-method",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ name = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return name;
+}
+
+/**
+ * gimp_context_set_paint_method:
+ * @name: The name of the paint method.
+ *
+ * Set the specified paint method as the active paint method.
+ *
+ * This procedure allows the active paint method to be set by
+ * specifying its name. The name is simply a string which corresponds
+ * to one of the names of the available paint methods. If there is no
+ * matching method found, this procedure will return an error.
+ * Otherwise, the specified method becomes active and will be used in
+ * all subsequent paint operations.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_context_set_paint_method (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-paint-method",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_stroke_method:
+ *
+ * Retrieve the currently active stroke method.
+ *
+ * This procedure returns the currently active stroke method.
+ *
+ * Returns: The active stroke method.
+ *
+ * Since: 2.10
+ **/
+GimpStrokeMethod
+gimp_context_get_stroke_method (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpStrokeMethod stroke_method = 0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-stroke-method",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ stroke_method = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return stroke_method;
+}
+
+/**
+ * gimp_context_set_stroke_method:
+ * @stroke_method: The new stroke method.
+ *
+ * Set the specified stroke method as the active stroke method.
+ *
+ * This procedure set the specified stroke method as the active stroke
+ * method. The new method will be used in all subsequent stroke
+ * operations.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_context_set_stroke_method (GimpStrokeMethod stroke_method)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-stroke-method",
+ &nreturn_vals,
+ GIMP_PDB_INT32, stroke_method,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_foreground:
+ * @foreground: The foreground color.
+ *
+ * Get the current GIMP foreground color.
+ *
+ * This procedure returns the current GIMP foreground color. The
+ * foreground color is used in a variety of tools such as paint tools,
+ * blending, and bucket fill.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_context_get_foreground (GimpRGB *foreground)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-foreground",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ *foreground = return_vals[1].data.d_color;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_set_foreground:
+ * @foreground: The foreground color.
+ *
+ * Set the current GIMP foreground color.
+ *
+ * This procedure sets the current GIMP foreground color. After this is
+ * set, operations which use foreground such as paint tools, blending,
+ * and bucket fill will use the new value.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_context_set_foreground (const GimpRGB *foreground)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-foreground",
+ &nreturn_vals,
+ GIMP_PDB_COLOR, foreground,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_background:
+ * @background: The background color.
+ *
+ * Get the current GIMP background color.
+ *
+ * This procedure returns the current GIMP background color. The
+ * background color is used in a variety of tools such as blending,
+ * erasing (with non-alpha images), and image filling.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_context_get_background (GimpRGB *background)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-background",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ *background = return_vals[1].data.d_color;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_set_background:
+ * @background: The background color.
+ *
+ * Set the current GIMP background color.
+ *
+ * This procedure sets the current GIMP background color. After this is
+ * set, operations which use background such as blending, filling
+ * images, clearing, and erasing (in non-alpha images) will use the new
+ * value.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_context_set_background (const GimpRGB *background)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-background",
+ &nreturn_vals,
+ GIMP_PDB_COLOR, background,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_set_default_colors:
+ *
+ * Set the current GIMP foreground and background colors to black and
+ * white.
+ *
+ * This procedure sets the current GIMP foreground and background
+ * colors to their initial default values, black and white.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_context_set_default_colors (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-default-colors",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_swap_colors:
+ *
+ * Swap the current GIMP foreground and background colors.
+ *
+ * This procedure swaps the current GIMP foreground and background
+ * colors, so that the new foreground color becomes the old background
+ * color and vice versa.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_context_swap_colors (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-swap-colors",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_opacity:
+ *
+ * Get the opacity.
+ *
+ * This procedure returns the opacity setting. The return value is a
+ * floating point number between 0 and 100.
+ *
+ * Returns: The opacity.
+ *
+ * Since: 2.2
+ **/
+gdouble
+gimp_context_get_opacity (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble opacity = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-opacity",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ opacity = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return opacity;
+}
+
+/**
+ * gimp_context_set_opacity:
+ * @opacity: The opacity.
+ *
+ * Set the opacity.
+ *
+ * This procedure modifies the opacity setting. The value should be a
+ * floating point number between 0 and 100.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_context_set_opacity (gdouble opacity)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-opacity",
+ &nreturn_vals,
+ GIMP_PDB_FLOAT, opacity,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_paint_mode:
+ *
+ * Get the paint mode.
+ *
+ * This procedure returns the paint-mode setting. The return value is
+ * an integer which corresponds to the values listed in the argument
+ * description.
+ *
+ * Returns: The paint mode.
+ *
+ * Since: 2.2
+ **/
+GimpLayerMode
+gimp_context_get_paint_mode (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpLayerMode paint_mode = 0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-paint-mode",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ paint_mode = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return paint_mode;
+}
+
+/**
+ * gimp_context_set_paint_mode:
+ * @paint_mode: The paint mode.
+ *
+ * Set the paint mode.
+ *
+ * This procedure modifies the paint_mode setting.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_context_set_paint_mode (GimpLayerMode paint_mode)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-paint-mode",
+ &nreturn_vals,
+ GIMP_PDB_INT32, paint_mode,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_line_width:
+ *
+ * Get the line width setting.
+ *
+ * This procedure returns the line width setting.
+ *
+ * Returns: The line width setting.
+ *
+ * Since: 2.10
+ **/
+gdouble
+gimp_context_get_line_width (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble line_width = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-line-width",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ line_width = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return line_width;
+}
+
+/**
+ * gimp_context_set_line_width:
+ * @line_width: The line width setting.
+ *
+ * Set the line width setting.
+ *
+ * This procedure modifies the line width setting for stroking lines.
+ *
+ * This setting affects the following procedures:
+ * gimp_drawable_edit_stroke_selection(),
+ * gimp_drawable_edit_stroke_item().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_context_set_line_width (gdouble line_width)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-line-width",
+ &nreturn_vals,
+ GIMP_PDB_FLOAT, line_width,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_line_width_unit:
+ *
+ * Get the line width unit setting.
+ *
+ * This procedure returns the line width unit setting.
+ *
+ * Returns: The line width unit setting.
+ *
+ * Since: 2.10
+ **/
+GimpUnit
+gimp_context_get_line_width_unit (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpUnit line_width_unit = 0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-line-width-unit",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ line_width_unit = return_vals[1].data.d_unit;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return line_width_unit;
+}
+
+/**
+ * gimp_context_set_line_width_unit:
+ * @line_width_unit: The line width setting unit.
+ *
+ * Set the line width unit setting.
+ *
+ * This procedure modifies the line width unit setting for stroking
+ * lines.
+ *
+ * This setting affects the following procedures:
+ * gimp_drawable_edit_stroke_selection(),
+ * gimp_drawable_edit_stroke_item().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_context_set_line_width_unit (GimpUnit line_width_unit)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-line-width-unit",
+ &nreturn_vals,
+ GIMP_PDB_INT32, line_width_unit,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_line_cap_style:
+ *
+ * Get the line cap style setting.
+ *
+ * This procedure returns the line cap style setting.
+ *
+ * Returns: The line cap style setting.
+ *
+ * Since: 2.10
+ **/
+GimpCapStyle
+gimp_context_get_line_cap_style (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpCapStyle cap_style = 0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-line-cap-style",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ cap_style = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return cap_style;
+}
+
+/**
+ * gimp_context_set_line_cap_style:
+ * @cap_style: The line cap style setting.
+ *
+ * Set the line cap style setting.
+ *
+ * This procedure modifies the line cap style setting for stroking
+ * lines.
+ *
+ * This setting affects the following procedures:
+ * gimp_drawable_edit_stroke_selection(),
+ * gimp_drawable_edit_stroke_item().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_context_set_line_cap_style (GimpCapStyle cap_style)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-line-cap-style",
+ &nreturn_vals,
+ GIMP_PDB_INT32, cap_style,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_line_join_style:
+ *
+ * Get the line join style setting.
+ *
+ * This procedure returns the line join style setting.
+ *
+ * Returns: The line join style setting.
+ *
+ * Since: 2.10
+ **/
+GimpJoinStyle
+gimp_context_get_line_join_style (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpJoinStyle join_style = 0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-line-join-style",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ join_style = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return join_style;
+}
+
+/**
+ * gimp_context_set_line_join_style:
+ * @join_style: The line join style setting.
+ *
+ * Set the line join style setting.
+ *
+ * This procedure modifies the line join style setting for stroking
+ * lines.
+ *
+ * This setting affects the following procedures:
+ * gimp_drawable_edit_stroke_selection(),
+ * gimp_drawable_edit_stroke_item().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_context_set_line_join_style (GimpJoinStyle join_style)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-line-join-style",
+ &nreturn_vals,
+ GIMP_PDB_INT32, join_style,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_line_miter_limit:
+ *
+ * Get the line miter limit setting.
+ *
+ * This procedure returns the line miter limit setting.
+ *
+ * Returns: The line miter limit setting.
+ *
+ * Since: 2.10
+ **/
+gdouble
+gimp_context_get_line_miter_limit (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble miter_limit = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-line-miter-limit",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ miter_limit = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return miter_limit;
+}
+
+/**
+ * gimp_context_set_line_miter_limit:
+ * @miter_limit: The line miter limit setting.
+ *
+ * Set the line miter limit setting.
+ *
+ * This procedure modifies the line miter limit setting for stroking
+ * lines.
+ * A mitered join is converted to a bevelled join if the miter would
+ * extend to a distance of more than (miter-limit * line-width) from
+ * the actual join point.
+ *
+ * This setting affects the following procedures:
+ * gimp_drawable_edit_stroke_selection(),
+ * gimp_drawable_edit_stroke_item().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_context_set_line_miter_limit (gdouble miter_limit)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-line-miter-limit",
+ &nreturn_vals,
+ GIMP_PDB_FLOAT, miter_limit,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_line_dash_offset:
+ *
+ * Get the line dash offset setting.
+ *
+ * This procedure returns the line dash offset setting.
+ *
+ * Returns: The line dash offset setting.
+ *
+ * Since: 2.10
+ **/
+gdouble
+gimp_context_get_line_dash_offset (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble dash_offset = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-line-dash-offset",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ dash_offset = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return dash_offset;
+}
+
+/**
+ * gimp_context_set_line_dash_offset:
+ * @dash_offset: The line dash offset setting.
+ *
+ * Set the line dash offset setting.
+ *
+ * This procedure modifies the line dash offset setting for stroking
+ * lines.
+ *
+ * This setting affects the following procedures:
+ * gimp_drawable_edit_stroke_selection(),
+ * gimp_drawable_edit_stroke_item().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_context_set_line_dash_offset (gdouble dash_offset)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-line-dash-offset",
+ &nreturn_vals,
+ GIMP_PDB_FLOAT, dash_offset,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_line_dash_pattern:
+ * @num_dashes: The number of dashes in the dash_pattern array.
+ * @dashes: The line dash pattern setting.
+ *
+ * Get the line dash pattern setting.
+ *
+ * This procedure returns the line dash pattern setting.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_context_get_line_dash_pattern (gint *num_dashes,
+ gdouble **dashes)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-line-dash-pattern",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ *num_dashes = 0;
+ *dashes = NULL;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *num_dashes = return_vals[1].data.d_int32;
+ *dashes = g_new (gdouble, *num_dashes);
+ memcpy (*dashes,
+ return_vals[2].data.d_floatarray,
+ *num_dashes * sizeof (gdouble));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_set_line_dash_pattern:
+ * @num_dashes: The number of dashes in the dash_pattern array.
+ * @dashes: The line dash pattern setting.
+ *
+ * Set the line dash pattern setting.
+ *
+ * This procedure modifies the line dash pattern setting for stroking
+ * lines.
+ *
+ * The unit of the dash pattern segments is the actual line width used
+ * for the stroke operation, in other words a segment length of 1.0
+ * results in a square segment shape (or gap shape).
+ *
+ * This setting affects the following procedures:
+ * gimp_drawable_edit_stroke_selection_(),
+ * gimp_drawable_edit_stroke_item().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_context_set_line_dash_pattern (gint num_dashes,
+ const gdouble *dashes)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-line-dash-pattern",
+ &nreturn_vals,
+ GIMP_PDB_INT32, num_dashes,
+ GIMP_PDB_FLOATARRAY, dashes,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_brush:
+ *
+ * Retrieve the currently active brush.
+ *
+ * This procedure returns the name of the currently active brush. All
+ * paint operations and stroke operations use this brush to control the
+ * application of paint to the image.
+ *
+ * Returns: The name of the active brush.
+ *
+ * Since: 2.2
+ **/
+gchar *
+gimp_context_get_brush (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-brush",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ name = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return name;
+}
+
+/**
+ * gimp_context_set_brush:
+ * @name: The name of the brush.
+ *
+ * Set the specified brush as the active brush.
+ *
+ * This procedure allows the active brush to be set by specifying its
+ * name. The name is simply a string which corresponds to one of the
+ * names of the installed brushes. If there is no matching brush found,
+ * this procedure will return an error. Otherwise, the specified brush
+ * becomes active and will be used in all subsequent paint operations.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_context_set_brush (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-brush",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_brush_size:
+ *
+ * Get brush size in pixels.
+ *
+ * Get the brush size in pixels for brush based paint tools.
+ *
+ * Returns: Brush size in pixels.
+ *
+ * Since: 2.8
+ **/
+gdouble
+gimp_context_get_brush_size (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble size = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-brush-size",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ size = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return size;
+}
+
+/**
+ * gimp_context_set_brush_size:
+ * @size: Brush size in pixels.
+ *
+ * Set brush size in pixels.
+ *
+ * Set the brush size in pixels for brush based paint tools.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_set_brush_size (gdouble size)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-brush-size",
+ &nreturn_vals,
+ GIMP_PDB_FLOAT, size,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_set_brush_default_size:
+ *
+ * Set brush size to its default.
+ *
+ * Set the brush size to the default (max of width and height) for
+ * paintbrush, airbrush, or pencil tools.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_set_brush_default_size (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-brush-default-size",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_brush_aspect_ratio:
+ *
+ * Get brush aspect ratio.
+ *
+ * Set the aspect ratio for brush based paint tools.
+ *
+ * Returns: Aspect ratio.
+ *
+ * Since: 2.8
+ **/
+gdouble
+gimp_context_get_brush_aspect_ratio (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble aspect = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-brush-aspect-ratio",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ aspect = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return aspect;
+}
+
+/**
+ * gimp_context_set_brush_aspect_ratio:
+ * @aspect: Aspect ratio.
+ *
+ * Set brush aspect ratio.
+ *
+ * Set the aspect ratio for brush based paint tools.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_set_brush_aspect_ratio (gdouble aspect)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-brush-aspect-ratio",
+ &nreturn_vals,
+ GIMP_PDB_FLOAT, aspect,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_brush_angle:
+ *
+ * Get brush angle in degrees.
+ *
+ * Set the angle in degrees for brush based paint tools.
+ *
+ * Returns: Angle in degrees.
+ *
+ * Since: 2.8
+ **/
+gdouble
+gimp_context_get_brush_angle (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble angle = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-brush-angle",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ angle = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return angle;
+}
+
+/**
+ * gimp_context_set_brush_angle:
+ * @angle: Angle in degrees.
+ *
+ * Set brush angle in degrees.
+ *
+ * Set the angle in degrees for brush based paint tools.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_set_brush_angle (gdouble angle)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-brush-angle",
+ &nreturn_vals,
+ GIMP_PDB_FLOAT, angle,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_brush_spacing:
+ *
+ * Get brush spacing as percent of size.
+ *
+ * Get the brush spacing as percent of size for brush based paint
+ * tools.
+ *
+ * Returns: Brush spacing as fraction of size.
+ *
+ * Since: 2.10
+ **/
+gdouble
+gimp_context_get_brush_spacing (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble spacing = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-brush-spacing",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ spacing = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return spacing;
+}
+
+/**
+ * gimp_context_set_brush_spacing:
+ * @spacing: Brush spacing as fraction of size.
+ *
+ * Set brush spacing as percent of size.
+ *
+ * Set the brush spacing as percent of size for brush based paint
+ * tools.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_context_set_brush_spacing (gdouble spacing)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-brush-spacing",
+ &nreturn_vals,
+ GIMP_PDB_FLOAT, spacing,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_set_brush_default_spacing:
+ *
+ * Set brush spacing to its default.
+ *
+ * Set the brush spacing to the default for paintbrush, airbrush, or
+ * pencil tools.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_context_set_brush_default_spacing (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-brush-default-spacing",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_brush_hardness:
+ *
+ * Get brush hardness in paint options.
+ *
+ * Get the brush hardness for brush based paint tools.
+ *
+ * Returns: Brush hardness.
+ *
+ * Since: 2.10
+ **/
+gdouble
+gimp_context_get_brush_hardness (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble hardness = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-brush-hardness",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ hardness = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return hardness;
+}
+
+/**
+ * gimp_context_set_brush_hardness:
+ * @hardness: Brush hardness.
+ *
+ * Set brush hardness.
+ *
+ * Set the brush hardness for brush based paint tools.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_context_set_brush_hardness (gdouble hardness)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-brush-hardness",
+ &nreturn_vals,
+ GIMP_PDB_FLOAT, hardness,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_set_brush_default_hardness:
+ *
+ * Set brush spacing to its default.
+ *
+ * Set the brush spacing to the default for paintbrush, airbrush, or
+ * pencil tools.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_context_set_brush_default_hardness (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-brush-default-hardness",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_brush_force:
+ *
+ * Get brush force in paint options.
+ *
+ * Get the brush application force for brush based paint tools.
+ *
+ * Returns: Brush application force.
+ *
+ * Since: 2.10
+ **/
+gdouble
+gimp_context_get_brush_force (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble force = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-brush-force",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ force = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return force;
+}
+
+/**
+ * gimp_context_set_brush_force:
+ * @force: Brush application force.
+ *
+ * Set brush application force.
+ *
+ * Set the brush application force for brush based paint tools.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_context_set_brush_force (gdouble force)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-brush-force",
+ &nreturn_vals,
+ GIMP_PDB_FLOAT, force,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_dynamics:
+ *
+ * Retrieve the currently active paint dynamics.
+ *
+ * This procedure returns the name of the currently active paint
+ * dynamics. All paint operations and stroke operations use this paint
+ * dynamics to control the application of paint to the image.
+ *
+ * Returns: The name of the active paint dynamics.
+ *
+ * Since: 2.8
+ **/
+gchar *
+gimp_context_get_dynamics (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-dynamics",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ name = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return name;
+}
+
+/**
+ * gimp_context_set_dynamics:
+ * @name: The name of the paint dynamics.
+ *
+ * Set the specified paint dynamics as the active paint dynamics.
+ *
+ * This procedure allows the active paint dynamics to be set by
+ * specifying its name. The name is simply a string which corresponds
+ * to one of the names of the installed paint dynamics. If there is no
+ * matching paint dynamics found, this procedure will return an error.
+ * Otherwise, the specified paint dynamics becomes active and will be
+ * used in all subsequent paint operations.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_set_dynamics (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-dynamics",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_mypaint_brush:
+ *
+ * Retrieve the currently active MyPaint brush.
+ *
+ * This procedure returns the name of the currently active MyPaint
+ * brush.
+ *
+ * Returns: The name of the active MyPaint brush.
+ *
+ * Since: 2.10
+ **/
+gchar *
+gimp_context_get_mypaint_brush (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-mypaint-brush",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ name = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return name;
+}
+
+/**
+ * gimp_context_set_mypaint_brush:
+ * @name: The name of the MyPaint brush.
+ *
+ * Set the specified MyPaint brush as the active MyPaint brush.
+ *
+ * This procedure allows the active MyPaint brush to be set by
+ * specifying its name. The name is simply a string which corresponds
+ * to one of the names of the installed MyPaint brushes. If there is no
+ * matching MyPaint brush found, this procedure will return an error.
+ * Otherwise, the specified MyPaint brush becomes active and will be
+ * used in all subsequent MyPaint paint operations.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_context_set_mypaint_brush (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-mypaint-brush",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_pattern:
+ *
+ * Retrieve the currently active pattern.
+ *
+ * This procedure returns name of the the currently active pattern. All
+ * clone and bucket-fill operations with patterns will use this pattern
+ * to control the application of paint to the image.
+ *
+ * Returns: The name of the active pattern.
+ *
+ * Since: 2.2
+ **/
+gchar *
+gimp_context_get_pattern (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-pattern",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ name = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return name;
+}
+
+/**
+ * gimp_context_set_pattern:
+ * @name: The name of the pattern.
+ *
+ * Set the specified pattern as the active pattern.
+ *
+ * This procedure allows the active pattern to be set by specifying its
+ * name. The name is simply a string which corresponds to one of the
+ * names of the installed patterns. If there is no matching pattern
+ * found, this procedure will return an error. Otherwise, the specified
+ * pattern becomes active and will be used in all subsequent paint
+ * operations.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_context_set_pattern (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-pattern",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_gradient:
+ *
+ * Retrieve the currently active gradient.
+ *
+ * This procedure returns the name of the currently active gradient.
+ *
+ * Returns: The name of the active gradient.
+ *
+ * Since: 2.2
+ **/
+gchar *
+gimp_context_get_gradient (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-gradient",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ name = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return name;
+}
+
+/**
+ * gimp_context_set_gradient:
+ * @name: The name of the gradient.
+ *
+ * Sets the specified gradient as the active gradient.
+ *
+ * This procedure lets you set the specified gradient as the active or
+ * \"current\" one. The name is simply a string which corresponds to
+ * one of the loaded gradients. If no matching gradient is found, this
+ * procedure will return an error. Otherwise, the specified gradient
+ * will become active and will be used for subsequent custom gradient
+ * operations.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_context_set_gradient (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-gradient",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_set_gradient_fg_bg_rgb:
+ *
+ * Sets the built-in FG-BG RGB gradient as the active gradient.
+ *
+ * This procedure sets the built-in FG-BG RGB gradient as the active
+ * gradient. The gradient will be used for subsequent gradient
+ * operations.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_context_set_gradient_fg_bg_rgb (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-gradient-fg-bg-rgb",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_set_gradient_fg_bg_hsv_cw:
+ *
+ * Sets the built-in FG-BG HSV (cw) gradient as the active gradient.
+ *
+ * This procedure sets the built-in FG-BG HSV (cw) gradient as the
+ * active gradient. The gradient will be used for subsequent gradient
+ * operations.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_context_set_gradient_fg_bg_hsv_cw (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-gradient-fg-bg-hsv-cw",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_set_gradient_fg_bg_hsv_ccw:
+ *
+ * Sets the built-in FG-BG HSV (ccw) gradient as the active gradient.
+ *
+ * This procedure sets the built-in FG-BG HSV (ccw) gradient as the
+ * active gradient. The gradient will be used for subsequent gradient
+ * operations.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_context_set_gradient_fg_bg_hsv_ccw (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-gradient-fg-bg-hsv-ccw",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_set_gradient_fg_transparent:
+ *
+ * Sets the built-in FG-Transparent gradient as the active gradient.
+ *
+ * This procedure sets the built-in FG-Transparent gradient as the
+ * active gradient. The gradient will be used for subsequent gradient
+ * operations.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_context_set_gradient_fg_transparent (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-gradient-fg-transparent",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_gradient_blend_color_space:
+ *
+ * Get the gradient blend color space.
+ *
+ * Get the gradient blend color space for paint tools and the gradient
+ * tool.
+ *
+ * Returns: Color blend space.
+ *
+ * Since: 2.10
+ **/
+GimpGradientBlendColorSpace
+gimp_context_get_gradient_blend_color_space (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpGradientBlendColorSpace blend_color_space = 0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-gradient-blend-color-space",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ blend_color_space = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return blend_color_space;
+}
+
+/**
+ * gimp_context_set_gradient_blend_color_space:
+ * @blend_color_space: Blend color space.
+ *
+ * Set the gradient blend color space.
+ *
+ * Set the gradient blend color space for paint tools and the gradient
+ * tool.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_context_set_gradient_blend_color_space (GimpGradientBlendColorSpace blend_color_space)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-gradient-blend-color-space",
+ &nreturn_vals,
+ GIMP_PDB_INT32, blend_color_space,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_gradient_repeat_mode:
+ *
+ * Get the gradient repeat mode.
+ *
+ * Get the gradient repeat mode for paint tools and the gradient tool.
+ *
+ * Returns: Repeat mode.
+ *
+ * Since: 2.10
+ **/
+GimpRepeatMode
+gimp_context_get_gradient_repeat_mode (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpRepeatMode repeat_mode = 0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-gradient-repeat-mode",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ repeat_mode = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return repeat_mode;
+}
+
+/**
+ * gimp_context_set_gradient_repeat_mode:
+ * @repeat_mode: Repeat mode.
+ *
+ * Set the gradient repeat mode.
+ *
+ * Set the gradient repeat mode for paint tools and the gradient tool.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_context_set_gradient_repeat_mode (GimpRepeatMode repeat_mode)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-gradient-repeat-mode",
+ &nreturn_vals,
+ GIMP_PDB_INT32, repeat_mode,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_gradient_reverse:
+ *
+ * Get the gradient reverse setting.
+ *
+ * Get the gradient reverse setting for paint tools and the gradient
+ * tool.
+ *
+ * Returns: Reverse.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_context_get_gradient_reverse (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean reverse = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-gradient-reverse",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ reverse = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return reverse;
+}
+
+/**
+ * gimp_context_set_gradient_reverse:
+ * @reverse: Reverse.
+ *
+ * Set the gradient reverse setting.
+ *
+ * Set the gradient reverse setting for paint tools and the gradient
+ * tool.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_context_set_gradient_reverse (gboolean reverse)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-gradient-reverse",
+ &nreturn_vals,
+ GIMP_PDB_INT32, reverse,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_palette:
+ *
+ * Retrieve the currently active palette.
+ *
+ * This procedure returns the name of the the currently active palette.
+ *
+ * Returns: The name of the active palette.
+ *
+ * Since: 2.2
+ **/
+gchar *
+gimp_context_get_palette (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-palette",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ name = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return name;
+}
+
+/**
+ * gimp_context_set_palette:
+ * @name: The name of the palette.
+ *
+ * Set the specified palette as the active palette.
+ *
+ * This procedure allows the active palette to be set by specifying its
+ * name. The name is simply a string which corresponds to one of the
+ * names of the installed palettes. If no matching palette is found,
+ * this procedure will return an error. Otherwise, the specified
+ * palette becomes active and will be used in all subsequent palette
+ * operations.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_context_set_palette (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-palette",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_font:
+ *
+ * Retrieve the currently active font.
+ *
+ * This procedure returns the name of the currently active font.
+ *
+ * Returns: The name of the active font.
+ *
+ * Since: 2.2
+ **/
+gchar *
+gimp_context_get_font (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-font",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ name = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return name;
+}
+
+/**
+ * gimp_context_set_font:
+ * @name: The name of the font.
+ *
+ * Set the specified font as the active font.
+ *
+ * This procedure allows the active font to be set by specifying its
+ * name. The name is simply a string which corresponds to one of the
+ * names of the installed fonts. If no matching font is found, this
+ * procedure will return an error. Otherwise, the specified font
+ * becomes active and will be used in all subsequent font operations.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_context_set_font (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-font",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_antialias:
+ *
+ * Get the antialias setting.
+ *
+ * This procedure returns the antialias setting.
+ *
+ * Returns: The antialias setting.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_get_antialias (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean antialias = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-antialias",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ antialias = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return antialias;
+}
+
+/**
+ * gimp_context_set_antialias:
+ * @antialias: The antialias setting.
+ *
+ * Set the antialias setting.
+ *
+ * This procedure modifies the antialias setting. If antialiasing is
+ * turned on, the edges of selected region will contain intermediate
+ * values which give the appearance of a sharper, less pixelized edge.
+ * This should be set as TRUE most of the time unless a binary-only
+ * selection is wanted.
+ *
+ * This setting affects the following procedures:
+ * gimp_image_select_color(), gimp_image_select_contiguous_color(),
+ * gimp_image_select_round_rectangle(), gimp_image_select_ellipse(),
+ * gimp_image_select_polygon(), gimp_image_select_item(),
+ * gimp_drawable_edit_bucket_fill(), gimp_drawable_edit_stroke_item(),
+ * gimp_drawable_edit_stroke_selection().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_set_antialias (gboolean antialias)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-antialias",
+ &nreturn_vals,
+ GIMP_PDB_INT32, antialias,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_feather:
+ *
+ * Get the feather setting.
+ *
+ * This procedure returns the feather setting.
+ *
+ * Returns: The feather setting.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_get_feather (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean feather = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-feather",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ feather = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return feather;
+}
+
+/**
+ * gimp_context_set_feather:
+ * @feather: The feather setting.
+ *
+ * Set the feather setting.
+ *
+ * This procedure modifies the feather setting. If the feather option
+ * is enabled, selections will be blurred before combining. The blur is
+ * a gaussian blur; its radii can be controlled using
+ * gimp_context_set_feather_radius().
+ *
+ * This setting affects the following procedures:
+ * gimp_image_select_color(), gimp_image_select_contiguous_color(),
+ * gimp_image_select_rectangle(), gimp_image_select_round_rectangle(),
+ * gimp_image_select_ellipse(), gimp_image_select_polygon(),
+ * gimp_image_select_item().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_set_feather (gboolean feather)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-feather",
+ &nreturn_vals,
+ GIMP_PDB_INT32, feather,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_feather_radius:
+ * @feather_radius_x: The horizontal feather radius.
+ * @feather_radius_y: The vertical feather radius.
+ *
+ * Get the feather radius setting.
+ *
+ * This procedure returns the feather radius setting.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_get_feather_radius (gdouble *feather_radius_x,
+ gdouble *feather_radius_y)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-feather-radius",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ *feather_radius_x = 0.0;
+ *feather_radius_y = 0.0;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *feather_radius_x = return_vals[1].data.d_float;
+ *feather_radius_y = return_vals[2].data.d_float;
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_set_feather_radius:
+ * @feather_radius_x: The horizontal feather radius.
+ * @feather_radius_y: The vertical feather radius.
+ *
+ * Set the feather radius setting.
+ *
+ * This procedure modifies the feather radius setting.
+ *
+ * This setting affects all procedures that are affected by
+ * gimp_context_set_feather().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_set_feather_radius (gdouble feather_radius_x,
+ gdouble feather_radius_y)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-feather-radius",
+ &nreturn_vals,
+ GIMP_PDB_FLOAT, feather_radius_x,
+ GIMP_PDB_FLOAT, feather_radius_y,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_sample_merged:
+ *
+ * Get the sample merged setting.
+ *
+ * This procedure returns the sample merged setting.
+ *
+ * Returns: The sample merged setting.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_get_sample_merged (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean sample_merged = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-sample-merged",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ sample_merged = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return sample_merged;
+}
+
+/**
+ * gimp_context_set_sample_merged:
+ * @sample_merged: The sample merged setting.
+ *
+ * Set the sample merged setting.
+ *
+ * This procedure modifies the sample merged setting. If an operation
+ * depends on the colors of the pixels present in a drawable, like when
+ * doing a seed fill, this setting controls whether the pixel data from
+ * the specified drawable is used ('sample-merged' is FALSE), or the
+ * pixel data from the composite image ('sample-merged' is TRUE. This
+ * is equivalent to sampling for colors after merging all visible
+ * layers).
+ *
+ * This setting affects the following procedures:
+ * gimp_image_select_color(), gimp_image_select_contiguous_color(),
+ * gimp_drawable_edit_bucket_fill().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_set_sample_merged (gboolean sample_merged)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-sample-merged",
+ &nreturn_vals,
+ GIMP_PDB_INT32, sample_merged,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_sample_criterion:
+ *
+ * Get the sample criterion setting.
+ *
+ * This procedure returns the sample criterion setting.
+ *
+ * Returns: The sample criterion setting.
+ *
+ * Since: 2.8
+ **/
+GimpSelectCriterion
+gimp_context_get_sample_criterion (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpSelectCriterion sample_criterion = 0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-sample-criterion",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ sample_criterion = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return sample_criterion;
+}
+
+/**
+ * gimp_context_set_sample_criterion:
+ * @sample_criterion: The sample criterion setting.
+ *
+ * Set the sample criterion setting.
+ *
+ * This procedure modifies the sample criterion setting. If an
+ * operation depends on the colors of the pixels present in a drawable,
+ * like when doing a seed fill, this setting controls how color
+ * similarity is determined. SELECT_CRITERION_COMPOSITE is the default
+ * value.
+ *
+ * This setting affects the following procedures:
+ * gimp_image_select_color(), gimp_image_select_contiguous_color(),
+ * gimp_drawable_edit_bucket_fill().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_set_sample_criterion (GimpSelectCriterion sample_criterion)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-sample-criterion",
+ &nreturn_vals,
+ GIMP_PDB_INT32, sample_criterion,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_sample_threshold:
+ *
+ * Get the sample threshold setting.
+ *
+ * This procedure returns the sample threshold setting.
+ *
+ * Returns: The sample threshold setting.
+ *
+ * Since: 2.8
+ **/
+gdouble
+gimp_context_get_sample_threshold (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble sample_threshold = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-sample-threshold",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ sample_threshold = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return sample_threshold;
+}
+
+/**
+ * gimp_context_set_sample_threshold:
+ * @sample_threshold: The sample threshold setting.
+ *
+ * Set the sample threshold setting.
+ *
+ * This procedure modifies the sample threshold setting. If an
+ * operation depends on the colors of the pixels present in a drawable,
+ * like when doing a seed fill, this setting controls what is
+ * \"sufficiently close\" to be considered a similar color. If the
+ * sample threshold has not been set explicitly, the default threshold
+ * set in gimprc will be used.
+ *
+ * This setting affects the following procedures:
+ * gimp_image_select_color(), gimp_image_select_contiguous_color(),
+ * gimp_drawable_edit_bucket_fill().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_set_sample_threshold (gdouble sample_threshold)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-sample-threshold",
+ &nreturn_vals,
+ GIMP_PDB_FLOAT, sample_threshold,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_sample_threshold_int:
+ *
+ * Get the sample threshold setting as an integer value.
+ *
+ * This procedure returns the sample threshold setting as an integer
+ * value. See gimp_context_get_sample_threshold().
+ *
+ * Returns: The sample threshold setting.
+ *
+ * Since: 2.8
+ **/
+gint
+gimp_context_get_sample_threshold_int (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint sample_threshold = 0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-sample-threshold-int",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ sample_threshold = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return sample_threshold;
+}
+
+/**
+ * gimp_context_set_sample_threshold_int:
+ * @sample_threshold: The sample threshold setting.
+ *
+ * Set the sample threshold setting as an integer value.
+ *
+ * This procedure modifies the sample threshold setting as an integer
+ * value. See gimp_context_set_sample_threshold().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_set_sample_threshold_int (gint sample_threshold)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-sample-threshold-int",
+ &nreturn_vals,
+ GIMP_PDB_INT32, sample_threshold,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_sample_transparent:
+ *
+ * Get the sample transparent setting.
+ *
+ * This procedure returns the sample transparent setting.
+ *
+ * Returns: The sample transparent setting.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_get_sample_transparent (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean sample_transparent = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-sample-transparent",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ sample_transparent = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return sample_transparent;
+}
+
+/**
+ * gimp_context_set_sample_transparent:
+ * @sample_transparent: The sample transparent setting.
+ *
+ * Set the sample transparent setting.
+ *
+ * This procedure modifies the sample transparent setting. If an
+ * operation depends on the colors of the pixels present in a drawable,
+ * like when doing a seed fill, this setting controls whether
+ * transparency is considered to be a unique selectable color. When
+ * this setting is TRUE, transparent areas can be selected or filled.
+ *
+ * This setting affects the following procedures:
+ * gimp_image_select_color(), gimp_image_select_contiguous_color(),
+ * gimp_drawable_edit_bucket_fill().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_set_sample_transparent (gboolean sample_transparent)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-sample-transparent",
+ &nreturn_vals,
+ GIMP_PDB_INT32, sample_transparent,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_diagonal_neighbors:
+ *
+ * Get the diagonal neighbors setting.
+ *
+ * This procedure returns the diagonal neighbors setting.
+ *
+ * Returns: The diagonal neighbors setting.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_context_get_diagonal_neighbors (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean diagonal_neighbors = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-diagonal-neighbors",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ diagonal_neighbors = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return diagonal_neighbors;
+}
+
+/**
+ * gimp_context_set_diagonal_neighbors:
+ * @diagonal_neighbors: The diagonal neighbors setting.
+ *
+ * Set the diagonal neighbors setting.
+ *
+ * This procedure modifies the diagonal neighbors setting. If the
+ * affected region of an operation is based on a seed point, like when
+ * doing a seed fill, then, when this setting is TRUE, all eight
+ * neighbors of each pixel are considered when calculating the affected
+ * region; in contrast, when this setting is FALSE, only the four
+ * orthogonal neighbors of each pixel are considered.
+ *
+ * This setting affects the following procedures:
+ * gimp_image_select_contiguous_color(),
+ * gimp_drawable_edit_bucket_fill().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_context_set_diagonal_neighbors (gboolean diagonal_neighbors)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-diagonal-neighbors",
+ &nreturn_vals,
+ GIMP_PDB_INT32, diagonal_neighbors,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_distance_metric:
+ *
+ * Get the distance metric used in some computations.
+ *
+ * This procedure returns the distance metric in the current context.
+ * See gimp_context_set_distance_metric() to know more about its usage.
+ *
+ * Returns: The distance metric.
+ *
+ * Since: 2.10
+ **/
+GeglDistanceMetric
+gimp_context_get_distance_metric (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GeglDistanceMetric metric = 0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-distance-metric",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ metric = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return metric;
+}
+
+/**
+ * gimp_context_set_distance_metric:
+ * @metric: The distance metric.
+ *
+ * Set the distance metric used in some computations.
+ *
+ * This procedure modifies the distance metric used in some
+ * computations, such as gimp_drawable_edit_gradient_fill(). In
+ * particular, it does not change the metric used in generic distance
+ * computation on canvas, as in the Measure tool.
+ *
+ * This setting affects the following procedures:
+ * gimp_drawable_edit_gradient_fill().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_context_set_distance_metric (GeglDistanceMetric metric)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-distance-metric",
+ &nreturn_vals,
+ GIMP_PDB_INT32, metric,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_interpolation:
+ *
+ * Get the interpolation type.
+ *
+ * This procedure returns the interpolation setting. The return value
+ * is an integer which corresponds to the values listed in the argument
+ * description. If the interpolation has not been set explicitly by
+ * gimp_context_set_interpolation(), the default interpolation set in
+ * gimprc will be used.
+ *
+ * Returns: The interpolation type.
+ *
+ * Since: 2.8
+ **/
+GimpInterpolationType
+gimp_context_get_interpolation (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpInterpolationType interpolation = 0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-interpolation",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ interpolation = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return interpolation;
+}
+
+/**
+ * gimp_context_set_interpolation:
+ * @interpolation: The interpolation type.
+ *
+ * Set the interpolation type.
+ *
+ * This procedure modifies the interpolation setting.
+ *
+ * This setting affects affects the following procedures:
+ * gimp_item_transform_flip(), gimp_item_transform_perspective(),
+ * gimp_item_transform_rotate(), gimp_item_transform_scale(),
+ * gimp_item_transform_shear(), gimp_item_transform_2d(),
+ * gimp_item_transform_matrix(), gimp_image_scale(),
+ * gimp_layer_scale().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_set_interpolation (GimpInterpolationType interpolation)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-interpolation",
+ &nreturn_vals,
+ GIMP_PDB_INT32, interpolation,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_transform_direction:
+ *
+ * Get the transform direction.
+ *
+ * This procedure returns the transform direction. The return value is
+ * an integer which corresponds to the values listed in the argument
+ * description.
+ *
+ * Returns: The transform direction.
+ *
+ * Since: 2.8
+ **/
+GimpTransformDirection
+gimp_context_get_transform_direction (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpTransformDirection transform_direction = 0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-transform-direction",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ transform_direction = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return transform_direction;
+}
+
+/**
+ * gimp_context_set_transform_direction:
+ * @transform_direction: The transform direction.
+ *
+ * Set the transform direction.
+ *
+ * This procedure modifies the transform direction setting.
+ *
+ * This setting affects affects the following procedures:
+ * gimp_item_transform_flip(), gimp_item_transform_perspective(),
+ * gimp_item_transform_rotate(), gimp_item_transform_scale(),
+ * gimp_item_transform_shear(), gimp_item_transform_2d(),
+ * gimp_item_transform_matrix().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_set_transform_direction (GimpTransformDirection transform_direction)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-transform-direction",
+ &nreturn_vals,
+ GIMP_PDB_INT32, transform_direction,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_transform_resize:
+ *
+ * Get the transform resize type.
+ *
+ * This procedure returns the transform resize setting. The return
+ * value is an integer which corresponds to the values listed in the
+ * argument description.
+ *
+ * Returns: The transform resize type.
+ *
+ * Since: 2.8
+ **/
+GimpTransformResize
+gimp_context_get_transform_resize (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpTransformResize transform_resize = 0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-transform-resize",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ transform_resize = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return transform_resize;
+}
+
+/**
+ * gimp_context_set_transform_resize:
+ * @transform_resize: The transform resize type.
+ *
+ * Set the transform resize type.
+ *
+ * This procedure modifies the transform resize setting. When
+ * transforming pixels, if the result of a transform operation has a
+ * different size than the original area, this setting determines how
+ * the resulting area is sized.
+ *
+ * This setting affects affects the following procedures:
+ * gimp_item_transform_flip(), gimp_item_transform_flip_simple(),
+ * gimp_item_transform_perspective(), gimp_item_transform_rotate(),
+ * gimp_item_transform_rotate_simple(), gimp_item_transform_scale(),
+ * gimp_item_transform_shear(), gimp_item_transform_2d(),
+ * gimp_item_transform_matrix().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_set_transform_resize (GimpTransformResize transform_resize)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-transform-resize",
+ &nreturn_vals,
+ GIMP_PDB_INT32, transform_resize,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_transform_recursion:
+ *
+ * Deprecated: There is no replacement for this procedure.
+ *
+ * Returns: This returns always 3 and is meaningless.
+ **/
+gint
+gimp_context_get_transform_recursion (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint transform_recursion = 0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-transform-recursion",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ transform_recursion = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return transform_recursion;
+}
+
+/**
+ * gimp_context_set_transform_recursion:
+ * @transform_recursion: This parameter is ignored.
+ *
+ * Deprecated: There is no replacement for this procedure.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_context_set_transform_recursion (gint transform_recursion)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-transform-recursion",
+ &nreturn_vals,
+ GIMP_PDB_INT32, transform_recursion,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_ink_size:
+ *
+ * Get ink blob size in pixels.
+ *
+ * Get the ink blob size in pixels for ink tool.
+ *
+ * Returns: ink blob size in pixels.
+ *
+ * Since: 2.8
+ **/
+gdouble
+gimp_context_get_ink_size (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble size = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-ink-size",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ size = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return size;
+}
+
+/**
+ * gimp_context_set_ink_size:
+ * @size: ink blob size in pixels.
+ *
+ * Set ink blob size in pixels.
+ *
+ * Set the ink blob size in pixels for ink tool.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_set_ink_size (gdouble size)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-ink-size",
+ &nreturn_vals,
+ GIMP_PDB_FLOAT, size,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_ink_angle:
+ *
+ * Get ink angle in degrees.
+ *
+ * Get the ink angle in degrees for ink tool.
+ *
+ * Returns: ink angle in degrees.
+ *
+ * Since: 2.8
+ **/
+gdouble
+gimp_context_get_ink_angle (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble angle = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-ink-angle",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ angle = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return angle;
+}
+
+/**
+ * gimp_context_set_ink_angle:
+ * @angle: ink angle in degrees.
+ *
+ * Set ink angle in degrees.
+ *
+ * Set the ink angle in degrees for ink tool.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_set_ink_angle (gdouble angle)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-ink-angle",
+ &nreturn_vals,
+ GIMP_PDB_FLOAT, angle,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_ink_size_sensitivity:
+ *
+ * Get ink size sensitivity.
+ *
+ * Get the ink size sensitivity for ink tool.
+ *
+ * Returns: ink size sensitivity.
+ *
+ * Since: 2.8
+ **/
+gdouble
+gimp_context_get_ink_size_sensitivity (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble size = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-ink-size-sensitivity",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ size = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return size;
+}
+
+/**
+ * gimp_context_set_ink_size_sensitivity:
+ * @size: ink size sensitivity.
+ *
+ * Set ink size sensitivity.
+ *
+ * Set the ink size sensitivity for ink tool.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_set_ink_size_sensitivity (gdouble size)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-ink-size-sensitivity",
+ &nreturn_vals,
+ GIMP_PDB_FLOAT, size,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_ink_tilt_sensitivity:
+ *
+ * Get ink tilt sensitivity.
+ *
+ * Get the ink tilt sensitivity for ink tool.
+ *
+ * Returns: ink tilt sensitivity.
+ *
+ * Since: 2.8
+ **/
+gdouble
+gimp_context_get_ink_tilt_sensitivity (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble tilt = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-ink-tilt-sensitivity",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ tilt = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return tilt;
+}
+
+/**
+ * gimp_context_set_ink_tilt_sensitivity:
+ * @tilt: ink tilt sensitivity.
+ *
+ * Set ink tilt sensitivity.
+ *
+ * Set the ink tilt sensitivity for ink tool.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_set_ink_tilt_sensitivity (gdouble tilt)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-ink-tilt-sensitivity",
+ &nreturn_vals,
+ GIMP_PDB_FLOAT, tilt,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_ink_speed_sensitivity:
+ *
+ * Get ink speed sensitivity.
+ *
+ * Get the ink speed sensitivity for ink tool.
+ *
+ * Returns: ink speed sensitivity.
+ *
+ * Since: 2.8
+ **/
+gdouble
+gimp_context_get_ink_speed_sensitivity (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble speed = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-ink-speed-sensitivity",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ speed = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return speed;
+}
+
+/**
+ * gimp_context_set_ink_speed_sensitivity:
+ * @speed: ink speed sensitivity.
+ *
+ * Set ink speed sensitivity.
+ *
+ * Set the ink speed sensitivity for ink tool.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_set_ink_speed_sensitivity (gdouble speed)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-ink-speed-sensitivity",
+ &nreturn_vals,
+ GIMP_PDB_FLOAT, speed,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_ink_blob_type:
+ *
+ * Get ink blob type.
+ *
+ * Get the ink blob type for ink tool.
+ *
+ * Returns: Ink blob type.
+ *
+ * Since: 2.8
+ **/
+GimpInkBlobType
+gimp_context_get_ink_blob_type (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpInkBlobType type = 0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-ink-blob-type",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ type = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return type;
+}
+
+/**
+ * gimp_context_set_ink_blob_type:
+ * @type: Ink blob type.
+ *
+ * Set ink blob type.
+ *
+ * Set the ink blob type for ink tool.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_set_ink_blob_type (GimpInkBlobType type)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-ink-blob-type",
+ &nreturn_vals,
+ GIMP_PDB_INT32, type,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_ink_blob_aspect_ratio:
+ *
+ * Get ink blob aspect ratio.
+ *
+ * Get the ink blob aspect ratio for ink tool.
+ *
+ * Returns: ink blob aspect ratio.
+ *
+ * Since: 2.8
+ **/
+gdouble
+gimp_context_get_ink_blob_aspect_ratio (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble aspect = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-ink-blob-aspect-ratio",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ aspect = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return aspect;
+}
+
+/**
+ * gimp_context_set_ink_blob_aspect_ratio:
+ * @aspect: ink blob aspect ratio.
+ *
+ * Set ink blob aspect ratio.
+ *
+ * Set the ink blob aspect ratio for ink tool.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_set_ink_blob_aspect_ratio (gdouble aspect)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-ink-blob-aspect-ratio",
+ &nreturn_vals,
+ GIMP_PDB_FLOAT, aspect,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_context_get_ink_blob_angle:
+ *
+ * Get ink blob angle in degrees.
+ *
+ * Get the ink blob angle in degrees for ink tool.
+ *
+ * Returns: ink blob angle in degrees.
+ *
+ * Since: 2.8
+ **/
+gdouble
+gimp_context_get_ink_blob_angle (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble angle = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-context-get-ink-blob-angle",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ angle = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return angle;
+}
+
+/**
+ * gimp_context_set_ink_blob_angle:
+ * @angle: ink blob angle in degrees.
+ *
+ * Set ink blob angle in degrees.
+ *
+ * Set the ink blob angle in degrees for ink tool.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_context_set_ink_blob_angle (gdouble angle)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-context-set-ink-blob-angle",
+ &nreturn_vals,
+ GIMP_PDB_FLOAT, angle,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimpcontext_pdb.h b/libgimp/gimpcontext_pdb.h
new file mode 100644
index 0000000..e735296
--- /dev/null
+++ b/libgimp/gimpcontext_pdb.h
@@ -0,0 +1,161 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpcontext_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_CONTEXT_PDB_H__
+#define __GIMP_CONTEXT_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_context_push (void);
+gboolean gimp_context_pop (void);
+gboolean gimp_context_set_defaults (void);
+gboolean gimp_context_list_paint_methods (gint *num_paint_methods,
+ gchar ***paint_methods);
+gchar* gimp_context_get_paint_method (void);
+gboolean gimp_context_set_paint_method (const gchar *name);
+GimpStrokeMethod gimp_context_get_stroke_method (void);
+gboolean gimp_context_set_stroke_method (GimpStrokeMethod stroke_method);
+gboolean gimp_context_get_foreground (GimpRGB *foreground);
+gboolean gimp_context_set_foreground (const GimpRGB *foreground);
+gboolean gimp_context_get_background (GimpRGB *background);
+gboolean gimp_context_set_background (const GimpRGB *background);
+gboolean gimp_context_set_default_colors (void);
+gboolean gimp_context_swap_colors (void);
+gdouble gimp_context_get_opacity (void);
+gboolean gimp_context_set_opacity (gdouble opacity);
+GimpLayerMode gimp_context_get_paint_mode (void);
+gboolean gimp_context_set_paint_mode (GimpLayerMode paint_mode);
+gdouble gimp_context_get_line_width (void);
+gboolean gimp_context_set_line_width (gdouble line_width);
+GimpUnit gimp_context_get_line_width_unit (void);
+gboolean gimp_context_set_line_width_unit (GimpUnit line_width_unit);
+GimpCapStyle gimp_context_get_line_cap_style (void);
+gboolean gimp_context_set_line_cap_style (GimpCapStyle cap_style);
+GimpJoinStyle gimp_context_get_line_join_style (void);
+gboolean gimp_context_set_line_join_style (GimpJoinStyle join_style);
+gdouble gimp_context_get_line_miter_limit (void);
+gboolean gimp_context_set_line_miter_limit (gdouble miter_limit);
+gdouble gimp_context_get_line_dash_offset (void);
+gboolean gimp_context_set_line_dash_offset (gdouble dash_offset);
+gboolean gimp_context_get_line_dash_pattern (gint *num_dashes,
+ gdouble **dashes);
+gboolean gimp_context_set_line_dash_pattern (gint num_dashes,
+ const gdouble *dashes);
+gchar* gimp_context_get_brush (void);
+gboolean gimp_context_set_brush (const gchar *name);
+gdouble gimp_context_get_brush_size (void);
+gboolean gimp_context_set_brush_size (gdouble size);
+gboolean gimp_context_set_brush_default_size (void);
+gdouble gimp_context_get_brush_aspect_ratio (void);
+gboolean gimp_context_set_brush_aspect_ratio (gdouble aspect);
+gdouble gimp_context_get_brush_angle (void);
+gboolean gimp_context_set_brush_angle (gdouble angle);
+gdouble gimp_context_get_brush_spacing (void);
+gboolean gimp_context_set_brush_spacing (gdouble spacing);
+gboolean gimp_context_set_brush_default_spacing (void);
+gdouble gimp_context_get_brush_hardness (void);
+gboolean gimp_context_set_brush_hardness (gdouble hardness);
+gboolean gimp_context_set_brush_default_hardness (void);
+gdouble gimp_context_get_brush_force (void);
+gboolean gimp_context_set_brush_force (gdouble force);
+gchar* gimp_context_get_dynamics (void);
+gboolean gimp_context_set_dynamics (const gchar *name);
+gchar* gimp_context_get_mypaint_brush (void);
+gboolean gimp_context_set_mypaint_brush (const gchar *name);
+gchar* gimp_context_get_pattern (void);
+gboolean gimp_context_set_pattern (const gchar *name);
+gchar* gimp_context_get_gradient (void);
+gboolean gimp_context_set_gradient (const gchar *name);
+gboolean gimp_context_set_gradient_fg_bg_rgb (void);
+gboolean gimp_context_set_gradient_fg_bg_hsv_cw (void);
+gboolean gimp_context_set_gradient_fg_bg_hsv_ccw (void);
+gboolean gimp_context_set_gradient_fg_transparent (void);
+GimpGradientBlendColorSpace gimp_context_get_gradient_blend_color_space (void);
+gboolean gimp_context_set_gradient_blend_color_space (GimpGradientBlendColorSpace blend_color_space);
+GimpRepeatMode gimp_context_get_gradient_repeat_mode (void);
+gboolean gimp_context_set_gradient_repeat_mode (GimpRepeatMode repeat_mode);
+gboolean gimp_context_get_gradient_reverse (void);
+gboolean gimp_context_set_gradient_reverse (gboolean reverse);
+gchar* gimp_context_get_palette (void);
+gboolean gimp_context_set_palette (const gchar *name);
+gchar* gimp_context_get_font (void);
+gboolean gimp_context_set_font (const gchar *name);
+gboolean gimp_context_get_antialias (void);
+gboolean gimp_context_set_antialias (gboolean antialias);
+gboolean gimp_context_get_feather (void);
+gboolean gimp_context_set_feather (gboolean feather);
+gboolean gimp_context_get_feather_radius (gdouble *feather_radius_x,
+ gdouble *feather_radius_y);
+gboolean gimp_context_set_feather_radius (gdouble feather_radius_x,
+ gdouble feather_radius_y);
+gboolean gimp_context_get_sample_merged (void);
+gboolean gimp_context_set_sample_merged (gboolean sample_merged);
+GimpSelectCriterion gimp_context_get_sample_criterion (void);
+gboolean gimp_context_set_sample_criterion (GimpSelectCriterion sample_criterion);
+gdouble gimp_context_get_sample_threshold (void);
+gboolean gimp_context_set_sample_threshold (gdouble sample_threshold);
+gint gimp_context_get_sample_threshold_int (void);
+gboolean gimp_context_set_sample_threshold_int (gint sample_threshold);
+gboolean gimp_context_get_sample_transparent (void);
+gboolean gimp_context_set_sample_transparent (gboolean sample_transparent);
+gboolean gimp_context_get_diagonal_neighbors (void);
+gboolean gimp_context_set_diagonal_neighbors (gboolean diagonal_neighbors);
+GeglDistanceMetric gimp_context_get_distance_metric (void);
+gboolean gimp_context_set_distance_metric (GeglDistanceMetric metric);
+GimpInterpolationType gimp_context_get_interpolation (void);
+gboolean gimp_context_set_interpolation (GimpInterpolationType interpolation);
+GimpTransformDirection gimp_context_get_transform_direction (void);
+gboolean gimp_context_set_transform_direction (GimpTransformDirection transform_direction);
+GimpTransformResize gimp_context_get_transform_resize (void);
+gboolean gimp_context_set_transform_resize (GimpTransformResize transform_resize);
+GIMP_DEPRECATED
+gint gimp_context_get_transform_recursion (void);
+GIMP_DEPRECATED
+gboolean gimp_context_set_transform_recursion (gint transform_recursion);
+gdouble gimp_context_get_ink_size (void);
+gboolean gimp_context_set_ink_size (gdouble size);
+gdouble gimp_context_get_ink_angle (void);
+gboolean gimp_context_set_ink_angle (gdouble angle);
+gdouble gimp_context_get_ink_size_sensitivity (void);
+gboolean gimp_context_set_ink_size_sensitivity (gdouble size);
+gdouble gimp_context_get_ink_tilt_sensitivity (void);
+gboolean gimp_context_set_ink_tilt_sensitivity (gdouble tilt);
+gdouble gimp_context_get_ink_speed_sensitivity (void);
+gboolean gimp_context_set_ink_speed_sensitivity (gdouble speed);
+GimpInkBlobType gimp_context_get_ink_blob_type (void);
+gboolean gimp_context_set_ink_blob_type (GimpInkBlobType type);
+gdouble gimp_context_get_ink_blob_aspect_ratio (void);
+gboolean gimp_context_set_ink_blob_aspect_ratio (gdouble aspect);
+gdouble gimp_context_get_ink_blob_angle (void);
+gboolean gimp_context_set_ink_blob_angle (gdouble angle);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_CONTEXT_PDB_H__ */
diff --git a/libgimp/gimpdebug_pdb.c b/libgimp/gimpdebug_pdb.c
new file mode 100644
index 0000000..64c165b
--- /dev/null
+++ b/libgimp/gimpdebug_pdb.c
@@ -0,0 +1,105 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpdebug_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpdebug
+ * @title: gimpdebug
+ * @short_description: Debug utility functions
+ *
+ * Miscellaneous debug utility functions. Not part of the stable
+ * library interface.
+ **/
+
+
+/**
+ * gimp_debug_timer_start:
+ *
+ * Starts measuring elapsed time.
+ *
+ * This procedure starts a timer, measuring the elapsed time since the
+ * call. Each call to this procedure should be matched by a call to
+ * gimp_debug_timer_end(), which returns the elapsed time.
+ * If there is already an active timer, it is not affected by the call,
+ * however, a matching gimp_debug_timer_end() call is still required.
+ *
+ * This is a debug utility procedure. It is subject to change at any
+ * point, and should not be used in production.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_debug_timer_start (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-debug-timer-start",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_debug_timer_end:
+ *
+ * Finishes measuring elapsed time.
+ *
+ * This procedure stops the timer started by a previous
+ * gimp_debug_timer_start() call, and prints and returns the elapsed
+ * time.
+ * If there was already an active timer at the time of corresponding
+ * call to gimp_debug_timer_start(), a dummy value is returned.
+ *
+ * This is a debug utility procedure. It is subject to change at any
+ * point, and should not be used in production.
+ *
+ * Returns: The elapsed time, in seconds.
+ **/
+gdouble
+gimp_debug_timer_end (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble elapsed = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-debug-timer-end",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ elapsed = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return elapsed;
+}
diff --git a/libgimp/gimpdebug_pdb.h b/libgimp/gimpdebug_pdb.h
new file mode 100644
index 0000000..234dc18
--- /dev/null
+++ b/libgimp/gimpdebug_pdb.h
@@ -0,0 +1,41 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpdebug_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_DEBUG_PDB_H__
+#define __GIMP_DEBUG_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_debug_timer_start (void);
+gdouble gimp_debug_timer_end (void);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_DEBUG_PDB_H__ */
diff --git a/libgimp/gimpdisplay_pdb.c b/libgimp/gimpdisplay_pdb.c
new file mode 100644
index 0000000..9f1eba0
--- /dev/null
+++ b/libgimp/gimpdisplay_pdb.c
@@ -0,0 +1,237 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpdisplay_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpdisplay
+ * @title: gimpdisplay
+ * @short_description: Functions to create, delete and flush displays (views) on an image.
+ *
+ * Functions to create, delete and flush displays (views) on an image.
+ **/
+
+
+/**
+ * gimp_display_is_valid:
+ * @display_ID: The display to check.
+ *
+ * Returns TRUE if the display is valid.
+ *
+ * This procedure checks if the given display ID is valid and refers to
+ * an existing display.
+ *
+ * Returns: Whether the display ID is valid.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_display_is_valid (gint32 display_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean valid = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-display-is-valid",
+ &nreturn_vals,
+ GIMP_PDB_DISPLAY, display_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ valid = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return valid;
+}
+
+/**
+ * gimp_display_new:
+ * @image_ID: The image.
+ *
+ * Create a new display for the specified image.
+ *
+ * Creates a new display for the specified image. If the image already
+ * has a display, another is added. Multiple displays are handled
+ * transparently by GIMP. The newly created display is returned and can
+ * be subsequently destroyed with a call to gimp_display_delete(). This
+ * procedure only makes sense for use with the GIMP UI, and will result
+ * in an execution error if called when GIMP has no UI.
+ *
+ * Returns: The new display.
+ **/
+gint32
+gimp_display_new (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 display_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-display-new",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ display_ID = return_vals[1].data.d_display;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return display_ID;
+}
+
+/**
+ * gimp_display_delete:
+ * @display_ID: The display to delete.
+ *
+ * Delete the specified display.
+ *
+ * This procedure removes the specified display. If this is the last
+ * remaining display for the underlying image, then the image is
+ * deleted also. Note that the display is closed no matter if the image
+ * is dirty or not. Better save the image before calling this
+ * procedure.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_display_delete (gint32 display_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-display-delete",
+ &nreturn_vals,
+ GIMP_PDB_DISPLAY, display_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_display_get_window_handle:
+ * @display_ID: The display to get the window handle from.
+ *
+ * Get a handle to the native window for an image display.
+ *
+ * This procedure returns a handle to the native window for a given
+ * image display. For example in the X backend of GDK, a native window
+ * handle is an Xlib XID. A value of 0 is returned for an invalid
+ * display or if this function is unimplemented for the windowing
+ * system that is being used.
+ *
+ * Returns: The native window handle or 0.
+ *
+ * Since: 2.4
+ **/
+gint
+gimp_display_get_window_handle (gint32 display_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint window = 0;
+
+ return_vals = gimp_run_procedure ("gimp-display-get-window-handle",
+ &nreturn_vals,
+ GIMP_PDB_DISPLAY, display_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ window = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return window;
+}
+
+/**
+ * gimp_displays_flush:
+ *
+ * Flush all internal changes to the user interface
+ *
+ * This procedure takes no arguments and returns nothing except a
+ * success status. Its purpose is to flush all pending updates of image
+ * manipulations to the user interface. It should be called whenever
+ * appropriate.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_displays_flush (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-displays-flush",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_displays_reconnect:
+ * @old_image_ID: The old image (must have at least one display).
+ * @new_image_ID: The new image (must not have a display).
+ *
+ * Reconnect displays from one image to another image.
+ *
+ * This procedure connects all displays of the old_image to the
+ * new_image. If the old_image has no display or new_image already has
+ * a display the reconnect is not performed and the procedure returns
+ * without success. You should rarely need to use this function.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_displays_reconnect (gint32 old_image_ID,
+ gint32 new_image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-displays-reconnect",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, old_image_ID,
+ GIMP_PDB_IMAGE, new_image_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimpdisplay_pdb.h b/libgimp/gimpdisplay_pdb.h
new file mode 100644
index 0000000..b9cdeb9
--- /dev/null
+++ b/libgimp/gimpdisplay_pdb.h
@@ -0,0 +1,46 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpdisplay_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_DISPLAY_PDB_H__
+#define __GIMP_DISPLAY_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_display_is_valid (gint32 display_ID);
+gint32 gimp_display_new (gint32 image_ID);
+gboolean gimp_display_delete (gint32 display_ID);
+gint gimp_display_get_window_handle (gint32 display_ID);
+gboolean gimp_displays_flush (void);
+gboolean gimp_displays_reconnect (gint32 old_image_ID,
+ gint32 new_image_ID);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_DISPLAY_PDB_H__ */
diff --git a/libgimp/gimpdrawable.c b/libgimp/gimpdrawable.c
new file mode 100644
index 0000000..5ea1079
--- /dev/null
+++ b/libgimp/gimpdrawable.c
@@ -0,0 +1,822 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpdrawable.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#define GIMP_DISABLE_DEPRECATION_WARNINGS
+
+#include "gimp.h"
+
+#include "gimptilebackendplugin.h"
+
+
+#define TILE_WIDTH gimp_tile_width()
+#define TILE_HEIGHT gimp_tile_height()
+
+
+/**
+ * gimp_drawable_get:
+ * @drawable_ID: the ID of the drawable
+ *
+ * This function creates a #GimpDrawable structure for the core
+ * drawable identified by @drawable_ID. The returned structure
+ * contains some basic information about the drawable and can also
+ * hold tile data for transfer to and from the core.
+ *
+ * Note that the name of this function is somewhat misleading, because
+ * it suggests that it simply returns a handle. This is not the case:
+ * if the function is called multiple times, it creates separate tile
+ * lists each time, which will usually produce undesired results.
+ *
+ * When a plug-in has finished working with a drawable, before exiting
+ * it should call gimp_drawable_detach() to make sure that all tile data is
+ * transferred back to the core.
+ *
+ * Return value: a new #GimpDrawable wrapper
+ **/
+GimpDrawable *
+gimp_drawable_get (gint32 drawable_ID)
+{
+ GimpDrawable *drawable;
+ gint width;
+ gint height;
+ gint bpp;
+
+ width = gimp_drawable_width (drawable_ID);
+ height = gimp_drawable_height (drawable_ID);
+ bpp = gimp_drawable_bpp (drawable_ID);
+
+ g_return_val_if_fail (width > 0 && height > 0 && bpp > 0, NULL);
+
+ drawable = g_slice_new0 (GimpDrawable);
+
+ drawable->drawable_id = drawable_ID;
+ drawable->width = width;
+ drawable->height = height;
+ drawable->bpp = bpp;
+ drawable->ntile_rows = (height + TILE_HEIGHT - 1) / TILE_HEIGHT;
+ drawable->ntile_cols = (width + TILE_WIDTH - 1) / TILE_WIDTH;
+
+ return drawable;
+}
+
+/**
+ * gimp_drawable_detach:
+ * @drawable: The #GimpDrawable to detach from the core
+ *
+ * This function is called when a plug-in is finished working
+ * with a drawable. It forces all tile data held in the tile
+ * list of the #GimpDrawable to be transferred to the core, and
+ * then frees all associated memory. You must not access the
+ * @drawable after having called gimp_drawable_detach().
+ **/
+void
+gimp_drawable_detach (GimpDrawable *drawable)
+{
+ g_return_if_fail (drawable != NULL);
+
+ gimp_drawable_flush (drawable);
+
+ if (drawable->tiles)
+ g_free (drawable->tiles);
+
+ if (drawable->shadow_tiles)
+ g_free (drawable->shadow_tiles);
+
+ g_slice_free (GimpDrawable, drawable);
+}
+
+/**
+ * gimp_drawable_flush:
+ * @drawable: The #GimpDrawable whose tile data is to be transferred
+ * to the core.
+ *
+ * This function causes all tile data in the tile list of @drawable to be
+ * transferred to the core. It is usually called in situations where a
+ * plug-in acts on a drawable, and then needs to read the results of its
+ * actions. Data transferred back from the core will not generally be valid
+ * unless gimp_drawable_flush() has been called beforehand.
+ **/
+void
+gimp_drawable_flush (GimpDrawable *drawable)
+{
+ GimpTile *tiles;
+ gint n_tiles;
+ gint i;
+
+ g_return_if_fail (drawable != NULL);
+
+ if (drawable->tiles)
+ {
+ tiles = drawable->tiles;
+ n_tiles = drawable->ntile_rows * drawable->ntile_cols;
+
+ for (i = 0; i < n_tiles; i++)
+ if ((tiles[i].ref_count > 0) && tiles[i].dirty)
+ gimp_tile_flush (&tiles[i]);
+ }
+
+ if (drawable->shadow_tiles)
+ {
+ tiles = drawable->shadow_tiles;
+ n_tiles = drawable->ntile_rows * drawable->ntile_cols;
+
+ for (i = 0; i < n_tiles; i++)
+ if ((tiles[i].ref_count > 0) && tiles[i].dirty)
+ gimp_tile_flush (&tiles[i]);
+ }
+
+ /* nuke all references to this drawable from the cache */
+ _gimp_tile_cache_flush_drawable (drawable);
+}
+
+GimpTile *
+gimp_drawable_get_tile (GimpDrawable *drawable,
+ gboolean shadow,
+ gint row,
+ gint col)
+{
+ GimpTile *tiles;
+ guint right_tile;
+ guint bottom_tile;
+ gint n_tiles;
+ gint tile_num;
+ gint i, j, k;
+
+ g_return_val_if_fail (drawable != NULL, NULL);
+
+ if (shadow)
+ tiles = drawable->shadow_tiles;
+ else
+ tiles = drawable->tiles;
+
+ if (! tiles)
+ {
+ n_tiles = drawable->ntile_rows * drawable->ntile_cols;
+ tiles = g_new (GimpTile, n_tiles);
+
+ right_tile = (drawable->width -
+ ((drawable->ntile_cols - 1) * TILE_WIDTH));
+ bottom_tile = (drawable->height -
+ ((drawable->ntile_rows - 1) * TILE_HEIGHT));
+
+ for (i = 0, k = 0; i < drawable->ntile_rows; i++)
+ {
+ for (j = 0; j < drawable->ntile_cols; j++, k++)
+ {
+ tiles[k].bpp = drawable->bpp;
+ tiles[k].tile_num = k;
+ tiles[k].ref_count = 0;
+ tiles[k].dirty = FALSE;
+ tiles[k].shadow = shadow;
+ tiles[k].data = NULL;
+ tiles[k].drawable = drawable;
+
+ if (j == (drawable->ntile_cols - 1))
+ tiles[k].ewidth = right_tile;
+ else
+ tiles[k].ewidth = TILE_WIDTH;
+
+ if (i == (drawable->ntile_rows - 1))
+ tiles[k].eheight = bottom_tile;
+ else
+ tiles[k].eheight = TILE_HEIGHT;
+ }
+ }
+
+ if (shadow)
+ drawable->shadow_tiles = tiles;
+ else
+ drawable->tiles = tiles;
+ }
+
+ tile_num = row * drawable->ntile_cols + col;
+
+ return &tiles[tile_num];
+}
+
+GimpTile *
+gimp_drawable_get_tile2 (GimpDrawable *drawable,
+ gboolean shadow,
+ gint x,
+ gint y)
+{
+ gint row;
+ gint col;
+
+ g_return_val_if_fail (drawable != NULL, NULL);
+
+ col = x / TILE_WIDTH;
+ row = y / TILE_HEIGHT;
+
+ return gimp_drawable_get_tile (drawable, shadow, row, col);
+}
+
+void
+gimp_drawable_get_color_uchar (gint32 drawable_ID,
+ const GimpRGB *color,
+ guchar *color_uchar)
+{
+ g_return_if_fail (color != NULL);
+ g_return_if_fail (color_uchar != NULL);
+
+ switch (gimp_drawable_type (drawable_ID))
+ {
+ case GIMP_RGB_IMAGE:
+ gimp_rgb_get_uchar (color,
+ &color_uchar[0], &color_uchar[1], &color_uchar[2]);
+ color_uchar[3] = 255;
+ break;
+
+ case GIMP_RGBA_IMAGE:
+ gimp_rgba_get_uchar (color,
+ &color_uchar[0], &color_uchar[1], &color_uchar[2],
+ &color_uchar[3]);
+ break;
+
+ case GIMP_GRAY_IMAGE:
+ color_uchar[0] = gimp_rgb_luminance_uchar (color);
+ color_uchar[1] = 255;
+ break;
+
+ case GIMP_GRAYA_IMAGE:
+ color_uchar[0] = gimp_rgb_luminance_uchar (color);
+ gimp_rgba_get_uchar (color, NULL, NULL, NULL, &color_uchar[1]);
+ break;
+
+ default:
+ break;
+ }
+}
+
+guchar *
+gimp_drawable_get_thumbnail_data (gint32 drawable_ID,
+ gint *width,
+ gint *height,
+ gint *bpp)
+{
+ gint ret_width;
+ gint ret_height;
+ guchar *image_data;
+ gint data_size;
+
+ _gimp_drawable_thumbnail (drawable_ID,
+ *width,
+ *height,
+ &ret_width,
+ &ret_height,
+ bpp,
+ &data_size,
+ &image_data);
+
+ *width = ret_width;
+ *height = ret_height;
+
+ return image_data;
+}
+
+guchar *
+gimp_drawable_get_sub_thumbnail_data (gint32 drawable_ID,
+ gint src_x,
+ gint src_y,
+ gint src_width,
+ gint src_height,
+ gint *dest_width,
+ gint *dest_height,
+ gint *bpp)
+{
+ gint ret_width;
+ gint ret_height;
+ guchar *image_data;
+ gint data_size;
+
+ _gimp_drawable_sub_thumbnail (drawable_ID,
+ src_x, src_y,
+ src_width, src_height,
+ *dest_width,
+ *dest_height,
+ &ret_width,
+ &ret_height,
+ bpp,
+ &data_size,
+ &image_data);
+
+ *dest_width = ret_width;
+ *dest_height = ret_height;
+
+ return image_data;
+}
+
+/**
+ * gimp_drawable_is_valid:
+ * @drawable_ID: The drawable to check.
+ *
+ * Deprecated: Use gimp_item_is_valid() instead.
+ *
+ * Returns: Whether the drawable ID is valid.
+ *
+ * Since: 2.4
+ */
+gboolean
+gimp_drawable_is_valid (gint32 drawable_ID)
+{
+ return gimp_item_is_valid (drawable_ID);
+}
+
+/**
+ * gimp_drawable_is_layer:
+ * @drawable_ID: The drawable.
+ *
+ * Deprecated: Use gimp_item_is_layer() instead.
+ *
+ * Returns: TRUE if the drawable is a layer, FALSE otherwise.
+ */
+gboolean
+gimp_drawable_is_layer (gint32 drawable_ID)
+{
+ return gimp_item_is_layer (drawable_ID);
+}
+
+/**
+ * gimp_drawable_is_text_layer:
+ * @drawable_ID: The drawable.
+ *
+ * Deprecated: Use gimp_item_is_text_layer() instead.
+ *
+ * Returns: TRUE if the drawable is a text layer, FALSE otherwise.
+ *
+ * Since: 2.6
+ */
+gboolean
+gimp_drawable_is_text_layer (gint32 drawable_ID)
+{
+ return gimp_item_is_text_layer (drawable_ID);
+}
+
+/**
+ * gimp_drawable_is_layer_mask:
+ * @drawable_ID: The drawable.
+ *
+ * Deprecated: Use gimp_item_is_layer_mask() instead.
+ *
+ * Returns: TRUE if the drawable is a layer mask, FALSE otherwise.
+ */
+gboolean
+gimp_drawable_is_layer_mask (gint32 drawable_ID)
+{
+ return gimp_item_is_layer_mask (drawable_ID);
+}
+
+/**
+ * gimp_drawable_is_channel:
+ * @drawable_ID: The drawable.
+ *
+ * Deprecated: Use gimp_item_is_channel() instead.
+ *
+ * Returns: TRUE if the drawable is a channel, FALSE otherwise.
+ */
+gboolean
+gimp_drawable_is_channel (gint32 drawable_ID)
+{
+ return gimp_item_is_channel (drawable_ID);
+}
+
+/**
+ * gimp_drawable_delete:
+ * @drawable_ID: The drawable to delete.
+ *
+ * Deprecated: Use gimp_item_delete() instead.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+gimp_drawable_delete (gint32 drawable_ID)
+{
+ return gimp_item_delete (drawable_ID);
+}
+
+/**
+ * gimp_drawable_get_image:
+ * @drawable_ID: The drawable.
+ *
+ * Deprecated: Use gimp_item_get_image() instead.
+ *
+ * Returns: The drawable's image.
+ */
+gint32
+gimp_drawable_get_image (gint32 drawable_ID)
+{
+ return gimp_item_get_image (drawable_ID);
+}
+
+/**
+ * gimp_drawable_get_name:
+ * @drawable_ID: The drawable.
+ *
+ * Deprecated: Use gimp_item_get_name() instead.
+ *
+ * Returns: The drawable name.
+ */
+gchar *
+gimp_drawable_get_name (gint32 drawable_ID)
+{
+ return gimp_item_get_name (drawable_ID);
+}
+
+/**
+ * gimp_drawable_set_name:
+ * @drawable_ID: The drawable.
+ * @name: The new drawable name.
+ *
+ * Deprecated: Use gimp_item_set_name() instead.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+gimp_drawable_set_name (gint32 drawable_ID,
+ const gchar *name)
+{
+ return gimp_item_set_name (drawable_ID, name);
+}
+
+/**
+ * gimp_drawable_get_visible:
+ * @drawable_ID: The drawable.
+ *
+ * Deprecated: Use gimp_item_get_visible() instead.
+ *
+ * Returns: The drawable visibility.
+ */
+gboolean
+gimp_drawable_get_visible (gint32 drawable_ID)
+{
+ return gimp_item_get_visible (drawable_ID);
+}
+
+/**
+ * gimp_drawable_set_visible:
+ * @drawable_ID: The drawable.
+ * @visible: The new drawable visibility.
+ *
+ * Deprecated: Use gimp_item_set_visible() instead.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+gimp_drawable_set_visible (gint32 drawable_ID,
+ gboolean visible)
+{
+ return gimp_item_set_visible (drawable_ID, visible);
+}
+
+/**
+ * gimp_drawable_get_linked:
+ * @drawable_ID: The drawable.
+ *
+ * Deprecated: Use gimp_item_get_linked() instead.
+ *
+ * Returns: The drawable linked state (for moves).
+ */
+gboolean
+gimp_drawable_get_linked (gint32 drawable_ID)
+{
+ return gimp_item_get_linked (drawable_ID);
+}
+
+/**
+ * gimp_drawable_set_linked:
+ * @drawable_ID: The drawable.
+ * @linked: The new drawable linked state.
+ *
+ * Deprecated: Use gimp_item_set_linked() instead.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+gimp_drawable_set_linked (gint32 drawable_ID,
+ gboolean linked)
+{
+ return gimp_item_set_linked (drawable_ID, linked);
+}
+
+/**
+ * gimp_drawable_get_tattoo:
+ * @drawable_ID: The drawable.
+ *
+ * Deprecated: Use gimp_item_get_tattoo() instead.
+ *
+ * Returns: The drawable tattoo.
+ */
+gint
+gimp_drawable_get_tattoo (gint32 drawable_ID)
+{
+ return gimp_item_get_tattoo (drawable_ID);
+}
+
+/**
+ * gimp_drawable_set_tattoo:
+ * @drawable_ID: The drawable.
+ * @tattoo: The new drawable tattoo.
+ *
+ * Deprecated: Use gimp_item_set_tattoo() instead.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+gimp_drawable_set_tattoo (gint32 drawable_ID,
+ gint tattoo)
+{
+ return gimp_item_set_tattoo (drawable_ID, tattoo);
+}
+
+/**
+ * gimp_drawable_parasite_find:
+ * @drawable_ID: The drawable.
+ * @name: The name of the parasite to find.
+ *
+ * Deprecated: Use gimp_item_get_parasite() instead.
+ *
+ * Returns: The found parasite.
+ **/
+GimpParasite *
+gimp_drawable_parasite_find (gint32 drawable_ID,
+ const gchar *name)
+{
+ return gimp_item_get_parasite (drawable_ID, name);
+}
+
+/**
+ * gimp_drawable_parasite_attach:
+ * @drawable_ID: The drawable.
+ * @parasite: The parasite to attach to a drawable.
+ *
+ * Deprecated: Use gimp_item_attach_parasite() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_drawable_parasite_attach (gint32 drawable_ID,
+ const GimpParasite *parasite)
+{
+ return gimp_item_attach_parasite (drawable_ID, parasite);
+}
+
+/**
+ * gimp_drawable_parasite_detach:
+ * @drawable_ID: The drawable.
+ * @name: The name of the parasite to detach from a drawable.
+ *
+ * Deprecated: Use gimp_item_detach_parasite() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_drawable_parasite_detach (gint32 drawable_ID,
+ const gchar *name)
+{
+ return gimp_item_detach_parasite (drawable_ID, name);
+}
+
+/**
+ * gimp_drawable_parasite_list:
+ * @drawable_ID: The drawable.
+ * @num_parasites: The number of attached parasites.
+ * @parasites: The names of currently attached parasites.
+ *
+ * Deprecated: Use gimp_item_get_parasite_list() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_drawable_parasite_list (gint32 drawable_ID,
+ gint *num_parasites,
+ gchar ***parasites)
+{
+ *parasites = gimp_item_get_parasite_list (drawable_ID, num_parasites);
+
+ return *parasites != NULL;
+}
+
+/**
+ * gimp_drawable_attach_new_parasite:
+ * @drawable_ID: the ID of the #GimpDrawable to attach the #GimpParasite to.
+ * @name: the name of the #GimpParasite to create and attach.
+ * @flags: the flags set on the #GimpParasite.
+ * @size: the size of the parasite data in bytes.
+ * @data: a pointer to the data attached with the #GimpParasite.
+ *
+ * Convenience function that creates a parasite and attaches it
+ * to GIMP.
+ *
+ * Deprecated: use gimp_item_attach_parasite() instead.
+ *
+ * Return value: TRUE on successful creation and attachment of
+ * the new parasite.
+ *
+ * See Also: gimp_drawable_parasite_attach()
+ */
+gboolean
+gimp_drawable_attach_new_parasite (gint32 drawable_ID,
+ const gchar *name,
+ gint flags,
+ gint size,
+ gconstpointer data)
+{
+ GimpParasite *parasite = gimp_parasite_new (name, flags, size, data);
+ gboolean success;
+
+ success = gimp_item_attach_parasite (drawable_ID, parasite);
+
+ gimp_parasite_free (parasite);
+
+ return success;
+}
+
+/**
+ * gimp_drawable_get_buffer:
+ * @drawable_ID: the ID of the #GimpDrawable to get the buffer for.
+ *
+ * Returns a #GeglBuffer of a specified drawable. The buffer can be used
+ * like any other GEGL buffer. Its data will we synced back with the core
+ * drawable when the buffer gets destroyed, or when gegl_buffer_flush()
+ * is called.
+ *
+ * Return value: The #GeglBuffer.
+ *
+ * See Also: gimp_drawable_get_shadow_buffer()
+ *
+ * Since: 2.10
+ */
+GeglBuffer *
+gimp_drawable_get_buffer (gint32 drawable_ID)
+{
+ gimp_plugin_enable_precision ();
+
+ if (gimp_item_is_valid (drawable_ID))
+ {
+ GimpDrawable *drawable;
+
+ drawable = gimp_drawable_get (drawable_ID);
+
+ if (drawable)
+ {
+ GeglTileBackend *backend;
+ GeglBuffer *buffer;
+
+ backend = _gimp_tile_backend_plugin_new (drawable, FALSE);
+ buffer = gegl_buffer_new_for_backend (NULL, backend);
+ g_object_unref (backend);
+
+ return buffer;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * gimp_drawable_get_shadow_buffer:
+ * @drawable_ID: the ID of the #GimpDrawable to get the buffer for.
+ *
+ * Returns a #GeglBuffer of a specified drawable's shadow tiles. The
+ * buffer can be used like any other GEGL buffer. Its data will we
+ * synced back with the core drawable's shadow tiles when the buffer
+ * gets destroyed, or when gegl_buffer_flush() is called.
+ *
+ * Return value: The #GeglBuffer.
+ *
+ * See Also: gimp_drawable_get_shadow_buffer()
+ *
+ * Since: 2.10
+ */
+GeglBuffer *
+gimp_drawable_get_shadow_buffer (gint32 drawable_ID)
+{
+ GimpDrawable *drawable;
+
+ gimp_plugin_enable_precision ();
+
+ drawable = gimp_drawable_get (drawable_ID);
+
+ if (drawable)
+ {
+ GeglTileBackend *backend;
+ GeglBuffer *buffer;
+
+ backend = _gimp_tile_backend_plugin_new (drawable, TRUE);
+ buffer = gegl_buffer_new_for_backend (NULL, backend);
+ g_object_unref (backend);
+
+ return buffer;
+ }
+
+ return NULL;
+}
+
+/**
+ * gimp_drawable_get_format:
+ * @drawable_ID: the ID of the #GimpDrawable to get the format for.
+ *
+ * Returns the #Babl format of the drawable.
+ *
+ * Return value: The #Babl format.
+ *
+ * Since: 2.10
+ */
+const Babl *
+gimp_drawable_get_format (gint32 drawable_ID)
+{
+ static GHashTable *palette_formats = NULL;
+ const Babl *format = NULL;
+ gchar *format_str = _gimp_drawable_get_format (drawable_ID);
+
+ if (format_str)
+ {
+ if (gimp_drawable_is_indexed (drawable_ID))
+ {
+ gint32 image_ID = gimp_item_get_image (drawable_ID);
+ guchar *colormap;
+ gint n_colors;
+
+ colormap = gimp_image_get_colormap (image_ID, &n_colors);
+
+ if (!palette_formats)
+ palette_formats = g_hash_table_new (g_str_hash, g_str_equal);
+
+ format = g_hash_table_lookup (palette_formats, format_str);
+
+ if (!format)
+ {
+ const Babl *palette;
+ const Babl *palette_alpha;
+
+ babl_new_palette (format_str, &palette, &palette_alpha);
+ g_hash_table_insert (palette_formats,
+ (gpointer) babl_get_name (palette),
+ (gpointer) palette);
+ g_hash_table_insert (palette_formats,
+ (gpointer) babl_get_name (palette_alpha),
+ (gpointer) palette_alpha);
+
+ if (gimp_drawable_has_alpha (drawable_ID))
+ format = palette_alpha;
+ else
+ format = palette;
+ }
+
+ if (colormap)
+ {
+ babl_palette_set_palette (format,
+ babl_format ("R'G'B' u8"),
+ colormap, n_colors);
+ g_free (colormap);
+ }
+ }
+ else
+ {
+ format = babl_format (format_str);
+ }
+
+ g_free (format_str);
+ }
+
+ return format;
+}
+/**
+ * gimp_drawable_get_thumbnail_format:
+ * @drawable_ID: the ID of the #GimpDrawable to get the thumbnail format for.
+ *
+ * Returns the #Babl thumbnail format of the drawable.
+ *
+ * Return value: The #Babl thumbnail format.
+ *
+ * Since: 2.10.14
+ */
+const Babl *
+gimp_drawable_get_thumbnail_format (gint32 drawable_ID)
+{
+ const Babl *format = NULL;
+ gchar *format_str = _gimp_drawable_get_thumbnail_format (drawable_ID);
+
+ if (format_str)
+ format = babl_format (format_str);
+
+ return format;
+}
diff --git a/libgimp/gimpdrawable.h b/libgimp/gimpdrawable.h
new file mode 100644
index 0000000..7d7aaec
--- /dev/null
+++ b/libgimp/gimpdrawable.h
@@ -0,0 +1,142 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpdrawable.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_DRAWABLE_H__
+#define __GIMP_DRAWABLE_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+struct _GimpDrawable
+{
+ gint32 drawable_id; /* drawable ID */
+ guint width; /* width of drawble */
+ guint height; /* height of drawble */
+ guint bpp; /* bytes per pixel of drawable */
+ guint ntile_rows; /* # of tile rows */
+ guint ntile_cols; /* # of tile columns */
+ GimpTile *tiles; /* the normal tiles */
+ GimpTile *shadow_tiles; /* the shadow tiles */
+};
+
+
+GeglBuffer * gimp_drawable_get_buffer (gint32 drawable_ID);
+GeglBuffer * gimp_drawable_get_shadow_buffer (gint32 drawable_ID);
+
+const Babl * gimp_drawable_get_format (gint32 drawable_ID);
+const Babl * gimp_drawable_get_thumbnail_format (gint32 drawable_ID);
+
+GIMP_DEPRECATED_FOR(gimp_drawable_get_buffer)
+GimpDrawable * gimp_drawable_get (gint32 drawable_ID);
+GIMP_DEPRECATED
+void gimp_drawable_detach (GimpDrawable *drawable);
+GIMP_DEPRECATED_FOR(gegl_buffer_flush)
+void gimp_drawable_flush (GimpDrawable *drawable);
+GIMP_DEPRECATED_FOR(gimp_drawable_get_buffer)
+GimpTile * gimp_drawable_get_tile (GimpDrawable *drawable,
+ gboolean shadow,
+ gint row,
+ gint col);
+GIMP_DEPRECATED_FOR(gimp_drawable_get_buffer)
+GimpTile * gimp_drawable_get_tile2 (GimpDrawable *drawable,
+ gboolean shadow,
+ gint x,
+ gint y);
+
+GIMP_DEPRECATED
+void gimp_drawable_get_color_uchar (gint32 drawable_ID,
+ const GimpRGB *color,
+ guchar *color_uchar);
+
+guchar * gimp_drawable_get_thumbnail_data (gint32 drawable_ID,
+ gint *width,
+ gint *height,
+ gint *bpp);
+guchar * gimp_drawable_get_sub_thumbnail_data (gint32 drawable_ID,
+ gint src_x,
+ gint src_y,
+ gint src_width,
+ gint src_height,
+ gint *dest_width,
+ gint *dest_height,
+ gint *bpp);
+
+GIMP_DEPRECATED_FOR(gimp_item_is_valid)
+gboolean gimp_drawable_is_valid (gint32 drawable_ID);
+GIMP_DEPRECATED_FOR(gimp_item_is_layer)
+gboolean gimp_drawable_is_layer (gint32 drawable_ID);
+GIMP_DEPRECATED_FOR(gimp_item_is_text_layer)
+gboolean gimp_drawable_is_text_layer (gint32 drawable_ID);
+GIMP_DEPRECATED_FOR(gimp_item_is_layer_mask)
+gboolean gimp_drawable_is_layer_mask (gint32 drawable_ID);
+GIMP_DEPRECATED_FOR(gimp_item_is_channel)
+gboolean gimp_drawable_is_channel (gint32 drawable_ID);
+GIMP_DEPRECATED_FOR(gimp_item_delete)
+gboolean gimp_drawable_delete (gint32 drawable_ID);
+GIMP_DEPRECATED_FOR(gimp_item_get_image)
+gint32 gimp_drawable_get_image (gint32 drawable_ID);
+GIMP_DEPRECATED_FOR(gimp_item_get_name)
+gchar* gimp_drawable_get_name (gint32 drawable_ID);
+GIMP_DEPRECATED_FOR(gimp_item_set_name)
+gboolean gimp_drawable_set_name (gint32 drawable_ID,
+ const gchar *name);
+GIMP_DEPRECATED_FOR(gimp_item_get_visible)
+gboolean gimp_drawable_get_visible (gint32 drawable_ID);
+GIMP_DEPRECATED_FOR(gimp_item_set_visible)
+gboolean gimp_drawable_set_visible (gint32 drawable_ID,
+ gboolean visible);
+GIMP_DEPRECATED_FOR(gimp_item_get_linked)
+gboolean gimp_drawable_get_linked (gint32 drawable_ID);
+GIMP_DEPRECATED_FOR(gimp_item_set_linked)
+gboolean gimp_drawable_set_linked (gint32 drawable_ID,
+ gboolean linked);
+GIMP_DEPRECATED_FOR(gimp_item_get_tattoo)
+gint gimp_drawable_get_tattoo (gint32 drawable_ID);
+GIMP_DEPRECATED_FOR(gimp_item_set_tattoo)
+gboolean gimp_drawable_set_tattoo (gint32 drawable_ID,
+ gint tattoo);
+GIMP_DEPRECATED_FOR(gimp_item_get_parasite)
+GimpParasite * gimp_drawable_parasite_find (gint32 drawable_ID,
+ const gchar *name);
+GIMP_DEPRECATED_FOR(gimp_item_attach_parasite)
+gboolean gimp_drawable_parasite_attach (gint32 drawable_ID,
+ const GimpParasite *parasite);
+GIMP_DEPRECATED_FOR(gimp_item_detach_parasite)
+gboolean gimp_drawable_parasite_detach (gint32 drawable_ID,
+ const gchar *name);
+GIMP_DEPRECATED_FOR(gimp_item_get_parasite_list)
+gboolean gimp_drawable_parasite_list (gint32 drawable_ID,
+ gint *num_parasites,
+ gchar ***parasites);
+GIMP_DEPRECATED_FOR(gimp_item_attach_parasite)
+gboolean gimp_drawable_attach_new_parasite (gint32 drawable_ID,
+ const gchar *name,
+ gint flags,
+ gint size,
+ gconstpointer data);
+
+G_END_DECLS
+
+#endif /* __GIMP_DRAWABLE_H__ */
diff --git a/libgimp/gimpdrawable_pdb.c b/libgimp/gimpdrawable_pdb.c
new file mode 100644
index 0000000..72da2b2
--- /dev/null
+++ b/libgimp/gimpdrawable_pdb.c
@@ -0,0 +1,1043 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpdrawable_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpdrawable
+ * @title: gimpdrawable
+ * @short_description: Functions to manipulate drawables.
+ *
+ * Functions to manipulate drawables.
+ **/
+
+
+/**
+ * _gimp_drawable_get_format:
+ * @drawable_ID: The drawable.
+ *
+ * Returns the drawable's Babl format
+ *
+ * This procedure returns the drawable's Babl format.
+ *
+ * Returns: The drawable's Babl format.
+ *
+ * Since: 2.10
+ **/
+gchar *
+_gimp_drawable_get_format (gint32 drawable_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *format = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-get-format",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ format = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return format;
+}
+
+/**
+ * _gimp_drawable_get_thumbnail_format:
+ * @drawable_ID: The drawable.
+ *
+ * Returns the drawable's thumbnail Babl format
+ *
+ * This procedure returns the drawable's thumbnail Babl format.
+ * Thumbnails are always 8-bit images, see gimp_drawable_thumbnail()
+ * and gimp_drawable_sub_thmbnail().
+ *
+ * Returns: The drawable's thumbnail Babl format.
+ *
+ * Since: 2.10.14
+ **/
+gchar *
+_gimp_drawable_get_thumbnail_format (gint32 drawable_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *format = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-get-thumbnail-format",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ format = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return format;
+}
+
+/**
+ * gimp_drawable_type:
+ * @drawable_ID: The drawable.
+ *
+ * Returns the drawable's type.
+ *
+ * This procedure returns the drawable's type.
+ *
+ * Returns: The drawable's type.
+ **/
+GimpImageType
+gimp_drawable_type (gint32 drawable_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpImageType type = 0;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-type",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ type = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return type;
+}
+
+/**
+ * gimp_drawable_type_with_alpha:
+ * @drawable_ID: The drawable.
+ *
+ * Returns the drawable's type with alpha.
+ *
+ * This procedure returns the drawable's type as if had an alpha
+ * channel. If the type is currently Gray, for instance, the returned
+ * type would be GrayA. If the drawable already has an alpha channel,
+ * the drawable's type is simply returned.
+ *
+ * Returns: The drawable's type with alpha.
+ **/
+GimpImageType
+gimp_drawable_type_with_alpha (gint32 drawable_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpImageType type_with_alpha = 0;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-type-with-alpha",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ type_with_alpha = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return type_with_alpha;
+}
+
+/**
+ * gimp_drawable_has_alpha:
+ * @drawable_ID: The drawable.
+ *
+ * Returns TRUE if the drawable has an alpha channel.
+ *
+ * This procedure returns whether the specified drawable has an alpha
+ * channel. This can only be true for layers, and the associated type
+ * will be one of: { RGBA , GRAYA, INDEXEDA }.
+ *
+ * Returns: Does the drawable have an alpha channel?
+ **/
+gboolean
+gimp_drawable_has_alpha (gint32 drawable_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean has_alpha = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-has-alpha",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ has_alpha = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return has_alpha;
+}
+
+/**
+ * gimp_drawable_is_rgb:
+ * @drawable_ID: The drawable.
+ *
+ * Returns whether the drawable is an RGB type.
+ *
+ * This procedure returns TRUE if the specified drawable is of type {
+ * RGB, RGBA }.
+ *
+ * Returns: TRUE if the drawable is an RGB type.
+ **/
+gboolean
+gimp_drawable_is_rgb (gint32 drawable_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean is_rgb = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-is-rgb",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ is_rgb = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return is_rgb;
+}
+
+/**
+ * gimp_drawable_is_gray:
+ * @drawable_ID: The drawable.
+ *
+ * Returns whether the drawable is a grayscale type.
+ *
+ * This procedure returns TRUE if the specified drawable is of type {
+ * Gray, GrayA }.
+ *
+ * Returns: TRUE if the drawable is a grayscale type.
+ **/
+gboolean
+gimp_drawable_is_gray (gint32 drawable_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean is_gray = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-is-gray",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ is_gray = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return is_gray;
+}
+
+/**
+ * gimp_drawable_is_indexed:
+ * @drawable_ID: The drawable.
+ *
+ * Returns whether the drawable is an indexed type.
+ *
+ * This procedure returns TRUE if the specified drawable is of type {
+ * Indexed, IndexedA }.
+ *
+ * Returns: TRUE if the drawable is an indexed type.
+ **/
+gboolean
+gimp_drawable_is_indexed (gint32 drawable_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean is_indexed = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-is-indexed",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ is_indexed = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return is_indexed;
+}
+
+/**
+ * gimp_drawable_bpp:
+ * @drawable_ID: The drawable.
+ *
+ * Returns the bytes per pixel.
+ *
+ * This procedure returns the number of bytes per pixel, which
+ * corresponds to the number of components unless
+ * gimp_plugin_enable_precision() was called.
+ *
+ * Returns: Bytes per pixel.
+ **/
+gint
+gimp_drawable_bpp (gint32 drawable_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint bpp = 0;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-bpp",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ bpp = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return bpp;
+}
+
+/**
+ * gimp_drawable_width:
+ * @drawable_ID: The drawable.
+ *
+ * Returns the width of the drawable.
+ *
+ * This procedure returns the specified drawable's width in pixels.
+ *
+ * Returns: Width of drawable.
+ **/
+gint
+gimp_drawable_width (gint32 drawable_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint width = 0;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-width",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ width = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return width;
+}
+
+/**
+ * gimp_drawable_height:
+ * @drawable_ID: The drawable.
+ *
+ * Returns the height of the drawable.
+ *
+ * This procedure returns the specified drawable's height in pixels.
+ *
+ * Returns: Height of drawable.
+ **/
+gint
+gimp_drawable_height (gint32 drawable_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint height = 0;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-height",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ height = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return height;
+}
+
+/**
+ * gimp_drawable_offsets:
+ * @drawable_ID: The drawable.
+ * @offset_x: x offset of drawable.
+ * @offset_y: y offset of drawable.
+ *
+ * Returns the offsets for the drawable.
+ *
+ * This procedure returns the specified drawable's offsets. This only
+ * makes sense if the drawable is a layer since channels are anchored.
+ * The offsets of a channel will be returned as 0.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_drawable_offsets (gint32 drawable_ID,
+ gint *offset_x,
+ gint *offset_y)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-offsets",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_END);
+
+ *offset_x = 0;
+ *offset_y = 0;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *offset_x = return_vals[1].data.d_int32;
+ *offset_y = return_vals[2].data.d_int32;
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_drawable_set_image:
+ * @drawable_ID: The drawable.
+ * @image_ID: The image.
+ *
+ * Deprecated: There is no replacement for this procedure.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_drawable_set_image (gint32 drawable_ID,
+ gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-set-image",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_drawable_mask_bounds:
+ * @drawable_ID: The drawable.
+ * @x1: x coordinate of the upper left corner of selection bounds.
+ * @y1: y coordinate of the upper left corner of selection bounds.
+ * @x2: x coordinate of the lower right corner of selection bounds.
+ * @y2: y coordinate of the lower right corner of selection bounds.
+ *
+ * Find the bounding box of the current selection in relation to the
+ * specified drawable.
+ *
+ * This procedure returns whether there is a selection. If there is
+ * one, the upper left and lower right-hand corners of its bounding box
+ * are returned. These coordinates are specified relative to the
+ * drawable's origin, and bounded by the drawable's extents. Please
+ * note that the pixel specified by the lower right-hand coordinate of
+ * the bounding box is not part of the selection. The selection ends at
+ * the upper left corner of this pixel. This means the width of the
+ * selection can be calculated as (x2 - x1), its height as (y2 - y1).
+ * Note that the returned boolean does NOT correspond with the returned
+ * region being empty or not, it always returns whether the selection
+ * is non_empty. See gimp_drawable_mask_intersect() for a boolean
+ * return value which is more useful in most cases.
+ *
+ * Returns: TRUE if there is a selection.
+ **/
+gboolean
+gimp_drawable_mask_bounds (gint32 drawable_ID,
+ gint *x1,
+ gint *y1,
+ gint *x2,
+ gint *y2)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean non_empty = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-mask-bounds",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ non_empty = return_vals[1].data.d_int32;
+ *x1 = return_vals[2].data.d_int32;
+ *y1 = return_vals[3].data.d_int32;
+ *x2 = return_vals[4].data.d_int32;
+ *y2 = return_vals[5].data.d_int32;
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return non_empty;
+}
+
+/**
+ * gimp_drawable_mask_intersect:
+ * @drawable_ID: The drawable.
+ * @x: x coordinate of the upper left corner of the intersection.
+ * @y: y coordinate of the upper left corner of the intersection.
+ * @width: width of the intersection.
+ * @height: height of the intersection.
+ *
+ * Find the bounding box of the current selection in relation to the
+ * specified drawable.
+ *
+ * This procedure returns whether there is an intersection between the
+ * drawable and the selection. Unlike gimp_drawable_mask_bounds(), the
+ * intersection's bounds are returned as x, y, width, height.
+ * If there is no selection this function returns TRUE and the returned
+ * bounds are the extents of the whole drawable.
+ *
+ * Returns: TRUE if the returned area is not empty.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_drawable_mask_intersect (gint32 drawable_ID,
+ gint *x,
+ gint *y,
+ gint *width,
+ gint *height)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean non_empty = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-mask-intersect",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ non_empty = return_vals[1].data.d_int32;
+ *x = return_vals[2].data.d_int32;
+ *y = return_vals[3].data.d_int32;
+ *width = return_vals[4].data.d_int32;
+ *height = return_vals[5].data.d_int32;
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return non_empty;
+}
+
+/**
+ * gimp_drawable_merge_shadow:
+ * @drawable_ID: The drawable.
+ * @undo: Push merge to undo stack?
+ *
+ * Merge the shadow buffer with the specified drawable.
+ *
+ * This procedure combines the contents of the drawable's shadow buffer
+ * (for temporary processing) with the specified drawable. The 'undo'
+ * parameter specifies whether to add an undo step for the operation.
+ * Requesting no undo is useful for such applications as 'auto-apply'.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_drawable_merge_shadow (gint32 drawable_ID,
+ gboolean undo)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-merge-shadow",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, undo,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_drawable_free_shadow:
+ * @drawable_ID: The drawable.
+ *
+ * Free the specified drawable's shadow data (if it exists).
+ *
+ * This procedure is intended as a memory saving device. If any shadow
+ * memory has been allocated, it will be freed automatically when the
+ * drawable is removed from the image, or when the plug-in procedure
+ * which allocated it returns.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.6
+ **/
+gboolean
+gimp_drawable_free_shadow (gint32 drawable_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-free-shadow",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_drawable_update:
+ * @drawable_ID: The drawable.
+ * @x: x coordinate of upper left corner of update region.
+ * @y: y coordinate of upper left corner of update region.
+ * @width: Width of update region.
+ * @height: Height of update region.
+ *
+ * Update the specified region of the drawable.
+ *
+ * This procedure updates the specified region of the drawable. The (x,
+ * y) coordinate pair is relative to the drawable's origin, not to the
+ * image origin. Therefore, the entire drawable can be updated using
+ * (0, 0, width, height).
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_drawable_update (gint32 drawable_ID,
+ gint x,
+ gint y,
+ gint width,
+ gint height)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-update",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, x,
+ GIMP_PDB_INT32, y,
+ GIMP_PDB_INT32, width,
+ GIMP_PDB_INT32, height,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_drawable_get_pixel:
+ * @drawable_ID: The drawable.
+ * @x_coord: The x coordinate.
+ * @y_coord: The y coordinate.
+ * @num_channels: The number of channels for the pixel.
+ *
+ * Gets the value of the pixel at the specified coordinates.
+ *
+ * This procedure gets the pixel value at the specified coordinates.
+ * The 'num_channels' argument must always be equal to the
+ * bytes-per-pixel value for the specified drawable.
+ *
+ * Returns: The pixel value.
+ **/
+guint8 *
+gimp_drawable_get_pixel (gint32 drawable_ID,
+ gint x_coord,
+ gint y_coord,
+ gint *num_channels)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ guint8 *pixel = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-get-pixel",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, x_coord,
+ GIMP_PDB_INT32, y_coord,
+ GIMP_PDB_END);
+
+ *num_channels = 0;
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ *num_channels = return_vals[1].data.d_int32;
+ pixel = g_new (guint8, *num_channels);
+ memcpy (pixel,
+ return_vals[2].data.d_int8array,
+ *num_channels * sizeof (guint8));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return pixel;
+}
+
+/**
+ * gimp_drawable_set_pixel:
+ * @drawable_ID: The drawable.
+ * @x_coord: The x coordinate.
+ * @y_coord: The y coordinate.
+ * @num_channels: The number of channels for the pixel.
+ * @pixel: The pixel value.
+ *
+ * Sets the value of the pixel at the specified coordinates.
+ *
+ * This procedure sets the pixel value at the specified coordinates.
+ * The 'num_channels' argument must always be equal to the
+ * bytes-per-pixel value for the specified drawable. Note that this
+ * function is not undoable, you should use it only on drawables you
+ * just created yourself.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_drawable_set_pixel (gint32 drawable_ID,
+ gint x_coord,
+ gint y_coord,
+ gint num_channels,
+ const guint8 *pixel)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-set-pixel",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, x_coord,
+ GIMP_PDB_INT32, y_coord,
+ GIMP_PDB_INT32, num_channels,
+ GIMP_PDB_INT8ARRAY, pixel,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_drawable_fill:
+ * @drawable_ID: The drawable.
+ * @fill_type: The type of fill.
+ *
+ * Fill the drawable with the specified fill mode.
+ *
+ * This procedure fills the drawable. If the fill mode is foreground
+ * the current foreground color is used. If the fill mode is
+ * background, the current background color is used. If the fill type
+ * is white, then white is used. Transparent fill only affects layers
+ * with an alpha channel, in which case the alpha channel is set to
+ * transparent. If the drawable has no alpha channel, it is filled to
+ * white. No fill leaves the drawable's contents undefined.
+ * This procedure is unlike gimp_edit_fill() or the bucket fill tool
+ * because it fills regardless of a selection. Its main purpose is to
+ * fill a newly created drawable before adding it to the image. This
+ * operation cannot be undone.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_drawable_fill (gint32 drawable_ID,
+ GimpFillType fill_type)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-fill",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, fill_type,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_drawable_offset:
+ * @drawable_ID: The drawable to offset.
+ * @wrap_around: wrap image around or fill vacated regions.
+ * @fill_type: fill vacated regions of drawable with background or transparent.
+ * @offset_x: offset by this amount in X direction.
+ * @offset_y: offset by this amount in Y direction.
+ *
+ * Offset the drawable by the specified amounts in the X and Y
+ * directions
+ *
+ * This procedure offsets the specified drawable by the amounts
+ * specified by 'offset_x' and 'offset_y'. If 'wrap_around' is set to
+ * TRUE, then portions of the drawable which are offset out of bounds
+ * are wrapped around. Alternatively, the undefined regions of the
+ * drawable can be filled with transparency or the background color, as
+ * specified by the 'fill-type' parameter.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_drawable_offset (gint32 drawable_ID,
+ gboolean wrap_around,
+ GimpOffsetType fill_type,
+ gint offset_x,
+ gint offset_y)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-offset",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, wrap_around,
+ GIMP_PDB_INT32, fill_type,
+ GIMP_PDB_INT32, offset_x,
+ GIMP_PDB_INT32, offset_y,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * _gimp_drawable_thumbnail:
+ * @drawable_ID: The drawable.
+ * @width: The requested thumbnail width.
+ * @height: The requested thumbnail height.
+ * @actual_width: The previews width.
+ * @actual_height: The previews height.
+ * @bpp: The previews bpp.
+ * @thumbnail_data_count: The number of bytes in thumbnail data.
+ * @thumbnail_data: The thumbnail data.
+ *
+ * Get a thumbnail of a drawable.
+ *
+ * This function gets data from which a thumbnail of a drawable preview
+ * can be created. Maximum x or y dimension is 1024 pixels. The pixels
+ * are returned in RGB[A] or GRAY[A] format. The bpp return value gives
+ * the number of bytes in the image.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+_gimp_drawable_thumbnail (gint32 drawable_ID,
+ gint width,
+ gint height,
+ gint *actual_width,
+ gint *actual_height,
+ gint *bpp,
+ gint *thumbnail_data_count,
+ guint8 **thumbnail_data)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-thumbnail",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, width,
+ GIMP_PDB_INT32, height,
+ GIMP_PDB_END);
+
+ *actual_width = 0;
+ *actual_height = 0;
+ *bpp = 0;
+ *thumbnail_data_count = 0;
+ *thumbnail_data = NULL;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *actual_width = return_vals[1].data.d_int32;
+ *actual_height = return_vals[2].data.d_int32;
+ *bpp = return_vals[3].data.d_int32;
+ *thumbnail_data_count = return_vals[4].data.d_int32;
+ *thumbnail_data = g_new (guint8, *thumbnail_data_count);
+ memcpy (*thumbnail_data,
+ return_vals[5].data.d_int8array,
+ *thumbnail_data_count * sizeof (guint8));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * _gimp_drawable_sub_thumbnail:
+ * @drawable_ID: The drawable.
+ * @src_x: The x coordinate of the area.
+ * @src_y: The y coordinate of the area.
+ * @src_width: The width of the area.
+ * @src_height: The height of the area.
+ * @dest_width: The thumbnail width.
+ * @dest_height: The thumbnail height.
+ * @width: The previews width.
+ * @height: The previews height.
+ * @bpp: The previews bpp.
+ * @thumbnail_data_count: The number of bytes in thumbnail data.
+ * @thumbnail_data: The thumbnail data.
+ *
+ * Get a thumbnail of a sub-area of a drawable drawable.
+ *
+ * This function gets data from which a thumbnail of a drawable preview
+ * can be created. Maximum x or y dimension is 1024 pixels. The pixels
+ * are returned in RGB[A] or GRAY[A] format. The bpp return value gives
+ * the number of bytes in the image.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+_gimp_drawable_sub_thumbnail (gint32 drawable_ID,
+ gint src_x,
+ gint src_y,
+ gint src_width,
+ gint src_height,
+ gint dest_width,
+ gint dest_height,
+ gint *width,
+ gint *height,
+ gint *bpp,
+ gint *thumbnail_data_count,
+ guint8 **thumbnail_data)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-sub-thumbnail",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, src_x,
+ GIMP_PDB_INT32, src_y,
+ GIMP_PDB_INT32, src_width,
+ GIMP_PDB_INT32, src_height,
+ GIMP_PDB_INT32, dest_width,
+ GIMP_PDB_INT32, dest_height,
+ GIMP_PDB_END);
+
+ *width = 0;
+ *height = 0;
+ *bpp = 0;
+ *thumbnail_data_count = 0;
+ *thumbnail_data = NULL;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *width = return_vals[1].data.d_int32;
+ *height = return_vals[2].data.d_int32;
+ *bpp = return_vals[3].data.d_int32;
+ *thumbnail_data_count = return_vals[4].data.d_int32;
+ *thumbnail_data = g_new (guint8, *thumbnail_data_count);
+ memcpy (*thumbnail_data,
+ return_vals[5].data.d_int8array,
+ *thumbnail_data_count * sizeof (guint8));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_drawable_foreground_extract:
+ * @drawable_ID: The drawable.
+ * @mode: The algorithm to use.
+ * @mask_ID: Tri-Map.
+ *
+ * Extract the foreground of a drawable using a given trimap.
+ *
+ * Image Segmentation by Uniform Color Clustering, see
+ * https://www.inf.fu-berlin.de/inst/pubs/tr-b-05-07.pdf
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_drawable_foreground_extract (gint32 drawable_ID,
+ GimpForegroundExtractMode mode,
+ gint32 mask_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-foreground-extract",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, mode,
+ GIMP_PDB_DRAWABLE, mask_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimpdrawable_pdb.h b/libgimp/gimpdrawable_pdb.h
new file mode 100644
index 0000000..ce289b2
--- /dev/null
+++ b/libgimp/gimpdrawable_pdb.h
@@ -0,0 +1,113 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpdrawable_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_DRAWABLE_PDB_H__
+#define __GIMP_DRAWABLE_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+G_GNUC_INTERNAL gchar* _gimp_drawable_get_format (gint32 drawable_ID);
+G_GNUC_INTERNAL gchar* _gimp_drawable_get_thumbnail_format (gint32 drawable_ID);
+GimpImageType gimp_drawable_type (gint32 drawable_ID);
+GimpImageType gimp_drawable_type_with_alpha (gint32 drawable_ID);
+gboolean gimp_drawable_has_alpha (gint32 drawable_ID);
+gboolean gimp_drawable_is_rgb (gint32 drawable_ID);
+gboolean gimp_drawable_is_gray (gint32 drawable_ID);
+gboolean gimp_drawable_is_indexed (gint32 drawable_ID);
+gint gimp_drawable_bpp (gint32 drawable_ID);
+gint gimp_drawable_width (gint32 drawable_ID);
+gint gimp_drawable_height (gint32 drawable_ID);
+gboolean gimp_drawable_offsets (gint32 drawable_ID,
+ gint *offset_x,
+ gint *offset_y);
+GIMP_DEPRECATED
+gboolean gimp_drawable_set_image (gint32 drawable_ID,
+ gint32 image_ID);
+gboolean gimp_drawable_mask_bounds (gint32 drawable_ID,
+ gint *x1,
+ gint *y1,
+ gint *x2,
+ gint *y2);
+gboolean gimp_drawable_mask_intersect (gint32 drawable_ID,
+ gint *x,
+ gint *y,
+ gint *width,
+ gint *height);
+gboolean gimp_drawable_merge_shadow (gint32 drawable_ID,
+ gboolean undo);
+gboolean gimp_drawable_free_shadow (gint32 drawable_ID);
+gboolean gimp_drawable_update (gint32 drawable_ID,
+ gint x,
+ gint y,
+ gint width,
+ gint height);
+guint8* gimp_drawable_get_pixel (gint32 drawable_ID,
+ gint x_coord,
+ gint y_coord,
+ gint *num_channels);
+gboolean gimp_drawable_set_pixel (gint32 drawable_ID,
+ gint x_coord,
+ gint y_coord,
+ gint num_channels,
+ const guint8 *pixel);
+gboolean gimp_drawable_fill (gint32 drawable_ID,
+ GimpFillType fill_type);
+gboolean gimp_drawable_offset (gint32 drawable_ID,
+ gboolean wrap_around,
+ GimpOffsetType fill_type,
+ gint offset_x,
+ gint offset_y);
+G_GNUC_INTERNAL gboolean _gimp_drawable_thumbnail (gint32 drawable_ID,
+ gint width,
+ gint height,
+ gint *actual_width,
+ gint *actual_height,
+ gint *bpp,
+ gint *thumbnail_data_count,
+ guint8 **thumbnail_data);
+G_GNUC_INTERNAL gboolean _gimp_drawable_sub_thumbnail (gint32 drawable_ID,
+ gint src_x,
+ gint src_y,
+ gint src_width,
+ gint src_height,
+ gint dest_width,
+ gint dest_height,
+ gint *width,
+ gint *height,
+ gint *bpp,
+ gint *thumbnail_data_count,
+ guint8 **thumbnail_data);
+gboolean gimp_drawable_foreground_extract (gint32 drawable_ID,
+ GimpForegroundExtractMode mode,
+ gint32 mask_ID);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_DRAWABLE_PDB_H__ */
diff --git a/libgimp/gimpdrawablecolor_pdb.c b/libgimp/gimpdrawablecolor_pdb.c
new file mode 100644
index 0000000..2684270
--- /dev/null
+++ b/libgimp/gimpdrawablecolor_pdb.c
@@ -0,0 +1,780 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpdrawablecolor_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpdrawablecolor
+ * @title: gimpdrawablecolor
+ * @short_description: Functions for manipulating a drawable's color.
+ *
+ * Functions for manipulating a drawable's color, including curves and
+ * histograms.
+ **/
+
+
+/**
+ * gimp_drawable_brightness_contrast:
+ * @drawable_ID: The drawable.
+ * @brightness: Brightness adjustment.
+ * @contrast: Contrast adjustment.
+ *
+ * Modify brightness/contrast in the specified drawable.
+ *
+ * This procedures allows the brightness and contrast of the specified
+ * drawable to be modified. Both 'brightness' and 'contrast' parameters
+ * are defined between -1.0 and 1.0.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_drawable_brightness_contrast (gint32 drawable_ID,
+ gdouble brightness,
+ gdouble contrast)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-brightness-contrast",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_FLOAT, brightness,
+ GIMP_PDB_FLOAT, contrast,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_drawable_color_balance:
+ * @drawable_ID: The drawable.
+ * @transfer_mode: Transfer mode.
+ * @preserve_lum: Preserve luminosity values at each pixel.
+ * @cyan_red: Cyan-Red color balance.
+ * @magenta_green: Magenta-Green color balance.
+ * @yellow_blue: Yellow-Blue color balance.
+ *
+ * Modify the color balance of the specified drawable.
+ *
+ * Modify the color balance of the specified drawable. There are three
+ * axis which can be modified: cyan-red, magenta-green, and
+ * yellow-blue. Negative values increase the amount of the former,
+ * positive values increase the amount of the latter. Color balance can
+ * be controlled with the 'transfer_mode' setting, which allows
+ * shadows, mid-tones, and highlights in an image to be affected
+ * differently. The 'preserve-lum' parameter, if TRUE, ensures that the
+ * luminosity of each pixel remains fixed.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_drawable_color_balance (gint32 drawable_ID,
+ GimpTransferMode transfer_mode,
+ gboolean preserve_lum,
+ gdouble cyan_red,
+ gdouble magenta_green,
+ gdouble yellow_blue)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-color-balance",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, transfer_mode,
+ GIMP_PDB_INT32, preserve_lum,
+ GIMP_PDB_FLOAT, cyan_red,
+ GIMP_PDB_FLOAT, magenta_green,
+ GIMP_PDB_FLOAT, yellow_blue,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_drawable_colorize_hsl:
+ * @drawable_ID: The drawable.
+ * @hue: Hue in degrees.
+ * @saturation: Saturation in percent.
+ * @lightness: Lightness in percent.
+ *
+ * Render the drawable as a grayscale image seen through a colored
+ * glass.
+ *
+ * Desaturates the drawable, then tints it with the specified color.
+ * This tool is only valid on RGB color images. It will not operate on
+ * grayscale drawables.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_drawable_colorize_hsl (gint32 drawable_ID,
+ gdouble hue,
+ gdouble saturation,
+ gdouble lightness)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-colorize-hsl",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_FLOAT, hue,
+ GIMP_PDB_FLOAT, saturation,
+ GIMP_PDB_FLOAT, lightness,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_drawable_curves_explicit:
+ * @drawable_ID: The drawable.
+ * @channel: The channel to modify.
+ * @num_values: The number of values in the new curve.
+ * @values: The explicit curve.
+ *
+ * Modifies the intensity curve(s) for specified drawable.
+ *
+ * Modifies the intensity mapping for one channel in the specified
+ * drawable. The channel can be either an intensity component, or the
+ * value. The 'values' parameter is an array of doubles which
+ * explicitly defines how each pixel value in the drawable will be
+ * modified. Use the gimp_curves_spline() function to modify intensity
+ * levels with Catmull Rom splines.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_drawable_curves_explicit (gint32 drawable_ID,
+ GimpHistogramChannel channel,
+ gint num_values,
+ const gdouble *values)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-curves-explicit",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, channel,
+ GIMP_PDB_INT32, num_values,
+ GIMP_PDB_FLOATARRAY, values,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_drawable_curves_spline:
+ * @drawable_ID: The drawable.
+ * @channel: The channel to modify.
+ * @num_points: The number of values in the control point array.
+ * @points: The spline control points: { cp1.x, cp1.y, cp2.x, cp2.y, ... }.
+ *
+ * Modifies the intensity curve(s) for specified drawable.
+ *
+ * Modifies the intensity mapping for one channel in the specified
+ * drawable. The channel can be either an intensity component, or the
+ * value. The 'points' parameter is an array of doubles which define a
+ * set of control points which describe a Catmull Rom spline which
+ * yields the final intensity curve. Use the gimp_curves_explicit()
+ * function to explicitly modify intensity levels.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_drawable_curves_spline (gint32 drawable_ID,
+ GimpHistogramChannel channel,
+ gint num_points,
+ const gdouble *points)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-curves-spline",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, channel,
+ GIMP_PDB_INT32, num_points,
+ GIMP_PDB_FLOATARRAY, points,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_drawable_extract_component:
+ * @drawable_ID: The drawable.
+ * @component: Component (RGB Red (0), RGB Green (1), RGB Blue (2), Hue (3), HSV Saturation (4), HSV Value (5), HSL Saturation (6), HSL Lightness (7), CMYK Cyan (8), CMYK Magenta (9), CMYK Yellow (10), CMYK Key (11), Y'CbCr Y' (12), Y'CbCr Cb (13), Y'CbCr Cr (14), LAB L (15), LAB A (16), LAB B (17), LCH C(ab) (18), LCH H(ab) (19), Alpha (20)).
+ * @invert: Invert the extracted component.
+ * @linear: Use linear output instead of gamma corrected.
+ *
+ * Extract a color model component.
+ *
+ * Extract a color model component.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10.34
+ **/
+gboolean
+gimp_drawable_extract_component (gint32 drawable_ID,
+ guint8 component,
+ gboolean invert,
+ gboolean linear)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-extract-component",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT8, component,
+ GIMP_PDB_INT32, invert,
+ GIMP_PDB_INT32, linear,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_drawable_desaturate:
+ * @drawable_ID: The drawable.
+ * @desaturate_mode: The formula to use to desaturate.
+ *
+ * Desaturate the contents of the specified drawable, with the
+ * specified formula.
+ *
+ * This procedure desaturates the contents of the specified drawable,
+ * with the specified formula. This procedure only works on drawables
+ * of type RGB color.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_drawable_desaturate (gint32 drawable_ID,
+ GimpDesaturateMode desaturate_mode)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-desaturate",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, desaturate_mode,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_drawable_equalize:
+ * @drawable_ID: The drawable.
+ * @mask_only: Equalization option.
+ *
+ * Equalize the contents of the specified drawable.
+ *
+ * This procedure equalizes the contents of the specified drawable.
+ * Each intensity channel is equalized independently. The equalized
+ * intensity is given as inten' = (255 - inten). The 'mask_only' option
+ * specifies whether to adjust only the area of the image within the
+ * selection bounds, or the entire image based on the histogram of the
+ * selected area. If there is no selection, the entire image is
+ * adjusted based on the histogram for the entire image.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_drawable_equalize (gint32 drawable_ID,
+ gboolean mask_only)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-equalize",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, mask_only,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_drawable_histogram:
+ * @drawable_ID: The drawable.
+ * @channel: The channel to query.
+ * @start_range: Start of the intensity measurement range.
+ * @end_range: End of the intensity measurement range.
+ * @mean: Mean intensity value.
+ * @std_dev: Standard deviation of intensity values.
+ * @median: Median intensity value.
+ * @pixels: Alpha-weighted pixel count for entire image.
+ * @count: Alpha-weighted pixel count for range.
+ * @percentile: Percentile that range falls under.
+ *
+ * Returns information on the intensity histogram for the specified
+ * drawable.
+ *
+ * This tool makes it possible to gather information about the
+ * intensity histogram of a drawable. A channel to examine is first
+ * specified. This can be either value, red, green, or blue, depending
+ * on whether the drawable is of type color or grayscale. Second, a
+ * range of intensities are specified. The gimp_drawable_histogram()
+ * function returns statistics based on the pixels in the drawable that
+ * fall under this range of values. Mean, standard deviation, median,
+ * number of pixels, and percentile are all returned. Additionally, the
+ * total count of pixels in the image is returned. Counts of pixels are
+ * weighted by any associated alpha values and by the current selection
+ * mask. That is, pixels that lie outside an active selection mask will
+ * not be counted. Similarly, pixels with transparent alpha values will
+ * not be counted. The returned mean, std_dev and median are in the
+ * range (0..255) for 8-bit images or if the plug-in is not
+ * precision-aware, and in the range (0.0..1.0) otherwise.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_drawable_histogram (gint32 drawable_ID,
+ GimpHistogramChannel channel,
+ gdouble start_range,
+ gdouble end_range,
+ gdouble *mean,
+ gdouble *std_dev,
+ gdouble *median,
+ gdouble *pixels,
+ gdouble *count,
+ gdouble *percentile)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-histogram",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, channel,
+ GIMP_PDB_FLOAT, start_range,
+ GIMP_PDB_FLOAT, end_range,
+ GIMP_PDB_END);
+
+ *mean = 0.0;
+ *std_dev = 0.0;
+ *median = 0.0;
+ *pixels = 0.0;
+ *count = 0.0;
+ *percentile = 0.0;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *mean = return_vals[1].data.d_float;
+ *std_dev = return_vals[2].data.d_float;
+ *median = return_vals[3].data.d_float;
+ *pixels = return_vals[4].data.d_float;
+ *count = return_vals[5].data.d_float;
+ *percentile = return_vals[6].data.d_float;
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_drawable_hue_saturation:
+ * @drawable_ID: The drawable.
+ * @hue_range: Range of affected hues.
+ * @hue_offset: Hue offset in degrees.
+ * @lightness: Lightness modification.
+ * @saturation: Saturation modification.
+ * @overlap: Overlap other hue channels.
+ *
+ * Modify hue, lightness, and saturation in the specified drawable.
+ *
+ * This procedure allows the hue, lightness, and saturation in the
+ * specified drawable to be modified. The 'hue-range' parameter
+ * provides the capability to limit range of affected hues. The
+ * 'overlap' parameter provides blending into neighboring hue channels
+ * when rendering.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_drawable_hue_saturation (gint32 drawable_ID,
+ GimpHueRange hue_range,
+ gdouble hue_offset,
+ gdouble lightness,
+ gdouble saturation,
+ gdouble overlap)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-hue-saturation",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, hue_range,
+ GIMP_PDB_FLOAT, hue_offset,
+ GIMP_PDB_FLOAT, lightness,
+ GIMP_PDB_FLOAT, saturation,
+ GIMP_PDB_FLOAT, overlap,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_drawable_invert:
+ * @drawable_ID: The drawable.
+ * @linear: Whether to invert in linear space.
+ *
+ * Invert the contents of the specified drawable.
+ *
+ * This procedure inverts the contents of the specified drawable. Each
+ * intensity channel is inverted independently. The inverted intensity
+ * is given as inten' = (255 - inten). If 'linear' is TRUE, the
+ * drawable is inverted in linear space.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_drawable_invert (gint32 drawable_ID,
+ gboolean linear)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-invert",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, linear,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_drawable_levels:
+ * @drawable_ID: The drawable.
+ * @channel: The channel to modify.
+ * @low_input: Intensity of lowest input.
+ * @high_input: Intensity of highest input.
+ * @clamp_input: Clamp input values before applying output levels.
+ * @gamma: Gamma adjustment factor.
+ * @low_output: Intensity of lowest output.
+ * @high_output: Intensity of highest output.
+ * @clamp_output: Clamp final output values.
+ *
+ * Modifies intensity levels in the specified drawable.
+ *
+ * This tool allows intensity levels in the specified drawable to be
+ * remapped according to a set of parameters. The low/high input levels
+ * specify an initial mapping from the source intensities. The gamma
+ * value determines how intensities between the low and high input
+ * intensities are interpolated. A gamma value of 1.0 results in a
+ * linear interpolation. Higher gamma values result in more high-level
+ * intensities. Lower gamma values result in more low-level
+ * intensities. The low/high output levels constrain the final
+ * intensity mapping--that is, no final intensity will be lower than
+ * the low output level and no final intensity will be higher than the
+ * high output level. This tool is only valid on RGB color and
+ * grayscale images.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_drawable_levels (gint32 drawable_ID,
+ GimpHistogramChannel channel,
+ gdouble low_input,
+ gdouble high_input,
+ gboolean clamp_input,
+ gdouble gamma,
+ gdouble low_output,
+ gdouble high_output,
+ gboolean clamp_output)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-levels",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, channel,
+ GIMP_PDB_FLOAT, low_input,
+ GIMP_PDB_FLOAT, high_input,
+ GIMP_PDB_INT32, clamp_input,
+ GIMP_PDB_FLOAT, gamma,
+ GIMP_PDB_FLOAT, low_output,
+ GIMP_PDB_FLOAT, high_output,
+ GIMP_PDB_INT32, clamp_output,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_drawable_levels_stretch:
+ * @drawable_ID: The drawable.
+ *
+ * Automatically modifies intensity levels in the specified drawable.
+ *
+ * This procedure allows intensity levels in the specified drawable to
+ * be remapped according to a set of guessed parameters. It is
+ * equivalent to clicking the \"Auto\" button in the Levels tool.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_drawable_levels_stretch (gint32 drawable_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-levels-stretch",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_drawable_shadows_highlights:
+ * @drawable_ID: The drawable.
+ * @shadows: Adjust exposure of shadows.
+ * @highlights: Adjust exposure of highlights.
+ * @whitepoint: Shift white point.
+ * @radius: Spatial extent.
+ * @compress: Compress the effect on shadows/highlights and preserve midtones.
+ * @shadows_ccorrect: Adjust saturation of shadows.
+ * @highlights_ccorrect: Adjust saturation of highlights.
+ *
+ * Perform shadows and highlights correction.
+ *
+ * This filter allows adjusting shadows and highlights in the image
+ * separately. The implementation closely follow its counterpart in the
+ * Darktable photography software.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10.34
+ **/
+gboolean
+gimp_drawable_shadows_highlights (gint32 drawable_ID,
+ gdouble shadows,
+ gdouble highlights,
+ gdouble whitepoint,
+ gdouble radius,
+ gdouble compress,
+ gdouble shadows_ccorrect,
+ gdouble highlights_ccorrect)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-shadows-highlights",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_FLOAT, shadows,
+ GIMP_PDB_FLOAT, highlights,
+ GIMP_PDB_FLOAT, whitepoint,
+ GIMP_PDB_FLOAT, radius,
+ GIMP_PDB_FLOAT, compress,
+ GIMP_PDB_FLOAT, shadows_ccorrect,
+ GIMP_PDB_FLOAT, highlights_ccorrect,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_drawable_posterize:
+ * @drawable_ID: The drawable.
+ * @levels: Levels of posterization.
+ *
+ * Posterize the specified drawable.
+ *
+ * This procedures reduces the number of shades allows in each
+ * intensity channel to the specified 'levels' parameter.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_drawable_posterize (gint32 drawable_ID,
+ gint levels)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-posterize",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, levels,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_drawable_threshold:
+ * @drawable_ID: The drawable.
+ * @channel: The channel to base the threshold on.
+ * @low_threshold: The low threshold value.
+ * @high_threshold: The high threshold value.
+ *
+ * Threshold the specified drawable.
+ *
+ * This procedures generates a threshold map of the specified drawable.
+ * All pixels between the values of 'low_threshold' and
+ * 'high_threshold', on the scale of 'channel' are replaced with white,
+ * and all other pixels with black.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_drawable_threshold (gint32 drawable_ID,
+ GimpHistogramChannel channel,
+ gdouble low_threshold,
+ gdouble high_threshold)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-threshold",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, channel,
+ GIMP_PDB_FLOAT, low_threshold,
+ GIMP_PDB_FLOAT, high_threshold,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimpdrawablecolor_pdb.h b/libgimp/gimpdrawablecolor_pdb.h
new file mode 100644
index 0000000..5d7aa9e
--- /dev/null
+++ b/libgimp/gimpdrawablecolor_pdb.h
@@ -0,0 +1,110 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpdrawablecolor_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_DRAWABLE_COLOR_PDB_H__
+#define __GIMP_DRAWABLE_COLOR_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_drawable_brightness_contrast (gint32 drawable_ID,
+ gdouble brightness,
+ gdouble contrast);
+gboolean gimp_drawable_color_balance (gint32 drawable_ID,
+ GimpTransferMode transfer_mode,
+ gboolean preserve_lum,
+ gdouble cyan_red,
+ gdouble magenta_green,
+ gdouble yellow_blue);
+gboolean gimp_drawable_colorize_hsl (gint32 drawable_ID,
+ gdouble hue,
+ gdouble saturation,
+ gdouble lightness);
+gboolean gimp_drawable_curves_explicit (gint32 drawable_ID,
+ GimpHistogramChannel channel,
+ gint num_values,
+ const gdouble *values);
+gboolean gimp_drawable_curves_spline (gint32 drawable_ID,
+ GimpHistogramChannel channel,
+ gint num_points,
+ const gdouble *points);
+gboolean gimp_drawable_extract_component (gint32 drawable_ID,
+ guint8 component,
+ gboolean invert,
+ gboolean linear);
+gboolean gimp_drawable_desaturate (gint32 drawable_ID,
+ GimpDesaturateMode desaturate_mode);
+gboolean gimp_drawable_equalize (gint32 drawable_ID,
+ gboolean mask_only);
+gboolean gimp_drawable_histogram (gint32 drawable_ID,
+ GimpHistogramChannel channel,
+ gdouble start_range,
+ gdouble end_range,
+ gdouble *mean,
+ gdouble *std_dev,
+ gdouble *median,
+ gdouble *pixels,
+ gdouble *count,
+ gdouble *percentile);
+gboolean gimp_drawable_hue_saturation (gint32 drawable_ID,
+ GimpHueRange hue_range,
+ gdouble hue_offset,
+ gdouble lightness,
+ gdouble saturation,
+ gdouble overlap);
+gboolean gimp_drawable_invert (gint32 drawable_ID,
+ gboolean linear);
+gboolean gimp_drawable_levels (gint32 drawable_ID,
+ GimpHistogramChannel channel,
+ gdouble low_input,
+ gdouble high_input,
+ gboolean clamp_input,
+ gdouble gamma,
+ gdouble low_output,
+ gdouble high_output,
+ gboolean clamp_output);
+gboolean gimp_drawable_levels_stretch (gint32 drawable_ID);
+gboolean gimp_drawable_shadows_highlights (gint32 drawable_ID,
+ gdouble shadows,
+ gdouble highlights,
+ gdouble whitepoint,
+ gdouble radius,
+ gdouble compress,
+ gdouble shadows_ccorrect,
+ gdouble highlights_ccorrect);
+gboolean gimp_drawable_posterize (gint32 drawable_ID,
+ gint levels);
+gboolean gimp_drawable_threshold (gint32 drawable_ID,
+ GimpHistogramChannel channel,
+ gdouble low_threshold,
+ gdouble high_threshold);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_DRAWABLE_COLOR_PDB_H__ */
diff --git a/libgimp/gimpdrawableedit_pdb.c b/libgimp/gimpdrawableedit_pdb.c
new file mode 100644
index 0000000..f8eb93b
--- /dev/null
+++ b/libgimp/gimpdrawableedit_pdb.c
@@ -0,0 +1,320 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpdrawableedit_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpdrawableedit
+ * @title: gimpdrawableedit
+ * @short_description: Drawable edit functions (clear, fill, gradient, stroke etc.)
+ *
+ * Drawable edit functions (clear, fill, gradient, stroke etc.)
+ **/
+
+
+/**
+ * gimp_drawable_edit_clear:
+ * @drawable_ID: The drawable to clear from.
+ *
+ * Clear selected area of drawable.
+ *
+ * This procedure clears the specified drawable. If the drawable has an
+ * alpha channel, the cleared pixels will become transparent. If the
+ * drawable does not have an alpha channel, cleared pixels will be set
+ * to the background color. This procedure only affects regions within
+ * a selection if there is a selection active.
+ *
+ * This procedure is affected by the following context setters:
+ * gimp_context_set_background().
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_drawable_edit_clear (gint32 drawable_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-edit-clear",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_drawable_edit_fill:
+ * @drawable_ID: The drawable to fill to.
+ * @fill_type: The type of fill.
+ *
+ * Fill selected area of drawable.
+ *
+ * This procedure fills the specified drawable according to fill mode.
+ * This procedure only affects regions within a selection if there is a
+ * selection active. If you want to fill the whole drawable, regardless
+ * of the selection, use gimp_drawable_fill().
+ *
+ * This procedure is affected by the following context setters:
+ * gimp_context_set_opacity(), gimp_context_set_paint_mode(),
+ * gimp_context_set_foreground(), gimp_context_set_background(),
+ * gimp_context_set_pattern().
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_drawable_edit_fill (gint32 drawable_ID,
+ GimpFillType fill_type)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-edit-fill",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, fill_type,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_drawable_edit_bucket_fill:
+ * @drawable_ID: The affected drawable.
+ * @fill_type: The type of fill.
+ * @x: The x coordinate of this bucket fill's application.
+ * @y: The y coordinate of this bucket fill's application.
+ *
+ * Fill the area by a seed fill starting at the specified coordinates.
+ *
+ * This procedure does a seed fill at the specified coordinates, using
+ * various parameters from the current context.
+ * In the case of merged sampling, the x and y coordinates are relative
+ * to the image's origin; otherwise, they are relative to the
+ * drawable's origin.
+ *
+ * This procedure is affected by the following context setters:
+ * gimp_context_set_opacity(), gimp_context_set_paint_mode(),
+ * gimp_context_set_foreground(), gimp_context_set_background(),
+ * gimp_context_set_pattern(), gimp_context_set_sample_threshold(),
+ * gimp_context_set_sample_merged(),
+ * gimp_context_set_sample_criterion(),
+ * gimp_context_set_diagonal_neighbors(), gimp_context_set_antialias().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_drawable_edit_bucket_fill (gint32 drawable_ID,
+ GimpFillType fill_type,
+ gdouble x,
+ gdouble y)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-edit-bucket-fill",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, fill_type,
+ GIMP_PDB_FLOAT, x,
+ GIMP_PDB_FLOAT, y,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_drawable_edit_gradient_fill:
+ * @drawable_ID: The affected drawable.
+ * @gradient_type: The type of gradient.
+ * @offset: Offset relates to the starting and ending coordinates specified for the blend. This parameter is mode dependent.
+ * @supersample: Do adaptive supersampling.
+ * @supersample_max_depth: Maximum recursion levels for supersampling.
+ * @supersample_threshold: Supersampling threshold.
+ * @dither: Use dithering to reduce banding.
+ * @x1: The x coordinate of this gradient's starting point.
+ * @y1: The y coordinate of this gradient's starting point.
+ * @x2: The x coordinate of this gradient's ending point.
+ * @y2: The y coordinate of this gradient's ending point.
+ *
+ * Draw a gradient between the starting and ending coordinates with the
+ * specified gradient type.
+ *
+ * This tool requires information on the gradient type. It creates the
+ * specified variety of gradient using the starting and ending
+ * coordinates as defined for each gradient type. For shapeburst
+ * gradient types, the context's distance metric is also relevant and
+ * can be updated with gimp_context_set_distance_metric().
+ *
+ * This procedure is affected by the following context setters:
+ * gimp_context_set_opacity(), gimp_context_set_paint_mode(),
+ * gimp_context_set_foreground(), gimp_context_set_background(),
+ * gimp_context_set_gradient() and all gradient property settings,
+ * gimp_context_set_distance_metric().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_drawable_edit_gradient_fill (gint32 drawable_ID,
+ GimpGradientType gradient_type,
+ gdouble offset,
+ gboolean supersample,
+ gint supersample_max_depth,
+ gdouble supersample_threshold,
+ gboolean dither,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-edit-gradient-fill",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, gradient_type,
+ GIMP_PDB_FLOAT, offset,
+ GIMP_PDB_INT32, supersample,
+ GIMP_PDB_INT32, supersample_max_depth,
+ GIMP_PDB_FLOAT, supersample_threshold,
+ GIMP_PDB_INT32, dither,
+ GIMP_PDB_FLOAT, x1,
+ GIMP_PDB_FLOAT, y1,
+ GIMP_PDB_FLOAT, x2,
+ GIMP_PDB_FLOAT, y2,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_drawable_edit_stroke_selection:
+ * @drawable_ID: The drawable to stroke to.
+ *
+ * Stroke the current selection
+ *
+ * This procedure strokes the current selection, painting along the
+ * selection boundary with the active paint method and brush, or using
+ * a plain line with configurable properties. The paint is applied to
+ * the specified drawable regardless of the active selection.
+ *
+ * This procedure is affected by the following context setters:
+ * gimp_context_set_opacity(), gimp_context_set_paint_mode(),
+ * gimp_context_set_paint_method(), gimp_context_set_stroke_method(),
+ * gimp_context_set_foreground(), gimp_context_set_brush() and all
+ * brush property settings, gimp_context_set_gradient() and all
+ * gradient property settings, gimp_context_set_line_width() and all
+ * line property settings, gimp_context_set_antialias().
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_drawable_edit_stroke_selection (gint32 drawable_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-edit-stroke-selection",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_drawable_edit_stroke_item:
+ * @drawable_ID: The drawable to stroke to.
+ * @item_ID: The item to stroke.
+ *
+ * Stroke the specified item
+ *
+ * This procedure strokes the specified item, painting along its
+ * outline (e.g. along a path, or along a channel's boundary), with the
+ * active paint method and brush, or using a plain line with
+ * configurable properties.
+ *
+ * This procedure is affected by the following context setters:
+ * gimp_context_set_opacity(), gimp_context_set_paint_mode(),
+ * gimp_context_set_paint_method(), gimp_context_set_stroke_method(),
+ * gimp_context_set_foreground(), gimp_context_set_brush() and all
+ * brush property settings, gimp_context_set_gradient() and all
+ * gradient property settings, gimp_context_set_line_width() and all
+ * line property settings, gimp_context_set_antialias().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_drawable_edit_stroke_item (gint32 drawable_ID,
+ gint32 item_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-edit-stroke-item",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimpdrawableedit_pdb.h b/libgimp/gimpdrawableedit_pdb.h
new file mode 100644
index 0000000..5b63662
--- /dev/null
+++ b/libgimp/gimpdrawableedit_pdb.h
@@ -0,0 +1,60 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpdrawableedit_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_DRAWABLE_EDIT_PDB_H__
+#define __GIMP_DRAWABLE_EDIT_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_drawable_edit_clear (gint32 drawable_ID);
+gboolean gimp_drawable_edit_fill (gint32 drawable_ID,
+ GimpFillType fill_type);
+gboolean gimp_drawable_edit_bucket_fill (gint32 drawable_ID,
+ GimpFillType fill_type,
+ gdouble x,
+ gdouble y);
+gboolean gimp_drawable_edit_gradient_fill (gint32 drawable_ID,
+ GimpGradientType gradient_type,
+ gdouble offset,
+ gboolean supersample,
+ gint supersample_max_depth,
+ gdouble supersample_threshold,
+ gboolean dither,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2);
+gboolean gimp_drawable_edit_stroke_selection (gint32 drawable_ID);
+gboolean gimp_drawable_edit_stroke_item (gint32 drawable_ID,
+ gint32 item_ID);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_DRAWABLE_EDIT_PDB_H__ */
diff --git a/libgimp/gimpdrawablepreview.c b/libgimp/gimpdrawablepreview.c
new file mode 100644
index 0000000..dcdc6c2
--- /dev/null
+++ b/libgimp/gimpdrawablepreview.c
@@ -0,0 +1,757 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpdrawablepreview.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+/* we use our own deprecated API here */
+#define GIMP_DISABLE_DEPRECATION_WARNINGS
+
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "gimpuitypes.h"
+
+#include "gimp.h"
+
+#include "gimpdrawablepreview.h"
+
+
+/**
+ * SECTION: gimpdrawablepreview
+ * @title: GimpDrawablePreview
+ * @short_description: A widget providing a preview of a #GimpDrawable.
+ *
+ * A widget providing a preview of a #GimpDrawable.
+ **/
+
+
+#define SELECTION_BORDER 8
+
+enum
+{
+ PROP_0,
+ PROP_DRAWABLE,
+ PROP_DRAWABLE_ID
+};
+
+typedef struct
+{
+ gint32 drawable_ID;
+} GimpDrawablePreviewPrivate;
+
+typedef struct
+{
+ gint x;
+ gint y;
+ gboolean update;
+} PreviewSettings;
+
+
+#define GIMP_DRAWABLE_PREVIEW_GET_PRIVATE(obj) \
+ ((GimpDrawablePreviewPrivate *) gimp_drawable_preview_get_instance_private ((GimpDrawablePreview *) (preview)))
+
+static void gimp_drawable_preview_constructed (GObject *object);
+static void gimp_drawable_preview_dispose (GObject *object);
+static void gimp_drawable_preview_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_drawable_preview_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_drawable_preview_style_set (GtkWidget *widget,
+ GtkStyle *prev_style);
+
+static void gimp_drawable_preview_draw_original (GimpPreview *preview);
+static void gimp_drawable_preview_draw_thumb (GimpPreview *preview,
+ GimpPreviewArea *area,
+ gint width,
+ gint height);
+static void gimp_drawable_preview_draw_buffer (GimpPreview *preview,
+ const guchar *buffer,
+ gint rowstride);
+
+static void gimp_drawable_preview_set_drawable (GimpDrawablePreview *preview,
+ GimpDrawable *drawable);
+static void gimp_drawable_preview_set_drawable_id
+ (GimpDrawablePreview *preview,
+ gint32 drawable_ID);
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpDrawablePreview, gimp_drawable_preview,
+ GIMP_TYPE_SCROLLED_PREVIEW)
+
+#define parent_class gimp_drawable_preview_parent_class
+
+static gint gimp_drawable_preview_counter = 0;
+
+
+static void
+gimp_drawable_preview_class_init (GimpDrawablePreviewClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GimpPreviewClass *preview_class = GIMP_PREVIEW_CLASS (klass);
+
+ object_class->constructed = gimp_drawable_preview_constructed;
+ object_class->dispose = gimp_drawable_preview_dispose;
+ object_class->get_property = gimp_drawable_preview_get_property;
+ object_class->set_property = gimp_drawable_preview_set_property;
+
+ widget_class->style_set = gimp_drawable_preview_style_set;
+
+ preview_class->draw = gimp_drawable_preview_draw_original;
+ preview_class->draw_thumb = gimp_drawable_preview_draw_thumb;
+ preview_class->draw_buffer = gimp_drawable_preview_draw_buffer;
+
+ /**
+ * GimpDrawablePreview:drawable:
+ *
+ * Deprecated: use the drawable-id property instead.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class, PROP_DRAWABLE,
+ g_param_spec_pointer ("drawable",
+ "Drawable",
+ "Deprecated: use the drawable-id property instead",
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * GimpDrawablePreview:drawable-id:
+ *
+ * The drawable the #GimpDrawablePreview is attached to.
+ *
+ * Since: 2.10
+ */
+ g_object_class_install_property (object_class, PROP_DRAWABLE_ID,
+ g_param_spec_int ("drawable-id",
+ "Drawable ID",
+ "The drawable this preview is attached to",
+ -1, G_MAXINT, -1,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+}
+
+static void
+gimp_drawable_preview_init (GimpDrawablePreview *preview)
+{
+ g_object_set (GIMP_PREVIEW (preview)->area,
+ "check-size", gimp_check_size (),
+ "check-type", gimp_check_type (),
+ NULL);
+}
+
+static void
+gimp_drawable_preview_constructed (GObject *object)
+{
+ gchar *data_name;
+ PreviewSettings settings;
+
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ data_name = g_strdup_printf ("%s-drawable-preview-%d",
+ g_get_prgname (),
+ ++gimp_drawable_preview_counter);
+
+ if (gimp_get_data (data_name, &settings))
+ {
+ gimp_preview_set_update (GIMP_PREVIEW (object), settings.update);
+ gimp_scrolled_preview_set_position (GIMP_SCROLLED_PREVIEW (object),
+ settings.x, settings.y);
+ }
+
+ g_object_set_data_full (object, "gimp-drawable-preview-data-name",
+ data_name, (GDestroyNotify) g_free);
+}
+
+static void
+gimp_drawable_preview_dispose (GObject *object)
+{
+ const gchar *data_name = g_object_get_data (G_OBJECT (object),
+ "gimp-drawable-preview-data-name");
+
+ if (data_name)
+ {
+ GimpPreview *preview = GIMP_PREVIEW (object);
+ PreviewSettings settings;
+
+ settings.x = preview->xoff + preview->xmin;
+ settings.y = preview->yoff + preview->ymin;
+ settings.update = gimp_preview_get_update (preview);
+
+ gimp_set_data (data_name, &settings, sizeof (PreviewSettings));
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gimp_drawable_preview_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpDrawablePreview *preview = GIMP_DRAWABLE_PREVIEW (object);
+
+ switch (property_id)
+ {
+ case PROP_DRAWABLE:
+ g_value_set_pointer (value, preview->drawable);
+ break;
+
+ case PROP_DRAWABLE_ID:
+ g_value_set_int (value,
+ gimp_drawable_preview_get_drawable_id (preview));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_drawable_preview_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpDrawablePreview *preview = GIMP_DRAWABLE_PREVIEW (object);
+ GimpDrawablePreviewPrivate *priv = GIMP_DRAWABLE_PREVIEW_GET_PRIVATE (preview);
+
+ switch (property_id)
+ {
+ case PROP_DRAWABLE:
+ g_return_if_fail (priv->drawable_ID < 1);
+ if (g_value_get_pointer (value))
+ gimp_drawable_preview_set_drawable (preview,
+ g_value_get_pointer (value));
+ break;
+
+ case PROP_DRAWABLE_ID:
+ gimp_drawable_preview_set_drawable_id (preview,
+ g_value_get_int (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_drawable_preview_style_set (GtkWidget *widget,
+ GtkStyle *prev_style)
+{
+ GimpPreview *preview = GIMP_PREVIEW (widget);
+ gint width = preview->xmax - preview->xmin;
+ gint height = preview->ymax - preview->ymin;
+ gint size;
+
+ if (GTK_WIDGET_CLASS (parent_class)->style_set)
+ GTK_WIDGET_CLASS (parent_class)->style_set (widget, prev_style);
+
+ gtk_widget_style_get (widget,
+ "size", &size,
+ NULL);
+
+ gtk_widget_set_size_request (GIMP_PREVIEW (preview)->area,
+ MIN (width, size), MIN (height, size));
+}
+
+static void
+gimp_drawable_preview_draw_original (GimpPreview *preview)
+{
+ GimpDrawablePreviewPrivate *priv = GIMP_DRAWABLE_PREVIEW_GET_PRIVATE (preview);
+ guchar *buffer;
+ gint width, height;
+ gint bpp;
+ GimpImageType type;
+
+ if (priv->drawable_ID < 1)
+ return;
+
+ preview->xoff = CLAMP (preview->xoff,
+ 0, preview->xmax - preview->xmin - preview->width);
+ preview->yoff = CLAMP (preview->yoff,
+ 0, preview->ymax - preview->ymin - preview->height);
+
+ width = preview->width;
+ height = preview->height;
+
+ buffer = gimp_drawable_get_sub_thumbnail_data (priv->drawable_ID,
+ preview->xoff + preview->xmin,
+ preview->yoff + preview->ymin,
+ preview->width, preview->height,
+ &width, &height, &bpp);
+
+ switch (bpp)
+ {
+ case 1: type = GIMP_GRAY_IMAGE; break;
+ case 2: type = GIMP_GRAYA_IMAGE; break;
+ case 3: type = GIMP_RGB_IMAGE; break;
+ case 4: type = GIMP_RGBA_IMAGE; break;
+ default:
+ g_free (buffer);
+ return;
+ }
+
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview->area),
+ 0, 0, width, height, type, buffer, width * bpp);
+ g_free (buffer);
+}
+
+static void
+gimp_drawable_preview_draw_thumb (GimpPreview *preview,
+ GimpPreviewArea *area,
+ gint width,
+ gint height)
+{
+ GimpDrawablePreviewPrivate *priv = GIMP_DRAWABLE_PREVIEW_GET_PRIVATE (preview);
+
+ if (priv->drawable_ID > 0)
+ _gimp_drawable_preview_area_draw_thumb (area, priv->drawable_ID,
+ width, height);
+}
+
+void
+_gimp_drawable_preview_area_draw_thumb (GimpPreviewArea *area,
+ gint32 drawable_ID,
+ gint width,
+ gint height)
+{
+ guchar *buffer;
+ gint x1, y1, x2, y2;
+ gint bpp;
+ gint size = 100;
+ gint nav_width, nav_height;
+
+ g_return_if_fail (GIMP_IS_PREVIEW_AREA (area));
+ g_return_if_fail (gimp_item_is_valid (drawable_ID));
+ g_return_if_fail (gimp_item_is_drawable (drawable_ID));
+
+ if (_gimp_drawable_preview_get_bounds (drawable_ID, &x1, &y1, &x2, &y2))
+ {
+ width = x2 - x1;
+ height = y2 - y1;
+ }
+ else
+ {
+ width = gimp_drawable_width (drawable_ID);
+ height = gimp_drawable_height (drawable_ID);
+ }
+
+ if (width > height)
+ {
+ nav_width = MIN (width, size);
+ nav_height = (height * nav_width) / width;
+ }
+ else
+ {
+ nav_height = MIN (height, size);
+ nav_width = (width * nav_height) / height;
+ }
+
+ if (_gimp_drawable_preview_get_bounds (drawable_ID, &x1, &y1, &x2, &y2))
+ {
+ buffer = gimp_drawable_get_sub_thumbnail_data (drawable_ID,
+ x1, y1, x2 - x1, y2 - y1,
+ &nav_width, &nav_height,
+ &bpp);
+ }
+ else
+ {
+ buffer = gimp_drawable_get_thumbnail_data (drawable_ID,
+ &nav_width, &nav_height,
+ &bpp);
+ }
+
+ if (buffer)
+ {
+ GimpImageType type;
+
+ gtk_widget_set_size_request (GTK_WIDGET (area), nav_width, nav_height);
+ gtk_widget_show (GTK_WIDGET (area));
+ gtk_widget_realize (GTK_WIDGET (area));
+
+ switch (bpp)
+ {
+ case 1: type = GIMP_GRAY_IMAGE; break;
+ case 2: type = GIMP_GRAYA_IMAGE; break;
+ case 3: type = GIMP_RGB_IMAGE; break;
+ case 4: type = GIMP_RGBA_IMAGE; break;
+ default:
+ g_free (buffer);
+ return;
+ }
+
+ gimp_preview_area_draw (area,
+ 0, 0, nav_width, nav_height,
+ type, buffer, bpp * nav_width);
+ g_free (buffer);
+ }
+}
+
+static void
+gimp_drawable_preview_draw_area (GimpDrawablePreview *preview,
+ gint x,
+ gint y,
+ gint width,
+ gint height,
+ const guchar *buf,
+ gint rowstride)
+{
+ GimpDrawablePreviewPrivate *priv = GIMP_DRAWABLE_PREVIEW_GET_PRIVATE (preview);
+ GimpPreview *gimp_preview = GIMP_PREVIEW (preview);
+ gint32 image_ID;
+
+ image_ID = gimp_item_get_image (priv->drawable_ID);
+
+ if (gimp_selection_is_empty (image_ID))
+ {
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (gimp_preview->area),
+ x - gimp_preview->xoff - gimp_preview->xmin,
+ y - gimp_preview->yoff - gimp_preview->ymin,
+ width,
+ height,
+ gimp_drawable_type (priv->drawable_ID),
+ buf, rowstride);
+ }
+ else
+ {
+ gint offset_x, offset_y;
+ gint mask_x, mask_y;
+ gint mask_width, mask_height;
+ gint draw_x, draw_y;
+ gint draw_width, draw_height;
+
+ gimp_drawable_offsets (priv->drawable_ID, &offset_x, &offset_y);
+
+ if (gimp_drawable_mask_intersect (priv->drawable_ID,
+ &mask_x, &mask_y,
+ &mask_width, &mask_height) &&
+ gimp_rectangle_intersect (mask_x, mask_y,
+ mask_width, mask_height,
+ x, y, width, height,
+ &draw_x, &draw_y,
+ &draw_width, &draw_height))
+ {
+ GimpImageType type;
+ gint32 selection_ID;
+ guchar *src;
+ guchar *sel;
+ gint d_w, d_h, d_bpp;
+ gint s_w, s_h, s_bpp;
+
+ d_w = draw_width;
+ d_h = draw_height;
+
+ s_w = draw_width;
+ s_h = draw_height;
+
+ selection_ID = gimp_image_get_selection (image_ID);
+
+ src = gimp_drawable_get_sub_thumbnail_data (priv->drawable_ID,
+ draw_x, draw_y,
+ draw_width, draw_height,
+ &d_w, &d_h,
+ &d_bpp);
+
+ sel = gimp_drawable_get_sub_thumbnail_data (selection_ID,
+ draw_x + offset_x,
+ draw_y + offset_y,
+ draw_width, draw_height,
+ &s_w, &s_h,
+ &s_bpp);
+
+ switch (d_bpp)
+ {
+ case 1: type = GIMP_GRAY_IMAGE; break;
+ case 2: type = GIMP_GRAYA_IMAGE; break;
+ case 3: type = GIMP_RGB_IMAGE; break;
+ case 4: type = GIMP_RGBA_IMAGE; break;
+ default:
+ g_free (sel);
+ g_free (src);
+ return;
+ }
+
+ gimp_preview_area_mask (GIMP_PREVIEW_AREA (gimp_preview->area),
+ draw_x - gimp_preview->xoff - gimp_preview->xmin,
+ draw_y - gimp_preview->yoff - gimp_preview->ymin,
+ draw_width,
+ draw_height,
+ type,
+ src, draw_width * d_bpp,
+ (buf +
+ (draw_x - x) * d_bpp +
+ (draw_y - y) * d_w * d_bpp),
+ rowstride,
+ sel, s_w);
+
+ g_free (sel);
+ g_free (src);
+ }
+ }
+}
+
+static void
+gimp_drawable_preview_draw_buffer (GimpPreview *preview,
+ const guchar *buffer,
+ gint rowstride)
+{
+ gimp_drawable_preview_draw_area (GIMP_DRAWABLE_PREVIEW (preview),
+ preview->xmin + preview->xoff,
+ preview->ymin + preview->yoff,
+ preview->width,
+ preview->height,
+ buffer, rowstride);
+}
+
+static void
+gimp_drawable_preview_set_drawable (GimpDrawablePreview *drawable_preview,
+ GimpDrawable *drawable)
+{
+ GimpPreview *preview = GIMP_PREVIEW (drawable_preview);
+ GimpDrawablePreviewPrivate *priv = GIMP_DRAWABLE_PREVIEW_GET_PRIVATE (preview);
+
+ g_return_if_fail (drawable_preview->drawable == NULL);
+ g_return_if_fail (priv->drawable_ID < 1);
+
+ drawable_preview->drawable = drawable;
+
+ gimp_drawable_preview_set_drawable_id (drawable_preview,
+ drawable->drawable_id);
+}
+
+static void
+gimp_drawable_preview_set_drawable_id (GimpDrawablePreview *drawable_preview,
+ gint32 drawable_ID)
+{
+ GimpPreview *preview = GIMP_PREVIEW (drawable_preview);
+ GimpDrawablePreviewPrivate *priv = GIMP_DRAWABLE_PREVIEW_GET_PRIVATE (preview);
+ gint x1, y1, x2, y2;
+
+ g_return_if_fail (priv->drawable_ID < 1);
+
+ priv->drawable_ID = drawable_ID;
+
+ _gimp_drawable_preview_get_bounds (drawable_ID, &x1, &y1, &x2, &y2);
+
+ gimp_preview_set_bounds (preview, x1, y1, x2, y2);
+
+ if (gimp_drawable_is_indexed (drawable_ID))
+ {
+ guint32 image_ID = gimp_item_get_image (drawable_ID);
+ guchar *cmap;
+ gint num_colors;
+
+ cmap = gimp_image_get_colormap (image_ID, &num_colors);
+ gimp_preview_area_set_colormap (GIMP_PREVIEW_AREA (preview->area),
+ cmap, num_colors);
+ g_free (cmap);
+ }
+}
+
+
+#define MAX3(a, b, c) (MAX (MAX ((a), (b)), (c)))
+#define MIN3(a, b, c) (MIN (MIN ((a), (b)), (c)))
+
+gboolean
+_gimp_drawable_preview_get_bounds (gint32 drawable_ID,
+ gint *xmin,
+ gint *ymin,
+ gint *xmax,
+ gint *ymax)
+{
+ gint width;
+ gint height;
+ gint offset_x;
+ gint offset_y;
+ gint x1, y1;
+ gint x2, y2;
+ gboolean retval;
+
+ g_return_val_if_fail (gimp_item_is_valid (drawable_ID), FALSE);
+ g_return_val_if_fail (gimp_item_is_drawable (drawable_ID), FALSE);
+
+ width = gimp_drawable_width (drawable_ID);
+ height = gimp_drawable_height (drawable_ID);
+
+ retval = gimp_drawable_mask_bounds (drawable_ID, &x1, &y1, &x2, &y2);
+
+ gimp_drawable_offsets (drawable_ID, &offset_x, &offset_y);
+
+ *xmin = MAX3 (x1 - SELECTION_BORDER, 0, - offset_x);
+ *ymin = MAX3 (y1 - SELECTION_BORDER, 0, - offset_y);
+ *xmax = MIN3 (x2 + SELECTION_BORDER, width, width + offset_x);
+ *ymax = MIN3 (y2 + SELECTION_BORDER, height, height + offset_y);
+
+ return retval;
+}
+
+
+/**
+ * gimp_drawable_preview_new_from_drawable_id:
+ * @drawable_ID: a drawable ID
+ *
+ * Creates a new #GimpDrawablePreview widget for @drawable_ID.
+ *
+ * Returns: A pointer to the new #GimpDrawablePreview widget.
+ *
+ * Since: 2.10
+ **/
+GtkWidget *
+gimp_drawable_preview_new_from_drawable_id (gint32 drawable_ID)
+{
+ g_return_val_if_fail (gimp_item_is_valid (drawable_ID), NULL);
+ g_return_val_if_fail (gimp_item_is_drawable (drawable_ID), NULL);
+
+ return g_object_new (GIMP_TYPE_DRAWABLE_PREVIEW,
+ "drawable-id", drawable_ID,
+ NULL);
+}
+
+/**
+ * gimp_drawable_preview_get_drawable_id:
+ * @preview: a #GimpDrawablePreview widget
+ *
+ * Return value: the drawable_ID that has been passed to
+ * gimp_drawable_preview_new_from_drawable_id().
+ *
+ * Since: 2.10
+ **/
+gint32
+gimp_drawable_preview_get_drawable_id (GimpDrawablePreview *preview)
+{
+ g_return_val_if_fail (GIMP_IS_DRAWABLE_PREVIEW (preview), -1);
+
+ return GIMP_DRAWABLE_PREVIEW_GET_PRIVATE (preview)->drawable_ID;
+}
+
+/**
+ * gimp_drawable_preview_new:
+ * @drawable: a #GimpDrawable
+ * @toggle: unused
+ *
+ * Creates a new #GimpDrawablePreview widget for @drawable.
+ *
+ * In GIMP 2.2 the @toggle parameter was provided to conviently access
+ * the state of the "Preview" check-button. This is not any longer
+ * necessary as the preview itself now stores this state, as well as
+ * the scroll offset.
+ *
+ * Returns: A pointer to the new #GimpDrawablePreview widget.
+ *
+ * Deprecated: Use gimp_drawable_preview_new_from_drawable_id() instead.
+ *
+ * Since: 2.2
+ **/
+GtkWidget *
+gimp_drawable_preview_new (GimpDrawable *drawable,
+ gboolean *toggle)
+{
+ g_return_val_if_fail (drawable != NULL, NULL);
+
+ return g_object_new (GIMP_TYPE_DRAWABLE_PREVIEW,
+ "drawable", drawable,
+ NULL);
+}
+
+/**
+ * gimp_drawable_preview_get_drawable:
+ * @preview: a #GimpDrawablePreview widget
+ *
+ * Return value: the #GimpDrawable that has been passed to
+ * gimp_drawable_preview_new().
+ *
+ * Deprecated: use gimp_drawable_preview_get_drawable_id() instead.
+ *
+ * Since: 2.2
+ **/
+GimpDrawable *
+gimp_drawable_preview_get_drawable (GimpDrawablePreview *preview)
+{
+ g_return_val_if_fail (GIMP_IS_DRAWABLE_PREVIEW (preview), NULL);
+
+ return preview->drawable;
+}
+
+/**
+ * gimp_drawable_preview_draw_region:
+ * @preview: a #GimpDrawablePreview widget
+ * @region: a #GimpPixelRgn
+ *
+ * Since: 2.2
+ **/
+void
+gimp_drawable_preview_draw_region (GimpDrawablePreview *preview,
+ const GimpPixelRgn *region)
+{
+ GimpDrawablePreviewPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_DRAWABLE_PREVIEW (preview));
+ g_return_if_fail (region != NULL);
+
+ priv = GIMP_DRAWABLE_PREVIEW_GET_PRIVATE (preview);
+
+ g_return_if_fail (priv->drawable_ID > 0);
+
+ /* If the data field is initialized, this region is currently being
+ * processed and we can access it directly.
+ */
+ if (region->data)
+ {
+ gimp_drawable_preview_draw_area (preview,
+ region->x,
+ region->y,
+ region->w,
+ region->h,
+ region->data,
+ region->rowstride);
+ }
+ else
+ {
+ GimpPixelRgn src = *region;
+ gpointer iter;
+
+ src.dirty = FALSE; /* we don't dirty the tiles, just read them */
+
+ for (iter = gimp_pixel_rgns_register (1, &src);
+ iter != NULL;
+ iter = gimp_pixel_rgns_process (iter))
+ {
+ gimp_drawable_preview_draw_area (preview,
+ src.x,
+ src.y,
+ src.w,
+ src.h,
+ src.data,
+ src.rowstride);
+ }
+ }
+}
diff --git a/libgimp/gimpdrawablepreview.h b/libgimp/gimpdrawablepreview.h
new file mode 100644
index 0000000..c442994
--- /dev/null
+++ b/libgimp/gimpdrawablepreview.h
@@ -0,0 +1,93 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpdrawablepreview.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_UI_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimpui.h> can be included directly."
+#endif
+
+#ifndef __GIMP_DRAWABLE_PREVIEW_H__
+#define __GIMP_DRAWABLE_PREVIEW_H__
+
+G_BEGIN_DECLS
+
+
+/* For information look into the C source or the html documentation */
+
+
+#define GIMP_TYPE_DRAWABLE_PREVIEW (gimp_drawable_preview_get_type ())
+#define GIMP_DRAWABLE_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_DRAWABLE_PREVIEW, GimpDrawablePreview))
+#define GIMP_DRAWABLE_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_DRAWABLE_PREVIEW, GimpDrawablePreviewClass))
+#define GIMP_IS_DRAWABLE_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_DRAWABLE_PREVIEW))
+#define GIMP_IS_DRAWABLE_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_DRAWABLE_PREVIEW))
+#define GIMP_DRAWABLE_PREVIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_DRAWABLE_PREVIEW, GimpDrawablePreviewClass))
+
+
+typedef struct _GimpDrawablePreviewClass GimpDrawablePreviewClass;
+
+struct _GimpDrawablePreview
+{
+ GimpScrolledPreview parent_instance;
+
+ GimpDrawable *drawable;
+};
+
+struct _GimpDrawablePreviewClass
+{
+ GimpScrolledPreviewClass parent_class;
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_drawable_preview_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_drawable_preview_new_from_drawable_id (gint32 drawable_ID);
+gint32 gimp_drawable_preview_get_drawable_id (GimpDrawablePreview *preview);
+
+GIMP_DEPRECATED_FOR(gimp_drawable_preview_new_from_drawable_id)
+GtkWidget * gimp_drawable_preview_new (GimpDrawable *drawable,
+ gboolean *toggle);
+GIMP_DEPRECATED_FOR(gimp_drawable_preview_get_drawable_id)
+GimpDrawable * gimp_drawable_preview_get_drawable (GimpDrawablePreview *preview);
+
+GIMP_DEPRECATED_FOR(gimp_preview_draw_buffer)
+void gimp_drawable_preview_draw_region (GimpDrawablePreview *preview,
+ const GimpPixelRgn *region);
+
+/* for internal use only */
+G_GNUC_INTERNAL void _gimp_drawable_preview_area_draw_thumb (GimpPreviewArea *area,
+ gint32 drawable_ID,
+ gint width,
+ gint height);
+G_GNUC_INTERNAL gboolean _gimp_drawable_preview_get_bounds (gint32 drawable_ID,
+ gint *xmin,
+ gint *ymin,
+ gint *xmax,
+ gint *ymax);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_DRAWABLE_PREVIEW_H__ */
+
diff --git a/libgimp/gimpdrawabletransform_pdb.c b/libgimp/gimpdrawabletransform_pdb.c
new file mode 100644
index 0000000..fa92d50
--- /dev/null
+++ b/libgimp/gimpdrawabletransform_pdb.c
@@ -0,0 +1,917 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpdrawabletransform_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpdrawabletransform
+ * @title: gimpdrawabletransform
+ * @short_description: Functions to perform transformatrions on drawables.
+ *
+ * Functions to perform transformatrions on drawables.
+ **/
+
+
+/**
+ * gimp_drawable_transform_flip_simple:
+ * @drawable_ID: The affected drawable.
+ * @flip_type: Type of flip.
+ * @auto_center: Whether to automatically position the axis in the selection center.
+ * @axis: coord. of flip axis.
+ * @clip_result: Whether to clip results.
+ *
+ * Deprecated: Use gimp_item_transform_flip_simple() instead.
+ *
+ * Returns: The flipped drawable.
+ *
+ * Since: 2.2
+ **/
+gint32
+gimp_drawable_transform_flip_simple (gint32 drawable_ID,
+ GimpOrientationType flip_type,
+ gboolean auto_center,
+ gdouble axis,
+ gboolean clip_result)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_drawable_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-transform-flip-simple",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, flip_type,
+ GIMP_PDB_INT32, auto_center,
+ GIMP_PDB_FLOAT, axis,
+ GIMP_PDB_INT32, clip_result,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_drawable_ID = return_vals[1].data.d_drawable;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_drawable_ID;
+}
+
+/**
+ * gimp_drawable_transform_flip:
+ * @drawable_ID: The affected drawable.
+ * @x0: horz. coord. of one end of axis.
+ * @y0: vert. coord. of one end of axis.
+ * @x1: horz. coord. of other end of axis.
+ * @y1: vert. coord. of other end of axis.
+ * @transform_direction: Direction of transformation.
+ * @interpolation: Type of interpolation.
+ * @supersample: This parameter is ignored.
+ * @recursion_level: This parameter is ignored.
+ * @clip_result: Whether to clip results.
+ *
+ * Deprecated: Use gimp_item_transform_flip() instead.
+ *
+ * Returns: The flipped drawable.
+ *
+ * Since: 2.2
+ **/
+gint32
+gimp_drawable_transform_flip (gint32 drawable_ID,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1,
+ GimpTransformDirection transform_direction,
+ GimpInterpolationType interpolation,
+ gboolean supersample,
+ gint recursion_level,
+ gboolean clip_result)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_drawable_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-transform-flip",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_FLOAT, x0,
+ GIMP_PDB_FLOAT, y0,
+ GIMP_PDB_FLOAT, x1,
+ GIMP_PDB_FLOAT, y1,
+ GIMP_PDB_INT32, transform_direction,
+ GIMP_PDB_INT32, interpolation,
+ GIMP_PDB_INT32, supersample,
+ GIMP_PDB_INT32, recursion_level,
+ GIMP_PDB_INT32, clip_result,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_drawable_ID = return_vals[1].data.d_drawable;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_drawable_ID;
+}
+
+/**
+ * gimp_drawable_transform_flip_default:
+ * @drawable_ID: The affected drawable.
+ * @x0: horz. coord. of one end of axis.
+ * @y0: vert. coord. of one end of axis.
+ * @x1: horz. coord. of other end of axis.
+ * @y1: vert. coord. of other end of axis.
+ * @interpolate: Whether to use interpolation and supersampling.
+ * @clip_result: Whether to clip results.
+ *
+ * Deprecated: Use gimp_item_transform_flip() instead.
+ *
+ * Returns: The flipped drawable.
+ *
+ * Since: 2.2
+ **/
+gint32
+gimp_drawable_transform_flip_default (gint32 drawable_ID,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1,
+ gboolean interpolate,
+ gboolean clip_result)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_drawable_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-transform-flip-default",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_FLOAT, x0,
+ GIMP_PDB_FLOAT, y0,
+ GIMP_PDB_FLOAT, x1,
+ GIMP_PDB_FLOAT, y1,
+ GIMP_PDB_INT32, interpolate,
+ GIMP_PDB_INT32, clip_result,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_drawable_ID = return_vals[1].data.d_drawable;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_drawable_ID;
+}
+
+/**
+ * gimp_drawable_transform_perspective:
+ * @drawable_ID: The affected drawable.
+ * @x0: The new x coordinate of upper-left corner of original bounding box.
+ * @y0: The new y coordinate of upper-left corner of original bounding box.
+ * @x1: The new x coordinate of upper-right corner of original bounding box.
+ * @y1: The new y coordinate of upper-right corner of original bounding box.
+ * @x2: The new x coordinate of lower-left corner of original bounding box.
+ * @y2: The new y coordinate of lower-left corner of original bounding box.
+ * @x3: The new x coordinate of lower-right corner of original bounding box.
+ * @y3: The new y coordinate of lower-right corner of original bounding box.
+ * @transform_direction: Direction of transformation.
+ * @interpolation: Type of interpolation.
+ * @supersample: This parameter is ignored.
+ * @recursion_level: This parameter is ignored.
+ * @clip_result: How to clip results.
+ *
+ * Deprecated: Use gimp_item_transform_perspective() instead.
+ *
+ * Returns: The newly mapped drawable.
+ *
+ * Since: 2.2
+ **/
+gint32
+gimp_drawable_transform_perspective (gint32 drawable_ID,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2,
+ gdouble x3,
+ gdouble y3,
+ GimpTransformDirection transform_direction,
+ GimpInterpolationType interpolation,
+ gboolean supersample,
+ gint recursion_level,
+ GimpTransformResize clip_result)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_drawable_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-transform-perspective",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_FLOAT, x0,
+ GIMP_PDB_FLOAT, y0,
+ GIMP_PDB_FLOAT, x1,
+ GIMP_PDB_FLOAT, y1,
+ GIMP_PDB_FLOAT, x2,
+ GIMP_PDB_FLOAT, y2,
+ GIMP_PDB_FLOAT, x3,
+ GIMP_PDB_FLOAT, y3,
+ GIMP_PDB_INT32, transform_direction,
+ GIMP_PDB_INT32, interpolation,
+ GIMP_PDB_INT32, supersample,
+ GIMP_PDB_INT32, recursion_level,
+ GIMP_PDB_INT32, clip_result,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_drawable_ID = return_vals[1].data.d_drawable;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_drawable_ID;
+}
+
+/**
+ * gimp_drawable_transform_perspective_default:
+ * @drawable_ID: The affected drawable.
+ * @x0: The new x coordinate of upper-left corner of original bounding box.
+ * @y0: The new y coordinate of upper-left corner of original bounding box.
+ * @x1: The new x coordinate of upper-right corner of original bounding box.
+ * @y1: The new y coordinate of upper-right corner of original bounding box.
+ * @x2: The new x coordinate of lower-left corner of original bounding box.
+ * @y2: The new y coordinate of lower-left corner of original bounding box.
+ * @x3: The new x coordinate of lower-right corner of original bounding box.
+ * @y3: The new y coordinate of lower-right corner of original bounding box.
+ * @interpolate: Whether to use interpolation and supersampling.
+ * @clip_result: How to clip results.
+ *
+ * Deprecated: Use gimp_item_transform_perspective() instead.
+ *
+ * Returns: The newly mapped drawable.
+ *
+ * Since: 2.2
+ **/
+gint32
+gimp_drawable_transform_perspective_default (gint32 drawable_ID,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2,
+ gdouble x3,
+ gdouble y3,
+ gboolean interpolate,
+ GimpTransformResize clip_result)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_drawable_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-transform-perspective-default",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_FLOAT, x0,
+ GIMP_PDB_FLOAT, y0,
+ GIMP_PDB_FLOAT, x1,
+ GIMP_PDB_FLOAT, y1,
+ GIMP_PDB_FLOAT, x2,
+ GIMP_PDB_FLOAT, y2,
+ GIMP_PDB_FLOAT, x3,
+ GIMP_PDB_FLOAT, y3,
+ GIMP_PDB_INT32, interpolate,
+ GIMP_PDB_INT32, clip_result,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_drawable_ID = return_vals[1].data.d_drawable;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_drawable_ID;
+}
+
+/**
+ * gimp_drawable_transform_rotate_simple:
+ * @drawable_ID: The affected drawable.
+ * @rotate_type: Type of rotation.
+ * @auto_center: Whether to automatically rotate around the selection center.
+ * @center_x: The hor. coordinate of the center of rotation.
+ * @center_y: The vert. coordinate of the center of rotation.
+ * @clip_result: Whether to clip results.
+ *
+ * Deprecated: Use gimp_item_transform_rotate_simple() instead.
+ *
+ * Returns: The rotated drawable.
+ *
+ * Since: 2.2
+ **/
+gint32
+gimp_drawable_transform_rotate_simple (gint32 drawable_ID,
+ GimpRotationType rotate_type,
+ gboolean auto_center,
+ gint center_x,
+ gint center_y,
+ gboolean clip_result)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_drawable_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-transform-rotate-simple",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, rotate_type,
+ GIMP_PDB_INT32, auto_center,
+ GIMP_PDB_INT32, center_x,
+ GIMP_PDB_INT32, center_y,
+ GIMP_PDB_INT32, clip_result,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_drawable_ID = return_vals[1].data.d_drawable;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_drawable_ID;
+}
+
+/**
+ * gimp_drawable_transform_rotate:
+ * @drawable_ID: The affected drawable.
+ * @angle: The angle of rotation (radians).
+ * @auto_center: Whether to automatically rotate around the selection center.
+ * @center_x: The hor. coordinate of the center of rotation.
+ * @center_y: The vert. coordinate of the center of rotation.
+ * @transform_direction: Direction of transformation.
+ * @interpolation: Type of interpolation.
+ * @supersample: This parameter is ignored.
+ * @recursion_level: This parameter is ignored.
+ * @clip_result: How to clip results.
+ *
+ * Deprecated: Use gimp_item_transform_rotate() instead.
+ *
+ * Returns: The rotated drawable.
+ *
+ * Since: 2.2
+ **/
+gint32
+gimp_drawable_transform_rotate (gint32 drawable_ID,
+ gdouble angle,
+ gboolean auto_center,
+ gint center_x,
+ gint center_y,
+ GimpTransformDirection transform_direction,
+ GimpInterpolationType interpolation,
+ gboolean supersample,
+ gint recursion_level,
+ GimpTransformResize clip_result)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_drawable_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-transform-rotate",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_FLOAT, angle,
+ GIMP_PDB_INT32, auto_center,
+ GIMP_PDB_INT32, center_x,
+ GIMP_PDB_INT32, center_y,
+ GIMP_PDB_INT32, transform_direction,
+ GIMP_PDB_INT32, interpolation,
+ GIMP_PDB_INT32, supersample,
+ GIMP_PDB_INT32, recursion_level,
+ GIMP_PDB_INT32, clip_result,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_drawable_ID = return_vals[1].data.d_drawable;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_drawable_ID;
+}
+
+/**
+ * gimp_drawable_transform_rotate_default:
+ * @drawable_ID: The affected drawable.
+ * @angle: The angle of rotation (radians).
+ * @auto_center: Whether to automatically rotate around the selection center.
+ * @center_x: The hor. coordinate of the center of rotation.
+ * @center_y: The vert. coordinate of the center of rotation.
+ * @interpolate: Whether to use interpolation and supersampling.
+ * @clip_result: How to clip results.
+ *
+ * Deprecated: Use gimp_item_transform_rotate() instead.
+ *
+ * Returns: The rotated drawable.
+ *
+ * Since: 2.2
+ **/
+gint32
+gimp_drawable_transform_rotate_default (gint32 drawable_ID,
+ gdouble angle,
+ gboolean auto_center,
+ gint center_x,
+ gint center_y,
+ gboolean interpolate,
+ GimpTransformResize clip_result)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_drawable_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-transform-rotate-default",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_FLOAT, angle,
+ GIMP_PDB_INT32, auto_center,
+ GIMP_PDB_INT32, center_x,
+ GIMP_PDB_INT32, center_y,
+ GIMP_PDB_INT32, interpolate,
+ GIMP_PDB_INT32, clip_result,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_drawable_ID = return_vals[1].data.d_drawable;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_drawable_ID;
+}
+
+/**
+ * gimp_drawable_transform_scale:
+ * @drawable_ID: The affected drawable.
+ * @x0: The new x coordinate of the upper-left corner of the scaled region.
+ * @y0: The new y coordinate of the upper-left corner of the scaled region.
+ * @x1: The new x coordinate of the lower-right corner of the scaled region.
+ * @y1: The new y coordinate of the lower-right corner of the scaled region.
+ * @transform_direction: Direction of transformation.
+ * @interpolation: Type of interpolation.
+ * @supersample: This parameter is ignored.
+ * @recursion_level: This parameter is ignored.
+ * @clip_result: How to clip results.
+ *
+ * Deprecated: Use gimp_item_transform_scale() instead.
+ *
+ * Returns: The scaled drawable.
+ *
+ * Since: 2.2
+ **/
+gint32
+gimp_drawable_transform_scale (gint32 drawable_ID,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1,
+ GimpTransformDirection transform_direction,
+ GimpInterpolationType interpolation,
+ gboolean supersample,
+ gint recursion_level,
+ GimpTransformResize clip_result)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_drawable_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-transform-scale",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_FLOAT, x0,
+ GIMP_PDB_FLOAT, y0,
+ GIMP_PDB_FLOAT, x1,
+ GIMP_PDB_FLOAT, y1,
+ GIMP_PDB_INT32, transform_direction,
+ GIMP_PDB_INT32, interpolation,
+ GIMP_PDB_INT32, supersample,
+ GIMP_PDB_INT32, recursion_level,
+ GIMP_PDB_INT32, clip_result,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_drawable_ID = return_vals[1].data.d_drawable;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_drawable_ID;
+}
+
+/**
+ * gimp_drawable_transform_scale_default:
+ * @drawable_ID: The affected drawable.
+ * @x0: The new x coordinate of the upper-left corner of the scaled region.
+ * @y0: The new y coordinate of the upper-left corner of the scaled region.
+ * @x1: The new x coordinate of the lower-right corner of the scaled region.
+ * @y1: The new y coordinate of the lower-right corner of the scaled region.
+ * @interpolate: Whether to use interpolation and supersampling.
+ * @clip_result: How to clip results.
+ *
+ * Deprecated: Use gimp_item_transform_scale() instead.
+ *
+ * Returns: The scaled drawable.
+ *
+ * Since: 2.2
+ **/
+gint32
+gimp_drawable_transform_scale_default (gint32 drawable_ID,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1,
+ gboolean interpolate,
+ GimpTransformResize clip_result)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_drawable_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-transform-scale-default",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_FLOAT, x0,
+ GIMP_PDB_FLOAT, y0,
+ GIMP_PDB_FLOAT, x1,
+ GIMP_PDB_FLOAT, y1,
+ GIMP_PDB_INT32, interpolate,
+ GIMP_PDB_INT32, clip_result,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_drawable_ID = return_vals[1].data.d_drawable;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_drawable_ID;
+}
+
+/**
+ * gimp_drawable_transform_shear:
+ * @drawable_ID: The affected drawable.
+ * @shear_type: Type of shear.
+ * @magnitude: The magnitude of the shear.
+ * @transform_direction: Direction of transformation.
+ * @interpolation: Type of interpolation.
+ * @supersample: This parameter is ignored.
+ * @recursion_level: This parameter is ignored.
+ * @clip_result: How to clip results.
+ *
+ * Deprecated: Use gimp_item_transform_shear() instead.
+ *
+ * Returns: The sheared drawable.
+ *
+ * Since: 2.2
+ **/
+gint32
+gimp_drawable_transform_shear (gint32 drawable_ID,
+ GimpOrientationType shear_type,
+ gdouble magnitude,
+ GimpTransformDirection transform_direction,
+ GimpInterpolationType interpolation,
+ gboolean supersample,
+ gint recursion_level,
+ GimpTransformResize clip_result)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_drawable_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-transform-shear",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, shear_type,
+ GIMP_PDB_FLOAT, magnitude,
+ GIMP_PDB_INT32, transform_direction,
+ GIMP_PDB_INT32, interpolation,
+ GIMP_PDB_INT32, supersample,
+ GIMP_PDB_INT32, recursion_level,
+ GIMP_PDB_INT32, clip_result,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_drawable_ID = return_vals[1].data.d_drawable;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_drawable_ID;
+}
+
+/**
+ * gimp_drawable_transform_shear_default:
+ * @drawable_ID: The affected drawable.
+ * @shear_type: Type of shear.
+ * @magnitude: The magnitude of the shear.
+ * @interpolate: Whether to use interpolation and supersampling.
+ * @clip_result: How to clip results.
+ *
+ * Deprecated: Use gimp_item_transform_shear() instead.
+ *
+ * Returns: The sheared drawable.
+ *
+ * Since: 2.2
+ **/
+gint32
+gimp_drawable_transform_shear_default (gint32 drawable_ID,
+ GimpOrientationType shear_type,
+ gdouble magnitude,
+ gboolean interpolate,
+ GimpTransformResize clip_result)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_drawable_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-transform-shear-default",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, shear_type,
+ GIMP_PDB_FLOAT, magnitude,
+ GIMP_PDB_INT32, interpolate,
+ GIMP_PDB_INT32, clip_result,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_drawable_ID = return_vals[1].data.d_drawable;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_drawable_ID;
+}
+
+/**
+ * gimp_drawable_transform_2d:
+ * @drawable_ID: The affected drawable.
+ * @source_x: X coordinate of the transformation center.
+ * @source_y: Y coordinate of the transformation center.
+ * @scale_x: Amount to scale in x direction.
+ * @scale_y: Amount to scale in y direction.
+ * @angle: The angle of rotation (radians).
+ * @dest_x: X coordinate of where the center goes.
+ * @dest_y: Y coordinate of where the center goes.
+ * @transform_direction: Direction of transformation.
+ * @interpolation: Type of interpolation.
+ * @supersample: This parameter is ignored.
+ * @recursion_level: This parameter is ignored.
+ * @clip_result: How to clip results.
+ *
+ * Deprecated: Use gimp_item_transform_2d() instead.
+ *
+ * Returns: The transformed drawable.
+ *
+ * Since: 2.2
+ **/
+gint32
+gimp_drawable_transform_2d (gint32 drawable_ID,
+ gdouble source_x,
+ gdouble source_y,
+ gdouble scale_x,
+ gdouble scale_y,
+ gdouble angle,
+ gdouble dest_x,
+ gdouble dest_y,
+ GimpTransformDirection transform_direction,
+ GimpInterpolationType interpolation,
+ gboolean supersample,
+ gint recursion_level,
+ GimpTransformResize clip_result)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_drawable_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-transform-2d",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_FLOAT, source_x,
+ GIMP_PDB_FLOAT, source_y,
+ GIMP_PDB_FLOAT, scale_x,
+ GIMP_PDB_FLOAT, scale_y,
+ GIMP_PDB_FLOAT, angle,
+ GIMP_PDB_FLOAT, dest_x,
+ GIMP_PDB_FLOAT, dest_y,
+ GIMP_PDB_INT32, transform_direction,
+ GIMP_PDB_INT32, interpolation,
+ GIMP_PDB_INT32, supersample,
+ GIMP_PDB_INT32, recursion_level,
+ GIMP_PDB_INT32, clip_result,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_drawable_ID = return_vals[1].data.d_drawable;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_drawable_ID;
+}
+
+/**
+ * gimp_drawable_transform_2d_default:
+ * @drawable_ID: The affected drawable.
+ * @source_x: X coordinate of the transformation center.
+ * @source_y: Y coordinate of the transformation center.
+ * @scale_x: Amount to scale in x direction.
+ * @scale_y: Amount to scale in y direction.
+ * @angle: The angle of rotation (radians).
+ * @dest_x: X coordinate of where the center goes.
+ * @dest_y: Y coordinate of where the center goes.
+ * @interpolate: Whether to use interpolation and supersampling.
+ * @clip_result: How to clip results.
+ *
+ * Deprecated: Use gimp_item_transform_2d() instead.
+ *
+ * Returns: The transformed drawable.
+ *
+ * Since: 2.2
+ **/
+gint32
+gimp_drawable_transform_2d_default (gint32 drawable_ID,
+ gdouble source_x,
+ gdouble source_y,
+ gdouble scale_x,
+ gdouble scale_y,
+ gdouble angle,
+ gdouble dest_x,
+ gdouble dest_y,
+ gboolean interpolate,
+ GimpTransformResize clip_result)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_drawable_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-transform-2d-default",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_FLOAT, source_x,
+ GIMP_PDB_FLOAT, source_y,
+ GIMP_PDB_FLOAT, scale_x,
+ GIMP_PDB_FLOAT, scale_y,
+ GIMP_PDB_FLOAT, angle,
+ GIMP_PDB_FLOAT, dest_x,
+ GIMP_PDB_FLOAT, dest_y,
+ GIMP_PDB_INT32, interpolate,
+ GIMP_PDB_INT32, clip_result,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_drawable_ID = return_vals[1].data.d_drawable;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_drawable_ID;
+}
+
+/**
+ * gimp_drawable_transform_matrix:
+ * @drawable_ID: The affected drawable.
+ * @coeff_0_0: coefficient (0,0) of the transformation matrix.
+ * @coeff_0_1: coefficient (0,1) of the transformation matrix.
+ * @coeff_0_2: coefficient (0,2) of the transformation matrix.
+ * @coeff_1_0: coefficient (1,0) of the transformation matrix.
+ * @coeff_1_1: coefficient (1,1) of the transformation matrix.
+ * @coeff_1_2: coefficient (1,2) of the transformation matrix.
+ * @coeff_2_0: coefficient (2,0) of the transformation matrix.
+ * @coeff_2_1: coefficient (2,1) of the transformation matrix.
+ * @coeff_2_2: coefficient (2,2) of the transformation matrix.
+ * @transform_direction: Direction of transformation.
+ * @interpolation: Type of interpolation.
+ * @supersample: This parameter is ignored.
+ * @recursion_level: This parameter is ignored.
+ * @clip_result: How to clip results.
+ *
+ * Deprecated: Use gimp_item_transform_matrix() instead.
+ *
+ * Returns: The transformed drawable.
+ *
+ * Since: 2.2
+ **/
+gint32
+gimp_drawable_transform_matrix (gint32 drawable_ID,
+ gdouble coeff_0_0,
+ gdouble coeff_0_1,
+ gdouble coeff_0_2,
+ gdouble coeff_1_0,
+ gdouble coeff_1_1,
+ gdouble coeff_1_2,
+ gdouble coeff_2_0,
+ gdouble coeff_2_1,
+ gdouble coeff_2_2,
+ GimpTransformDirection transform_direction,
+ GimpInterpolationType interpolation,
+ gboolean supersample,
+ gint recursion_level,
+ GimpTransformResize clip_result)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_drawable_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-transform-matrix",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_FLOAT, coeff_0_0,
+ GIMP_PDB_FLOAT, coeff_0_1,
+ GIMP_PDB_FLOAT, coeff_0_2,
+ GIMP_PDB_FLOAT, coeff_1_0,
+ GIMP_PDB_FLOAT, coeff_1_1,
+ GIMP_PDB_FLOAT, coeff_1_2,
+ GIMP_PDB_FLOAT, coeff_2_0,
+ GIMP_PDB_FLOAT, coeff_2_1,
+ GIMP_PDB_FLOAT, coeff_2_2,
+ GIMP_PDB_INT32, transform_direction,
+ GIMP_PDB_INT32, interpolation,
+ GIMP_PDB_INT32, supersample,
+ GIMP_PDB_INT32, recursion_level,
+ GIMP_PDB_INT32, clip_result,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_drawable_ID = return_vals[1].data.d_drawable;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_drawable_ID;
+}
+
+/**
+ * gimp_drawable_transform_matrix_default:
+ * @drawable_ID: The affected drawable.
+ * @coeff_0_0: coefficient (0,0) of the transformation matrix.
+ * @coeff_0_1: coefficient (0,1) of the transformation matrix.
+ * @coeff_0_2: coefficient (0,2) of the transformation matrix.
+ * @coeff_1_0: coefficient (1,0) of the transformation matrix.
+ * @coeff_1_1: coefficient (1,1) of the transformation matrix.
+ * @coeff_1_2: coefficient (1,2) of the transformation matrix.
+ * @coeff_2_0: coefficient (2,0) of the transformation matrix.
+ * @coeff_2_1: coefficient (2,1) of the transformation matrix.
+ * @coeff_2_2: coefficient (2,2) of the transformation matrix.
+ * @interpolate: Whether to use interpolation and supersampling.
+ * @clip_result: How to clip results.
+ *
+ * Deprecated: Use gimp_item_transform_matrix() instead.
+ *
+ * Returns: The transformed drawable.
+ *
+ * Since: 2.2
+ **/
+gint32
+gimp_drawable_transform_matrix_default (gint32 drawable_ID,
+ gdouble coeff_0_0,
+ gdouble coeff_0_1,
+ gdouble coeff_0_2,
+ gdouble coeff_1_0,
+ gdouble coeff_1_1,
+ gdouble coeff_1_2,
+ gdouble coeff_2_0,
+ gdouble coeff_2_1,
+ gdouble coeff_2_2,
+ gboolean interpolate,
+ GimpTransformResize clip_result)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_drawable_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-drawable-transform-matrix-default",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_FLOAT, coeff_0_0,
+ GIMP_PDB_FLOAT, coeff_0_1,
+ GIMP_PDB_FLOAT, coeff_0_2,
+ GIMP_PDB_FLOAT, coeff_1_0,
+ GIMP_PDB_FLOAT, coeff_1_1,
+ GIMP_PDB_FLOAT, coeff_1_2,
+ GIMP_PDB_FLOAT, coeff_2_0,
+ GIMP_PDB_FLOAT, coeff_2_1,
+ GIMP_PDB_FLOAT, coeff_2_2,
+ GIMP_PDB_INT32, interpolate,
+ GIMP_PDB_INT32, clip_result,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_drawable_ID = return_vals[1].data.d_drawable;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_drawable_ID;
+}
diff --git a/libgimp/gimpdrawabletransform_pdb.h b/libgimp/gimpdrawabletransform_pdb.h
new file mode 100644
index 0000000..5ec36f5
--- /dev/null
+++ b/libgimp/gimpdrawabletransform_pdb.h
@@ -0,0 +1,205 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpdrawabletransform_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_DRAWABLE_TRANSFORM_PDB_H__
+#define __GIMP_DRAWABLE_TRANSFORM_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+GIMP_DEPRECATED_FOR(gimp_item_transform_flip_simple)
+gint32 gimp_drawable_transform_flip_simple (gint32 drawable_ID,
+ GimpOrientationType flip_type,
+ gboolean auto_center,
+ gdouble axis,
+ gboolean clip_result);
+GIMP_DEPRECATED_FOR(gimp_item_transform_flip)
+gint32 gimp_drawable_transform_flip (gint32 drawable_ID,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1,
+ GimpTransformDirection transform_direction,
+ GimpInterpolationType interpolation,
+ gboolean supersample,
+ gint recursion_level,
+ gboolean clip_result);
+GIMP_DEPRECATED_FOR(gimp_item_transform_flip)
+gint32 gimp_drawable_transform_flip_default (gint32 drawable_ID,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1,
+ gboolean interpolate,
+ gboolean clip_result);
+GIMP_DEPRECATED_FOR(gimp_item_transform_perspective)
+gint32 gimp_drawable_transform_perspective (gint32 drawable_ID,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2,
+ gdouble x3,
+ gdouble y3,
+ GimpTransformDirection transform_direction,
+ GimpInterpolationType interpolation,
+ gboolean supersample,
+ gint recursion_level,
+ GimpTransformResize clip_result);
+GIMP_DEPRECATED_FOR(gimp_item_transform_perspective)
+gint32 gimp_drawable_transform_perspective_default (gint32 drawable_ID,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2,
+ gdouble x3,
+ gdouble y3,
+ gboolean interpolate,
+ GimpTransformResize clip_result);
+GIMP_DEPRECATED_FOR(gimp_item_transform_rotate_simple)
+gint32 gimp_drawable_transform_rotate_simple (gint32 drawable_ID,
+ GimpRotationType rotate_type,
+ gboolean auto_center,
+ gint center_x,
+ gint center_y,
+ gboolean clip_result);
+GIMP_DEPRECATED_FOR(gimp_item_transform_rotate)
+gint32 gimp_drawable_transform_rotate (gint32 drawable_ID,
+ gdouble angle,
+ gboolean auto_center,
+ gint center_x,
+ gint center_y,
+ GimpTransformDirection transform_direction,
+ GimpInterpolationType interpolation,
+ gboolean supersample,
+ gint recursion_level,
+ GimpTransformResize clip_result);
+GIMP_DEPRECATED_FOR(gimp_item_transform_rotate)
+gint32 gimp_drawable_transform_rotate_default (gint32 drawable_ID,
+ gdouble angle,
+ gboolean auto_center,
+ gint center_x,
+ gint center_y,
+ gboolean interpolate,
+ GimpTransformResize clip_result);
+GIMP_DEPRECATED_FOR(gimp_item_transform_scale)
+gint32 gimp_drawable_transform_scale (gint32 drawable_ID,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1,
+ GimpTransformDirection transform_direction,
+ GimpInterpolationType interpolation,
+ gboolean supersample,
+ gint recursion_level,
+ GimpTransformResize clip_result);
+GIMP_DEPRECATED_FOR(gimp_item_transform_scale)
+gint32 gimp_drawable_transform_scale_default (gint32 drawable_ID,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1,
+ gboolean interpolate,
+ GimpTransformResize clip_result);
+GIMP_DEPRECATED_FOR(gimp_item_transform_shear)
+gint32 gimp_drawable_transform_shear (gint32 drawable_ID,
+ GimpOrientationType shear_type,
+ gdouble magnitude,
+ GimpTransformDirection transform_direction,
+ GimpInterpolationType interpolation,
+ gboolean supersample,
+ gint recursion_level,
+ GimpTransformResize clip_result);
+GIMP_DEPRECATED_FOR(gimp_item_transform_shear)
+gint32 gimp_drawable_transform_shear_default (gint32 drawable_ID,
+ GimpOrientationType shear_type,
+ gdouble magnitude,
+ gboolean interpolate,
+ GimpTransformResize clip_result);
+GIMP_DEPRECATED_FOR(gimp_item_transform_2d)
+gint32 gimp_drawable_transform_2d (gint32 drawable_ID,
+ gdouble source_x,
+ gdouble source_y,
+ gdouble scale_x,
+ gdouble scale_y,
+ gdouble angle,
+ gdouble dest_x,
+ gdouble dest_y,
+ GimpTransformDirection transform_direction,
+ GimpInterpolationType interpolation,
+ gboolean supersample,
+ gint recursion_level,
+ GimpTransformResize clip_result);
+GIMP_DEPRECATED_FOR(gimp_item_transform_2d)
+gint32 gimp_drawable_transform_2d_default (gint32 drawable_ID,
+ gdouble source_x,
+ gdouble source_y,
+ gdouble scale_x,
+ gdouble scale_y,
+ gdouble angle,
+ gdouble dest_x,
+ gdouble dest_y,
+ gboolean interpolate,
+ GimpTransformResize clip_result);
+GIMP_DEPRECATED_FOR(gimp_item_transform_matrix)
+gint32 gimp_drawable_transform_matrix (gint32 drawable_ID,
+ gdouble coeff_0_0,
+ gdouble coeff_0_1,
+ gdouble coeff_0_2,
+ gdouble coeff_1_0,
+ gdouble coeff_1_1,
+ gdouble coeff_1_2,
+ gdouble coeff_2_0,
+ gdouble coeff_2_1,
+ gdouble coeff_2_2,
+ GimpTransformDirection transform_direction,
+ GimpInterpolationType interpolation,
+ gboolean supersample,
+ gint recursion_level,
+ GimpTransformResize clip_result);
+GIMP_DEPRECATED_FOR(gimp_item_transform_matrix)
+gint32 gimp_drawable_transform_matrix_default (gint32 drawable_ID,
+ gdouble coeff_0_0,
+ gdouble coeff_0_1,
+ gdouble coeff_0_2,
+ gdouble coeff_1_0,
+ gdouble coeff_1_1,
+ gdouble coeff_1_2,
+ gdouble coeff_2_0,
+ gdouble coeff_2_1,
+ gdouble coeff_2_2,
+ gboolean interpolate,
+ GimpTransformResize clip_result);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_DRAWABLE_TRANSFORM_PDB_H__ */
diff --git a/libgimp/gimpdynamics_pdb.c b/libgimp/gimpdynamics_pdb.c
new file mode 100644
index 0000000..929ce35
--- /dev/null
+++ b/libgimp/gimpdynamics_pdb.c
@@ -0,0 +1,113 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpdynamics_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpdynamics
+ * @title: gimpdynamics
+ * @short_description: Operations related to paint dynamics.
+ *
+ * Operations related to paint dynamics.
+ **/
+
+
+/**
+ * gimp_dynamics_refresh:
+ *
+ * Refresh current paint dynamics. This function always succeeds.
+ *
+ * This procedure retrieves all paint dynamics currently in the user's
+ * paint dynamics path and updates the paint dynamics dialogs
+ * accordingly.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_dynamics_refresh (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-dynamics-refresh",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_dynamics_get_list:
+ * @filter: An optional regular expression used to filter the list.
+ * @num_dynamics: The number of available paint dynamics.
+ *
+ * Retrieve the list of loaded paint dynamics.
+ *
+ * This procedure returns a list of the paint dynamics that are
+ * currently available.
+ *
+ * Returns: The list of paint dynamics names. The returned value must
+ * be freed with g_strfreev().
+ *
+ * Since: 2.8
+ **/
+gchar **
+gimp_dynamics_get_list (const gchar *filter,
+ gint *num_dynamics)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar **dynamics_list = NULL;
+ gint i;
+
+ return_vals = gimp_run_procedure ("gimp-dynamics-get-list",
+ &nreturn_vals,
+ GIMP_PDB_STRING, filter,
+ GIMP_PDB_END);
+
+ *num_dynamics = 0;
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ *num_dynamics = return_vals[1].data.d_int32;
+ if (*num_dynamics > 0)
+ {
+ dynamics_list = g_new0 (gchar *, *num_dynamics + 1);
+ for (i = 0; i < *num_dynamics; i++)
+ dynamics_list[i] = g_strdup (return_vals[2].data.d_stringarray[i]);
+ }
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return dynamics_list;
+}
diff --git a/libgimp/gimpdynamics_pdb.h b/libgimp/gimpdynamics_pdb.h
new file mode 100644
index 0000000..6623ffc
--- /dev/null
+++ b/libgimp/gimpdynamics_pdb.h
@@ -0,0 +1,42 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpdynamics_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_DYNAMICS_PDB_H__
+#define __GIMP_DYNAMICS_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_dynamics_refresh (void);
+gchar** gimp_dynamics_get_list (const gchar *filter,
+ gint *num_dynamics);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_DYNAMICS_PDB_H__ */
diff --git a/libgimp/gimpedit.c b/libgimp/gimpedit.c
new file mode 100644
index 0000000..0dbf929
--- /dev/null
+++ b/libgimp/gimpedit.c
@@ -0,0 +1,67 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpedit.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * gimp_edit_paste_as_new:
+ *
+ * Paste buffer to a new image.
+ *
+ * This procedure pastes a copy of the internal GIMP edit buffer to a
+ * new image. The GIMP edit buffer will be empty unless a call was
+ * previously made to either gimp_edit_cut() or gimp_edit_copy(). This
+ * procedure returns the new image or -1 if the edit buffer was empty.
+ *
+ * Deprecated: Use gimp_edit_paste_as_new_image() instead.
+ *
+ * Returns: The new image.
+ *
+ * Since: 2.4
+ **/
+gint32
+gimp_edit_paste_as_new (void)
+{
+ return gimp_edit_paste_as_new_image ();
+}
+
+/**
+ * gimp_edit_named_paste_as_new:
+ * @buffer_name: The name of the buffer to paste.
+ *
+ * Paste named buffer to a new image.
+ *
+ * This procedure works like gimp_edit_paste_as_new_image() but pastes a
+ * named buffer instead of the global buffer.
+ *
+ * Deprecated: Use gimp_edit_named_paste_as_new_image() instead.
+ *
+ * Returns: The new image.
+ *
+ * Since: 2.4
+ **/
+gint32
+gimp_edit_named_paste_as_new (const gchar *buffer_name)
+{
+ return gimp_edit_named_paste_as_new_image (buffer_name);
+}
diff --git a/libgimp/gimpedit.h b/libgimp/gimpedit.h
new file mode 100644
index 0000000..d77a5f6
--- /dev/null
+++ b/libgimp/gimpedit.h
@@ -0,0 +1,41 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpedit.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_EDIT_H__
+#define __GIMP_EDIT_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+GIMP_DEPRECATED_FOR(gimp_edit_paste_as_new_image)
+gint32 gimp_edit_paste_as_new (void);
+
+GIMP_DEPRECATED_FOR(gimp_edit_named_paste_as_new_image)
+gint32 gimp_edit_named_paste_as_new (const gchar *buffer_name);
+
+G_END_DECLS
+
+#endif /* __GIMP_EDIT_H__ */
diff --git a/libgimp/gimpedit_pdb.c b/libgimp/gimpedit_pdb.c
new file mode 100644
index 0000000..76314a9
--- /dev/null
+++ b/libgimp/gimpedit_pdb.c
@@ -0,0 +1,781 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpedit_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpedit
+ * @title: gimpedit
+ * @short_description: Edit menu functions (cut, copy, paste, clear, etc.)
+ *
+ * Edit menu functions (cut, copy, paste, clear, etc.)
+ **/
+
+
+/**
+ * gimp_edit_cut:
+ * @drawable_ID: The drawable to cut from.
+ *
+ * Cut from the specified drawable.
+ *
+ * If there is a selection in the image, then the area specified by the
+ * selection is cut from the specified drawable and placed in an
+ * internal GIMP edit buffer. It can subsequently be retrieved using
+ * the gimp_edit_paste() command. If there is no selection, then the
+ * specified drawable will be removed and its contents stored in the
+ * internal GIMP edit buffer. This procedure will fail if the selected
+ * area lies completely outside the bounds of the current drawable and
+ * there is nothing to copy from.
+ *
+ * Returns: TRUE if the cut was successful, FALSE if there was nothing
+ * to copy from.
+ **/
+gboolean
+gimp_edit_cut (gint32 drawable_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean non_empty = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-edit-cut",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ non_empty = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return non_empty;
+}
+
+/**
+ * gimp_edit_copy:
+ * @drawable_ID: The drawable to copy from.
+ *
+ * Copy from the specified drawable.
+ *
+ * If there is a selection in the image, then the area specified by the
+ * selection is copied from the specified drawable and placed in an
+ * internal GIMP edit buffer. It can subsequently be retrieved using
+ * the gimp_edit_paste() command. If there is no selection, then the
+ * specified drawable's contents will be stored in the internal GIMP
+ * edit buffer. This procedure will fail if the selected area lies
+ * completely outside the bounds of the current drawable and there is
+ * nothing to copy from.
+ *
+ * Returns: TRUE if the cut was successful, FALSE if there was nothing
+ * to copy from.
+ **/
+gboolean
+gimp_edit_copy (gint32 drawable_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean non_empty = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-edit-copy",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ non_empty = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return non_empty;
+}
+
+/**
+ * gimp_edit_copy_visible:
+ * @image_ID: The image to copy from.
+ *
+ * Copy from the projection.
+ *
+ * If there is a selection in the image, then the area specified by the
+ * selection is copied from the projection and placed in an internal
+ * GIMP edit buffer. It can subsequently be retrieved using the
+ * gimp_edit_paste() command. If there is no selection, then the
+ * projection's contents will be stored in the internal GIMP edit
+ * buffer.
+ *
+ * Returns: TRUE if the copy was successful.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_edit_copy_visible (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean non_empty = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-edit-copy-visible",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ non_empty = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return non_empty;
+}
+
+/**
+ * gimp_edit_paste:
+ * @drawable_ID: The drawable to paste to.
+ * @paste_into: Clear selection, or paste behind it?
+ *
+ * Paste buffer to the specified drawable.
+ *
+ * This procedure pastes a copy of the internal GIMP edit buffer to the
+ * specified drawable. The GIMP edit buffer will be empty unless a call
+ * was previously made to either gimp_edit_cut() or gimp_edit_copy().
+ * The \"paste_into\" option specifies whether to clear the current
+ * image selection, or to paste the buffer \"behind\" the selection.
+ * This allows the selection to act as a mask for the pasted buffer.
+ * Anywhere that the selection mask is non-zero, the pasted buffer will
+ * show through. The pasted buffer will be a new layer in the image
+ * which is designated as the image floating selection. If the image
+ * has a floating selection at the time of pasting, the old floating
+ * selection will be anchored to its drawable before the new floating
+ * selection is added. This procedure returns the new floating layer.
+ * The resulting floating selection will already be attached to the
+ * specified drawable, and a subsequent call to floating_sel_attach is
+ * not needed.
+ *
+ * Returns: The new floating selection.
+ **/
+gint32
+gimp_edit_paste (gint32 drawable_ID,
+ gboolean paste_into)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 floating_sel_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-edit-paste",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, paste_into,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ floating_sel_ID = return_vals[1].data.d_layer;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return floating_sel_ID;
+}
+
+/**
+ * gimp_edit_paste_as_new_image:
+ *
+ * Paste buffer to a new image.
+ *
+ * This procedure pastes a copy of the internal GIMP edit buffer to a
+ * new image. The GIMP edit buffer will be empty unless a call was
+ * previously made to either gimp_edit_cut() or gimp_edit_copy(). This
+ * procedure returns the new image or -1 if the edit buffer was empty.
+ *
+ * Returns: The new image.
+ *
+ * Since: 2.10
+ **/
+gint32
+gimp_edit_paste_as_new_image (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 image_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-edit-paste-as-new-image",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ image_ID = return_vals[1].data.d_image;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return image_ID;
+}
+
+/**
+ * gimp_edit_named_cut:
+ * @drawable_ID: The drawable to cut from.
+ * @buffer_name: The name of the buffer to create.
+ *
+ * Cut into a named buffer.
+ *
+ * This procedure works like gimp_edit_cut(), but additionally stores
+ * the cut buffer into a named buffer that will stay available for
+ * later pasting, regardless of any intermediate copy or cut
+ * operations.
+ *
+ * Returns: The real name given to the buffer, or NULL if the cut
+ * failed.
+ *
+ * Since: 2.4
+ **/
+gchar *
+gimp_edit_named_cut (gint32 drawable_ID,
+ const gchar *buffer_name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *real_name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-edit-named-cut",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_STRING, buffer_name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ real_name = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return real_name;
+}
+
+/**
+ * gimp_edit_named_copy:
+ * @drawable_ID: The drawable to copy from.
+ * @buffer_name: The name of the buffer to create.
+ *
+ * Copy into a named buffer.
+ *
+ * This procedure works like gimp_edit_copy(), but additionally stores
+ * the copied buffer into a named buffer that will stay available for
+ * later pasting, regardless of any intermediate copy or cut
+ * operations.
+ *
+ * Returns: The real name given to the buffer, or NULL if the copy
+ * failed.
+ *
+ * Since: 2.4
+ **/
+gchar *
+gimp_edit_named_copy (gint32 drawable_ID,
+ const gchar *buffer_name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *real_name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-edit-named-copy",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_STRING, buffer_name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ real_name = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return real_name;
+}
+
+/**
+ * gimp_edit_named_copy_visible:
+ * @image_ID: The image to copy from.
+ * @buffer_name: The name of the buffer to create.
+ *
+ * Copy from the projection into a named buffer.
+ *
+ * This procedure works like gimp_edit_copy_visible(), but additionally
+ * stores the copied buffer into a named buffer that will stay
+ * available for later pasting, regardless of any intermediate copy or
+ * cut operations.
+ *
+ * Returns: The real name given to the buffer, or NULL if the copy
+ * failed.
+ *
+ * Since: 2.4
+ **/
+gchar *
+gimp_edit_named_copy_visible (gint32 image_ID,
+ const gchar *buffer_name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *real_name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-edit-named-copy-visible",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_STRING, buffer_name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ real_name = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return real_name;
+}
+
+/**
+ * gimp_edit_named_paste:
+ * @drawable_ID: The drawable to paste to.
+ * @buffer_name: The name of the buffer to paste.
+ * @paste_into: Clear selection, or paste behind it?
+ *
+ * Paste named buffer to the specified drawable.
+ *
+ * This procedure works like gimp_edit_paste() but pastes a named
+ * buffer instead of the global buffer.
+ *
+ * Returns: The new floating selection.
+ *
+ * Since: 2.4
+ **/
+gint32
+gimp_edit_named_paste (gint32 drawable_ID,
+ const gchar *buffer_name,
+ gboolean paste_into)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 floating_sel_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-edit-named-paste",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_STRING, buffer_name,
+ GIMP_PDB_INT32, paste_into,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ floating_sel_ID = return_vals[1].data.d_layer;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return floating_sel_ID;
+}
+
+/**
+ * gimp_edit_named_paste_as_new_image:
+ * @buffer_name: The name of the buffer to paste.
+ *
+ * Paste named buffer to a new image.
+ *
+ * This procedure works like gimp_edit_paste_as_new_image() but pastes
+ * a named buffer instead of the global buffer.
+ *
+ * Returns: The new image.
+ *
+ * Since: 2.10
+ **/
+gint32
+gimp_edit_named_paste_as_new_image (const gchar *buffer_name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 image_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-edit-named-paste-as-new-image",
+ &nreturn_vals,
+ GIMP_PDB_STRING, buffer_name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ image_ID = return_vals[1].data.d_image;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return image_ID;
+}
+
+/**
+ * gimp_edit_clear:
+ * @drawable_ID: The drawable to clear from.
+ *
+ * Clear selected area of drawable.
+ *
+ * This procedure clears the specified drawable. If the drawable has an
+ * alpha channel, the cleared pixels will become transparent. If the
+ * drawable does not have an alpha channel, cleared pixels will be set
+ * to the background color. This procedure only affects regions within
+ * a selection if there is a selection active.
+ *
+ * Deprecated: Use gimp_drawable_edit_clear() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_edit_clear (gint32 drawable_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-edit-clear",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_edit_fill:
+ * @drawable_ID: The drawable to fill to.
+ * @fill_type: The type of fill.
+ *
+ * Fill selected area of drawable.
+ *
+ * This procedure fills the specified drawable with the fill mode. If
+ * the fill mode is foreground, the current foreground color is used.
+ * If the fill mode is background, the current background color is
+ * used. Other fill modes should not be used. This procedure only
+ * affects regions within a selection if there is a selection active.
+ * If you want to fill the whole drawable, regardless of the selection,
+ * use gimp_drawable_fill().
+ *
+ * Deprecated: Use gimp_drawable_edit_fill() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_edit_fill (gint32 drawable_ID,
+ GimpFillType fill_type)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-edit-fill",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, fill_type,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_edit_bucket_fill:
+ * @drawable_ID: The affected drawable.
+ * @fill_mode: The type of fill.
+ * @paint_mode: The paint application mode.
+ * @opacity: The opacity of the final bucket fill.
+ * @threshold: The threshold determines how extensive the seed fill will be. It's value is specified in terms of intensity levels. This parameter is only valid when there is no selection in the specified image.
+ * @sample_merged: Use the composite image, not the drawable.
+ * @x: The x coordinate of this bucket fill's application. This parameter is only valid when there is no selection in the specified image.
+ * @y: The y coordinate of this bucket fill's application. This parameter is only valid when there is no selection in the specified image.
+ *
+ * Fill the area specified either by the current selection if there is
+ * one, or by a seed fill starting at the specified coordinates.
+ *
+ * This tool requires information on the paint application mode, and
+ * the fill mode, which can either be in the foreground color, or in
+ * the currently active pattern. If there is no selection, a seed fill
+ * is executed at the specified coordinates and extends outward in
+ * keeping with the threshold parameter. If there is a selection in the
+ * target image, the threshold, sample merged, x, and y arguments are
+ * unused. If the sample_merged parameter is TRUE, the data of the
+ * composite image will be used instead of that for the specified
+ * drawable. This is equivalent to sampling for colors after merging
+ * all visible layers. In the case of merged sampling, the x and y
+ * coordinates are relative to the image's origin; otherwise, they are
+ * relative to the drawable's origin.
+ *
+ * Deprecated: Use gimp_drawable_edit_bucket_fill() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_edit_bucket_fill (gint32 drawable_ID,
+ GimpBucketFillMode fill_mode,
+ GimpLayerMode paint_mode,
+ gdouble opacity,
+ gdouble threshold,
+ gboolean sample_merged,
+ gdouble x,
+ gdouble y)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-edit-bucket-fill",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, fill_mode,
+ GIMP_PDB_INT32, paint_mode,
+ GIMP_PDB_FLOAT, opacity,
+ GIMP_PDB_FLOAT, threshold,
+ GIMP_PDB_INT32, sample_merged,
+ GIMP_PDB_FLOAT, x,
+ GIMP_PDB_FLOAT, y,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_edit_bucket_fill_full:
+ * @drawable_ID: The affected drawable.
+ * @fill_mode: The type of fill.
+ * @paint_mode: The paint application mode.
+ * @opacity: The opacity of the final bucket fill.
+ * @threshold: The threshold determines how extensive the seed fill will be. It's value is specified in terms of intensity levels. This parameter is only valid when there is no selection in the specified image.
+ * @sample_merged: Use the composite image, not the drawable.
+ * @fill_transparent: Whether to consider transparent pixels for filling. If TRUE, transparency is considered as a unique fillable color.
+ * @select_criterion: The criterion used to determine color similarity. SELECT_CRITERION_COMPOSITE is the standard choice.
+ * @x: The x coordinate of this bucket fill's application. This parameter is only valid when there is no selection in the specified image.
+ * @y: The y coordinate of this bucket fill's application. This parameter is only valid when there is no selection in the specified image.
+ *
+ * Fill the area specified either by the current selection if there is
+ * one, or by a seed fill starting at the specified coordinates.
+ *
+ * This tool requires information on the paint application mode, and
+ * the fill mode, which can either be in the foreground color, or in
+ * the currently active pattern. If there is no selection, a seed fill
+ * is executed at the specified coordinates and extends outward in
+ * keeping with the threshold parameter. If there is a selection in the
+ * target image, the threshold, sample merged, x, and y arguments are
+ * unused. If the sample_merged parameter is TRUE, the data of the
+ * composite image will be used instead of that for the specified
+ * drawable. This is equivalent to sampling for colors after merging
+ * all visible layers. In the case of merged sampling, the x and y
+ * coordinates are relative to the image's origin; otherwise, they are
+ * relative to the drawable's origin.
+ *
+ * Deprecated: Use gimp_drawable_edit_bucket_fill() instead.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_edit_bucket_fill_full (gint32 drawable_ID,
+ GimpBucketFillMode fill_mode,
+ GimpLayerMode paint_mode,
+ gdouble opacity,
+ gdouble threshold,
+ gboolean sample_merged,
+ gboolean fill_transparent,
+ GimpSelectCriterion select_criterion,
+ gdouble x,
+ gdouble y)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-edit-bucket-fill-full",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, fill_mode,
+ GIMP_PDB_INT32, paint_mode,
+ GIMP_PDB_FLOAT, opacity,
+ GIMP_PDB_FLOAT, threshold,
+ GIMP_PDB_INT32, sample_merged,
+ GIMP_PDB_INT32, fill_transparent,
+ GIMP_PDB_INT32, select_criterion,
+ GIMP_PDB_FLOAT, x,
+ GIMP_PDB_FLOAT, y,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_edit_blend:
+ * @drawable_ID: The affected drawable.
+ * @blend_mode: The type of blend.
+ * @paint_mode: The paint application mode.
+ * @gradient_type: The type of gradient.
+ * @opacity: The opacity of the final blend.
+ * @offset: Offset relates to the starting and ending coordinates specified for the blend. This parameter is mode dependent.
+ * @repeat: Repeat mode.
+ * @reverse: Use the reverse gradient.
+ * @supersample: Do adaptive supersampling.
+ * @max_depth: Maximum recursion levels for supersampling.
+ * @threshold: Supersampling threshold.
+ * @dither: Use dithering to reduce banding.
+ * @x1: The x coordinate of this blend's starting point.
+ * @y1: The y coordinate of this blend's starting point.
+ * @x2: The x coordinate of this blend's ending point.
+ * @y2: The y coordinate of this blend's ending point.
+ *
+ * Blend between the starting and ending coordinates with the specified
+ * blend mode and gradient type.
+ *
+ * This tool requires information on the paint application mode, the
+ * blend mode, and the gradient type. It creates the specified variety
+ * of blend using the starting and ending coordinates as defined for
+ * each gradient type. For shapeburst gradient types, the context's
+ * distance metric is also relevant and can be updated with
+ * gimp_context_set_distance_metric().
+ *
+ * Deprecated: Use gimp_drawable_edit_gradient_fill() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_edit_blend (gint32 drawable_ID,
+ GimpBlendMode blend_mode,
+ GimpLayerMode paint_mode,
+ GimpGradientType gradient_type,
+ gdouble opacity,
+ gdouble offset,
+ GimpRepeatMode repeat,
+ gboolean reverse,
+ gboolean supersample,
+ gint max_depth,
+ gdouble threshold,
+ gboolean dither,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-edit-blend",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, blend_mode,
+ GIMP_PDB_INT32, paint_mode,
+ GIMP_PDB_INT32, gradient_type,
+ GIMP_PDB_FLOAT, opacity,
+ GIMP_PDB_FLOAT, offset,
+ GIMP_PDB_INT32, repeat,
+ GIMP_PDB_INT32, reverse,
+ GIMP_PDB_INT32, supersample,
+ GIMP_PDB_INT32, max_depth,
+ GIMP_PDB_FLOAT, threshold,
+ GIMP_PDB_INT32, dither,
+ GIMP_PDB_FLOAT, x1,
+ GIMP_PDB_FLOAT, y1,
+ GIMP_PDB_FLOAT, x2,
+ GIMP_PDB_FLOAT, y2,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_edit_stroke:
+ * @drawable_ID: The drawable to stroke to.
+ *
+ * Stroke the current selection
+ *
+ * This procedure strokes the current selection, painting along the
+ * selection boundary with the active brush and foreground color. The
+ * paint is applied to the specified drawable regardless of the active
+ * selection.
+ *
+ * Deprecated: Use gimp_drawable_edit_stroke_selection() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_edit_stroke (gint32 drawable_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-edit-stroke",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_edit_stroke_vectors:
+ * @drawable_ID: The drawable to stroke to.
+ * @vectors_ID: The vectors object.
+ *
+ * Stroke the specified vectors object
+ *
+ * This procedure strokes the specified vectors object, painting along
+ * the path with the active brush and foreground color.
+ *
+ * Deprecated: Use gimp_drawable_edit_stroke_item() instead.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_edit_stroke_vectors (gint32 drawable_ID,
+ gint32 vectors_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-edit-stroke-vectors",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_VECTORS, vectors_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimpedit_pdb.h b/libgimp/gimpedit_pdb.h
new file mode 100644
index 0000000..4a3e1b7
--- /dev/null
+++ b/libgimp/gimpedit_pdb.h
@@ -0,0 +1,102 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpedit_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_EDIT_PDB_H__
+#define __GIMP_EDIT_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_edit_cut (gint32 drawable_ID);
+gboolean gimp_edit_copy (gint32 drawable_ID);
+gboolean gimp_edit_copy_visible (gint32 image_ID);
+gint32 gimp_edit_paste (gint32 drawable_ID,
+ gboolean paste_into);
+gint32 gimp_edit_paste_as_new_image (void);
+gchar* gimp_edit_named_cut (gint32 drawable_ID,
+ const gchar *buffer_name);
+gchar* gimp_edit_named_copy (gint32 drawable_ID,
+ const gchar *buffer_name);
+gchar* gimp_edit_named_copy_visible (gint32 image_ID,
+ const gchar *buffer_name);
+gint32 gimp_edit_named_paste (gint32 drawable_ID,
+ const gchar *buffer_name,
+ gboolean paste_into);
+gint32 gimp_edit_named_paste_as_new_image (const gchar *buffer_name);
+GIMP_DEPRECATED_FOR(gimp_drawable_edit_clear)
+gboolean gimp_edit_clear (gint32 drawable_ID);
+GIMP_DEPRECATED_FOR(gimp_drawable_edit_fill)
+gboolean gimp_edit_fill (gint32 drawable_ID,
+ GimpFillType fill_type);
+GIMP_DEPRECATED_FOR(gimp_drawable_edit_bucket_fill)
+gboolean gimp_edit_bucket_fill (gint32 drawable_ID,
+ GimpBucketFillMode fill_mode,
+ GimpLayerMode paint_mode,
+ gdouble opacity,
+ gdouble threshold,
+ gboolean sample_merged,
+ gdouble x,
+ gdouble y);
+GIMP_DEPRECATED_FOR(gimp_drawable_edit_bucket_fill)
+gboolean gimp_edit_bucket_fill_full (gint32 drawable_ID,
+ GimpBucketFillMode fill_mode,
+ GimpLayerMode paint_mode,
+ gdouble opacity,
+ gdouble threshold,
+ gboolean sample_merged,
+ gboolean fill_transparent,
+ GimpSelectCriterion select_criterion,
+ gdouble x,
+ gdouble y);
+GIMP_DEPRECATED_FOR(gimp_drawable_edit_gradient_fill)
+gboolean gimp_edit_blend (gint32 drawable_ID,
+ GimpBlendMode blend_mode,
+ GimpLayerMode paint_mode,
+ GimpGradientType gradient_type,
+ gdouble opacity,
+ gdouble offset,
+ GimpRepeatMode repeat,
+ gboolean reverse,
+ gboolean supersample,
+ gint max_depth,
+ gdouble threshold,
+ gboolean dither,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2);
+GIMP_DEPRECATED_FOR(gimp_drawable_edit_stroke_selection)
+gboolean gimp_edit_stroke (gint32 drawable_ID);
+GIMP_DEPRECATED_FOR(gimp_drawable_edit_stroke_item)
+gboolean gimp_edit_stroke_vectors (gint32 drawable_ID,
+ gint32 vectors_ID);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_EDIT_PDB_H__ */
diff --git a/libgimp/gimpenums.c b/libgimp/gimpenums.c
new file mode 100644
index 0000000..a497cd8
--- /dev/null
+++ b/libgimp/gimpenums.c
@@ -0,0 +1,541 @@
+
+/* Generated data (by gimp-mkenums) */
+
+#include "config.h"
+#include <gio/gio.h>
+#include <gegl.h>
+#undef GIMP_DISABLE_DEPRECATED
+#include "libgimpbase/gimpbase.h"
+#include "libgimpbase/gimpbase-private.h"
+#include "libgimpconfig/gimpconfigenums.h"
+#include "gimpenums.h"
+
+/* enumerations from "../../libgimp/gimpenums.h" */
+GType
+gimp_brush_application_mode_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_BRUSH_HARD, "GIMP_BRUSH_HARD", "hard" },
+ { GIMP_BRUSH_SOFT, "GIMP_BRUSH_SOFT", "soft" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_BRUSH_HARD, "GIMP_BRUSH_HARD", NULL },
+ { GIMP_BRUSH_SOFT, "GIMP_BRUSH_SOFT", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpBrushApplicationMode", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "brush-application-mode");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_convert_dither_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_CONVERT_DITHER_NONE, "GIMP_CONVERT_DITHER_NONE", "none" },
+ { GIMP_CONVERT_DITHER_FS, "GIMP_CONVERT_DITHER_FS", "fs" },
+ { GIMP_CONVERT_DITHER_FS_LOWBLEED, "GIMP_CONVERT_DITHER_FS_LOWBLEED", "fs-lowbleed" },
+ { GIMP_CONVERT_DITHER_FIXED, "GIMP_CONVERT_DITHER_FIXED", "fixed" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_CONVERT_DITHER_NONE, "GIMP_CONVERT_DITHER_NONE", NULL },
+ { GIMP_CONVERT_DITHER_FS, "GIMP_CONVERT_DITHER_FS", NULL },
+ { GIMP_CONVERT_DITHER_FS_LOWBLEED, "GIMP_CONVERT_DITHER_FS_LOWBLEED", NULL },
+ { GIMP_CONVERT_DITHER_FIXED, "GIMP_CONVERT_DITHER_FIXED", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpConvertDitherType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "convert-dither-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_histogram_channel_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_HISTOGRAM_VALUE, "GIMP_HISTOGRAM_VALUE", "value" },
+ { GIMP_HISTOGRAM_RED, "GIMP_HISTOGRAM_RED", "red" },
+ { GIMP_HISTOGRAM_GREEN, "GIMP_HISTOGRAM_GREEN", "green" },
+ { GIMP_HISTOGRAM_BLUE, "GIMP_HISTOGRAM_BLUE", "blue" },
+ { GIMP_HISTOGRAM_ALPHA, "GIMP_HISTOGRAM_ALPHA", "alpha" },
+ { GIMP_HISTOGRAM_LUMINANCE, "GIMP_HISTOGRAM_LUMINANCE", "luminance" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_HISTOGRAM_VALUE, "GIMP_HISTOGRAM_VALUE", NULL },
+ { GIMP_HISTOGRAM_RED, "GIMP_HISTOGRAM_RED", NULL },
+ { GIMP_HISTOGRAM_GREEN, "GIMP_HISTOGRAM_GREEN", NULL },
+ { GIMP_HISTOGRAM_BLUE, "GIMP_HISTOGRAM_BLUE", NULL },
+ { GIMP_HISTOGRAM_ALPHA, "GIMP_HISTOGRAM_ALPHA", NULL },
+ { GIMP_HISTOGRAM_LUMINANCE, "GIMP_HISTOGRAM_LUMINANCE", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpHistogramChannel", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "histogram-channel");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_layer_color_space_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_LAYER_COLOR_SPACE_AUTO, "GIMP_LAYER_COLOR_SPACE_AUTO", "auto" },
+ { GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, "GIMP_LAYER_COLOR_SPACE_RGB_LINEAR", "rgb-linear" },
+ { GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, "GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL", "rgb-perceptual" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_LAYER_COLOR_SPACE_AUTO, "GIMP_LAYER_COLOR_SPACE_AUTO", NULL },
+ { GIMP_LAYER_COLOR_SPACE_RGB_LINEAR, "GIMP_LAYER_COLOR_SPACE_RGB_LINEAR", NULL },
+ { GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL, "GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpLayerColorSpace", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "layer-color-space");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_layer_composite_mode_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_LAYER_COMPOSITE_AUTO, "GIMP_LAYER_COMPOSITE_AUTO", "auto" },
+ { GIMP_LAYER_COMPOSITE_UNION, "GIMP_LAYER_COMPOSITE_UNION", "union" },
+ { GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, "GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP", "clip-to-backdrop" },
+ { GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER, "GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER", "clip-to-layer" },
+ { GIMP_LAYER_COMPOSITE_INTERSECTION, "GIMP_LAYER_COMPOSITE_INTERSECTION", "intersection" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_LAYER_COMPOSITE_AUTO, "GIMP_LAYER_COMPOSITE_AUTO", NULL },
+ { GIMP_LAYER_COMPOSITE_UNION, "GIMP_LAYER_COMPOSITE_UNION", NULL },
+ { GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP, "GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP", NULL },
+ { GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER, "GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER", NULL },
+ { GIMP_LAYER_COMPOSITE_INTERSECTION, "GIMP_LAYER_COMPOSITE_INTERSECTION", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpLayerCompositeMode", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "layer-composite-mode");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_layer_mode_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_LAYER_MODE_NORMAL_LEGACY, "GIMP_LAYER_MODE_NORMAL_LEGACY", "normal-legacy" },
+ { GIMP_LAYER_MODE_DISSOLVE, "GIMP_LAYER_MODE_DISSOLVE", "dissolve" },
+ { GIMP_LAYER_MODE_BEHIND_LEGACY, "GIMP_LAYER_MODE_BEHIND_LEGACY", "behind-legacy" },
+ { GIMP_LAYER_MODE_MULTIPLY_LEGACY, "GIMP_LAYER_MODE_MULTIPLY_LEGACY", "multiply-legacy" },
+ { GIMP_LAYER_MODE_SCREEN_LEGACY, "GIMP_LAYER_MODE_SCREEN_LEGACY", "screen-legacy" },
+ { GIMP_LAYER_MODE_OVERLAY_LEGACY, "GIMP_LAYER_MODE_OVERLAY_LEGACY", "overlay-legacy" },
+ { GIMP_LAYER_MODE_DIFFERENCE_LEGACY, "GIMP_LAYER_MODE_DIFFERENCE_LEGACY", "difference-legacy" },
+ { GIMP_LAYER_MODE_ADDITION_LEGACY, "GIMP_LAYER_MODE_ADDITION_LEGACY", "addition-legacy" },
+ { GIMP_LAYER_MODE_SUBTRACT_LEGACY, "GIMP_LAYER_MODE_SUBTRACT_LEGACY", "subtract-legacy" },
+ { GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY, "GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY", "darken-only-legacy" },
+ { GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY, "GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY", "lighten-only-legacy" },
+ { GIMP_LAYER_MODE_HSV_HUE_LEGACY, "GIMP_LAYER_MODE_HSV_HUE_LEGACY", "hsv-hue-legacy" },
+ { GIMP_LAYER_MODE_HSV_SATURATION_LEGACY, "GIMP_LAYER_MODE_HSV_SATURATION_LEGACY", "hsv-saturation-legacy" },
+ { GIMP_LAYER_MODE_HSL_COLOR_LEGACY, "GIMP_LAYER_MODE_HSL_COLOR_LEGACY", "hsl-color-legacy" },
+ { GIMP_LAYER_MODE_HSV_VALUE_LEGACY, "GIMP_LAYER_MODE_HSV_VALUE_LEGACY", "hsv-value-legacy" },
+ { GIMP_LAYER_MODE_DIVIDE_LEGACY, "GIMP_LAYER_MODE_DIVIDE_LEGACY", "divide-legacy" },
+ { GIMP_LAYER_MODE_DODGE_LEGACY, "GIMP_LAYER_MODE_DODGE_LEGACY", "dodge-legacy" },
+ { GIMP_LAYER_MODE_BURN_LEGACY, "GIMP_LAYER_MODE_BURN_LEGACY", "burn-legacy" },
+ { GIMP_LAYER_MODE_HARDLIGHT_LEGACY, "GIMP_LAYER_MODE_HARDLIGHT_LEGACY", "hardlight-legacy" },
+ { GIMP_LAYER_MODE_SOFTLIGHT_LEGACY, "GIMP_LAYER_MODE_SOFTLIGHT_LEGACY", "softlight-legacy" },
+ { GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY, "GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY", "grain-extract-legacy" },
+ { GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY, "GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY", "grain-merge-legacy" },
+ { GIMP_LAYER_MODE_COLOR_ERASE_LEGACY, "GIMP_LAYER_MODE_COLOR_ERASE_LEGACY", "color-erase-legacy" },
+ { GIMP_LAYER_MODE_OVERLAY, "GIMP_LAYER_MODE_OVERLAY", "overlay" },
+ { GIMP_LAYER_MODE_LCH_HUE, "GIMP_LAYER_MODE_LCH_HUE", "lch-hue" },
+ { GIMP_LAYER_MODE_LCH_CHROMA, "GIMP_LAYER_MODE_LCH_CHROMA", "lch-chroma" },
+ { GIMP_LAYER_MODE_LCH_COLOR, "GIMP_LAYER_MODE_LCH_COLOR", "lch-color" },
+ { GIMP_LAYER_MODE_LCH_LIGHTNESS, "GIMP_LAYER_MODE_LCH_LIGHTNESS", "lch-lightness" },
+ { GIMP_LAYER_MODE_NORMAL, "GIMP_LAYER_MODE_NORMAL", "normal" },
+ { GIMP_LAYER_MODE_BEHIND, "GIMP_LAYER_MODE_BEHIND", "behind" },
+ { GIMP_LAYER_MODE_MULTIPLY, "GIMP_LAYER_MODE_MULTIPLY", "multiply" },
+ { GIMP_LAYER_MODE_SCREEN, "GIMP_LAYER_MODE_SCREEN", "screen" },
+ { GIMP_LAYER_MODE_DIFFERENCE, "GIMP_LAYER_MODE_DIFFERENCE", "difference" },
+ { GIMP_LAYER_MODE_ADDITION, "GIMP_LAYER_MODE_ADDITION", "addition" },
+ { GIMP_LAYER_MODE_SUBTRACT, "GIMP_LAYER_MODE_SUBTRACT", "subtract" },
+ { GIMP_LAYER_MODE_DARKEN_ONLY, "GIMP_LAYER_MODE_DARKEN_ONLY", "darken-only" },
+ { GIMP_LAYER_MODE_LIGHTEN_ONLY, "GIMP_LAYER_MODE_LIGHTEN_ONLY", "lighten-only" },
+ { GIMP_LAYER_MODE_HSV_HUE, "GIMP_LAYER_MODE_HSV_HUE", "hsv-hue" },
+ { GIMP_LAYER_MODE_HSV_SATURATION, "GIMP_LAYER_MODE_HSV_SATURATION", "hsv-saturation" },
+ { GIMP_LAYER_MODE_HSL_COLOR, "GIMP_LAYER_MODE_HSL_COLOR", "hsl-color" },
+ { GIMP_LAYER_MODE_HSV_VALUE, "GIMP_LAYER_MODE_HSV_VALUE", "hsv-value" },
+ { GIMP_LAYER_MODE_DIVIDE, "GIMP_LAYER_MODE_DIVIDE", "divide" },
+ { GIMP_LAYER_MODE_DODGE, "GIMP_LAYER_MODE_DODGE", "dodge" },
+ { GIMP_LAYER_MODE_BURN, "GIMP_LAYER_MODE_BURN", "burn" },
+ { GIMP_LAYER_MODE_HARDLIGHT, "GIMP_LAYER_MODE_HARDLIGHT", "hardlight" },
+ { GIMP_LAYER_MODE_SOFTLIGHT, "GIMP_LAYER_MODE_SOFTLIGHT", "softlight" },
+ { GIMP_LAYER_MODE_GRAIN_EXTRACT, "GIMP_LAYER_MODE_GRAIN_EXTRACT", "grain-extract" },
+ { GIMP_LAYER_MODE_GRAIN_MERGE, "GIMP_LAYER_MODE_GRAIN_MERGE", "grain-merge" },
+ { GIMP_LAYER_MODE_VIVID_LIGHT, "GIMP_LAYER_MODE_VIVID_LIGHT", "vivid-light" },
+ { GIMP_LAYER_MODE_PIN_LIGHT, "GIMP_LAYER_MODE_PIN_LIGHT", "pin-light" },
+ { GIMP_LAYER_MODE_LINEAR_LIGHT, "GIMP_LAYER_MODE_LINEAR_LIGHT", "linear-light" },
+ { GIMP_LAYER_MODE_HARD_MIX, "GIMP_LAYER_MODE_HARD_MIX", "hard-mix" },
+ { GIMP_LAYER_MODE_EXCLUSION, "GIMP_LAYER_MODE_EXCLUSION", "exclusion" },
+ { GIMP_LAYER_MODE_LINEAR_BURN, "GIMP_LAYER_MODE_LINEAR_BURN", "linear-burn" },
+ { GIMP_LAYER_MODE_LUMA_DARKEN_ONLY, "GIMP_LAYER_MODE_LUMA_DARKEN_ONLY", "luma-darken-only" },
+ { GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY, "GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY", "luma-lighten-only" },
+ { GIMP_LAYER_MODE_LUMINANCE, "GIMP_LAYER_MODE_LUMINANCE", "luminance" },
+ { GIMP_LAYER_MODE_COLOR_ERASE, "GIMP_LAYER_MODE_COLOR_ERASE", "color-erase" },
+ { GIMP_LAYER_MODE_ERASE, "GIMP_LAYER_MODE_ERASE", "erase" },
+ { GIMP_LAYER_MODE_MERGE, "GIMP_LAYER_MODE_MERGE", "merge" },
+ { GIMP_LAYER_MODE_SPLIT, "GIMP_LAYER_MODE_SPLIT", "split" },
+ { GIMP_LAYER_MODE_PASS_THROUGH, "GIMP_LAYER_MODE_PASS_THROUGH", "pass-through" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_LAYER_MODE_NORMAL_LEGACY, "GIMP_LAYER_MODE_NORMAL_LEGACY", NULL },
+ { GIMP_LAYER_MODE_DISSOLVE, "GIMP_LAYER_MODE_DISSOLVE", NULL },
+ { GIMP_LAYER_MODE_BEHIND_LEGACY, "GIMP_LAYER_MODE_BEHIND_LEGACY", NULL },
+ { GIMP_LAYER_MODE_MULTIPLY_LEGACY, "GIMP_LAYER_MODE_MULTIPLY_LEGACY", NULL },
+ { GIMP_LAYER_MODE_SCREEN_LEGACY, "GIMP_LAYER_MODE_SCREEN_LEGACY", NULL },
+ { GIMP_LAYER_MODE_OVERLAY_LEGACY, "GIMP_LAYER_MODE_OVERLAY_LEGACY", NULL },
+ { GIMP_LAYER_MODE_DIFFERENCE_LEGACY, "GIMP_LAYER_MODE_DIFFERENCE_LEGACY", NULL },
+ { GIMP_LAYER_MODE_ADDITION_LEGACY, "GIMP_LAYER_MODE_ADDITION_LEGACY", NULL },
+ { GIMP_LAYER_MODE_SUBTRACT_LEGACY, "GIMP_LAYER_MODE_SUBTRACT_LEGACY", NULL },
+ { GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY, "GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY", NULL },
+ { GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY, "GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY", NULL },
+ { GIMP_LAYER_MODE_HSV_HUE_LEGACY, "GIMP_LAYER_MODE_HSV_HUE_LEGACY", NULL },
+ { GIMP_LAYER_MODE_HSV_SATURATION_LEGACY, "GIMP_LAYER_MODE_HSV_SATURATION_LEGACY", NULL },
+ { GIMP_LAYER_MODE_HSL_COLOR_LEGACY, "GIMP_LAYER_MODE_HSL_COLOR_LEGACY", NULL },
+ { GIMP_LAYER_MODE_HSV_VALUE_LEGACY, "GIMP_LAYER_MODE_HSV_VALUE_LEGACY", NULL },
+ { GIMP_LAYER_MODE_DIVIDE_LEGACY, "GIMP_LAYER_MODE_DIVIDE_LEGACY", NULL },
+ { GIMP_LAYER_MODE_DODGE_LEGACY, "GIMP_LAYER_MODE_DODGE_LEGACY", NULL },
+ { GIMP_LAYER_MODE_BURN_LEGACY, "GIMP_LAYER_MODE_BURN_LEGACY", NULL },
+ { GIMP_LAYER_MODE_HARDLIGHT_LEGACY, "GIMP_LAYER_MODE_HARDLIGHT_LEGACY", NULL },
+ { GIMP_LAYER_MODE_SOFTLIGHT_LEGACY, "GIMP_LAYER_MODE_SOFTLIGHT_LEGACY", NULL },
+ { GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY, "GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY", NULL },
+ { GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY, "GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY", NULL },
+ { GIMP_LAYER_MODE_COLOR_ERASE_LEGACY, "GIMP_LAYER_MODE_COLOR_ERASE_LEGACY", NULL },
+ { GIMP_LAYER_MODE_OVERLAY, "GIMP_LAYER_MODE_OVERLAY", NULL },
+ { GIMP_LAYER_MODE_LCH_HUE, "GIMP_LAYER_MODE_LCH_HUE", NULL },
+ { GIMP_LAYER_MODE_LCH_CHROMA, "GIMP_LAYER_MODE_LCH_CHROMA", NULL },
+ { GIMP_LAYER_MODE_LCH_COLOR, "GIMP_LAYER_MODE_LCH_COLOR", NULL },
+ { GIMP_LAYER_MODE_LCH_LIGHTNESS, "GIMP_LAYER_MODE_LCH_LIGHTNESS", NULL },
+ { GIMP_LAYER_MODE_NORMAL, "GIMP_LAYER_MODE_NORMAL", NULL },
+ { GIMP_LAYER_MODE_BEHIND, "GIMP_LAYER_MODE_BEHIND", NULL },
+ { GIMP_LAYER_MODE_MULTIPLY, "GIMP_LAYER_MODE_MULTIPLY", NULL },
+ { GIMP_LAYER_MODE_SCREEN, "GIMP_LAYER_MODE_SCREEN", NULL },
+ { GIMP_LAYER_MODE_DIFFERENCE, "GIMP_LAYER_MODE_DIFFERENCE", NULL },
+ { GIMP_LAYER_MODE_ADDITION, "GIMP_LAYER_MODE_ADDITION", NULL },
+ { GIMP_LAYER_MODE_SUBTRACT, "GIMP_LAYER_MODE_SUBTRACT", NULL },
+ { GIMP_LAYER_MODE_DARKEN_ONLY, "GIMP_LAYER_MODE_DARKEN_ONLY", NULL },
+ { GIMP_LAYER_MODE_LIGHTEN_ONLY, "GIMP_LAYER_MODE_LIGHTEN_ONLY", NULL },
+ { GIMP_LAYER_MODE_HSV_HUE, "GIMP_LAYER_MODE_HSV_HUE", NULL },
+ { GIMP_LAYER_MODE_HSV_SATURATION, "GIMP_LAYER_MODE_HSV_SATURATION", NULL },
+ { GIMP_LAYER_MODE_HSL_COLOR, "GIMP_LAYER_MODE_HSL_COLOR", NULL },
+ { GIMP_LAYER_MODE_HSV_VALUE, "GIMP_LAYER_MODE_HSV_VALUE", NULL },
+ { GIMP_LAYER_MODE_DIVIDE, "GIMP_LAYER_MODE_DIVIDE", NULL },
+ { GIMP_LAYER_MODE_DODGE, "GIMP_LAYER_MODE_DODGE", NULL },
+ { GIMP_LAYER_MODE_BURN, "GIMP_LAYER_MODE_BURN", NULL },
+ { GIMP_LAYER_MODE_HARDLIGHT, "GIMP_LAYER_MODE_HARDLIGHT", NULL },
+ { GIMP_LAYER_MODE_SOFTLIGHT, "GIMP_LAYER_MODE_SOFTLIGHT", NULL },
+ { GIMP_LAYER_MODE_GRAIN_EXTRACT, "GIMP_LAYER_MODE_GRAIN_EXTRACT", NULL },
+ { GIMP_LAYER_MODE_GRAIN_MERGE, "GIMP_LAYER_MODE_GRAIN_MERGE", NULL },
+ { GIMP_LAYER_MODE_VIVID_LIGHT, "GIMP_LAYER_MODE_VIVID_LIGHT", NULL },
+ { GIMP_LAYER_MODE_PIN_LIGHT, "GIMP_LAYER_MODE_PIN_LIGHT", NULL },
+ { GIMP_LAYER_MODE_LINEAR_LIGHT, "GIMP_LAYER_MODE_LINEAR_LIGHT", NULL },
+ { GIMP_LAYER_MODE_HARD_MIX, "GIMP_LAYER_MODE_HARD_MIX", NULL },
+ { GIMP_LAYER_MODE_EXCLUSION, "GIMP_LAYER_MODE_EXCLUSION", NULL },
+ { GIMP_LAYER_MODE_LINEAR_BURN, "GIMP_LAYER_MODE_LINEAR_BURN", NULL },
+ { GIMP_LAYER_MODE_LUMA_DARKEN_ONLY, "GIMP_LAYER_MODE_LUMA_DARKEN_ONLY", NULL },
+ { GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY, "GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY", NULL },
+ { GIMP_LAYER_MODE_LUMINANCE, "GIMP_LAYER_MODE_LUMINANCE", NULL },
+ { GIMP_LAYER_MODE_COLOR_ERASE, "GIMP_LAYER_MODE_COLOR_ERASE", NULL },
+ { GIMP_LAYER_MODE_ERASE, "GIMP_LAYER_MODE_ERASE", NULL },
+ { GIMP_LAYER_MODE_MERGE, "GIMP_LAYER_MODE_MERGE", NULL },
+ { GIMP_LAYER_MODE_SPLIT, "GIMP_LAYER_MODE_SPLIT", NULL },
+ { GIMP_LAYER_MODE_PASS_THROUGH, "GIMP_LAYER_MODE_PASS_THROUGH", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpLayerMode", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "layer-mode");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+
+/* Generated data ends here */
+
+
+typedef GType (* GimpGetTypeFunc) (void);
+
+static const GimpGetTypeFunc get_type_funcs[] =
+{
+ gegl_distance_metric_get_type,
+ gimp_add_mask_type_get_type,
+ gimp_blend_mode_get_type,
+ gimp_brush_application_mode_get_type,
+ gimp_brush_generated_shape_get_type,
+ gimp_bucket_fill_mode_get_type,
+ gimp_cap_style_get_type,
+ gimp_channel_ops_get_type,
+ gimp_channel_type_get_type,
+ gimp_clone_type_get_type,
+ gimp_color_management_mode_get_type,
+ gimp_color_rendering_intent_get_type,
+ gimp_color_tag_get_type,
+ gimp_component_type_get_type,
+ gimp_convert_dither_type_get_type,
+ gimp_convert_palette_type_get_type,
+ gimp_convolve_type_get_type,
+ gimp_desaturate_mode_get_type,
+ gimp_dodge_burn_type_get_type,
+ gimp_fill_type_get_type,
+ gimp_foreground_extract_mode_get_type,
+ gimp_gradient_blend_color_space_get_type,
+ gimp_gradient_segment_color_get_type,
+ gimp_gradient_segment_type_get_type,
+ gimp_gradient_type_get_type,
+ gimp_grid_style_get_type,
+ gimp_histogram_channel_get_type,
+ gimp_hue_range_get_type,
+ gimp_icon_type_get_type,
+ gimp_image_base_type_get_type,
+ gimp_image_type_get_type,
+ gimp_ink_blob_type_get_type,
+ gimp_interpolation_type_get_type,
+ gimp_join_style_get_type,
+ gimp_layer_color_space_get_type,
+ gimp_layer_composite_mode_get_type,
+ gimp_layer_mode_get_type,
+ gimp_mask_apply_mode_get_type,
+ gimp_merge_type_get_type,
+ gimp_message_handler_type_get_type,
+ gimp_offset_type_get_type,
+ gimp_orientation_type_get_type,
+ gimp_pdb_arg_type_get_type,
+ gimp_pdb_error_handler_get_type,
+ gimp_pdb_proc_type_get_type,
+ gimp_pdb_status_type_get_type,
+ gimp_paint_application_mode_get_type,
+ gimp_precision_get_type,
+ gimp_progress_command_get_type,
+ gimp_repeat_mode_get_type,
+ gimp_rotation_type_get_type,
+ gimp_run_mode_get_type,
+ gimp_select_criterion_get_type,
+ gimp_size_type_get_type,
+ gimp_stack_trace_mode_get_type,
+ gimp_stroke_method_get_type,
+ gimp_text_direction_get_type,
+ gimp_text_hint_style_get_type,
+ gimp_text_justification_get_type,
+ gimp_transfer_mode_get_type,
+ gimp_transform_direction_get_type,
+ gimp_transform_resize_get_type,
+ gimp_user_directory_get_type,
+ gimp_vectors_stroke_type_get_type
+};
+
+static const gchar * const type_names[] =
+{
+ "GeglDistanceMetric",
+ "GimpAddMaskType",
+ "GimpBlendMode",
+ "GimpBrushApplicationMode",
+ "GimpBrushGeneratedShape",
+ "GimpBucketFillMode",
+ "GimpCapStyle",
+ "GimpChannelOps",
+ "GimpChannelType",
+ "GimpCloneType",
+ "GimpColorManagementMode",
+ "GimpColorRenderingIntent",
+ "GimpColorTag",
+ "GimpComponentType",
+ "GimpConvertDitherType",
+ "GimpConvertPaletteType",
+ "GimpConvolveType",
+ "GimpDesaturateMode",
+ "GimpDodgeBurnType",
+ "GimpFillType",
+ "GimpForegroundExtractMode",
+ "GimpGradientBlendColorSpace",
+ "GimpGradientSegmentColor",
+ "GimpGradientSegmentType",
+ "GimpGradientType",
+ "GimpGridStyle",
+ "GimpHistogramChannel",
+ "GimpHueRange",
+ "GimpIconType",
+ "GimpImageBaseType",
+ "GimpImageType",
+ "GimpInkBlobType",
+ "GimpInterpolationType",
+ "GimpJoinStyle",
+ "GimpLayerColorSpace",
+ "GimpLayerCompositeMode",
+ "GimpLayerMode",
+ "GimpMaskApplyMode",
+ "GimpMergeType",
+ "GimpMessageHandlerType",
+ "GimpOffsetType",
+ "GimpOrientationType",
+ "GimpPDBArgType",
+ "GimpPDBErrorHandler",
+ "GimpPDBProcType",
+ "GimpPDBStatusType",
+ "GimpPaintApplicationMode",
+ "GimpPrecision",
+ "GimpProgressCommand",
+ "GimpRepeatMode",
+ "GimpRotationType",
+ "GimpRunMode",
+ "GimpSelectCriterion",
+ "GimpSizeType",
+ "GimpStackTraceMode",
+ "GimpStrokeMethod",
+ "GimpTextDirection",
+ "GimpTextHintStyle",
+ "GimpTextJustification",
+ "GimpTransferMode",
+ "GimpTransformDirection",
+ "GimpTransformResize",
+ "GimpUserDirectory",
+ "GimpVectorsStrokeType"
+};
+
+static gboolean enums_initialized = FALSE;
+
+GType gimp_convert_dither_type_compat_get_type (void);
+GType gimp_layer_mode_effects_get_type (void);
+
+/**
+ * gimp_enums_init:
+ *
+ * This function makes sure all the enum types are registered
+ * with the #GType system. This is intended for use by language
+ * bindings that need the symbols early, before gimp_main is run.
+ * It's not necessary for plug-ins to call this directly, because
+ * the normal plug-in initialization code will handle it implicitly.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_enums_init (void)
+{
+ const GimpGetTypeFunc *funcs = get_type_funcs;
+ GQuark quark;
+ gint i;
+
+ if (enums_initialized)
+ return;
+
+ for (i = 0; i < G_N_ELEMENTS (get_type_funcs); i++, funcs++)
+ {
+ GType type = (*funcs) ();
+
+ g_type_class_ref (type);
+ }
+
+ /* keep compat enum code in sync with app/app.c (app_libs_init) */
+ quark = g_quark_from_static_string ("gimp-compat-enum");
+
+ g_type_set_qdata (GIMP_TYPE_CONVERT_DITHER_TYPE, quark,
+ (gpointer) gimp_convert_dither_type_compat_get_type ());
+ g_type_set_qdata (GIMP_TYPE_LAYER_MODE, quark,
+ (gpointer) gimp_layer_mode_effects_get_type ());
+
+ gimp_base_compat_enums_init ();
+
+ enums_initialized = TRUE;
+}
+
+/**
+ * gimp_enums_get_type_names:
+ * @n_type_names: return location for the number of names
+ *
+ * This function gives access to the list of enums registered by libgimp.
+ * The returned array is static and must not be modified.
+ *
+ * Return value: an array with type names
+ *
+ * Since: 2.2
+ **/
+const gchar **
+gimp_enums_get_type_names (gint *n_type_names)
+{
+ g_return_val_if_fail (n_type_names != NULL, NULL);
+
+ *n_type_names = G_N_ELEMENTS (type_names);
+
+ return (const gchar **) type_names;
+}
diff --git a/libgimp/gimpenums.c.tail b/libgimp/gimpenums.c.tail
new file mode 100644
index 0000000..e8821e2
--- /dev/null
+++ b/libgimp/gimpenums.c.tail
@@ -0,0 +1,205 @@
+
+typedef GType (* GimpGetTypeFunc) (void);
+
+static const GimpGetTypeFunc get_type_funcs[] =
+{
+ gegl_distance_metric_get_type,
+ gimp_add_mask_type_get_type,
+ gimp_blend_mode_get_type,
+ gimp_brush_application_mode_get_type,
+ gimp_brush_generated_shape_get_type,
+ gimp_bucket_fill_mode_get_type,
+ gimp_cap_style_get_type,
+ gimp_channel_ops_get_type,
+ gimp_channel_type_get_type,
+ gimp_clone_type_get_type,
+ gimp_color_management_mode_get_type,
+ gimp_color_rendering_intent_get_type,
+ gimp_color_tag_get_type,
+ gimp_component_type_get_type,
+ gimp_convert_dither_type_get_type,
+ gimp_convert_palette_type_get_type,
+ gimp_convolve_type_get_type,
+ gimp_desaturate_mode_get_type,
+ gimp_dodge_burn_type_get_type,
+ gimp_fill_type_get_type,
+ gimp_foreground_extract_mode_get_type,
+ gimp_gradient_blend_color_space_get_type,
+ gimp_gradient_segment_color_get_type,
+ gimp_gradient_segment_type_get_type,
+ gimp_gradient_type_get_type,
+ gimp_grid_style_get_type,
+ gimp_histogram_channel_get_type,
+ gimp_hue_range_get_type,
+ gimp_icon_type_get_type,
+ gimp_image_base_type_get_type,
+ gimp_image_type_get_type,
+ gimp_ink_blob_type_get_type,
+ gimp_interpolation_type_get_type,
+ gimp_join_style_get_type,
+ gimp_layer_color_space_get_type,
+ gimp_layer_composite_mode_get_type,
+ gimp_layer_mode_get_type,
+ gimp_mask_apply_mode_get_type,
+ gimp_merge_type_get_type,
+ gimp_message_handler_type_get_type,
+ gimp_offset_type_get_type,
+ gimp_orientation_type_get_type,
+ gimp_pdb_arg_type_get_type,
+ gimp_pdb_error_handler_get_type,
+ gimp_pdb_proc_type_get_type,
+ gimp_pdb_status_type_get_type,
+ gimp_paint_application_mode_get_type,
+ gimp_precision_get_type,
+ gimp_progress_command_get_type,
+ gimp_repeat_mode_get_type,
+ gimp_rotation_type_get_type,
+ gimp_run_mode_get_type,
+ gimp_select_criterion_get_type,
+ gimp_size_type_get_type,
+ gimp_stack_trace_mode_get_type,
+ gimp_stroke_method_get_type,
+ gimp_text_direction_get_type,
+ gimp_text_hint_style_get_type,
+ gimp_text_justification_get_type,
+ gimp_transfer_mode_get_type,
+ gimp_transform_direction_get_type,
+ gimp_transform_resize_get_type,
+ gimp_user_directory_get_type,
+ gimp_vectors_stroke_type_get_type
+};
+
+static const gchar * const type_names[] =
+{
+ "GeglDistanceMetric",
+ "GimpAddMaskType",
+ "GimpBlendMode",
+ "GimpBrushApplicationMode",
+ "GimpBrushGeneratedShape",
+ "GimpBucketFillMode",
+ "GimpCapStyle",
+ "GimpChannelOps",
+ "GimpChannelType",
+ "GimpCloneType",
+ "GimpColorManagementMode",
+ "GimpColorRenderingIntent",
+ "GimpColorTag",
+ "GimpComponentType",
+ "GimpConvertDitherType",
+ "GimpConvertPaletteType",
+ "GimpConvolveType",
+ "GimpDesaturateMode",
+ "GimpDodgeBurnType",
+ "GimpFillType",
+ "GimpForegroundExtractMode",
+ "GimpGradientBlendColorSpace",
+ "GimpGradientSegmentColor",
+ "GimpGradientSegmentType",
+ "GimpGradientType",
+ "GimpGridStyle",
+ "GimpHistogramChannel",
+ "GimpHueRange",
+ "GimpIconType",
+ "GimpImageBaseType",
+ "GimpImageType",
+ "GimpInkBlobType",
+ "GimpInterpolationType",
+ "GimpJoinStyle",
+ "GimpLayerColorSpace",
+ "GimpLayerCompositeMode",
+ "GimpLayerMode",
+ "GimpMaskApplyMode",
+ "GimpMergeType",
+ "GimpMessageHandlerType",
+ "GimpOffsetType",
+ "GimpOrientationType",
+ "GimpPDBArgType",
+ "GimpPDBErrorHandler",
+ "GimpPDBProcType",
+ "GimpPDBStatusType",
+ "GimpPaintApplicationMode",
+ "GimpPrecision",
+ "GimpProgressCommand",
+ "GimpRepeatMode",
+ "GimpRotationType",
+ "GimpRunMode",
+ "GimpSelectCriterion",
+ "GimpSizeType",
+ "GimpStackTraceMode",
+ "GimpStrokeMethod",
+ "GimpTextDirection",
+ "GimpTextHintStyle",
+ "GimpTextJustification",
+ "GimpTransferMode",
+ "GimpTransformDirection",
+ "GimpTransformResize",
+ "GimpUserDirectory",
+ "GimpVectorsStrokeType"
+};
+
+static gboolean enums_initialized = FALSE;
+
+GType gimp_convert_dither_type_compat_get_type (void);
+GType gimp_layer_mode_effects_get_type (void);
+
+/**
+ * gimp_enums_init:
+ *
+ * This function makes sure all the enum types are registered
+ * with the #GType system. This is intended for use by language
+ * bindings that need the symbols early, before gimp_main is run.
+ * It's not necessary for plug-ins to call this directly, because
+ * the normal plug-in initialization code will handle it implicitly.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_enums_init (void)
+{
+ const GimpGetTypeFunc *funcs = get_type_funcs;
+ GQuark quark;
+ gint i;
+
+ if (enums_initialized)
+ return;
+
+ for (i = 0; i < G_N_ELEMENTS (get_type_funcs); i++, funcs++)
+ {
+ GType type = (*funcs) ();
+
+ g_type_class_ref (type);
+ }
+
+ /* keep compat enum code in sync with app/app.c (app_libs_init) */
+ quark = g_quark_from_static_string ("gimp-compat-enum");
+
+ g_type_set_qdata (GIMP_TYPE_CONVERT_DITHER_TYPE, quark,
+ (gpointer) gimp_convert_dither_type_compat_get_type ());
+ g_type_set_qdata (GIMP_TYPE_LAYER_MODE, quark,
+ (gpointer) gimp_layer_mode_effects_get_type ());
+
+ gimp_base_compat_enums_init ();
+
+ enums_initialized = TRUE;
+}
+
+/**
+ * gimp_enums_get_type_names:
+ * @n_type_names: return location for the number of names
+ *
+ * This function gives access to the list of enums registered by libgimp.
+ * The returned array is static and must not be modified.
+ *
+ * Return value: an array with type names
+ *
+ * Since: 2.2
+ **/
+const gchar **
+gimp_enums_get_type_names (gint *n_type_names)
+{
+ g_return_val_if_fail (n_type_names != NULL, NULL);
+
+ *n_type_names = G_N_ELEMENTS (type_names);
+
+ return (const gchar **) type_names;
+}
diff --git a/libgimp/gimpenums.h b/libgimp/gimpenums.h
new file mode 100644
index 0000000..6265ee8
--- /dev/null
+++ b/libgimp/gimpenums.h
@@ -0,0 +1,170 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is autogenerated by enumcode.pl */
+
+#ifndef __GIMP_ENUMS_H__
+#define __GIMP_ENUMS_H__
+
+G_BEGIN_DECLS
+
+
+#define GIMP_TYPE_BRUSH_APPLICATION_MODE (gimp_brush_application_mode_get_type ())
+
+GType gimp_brush_application_mode_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_BRUSH_HARD,
+ GIMP_BRUSH_SOFT
+} GimpBrushApplicationMode;
+
+
+#define GIMP_TYPE_CONVERT_DITHER_TYPE (gimp_convert_dither_type_get_type ())
+
+GType gimp_convert_dither_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_CONVERT_DITHER_NONE,
+ GIMP_CONVERT_DITHER_FS,
+ GIMP_CONVERT_DITHER_FS_LOWBLEED,
+ GIMP_CONVERT_DITHER_FIXED
+} GimpConvertDitherType;
+
+
+#define GIMP_TYPE_HISTOGRAM_CHANNEL (gimp_histogram_channel_get_type ())
+
+GType gimp_histogram_channel_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_HISTOGRAM_VALUE,
+ GIMP_HISTOGRAM_RED,
+ GIMP_HISTOGRAM_GREEN,
+ GIMP_HISTOGRAM_BLUE,
+ GIMP_HISTOGRAM_ALPHA,
+ GIMP_HISTOGRAM_LUMINANCE
+} GimpHistogramChannel;
+
+
+#define GIMP_TYPE_LAYER_COLOR_SPACE (gimp_layer_color_space_get_type ())
+
+GType gimp_layer_color_space_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_LAYER_COLOR_SPACE_AUTO,
+ GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+ GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL
+} GimpLayerColorSpace;
+
+
+#define GIMP_TYPE_LAYER_COMPOSITE_MODE (gimp_layer_composite_mode_get_type ())
+
+GType gimp_layer_composite_mode_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_LAYER_COMPOSITE_AUTO,
+ GIMP_LAYER_COMPOSITE_UNION,
+ GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP,
+ GIMP_LAYER_COMPOSITE_CLIP_TO_LAYER,
+ GIMP_LAYER_COMPOSITE_INTERSECTION
+} GimpLayerCompositeMode;
+
+
+#define GIMP_TYPE_LAYER_MODE (gimp_layer_mode_get_type ())
+
+GType gimp_layer_mode_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_LAYER_MODE_NORMAL_LEGACY,
+ GIMP_LAYER_MODE_DISSOLVE,
+ GIMP_LAYER_MODE_BEHIND_LEGACY,
+ GIMP_LAYER_MODE_MULTIPLY_LEGACY,
+ GIMP_LAYER_MODE_SCREEN_LEGACY,
+ GIMP_LAYER_MODE_OVERLAY_LEGACY,
+ GIMP_LAYER_MODE_DIFFERENCE_LEGACY,
+ GIMP_LAYER_MODE_ADDITION_LEGACY,
+ GIMP_LAYER_MODE_SUBTRACT_LEGACY,
+ GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY,
+ GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY,
+ GIMP_LAYER_MODE_HSV_HUE_LEGACY,
+ GIMP_LAYER_MODE_HSV_SATURATION_LEGACY,
+ GIMP_LAYER_MODE_HSL_COLOR_LEGACY,
+ GIMP_LAYER_MODE_HSV_VALUE_LEGACY,
+ GIMP_LAYER_MODE_DIVIDE_LEGACY,
+ GIMP_LAYER_MODE_DODGE_LEGACY,
+ GIMP_LAYER_MODE_BURN_LEGACY,
+ GIMP_LAYER_MODE_HARDLIGHT_LEGACY,
+ GIMP_LAYER_MODE_SOFTLIGHT_LEGACY,
+ GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY,
+ GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY,
+ GIMP_LAYER_MODE_COLOR_ERASE_LEGACY,
+ GIMP_LAYER_MODE_OVERLAY,
+ GIMP_LAYER_MODE_LCH_HUE,
+ GIMP_LAYER_MODE_LCH_CHROMA,
+ GIMP_LAYER_MODE_LCH_COLOR,
+ GIMP_LAYER_MODE_LCH_LIGHTNESS,
+ GIMP_LAYER_MODE_NORMAL,
+ GIMP_LAYER_MODE_BEHIND,
+ GIMP_LAYER_MODE_MULTIPLY,
+ GIMP_LAYER_MODE_SCREEN,
+ GIMP_LAYER_MODE_DIFFERENCE,
+ GIMP_LAYER_MODE_ADDITION,
+ GIMP_LAYER_MODE_SUBTRACT,
+ GIMP_LAYER_MODE_DARKEN_ONLY,
+ GIMP_LAYER_MODE_LIGHTEN_ONLY,
+ GIMP_LAYER_MODE_HSV_HUE,
+ GIMP_LAYER_MODE_HSV_SATURATION,
+ GIMP_LAYER_MODE_HSL_COLOR,
+ GIMP_LAYER_MODE_HSV_VALUE,
+ GIMP_LAYER_MODE_DIVIDE,
+ GIMP_LAYER_MODE_DODGE,
+ GIMP_LAYER_MODE_BURN,
+ GIMP_LAYER_MODE_HARDLIGHT,
+ GIMP_LAYER_MODE_SOFTLIGHT,
+ GIMP_LAYER_MODE_GRAIN_EXTRACT,
+ GIMP_LAYER_MODE_GRAIN_MERGE,
+ GIMP_LAYER_MODE_VIVID_LIGHT,
+ GIMP_LAYER_MODE_PIN_LIGHT,
+ GIMP_LAYER_MODE_LINEAR_LIGHT,
+ GIMP_LAYER_MODE_HARD_MIX,
+ GIMP_LAYER_MODE_EXCLUSION,
+ GIMP_LAYER_MODE_LINEAR_BURN,
+ GIMP_LAYER_MODE_LUMA_DARKEN_ONLY,
+ GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY,
+ GIMP_LAYER_MODE_LUMINANCE,
+ GIMP_LAYER_MODE_COLOR_ERASE,
+ GIMP_LAYER_MODE_ERASE,
+ GIMP_LAYER_MODE_MERGE,
+ GIMP_LAYER_MODE_SPLIT,
+ GIMP_LAYER_MODE_PASS_THROUGH
+} GimpLayerMode;
+
+
+void gimp_enums_init (void);
+
+const gchar ** gimp_enums_get_type_names (gint *n_type_names);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_ENUMS_H__ */
diff --git a/libgimp/gimpexport.c b/libgimp/gimpexport.c
new file mode 100644
index 0000000..e5621da
--- /dev/null
+++ b/libgimp/gimpexport.c
@@ -0,0 +1,1157 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpexport.c
+ * Copyright (C) 1999-2004 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "gimp.h"
+#include "gimpui.h"
+
+#include "libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpexport
+ * @title: gimpexport
+ * @short_description: Export an image before it is saved.
+ *
+ * This function should be called by all save_plugins unless they are
+ * able to save all image formats GIMP knows about. It takes care of
+ * asking the user if she wishes to export the image to a format the
+ * save_plugin can handle. It then performs the necessary conversions
+ * (e.g. Flatten) on a copy of the image so that the image can be
+ * saved without changing the original image.
+ *
+ * The capabilities of the save_plugin are specified by combining
+ * #GimpExportCapabilities using a bitwise OR.
+ *
+ * Make sure you have initialized GTK+ before you call this function
+ * as it will most probably have to open a dialog.
+ **/
+
+
+typedef void (* ExportFunc) (gint32 imageID,
+ gint32 *drawable_ID);
+
+
+/* the export action structure */
+typedef struct
+{
+ ExportFunc default_action;
+ ExportFunc alt_action;
+ const gchar *reason;
+ const gchar *possibilities[2];
+ gint choice;
+} ExportAction;
+
+
+/* the functions that do the actual export */
+
+static void
+export_merge (gint32 image_ID,
+ gint32 *drawable_ID)
+{
+ gint32 nlayers;
+ gint32 nvisible = 0;
+ gint32 i;
+ gint32 *layers;
+ gint32 merged;
+ gint32 transp;
+
+ layers = gimp_image_get_layers (image_ID, &nlayers);
+ for (i = 0; i < nlayers; i++)
+ {
+ if (gimp_item_get_visible (layers[i]))
+ nvisible++;
+ }
+
+ if (nvisible <= 1)
+ {
+ /* if there is only one (or zero) visible layer, add a new
+ * transparent layer that has the same size as the canvas. The
+ * merge that follows will ensure that the offset, opacity and
+ * size are correct
+ */
+ transp = gimp_layer_new (image_ID, "-",
+ gimp_image_width (image_ID),
+ gimp_image_height (image_ID),
+ gimp_drawable_type (*drawable_ID) | 1,
+ 100.0, GIMP_LAYER_MODE_NORMAL);
+ gimp_image_insert_layer (image_ID, transp, -1, 1);
+ gimp_selection_none (image_ID);
+ gimp_drawable_edit_clear (transp);
+ nvisible++;
+ }
+
+ if (nvisible > 1)
+ {
+ g_free (layers);
+ merged = gimp_image_merge_visible_layers (image_ID, GIMP_CLIP_TO_IMAGE);
+
+ if (merged != -1)
+ *drawable_ID = merged;
+ else
+ return; /* shouldn't happen */
+
+ layers = gimp_image_get_layers (image_ID, &nlayers);
+
+ /* make sure that the merged drawable matches the image size */
+ if (gimp_drawable_width (merged) != gimp_image_width (image_ID) ||
+ gimp_drawable_height (merged) != gimp_image_height (image_ID))
+ {
+ gint off_x, off_y;
+
+ gimp_drawable_offsets (merged, &off_x, &off_y);
+ gimp_layer_resize (merged,
+ gimp_image_width (image_ID),
+ gimp_image_height (image_ID),
+ off_x, off_y);
+ }
+ }
+
+ /* remove any remaining (invisible) layers */
+ for (i = 0; i < nlayers; i++)
+ {
+ if (layers[i] != *drawable_ID)
+ gimp_image_remove_layer (image_ID, layers[i]);
+ }
+ g_free (layers);
+}
+
+static void
+export_flatten (gint32 image_ID,
+ gint32 *drawable_ID)
+{
+ gint32 flattened;
+
+ flattened = gimp_image_flatten (image_ID);
+
+ if (flattened != -1)
+ *drawable_ID = flattened;
+}
+
+static void
+export_remove_alpha (gint32 image_ID,
+ gint32 *drawable_ID)
+{
+ gint32 n_layers;
+ gint32 *layers;
+ gint i;
+
+ layers = gimp_image_get_layers (image_ID, &n_layers);
+
+ for (i = 0; i < n_layers; i++)
+ {
+ if (gimp_drawable_has_alpha (layers[i]))
+ gimp_layer_flatten (layers[i]);
+ }
+
+ g_free (layers);
+}
+
+static void
+export_apply_masks (gint32 image_ID,
+ gint *drawable_ID)
+{
+ gint32 n_layers;
+ gint32 *layers;
+ gint i;
+
+ layers = gimp_image_get_layers (image_ID, &n_layers);
+
+ for (i = 0; i < n_layers; i++)
+ {
+ if (gimp_layer_get_mask (layers[i]) != -1)
+ {
+ /* we can't apply the mask directly to a layer group, so merge it
+ * first
+ */
+ if (gimp_item_is_group (layers[i]))
+ layers[i] = gimp_image_merge_layer_group (image_ID, layers[i]);
+
+ gimp_layer_remove_mask (layers[i], GIMP_MASK_APPLY);
+ }
+ }
+
+ g_free (layers);
+}
+
+static void
+export_convert_rgb (gint32 image_ID,
+ gint32 *drawable_ID)
+{
+ gimp_image_convert_rgb (image_ID);
+}
+
+static void
+export_convert_grayscale (gint32 image_ID,
+ gint32 *drawable_ID)
+{
+ gimp_image_convert_grayscale (image_ID);
+}
+
+static void
+export_convert_indexed (gint32 image_ID,
+ gint32 *drawable_ID)
+{
+ gint32 nlayers;
+
+ /* check alpha */
+ g_free (gimp_image_get_layers (image_ID, &nlayers));
+ if (nlayers > 1 || gimp_drawable_has_alpha (*drawable_ID))
+ gimp_image_convert_indexed (image_ID,
+ GIMP_CONVERT_DITHER_NONE,
+ GIMP_CONVERT_PALETTE_GENERATE,
+ 255, FALSE, FALSE, "");
+ else
+ gimp_image_convert_indexed (image_ID,
+ GIMP_CONVERT_DITHER_NONE,
+ GIMP_CONVERT_PALETTE_GENERATE,
+ 256, FALSE, FALSE, "");
+}
+
+static void
+export_convert_bitmap (gint32 image_ID,
+ gint32 *drawable_ID)
+{
+ if (gimp_image_base_type (image_ID) == GIMP_INDEXED)
+ gimp_image_convert_rgb (image_ID);
+
+ gimp_image_convert_indexed (image_ID,
+ GIMP_CONVERT_DITHER_FS,
+ GIMP_CONVERT_PALETTE_GENERATE,
+ 2, FALSE, FALSE, "");
+}
+
+static void
+export_add_alpha (gint32 image_ID,
+ gint32 *drawable_ID)
+{
+ gint32 nlayers;
+ gint32 i;
+ gint32 *layers;
+
+ layers = gimp_image_get_layers (image_ID, &nlayers);
+ for (i = 0; i < nlayers; i++)
+ {
+ if (!gimp_drawable_has_alpha (layers[i]))
+ gimp_layer_add_alpha (layers[i]);
+ }
+ g_free (layers);
+}
+
+static void
+export_crop_image (gint32 image_ID,
+ gint32 *drawable_ID)
+{
+ gimp_image_crop (image_ID,
+ gimp_image_width (image_ID),
+ gimp_image_height (image_ID),
+ 0, 0);
+}
+
+static void
+export_resize_image (gint32 image_ID,
+ gint32 *drawable_ID)
+{
+ gimp_image_resize_to_layers (image_ID);
+}
+
+static void
+export_void (gint32 image_ID,
+ gint32 *drawable_ID)
+{
+ /* do nothing */
+}
+
+
+/* a set of predefined actions */
+
+static ExportAction export_action_merge =
+{
+ export_merge,
+ NULL,
+ N_("%s plug-in can't handle layers"),
+ { N_("Merge Visible Layers"), NULL },
+ 0
+};
+
+static ExportAction export_action_merge_single =
+{
+ export_merge,
+ NULL,
+ N_("%s plug-in can't handle layer offsets, size or opacity"),
+ { N_("Merge Visible Layers"), NULL },
+ 0
+};
+
+static ExportAction export_action_animate_or_merge =
+{
+ NULL,
+ export_merge,
+ N_("%s plug-in can only handle layers as animation frames"),
+ { N_("Save as Animation"), N_("Merge Visible Layers") },
+ 0
+};
+
+static ExportAction export_action_animate_or_flatten =
+{
+ NULL,
+ export_flatten,
+ N_("%s plug-in can only handle layers as animation frames"),
+ { N_("Save as Animation"), N_("Flatten Image") },
+ 0
+};
+
+static ExportAction export_action_merge_or_flatten =
+{
+ export_flatten,
+ export_merge,
+ N_("%s plug-in can't handle layers"),
+ { N_("Flatten Image"), N_("Merge Visible Layers") },
+ 1
+};
+
+static ExportAction export_action_flatten =
+{
+ export_flatten,
+ NULL,
+ N_("%s plug-in can't handle transparency"),
+ { N_("Flatten Image"), NULL },
+ 0
+};
+
+static ExportAction export_action_remove_alpha =
+{
+ export_remove_alpha,
+ NULL,
+ N_("%s plug-in can't handle transparent layers"),
+ { N_("Flatten Image"), NULL },
+ 0
+};
+
+static ExportAction export_action_apply_masks =
+{
+ export_apply_masks,
+ NULL,
+ N_("%s plug-in can't handle layer masks"),
+ { N_("Apply Layer Masks"), NULL },
+ 0
+};
+
+static ExportAction export_action_convert_rgb =
+{
+ export_convert_rgb,
+ NULL,
+ N_("%s plug-in can only handle RGB images"),
+ { N_("Convert to RGB"), NULL },
+ 0
+};
+
+static ExportAction export_action_convert_grayscale =
+{
+ export_convert_grayscale,
+ NULL,
+ N_("%s plug-in can only handle grayscale images"),
+ { N_("Convert to Grayscale"), NULL },
+ 0
+};
+
+static ExportAction export_action_convert_indexed =
+{
+ export_convert_indexed,
+ NULL,
+ N_("%s plug-in can only handle indexed images"),
+ { N_("Convert to Indexed using default settings\n"
+ "(Do it manually to tune the result)"), NULL },
+ 0
+};
+
+static ExportAction export_action_convert_bitmap =
+{
+ export_convert_bitmap,
+ NULL,
+ N_("%s plug-in can only handle bitmap (two color) indexed images"),
+ { N_("Convert to Indexed using bitmap default settings\n"
+ "(Do it manually to tune the result)"), NULL },
+ 0
+};
+
+static ExportAction export_action_convert_rgb_or_grayscale =
+{
+ export_convert_rgb,
+ export_convert_grayscale,
+ N_("%s plug-in can only handle RGB or grayscale images"),
+ { N_("Convert to RGB"), N_("Convert to Grayscale")},
+ 0
+};
+
+static ExportAction export_action_convert_rgb_or_indexed =
+{
+ export_convert_rgb,
+ export_convert_indexed,
+ N_("%s plug-in can only handle RGB or indexed images"),
+ { N_("Convert to RGB"), N_("Convert to Indexed using default settings\n"
+ "(Do it manually to tune the result)")},
+ 0
+};
+
+static ExportAction export_action_convert_indexed_or_grayscale =
+{
+ export_convert_indexed,
+ export_convert_grayscale,
+ N_("%s plug-in can only handle grayscale or indexed images"),
+ { N_("Convert to Indexed using default settings\n"
+ "(Do it manually to tune the result)"),
+ N_("Convert to Grayscale") },
+ 0
+};
+
+static ExportAction export_action_add_alpha =
+{
+ export_add_alpha,
+ NULL,
+ N_("%s plug-in needs an alpha channel"),
+ { N_("Add Alpha Channel"), NULL},
+ 0
+};
+
+static ExportAction export_action_crop_or_resize =
+{
+ export_crop_image,
+ export_resize_image,
+ N_("%s plug-in needs to crop the layers to the image bounds"),
+ { N_("Crop Layers"), N_("Resize Image to Layers")},
+ 0
+};
+
+
+static ExportFunc
+export_action_get_func (const ExportAction *action)
+{
+ if (action->choice == 0 && action->default_action)
+ {
+ return action->default_action;
+ }
+
+ if (action->choice == 1 && action->alt_action)
+ {
+ return action->alt_action;
+ }
+
+ return export_void;
+}
+
+static void
+export_action_perform (const ExportAction *action,
+ gint32 image_ID,
+ gint32 *drawable_ID)
+{
+ export_action_get_func (action) (image_ID, drawable_ID);
+}
+
+
+/* dialog functions */
+
+static void
+export_toggle_callback (GtkWidget *widget,
+ gpointer data)
+{
+ gint *choice = (gint *) data;
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ *choice = FALSE;
+ else
+ *choice = TRUE;
+}
+
+static GimpExportReturn
+confirm_save_dialog (const gchar *message,
+ const gchar *format_name)
+{
+ GtkWidget *dialog;
+ GtkWidget *hbox;
+ GtkWidget *image;
+ GtkWidget *main_vbox;
+ GtkWidget *label;
+ gchar *text;
+ GimpExportReturn retval;
+
+ g_return_val_if_fail (message != NULL, GIMP_EXPORT_CANCEL);
+ g_return_val_if_fail (format_name != NULL, GIMP_EXPORT_CANCEL);
+
+ dialog = gimp_dialog_new (_("Confirm Save"), "gimp-export-image-confirm",
+ NULL, 0,
+ gimp_standard_help_func,
+ "gimp-export-confirm-dialog",
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("C_onfirm"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ hbox, TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
+ gtk_widget_show (hbox);
+
+ image = gtk_image_new_from_icon_name ("dialog-warning",
+ GTK_ICON_SIZE_DIALOG);
+ gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
+ gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
+ gtk_widget_show (image);
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_box_pack_start (GTK_BOX (hbox), main_vbox, FALSE, FALSE, 0);
+ gtk_widget_show (main_vbox);
+
+ text = g_strdup_printf (message, format_name);
+ label = gtk_label_new (text);
+ g_free (text);
+
+ gimp_label_set_attributes (GTK_LABEL (label),
+ PANGO_ATTR_SCALE, PANGO_SCALE_LARGE,
+ PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD,
+ -1);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
+ gtk_box_pack_start (GTK_BOX (main_vbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ gtk_widget_show (dialog);
+
+ switch (gimp_dialog_run (GIMP_DIALOG (dialog)))
+ {
+ case GTK_RESPONSE_OK:
+ retval = GIMP_EXPORT_EXPORT;
+ break;
+
+ default:
+ retval = GIMP_EXPORT_CANCEL;
+ break;
+ }
+
+ gtk_widget_destroy (dialog);
+
+ return retval;
+}
+
+static GimpExportReturn
+export_dialog (GSList *actions,
+ const gchar *format_name)
+{
+ GtkWidget *dialog;
+ GtkWidget *hbox;
+ GtkWidget *image;
+ GtkWidget *main_vbox;
+ GtkWidget *label;
+ GSList *list;
+ gchar *text;
+ GimpExportReturn retval;
+
+ g_return_val_if_fail (actions != NULL, GIMP_EXPORT_CANCEL);
+ g_return_val_if_fail (format_name != NULL, GIMP_EXPORT_CANCEL);
+
+ dialog = gimp_dialog_new (_("Export File"), "gimp-export-image",
+ NULL, 0,
+ gimp_standard_help_func, "gimp-export-dialog",
+
+ _("_Ignore"), GTK_RESPONSE_NO,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Export"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_NO,
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ hbox, TRUE, TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
+ gtk_widget_show (hbox);
+
+ image = gtk_image_new_from_icon_name ("dialog-information",
+ GTK_ICON_SIZE_DIALOG);
+ gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
+ gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
+ gtk_widget_show (image);
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_box_pack_start (GTK_BOX (hbox), main_vbox, FALSE, FALSE, 0);
+ gtk_widget_show (main_vbox);
+
+ /* the headline */
+ text = g_strdup_printf (_("Your image should be exported before it "
+ "can be saved as %s for the following reasons:"),
+ format_name);
+ label = gtk_label_new (text);
+ g_free (text);
+
+ gimp_label_set_attributes (GTK_LABEL (label),
+ PANGO_ATTR_SCALE, PANGO_SCALE_LARGE,
+ -1);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
+ gtk_box_pack_start (GTK_BOX (main_vbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ for (list = actions; list; list = g_slist_next (list))
+ {
+ ExportAction *action = list->data;
+ GtkWidget *frame;
+ GtkWidget *vbox;
+
+ text = g_strdup_printf (gettext (action->reason), format_name);
+ frame = gimp_frame_new (text);
+ g_free (text);
+
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+
+ if (action->possibilities[0] && action->possibilities[1])
+ {
+ GtkWidget *button;
+ GSList *radio_group = NULL;
+
+ button = gtk_radio_button_new_with_label (radio_group,
+ gettext (action->possibilities[0]));
+ gtk_label_set_justify (GTK_LABEL (gtk_bin_get_child (GTK_BIN (button))),
+ GTK_JUSTIFY_LEFT);
+ radio_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (export_toggle_callback),
+ &action->choice);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+ (action->choice == 0));
+ gtk_widget_show (button);
+
+ button = gtk_radio_button_new_with_label (radio_group,
+ gettext (action->possibilities[1]));
+ gtk_label_set_justify (GTK_LABEL (gtk_bin_get_child (GTK_BIN (button))),
+ GTK_JUSTIFY_LEFT);
+ radio_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+ (action->choice == 1));
+ gtk_widget_show (button);
+ }
+ else if (action->possibilities[0])
+ {
+ label = gtk_label_new (gettext (action->possibilities[0]));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+ action->choice = 0;
+ }
+
+ gtk_widget_show (vbox);
+ }
+
+ /* the footline */
+ label = gtk_label_new (_("The export conversion won't modify your "
+ "original image."));
+ gimp_label_set_attributes (GTK_LABEL (label),
+ PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
+ -1);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
+ gtk_box_pack_start (GTK_BOX (main_vbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ gtk_widget_show (dialog);
+
+ switch (gimp_dialog_run (GIMP_DIALOG (dialog)))
+ {
+ case GTK_RESPONSE_OK:
+ retval = GIMP_EXPORT_EXPORT;
+ break;
+
+ case GTK_RESPONSE_NO:
+ retval = GIMP_EXPORT_IGNORE;
+ break;
+
+ default:
+ retval = GIMP_EXPORT_CANCEL;
+ break;
+ }
+
+ gtk_widget_destroy (dialog);
+
+ return retval;
+}
+
+/**
+ * gimp_export_image:
+ * @image_ID: Pointer to the image_ID.
+ * @drawable_ID: Pointer to the drawable_ID.
+ * @format_name: The (short) name of the image_format (e.g. JPEG or GIF).
+ * @capabilities: What can the image_format do?
+ *
+ * Takes an image and a drawable to be saved together with a
+ * description of the capabilities of the image_format. If the
+ * type of image doesn't match the capabilities of the format
+ * a dialog is opened that informs the user that the image has
+ * to be exported and offers to do the necessary conversions.
+ *
+ * If the user chooses to export the image, a copy is created.
+ * This copy is then converted, the image_ID and drawable_ID
+ * are changed to point to the new image and the procedure returns
+ * GIMP_EXPORT_EXPORT. The save_plugin has to take care of deleting the
+ * created image using gimp_image_delete() when it has saved it.
+ *
+ * If the user chooses to Ignore the export problem, the image_ID
+ * and drawable_ID is not altered, GIMP_EXPORT_IGNORE is returned and
+ * the save_plugin should try to save the original image. If the
+ * user chooses Cancel, GIMP_EXPORT_CANCEL is returned and the
+ * save_plugin should quit itself with status %GIMP_PDB_CANCEL.
+ *
+ * If @format_name is NULL, no dialogs will be shown and this function
+ * will behave as if the user clicked on the 'Export' button, if a
+ * dialog would have been shown.
+ *
+ * Returns: An enum of #GimpExportReturn describing the user_action.
+ **/
+GimpExportReturn
+gimp_export_image (gint32 *image_ID,
+ gint32 *drawable_ID,
+ const gchar *format_name,
+ GimpExportCapabilities capabilities)
+{
+ GSList *actions = NULL;
+ GimpImageBaseType type;
+ gint32 i;
+ gint32 n_layers;
+ gint32 *layers;
+ gboolean interactive = FALSE;
+ gboolean added_flatten = FALSE;
+ gboolean has_layer_masks = FALSE;
+ gboolean background_has_alpha = TRUE;
+ GimpExportReturn retval = GIMP_EXPORT_CANCEL;
+
+ g_return_val_if_fail (*image_ID > -1 && *drawable_ID > -1, FALSE);
+
+ /* do some sanity checks */
+ if (capabilities & GIMP_EXPORT_NEEDS_ALPHA)
+ capabilities |= GIMP_EXPORT_CAN_HANDLE_ALPHA;
+
+ if (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS_AS_ANIMATION)
+ capabilities |= GIMP_EXPORT_CAN_HANDLE_LAYERS;
+
+ if (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYER_MASKS)
+ capabilities |= GIMP_EXPORT_CAN_HANDLE_LAYERS;
+
+ if (format_name && g_getenv ("GIMP_INTERACTIVE_EXPORT"))
+ interactive = TRUE;
+
+ /* ask for confirmation if the user is not saving a layer (see bug #51114) */
+ if (interactive &&
+ ! gimp_item_is_layer (*drawable_ID) &&
+ ! (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS))
+ {
+ if (gimp_item_is_layer_mask (*drawable_ID))
+ {
+ retval = confirm_save_dialog
+ (_("You are about to save a layer mask as %s.\n"
+ "This will not save the visible layers."), format_name);
+ }
+ else if (gimp_item_is_channel (*drawable_ID))
+ {
+ retval = confirm_save_dialog
+ (_("You are about to save a channel (saved selection) as %s.\n"
+ "This will not save the visible layers."), format_name);
+ }
+ else
+ {
+ /* this should not happen */
+ g_warning ("%s: unknown drawable type!", G_STRFUNC);
+ }
+
+ /* cancel - the user can then select an appropriate layer to save */
+ if (retval == GIMP_EXPORT_CANCEL)
+ return GIMP_EXPORT_CANCEL;
+ }
+
+
+ /* check alpha and layer masks */
+ layers = gimp_image_get_layers (*image_ID, &n_layers);
+
+ for (i = 0; i < n_layers; i++)
+ {
+ if (gimp_drawable_has_alpha (layers[i]))
+ {
+ if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_ALPHA))
+ {
+ if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS))
+ {
+ actions = g_slist_prepend (actions, &export_action_flatten);
+ added_flatten = TRUE;
+ break;
+ }
+ else
+ {
+ actions = g_slist_prepend (actions, &export_action_remove_alpha);
+ break;
+ }
+ }
+ }
+ else
+ {
+ /* If this is the last layer, it's visible and has no alpha
+ * channel, then the image has a "flat" background
+ */
+ if (i == n_layers - 1 && gimp_item_get_visible (layers[i]))
+ background_has_alpha = FALSE;
+
+ if (capabilities & GIMP_EXPORT_NEEDS_ALPHA)
+ {
+ actions = g_slist_prepend (actions, &export_action_add_alpha);
+ break;
+ }
+ }
+ }
+
+ if (! added_flatten)
+ {
+ for (i = 0; i < n_layers; i++)
+ {
+ if (gimp_layer_get_mask (layers[i]) != -1)
+ has_layer_masks = TRUE;
+ }
+ }
+
+ if (! added_flatten)
+ {
+ gint32 n_children;
+ gint32 *children;
+
+ children = gimp_item_get_children (layers[0], &n_children);
+
+ if ((capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS) &&
+ (capabilities & GIMP_EXPORT_NEEDS_CROP))
+ {
+ GeglRectangle image_bounds;
+ gboolean needs_crop = FALSE;
+
+ image_bounds.x = 0;
+ image_bounds.y = 0;
+ image_bounds.width = gimp_image_width (*image_ID);
+ image_bounds.height = gimp_image_height (*image_ID);
+
+ for (i = 0; i < n_layers; i++)
+ {
+ GeglRectangle layer_bounds;
+
+ gimp_drawable_offsets (layers[i],
+ &layer_bounds.x, &layer_bounds.y);
+
+ layer_bounds.width = gimp_drawable_width (layers[i]);
+ layer_bounds.height = gimp_drawable_height (layers[i]);
+
+ if (! gegl_rectangle_contains (&image_bounds, &layer_bounds))
+ {
+ needs_crop = TRUE;
+
+ break;
+ }
+ }
+
+ if (needs_crop)
+ {
+ actions = g_slist_prepend (actions,
+ &export_action_crop_or_resize);
+ }
+ }
+
+ /* check if layer size != canvas size, opacity != 100%, or offsets != 0 */
+ if (n_layers == 1 &&
+ ! children &&
+ gimp_item_is_layer (*drawable_ID) &&
+ ! (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS))
+ {
+ gint offset_x;
+ gint offset_y;
+
+ gimp_drawable_offsets (*drawable_ID, &offset_x, &offset_y);
+
+ if ((gimp_layer_get_opacity (*drawable_ID) < 100.0) ||
+ (gimp_image_width (*image_ID) !=
+ gimp_drawable_width (*drawable_ID)) ||
+ (gimp_image_height (*image_ID) !=
+ gimp_drawable_height (*drawable_ID)) ||
+ offset_x || offset_y)
+ {
+ if (capabilities & GIMP_EXPORT_CAN_HANDLE_ALPHA)
+ {
+ actions = g_slist_prepend (actions,
+ &export_action_merge_single);
+ }
+ else
+ {
+ actions = g_slist_prepend (actions,
+ &export_action_flatten);
+ }
+ }
+ }
+ /* check multiple layers */
+ else if (n_layers > 1)
+ {
+ if (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS_AS_ANIMATION)
+ {
+ if (background_has_alpha ||
+ capabilities & GIMP_EXPORT_NEEDS_ALPHA)
+ actions = g_slist_prepend (actions,
+ &export_action_animate_or_merge);
+ else
+ actions = g_slist_prepend (actions,
+ &export_action_animate_or_flatten);
+ }
+ else if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS))
+ {
+ if (capabilities & GIMP_EXPORT_NEEDS_ALPHA)
+ actions = g_slist_prepend (actions,
+ &export_action_merge);
+ else
+ actions = g_slist_prepend (actions,
+ &export_action_merge_or_flatten);
+ }
+ }
+ /* check for a single toplevel layer group */
+ else if (children)
+ {
+ if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS))
+ {
+ if (capabilities & GIMP_EXPORT_NEEDS_ALPHA)
+ actions = g_slist_prepend (actions,
+ &export_action_merge);
+ else
+ actions = g_slist_prepend (actions,
+ &export_action_merge_or_flatten);
+ }
+ }
+
+ g_free (children);
+
+ /* check layer masks */
+ if (has_layer_masks &&
+ ! (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYER_MASKS))
+ actions = g_slist_prepend (actions, &export_action_apply_masks);
+ }
+
+ g_free (layers);
+
+ /* check the image type */
+ type = gimp_image_base_type (*image_ID);
+ switch (type)
+ {
+ case GIMP_RGB:
+ if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_RGB))
+ {
+ if ((capabilities & GIMP_EXPORT_CAN_HANDLE_INDEXED) &&
+ (capabilities & GIMP_EXPORT_CAN_HANDLE_GRAY))
+ actions = g_slist_prepend (actions,
+ &export_action_convert_indexed_or_grayscale);
+ else if (capabilities & GIMP_EXPORT_CAN_HANDLE_INDEXED)
+ actions = g_slist_prepend (actions,
+ &export_action_convert_indexed);
+ else if (capabilities & GIMP_EXPORT_CAN_HANDLE_GRAY)
+ actions = g_slist_prepend (actions,
+ &export_action_convert_grayscale);
+ else if (capabilities & GIMP_EXPORT_CAN_HANDLE_BITMAP)
+ actions = g_slist_prepend (actions,
+ &export_action_convert_bitmap);
+ }
+ break;
+
+ case GIMP_GRAY:
+ if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_GRAY))
+ {
+ if ((capabilities & GIMP_EXPORT_CAN_HANDLE_RGB) &&
+ (capabilities & GIMP_EXPORT_CAN_HANDLE_INDEXED))
+ actions = g_slist_prepend (actions,
+ &export_action_convert_rgb_or_indexed);
+ else if (capabilities & GIMP_EXPORT_CAN_HANDLE_RGB)
+ actions = g_slist_prepend (actions,
+ &export_action_convert_rgb);
+ else if (capabilities & GIMP_EXPORT_CAN_HANDLE_INDEXED)
+ actions = g_slist_prepend (actions,
+ &export_action_convert_indexed);
+ else if (capabilities & GIMP_EXPORT_CAN_HANDLE_BITMAP)
+ actions = g_slist_prepend (actions,
+ &export_action_convert_bitmap);
+ }
+ break;
+
+ case GIMP_INDEXED:
+ if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_INDEXED))
+ {
+ if ((capabilities & GIMP_EXPORT_CAN_HANDLE_RGB) &&
+ (capabilities & GIMP_EXPORT_CAN_HANDLE_GRAY))
+ actions = g_slist_prepend (actions,
+ &export_action_convert_rgb_or_grayscale);
+ else if (capabilities & GIMP_EXPORT_CAN_HANDLE_RGB)
+ actions = g_slist_prepend (actions,
+ &export_action_convert_rgb);
+ else if (capabilities & GIMP_EXPORT_CAN_HANDLE_GRAY)
+ actions = g_slist_prepend (actions,
+ &export_action_convert_grayscale);
+ else if (capabilities & GIMP_EXPORT_CAN_HANDLE_BITMAP)
+ {
+ gint n_colors;
+
+ g_free (gimp_image_get_colormap (*image_ID, &n_colors));
+
+ if (n_colors > 2)
+ actions = g_slist_prepend (actions,
+ &export_action_convert_bitmap);
+ }
+ }
+ break;
+ }
+
+ if (actions)
+ {
+ actions = g_slist_reverse (actions);
+
+ if (interactive)
+ retval = export_dialog (actions, format_name);
+ else
+ retval = GIMP_EXPORT_EXPORT;
+ }
+ else
+ {
+ retval = GIMP_EXPORT_IGNORE;
+ }
+
+ if (retval == GIMP_EXPORT_EXPORT)
+ {
+ GSList *list;
+
+ *image_ID = gimp_image_duplicate (*image_ID);
+ *drawable_ID = gimp_image_get_active_layer (*image_ID);
+
+ gimp_image_undo_disable (*image_ID);
+
+ for (list = actions; list; list = list->next)
+ {
+ export_action_perform (list->data, *image_ID, drawable_ID);
+ }
+ }
+
+ g_slist_free (actions);
+
+ return retval;
+}
+
+/**
+ * gimp_export_dialog_new:
+ * @format_name: The short name of the image_format (e.g. JPEG or PNG).
+ * @role: The dialog's @role which will be set with
+ * gtk_window_set_role().
+ * @help_id: The GIMP help id.
+ *
+ * Creates a new export dialog. All file plug-ins should use this
+ * dialog to get a consistent look on the export dialogs. Use
+ * gimp_export_dialog_get_content_area() to get a #GtkVBox to be
+ * filled with export options. The export dialog is a wrapped
+ * #GimpDialog.
+ *
+ * The dialog response when the user clicks on the Export button is
+ * %GTK_RESPONSE_OK, and when the Cancel button is clicked it is
+ * %GTK_RESPONSE_CANCEL.
+ *
+ * Returns: The new export dialog.
+ *
+ * Since: 2.8
+ **/
+GtkWidget *
+gimp_export_dialog_new (const gchar *format_name,
+ const gchar *role,
+ const gchar *help_id)
+{
+ GtkWidget *dialog;
+ /* TRANSLATORS: the %s parameter is an image format name (ex: PNG). */
+ gchar *title = g_strdup_printf (_("Export Image as %s"), format_name);
+
+ dialog = gimp_dialog_new (title, role,
+ NULL, 0,
+ gimp_standard_help_func, help_id,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Export"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ g_free (title);
+
+ return dialog;
+}
+
+/**
+ * gimp_export_dialog_get_content_area:
+ * @dialog: A dialog created with gimp_export_dialog_new()
+ *
+ * Returns the #GtkVBox of the passed export dialog to be filled with
+ * export options.
+ *
+ * Returns: The #GtkVBox to fill with export options.
+ *
+ * Since: 2.8
+ **/
+GtkWidget *
+gimp_export_dialog_get_content_area (GtkWidget *dialog)
+{
+ return gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+}
diff --git a/libgimp/gimpexport.h b/libgimp/gimpexport.h
new file mode 100644
index 0000000..bc8c385
--- /dev/null
+++ b/libgimp/gimpexport.h
@@ -0,0 +1,93 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1999 Peter Mattis and Spencer Kimball
+ *
+ * gimpexport.h
+ * Copyright (C) 1999-2000 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_UI_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimpui.h> can be included directly."
+#endif
+
+#ifndef __GIMP_EXPORT_H__
+#define __GIMP_EXPORT_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+/**
+ * GimpExportCapabilities:
+ * @GIMP_EXPORT_CAN_HANDLE_RGB: Handles RGB images
+ * @GIMP_EXPORT_CAN_HANDLE_GRAY: Handles grayscale images
+ * @GIMP_EXPORT_CAN_HANDLE_INDEXED: Handles indexed images
+ * @GIMP_EXPORT_CAN_HANDLE_BITMAP: Handles two-color indexed images
+ * @GIMP_EXPORT_CAN_HANDLE_ALPHA: Handles alpha channels
+ * @GIMP_EXPORT_CAN_HANDLE_LAYERS: Hanldes layers
+ * @GIMP_EXPORT_CAN_HANDLE_LAYERS_AS_ANIMATION: Handles aminations of layers
+ * @GIMP_EXPORT_CAN_HANDLE_LAYER_MASKS: Handles layer masks
+ * @GIMP_EXPORT_NEEDS_ALPHA: Needs alpha channels
+ * @GIMP_EXPORT_NEEDS_CROP: Needs to crop content to image bounds
+ *
+ * The types of images and layers an export procedure can handle
+ **/
+typedef enum
+{
+ GIMP_EXPORT_CAN_HANDLE_RGB = 1 << 0,
+ GIMP_EXPORT_CAN_HANDLE_GRAY = 1 << 1,
+ GIMP_EXPORT_CAN_HANDLE_INDEXED = 1 << 2,
+ GIMP_EXPORT_CAN_HANDLE_BITMAP = 1 << 3,
+ GIMP_EXPORT_CAN_HANDLE_ALPHA = 1 << 4,
+ GIMP_EXPORT_CAN_HANDLE_LAYERS = 1 << 5,
+ GIMP_EXPORT_CAN_HANDLE_LAYERS_AS_ANIMATION = 1 << 6,
+ GIMP_EXPORT_CAN_HANDLE_LAYER_MASKS = 1 << 7,
+ GIMP_EXPORT_NEEDS_ALPHA = 1 << 8,
+ GIMP_EXPORT_NEEDS_CROP = 1 << 9
+} GimpExportCapabilities;
+
+
+/**
+ * GimpExportReturn:
+ * @GIMP_EXPORT_CANCEL: The export was cancelled
+ * @GIMP_EXPORT_IGNORE: The image is unmodified but export shall continue anyway
+ * @GIMP_EXPORT_EXPORT: The chosen transforms were applied to the image
+ *
+ * Possible return values of gimp_export_image().
+ **/
+typedef enum
+{
+ GIMP_EXPORT_CANCEL,
+ GIMP_EXPORT_IGNORE,
+ GIMP_EXPORT_EXPORT
+} GimpExportReturn;
+
+
+GimpExportReturn gimp_export_image (gint32 *image_ID,
+ gint32 *drawable_ID,
+ const gchar *format_name,
+ GimpExportCapabilities capabilities);
+
+GtkWidget * gimp_export_dialog_new (const gchar *format_name,
+ const gchar *role,
+ const gchar *help_id);
+GtkWidget * gimp_export_dialog_get_content_area (GtkWidget *dialog);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_EXPORT_H__ */
diff --git a/libgimp/gimpfileops_pdb.c b/libgimp/gimpfileops_pdb.c
new file mode 100644
index 0000000..4e5d8ae
--- /dev/null
+++ b/libgimp/gimpfileops_pdb.c
@@ -0,0 +1,550 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpfileops_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpfileops
+ * @title: gimpfileops
+ * @short_description: Image file operations (load, save, etc.)
+ *
+ * Image file operations (load, save, etc.)
+ **/
+
+
+/**
+ * gimp_file_load:
+ * @run_mode: The run mode.
+ * @filename: The name of the file to load.
+ * @raw_filename: The name as entered by the user.
+ *
+ * Loads an image file by invoking the right load handler.
+ *
+ * This procedure invokes the correct file load handler using magic if
+ * possible, and falling back on the file's extension and/or prefix if
+ * not. The name of the file to load is typically a full pathname, and
+ * the name entered is what the user actually typed before prepending a
+ * directory path. The reason for this is that if the user types
+ * https://www.gimp.org/foo.png he wants to fetch a URL, and the full
+ * pathname will not look like a URL.
+ *
+ * Returns: The output image.
+ **/
+gint32
+gimp_file_load (GimpRunMode run_mode,
+ const gchar *filename,
+ const gchar *raw_filename)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 image_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-file-load",
+ &nreturn_vals,
+ GIMP_PDB_INT32, run_mode,
+ GIMP_PDB_STRING, filename,
+ GIMP_PDB_STRING, raw_filename,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ image_ID = return_vals[1].data.d_image;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return image_ID;
+}
+
+/**
+ * gimp_file_load_layer:
+ * @run_mode: The run mode.
+ * @image_ID: Destination image.
+ * @filename: The name of the file to load.
+ *
+ * Loads an image file as a layer for an existing image.
+ *
+ * This procedure behaves like the file-load procedure but opens the
+ * specified image as a layer for an existing image. The returned layer
+ * needs to be added to the existing image with
+ * gimp_image_insert_layer().
+ *
+ * Returns: The layer created when loading the image file.
+ *
+ * Since: 2.4
+ **/
+gint32
+gimp_file_load_layer (GimpRunMode run_mode,
+ gint32 image_ID,
+ const gchar *filename)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 layer_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-file-load-layer",
+ &nreturn_vals,
+ GIMP_PDB_INT32, run_mode,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_STRING, filename,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ layer_ID = return_vals[1].data.d_layer;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return layer_ID;
+}
+
+/**
+ * gimp_file_load_layers:
+ * @run_mode: The run mode.
+ * @image_ID: Destination image.
+ * @filename: The name of the file to load.
+ * @num_layers: The number of loaded layers.
+ *
+ * Loads an image file as layers for an existing image.
+ *
+ * This procedure behaves like the file-load procedure but opens the
+ * specified image as layers for an existing image. The returned layers
+ * needs to be added to the existing image with
+ * gimp_image_insert_layer().
+ *
+ * Returns: The list of loaded layers.
+ *
+ * Since: 2.4
+ **/
+gint *
+gimp_file_load_layers (GimpRunMode run_mode,
+ gint32 image_ID,
+ const gchar *filename,
+ gint *num_layers)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint *layer_ids = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-file-load-layers",
+ &nreturn_vals,
+ GIMP_PDB_INT32, run_mode,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_STRING, filename,
+ GIMP_PDB_END);
+
+ *num_layers = 0;
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ *num_layers = return_vals[1].data.d_int32;
+ layer_ids = g_new (gint32, *num_layers);
+ memcpy (layer_ids,
+ return_vals[2].data.d_int32array,
+ *num_layers * sizeof (gint32));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return layer_ids;
+}
+
+/**
+ * gimp_file_save:
+ * @run_mode: The run mode.
+ * @image_ID: Input image.
+ * @drawable_ID: Drawable to save.
+ * @filename: The name of the file to save the image in.
+ * @raw_filename: The name as entered by the user.
+ *
+ * Saves a file by extension.
+ *
+ * This procedure invokes the correct file save handler according to
+ * the file's extension and/or prefix. The name of the file to save is
+ * typically a full pathname, and the name entered is what the user
+ * actually typed before prepending a directory path. The reason for
+ * this is that if the user types https://www.gimp.org/foo.png she
+ * wants to fetch a URL, and the full pathname will not look like a
+ * URL.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_file_save (GimpRunMode run_mode,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ const gchar *filename,
+ const gchar *raw_filename)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-file-save",
+ &nreturn_vals,
+ GIMP_PDB_INT32, run_mode,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_STRING, filename,
+ GIMP_PDB_STRING, raw_filename,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_file_save_thumbnail:
+ * @image_ID: The image.
+ * @filename: The name of the file the thumbnail belongs to.
+ *
+ * Saves a thumbnail for the given image
+ *
+ * This procedure saves a thumbnail for the given image according to
+ * the Free Desktop Thumbnail Managing Standard. The thumbnail is saved
+ * so that it belongs to the file with the given filename. This means
+ * you have to save the image under this name first, otherwise this
+ * procedure will fail. This procedure may become useful if you want to
+ * explicitly save a thumbnail with a file.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_file_save_thumbnail (gint32 image_ID,
+ const gchar *filename)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-file-save-thumbnail",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_STRING, filename,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_register_magic_load_handler:
+ * @procedure_name: The name of the procedure to be used for loading.
+ * @extensions: comma separated list of extensions this handler can load (i.e. \"jpg,jpeg\").
+ * @prefixes: comma separated list of prefixes this handler can load (i.e. \"http:,ftp:\").
+ * @magics: comma separated list of magic file information this handler can load (i.e. \"0,string,GIF\").
+ *
+ * Registers a file load handler procedure.
+ *
+ * Registers a procedural database procedure to be called to load files
+ * of a particular file format using magic file information.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_register_magic_load_handler (const gchar *procedure_name,
+ const gchar *extensions,
+ const gchar *prefixes,
+ const gchar *magics)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-register-magic-load-handler",
+ &nreturn_vals,
+ GIMP_PDB_STRING, procedure_name,
+ GIMP_PDB_STRING, extensions,
+ GIMP_PDB_STRING, prefixes,
+ GIMP_PDB_STRING, magics,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_register_load_handler:
+ * @procedure_name: The name of the procedure to be used for loading.
+ * @extensions: comma separated list of extensions this handler can load (i.e. \"jpg,jpeg\").
+ * @prefixes: comma separated list of prefixes this handler can load (i.e. \"http:,ftp:\").
+ *
+ * Registers a file load handler procedure.
+ *
+ * Registers a procedural database procedure to be called to load files
+ * of a particular file format.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_register_load_handler (const gchar *procedure_name,
+ const gchar *extensions,
+ const gchar *prefixes)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-register-load-handler",
+ &nreturn_vals,
+ GIMP_PDB_STRING, procedure_name,
+ GIMP_PDB_STRING, extensions,
+ GIMP_PDB_STRING, prefixes,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_register_save_handler:
+ * @procedure_name: The name of the procedure to be used for saving.
+ * @extensions: comma separated list of extensions this handler can save (i.e. \"jpg,jpeg\").
+ * @prefixes: comma separated list of prefixes this handler can save (i.e. \"http:,ftp:\").
+ *
+ * Registers a file save handler procedure.
+ *
+ * Registers a procedural database procedure to be called to save files
+ * in a particular file format.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_register_save_handler (const gchar *procedure_name,
+ const gchar *extensions,
+ const gchar *prefixes)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-register-save-handler",
+ &nreturn_vals,
+ GIMP_PDB_STRING, procedure_name,
+ GIMP_PDB_STRING, extensions,
+ GIMP_PDB_STRING, prefixes,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_register_file_handler_priority:
+ * @procedure_name: The name of the procedure to set the priority of.
+ * @priority: The procedure priority.
+ *
+ * Sets the priority of a file handler procedure.
+ *
+ * Sets the priority of a file handler procedure. When more than one
+ * procedure matches a given file, the procedure with the lowest
+ * priority is used; if more than one procedure has the lowest
+ * priority, it is unspecified which one of them is used. The default
+ * priority for file handler procedures is 0.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10.6
+ **/
+gboolean
+gimp_register_file_handler_priority (const gchar *procedure_name,
+ gint priority)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-register-file-handler-priority",
+ &nreturn_vals,
+ GIMP_PDB_STRING, procedure_name,
+ GIMP_PDB_INT32, priority,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_register_file_handler_mime:
+ * @procedure_name: The name of the procedure to associate a MIME type with.
+ * @mime_types: A comma-separated list of MIME types, such as \"image/jpeg\".
+ *
+ * Associates MIME types with a file handler procedure.
+ *
+ * Registers MIME types for a file handler procedure. This allows GIMP
+ * to determine the MIME type of the file opened or saved using this
+ * procedure. It is recommended that only one MIME type is registered
+ * per file procedure; when registering more than one MIME type, GIMP
+ * will associate the first one with files opened or saved with this
+ * procedure.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_register_file_handler_mime (const gchar *procedure_name,
+ const gchar *mime_types)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-register-file-handler-mime",
+ &nreturn_vals,
+ GIMP_PDB_STRING, procedure_name,
+ GIMP_PDB_STRING, mime_types,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_register_file_handler_uri:
+ * @procedure_name: The name of the procedure to enable URIs for.
+ *
+ * Registers a file handler procedure as capable of handling URIs.
+ *
+ * Registers a file handler procedure as capable of handling URIs. This
+ * allows GIMP to call the procedure directly for all kinds of URIs,
+ * and the 'filename' traditionally passed to file procedures turns
+ * into an URI.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_register_file_handler_uri (const gchar *procedure_name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-register-file-handler-uri",
+ &nreturn_vals,
+ GIMP_PDB_STRING, procedure_name,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_register_file_handler_raw:
+ * @procedure_name: The name of the procedure to enable raw handling for.
+ *
+ * Registers a file handler procedure as capable of handling raw camera
+ * files.
+ *
+ * Registers a file handler procedure as capable of handling raw
+ * digital camera files. Use this procedure only to register raw load
+ * handlers, calling it on a save handler will generate an error.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_register_file_handler_raw (const gchar *procedure_name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-register-file-handler-raw",
+ &nreturn_vals,
+ GIMP_PDB_STRING, procedure_name,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_register_thumbnail_loader:
+ * @load_proc: The name of the procedure the thumbnail loader with.
+ * @thumb_proc: The name of the thumbnail load procedure.
+ *
+ * Associates a thumbnail loader with a file load procedure.
+ *
+ * Some file formats allow for embedded thumbnails, other file formats
+ * contain a scalable image or provide the image data in different
+ * resolutions. A file plug-in for such a format may register a special
+ * procedure that allows GIMP to load a thumbnail preview of the image.
+ * This procedure is then associated with the standard load procedure
+ * using this function.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_register_thumbnail_loader (const gchar *load_proc,
+ const gchar *thumb_proc)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-register-thumbnail-loader",
+ &nreturn_vals,
+ GIMP_PDB_STRING, load_proc,
+ GIMP_PDB_STRING, thumb_proc,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimpfileops_pdb.h b/libgimp/gimpfileops_pdb.h
new file mode 100644
index 0000000..80d2589
--- /dev/null
+++ b/libgimp/gimpfileops_pdb.h
@@ -0,0 +1,74 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpfileops_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_FILEOPS_PDB_H__
+#define __GIMP_FILEOPS_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gint32 gimp_file_load (GimpRunMode run_mode,
+ const gchar *filename,
+ const gchar *raw_filename);
+gint32 gimp_file_load_layer (GimpRunMode run_mode,
+ gint32 image_ID,
+ const gchar *filename);
+gint* gimp_file_load_layers (GimpRunMode run_mode,
+ gint32 image_ID,
+ const gchar *filename,
+ gint *num_layers);
+gboolean gimp_file_save (GimpRunMode run_mode,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ const gchar *filename,
+ const gchar *raw_filename);
+gboolean gimp_file_save_thumbnail (gint32 image_ID,
+ const gchar *filename);
+gboolean gimp_register_magic_load_handler (const gchar *procedure_name,
+ const gchar *extensions,
+ const gchar *prefixes,
+ const gchar *magics);
+gboolean gimp_register_load_handler (const gchar *procedure_name,
+ const gchar *extensions,
+ const gchar *prefixes);
+gboolean gimp_register_save_handler (const gchar *procedure_name,
+ const gchar *extensions,
+ const gchar *prefixes);
+gboolean gimp_register_file_handler_priority (const gchar *procedure_name,
+ gint priority);
+gboolean gimp_register_file_handler_mime (const gchar *procedure_name,
+ const gchar *mime_types);
+gboolean gimp_register_file_handler_uri (const gchar *procedure_name);
+gboolean gimp_register_file_handler_raw (const gchar *procedure_name);
+gboolean gimp_register_thumbnail_loader (const gchar *load_proc,
+ const gchar *thumb_proc);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_FILEOPS_PDB_H__ */
diff --git a/libgimp/gimpfloatingsel_pdb.c b/libgimp/gimpfloatingsel_pdb.c
new file mode 100644
index 0000000..b49e69e
--- /dev/null
+++ b/libgimp/gimpfloatingsel_pdb.c
@@ -0,0 +1,227 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpfloatingsel_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpfloatingsel
+ * @title: gimpfloatingsel
+ * @short_description: Functions for removing or attaching floating selections.
+ *
+ * Functions for removing or attaching floating selections.
+ **/
+
+
+/**
+ * gimp_floating_sel_remove:
+ * @floating_sel_ID: The floating selection.
+ *
+ * Remove the specified floating selection from its associated
+ * drawable.
+ *
+ * This procedure removes the floating selection completely, without
+ * any side effects. The associated drawable is then set to active.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_floating_sel_remove (gint32 floating_sel_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-floating-sel-remove",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, floating_sel_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_floating_sel_anchor:
+ * @floating_sel_ID: The floating selection.
+ *
+ * Anchor the specified floating selection to its associated drawable.
+ *
+ * This procedure anchors the floating selection to its associated
+ * drawable. This is similar to merging with a merge type of
+ * ClipToBottomLayer. The floating selection layer is no longer valid
+ * after this operation.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_floating_sel_anchor (gint32 floating_sel_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-floating-sel-anchor",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, floating_sel_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_floating_sel_to_layer:
+ * @floating_sel_ID: The floating selection.
+ *
+ * Transforms the specified floating selection into a layer.
+ *
+ * This procedure transforms the specified floating selection into a
+ * layer with the same offsets and extents. The composited image will
+ * look precisely the same, but the floating selection layer will no
+ * longer be clipped to the extents of the drawable it was attached to.
+ * The floating selection will become the active layer. This procedure
+ * will not work if the floating selection has a different base type
+ * from the underlying image. This might be the case if the floating
+ * selection is above an auxiliary channel or a layer mask.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_floating_sel_to_layer (gint32 floating_sel_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-floating-sel-to-layer",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, floating_sel_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_floating_sel_attach:
+ * @layer_ID: The layer (is attached as floating selection).
+ * @drawable_ID: The drawable (where to attach the floating selection).
+ *
+ * Attach the specified layer as floating to the specified drawable.
+ *
+ * This procedure attaches the layer as floating selection to the
+ * drawable.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_floating_sel_attach (gint32 layer_ID,
+ gint32 drawable_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-floating-sel-attach",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_floating_sel_rigor:
+ * @floating_sel_ID: The floating selection.
+ * @undo: .
+ *
+ * Deprecated: There is no replacement for this procedure.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_floating_sel_rigor (gint32 floating_sel_ID,
+ gboolean undo)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-floating-sel-rigor",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, floating_sel_ID,
+ GIMP_PDB_INT32, undo,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_floating_sel_relax:
+ * @floating_sel_ID: The floating selection.
+ * @undo: .
+ *
+ * Deprecated: There is no replacement for this procedure.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_floating_sel_relax (gint32 floating_sel_ID,
+ gboolean undo)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-floating-sel-relax",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, floating_sel_ID,
+ GIMP_PDB_INT32, undo,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimpfloatingsel_pdb.h b/libgimp/gimpfloatingsel_pdb.h
new file mode 100644
index 0000000..e6d87dd
--- /dev/null
+++ b/libgimp/gimpfloatingsel_pdb.h
@@ -0,0 +1,50 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpfloatingsel_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_FLOATING_SEL_PDB_H__
+#define __GIMP_FLOATING_SEL_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_floating_sel_remove (gint32 floating_sel_ID);
+gboolean gimp_floating_sel_anchor (gint32 floating_sel_ID);
+gboolean gimp_floating_sel_to_layer (gint32 floating_sel_ID);
+gboolean gimp_floating_sel_attach (gint32 layer_ID,
+ gint32 drawable_ID);
+GIMP_DEPRECATED
+gboolean gimp_floating_sel_rigor (gint32 floating_sel_ID,
+ gboolean undo);
+GIMP_DEPRECATED
+gboolean gimp_floating_sel_relax (gint32 floating_sel_ID,
+ gboolean undo);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_FLOATING_SEL_PDB_H__ */
diff --git a/libgimp/gimpfontmenu.c b/libgimp/gimpfontmenu.c
new file mode 100644
index 0000000..5e85539
--- /dev/null
+++ b/libgimp/gimpfontmenu.c
@@ -0,0 +1,142 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpfontmenu.c
+ * Copyright (C) 2003 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "gimp.h"
+
+#include "gimpuitypes.h"
+#include "gimpfontmenu.h"
+#include "gimpfontselectbutton.h"
+
+
+/**
+ * SECTION: gimpfontmenu
+ * @title: gimpfontmenu
+ * @short_description: A widget for selecting fonts.
+ *
+ * A widget for selecting fonts.
+ **/
+
+
+typedef struct
+{
+ GimpRunFontCallback callback;
+ gpointer data;
+} CompatCallbackData;
+
+
+static void compat_callback (GimpFontSelectButton *font_button,
+ const gchar *font_name,
+ gboolean dialog_closing,
+ CompatCallbackData *data);
+static void compat_callback_data_free (CompatCallbackData *data);
+
+
+/**
+ * gimp_font_select_widget_new:
+ * @title: Title of the dialog to use or %NULL to use the default title.
+ * @font_name: Initial font name.
+ * @callback: A function to call when the selected font changes.
+ * @data: A pointer to arbitrary data to be used in the call to @callback.
+ *
+ * Creates a new #GtkWidget that completely controls the selection of
+ * a font. This widget is suitable for placement in a table in a
+ * plug-in dialog.
+ *
+ * Returns: A #GtkWidget that you can use in your UI.
+ */
+GtkWidget *
+gimp_font_select_widget_new (const gchar *title,
+ const gchar *font_name,
+ GimpRunFontCallback callback,
+ gpointer data)
+{
+ GtkWidget *font_button;
+ CompatCallbackData *compat_data;
+
+ g_return_val_if_fail (callback != NULL, NULL);
+
+ font_button = gimp_font_select_button_new (title, font_name);
+
+ compat_data = g_slice_new (CompatCallbackData);
+
+ compat_data->callback = callback;
+ compat_data->data = data;
+
+ g_signal_connect_data (font_button, "font-set",
+ G_CALLBACK (compat_callback),
+ compat_data,
+ (GClosureNotify) compat_callback_data_free, 0);
+
+ return font_button;
+}
+
+/**
+ * gimp_font_select_widget_close:
+ * @widget: A font select widget.
+ *
+ * Closes the popup window associated with @widget.
+ */
+void
+gimp_font_select_widget_close (GtkWidget *widget)
+{
+ g_return_if_fail (widget != NULL);
+
+ gimp_select_button_close_popup (GIMP_SELECT_BUTTON (widget));
+}
+
+/**
+ * gimp_font_select_widget_set:
+ * @widget: A font select widget.
+ * @font_name: Font name to set; %NULL means no change.
+ *
+ * Sets the current font for the font select widget. Calls the
+ * callback function if one was supplied in the call to
+ * gimp_font_select_widget_new().
+ */
+void
+gimp_font_select_widget_set (GtkWidget *widget,
+ const gchar *font_name)
+{
+ g_return_if_fail (widget != NULL);
+
+ gimp_font_select_button_set_font (GIMP_FONT_SELECT_BUTTON (widget),
+ font_name);
+}
+
+
+static void
+compat_callback (GimpFontSelectButton *font_button,
+ const gchar *font_name,
+ gboolean dialog_closing,
+ CompatCallbackData *data)
+{
+ data->callback (font_name, dialog_closing, data->data);
+}
+
+static void
+compat_callback_data_free (CompatCallbackData *data)
+{
+ g_slice_free (CompatCallbackData, data);
+}
diff --git a/libgimp/gimpfontmenu.h b/libgimp/gimpfontmenu.h
new file mode 100644
index 0000000..64b8c8f
--- /dev/null
+++ b/libgimp/gimpfontmenu.h
@@ -0,0 +1,49 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpfontmenu.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_UI_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimpui.h> can be included directly."
+#endif
+
+#ifndef __GIMP_FONT_MENU_H__
+#define __GIMP_FONT_MENU_H__
+
+/* These functions are deprecated and should not be used in newly
+ * written code.
+ */
+
+G_BEGIN_DECLS
+
+GIMP_DEPRECATED_FOR(gimp_font_select_button_new)
+GtkWidget * gimp_font_select_widget_new (const gchar *title,
+ const gchar *font_name,
+ GimpRunFontCallback callback,
+ gpointer data);
+
+GIMP_DEPRECATED_FOR(gimp_select_button_close_popup)
+void gimp_font_select_widget_close (GtkWidget *widget);
+GIMP_DEPRECATED_FOR(gimp_font_select_button_set_font)
+void gimp_font_select_widget_set (GtkWidget *widget,
+ const gchar *font_name);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_FONT_MENU_H__ */
diff --git a/libgimp/gimpfonts_pdb.c b/libgimp/gimpfonts_pdb.c
new file mode 100644
index 0000000..c84b2bd
--- /dev/null
+++ b/libgimp/gimpfonts_pdb.c
@@ -0,0 +1,109 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpfonts_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpfonts
+ * @title: gimpfonts
+ * @short_description: Operations related to fonts.
+ *
+ * Operations related to fonts.
+ **/
+
+
+/**
+ * gimp_fonts_refresh:
+ *
+ * Refresh current fonts. This function always succeeds.
+ *
+ * This procedure retrieves all fonts currently in the user's font path
+ * and updates the font dialogs accordingly. Depending on the amount of
+ * fonts on the system, this can take considerable time.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_fonts_refresh (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-fonts-refresh",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_fonts_get_list:
+ * @filter: An optional regular expression used to filter the list.
+ * @num_fonts: The number of available fonts.
+ *
+ * Retrieve the list of loaded fonts.
+ *
+ * This procedure returns a list of the fonts that are currently
+ * available.
+ *
+ * Returns: The list of font names. The returned value must be freed
+ * with g_strfreev().
+ **/
+gchar **
+gimp_fonts_get_list (const gchar *filter,
+ gint *num_fonts)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar **font_list = NULL;
+ gint i;
+
+ return_vals = gimp_run_procedure ("gimp-fonts-get-list",
+ &nreturn_vals,
+ GIMP_PDB_STRING, filter,
+ GIMP_PDB_END);
+
+ *num_fonts = 0;
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ *num_fonts = return_vals[1].data.d_int32;
+ if (*num_fonts > 0)
+ {
+ font_list = g_new0 (gchar *, *num_fonts + 1);
+ for (i = 0; i < *num_fonts; i++)
+ font_list[i] = g_strdup (return_vals[2].data.d_stringarray[i]);
+ }
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return font_list;
+}
diff --git a/libgimp/gimpfonts_pdb.h b/libgimp/gimpfonts_pdb.h
new file mode 100644
index 0000000..8ef2a7b
--- /dev/null
+++ b/libgimp/gimpfonts_pdb.h
@@ -0,0 +1,42 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpfonts_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_FONTS_PDB_H__
+#define __GIMP_FONTS_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_fonts_refresh (void);
+gchar** gimp_fonts_get_list (const gchar *filter,
+ gint *num_fonts);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_FONTS_PDB_H__ */
diff --git a/libgimp/gimpfontselect.c b/libgimp/gimpfontselect.c
new file mode 100644
index 0000000..2012af6
--- /dev/null
+++ b/libgimp/gimpfontselect.c
@@ -0,0 +1,208 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpfontselect.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+typedef struct
+{
+ gchar *font_callback;
+ guint idle_id;
+ gchar *font_name;
+ GimpRunFontCallback callback;
+ gboolean closing;
+ gpointer data;
+} GimpFontData;
+
+
+/* local function prototypes */
+
+static void gimp_font_data_free (GimpFontData *data);
+
+static void gimp_temp_font_run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+static gboolean gimp_temp_font_run_idle (GimpFontData *font_data);
+
+
+/* private variables */
+
+static GHashTable *gimp_font_select_ht = NULL;
+
+
+/* public functions */
+
+const gchar *
+gimp_font_select_new (const gchar *title,
+ const gchar *font_name,
+ GimpRunFontCallback callback,
+ gpointer data)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_STRING, "str", "String" },
+ { GIMP_PDB_INT32, "dialog-status", "If the dialog was closing "
+ "[0 = No, 1 = Yes]" }
+ };
+
+ gchar *font_callback = gimp_procedural_db_temp_name ();
+
+ gimp_install_temp_proc (font_callback,
+ "Temporary font popup callback procedure",
+ "",
+ "",
+ "",
+ "",
+ NULL,
+ "",
+ GIMP_TEMPORARY,
+ G_N_ELEMENTS (args), 0,
+ args, NULL,
+ gimp_temp_font_run);
+
+ if (gimp_fonts_popup (font_callback, title, font_name))
+ {
+ GimpFontData *font_data;
+
+ gimp_extension_enable (); /* Allow callbacks to be watched */
+
+ /* Now add to hash table so we can find it again */
+ if (! gimp_font_select_ht)
+ {
+ gimp_font_select_ht =
+ g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free,
+ (GDestroyNotify) gimp_font_data_free);
+ }
+
+ font_data = g_slice_new0 (GimpFontData);
+
+ font_data->font_callback = font_callback;
+ font_data->callback = callback;
+ font_data->data = data;
+
+ g_hash_table_insert (gimp_font_select_ht, font_callback, font_data);
+
+ return font_callback;
+ }
+
+ gimp_uninstall_temp_proc (font_callback);
+ g_free (font_callback);
+
+ return NULL;
+}
+
+void
+gimp_font_select_destroy (const gchar *font_callback)
+{
+ GimpFontData *font_data;
+
+ g_return_if_fail (font_callback != NULL);
+ g_return_if_fail (gimp_font_select_ht != NULL);
+
+ font_data = g_hash_table_lookup (gimp_font_select_ht, font_callback);
+
+ if (! font_data)
+ {
+ g_warning ("Can't find internal font data");
+ return;
+ }
+
+ if (font_data->idle_id)
+ g_source_remove (font_data->idle_id);
+
+ g_free (font_data->font_name);
+
+ if (font_data->font_callback)
+ gimp_fonts_close_popup (font_data->font_callback);
+
+ gimp_uninstall_temp_proc (font_callback);
+
+ g_hash_table_remove (gimp_font_select_ht, font_callback);
+}
+
+
+/* private functions */
+
+static void
+gimp_font_data_free (GimpFontData *data)
+{
+ g_slice_free (GimpFontData, data);
+}
+
+static void
+gimp_temp_font_run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpFontData *font_data;
+
+ font_data = g_hash_table_lookup (gimp_font_select_ht, name);
+
+ if (! font_data)
+ {
+ g_warning ("Can't find internal font data");
+ }
+ else
+ {
+ g_free (font_data->font_name);
+
+ font_data->font_name = g_strdup (param[0].data.d_string);
+ font_data->closing = param[1].data.d_int32;
+
+ if (! font_data->idle_id)
+ font_data->idle_id = g_idle_add ((GSourceFunc) gimp_temp_font_run_idle,
+ font_data);
+ }
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_SUCCESS;
+}
+
+static gboolean
+gimp_temp_font_run_idle (GimpFontData *font_data)
+{
+ font_data->idle_id = 0;
+
+ if (font_data->callback)
+ font_data->callback (font_data->font_name,
+ font_data->closing,
+ font_data->data);
+
+ if (font_data->closing)
+ {
+ gchar *font_callback = font_data->font_callback;
+
+ font_data->font_callback = NULL;
+ gimp_font_select_destroy (font_callback);
+ }
+
+ return FALSE;
+}
diff --git a/libgimp/gimpfontselect.h b/libgimp/gimpfontselect.h
new file mode 100644
index 0000000..3adfb1d
--- /dev/null
+++ b/libgimp/gimpfontselect.h
@@ -0,0 +1,45 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpfontselect.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_FONT_SELECT_H__
+#define __GIMP_FONT_SELECT_H__
+
+G_BEGIN_DECLS
+
+
+typedef void (* GimpRunFontCallback) (const gchar *font_name,
+ gboolean dialog_closing,
+ gpointer user_data);
+
+
+const gchar * gimp_font_select_new (const gchar *title,
+ const gchar *font_name,
+ GimpRunFontCallback callback,
+ gpointer data);
+void gimp_font_select_destroy (const gchar *font_callback);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_FONT_SELECT_H__ */
diff --git a/libgimp/gimpfontselect_pdb.c b/libgimp/gimpfontselect_pdb.c
new file mode 100644
index 0000000..14c1218
--- /dev/null
+++ b/libgimp/gimpfontselect_pdb.c
@@ -0,0 +1,131 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpfontselect_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpfontselect
+ * @title: gimpfontselect
+ * @short_description: Functions providing a font selection dialog.
+ *
+ * Functions providing a font selection dialog.
+ **/
+
+
+/**
+ * gimp_fonts_popup:
+ * @font_callback: The callback PDB proc to call when font selection is made.
+ * @popup_title: Title of the font selection dialog.
+ * @initial_font: The name of the font to set as the first selected.
+ *
+ * Invokes the Gimp font selection.
+ *
+ * This procedure opens the font selection dialog.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_fonts_popup (const gchar *font_callback,
+ const gchar *popup_title,
+ const gchar *initial_font)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-fonts-popup",
+ &nreturn_vals,
+ GIMP_PDB_STRING, font_callback,
+ GIMP_PDB_STRING, popup_title,
+ GIMP_PDB_STRING, initial_font,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_fonts_close_popup:
+ * @font_callback: The name of the callback registered for this pop-up.
+ *
+ * Close the font selection dialog.
+ *
+ * This procedure closes an opened font selection dialog.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_fonts_close_popup (const gchar *font_callback)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-fonts-close-popup",
+ &nreturn_vals,
+ GIMP_PDB_STRING, font_callback,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_fonts_set_popup:
+ * @font_callback: The name of the callback registered for this pop-up.
+ * @font_name: The name of the font to set as selected.
+ *
+ * Sets the current font in a font selection dialog.
+ *
+ * Sets the current font in a font selection dialog.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_fonts_set_popup (const gchar *font_callback,
+ const gchar *font_name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-fonts-set-popup",
+ &nreturn_vals,
+ GIMP_PDB_STRING, font_callback,
+ GIMP_PDB_STRING, font_name,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimpfontselect_pdb.h b/libgimp/gimpfontselect_pdb.h
new file mode 100644
index 0000000..1685e35
--- /dev/null
+++ b/libgimp/gimpfontselect_pdb.h
@@ -0,0 +1,45 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpfontselect_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_FONT_SELECT_PDB_H__
+#define __GIMP_FONT_SELECT_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_fonts_popup (const gchar *font_callback,
+ const gchar *popup_title,
+ const gchar *initial_font);
+gboolean gimp_fonts_close_popup (const gchar *font_callback);
+gboolean gimp_fonts_set_popup (const gchar *font_callback,
+ const gchar *font_name);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_FONT_SELECT_PDB_H__ */
diff --git a/libgimp/gimpfontselectbutton.c b/libgimp/gimpfontselectbutton.c
new file mode 100644
index 0000000..287b666
--- /dev/null
+++ b/libgimp/gimpfontselectbutton.c
@@ -0,0 +1,471 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpfontselectbutton.c
+ * Copyright (C) 2003 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "gimp.h"
+
+#include "gimpuitypes.h"
+#include "gimpfontselectbutton.h"
+#include "gimpuimarshal.h"
+
+#include "libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpfontselectbutton
+ * @title: GimpFontSelectButton
+ * @short_description: A button which pops up a font selection dialog.
+ *
+ * A button which pops up a font selection dialog.
+ **/
+
+
+#define GIMP_FONT_SELECT_BUTTON_GET_PRIVATE(obj) ((GimpFontSelectButtonPrivate *) gimp_font_select_button_get_instance_private ((GimpFontSelectButton *) (obj)))
+
+typedef struct _GimpFontSelectButtonPrivate GimpFontSelectButtonPrivate;
+
+struct _GimpFontSelectButtonPrivate
+{
+ gchar *title;
+
+ gchar *font_name; /* local copy */
+
+ GtkWidget *inside;
+ GtkWidget *label;
+};
+
+enum
+{
+ FONT_SET,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_TITLE,
+ PROP_FONT_NAME
+};
+
+
+/* local function prototypes */
+
+static void gimp_font_select_button_finalize (GObject *object);
+
+static void gimp_font_select_button_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_font_select_button_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_font_select_button_clicked (GimpFontSelectButton *button);
+
+static void gimp_font_select_button_callback (const gchar *font_name,
+ gboolean dialog_closing,
+ gpointer user_data);
+
+static void gimp_font_select_drag_data_received (GimpFontSelectButton *button,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time);
+
+static GtkWidget * gimp_font_select_button_create_inside (GimpFontSelectButton *button);
+
+
+static const GtkTargetEntry target = { "application/x-gimp-font-name", 0 };
+
+static guint font_button_signals[LAST_SIGNAL] = { 0 };
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpFontSelectButton, gimp_font_select_button,
+ GIMP_TYPE_SELECT_BUTTON)
+
+
+static void
+gimp_font_select_button_class_init (GimpFontSelectButtonClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpSelectButtonClass *select_button_class = GIMP_SELECT_BUTTON_CLASS (klass);
+
+ object_class->finalize = gimp_font_select_button_finalize;
+ object_class->set_property = gimp_font_select_button_set_property;
+ object_class->get_property = gimp_font_select_button_get_property;
+
+ select_button_class->select_destroy = gimp_font_select_destroy;
+
+ klass->font_set = NULL;
+
+ /**
+ * GimpFontSelectButton:title:
+ *
+ * The title to be used for the font selection popup dialog.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class, PROP_TITLE,
+ g_param_spec_string ("title",
+ "Title",
+ "The title to be used for the font selection popup dialog",
+ _("Font Selection"),
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * GimpFontSelectButton:font-name:
+ *
+ * The name of the currently selected font.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class, PROP_FONT_NAME,
+ g_param_spec_string ("font-name",
+ "Font name",
+ "The name of the currently selected font",
+ "Sans-serif",
+ GIMP_PARAM_READWRITE));
+
+ /**
+ * GimpFontSelectButton::font-set:
+ * @widget: the object which received the signal.
+ * @font_name: the name of the currently selected font.
+ * @dialog_closing: whether the dialog was closed or not.
+ *
+ * The ::font-set signal is emitted when the user selects a font.
+ *
+ * Since: 2.4
+ */
+ font_button_signals[FONT_SET] =
+ g_signal_new ("font-set",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpFontSelectButtonClass, font_set),
+ NULL, NULL,
+ _gimpui_marshal_VOID__STRING_BOOLEAN,
+ G_TYPE_NONE, 2,
+ G_TYPE_STRING,
+ G_TYPE_BOOLEAN);
+}
+
+static void
+gimp_font_select_button_init (GimpFontSelectButton *button)
+{
+ GimpFontSelectButtonPrivate *priv;
+
+ priv = GIMP_FONT_SELECT_BUTTON_GET_PRIVATE (button);
+
+ priv->font_name = NULL;
+
+ priv->inside = gimp_font_select_button_create_inside (button);
+ gtk_container_add (GTK_CONTAINER (button), priv->inside);
+}
+
+/**
+ * gimp_font_select_button_new:
+ * @title: Title of the dialog to use or %NULL to use the default title.
+ * @font_name: Initial font name.
+ *
+ * Creates a new #GtkWidget that completely controls the selection of
+ * a font. This widget is suitable for placement in a table in a
+ * plug-in dialog.
+ *
+ * Returns: A #GtkWidget that you can use in your UI.
+ *
+ * Since: 2.4
+ */
+GtkWidget *
+gimp_font_select_button_new (const gchar *title,
+ const gchar *font_name)
+{
+ GtkWidget *button;
+
+ if (title)
+ button = g_object_new (GIMP_TYPE_FONT_SELECT_BUTTON,
+ "title", title,
+ "font-name", font_name,
+ NULL);
+ else
+ button = g_object_new (GIMP_TYPE_FONT_SELECT_BUTTON,
+ "font-name", font_name,
+ NULL);
+
+ return button;
+}
+
+/**
+ * gimp_font_select_button_get_font:
+ * @button: A #GimpFontSelectButton
+ *
+ * Retrieves the name of currently selected font.
+ *
+ * Returns: an internal copy of the font name which must not be freed.
+ *
+ * Since: 2.4
+ */
+const gchar *
+gimp_font_select_button_get_font (GimpFontSelectButton *button)
+{
+ GimpFontSelectButtonPrivate *priv;
+
+ g_return_val_if_fail (GIMP_IS_FONT_SELECT_BUTTON (button), NULL);
+
+ priv = GIMP_FONT_SELECT_BUTTON_GET_PRIVATE (button);
+ return priv->font_name;
+}
+
+/**
+ * gimp_font_select_button_set_font:
+ * @button: A #GimpFontSelectButton
+ * @font_name: Font name to set; %NULL means no change.
+ *
+ * Sets the current font for the font select button.
+ *
+ * Since: 2.4
+ */
+void
+gimp_font_select_button_set_font (GimpFontSelectButton *button,
+ const gchar *font_name)
+{
+ GimpSelectButton *select_button;
+
+ g_return_if_fail (GIMP_IS_FONT_SELECT_BUTTON (button));
+
+ select_button = GIMP_SELECT_BUTTON (button);
+
+ if (select_button->temp_callback)
+ gimp_fonts_set_popup (select_button->temp_callback, font_name);
+ else
+ gimp_font_select_button_callback (font_name, FALSE, button);
+}
+
+
+/* private functions */
+
+static void
+gimp_font_select_button_finalize (GObject *object)
+{
+ GimpFontSelectButtonPrivate *priv;
+
+ priv = GIMP_FONT_SELECT_BUTTON_GET_PRIVATE (object);
+
+ g_clear_pointer (&priv->font_name, g_free);
+ g_clear_pointer (&priv->title, g_free);
+
+ G_OBJECT_CLASS (gimp_font_select_button_parent_class)->finalize (object);
+}
+
+static void
+gimp_font_select_button_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpFontSelectButton *button = GIMP_FONT_SELECT_BUTTON (object);
+ GimpFontSelectButtonPrivate *priv;
+
+ priv = GIMP_FONT_SELECT_BUTTON_GET_PRIVATE (button);
+
+ switch (property_id)
+ {
+ case PROP_TITLE:
+ priv->title = g_value_dup_string (value);
+ break;
+ case PROP_FONT_NAME:
+ gimp_font_select_button_set_font (button,
+ g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_font_select_button_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpFontSelectButton *button = GIMP_FONT_SELECT_BUTTON (object);
+ GimpFontSelectButtonPrivate *priv;
+
+ priv = GIMP_FONT_SELECT_BUTTON_GET_PRIVATE (button);
+
+ switch (property_id)
+ {
+ case PROP_TITLE:
+ g_value_set_string (value, priv->title);
+ break;
+ case PROP_FONT_NAME:
+ g_value_set_string (value, priv->font_name);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_font_select_button_callback (const gchar *font_name,
+ gboolean dialog_closing,
+ gpointer user_data)
+{
+ GimpFontSelectButton *button;
+ GimpFontSelectButtonPrivate *priv;
+ GimpSelectButton *select_button;
+
+ button = GIMP_FONT_SELECT_BUTTON (user_data);
+
+ priv = GIMP_FONT_SELECT_BUTTON_GET_PRIVATE (button);
+ select_button = GIMP_SELECT_BUTTON (button);
+
+ g_free (priv->font_name);
+ priv->font_name = g_strdup (font_name);
+
+ gtk_label_set_text (GTK_LABEL (priv->label), font_name);
+
+ if (dialog_closing)
+ select_button->temp_callback = NULL;
+
+ g_signal_emit (button, font_button_signals[FONT_SET], 0,
+ font_name, dialog_closing);
+ g_object_notify (G_OBJECT (button), "font-name");
+}
+
+static void
+gimp_font_select_button_clicked (GimpFontSelectButton *button)
+{
+ GimpFontSelectButtonPrivate *priv;
+ GimpSelectButton *select_button;
+
+ priv = GIMP_FONT_SELECT_BUTTON_GET_PRIVATE (button);
+ select_button = GIMP_SELECT_BUTTON (button);
+
+ if (select_button->temp_callback)
+ {
+ /* calling gimp_fonts_set_popup() raises the dialog */
+ gimp_fonts_set_popup (select_button->temp_callback,
+ priv->font_name);
+ }
+ else
+ {
+ select_button->temp_callback =
+ gimp_font_select_new (priv->title, priv->font_name,
+ gimp_font_select_button_callback,
+ button);
+ }
+}
+
+static void
+gimp_font_select_drag_data_received (GimpFontSelectButton *button,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time)
+{
+ gint length = gtk_selection_data_get_length (selection);
+ gchar *str;
+
+ if (gtk_selection_data_get_format (selection) != 8 || length < 1)
+ {
+ g_warning ("%s: received invalid font data", G_STRFUNC);
+ return;
+ }
+
+ str = g_strndup ((const gchar *) gtk_selection_data_get_data (selection),
+ length);
+
+ if (g_utf8_validate (str, -1, NULL))
+ {
+ gint pid;
+ gpointer unused;
+ gint name_offset = 0;
+
+ if (sscanf (str, "%i:%p:%n", &pid, &unused, &name_offset) >= 2 &&
+ pid == gimp_getpid () && name_offset > 0)
+ {
+ gchar *name = str + name_offset;
+
+ gimp_font_select_button_set_font (button, name);
+ }
+ }
+
+ g_free (str);
+}
+
+static GtkWidget *
+gimp_font_select_button_create_inside (GimpFontSelectButton *font_button)
+{
+ GtkWidget *button;
+ GtkWidget *hbox;
+ GtkWidget *image;
+ GimpFontSelectButtonPrivate *priv;
+
+ priv = GIMP_FONT_SELECT_BUTTON_GET_PRIVATE (font_button);
+
+ gtk_widget_push_composite_child ();
+
+ button = gtk_button_new ();
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+ gtk_container_add (GTK_CONTAINER (button), hbox);
+
+ image = gtk_image_new_from_icon_name (GIMP_ICON_FONT,
+ GTK_ICON_SIZE_BUTTON);
+ gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
+
+ priv->label = gtk_label_new (priv->font_name);
+ gtk_box_pack_start (GTK_BOX (hbox), priv->label, TRUE, TRUE, 4);
+
+ gtk_widget_show_all (button);
+
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (gimp_font_select_button_clicked),
+ font_button);
+
+ gtk_drag_dest_set (GTK_WIDGET (button),
+ GTK_DEST_DEFAULT_HIGHLIGHT |
+ GTK_DEST_DEFAULT_MOTION |
+ GTK_DEST_DEFAULT_DROP,
+ &target, 1,
+ GDK_ACTION_COPY);
+
+ g_signal_connect_swapped (button, "drag-data-received",
+ G_CALLBACK (gimp_font_select_drag_data_received),
+ font_button);
+
+ gtk_widget_pop_composite_child ();
+
+ return button;
+}
diff --git a/libgimp/gimpfontselectbutton.h b/libgimp/gimpfontselectbutton.h
new file mode 100644
index 0000000..df413b6
--- /dev/null
+++ b/libgimp/gimpfontselectbutton.h
@@ -0,0 +1,79 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpfontselectbutton.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_UI_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimpui.h> can be included directly."
+#endif
+
+#ifndef __GIMP_FONT_SELECT_BUTTON_H__
+#define __GIMP_FONT_SELECT_BUTTON_H__
+
+#include <libgimp/gimpselectbutton.h>
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+#define GIMP_TYPE_FONT_SELECT_BUTTON (gimp_font_select_button_get_type ())
+#define GIMP_FONT_SELECT_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_FONT_SELECT_BUTTON, GimpFontSelectButton))
+#define GIMP_FONT_SELECT_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_FONT_SELECT_BUTTON, GimpFontSelectButtonClass))
+#define GIMP_IS_FONT_SELECT_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_FONT_SELECT_BUTTON))
+#define GIMP_IS_FONT_SELECT_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_FONT_SELECT_BUTTON))
+#define GIMP_FONT_SELECT_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_FONT_SELECT_BUTTON, GimpFontSelectButtonClass))
+
+
+typedef struct _GimpFontSelectButtonClass GimpFontSelectButtonClass;
+
+struct _GimpFontSelectButton
+{
+ GimpSelectButton parent_instance;
+};
+
+struct _GimpFontSelectButtonClass
+{
+ GimpSelectButtonClass parent_class;
+
+ /* font_set signal is emitted when font is chosen */
+ void (* font_set) (GimpFontSelectButton *button,
+ const gchar *font_name,
+ gboolean dialog_closing);
+
+ /* Padding for future expansion */
+ void (*_gimp_reserved1) (void);
+ void (*_gimp_reserved2) (void);
+ void (*_gimp_reserved3) (void);
+ void (*_gimp_reserved4) (void);
+};
+
+
+GType gimp_font_select_button_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_font_select_button_new (const gchar *title,
+ const gchar *font_name);
+
+const gchar * gimp_font_select_button_get_font (GimpFontSelectButton *button);
+void gimp_font_select_button_set_font (GimpFontSelectButton *button,
+ const gchar *font_name);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_FONT_SELECT_BUTTON_H__ */
diff --git a/libgimp/gimpgimprc.c b/libgimp/gimpgimprc.c
new file mode 100644
index 0000000..15dcde7
--- /dev/null
+++ b/libgimp/gimpgimprc.c
@@ -0,0 +1,60 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2005 Peter Mattis and Spencer Kimball
+ *
+ * gimpgimprc.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * gimp_get_color_configuration:
+ *
+ * Retrieve a copy of the current color management configuration.
+ *
+ * Returns: A copy of the core's #GimpColorConfig. You should unref
+ * this copy if you don't need it any longer.
+ *
+ * Since: 2.4
+ */
+GimpColorConfig *
+gimp_get_color_configuration (void)
+{
+ GimpColorConfig *config;
+ gchar *text;
+ GError *error = NULL;
+
+ text = _gimp_get_color_configuration ();
+
+ g_return_val_if_fail (text != NULL, NULL);
+
+ config = g_object_new (GIMP_TYPE_COLOR_CONFIG, NULL);
+
+ if (! gimp_config_deserialize_string (GIMP_CONFIG (config), text, -1, NULL,
+ &error))
+ {
+ g_warning ("failed to deserialize color configuration: %s",
+ error->message);
+ g_error_free (error);
+ }
+
+ g_free (text);
+
+ return config;
+}
diff --git a/libgimp/gimpgimprc.h b/libgimp/gimpgimprc.h
new file mode 100644
index 0000000..a1326f9
--- /dev/null
+++ b/libgimp/gimpgimprc.h
@@ -0,0 +1,38 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2005 Peter Mattis and Spencer Kimball
+ *
+ * gimpgimprc.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_GIMPRC_H__
+#define __GIMP_GIMPRC_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+GimpColorConfig * gimp_get_color_configuration (void);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_GIMPRC_H__ */
diff --git a/libgimp/gimpgimprc_pdb.c b/libgimp/gimpgimprc_pdb.c
new file mode 100644
index 0000000..499b8fc
--- /dev/null
+++ b/libgimp/gimpgimprc_pdb.c
@@ -0,0 +1,330 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpgimprc_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpgimprc
+ * @title: gimpgimprc
+ * @short_description: Interactions with settings from gimprc.
+ *
+ * Interactions with settings from gimprc.
+ **/
+
+
+/**
+ * gimp_gimprc_query:
+ * @token: The token to query for.
+ *
+ * Queries the gimprc file parser for information on a specified token.
+ *
+ * This procedure is used to locate additional information contained in
+ * the gimprc file considered extraneous to the operation of GIMP.
+ * Plug-ins that need configuration information can expect it will be
+ * stored in the user gimprc file and can use this procedure to
+ * retrieve it. This query procedure will return the value associated
+ * with the specified token. This corresponds _only_ to entries with
+ * the format: (&lt;token&gt; &lt;value&gt;). The value must be a
+ * string. Entries not corresponding to this format will cause warnings
+ * to be issued on gimprc parsing and will not be queryable.
+ *
+ * Returns: The value associated with the queried token.
+ **/
+gchar *
+gimp_gimprc_query (const gchar *token)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *value = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-gimprc-query",
+ &nreturn_vals,
+ GIMP_PDB_STRING, token,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ value = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return value;
+}
+
+/**
+ * gimp_gimprc_set:
+ * @token: The token to add or modify.
+ * @value: The value to set the token to.
+ *
+ * Sets a gimprc token to a value and saves it in the gimprc.
+ *
+ * This procedure is used to add or change additional information in
+ * the gimprc file that is considered extraneous to the operation of
+ * GIMP. Plug-ins that need configuration information can use this
+ * function to store it, and gimp_gimprc_query() to retrieve it. This
+ * will accept _only_ string values in UTF-8 encoding.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_gimprc_set (const gchar *token,
+ const gchar *value)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gimprc-set",
+ &nreturn_vals,
+ GIMP_PDB_STRING, token,
+ GIMP_PDB_STRING, value,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_get_default_comment:
+ *
+ * Get the default image comment as specified in the Preferences.
+ *
+ * Returns a copy of the default image comment.
+ *
+ * Returns: Default image comment.
+ **/
+gchar *
+gimp_get_default_comment (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *comment = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-get-default-comment",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ comment = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return comment;
+}
+
+/**
+ * gimp_get_default_unit:
+ *
+ * Get the default unit (taken from the user's locale).
+ *
+ * Returns the default unit's integer ID.
+ *
+ * Returns: Default unit.
+ *
+ * Since: 2.4
+ **/
+GimpUnit
+gimp_get_default_unit (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpUnit unit_id = 0;
+
+ return_vals = gimp_run_procedure ("gimp-get-default-unit",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ unit_id = return_vals[1].data.d_unit;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return unit_id;
+}
+
+/**
+ * gimp_get_monitor_resolution:
+ * @xres: X resolution.
+ * @yres: Y resolution.
+ *
+ * Get the monitor resolution as specified in the Preferences.
+ *
+ * Returns the resolution of the monitor in pixels/inch. This value is
+ * taken from the Preferences (or the windowing system if this is set
+ * in the Preferences) and there's no guarantee for the value to be
+ * reasonable.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_get_monitor_resolution (gdouble *xres,
+ gdouble *yres)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-get-monitor-resolution",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ *xres = 0.0;
+ *yres = 0.0;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *xres = return_vals[1].data.d_float;
+ *yres = return_vals[2].data.d_float;
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_get_theme_dir:
+ *
+ * Get the directory of the current GUI theme.
+ *
+ * Returns a copy of the current GUI theme dir.
+ *
+ * Deprecated: There is no replacement for this procedure.
+ *
+ * Returns: The GUI theme dir.
+ **/
+gchar *
+gimp_get_theme_dir (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *theme_dir = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-get-theme-dir",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ theme_dir = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return theme_dir;
+}
+
+/**
+ * gimp_get_icon_theme_dir:
+ *
+ * Get the directory of the current icon theme.
+ *
+ * Returns a copy of the current icon theme dir.
+ *
+ * Deprecated: There is no replacement for this procedure.
+ *
+ * Returns: The icon theme dir.
+ *
+ * Since: 2.10
+ **/
+gchar *
+gimp_get_icon_theme_dir (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *icon_theme_dir = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-get-icon-theme-dir",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ icon_theme_dir = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return icon_theme_dir;
+}
+
+/**
+ * _gimp_get_color_configuration:
+ *
+ * Get a serialized version of the color management configuration.
+ *
+ * Returns a string that can be deserialized into a GimpColorConfig
+ * object representing the current color management configuration.
+ *
+ * Returns: Serialized color management configuration.
+ *
+ * Since: 2.4
+ **/
+gchar *
+_gimp_get_color_configuration (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *config = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-get-color-configuration",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ config = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return config;
+}
+
+/**
+ * gimp_get_module_load_inhibit:
+ *
+ * Get the list of modules which should not be loaded.
+ *
+ * Returns a copy of the list of modules which should not be loaded.
+ *
+ * Returns: The list of modules.
+ **/
+gchar *
+gimp_get_module_load_inhibit (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *load_inhibit = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-get-module-load-inhibit",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ load_inhibit = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return load_inhibit;
+}
diff --git a/libgimp/gimpgimprc_pdb.h b/libgimp/gimpgimprc_pdb.h
new file mode 100644
index 0000000..341204f
--- /dev/null
+++ b/libgimp/gimpgimprc_pdb.h
@@ -0,0 +1,52 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpgimprc_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_GIMPRC_PDB_H__
+#define __GIMP_GIMPRC_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gchar* gimp_gimprc_query (const gchar *token);
+gboolean gimp_gimprc_set (const gchar *token,
+ const gchar *value);
+gchar* gimp_get_default_comment (void);
+GimpUnit gimp_get_default_unit (void);
+gboolean gimp_get_monitor_resolution (gdouble *xres,
+ gdouble *yres);
+GIMP_DEPRECATED
+gchar* gimp_get_theme_dir (void);
+GIMP_DEPRECATED
+gchar* gimp_get_icon_theme_dir (void);
+G_GNUC_INTERNAL gchar* _gimp_get_color_configuration (void);
+gchar* gimp_get_module_load_inhibit (void);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_GIMPRC_PDB_H__ */
diff --git a/libgimp/gimpgradient_pdb.c b/libgimp/gimpgradient_pdb.c
new file mode 100644
index 0000000..f47ee8f
--- /dev/null
+++ b/libgimp/gimpgradient_pdb.c
@@ -0,0 +1,1320 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpgradient_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpgradient
+ * @title: gimpgradient
+ * @short_description: Functions operating on a single gradient.
+ *
+ * Functions operating on a single gradient.
+ **/
+
+
+/**
+ * gimp_gradient_new:
+ * @name: The requested name of the new gradient.
+ *
+ * Creates a new gradient
+ *
+ * This procedure creates a new, uninitialized gradient
+ *
+ * Returns: The actual new gradient name.
+ *
+ * Since: 2.2
+ **/
+gchar *
+gimp_gradient_new (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *actual_name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-new",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ actual_name = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return actual_name;
+}
+
+/**
+ * gimp_gradient_duplicate:
+ * @name: The gradient name.
+ *
+ * Duplicates a gradient
+ *
+ * This procedure creates an identical gradient by a different name
+ *
+ * Returns: The name of the gradient's copy.
+ *
+ * Since: 2.2
+ **/
+gchar *
+gimp_gradient_duplicate (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *copy_name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-duplicate",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ copy_name = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return copy_name;
+}
+
+/**
+ * gimp_gradient_is_editable:
+ * @name: The gradient name.
+ *
+ * Tests if gradient can be edited
+ *
+ * Returns TRUE if you have permission to change the gradient
+ *
+ * Returns: TRUE if the gradient can be edited.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_gradient_is_editable (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean editable = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-is-editable",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ editable = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return editable;
+}
+
+/**
+ * gimp_gradient_rename:
+ * @name: The gradient name.
+ * @new_name: The new name of the gradient.
+ *
+ * Rename a gradient
+ *
+ * This procedure renames a gradient
+ *
+ * Returns: The actual new name of the gradient.
+ *
+ * Since: 2.2
+ **/
+gchar *
+gimp_gradient_rename (const gchar *name,
+ const gchar *new_name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *actual_name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-rename",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_STRING, new_name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ actual_name = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return actual_name;
+}
+
+/**
+ * gimp_gradient_delete:
+ * @name: The gradient name.
+ *
+ * Deletes a gradient
+ *
+ * This procedure deletes a gradient
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_gradient_delete (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-delete",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_gradient_get_number_of_segments:
+ * @name: The gradient name.
+ *
+ * Returns the number of segments of the specified gradient
+ *
+ * This procedure returns the number of segments of the specified
+ * gradient.
+ *
+ * Returns: Number of segments.
+ *
+ * Since: 2.6
+ **/
+gint
+gimp_gradient_get_number_of_segments (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint num_segments = 0;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-get-number-of-segments",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ num_segments = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return num_segments;
+}
+
+/**
+ * gimp_gradient_get_uniform_samples:
+ * @name: The gradient name.
+ * @num_samples: The number of samples to take.
+ * @reverse: Use the reverse gradient.
+ * @num_color_samples: Length of the color_samples array (4 * num_samples).
+ * @color_samples: Color samples: { R1, G1, B1, A1, ..., Rn, Gn, Bn, An }.
+ *
+ * Sample the specified in uniform parts.
+ *
+ * This procedure samples the active gradient in the specified number
+ * of uniform parts. It returns a list of floating-point values which
+ * correspond to the RGBA values for each sample. The minimum number of
+ * samples to take is 2, in which case the returned colors will
+ * correspond to the { 0.0, 1.0 } positions in the gradient. For
+ * example, if the number of samples is 3, the procedure will return
+ * the colors at positions { 0.0, 0.5, 1.0 }.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_gradient_get_uniform_samples (const gchar *name,
+ gint num_samples,
+ gboolean reverse,
+ gint *num_color_samples,
+ gdouble **color_samples)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-get-uniform-samples",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, num_samples,
+ GIMP_PDB_INT32, reverse,
+ GIMP_PDB_END);
+
+ *num_color_samples = 0;
+ *color_samples = NULL;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *num_color_samples = return_vals[1].data.d_int32;
+ *color_samples = g_new (gdouble, *num_color_samples);
+ memcpy (*color_samples,
+ return_vals[2].data.d_floatarray,
+ *num_color_samples * sizeof (gdouble));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_gradient_get_custom_samples:
+ * @name: The gradient name.
+ * @num_samples: The number of samples to take.
+ * @positions: The list of positions to sample along the gradient.
+ * @reverse: Use the reverse gradient.
+ * @num_color_samples: Length of the color_samples array (4 * num_samples).
+ * @color_samples: Color samples: { R1, G1, B1, A1, ..., Rn, Gn, Bn, An }.
+ *
+ * Sample the specified gradient in custom positions.
+ *
+ * This procedure samples the active gradient in the specified number
+ * of points. The procedure will sample the gradient in the specified
+ * positions from the list. The left endpoint of the gradient
+ * corresponds to position 0.0, and the right endpoint corresponds to
+ * 1.0. The procedure returns a list of floating-point values which
+ * correspond to the RGBA values for each sample.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_gradient_get_custom_samples (const gchar *name,
+ gint num_samples,
+ const gdouble *positions,
+ gboolean reverse,
+ gint *num_color_samples,
+ gdouble **color_samples)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-get-custom-samples",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, num_samples,
+ GIMP_PDB_FLOATARRAY, positions,
+ GIMP_PDB_INT32, reverse,
+ GIMP_PDB_END);
+
+ *num_color_samples = 0;
+ *color_samples = NULL;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *num_color_samples = return_vals[1].data.d_int32;
+ *color_samples = g_new (gdouble, *num_color_samples);
+ memcpy (*color_samples,
+ return_vals[2].data.d_floatarray,
+ *num_color_samples * sizeof (gdouble));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_gradient_segment_get_left_color:
+ * @name: The gradient name.
+ * @segment: The index of the segment within the gradient.
+ * @color: The return color.
+ * @opacity: The opacity of the endpoint.
+ *
+ * Retrieves the left endpoint color of the specified segment
+ *
+ * This procedure retrieves the left endpoint color of the specified
+ * segment of the specified gradient.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_gradient_segment_get_left_color (const gchar *name,
+ gint segment,
+ GimpRGB *color,
+ gdouble *opacity)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-segment-get-left-color",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, segment,
+ GIMP_PDB_END);
+
+ *opacity = 0.0;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *color = return_vals[1].data.d_color;
+ *opacity = return_vals[2].data.d_float;
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_gradient_segment_set_left_color:
+ * @name: The gradient name.
+ * @segment: The index of the segment within the gradient.
+ * @color: The color to set.
+ * @opacity: The opacity to set for the endpoint.
+ *
+ * Sets the left endpoint color of the specified segment
+ *
+ * This procedure sets the left endpoint color of the specified segment
+ * of the specified gradient.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_gradient_segment_set_left_color (const gchar *name,
+ gint segment,
+ const GimpRGB *color,
+ gdouble opacity)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-segment-set-left-color",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, segment,
+ GIMP_PDB_COLOR, color,
+ GIMP_PDB_FLOAT, opacity,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_gradient_segment_get_right_color:
+ * @name: The gradient name.
+ * @segment: The index of the segment within the gradient.
+ * @color: The return color.
+ * @opacity: The opacity of the endpoint.
+ *
+ * Retrieves the right endpoint color of the specified segment
+ *
+ * This procedure retrieves the right endpoint color of the specified
+ * segment of the specified gradient.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_gradient_segment_get_right_color (const gchar *name,
+ gint segment,
+ GimpRGB *color,
+ gdouble *opacity)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-segment-get-right-color",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, segment,
+ GIMP_PDB_END);
+
+ *opacity = 0.0;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *color = return_vals[1].data.d_color;
+ *opacity = return_vals[2].data.d_float;
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_gradient_segment_set_right_color:
+ * @name: The gradient name.
+ * @segment: The index of the segment within the gradient.
+ * @color: The color to set.
+ * @opacity: The opacity to set for the endpoint.
+ *
+ * Sets the right endpoint color of the specified segment
+ *
+ * This procedure sets the right endpoint color of the specified
+ * segment of the specified gradient.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_gradient_segment_set_right_color (const gchar *name,
+ gint segment,
+ const GimpRGB *color,
+ gdouble opacity)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-segment-set-right-color",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, segment,
+ GIMP_PDB_COLOR, color,
+ GIMP_PDB_FLOAT, opacity,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_gradient_segment_get_left_pos:
+ * @name: The gradient name.
+ * @segment: The index of the segment within the gradient.
+ * @pos: The return position.
+ *
+ * Retrieves the left endpoint position of the specified segment
+ *
+ * This procedure retrieves the left endpoint position of the specified
+ * segment of the specified gradient.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_gradient_segment_get_left_pos (const gchar *name,
+ gint segment,
+ gdouble *pos)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-segment-get-left-pos",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, segment,
+ GIMP_PDB_END);
+
+ *pos = 0.0;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ *pos = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_gradient_segment_set_left_pos:
+ * @name: The gradient name.
+ * @segment: The index of the segment within the gradient.
+ * @pos: The position to set the guidepoint to.
+ * @final_pos: The return position.
+ *
+ * Sets the left endpoint position of the specified segment
+ *
+ * This procedure sets the left endpoint position of the specified
+ * segment of the specified gradient. The final position will be
+ * between the position of the middle point to the left to the middle
+ * point of the current segment.
+ * This procedure returns the final position.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_gradient_segment_set_left_pos (const gchar *name,
+ gint segment,
+ gdouble pos,
+ gdouble *final_pos)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-segment-set-left-pos",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, segment,
+ GIMP_PDB_FLOAT, pos,
+ GIMP_PDB_END);
+
+ *final_pos = 0.0;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ *final_pos = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_gradient_segment_get_middle_pos:
+ * @name: The gradient name.
+ * @segment: The index of the segment within the gradient.
+ * @pos: The return position.
+ *
+ * Retrieves the middle point position of the specified segment
+ *
+ * This procedure retrieves the middle point position of the specified
+ * segment of the specified gradient.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_gradient_segment_get_middle_pos (const gchar *name,
+ gint segment,
+ gdouble *pos)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-segment-get-middle-pos",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, segment,
+ GIMP_PDB_END);
+
+ *pos = 0.0;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ *pos = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_gradient_segment_set_middle_pos:
+ * @name: The gradient name.
+ * @segment: The index of the segment within the gradient.
+ * @pos: The position to set the guidepoint to.
+ * @final_pos: The return position.
+ *
+ * Sets the middle point position of the specified segment
+ *
+ * This procedure sets the middle point position of the specified
+ * segment of the specified gradient. The final position will be
+ * between the two endpoints of the segment.
+ * This procedure returns the final position.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_gradient_segment_set_middle_pos (const gchar *name,
+ gint segment,
+ gdouble pos,
+ gdouble *final_pos)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-segment-set-middle-pos",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, segment,
+ GIMP_PDB_FLOAT, pos,
+ GIMP_PDB_END);
+
+ *final_pos = 0.0;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ *final_pos = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_gradient_segment_get_right_pos:
+ * @name: The gradient name.
+ * @segment: The index of the segment within the gradient.
+ * @pos: The return position.
+ *
+ * Retrieves the right endpoint position of the specified segment
+ *
+ * This procedure retrieves the right endpoint position of the
+ * specified segment of the specified gradient.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_gradient_segment_get_right_pos (const gchar *name,
+ gint segment,
+ gdouble *pos)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-segment-get-right-pos",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, segment,
+ GIMP_PDB_END);
+
+ *pos = 0.0;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ *pos = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_gradient_segment_set_right_pos:
+ * @name: The gradient name.
+ * @segment: The index of the segment within the gradient.
+ * @pos: The position to set the guidepoint to.
+ * @final_pos: The return position.
+ *
+ * Sets the right endpoint position of the specified segment
+ *
+ * This procedure sets the right endpoint position of the specified
+ * segment of the specified gradient. The final position will be
+ * between the position of the middle point of the current segment and
+ * the middle point of the segment to the right.
+ * This procedure returns the final position.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_gradient_segment_set_right_pos (const gchar *name,
+ gint segment,
+ gdouble pos,
+ gdouble *final_pos)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-segment-set-right-pos",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, segment,
+ GIMP_PDB_FLOAT, pos,
+ GIMP_PDB_END);
+
+ *final_pos = 0.0;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ *final_pos = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_gradient_segment_get_blending_function:
+ * @name: The gradient name.
+ * @segment: The index of the segment within the gradient.
+ * @blend_func: The blending function of the segment.
+ *
+ * Retrieves the gradient segment's blending function
+ *
+ * This procedure retrieves the blending function of the segment at the
+ * specified gradient name and segment index.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_gradient_segment_get_blending_function (const gchar *name,
+ gint segment,
+ GimpGradientSegmentType *blend_func)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-segment-get-blending-function",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, segment,
+ GIMP_PDB_END);
+
+ *blend_func = 0;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ *blend_func = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_gradient_segment_get_coloring_type:
+ * @name: The gradient name.
+ * @segment: The index of the segment within the gradient.
+ * @coloring_type: The coloring type of the segment.
+ *
+ * Retrieves the gradient segment's coloring type
+ *
+ * This procedure retrieves the coloring type of the segment at the
+ * specified gradient name and segment index.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_gradient_segment_get_coloring_type (const gchar *name,
+ gint segment,
+ GimpGradientSegmentColor *coloring_type)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-segment-get-coloring-type",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, segment,
+ GIMP_PDB_END);
+
+ *coloring_type = 0;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ *coloring_type = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_gradient_segment_range_set_blending_function:
+ * @name: The gradient name.
+ * @start_segment: The index of the first segment to operate on.
+ * @end_segment: The index of the last segment to operate on. If negative, the selection will extend to the end of the string.
+ * @blending_function: The blending function.
+ *
+ * Change the blending function of a segments range
+ *
+ * This function changes the blending function of a segment range to
+ * the specified blending function.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_gradient_segment_range_set_blending_function (const gchar *name,
+ gint start_segment,
+ gint end_segment,
+ GimpGradientSegmentType blending_function)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-segment-range-set-blending-function",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, start_segment,
+ GIMP_PDB_INT32, end_segment,
+ GIMP_PDB_INT32, blending_function,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_gradient_segment_range_set_coloring_type:
+ * @name: The gradient name.
+ * @start_segment: The index of the first segment to operate on.
+ * @end_segment: The index of the last segment to operate on. If negative, the selection will extend to the end of the string.
+ * @coloring_type: The coloring type.
+ *
+ * Change the coloring type of a segments range
+ *
+ * This function changes the coloring type of a segment range to the
+ * specified coloring type.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_gradient_segment_range_set_coloring_type (const gchar *name,
+ gint start_segment,
+ gint end_segment,
+ GimpGradientSegmentColor coloring_type)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-segment-range-set-coloring-type",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, start_segment,
+ GIMP_PDB_INT32, end_segment,
+ GIMP_PDB_INT32, coloring_type,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_gradient_segment_range_flip:
+ * @name: The gradient name.
+ * @start_segment: The index of the first segment to operate on.
+ * @end_segment: The index of the last segment to operate on. If negative, the selection will extend to the end of the string.
+ *
+ * Flip the segment range
+ *
+ * This function flips a segment range.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_gradient_segment_range_flip (const gchar *name,
+ gint start_segment,
+ gint end_segment)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-segment-range-flip",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, start_segment,
+ GIMP_PDB_INT32, end_segment,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_gradient_segment_range_replicate:
+ * @name: The gradient name.
+ * @start_segment: The index of the first segment to operate on.
+ * @end_segment: The index of the last segment to operate on. If negative, the selection will extend to the end of the string.
+ * @replicate_times: The number of times to replicate.
+ *
+ * Replicate the segment range
+ *
+ * This function replicates a segment range a given number of times.
+ * Instead of the original segment range, several smaller scaled copies
+ * of it will appear in equal widths.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_gradient_segment_range_replicate (const gchar *name,
+ gint start_segment,
+ gint end_segment,
+ gint replicate_times)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-segment-range-replicate",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, start_segment,
+ GIMP_PDB_INT32, end_segment,
+ GIMP_PDB_INT32, replicate_times,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_gradient_segment_range_split_midpoint:
+ * @name: The gradient name.
+ * @start_segment: The index of the first segment to operate on.
+ * @end_segment: The index of the last segment to operate on. If negative, the selection will extend to the end of the string.
+ *
+ * Splits each segment in the segment range at midpoint
+ *
+ * This function splits each segment in the segment range at its
+ * midpoint.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_gradient_segment_range_split_midpoint (const gchar *name,
+ gint start_segment,
+ gint end_segment)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-segment-range-split-midpoint",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, start_segment,
+ GIMP_PDB_INT32, end_segment,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_gradient_segment_range_split_uniform:
+ * @name: The gradient name.
+ * @start_segment: The index of the first segment to operate on.
+ * @end_segment: The index of the last segment to operate on. If negative, the selection will extend to the end of the string.
+ * @split_parts: The number of uniform divisions to split each segment to.
+ *
+ * Splits each segment in the segment range uniformly
+ *
+ * This function splits each segment in the segment range uniformly
+ * according to the number of times specified by the parameter.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_gradient_segment_range_split_uniform (const gchar *name,
+ gint start_segment,
+ gint end_segment,
+ gint split_parts)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-segment-range-split-uniform",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, start_segment,
+ GIMP_PDB_INT32, end_segment,
+ GIMP_PDB_INT32, split_parts,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_gradient_segment_range_delete:
+ * @name: The gradient name.
+ * @start_segment: The index of the first segment to operate on.
+ * @end_segment: The index of the last segment to operate on. If negative, the selection will extend to the end of the string.
+ *
+ * Delete the segment range
+ *
+ * This function deletes a segment range.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_gradient_segment_range_delete (const gchar *name,
+ gint start_segment,
+ gint end_segment)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-segment-range-delete",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, start_segment,
+ GIMP_PDB_INT32, end_segment,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_gradient_segment_range_redistribute_handles:
+ * @name: The gradient name.
+ * @start_segment: The index of the first segment to operate on.
+ * @end_segment: The index of the last segment to operate on. If negative, the selection will extend to the end of the string.
+ *
+ * Uniformly redistribute the segment range's handles
+ *
+ * This function redistributes the handles of the specified segment
+ * range of the specified gradient, so they'll be evenly spaced.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_gradient_segment_range_redistribute_handles (const gchar *name,
+ gint start_segment,
+ gint end_segment)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-segment-range-redistribute-handles",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, start_segment,
+ GIMP_PDB_INT32, end_segment,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_gradient_segment_range_blend_colors:
+ * @name: The gradient name.
+ * @start_segment: The index of the first segment to operate on.
+ * @end_segment: The index of the last segment to operate on. If negative, the selection will extend to the end of the string.
+ *
+ * Blend the colors of the segment range.
+ *
+ * This function blends the colors (but not the opacity) of the
+ * segments' range of the gradient. Using it, the colors' transition
+ * will be uniform across the range.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_gradient_segment_range_blend_colors (const gchar *name,
+ gint start_segment,
+ gint end_segment)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-segment-range-blend-colors",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, start_segment,
+ GIMP_PDB_INT32, end_segment,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_gradient_segment_range_blend_opacity:
+ * @name: The gradient name.
+ * @start_segment: The index of the first segment to operate on.
+ * @end_segment: The index of the last segment to operate on. If negative, the selection will extend to the end of the string.
+ *
+ * Blend the opacity of the segment range.
+ *
+ * This function blends the opacity (but not the colors) of the
+ * segments' range of the gradient. Using it, the opacity's transition
+ * will be uniform across the range.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_gradient_segment_range_blend_opacity (const gchar *name,
+ gint start_segment,
+ gint end_segment)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-segment-range-blend-opacity",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, start_segment,
+ GIMP_PDB_INT32, end_segment,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_gradient_segment_range_move:
+ * @name: The gradient name.
+ * @start_segment: The index of the first segment to operate on.
+ * @end_segment: The index of the last segment to operate on. If negative, the selection will extend to the end of the string.
+ * @delta: The delta to move the segment range.
+ * @control_compress: Whether or not to compress the neighboring segments.
+ *
+ * Move the position of an entire segment range by a delta.
+ *
+ * This function moves the position of an entire segment range by a
+ * delta. The actual delta (which is returned) will be limited by the
+ * control points of the neighboring segments.
+ *
+ * Returns: The final delta by which the range moved.
+ *
+ * Since: 2.2
+ **/
+gdouble
+gimp_gradient_segment_range_move (const gchar *name,
+ gint start_segment,
+ gint end_segment,
+ gdouble delta,
+ gboolean control_compress)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble final_delta = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-gradient-segment-range-move",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, start_segment,
+ GIMP_PDB_INT32, end_segment,
+ GIMP_PDB_FLOAT, delta,
+ GIMP_PDB_INT32, control_compress,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ final_delta = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return final_delta;
+}
diff --git a/libgimp/gimpgradient_pdb.h b/libgimp/gimpgradient_pdb.h
new file mode 100644
index 0000000..e0ea6b3
--- /dev/null
+++ b/libgimp/gimpgradient_pdb.h
@@ -0,0 +1,139 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpgradient_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_GRADIENT_PDB_H__
+#define __GIMP_GRADIENT_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gchar* gimp_gradient_new (const gchar *name);
+gchar* gimp_gradient_duplicate (const gchar *name);
+gboolean gimp_gradient_is_editable (const gchar *name);
+gchar* gimp_gradient_rename (const gchar *name,
+ const gchar *new_name);
+gboolean gimp_gradient_delete (const gchar *name);
+gint gimp_gradient_get_number_of_segments (const gchar *name);
+gboolean gimp_gradient_get_uniform_samples (const gchar *name,
+ gint num_samples,
+ gboolean reverse,
+ gint *num_color_samples,
+ gdouble **color_samples);
+gboolean gimp_gradient_get_custom_samples (const gchar *name,
+ gint num_samples,
+ const gdouble *positions,
+ gboolean reverse,
+ gint *num_color_samples,
+ gdouble **color_samples);
+gboolean gimp_gradient_segment_get_left_color (const gchar *name,
+ gint segment,
+ GimpRGB *color,
+ gdouble *opacity);
+gboolean gimp_gradient_segment_set_left_color (const gchar *name,
+ gint segment,
+ const GimpRGB *color,
+ gdouble opacity);
+gboolean gimp_gradient_segment_get_right_color (const gchar *name,
+ gint segment,
+ GimpRGB *color,
+ gdouble *opacity);
+gboolean gimp_gradient_segment_set_right_color (const gchar *name,
+ gint segment,
+ const GimpRGB *color,
+ gdouble opacity);
+gboolean gimp_gradient_segment_get_left_pos (const gchar *name,
+ gint segment,
+ gdouble *pos);
+gboolean gimp_gradient_segment_set_left_pos (const gchar *name,
+ gint segment,
+ gdouble pos,
+ gdouble *final_pos);
+gboolean gimp_gradient_segment_get_middle_pos (const gchar *name,
+ gint segment,
+ gdouble *pos);
+gboolean gimp_gradient_segment_set_middle_pos (const gchar *name,
+ gint segment,
+ gdouble pos,
+ gdouble *final_pos);
+gboolean gimp_gradient_segment_get_right_pos (const gchar *name,
+ gint segment,
+ gdouble *pos);
+gboolean gimp_gradient_segment_set_right_pos (const gchar *name,
+ gint segment,
+ gdouble pos,
+ gdouble *final_pos);
+gboolean gimp_gradient_segment_get_blending_function (const gchar *name,
+ gint segment,
+ GimpGradientSegmentType *blend_func);
+gboolean gimp_gradient_segment_get_coloring_type (const gchar *name,
+ gint segment,
+ GimpGradientSegmentColor *coloring_type);
+gboolean gimp_gradient_segment_range_set_blending_function (const gchar *name,
+ gint start_segment,
+ gint end_segment,
+ GimpGradientSegmentType blending_function);
+gboolean gimp_gradient_segment_range_set_coloring_type (const gchar *name,
+ gint start_segment,
+ gint end_segment,
+ GimpGradientSegmentColor coloring_type);
+gboolean gimp_gradient_segment_range_flip (const gchar *name,
+ gint start_segment,
+ gint end_segment);
+gboolean gimp_gradient_segment_range_replicate (const gchar *name,
+ gint start_segment,
+ gint end_segment,
+ gint replicate_times);
+gboolean gimp_gradient_segment_range_split_midpoint (const gchar *name,
+ gint start_segment,
+ gint end_segment);
+gboolean gimp_gradient_segment_range_split_uniform (const gchar *name,
+ gint start_segment,
+ gint end_segment,
+ gint split_parts);
+gboolean gimp_gradient_segment_range_delete (const gchar *name,
+ gint start_segment,
+ gint end_segment);
+gboolean gimp_gradient_segment_range_redistribute_handles (const gchar *name,
+ gint start_segment,
+ gint end_segment);
+gboolean gimp_gradient_segment_range_blend_colors (const gchar *name,
+ gint start_segment,
+ gint end_segment);
+gboolean gimp_gradient_segment_range_blend_opacity (const gchar *name,
+ gint start_segment,
+ gint end_segment);
+gdouble gimp_gradient_segment_range_move (const gchar *name,
+ gint start_segment,
+ gint end_segment,
+ gdouble delta,
+ gboolean control_compress);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_GRADIENT_PDB_H__ */
diff --git a/libgimp/gimpgradientmenu.c b/libgimp/gimpgradientmenu.c
new file mode 100644
index 0000000..e3f4ff3
--- /dev/null
+++ b/libgimp/gimpgradientmenu.c
@@ -0,0 +1,148 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpgradientmenu.c
+ * Copyright (C) 1998 Andy Thomas
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "gimp.h"
+
+#include "gimpuitypes.h"
+#include "gimpgradientmenu.h"
+#include "gimpgradientselectbutton.h"
+
+
+/**
+ * SECTION: gimpgradientmenu
+ * @title: gimpgradientmenu
+ * @short_description: A widget for selecting gradients.
+ *
+ * A widget for selecting gradients.
+ **/
+
+
+typedef struct
+{
+ GimpRunGradientCallback callback;
+ gpointer data;
+} CompatCallbackData;
+
+
+static void compat_callback (GimpGradientSelectButton *gradient_button,
+ const gchar *gradient_name,
+ gint width,
+ const gdouble *grad_data,
+ gboolean dialog_closing,
+ CompatCallbackData *data);
+static void compat_callback_data_free (CompatCallbackData *data);
+
+
+/**
+ * gimp_gradient_select_widget_new:
+ * @title: Title of the dialog to use or %NULL to use the default title.
+ * @gradient_name: Initial gradient name.
+ * @callback: A function to call when the selected gradient changes.
+ * @data: A pointer to arbitrary data to be used in the call to @callback.
+ *
+ * Creates a new #GtkWidget that completely controls the selection of
+ * a gradient. This widget is suitable for placement in a table in a
+ * plug-in dialog.
+ *
+ * Returns: A #GtkWidget that you can use in your UI.
+ *
+ * Since: 2.2
+ */
+GtkWidget *
+gimp_gradient_select_widget_new (const gchar *title,
+ const gchar *gradient_name,
+ GimpRunGradientCallback callback,
+ gpointer data)
+{
+ GtkWidget *gradient_button;
+ CompatCallbackData *compat_data;
+
+ g_return_val_if_fail (callback != NULL, NULL);
+
+ gradient_button = gimp_gradient_select_button_new (title, gradient_name);
+
+ compat_data = g_slice_new (CompatCallbackData);
+
+ compat_data->callback = callback;
+ compat_data->data = data;
+
+ g_signal_connect_data (gradient_button, "gradient-set",
+ G_CALLBACK (compat_callback),
+ compat_data,
+ (GClosureNotify) compat_callback_data_free, 0);
+
+ return gradient_button;
+}
+
+/**
+ * gimp_gradient_select_widget_close:
+ * @widget: A gradient select widget.
+ *
+ * Closes the popup window associated with @widget.
+ */
+void
+gimp_gradient_select_widget_close (GtkWidget *widget)
+{
+ g_return_if_fail (widget != NULL);
+
+ gimp_select_button_close_popup (GIMP_SELECT_BUTTON (widget));
+}
+
+/**
+ * gimp_gradient_select_widget_set:
+ * @widget: A gradient select widget.
+ * @gradient_name: Gradient name to set.
+ *
+ * Sets the current gradient for the gradient select widget. Calls the
+ * callback function if one was supplied in the call to
+ * gimp_gradient_select_widget_new().
+ */
+void
+gimp_gradient_select_widget_set (GtkWidget *widget,
+ const gchar *gradient_name)
+{
+ g_return_if_fail (widget != NULL);
+
+ gimp_gradient_select_button_set_gradient (GIMP_GRADIENT_SELECT_BUTTON (widget),
+ gradient_name);
+}
+
+
+static void
+compat_callback (GimpGradientSelectButton *gradient_button,
+ const gchar *gradient_name,
+ gint width,
+ const gdouble *grad_data,
+ gboolean dialog_closing,
+ CompatCallbackData *data)
+{
+ data->callback (gradient_name, width, grad_data, dialog_closing, data->data);
+}
+
+static void
+compat_callback_data_free (CompatCallbackData *data)
+{
+ g_slice_free (CompatCallbackData, data);
+}
diff --git a/libgimp/gimpgradientmenu.h b/libgimp/gimpgradientmenu.h
new file mode 100644
index 0000000..975fab8
--- /dev/null
+++ b/libgimp/gimpgradientmenu.h
@@ -0,0 +1,49 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpgradientmenu.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_UI_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimpui.h> can be included directly."
+#endif
+
+#ifndef __GIMP_GRAIDENT_MENU_H__
+#define __GIMP_GRADIENT_MENU_H__
+
+/* These functions are deprecated and should not be used in newly
+ * written code.
+ */
+
+G_BEGIN_DECLS
+
+GIMP_DEPRECATED_FOR(gimp_gradient_select_button_new)
+GtkWidget * gimp_gradient_select_widget_new (const gchar *title,
+ const gchar *gradient_name,
+ GimpRunGradientCallback callback,
+ gpointer data);
+
+GIMP_DEPRECATED_FOR(gimp_select_button_close_popup)
+void gimp_gradient_select_widget_close (GtkWidget *widget);
+GIMP_DEPRECATED_FOR(gimp_gradient_select_button_set_gradient)
+void gimp_gradient_select_widget_set (GtkWidget *widget,
+ const gchar *gradient_name);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_GRADIENT_MENU_H__ */
diff --git a/libgimp/gimpgradients.c b/libgimp/gimpgradients.c
new file mode 100644
index 0000000..08f7249
--- /dev/null
+++ b/libgimp/gimpgradients.c
@@ -0,0 +1,53 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpgradients.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gimp.h"
+#include "gimpgradients.h"
+
+/**
+ * gimp_gradients_get_gradient:
+ *
+ * This procedure is deprecated! Use gimp_context_get_gradient() instead.
+ *
+ * Returns: The name of the active gradient.
+ */
+gchar *
+gimp_gradients_get_gradient (void)
+{
+ return gimp_context_get_gradient ();
+}
+
+/**
+ * gimp_gradients_set_gradient:
+ * @name: The name of the gradient to set.
+ *
+ * This procedure is deprecated! Use gimp_context_set_gradient() instead.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+gimp_gradients_set_gradient (const gchar *name)
+{
+ return gimp_context_set_gradient (name);
+}
diff --git a/libgimp/gimpgradients.h b/libgimp/gimpgradients.h
new file mode 100644
index 0000000..e1edd43
--- /dev/null
+++ b/libgimp/gimpgradients.h
@@ -0,0 +1,39 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpgradients.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_GRADIENTS_H__
+#define __GIMP_GRADIENTS_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+GIMP_DEPRECATED_FOR(gimp_context_get_gradient)
+gchar * gimp_gradients_get_gradient (void);
+GIMP_DEPRECATED_FOR(gimp_context_set_gradient)
+gboolean gimp_gradients_set_gradient (const gchar *name);
+
+G_END_DECLS
+
+#endif /* __GIMP_GRADIENTS_H__ */
diff --git a/libgimp/gimpgradients_pdb.c b/libgimp/gimpgradients_pdb.c
new file mode 100644
index 0000000..cc211d4
--- /dev/null
+++ b/libgimp/gimpgradients_pdb.c
@@ -0,0 +1,237 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpgradients_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpgradients
+ * @title: gimpgradients
+ * @short_description: Operations related to gradients.
+ *
+ * Operations related to gradients.
+ **/
+
+
+/**
+ * gimp_gradients_refresh:
+ *
+ * Refresh current gradients. This function always succeeds.
+ *
+ * This procedure retrieves all gradients currently in the user's
+ * gradient path and updates the gradient dialogs accordingly.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_gradients_refresh (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gradients-refresh",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_gradients_get_list:
+ * @filter: An optional regular expression used to filter the list.
+ * @num_gradients: The number of loaded gradients.
+ *
+ * Retrieve the list of loaded gradients.
+ *
+ * This procedure returns a list of the gradients that are currently
+ * loaded. You can later use the gimp_context_set_gradient() function
+ * to set the active gradient.
+ *
+ * Returns: The list of gradient names. The returned value must be
+ * freed with g_strfreev().
+ **/
+gchar **
+gimp_gradients_get_list (const gchar *filter,
+ gint *num_gradients)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar **gradient_list = NULL;
+ gint i;
+
+ return_vals = gimp_run_procedure ("gimp-gradients-get-list",
+ &nreturn_vals,
+ GIMP_PDB_STRING, filter,
+ GIMP_PDB_END);
+
+ *num_gradients = 0;
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ *num_gradients = return_vals[1].data.d_int32;
+ if (*num_gradients > 0)
+ {
+ gradient_list = g_new0 (gchar *, *num_gradients + 1);
+ for (i = 0; i < *num_gradients; i++)
+ gradient_list[i] = g_strdup (return_vals[2].data.d_stringarray[i]);
+ }
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return gradient_list;
+}
+
+/**
+ * gimp_gradients_sample_uniform:
+ * @num_samples: The number of samples to take.
+ * @reverse: Use the reverse gradient.
+ *
+ * Deprecated: Use gimp_gradient_get_uniform_samples() instead.
+ *
+ * Returns: Color samples: { R1, G1, B1, A1, ..., Rn, Gn, Bn, An }.
+ **/
+gdouble *
+gimp_gradients_sample_uniform (gint num_samples,
+ gboolean reverse)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble *color_samples = NULL;
+ gint num_color_samples;
+
+ return_vals = gimp_run_procedure ("gimp-gradients-sample-uniform",
+ &nreturn_vals,
+ GIMP_PDB_INT32, num_samples,
+ GIMP_PDB_INT32, reverse,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ num_color_samples = return_vals[1].data.d_int32;
+ color_samples = g_new (gdouble, num_color_samples);
+ memcpy (color_samples,
+ return_vals[2].data.d_floatarray,
+ num_color_samples * sizeof (gdouble));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return color_samples;
+}
+
+/**
+ * gimp_gradients_sample_custom:
+ * @num_samples: The number of samples to take.
+ * @positions: The list of positions to sample along the gradient.
+ * @reverse: Use the reverse gradient.
+ *
+ * Deprecated: Use gimp_gradient_get_custom_samples() instead.
+ *
+ * Returns: Color samples: { R1, G1, B1, A1, ..., Rn, Gn, Bn, An }.
+ **/
+gdouble *
+gimp_gradients_sample_custom (gint num_samples,
+ const gdouble *positions,
+ gboolean reverse)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble *color_samples = NULL;
+ gint num_color_samples;
+
+ return_vals = gimp_run_procedure ("gimp-gradients-sample-custom",
+ &nreturn_vals,
+ GIMP_PDB_INT32, num_samples,
+ GIMP_PDB_FLOATARRAY, positions,
+ GIMP_PDB_INT32, reverse,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ num_color_samples = return_vals[1].data.d_int32;
+ color_samples = g_new (gdouble, num_color_samples);
+ memcpy (color_samples,
+ return_vals[2].data.d_floatarray,
+ num_color_samples * sizeof (gdouble));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return color_samples;
+}
+
+/**
+ * gimp_gradients_get_gradient_data:
+ * @name: The gradient name (\"\" means current active gradient).
+ * @sample_size: Size of the sample to return when the gradient is changed.
+ * @reverse: Use the reverse gradient.
+ * @width: The gradient sample width (r,g,b,a).
+ * @grad_data: The gradient sample data.
+ *
+ * Deprecated: Use gimp_gradient_get_uniform_samples() instead.
+ *
+ * Returns: The gradient name.
+ **/
+gchar *
+gimp_gradients_get_gradient_data (const gchar *name,
+ gint sample_size,
+ gboolean reverse,
+ gint *width,
+ gdouble **grad_data)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *actual_name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-gradients-get-gradient-data",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, sample_size,
+ GIMP_PDB_INT32, reverse,
+ GIMP_PDB_END);
+
+ *width = 0;
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ actual_name = g_strdup (return_vals[1].data.d_string);
+ *width = return_vals[2].data.d_int32;
+ *grad_data = g_new (gdouble, *width);
+ memcpy (*grad_data,
+ return_vals[3].data.d_floatarray,
+ *width * sizeof (gdouble));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return actual_name;
+}
diff --git a/libgimp/gimpgradients_pdb.h b/libgimp/gimpgradients_pdb.h
new file mode 100644
index 0000000..f869f47
--- /dev/null
+++ b/libgimp/gimpgradients_pdb.h
@@ -0,0 +1,55 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpgradients_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_GRADIENTS_PDB_H__
+#define __GIMP_GRADIENTS_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_gradients_refresh (void);
+gchar** gimp_gradients_get_list (const gchar *filter,
+ gint *num_gradients);
+GIMP_DEPRECATED_FOR(gimp_gradient_get_uniform_samples)
+gdouble* gimp_gradients_sample_uniform (gint num_samples,
+ gboolean reverse);
+GIMP_DEPRECATED_FOR(gimp_gradient_get_custom_samples)
+gdouble* gimp_gradients_sample_custom (gint num_samples,
+ const gdouble *positions,
+ gboolean reverse);
+GIMP_DEPRECATED_FOR(gimp_gradient_get_uniform_samples)
+gchar* gimp_gradients_get_gradient_data (const gchar *name,
+ gint sample_size,
+ gboolean reverse,
+ gint *width,
+ gdouble **grad_data);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_GRADIENTS_PDB_H__ */
diff --git a/libgimp/gimpgradientselect.c b/libgimp/gimpgradientselect.c
new file mode 100644
index 0000000..28e5de1
--- /dev/null
+++ b/libgimp/gimpgradientselect.c
@@ -0,0 +1,225 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpgradientselect.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+typedef struct
+{
+ gchar *gradient_callback;
+ guint idle_id;
+ gchar *gradient_name;
+ gint width;
+ gdouble *gradient_data;
+ GimpRunGradientCallback callback;
+ gboolean closing;
+ gpointer data;
+} GimpGradientData;
+
+
+/* local function prototypes */
+
+static void gimp_gradient_data_free (GimpGradientData *data);
+
+static void gimp_temp_gradient_run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+static gboolean gimp_temp_gradient_run_idle (GimpGradientData *gradient_data);
+
+
+/* private variables */
+
+static GHashTable *gimp_gradient_select_ht = NULL;
+
+
+/* public functions */
+
+const gchar *
+gimp_gradient_select_new (const gchar *title,
+ const gchar *gradient_name,
+ gint sample_size,
+ GimpRunGradientCallback callback,
+ gpointer data)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_STRING, "str", "String" },
+ { GIMP_PDB_INT32, "gradient-width", "Gradient width" },
+ { GIMP_PDB_FLOATARRAY,"gradient-data", "The gradient mask data" },
+ { GIMP_PDB_INT32, "dialog-status", "If the dialog was closing "
+ "[0 = No, 1 = Yes]" }
+ };
+
+ gchar *gradient_callback = gimp_procedural_db_temp_name ();
+
+ gimp_install_temp_proc (gradient_callback,
+ "Temporary gradient popup callback procedure",
+ "",
+ "",
+ "",
+ "",
+ NULL,
+ "",
+ GIMP_TEMPORARY,
+ G_N_ELEMENTS (args), 0,
+ args, NULL,
+ gimp_temp_gradient_run);
+
+ if (gimp_gradients_popup (gradient_callback, title, gradient_name,
+ sample_size))
+ {
+ GimpGradientData *gradient_data;
+
+ gimp_extension_enable (); /* Allow callbacks to be watched */
+
+ /* Now add to hash table so we can find it again */
+ if (! gimp_gradient_select_ht)
+ {
+ gimp_gradient_select_ht =
+ g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free,
+ (GDestroyNotify) gimp_gradient_data_free);
+ }
+
+ gradient_data = g_slice_new0 (GimpGradientData);
+
+ gradient_data->gradient_callback = gradient_callback;
+ gradient_data->callback = callback;
+ gradient_data->data = data;
+
+ g_hash_table_insert (gimp_gradient_select_ht,
+ gradient_callback, gradient_data);
+
+ return gradient_callback;
+ }
+
+ gimp_uninstall_temp_proc (gradient_callback);
+ g_free (gradient_callback);
+
+ return NULL;
+}
+
+void
+gimp_gradient_select_destroy (const gchar *gradient_callback)
+{
+ GimpGradientData *gradient_data;
+
+ g_return_if_fail (gradient_callback != NULL);
+ g_return_if_fail (gimp_gradient_select_ht != NULL);
+
+ gradient_data = g_hash_table_lookup (gimp_gradient_select_ht,
+ gradient_callback);
+
+ if (! gradient_data)
+ {
+ g_warning ("Can't find internal gradient data");
+ return;
+ }
+
+ if (gradient_data->idle_id)
+ g_source_remove (gradient_data->idle_id);
+
+ g_free (gradient_data->gradient_name);
+ g_free (gradient_data->gradient_data);
+
+ if (gradient_data->gradient_callback)
+ gimp_gradients_close_popup (gradient_data->gradient_callback);
+
+ gimp_uninstall_temp_proc (gradient_callback);
+
+ g_hash_table_remove (gimp_gradient_select_ht, gradient_callback);
+}
+
+
+/* private functions */
+
+static void
+gimp_gradient_data_free (GimpGradientData *data)
+{
+ g_slice_free (GimpGradientData, data);
+}
+
+static void
+gimp_temp_gradient_run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpGradientData *gradient_data;
+
+ gradient_data = g_hash_table_lookup (gimp_gradient_select_ht, name);
+
+ if (! gradient_data)
+ {
+ g_warning ("Can't find internal gradient data");
+ }
+ else
+ {
+ g_free (gradient_data->gradient_name);
+ g_free (gradient_data->gradient_data);
+
+ gradient_data->gradient_name = g_strdup (param[0].data.d_string);
+ gradient_data->width = param[1].data.d_int32;
+ gradient_data->gradient_data = g_memdup (param[2].data.d_floatarray,
+ param[1].data.d_int32 *
+ sizeof (gdouble));
+ gradient_data->closing = param[3].data.d_int32;
+
+ if (! gradient_data->idle_id)
+ gradient_data->idle_id =
+ g_idle_add ((GSourceFunc) gimp_temp_gradient_run_idle,
+ gradient_data);
+ }
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_SUCCESS;
+}
+
+static gboolean
+gimp_temp_gradient_run_idle (GimpGradientData *gradient_data)
+{
+ gradient_data->idle_id = 0;
+
+ if (gradient_data->callback)
+ gradient_data->callback (gradient_data->gradient_name,
+ gradient_data->width,
+ gradient_data->gradient_data,
+ gradient_data->closing,
+ gradient_data->data);
+
+ if (gradient_data->closing)
+ {
+ gchar *gradient_callback = gradient_data->gradient_callback;
+
+ gradient_data->gradient_callback = NULL;
+ gimp_gradient_select_destroy (gradient_callback);
+ }
+
+ return FALSE;
+}
diff --git a/libgimp/gimpgradientselect.h b/libgimp/gimpgradientselect.h
new file mode 100644
index 0000000..15063cc
--- /dev/null
+++ b/libgimp/gimpgradientselect.h
@@ -0,0 +1,48 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpgradientselect.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_GRADIENT_SELECT_H__
+#define __GIMP_GRADIENT_SELECT_H__
+
+G_BEGIN_DECLS
+
+
+typedef void (* GimpRunGradientCallback) (const gchar *gradient_name,
+ gint width,
+ const gdouble *grad_data,
+ gboolean dialog_closing,
+ gpointer user_data);
+
+
+const gchar * gimp_gradient_select_new (const gchar *title,
+ const gchar *gradient_name,
+ gint sample_size,
+ GimpRunGradientCallback callback,
+ gpointer data);
+void gimp_gradient_select_destroy (const gchar *gradient_callback);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_GRADIENT_SELECT_H__ */
diff --git a/libgimp/gimpgradientselect_pdb.c b/libgimp/gimpgradientselect_pdb.c
new file mode 100644
index 0000000..86720d1
--- /dev/null
+++ b/libgimp/gimpgradientselect_pdb.c
@@ -0,0 +1,134 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpgradientselect_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpgradientselect
+ * @title: gimpgradientselect
+ * @short_description: Functions providing a gradient selection dialog.
+ *
+ * Functions providing a gradient selection dialog.
+ **/
+
+
+/**
+ * gimp_gradients_popup:
+ * @gradient_callback: The callback PDB proc to call when gradient selection is made.
+ * @popup_title: Title of the gradient selection dialog.
+ * @initial_gradient: The name of the gradient to set as the first selected.
+ * @sample_size: Size of the sample to return when the gradient is changed.
+ *
+ * Invokes the Gimp gradients selection.
+ *
+ * This procedure opens the gradient selection dialog.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_gradients_popup (const gchar *gradient_callback,
+ const gchar *popup_title,
+ const gchar *initial_gradient,
+ gint sample_size)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gradients-popup",
+ &nreturn_vals,
+ GIMP_PDB_STRING, gradient_callback,
+ GIMP_PDB_STRING, popup_title,
+ GIMP_PDB_STRING, initial_gradient,
+ GIMP_PDB_INT32, sample_size,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_gradients_close_popup:
+ * @gradient_callback: The name of the callback registered for this pop-up.
+ *
+ * Close the gradient selection dialog.
+ *
+ * This procedure closes an opened gradient selection dialog.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_gradients_close_popup (const gchar *gradient_callback)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gradients-close-popup",
+ &nreturn_vals,
+ GIMP_PDB_STRING, gradient_callback,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_gradients_set_popup:
+ * @gradient_callback: The name of the callback registered for this pop-up.
+ * @gradient_name: The name of the gradient to set as selected.
+ *
+ * Sets the current gradient in a gradient selection dialog.
+ *
+ * Sets the current gradient in a gradient selection dialog.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_gradients_set_popup (const gchar *gradient_callback,
+ const gchar *gradient_name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-gradients-set-popup",
+ &nreturn_vals,
+ GIMP_PDB_STRING, gradient_callback,
+ GIMP_PDB_STRING, gradient_name,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimpgradientselect_pdb.h b/libgimp/gimpgradientselect_pdb.h
new file mode 100644
index 0000000..e3b8593
--- /dev/null
+++ b/libgimp/gimpgradientselect_pdb.h
@@ -0,0 +1,46 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpgradientselect_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_GRADIENT_SELECT_PDB_H__
+#define __GIMP_GRADIENT_SELECT_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_gradients_popup (const gchar *gradient_callback,
+ const gchar *popup_title,
+ const gchar *initial_gradient,
+ gint sample_size);
+gboolean gimp_gradients_close_popup (const gchar *gradient_callback);
+gboolean gimp_gradients_set_popup (const gchar *gradient_callback,
+ const gchar *gradient_name);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_GRADIENT_SELECT_PDB_H__ */
diff --git a/libgimp/gimpgradientselectbutton.c b/libgimp/gimpgradientselectbutton.c
new file mode 100644
index 0000000..fac6e3a
--- /dev/null
+++ b/libgimp/gimpgradientselectbutton.c
@@ -0,0 +1,628 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpgradientselectbutton.c
+ * Copyright (C) 1998 Andy Thomas
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "gimp.h"
+
+#include "gimpuitypes.h"
+#include "gimpgradientselectbutton.h"
+#include "gimpuimarshal.h"
+
+#include "libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpgradientselectbutton
+ * @title: GimpGradientSelectButton
+ * @short_description: A button which pops up a gradient select dialog.
+ *
+ * A button which pops up a gradient select dialog.
+ **/
+
+
+#define CELL_HEIGHT 18
+#define CELL_WIDTH 84
+
+
+#define GIMP_GRADIENT_SELECT_BUTTON_GET_PRIVATE(obj) ((GimpGradientSelectButtonPrivate *) gimp_gradient_select_button_get_instance_private ((GimpGradientSelectButton *) (obj)))
+
+typedef struct _GimpGradientSelectButtonPrivate GimpGradientSelectButtonPrivate;
+
+struct _GimpGradientSelectButtonPrivate
+{
+ gchar *title;
+
+ gchar *gradient_name; /* Local copy */
+ gint sample_size;
+ gboolean reverse;
+ gint n_samples;
+ gdouble *gradient_data; /* Local copy */
+
+ GtkWidget *inside;
+ GtkWidget *preview;
+};
+
+enum
+{
+ GRADIENT_SET,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_TITLE,
+ PROP_GRADIENT_NAME
+};
+
+
+/* local function prototypes */
+
+static void gimp_gradient_select_button_finalize (GObject *object);
+
+static void gimp_gradient_select_button_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_gradient_select_button_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_gradient_select_button_clicked (GimpGradientSelectButton *button);
+
+static void gimp_gradient_select_button_callback (const gchar *gradient_name,
+ gint n_samples,
+ const gdouble *gradient_data,
+ gboolean dialog_closing,
+ gpointer user_data);
+
+static void gimp_gradient_select_preview_size_allocate
+ (GtkWidget *widget,
+ GtkAllocation *allocation,
+ GimpGradientSelectButton *button);
+static gboolean gimp_gradient_select_preview_expose (GtkWidget *preview,
+ GdkEventExpose *event,
+ GimpGradientSelectButton *button);
+
+static void gimp_gradient_select_drag_data_received (GimpGradientSelectButton *button,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time);
+
+static GtkWidget * gimp_gradient_select_button_create_inside (GimpGradientSelectButton *button);
+
+
+static const GtkTargetEntry target = { "application/x-gimp-gradient-name", 0 };
+
+static guint gradient_button_signals[LAST_SIGNAL] = { 0 };
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpGradientSelectButton,
+ gimp_gradient_select_button,
+ GIMP_TYPE_SELECT_BUTTON)
+
+
+static void
+gimp_gradient_select_button_class_init (GimpGradientSelectButtonClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpSelectButtonClass *select_button_class = GIMP_SELECT_BUTTON_CLASS (klass);
+
+ object_class->finalize = gimp_gradient_select_button_finalize;
+ object_class->set_property = gimp_gradient_select_button_set_property;
+ object_class->get_property = gimp_gradient_select_button_get_property;
+
+ select_button_class->select_destroy = gimp_gradient_select_destroy;
+
+ klass->gradient_set = NULL;
+
+ /**
+ * GimpGradientSelectButton:title:
+ *
+ * The title to be used for the gradient selection popup dialog.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class, PROP_TITLE,
+ g_param_spec_string ("title",
+ "Title",
+ "The title to be used for the gradient selection popup dialog",
+ _("Gradient Selection"),
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * GimpGradientSelectButton:gradient-name:
+ *
+ * The name of the currently selected gradient.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class, PROP_GRADIENT_NAME,
+ g_param_spec_string ("gradient-name",
+ "Gradient name",
+ "The name of the currently selected gradient",
+ NULL,
+ GIMP_PARAM_READWRITE));
+
+ /**
+ * GimpGradientSelectButton::gradient-set:
+ * @widget: the object which received the signal.
+ * @gradient_name: the name of the currently selected gradient.
+ * @width: width of the gradient
+ * @grad_data: gradient data
+ * @dialog_closing: whether the dialog was closed or not.
+ *
+ * The ::gradient-set signal is emitted when the user selects a gradient.
+ *
+ * Since: 2.4
+ */
+ gradient_button_signals[GRADIENT_SET] =
+ g_signal_new ("gradient-set",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpGradientSelectButtonClass, gradient_set),
+ NULL, NULL,
+ _gimpui_marshal_VOID__STRING_INT_POINTER_BOOLEAN,
+ G_TYPE_NONE, 4,
+ G_TYPE_STRING,
+ G_TYPE_INT,
+ G_TYPE_POINTER,
+ G_TYPE_BOOLEAN);
+}
+
+static void
+gimp_gradient_select_button_init (GimpGradientSelectButton *button)
+{
+ GimpGradientSelectButtonPrivate *priv;
+
+ priv = GIMP_GRADIENT_SELECT_BUTTON_GET_PRIVATE (button);
+
+ priv->gradient_name = gimp_context_get_gradient ();
+ priv->sample_size = CELL_WIDTH;
+ priv->reverse = FALSE;
+
+ priv->inside = gimp_gradient_select_button_create_inside (button);
+ gtk_container_add (GTK_CONTAINER (button), priv->inside);
+}
+
+/**
+ * gimp_gradient_select_button_new:
+ * @title: Title of the dialog to use or %NULL to use the default title.
+ * @gradient_name: Initial gradient name.
+ *
+ * Creates a new #GtkWidget that completely controls the selection of
+ * a gradient. This widget is suitable for placement in a table in a
+ * plug-in dialog.
+ *
+ * Returns: A #GtkWidget that you can use in your UI.
+ *
+ * Since: 2.4
+ */
+GtkWidget *
+gimp_gradient_select_button_new (const gchar *title,
+ const gchar *gradient_name)
+{
+ GtkWidget *button;
+
+ if (title)
+ button = g_object_new (GIMP_TYPE_GRADIENT_SELECT_BUTTON,
+ "title", title,
+ "gradient-name", gradient_name,
+ NULL);
+ else
+ button = g_object_new (GIMP_TYPE_GRADIENT_SELECT_BUTTON,
+ "gradient-name", gradient_name,
+ NULL);
+
+ return button;
+}
+
+/**
+ * gimp_gradient_select_button_get_gradient:
+ * @button: A #GimpGradientSelectButton
+ *
+ * Retrieves the name of currently selected gradient.
+ *
+ * Returns: an internal copy of the gradient name which must not be freed.
+ *
+ * Since: 2.4
+ */
+const gchar *
+gimp_gradient_select_button_get_gradient (GimpGradientSelectButton *button)
+{
+ GimpGradientSelectButtonPrivate *priv;
+
+ g_return_val_if_fail (GIMP_IS_GRADIENT_SELECT_BUTTON (button), NULL);
+
+ priv = GIMP_GRADIENT_SELECT_BUTTON_GET_PRIVATE (button);
+ return priv->gradient_name;
+}
+
+/**
+ * gimp_gradient_select_button_set_gradient:
+ * @button: A #GimpGradientSelectButton
+ * @gradient_name: Gradient name to set.
+ *
+ * Sets the current gradient for the gradient select button.
+ *
+ * Since: 2.4
+ */
+void
+gimp_gradient_select_button_set_gradient (GimpGradientSelectButton *button,
+ const gchar *gradient_name)
+{
+ GimpGradientSelectButtonPrivate *priv;
+ GimpSelectButton *select_button;
+
+ g_return_if_fail (GIMP_IS_GRADIENT_SELECT_BUTTON (button));
+
+ priv = GIMP_GRADIENT_SELECT_BUTTON_GET_PRIVATE (button);
+ select_button = GIMP_SELECT_BUTTON (button);
+
+ if (select_button->temp_callback)
+ {
+ gimp_gradients_set_popup (select_button->temp_callback, gradient_name);
+ }
+ else
+ {
+ gchar *name;
+ gdouble *samples;
+ gint n_samples;
+
+ if (gradient_name && *gradient_name)
+ name = g_strdup (gradient_name);
+ else
+ name = gimp_context_get_gradient ();
+
+ if (gimp_gradient_get_uniform_samples (name,
+ priv->sample_size,
+ priv->reverse,
+ &n_samples,
+ &samples))
+ {
+ gimp_gradient_select_button_callback (name,
+ n_samples, samples,
+ FALSE, button);
+
+ g_free (samples);
+ }
+
+ g_free (name);
+ }
+}
+
+
+/* private functions */
+
+static void
+gimp_gradient_select_button_finalize (GObject *object)
+{
+ GimpGradientSelectButtonPrivate *priv;
+
+ priv = GIMP_GRADIENT_SELECT_BUTTON_GET_PRIVATE (object);
+
+ g_clear_pointer (&priv->gradient_name, g_free);
+ g_clear_pointer (&priv->gradient_data, g_free);
+ g_clear_pointer (&priv->title, g_free);
+
+ G_OBJECT_CLASS (gimp_gradient_select_button_parent_class)->finalize (object);
+}
+
+static void
+gimp_gradient_select_button_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpGradientSelectButton *button;
+ GimpGradientSelectButtonPrivate *priv;
+
+ button = GIMP_GRADIENT_SELECT_BUTTON (object);
+ priv = GIMP_GRADIENT_SELECT_BUTTON_GET_PRIVATE (button);
+
+ switch (property_id)
+ {
+ case PROP_TITLE:
+ priv->title = g_value_dup_string (value);
+ break;
+ case PROP_GRADIENT_NAME:
+ gimp_gradient_select_button_set_gradient (button,
+ g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_gradient_select_button_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpGradientSelectButton *button;
+ GimpGradientSelectButtonPrivate *priv;
+
+ button = GIMP_GRADIENT_SELECT_BUTTON (object);
+ priv = GIMP_GRADIENT_SELECT_BUTTON_GET_PRIVATE (button);
+
+ switch (property_id)
+ {
+ case PROP_TITLE:
+ g_value_set_string (value, priv->title);
+ break;
+ case PROP_GRADIENT_NAME:
+ g_value_set_string (value, priv->gradient_name);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_gradient_select_button_callback (const gchar *gradient_name,
+ gint n_samples,
+ const gdouble *gradient_data,
+ gboolean dialog_closing,
+ gpointer user_data)
+{
+ GimpGradientSelectButton *button;
+ GimpGradientSelectButtonPrivate *priv;
+ GimpSelectButton *select_button;
+
+ button = GIMP_GRADIENT_SELECT_BUTTON (user_data);
+
+ priv = GIMP_GRADIENT_SELECT_BUTTON_GET_PRIVATE (button);
+ select_button = GIMP_SELECT_BUTTON (button);
+
+ g_free (priv->gradient_name);
+ g_free (priv->gradient_data);
+
+ priv->gradient_name = g_strdup (gradient_name);
+ priv->n_samples = n_samples;
+ priv->gradient_data = g_memdup (gradient_data, n_samples * sizeof (gdouble));
+
+ gtk_widget_queue_draw (priv->preview);
+
+ if (dialog_closing)
+ select_button->temp_callback = NULL;
+
+ g_signal_emit (button, gradient_button_signals[GRADIENT_SET], 0,
+ gradient_name, n_samples, gradient_data, dialog_closing);
+ g_object_notify (G_OBJECT (button), "gradient-name");
+}
+
+static void
+gimp_gradient_select_button_clicked (GimpGradientSelectButton *button)
+{
+ GimpGradientSelectButtonPrivate *priv;
+ GimpSelectButton *select_button;
+
+ priv = GIMP_GRADIENT_SELECT_BUTTON_GET_PRIVATE (button);
+ select_button = GIMP_SELECT_BUTTON (button);
+
+ if (select_button->temp_callback)
+ {
+ /* calling gimp_gradients_set_popup() raises the dialog */
+ gimp_gradients_set_popup (select_button->temp_callback,
+ priv->gradient_name);
+ }
+ else
+ {
+ select_button->temp_callback =
+ gimp_gradient_select_new (priv->title, priv->gradient_name,
+ priv->sample_size,
+ gimp_gradient_select_button_callback,
+ button);
+ }
+}
+
+static void
+gimp_gradient_select_preview_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation,
+ GimpGradientSelectButton *button)
+{
+ gdouble *samples;
+ gint n_samples;
+ GimpGradientSelectButtonPrivate *priv;
+
+ priv = GIMP_GRADIENT_SELECT_BUTTON_GET_PRIVATE (button);
+
+ if (gimp_gradient_get_uniform_samples (priv->gradient_name,
+ allocation->width,
+ priv->reverse,
+ &n_samples,
+ &samples))
+ {
+ g_free (priv->gradient_data);
+
+ priv->sample_size = allocation->width;
+ priv->n_samples = n_samples;
+ priv->gradient_data = samples;
+ }
+}
+
+static gboolean
+gimp_gradient_select_preview_expose (GtkWidget *widget,
+ GdkEventExpose *event,
+ GimpGradientSelectButton *button)
+{
+ GimpGradientSelectButtonPrivate *priv;
+ GtkAllocation allocation;
+ cairo_t *cr;
+ cairo_pattern_t *pattern;
+ cairo_surface_t *surface;
+ const gdouble *src;
+ guchar *dest;
+ gint width;
+ gint x;
+
+ priv = GIMP_GRADIENT_SELECT_BUTTON_GET_PRIVATE (button);
+
+ src = priv->gradient_data;
+ if (! src)
+ return FALSE;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ cr = gdk_cairo_create (event->window);
+
+ gdk_cairo_region (cr, event->region);
+ cairo_clip (cr);
+
+ pattern = gimp_cairo_checkerboard_create (cr, GIMP_CHECK_SIZE_SM, NULL, NULL);
+ cairo_set_source (cr, pattern);
+ cairo_pattern_destroy (pattern);
+
+ cairo_paint (cr);
+
+ width = priv->n_samples / 4;
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, 1);
+
+ for (x = 0, dest = cairo_image_surface_get_data (surface);
+ x < width;
+ x++, src += 4, dest += 4)
+ {
+ GimpRGB color;
+ guchar r, g, b, a;
+
+ gimp_rgba_set (&color, src[0], src[1], src[2], src[3]);
+ gimp_rgba_get_uchar (&color, &r, &g, &b, &a);
+
+ GIMP_CAIRO_ARGB32_SET_PIXEL(dest, r, g, b, a);
+ }
+
+ cairo_surface_mark_dirty (surface);
+
+ pattern = cairo_pattern_create_for_surface (surface);
+ cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REFLECT);
+ cairo_surface_destroy (surface);
+
+ cairo_scale (cr, (gdouble) allocation.width / (gdouble) width, 1.0);
+
+ cairo_set_source (cr, pattern);
+ cairo_pattern_destroy (pattern);
+
+ cairo_paint (cr);
+
+ cairo_destroy (cr);
+
+ return FALSE;
+}
+
+static void
+gimp_gradient_select_drag_data_received (GimpGradientSelectButton *button,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time)
+{
+ gint length = gtk_selection_data_get_length (selection);
+ gchar *str;
+
+ if (gtk_selection_data_get_format (selection) != 8 || length < 1)
+ {
+ g_warning ("%s: received invalid gradient data", G_STRFUNC);
+ return;
+ }
+
+ str = g_strndup ((const gchar *) gtk_selection_data_get_data (selection),
+ length);
+
+ if (g_utf8_validate (str, -1, NULL))
+ {
+ gint pid;
+ gpointer unused;
+ gint name_offset = 0;
+
+ if (sscanf (str, "%i:%p:%n", &pid, &unused, &name_offset) >= 2 &&
+ pid == gimp_getpid () && name_offset > 0)
+ {
+ gchar *name = str + name_offset;
+
+ gimp_gradient_select_button_set_gradient (button, name);
+ }
+ }
+
+ g_free (str);
+}
+
+static GtkWidget *
+gimp_gradient_select_button_create_inside (GimpGradientSelectButton *gradient_button)
+{
+ GtkWidget *button;
+ GimpGradientSelectButtonPrivate *priv;
+
+ priv = GIMP_GRADIENT_SELECT_BUTTON_GET_PRIVATE (gradient_button);
+
+ gtk_widget_push_composite_child ();
+
+ button = gtk_button_new ();
+
+ priv->preview = gtk_drawing_area_new ();
+ gtk_widget_set_size_request (priv->preview, CELL_WIDTH, CELL_HEIGHT);
+ gtk_container_add (GTK_CONTAINER (button), priv->preview);
+
+ g_signal_connect (priv->preview, "size-allocate",
+ G_CALLBACK (gimp_gradient_select_preview_size_allocate),
+ gradient_button);
+
+ g_signal_connect (priv->preview, "expose-event",
+ G_CALLBACK (gimp_gradient_select_preview_expose),
+ gradient_button);
+
+ gtk_widget_show_all (button);
+
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (gimp_gradient_select_button_clicked),
+ gradient_button);
+
+ gtk_drag_dest_set (GTK_WIDGET (button),
+ GTK_DEST_DEFAULT_HIGHLIGHT |
+ GTK_DEST_DEFAULT_MOTION |
+ GTK_DEST_DEFAULT_DROP,
+ &target, 1,
+ GDK_ACTION_COPY);
+
+ g_signal_connect_swapped (button, "drag-data-received",
+ G_CALLBACK (gimp_gradient_select_drag_data_received),
+ gradient_button);
+
+ gtk_widget_pop_composite_child ();
+
+ return button;
+}
diff --git a/libgimp/gimpgradientselectbutton.h b/libgimp/gimpgradientselectbutton.h
new file mode 100644
index 0000000..a6f2958
--- /dev/null
+++ b/libgimp/gimpgradientselectbutton.h
@@ -0,0 +1,81 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpgradientselectbutton.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_UI_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimpui.h> can be included directly."
+#endif
+
+#ifndef __GIMP_GRADIENT_SELECT_BUTTON_H__
+#define __GIMP_GRADIENT_SELECT_BUTTON_H__
+
+#include <libgimp/gimpselectbutton.h>
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+#define GIMP_TYPE_GRADIENT_SELECT_BUTTON (gimp_gradient_select_button_get_type ())
+#define GIMP_GRADIENT_SELECT_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_GRADIENT_SELECT_BUTTON, GimpGradientSelectButton))
+#define GIMP_GRADIENT_SELECT_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_GRADIENT_SELECT_BUTTON, GimpGradientSelectButtonClass))
+#define GIMP_IS_GRADIENT_SELECT_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_GRADIENT_SELECT_BUTTON))
+#define GIMP_IS_GRADIENT_SELECT_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_GRADIENT_SELECT_BUTTON))
+#define GIMP_GRADIENT_SELECT_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_GRADIENT_SELECT_BUTTON, GimpGradientSelectButtonClass))
+
+
+typedef struct _GimpGradientSelectButtonClass GimpGradientSelectButtonClass;
+
+struct _GimpGradientSelectButton
+{
+ GimpSelectButton parent_instance;
+};
+
+struct _GimpGradientSelectButtonClass
+{
+ GimpSelectButtonClass parent_class;
+
+ /* gradient_set signal is emitted when gradient is chosen */
+ void (* gradient_set) (GimpGradientSelectButton *button,
+ const gchar *gradient_name,
+ gint width,
+ const gdouble *gradient_data,
+ gboolean dialog_closing);
+
+ /* Padding for future expansion */
+ void (*_gimp_reserved1) (void);
+ void (*_gimp_reserved2) (void);
+ void (*_gimp_reserved3) (void);
+ void (*_gimp_reserved4) (void);
+};
+
+
+GType gimp_gradient_select_button_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_gradient_select_button_new (const gchar *title,
+ const gchar *gradient_name);
+
+const gchar * gimp_gradient_select_button_get_gradient (GimpGradientSelectButton *button);
+void gimp_gradient_select_button_set_gradient (GimpGradientSelectButton *button,
+ const gchar *gradient_name);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_GRADIENT_SELECT_BUTTON_H__ */
diff --git a/libgimp/gimphelp_pdb.c b/libgimp/gimphelp_pdb.c
new file mode 100644
index 0000000..38e2be5
--- /dev/null
+++ b/libgimp/gimphelp_pdb.c
@@ -0,0 +1,72 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimphelp_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimphelp
+ * @title: gimphelp
+ * @short_description: Loading help pages using gimp_help.
+ *
+ * Loading help pages using gimp_help.
+ **/
+
+
+/**
+ * gimp_help:
+ * @help_domain: The help domain in which help_id is registered.
+ * @help_id: The help page's ID.
+ *
+ * Load a help page.
+ *
+ * This procedure loads the specified help page into the helpbrowser or
+ * what ever is configured as help viewer. The help page is identified
+ * by its domain and ID: if help_domain is NULL, we use the help_domain
+ * which was registered using the gimp_plugin_help_register()
+ * procedure. If help_domain is NULL and no help domain was registered,
+ * the help domain of the main GIMP installation is used.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_help (const gchar *help_domain,
+ const gchar *help_id)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-help",
+ &nreturn_vals,
+ GIMP_PDB_STRING, help_domain,
+ GIMP_PDB_STRING, help_id,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimphelp_pdb.h b/libgimp/gimphelp_pdb.h
new file mode 100644
index 0000000..850bab4
--- /dev/null
+++ b/libgimp/gimphelp_pdb.h
@@ -0,0 +1,41 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimphelp_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_HELP_PDB_H__
+#define __GIMP_HELP_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_help (const gchar *help_domain,
+ const gchar *help_id);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_HELP_PDB_H__ */
diff --git a/libgimp/gimpimage.c b/libgimp/gimpimage.c
new file mode 100644
index 0000000..2b3c110
--- /dev/null
+++ b/libgimp/gimpimage.c
@@ -0,0 +1,520 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2000 Peter Mattis and Spencer Kimball
+ *
+ * gimpimage.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gimp.h"
+#include "gimpimage.h"
+
+/**
+ * gimp_image_get_colormap:
+ * @image_ID: The image.
+ * @num_colors: Returns the number of colors in the colormap array.
+ *
+ * Returns the image's colormap
+ *
+ * This procedure returns an actual pointer to the image's colormap, as
+ * well as the number of colors contained in the colormap. If the image
+ * is not of base type INDEXED, this pointer will be NULL.
+ *
+ * Returns: The image's colormap.
+ */
+guchar *
+gimp_image_get_colormap (gint32 image_ID,
+ gint *num_colors)
+{
+ gint num_bytes;
+ guchar *cmap;
+
+ cmap = _gimp_image_get_colormap (image_ID, &num_bytes);
+
+ if (num_colors)
+ *num_colors = num_bytes / 3;
+
+ return cmap;
+}
+
+/**
+ * gimp_image_set_colormap:
+ * @image_ID: The image.
+ * @colormap: The new colormap values.
+ * @num_colors: Number of colors in the colormap array.
+ *
+ * Sets the entries in the image's colormap.
+ *
+ * This procedure sets the entries in the specified image's colormap.
+ * The number of colors is specified by the \"num_colors\" parameter
+ * and corresponds to the number of INT8 triples that must be contained
+ * in the \"cmap\" array.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+gimp_image_set_colormap (gint32 image_ID,
+ const guchar *colormap,
+ gint num_colors)
+{
+ return _gimp_image_set_colormap (image_ID, num_colors * 3, colormap);
+}
+
+guchar *
+gimp_image_get_thumbnail_data (gint32 image_ID,
+ gint *width,
+ gint *height,
+ gint *bpp)
+{
+ gint ret_width;
+ gint ret_height;
+ guchar *image_data;
+ gint data_size;
+
+ _gimp_image_thumbnail (image_ID,
+ *width,
+ *height,
+ &ret_width,
+ &ret_height,
+ bpp,
+ &data_size,
+ &image_data);
+
+ *width = ret_width;
+ *height = ret_height;
+
+ return image_data;
+}
+
+/**
+ * gimp_image_get_metadata:
+ * @image_ID: The image.
+ *
+ * Returns the image's metadata.
+ *
+ * Returns exif/iptc/xmp metadata from the image.
+ *
+ * Returns: The exif/ptc/xmp metadata, or %NULL if there is none.
+ *
+ * Since: 2.10
+ **/
+GimpMetadata *
+gimp_image_get_metadata (gint32 image_ID)
+{
+ GimpMetadata *metadata = NULL;
+ gchar *metadata_string;
+
+ metadata_string = _gimp_image_get_metadata (image_ID);
+ if (metadata_string)
+ {
+ metadata = gimp_metadata_deserialize (metadata_string);
+ g_free (metadata_string);
+ }
+
+ return metadata;
+}
+
+/**
+ * gimp_image_set_metadata:
+ * @image_ID: The image.
+ * @metadata: The exif/ptc/xmp metadata.
+ *
+ * Set the image's metadata.
+ *
+ * Sets exif/iptc/xmp metadata on the image, or deletes it if
+ * @metadata is %NULL.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_image_set_metadata (gint32 image_ID,
+ GimpMetadata *metadata)
+{
+ gchar *metadata_string = NULL;
+ gboolean success;
+
+ if (metadata)
+ metadata_string = gimp_metadata_serialize (metadata);
+
+ success = _gimp_image_set_metadata (image_ID, metadata_string);
+
+ if (metadata_string)
+ g_free (metadata_string);
+
+ return success;
+}
+
+/**
+ * gimp_image_get_cmap:
+ * @image_ID: The image.
+ * @num_colors: Number of colors in the colormap array.
+ *
+ * Deprecated: Use gimp_image_get_colormap() instead.
+ *
+ * Returns: The image's colormap.
+ */
+guchar *
+gimp_image_get_cmap (gint32 image_ID,
+ gint *num_colors)
+{
+ return gimp_image_get_colormap (image_ID, num_colors);
+}
+
+/**
+ * gimp_image_set_cmap:
+ * @image_ID: The image.
+ * @cmap: The new colormap values.
+ * @num_colors: Number of colors in the colormap array.
+ *
+ * Deprecated: Use gimp_image_set_colormap() instead.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+gimp_image_set_cmap (gint32 image_ID,
+ const guchar *cmap,
+ gint num_colors)
+{
+ return gimp_image_set_colormap (image_ID, cmap, num_colors);
+}
+
+/**
+ * gimp_image_get_layer_position:
+ * @image_ID: The image.
+ * @layer_ID: The layer.
+ *
+ * Deprecated: Use gimp_image_get_item_position() instead.
+ *
+ * Returns: The position of the layer in the layer stack.
+ *
+ * Since: 2.4
+ **/
+gint
+gimp_image_get_layer_position (gint32 image_ID,
+ gint32 layer_ID)
+{
+ return gimp_image_get_item_position (image_ID, layer_ID);
+}
+
+/**
+ * gimp_image_raise_layer:
+ * @image_ID: The image.
+ * @layer_ID: The layer to raise.
+ *
+ * Deprecated: Use gimp_image_raise_item() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_raise_layer (gint32 image_ID,
+ gint32 layer_ID)
+{
+ return gimp_image_raise_item (image_ID, layer_ID);
+}
+
+/**
+ * gimp_image_lower_layer:
+ * @image_ID: The image.
+ * @layer_ID: The layer to lower.
+ *
+ * Deprecated: Use gimp_image_lower_item() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_lower_layer (gint32 image_ID,
+ gint32 layer_ID)
+{
+ return gimp_image_lower_item (image_ID, layer_ID);
+}
+
+/**
+ * gimp_image_raise_layer_to_top:
+ * @image_ID: The image.
+ * @layer_ID: The layer to raise to top.
+ *
+ * Deprecated: Use gimp_image_raise_item_to_top() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_raise_layer_to_top (gint32 image_ID,
+ gint32 layer_ID)
+{
+ return gimp_image_raise_item_to_top (image_ID, layer_ID);
+}
+
+/**
+ * gimp_image_lower_layer_to_bottom:
+ * @image_ID: The image.
+ * @layer_ID: The layer to lower to bottom.
+ *
+ * Deprecated: Use gimp_image_lower_item_to_bottom() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_lower_layer_to_bottom (gint32 image_ID,
+ gint32 layer_ID)
+{
+ return gimp_image_lower_item_to_bottom (image_ID, layer_ID);
+}
+
+/**
+ * gimp_image_get_channel_position:
+ * @image_ID: The image.
+ * @channel_ID: The channel.
+ *
+ * Deprecated: Use gimp_image_get_item_position() instead.
+ *
+ * Returns: The position of the channel in the channel stack.
+ *
+ * Since: 2.4
+ **/
+gint
+gimp_image_get_channel_position (gint32 image_ID,
+ gint32 channel_ID)
+{
+ return gimp_image_get_item_position (image_ID, channel_ID);
+}
+
+/**
+ * gimp_image_raise_channel:
+ * @image_ID: The image.
+ * @channel_ID: The channel to raise.
+ *
+ * Deprecated: Use gimp_image_raise_item() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_raise_channel (gint32 image_ID,
+ gint32 channel_ID)
+{
+ return gimp_image_raise_item (image_ID, channel_ID);
+}
+
+/**
+ * gimp_image_lower_channel:
+ * @image_ID: The image.
+ * @channel_ID: The channel to lower.
+ *
+ * Deprecated: Use gimp_image_lower_item() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_lower_channel (gint32 image_ID,
+ gint32 channel_ID)
+{
+ return gimp_image_lower_item (image_ID, channel_ID);
+}
+
+/**
+ * gimp_image_get_vectors_position:
+ * @image_ID: The image.
+ * @vectors_ID: The vectors object.
+ *
+ * Deprecated: Use gimp_image_get_item_position() instead.
+ *
+ * Returns: The position of the vectors object in the vectors stack.
+ *
+ * Since: 2.4
+ **/
+gint
+gimp_image_get_vectors_position (gint32 image_ID,
+ gint32 vectors_ID)
+{
+ return gimp_image_get_item_position (image_ID, vectors_ID);
+}
+
+/**
+ * gimp_image_raise_vectors:
+ * @image_ID: The image.
+ * @vectors_ID: The vectors object to raise.
+ *
+ * Deprecated: Use gimp_image_raise_item() instead.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_image_raise_vectors (gint32 image_ID,
+ gint32 vectors_ID)
+{
+ return gimp_image_raise_item (image_ID, vectors_ID);
+}
+
+/**
+ * gimp_image_lower_vectors:
+ * @image_ID: The image.
+ * @vectors_ID: The vectors object to lower.
+ *
+ * Deprecated: Use gimp_image_lower_item() instead.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_image_lower_vectors (gint32 image_ID,
+ gint32 vectors_ID)
+{
+ return gimp_image_lower_item (image_ID, vectors_ID);
+}
+
+/**
+ * gimp_image_raise_vectors_to_top:
+ * @image_ID: The image.
+ * @vectors_ID: The vectors object to raise to top.
+ *
+ * Deprecated: Use gimp_image_raise_item_to_top() instead.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_image_raise_vectors_to_top (gint32 image_ID,
+ gint32 vectors_ID)
+{
+ return gimp_image_raise_item_to_top (image_ID, vectors_ID);
+}
+
+/**
+ * gimp_image_lower_vectors_to_bottom:
+ * @image_ID: The image.
+ * @vectors_ID: The vectors object to lower to bottom.
+ *
+ * Deprecated: Use gimp_image_lower_item_to_bottom() instead.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_image_lower_vectors_to_bottom (gint32 image_ID,
+ gint32 vectors_ID)
+{
+ return gimp_image_lower_item_to_bottom (image_ID, vectors_ID);
+}
+
+/**
+ * gimp_image_parasite_find:
+ * @image_ID: The image.
+ * @name: The name of the parasite to find.
+ *
+ * Deprecated: Use gimp_image_get_parasite() instead.
+ *
+ * Returns: The found parasite.
+ **/
+GimpParasite *
+gimp_image_parasite_find (gint32 image_ID,
+ const gchar *name)
+{
+ return gimp_image_get_parasite (image_ID, name);
+}
+
+/**
+ * gimp_image_parasite_attach:
+ * @image_ID: The image.
+ * @parasite: The parasite to attach to an image.
+ *
+ * Deprecated: Use gimp_image_attach_parasite() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_parasite_attach (gint32 image_ID,
+ const GimpParasite *parasite)
+{
+ return gimp_image_attach_parasite (image_ID, parasite);
+}
+
+/**
+ * gimp_image_parasite_detach:
+ * @image_ID: The image.
+ * @name: The name of the parasite to detach from an image.
+ *
+ * Deprecated: Use gimp_image_detach_parasite() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_parasite_detach (gint32 image_ID,
+ const gchar *name)
+{
+ return gimp_image_detach_parasite (image_ID, name);
+}
+
+/**
+ * gimp_image_parasite_list:
+ * @image_ID: The image.
+ * @num_parasites: The number of attached parasites.
+ * @parasites: The names of currently attached parasites.
+ *
+ * Deprecated: Use gimp_image_get_parasite_list() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_parasite_list (gint32 image_ID,
+ gint *num_parasites,
+ gchar ***parasites)
+{
+ *parasites = gimp_image_get_parasite_list (image_ID, num_parasites);
+
+ return *parasites != NULL;
+}
+
+/**
+ * gimp_image_attach_new_parasite:
+ * @image_ID: the ID of the image to attach the #GimpParasite to.
+ * @name: the name of the #GimpParasite to create and attach.
+ * @flags: the flags set on the #GimpParasite.
+ * @size: the size of the parasite data in bytes.
+ * @data: a pointer to the data attached with the #GimpParasite.
+ *
+ * Convenience function that creates a parasite and attaches it
+ * to GIMP.
+ *
+ * Deprecated: Use gimp_image_attach_parasite() instead.
+ *
+ * Return value: TRUE on successful creation and attachment of
+ * the new parasite.
+ *
+ * See Also: gimp_image_parasite_attach()
+ */
+gboolean
+gimp_image_attach_new_parasite (gint32 image_ID,
+ const gchar *name,
+ gint flags,
+ gint size,
+ gconstpointer data)
+{
+ GimpParasite *parasite = gimp_parasite_new (name, flags, size, data);
+ gboolean success;
+
+ success = gimp_image_attach_parasite (image_ID, parasite);
+
+ gimp_parasite_free (parasite);
+
+ return success;
+}
diff --git a/libgimp/gimpimage.h b/libgimp/gimpimage.h
new file mode 100644
index 0000000..fd0c44a
--- /dev/null
+++ b/libgimp/gimpimage.h
@@ -0,0 +1,116 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2000 Peter Mattis and Spencer Kimball
+ *
+ * gimpimage.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_IMAGE_H__
+#define __GIMP_IMAGE_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+guchar * gimp_image_get_colormap (gint32 image_ID,
+ gint *num_colors);
+gboolean gimp_image_set_colormap (gint32 image_ID,
+ const guchar *colormap,
+ gint num_colors);
+
+guchar * gimp_image_get_thumbnail_data (gint32 image_ID,
+ gint *width,
+ gint *height,
+ gint *bpp);
+
+GimpMetadata * gimp_image_get_metadata (gint32 image_ID);
+gboolean gimp_image_set_metadata (gint32 image_ID,
+ GimpMetadata *metadata);
+
+GIMP_DEPRECATED_FOR(gimp_image_get_colormap)
+guchar * gimp_image_get_cmap (gint32 image_ID,
+ gint *num_colors);
+GIMP_DEPRECATED_FOR(gimp_image_set_colormap)
+gboolean gimp_image_set_cmap (gint32 image_ID,
+ const guchar *cmap,
+ gint num_colors);
+GIMP_DEPRECATED_FOR(gimp_image_get_item_position)
+gint gimp_image_get_layer_position (gint32 image_ID,
+ gint32 layer_ID);
+GIMP_DEPRECATED_FOR(gimp_image_raise_item)
+gboolean gimp_image_raise_layer (gint32 image_ID,
+ gint32 layer_ID);
+GIMP_DEPRECATED_FOR(gimp_image_lower_item)
+gboolean gimp_image_lower_layer (gint32 image_ID,
+ gint32 layer_ID);
+GIMP_DEPRECATED_FOR(gimp_image_raise_item_to_top)
+gboolean gimp_image_raise_layer_to_top (gint32 image_ID,
+ gint32 layer_ID);
+GIMP_DEPRECATED_FOR(gimp_image_lower_item_to_bottom)
+gboolean gimp_image_lower_layer_to_bottom (gint32 image_ID,
+ gint32 layer_ID);
+GIMP_DEPRECATED_FOR(gimp_image_get_item_position)
+gint gimp_image_get_channel_position (gint32 image_ID,
+ gint32 channel_ID);
+GIMP_DEPRECATED_FOR(gimp_image_raise_item)
+gboolean gimp_image_raise_channel (gint32 image_ID,
+ gint32 channel_ID);
+GIMP_DEPRECATED_FOR(gimp_image_lower_item)
+gboolean gimp_image_lower_channel (gint32 image_ID,
+ gint32 channel_ID);
+GIMP_DEPRECATED_FOR(gimp_image_get_item_position)
+gint gimp_image_get_vectors_position (gint32 image_ID,
+ gint32 vectors_ID);
+GIMP_DEPRECATED_FOR(gimp_image_raise_item)
+gboolean gimp_image_raise_vectors (gint32 image_ID,
+ gint32 vectors_ID);
+GIMP_DEPRECATED_FOR(gimp_image_lower_item)
+gboolean gimp_image_lower_vectors (gint32 image_ID,
+ gint32 vectors_ID);
+GIMP_DEPRECATED_FOR(gimp_image_raise_item_to_top)
+gboolean gimp_image_raise_vectors_to_top (gint32 image_ID,
+ gint32 vectors_ID);
+GIMP_DEPRECATED_FOR(gimp_image_lower_item_to_bottom)
+gboolean gimp_image_lower_vectors_to_bottom (gint32 image_ID,
+ gint32 vectors_ID);
+GIMP_DEPRECATED_FOR(gimp_image_get_parasite)
+GimpParasite * gimp_image_parasite_find (gint32 image_ID,
+ const gchar *name);
+GIMP_DEPRECATED_FOR(gimp_image_attach_parasite)
+gboolean gimp_image_parasite_attach (gint32 image_ID,
+ const GimpParasite *parasite);
+GIMP_DEPRECATED_FOR(gimp_image_detach_parasite)
+gboolean gimp_image_parasite_detach (gint32 image_ID,
+ const gchar *name);
+GIMP_DEPRECATED_FOR(gimp_image_get_parasite_list)
+gboolean gimp_image_parasite_list (gint32 image_ID,
+ gint *num_parasites,
+ gchar ***parasites);
+GIMP_DEPRECATED_FOR(gimp_image_attach_parasite)
+gboolean gimp_image_attach_new_parasite (gint32 image_ID,
+ const gchar *name,
+ gint flags,
+ gint size,
+ gconstpointer data);
+
+G_END_DECLS
+
+#endif /* __GIMP_IMAGE_H__ */
diff --git a/libgimp/gimpimage_pdb.c b/libgimp/gimpimage_pdb.c
new file mode 100644
index 0000000..b38a152
--- /dev/null
+++ b/libgimp/gimpimage_pdb.c
@@ -0,0 +1,3223 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpimage_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpimage
+ * @title: gimpimage
+ * @short_description: Operations on complete images.
+ *
+ * Operations on complete images: creation, resizing/rescaling, and
+ * operations involving multiple layers.
+ **/
+
+
+/**
+ * gimp_image_is_valid:
+ * @image_ID: The image to check.
+ *
+ * Returns TRUE if the image is valid.
+ *
+ * This procedure checks if the given image ID is valid and refers to
+ * an existing image.
+ *
+ * Returns: Whether the image ID is valid.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_image_is_valid (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean valid = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-image-is-valid",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ valid = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return valid;
+}
+
+/**
+ * gimp_image_list:
+ * @num_images: The number of images currently open.
+ *
+ * Returns the list of images currently open.
+ *
+ * This procedure returns the list of images currently open in GIMP.
+ *
+ * Returns: The list of images currently open. The returned value must
+ * be freed with g_free().
+ **/
+gint *
+gimp_image_list (gint *num_images)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint *image_ids = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-image-list",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ *num_images = 0;
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ *num_images = return_vals[1].data.d_int32;
+ image_ids = g_new (gint32, *num_images);
+ memcpy (image_ids,
+ return_vals[2].data.d_int32array,
+ *num_images * sizeof (gint32));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return image_ids;
+}
+
+/**
+ * gimp_image_new:
+ * @width: The width of the image.
+ * @height: The height of the image.
+ * @type: The type of image.
+ *
+ * Creates a new image with the specified width, height, and type.
+ *
+ * Creates a new image, undisplayed, with the specified extents and
+ * type. A layer should be created and added before this image is
+ * displayed, or subsequent calls to gimp_display_new() with this image
+ * as an argument will fail. Layers can be created using the
+ * gimp_layer_new() commands. They can be added to an image using the
+ * gimp_image_insert_layer() command.
+ *
+ * If your image's type if INDEXED, a colormap must also be added with
+ * gimp_image_set_colormap(). An indexed image without a colormap will
+ * output unexpected colors.
+ *
+ * Returns: The ID of the newly created image.
+ **/
+gint32
+gimp_image_new (gint width,
+ gint height,
+ GimpImageBaseType type)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 image_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-image-new",
+ &nreturn_vals,
+ GIMP_PDB_INT32, width,
+ GIMP_PDB_INT32, height,
+ GIMP_PDB_INT32, type,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ image_ID = return_vals[1].data.d_image;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return image_ID;
+}
+
+/**
+ * gimp_image_new_with_precision:
+ * @width: The width of the image.
+ * @height: The height of the image.
+ * @type: The type of image.
+ * @precision: The precision.
+ *
+ * Creates a new image with the specified width, height, type and
+ * precision.
+ *
+ * Creates a new image, undisplayed with the specified extents, type
+ * and precision. Indexed images can only be created at
+ * GIMP_PRECISION_U8_GAMMA precision. See gimp_image_new() for further
+ * details.
+ *
+ * Returns: The ID of the newly created image.
+ *
+ * Since: 2.10
+ **/
+gint32
+gimp_image_new_with_precision (gint width,
+ gint height,
+ GimpImageBaseType type,
+ GimpPrecision precision)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 image_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-image-new-with-precision",
+ &nreturn_vals,
+ GIMP_PDB_INT32, width,
+ GIMP_PDB_INT32, height,
+ GIMP_PDB_INT32, type,
+ GIMP_PDB_INT32, precision,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ image_ID = return_vals[1].data.d_image;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return image_ID;
+}
+
+/**
+ * gimp_image_duplicate:
+ * @image_ID: The image.
+ *
+ * Duplicate the specified image
+ *
+ * This procedure duplicates the specified image, copying all layers,
+ * channels, and image information.
+ *
+ * Returns: The new, duplicated image.
+ **/
+gint32
+gimp_image_duplicate (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 new_image_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-image-duplicate",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ new_image_ID = return_vals[1].data.d_image;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return new_image_ID;
+}
+
+/**
+ * gimp_image_delete:
+ * @image_ID: The image.
+ *
+ * Delete the specified image.
+ *
+ * If there are no displays associated with this image it will be
+ * deleted. This means that you can not delete an image through the PDB
+ * that was created by the user. If the associated display was however
+ * created through the PDB and you know the display ID, you may delete
+ * the display. Removal of the last associated display will then delete
+ * the image.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_delete (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-delete",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_base_type:
+ * @image_ID: The image.
+ *
+ * Get the base type of the image.
+ *
+ * This procedure returns the image's base type. Layers in the image
+ * must be of this subtype, but can have an optional alpha channel.
+ *
+ * Returns: The image's base type.
+ **/
+GimpImageBaseType
+gimp_image_base_type (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpImageBaseType base_type = 0;
+
+ return_vals = gimp_run_procedure ("gimp-image-base-type",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ base_type = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return base_type;
+}
+
+/**
+ * gimp_image_get_precision:
+ * @image_ID: The image.
+ *
+ * Get the precision of the image.
+ *
+ * This procedure returns the image's precision.
+ *
+ * Returns: The image's precision.
+ *
+ * Since: 2.10
+ **/
+GimpPrecision
+gimp_image_get_precision (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpPrecision precision = 0;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-precision",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ precision = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return precision;
+}
+
+/**
+ * gimp_image_get_default_new_layer_mode:
+ * @image_ID: The image.
+ *
+ * Get the default mode for newly created layers of this image.
+ *
+ * Returns the default mode for newly created layers of this image.
+ *
+ * Returns: The layer mode.
+ *
+ * Since: 2.10
+ **/
+GimpLayerMode
+gimp_image_get_default_new_layer_mode (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpLayerMode mode = 0;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-default-new-layer-mode",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ mode = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return mode;
+}
+
+/**
+ * gimp_image_width:
+ * @image_ID: The image.
+ *
+ * Return the width of the image
+ *
+ * This procedure returns the image's width. This value is independent
+ * of any of the layers in this image. This is the \"canvas\" width.
+ *
+ * Returns: The image's width.
+ **/
+gint
+gimp_image_width (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint width = 0;
+
+ return_vals = gimp_run_procedure ("gimp-image-width",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ width = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return width;
+}
+
+/**
+ * gimp_image_height:
+ * @image_ID: The image.
+ *
+ * Return the height of the image
+ *
+ * This procedure returns the image's height. This value is independent
+ * of any of the layers in this image. This is the \"canvas\" height.
+ *
+ * Returns: The image's height.
+ **/
+gint
+gimp_image_height (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint height = 0;
+
+ return_vals = gimp_run_procedure ("gimp-image-height",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ height = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return height;
+}
+
+/**
+ * gimp_image_free_shadow:
+ * @image_ID: The image.
+ *
+ * Deprecated: Use gimp_drawable_free_shadow() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_free_shadow (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-free-shadow",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_get_layers:
+ * @image_ID: The image.
+ * @num_layers: The number of layers contained in the image.
+ *
+ * Returns the list of layers contained in the specified image.
+ *
+ * This procedure returns the list of layers contained in the specified
+ * image. The order of layers is from topmost to bottommost.
+ *
+ * Returns: The list of layers contained in the image. The returned
+ * value must be freed with g_free().
+ **/
+gint *
+gimp_image_get_layers (gint32 image_ID,
+ gint *num_layers)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint *layer_ids = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-layers",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ *num_layers = 0;
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ *num_layers = return_vals[1].data.d_int32;
+ layer_ids = g_new (gint32, *num_layers);
+ memcpy (layer_ids,
+ return_vals[2].data.d_int32array,
+ *num_layers * sizeof (gint32));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return layer_ids;
+}
+
+/**
+ * gimp_image_get_channels:
+ * @image_ID: The image.
+ * @num_channels: The number of channels contained in the image.
+ *
+ * Returns the list of channels contained in the specified image.
+ *
+ * This procedure returns the list of channels contained in the
+ * specified image. This does not include the selection mask, or layer
+ * masks. The order is from topmost to bottommost. Note that
+ * \"channels\" are custom channels and do not include the image's
+ * color components.
+ *
+ * Returns: The list of channels contained in the image. The returned
+ * value must be freed with g_free().
+ **/
+gint *
+gimp_image_get_channels (gint32 image_ID,
+ gint *num_channels)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint *channel_ids = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-channels",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ *num_channels = 0;
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ *num_channels = return_vals[1].data.d_int32;
+ channel_ids = g_new (gint32, *num_channels);
+ memcpy (channel_ids,
+ return_vals[2].data.d_int32array,
+ *num_channels * sizeof (gint32));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return channel_ids;
+}
+
+/**
+ * gimp_image_get_vectors:
+ * @image_ID: The image.
+ * @num_vectors: The number of vectors contained in the image.
+ *
+ * Returns the list of vectors contained in the specified image.
+ *
+ * This procedure returns the list of vectors contained in the
+ * specified image.
+ *
+ * Returns: The list of vectors contained in the image. The returned
+ * value must be freed with g_free().
+ *
+ * Since: 2.4
+ **/
+gint *
+gimp_image_get_vectors (gint32 image_ID,
+ gint *num_vectors)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint *vector_ids = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-vectors",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ *num_vectors = 0;
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ *num_vectors = return_vals[1].data.d_int32;
+ vector_ids = g_new (gint32, *num_vectors);
+ memcpy (vector_ids,
+ return_vals[2].data.d_int32array,
+ *num_vectors * sizeof (gint32));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return vector_ids;
+}
+
+/**
+ * gimp_image_get_active_drawable:
+ * @image_ID: The image.
+ *
+ * Get the image's active drawable
+ *
+ * This procedure returns the ID of the image's active drawable. This
+ * can be either a layer, a channel, or a layer mask. The active
+ * drawable is specified by the active image channel. If that is -1,
+ * then by the active image layer. If the active image layer has a
+ * layer mask and the layer mask is in edit mode, then the layer mask
+ * is the active drawable.
+ *
+ * Returns: The active drawable.
+ **/
+gint32
+gimp_image_get_active_drawable (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 drawable_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-active-drawable",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ drawable_ID = return_vals[1].data.d_drawable;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return drawable_ID;
+}
+
+/**
+ * gimp_image_unset_active_channel:
+ * @image_ID: The image.
+ *
+ * Unsets the active channel in the specified image.
+ *
+ * If an active channel exists, it is unset. There then exists no
+ * active channel, and if desired, one can be set through a call to
+ * 'Set Active Channel'. No error is returned in the case of no
+ * existing active channel.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_unset_active_channel (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-unset-active-channel",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_get_floating_sel:
+ * @image_ID: The image.
+ *
+ * Return the floating selection of the image.
+ *
+ * This procedure returns the image's floating selection, if it exists.
+ * If it doesn't exist, -1 is returned as the layer ID.
+ *
+ * Returns: The image's floating selection.
+ **/
+gint32
+gimp_image_get_floating_sel (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 floating_sel_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-floating-sel",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ floating_sel_ID = return_vals[1].data.d_layer;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return floating_sel_ID;
+}
+
+/**
+ * gimp_image_floating_sel_attached_to:
+ * @image_ID: The image.
+ *
+ * Return the drawable the floating selection is attached to.
+ *
+ * This procedure returns the drawable the image's floating selection
+ * is attached to, if it exists. If it doesn't exist, -1 is returned as
+ * the drawable ID.
+ *
+ * Returns: The drawable the floating selection is attached to.
+ **/
+gint32
+gimp_image_floating_sel_attached_to (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 drawable_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-image-floating-sel-attached-to",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ drawable_ID = return_vals[1].data.d_drawable;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return drawable_ID;
+}
+
+/**
+ * gimp_image_pick_color:
+ * @image_ID: The image.
+ * @drawable_ID: The drawable to pick from.
+ * @x: x coordinate of upper-left corner of rectangle.
+ * @y: y coordinate of upper-left corner of rectangle.
+ * @sample_merged: Use the composite image, not the drawable.
+ * @sample_average: Average the color of all the pixels in a specified radius.
+ * @average_radius: The radius of pixels to average.
+ * @color: The return color.
+ *
+ * Determine the color at the given drawable coordinates
+ *
+ * This tool determines the color at the specified coordinates. The
+ * returned color is an RGB triplet even for grayscale and indexed
+ * drawables. If the coordinates lie outside of the extents of the
+ * specified drawable, then an error is returned. If the drawable has
+ * an alpha channel, the algorithm examines the alpha value of the
+ * drawable at the coordinates. If the alpha value is completely
+ * transparent (0), then an error is returned. If the sample_merged
+ * parameter is TRUE, the data of the composite image will be used
+ * instead of that for the specified drawable. This is equivalent to
+ * sampling for colors after merging all visible layers. In the case of
+ * a merged sampling, the supplied drawable is ignored.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_pick_color (gint32 image_ID,
+ gint32 drawable_ID,
+ gdouble x,
+ gdouble y,
+ gboolean sample_merged,
+ gboolean sample_average,
+ gdouble average_radius,
+ GimpRGB *color)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-pick-color",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_FLOAT, x,
+ GIMP_PDB_FLOAT, y,
+ GIMP_PDB_INT32, sample_merged,
+ GIMP_PDB_INT32, sample_average,
+ GIMP_PDB_FLOAT, average_radius,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ *color = return_vals[1].data.d_color;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_pick_correlate_layer:
+ * @image_ID: The image.
+ * @x: The x coordinate for the pick.
+ * @y: The y coordinate for the pick.
+ *
+ * Find the layer visible at the specified coordinates.
+ *
+ * This procedure finds the layer which is visible at the specified
+ * coordinates. Layers which do not qualify are those whose extents do
+ * not pass within the specified coordinates, or which are transparent
+ * at the specified coordinates. This procedure will return -1 if no
+ * layer is found.
+ *
+ * Returns: The layer found at the specified coordinates.
+ **/
+gint32
+gimp_image_pick_correlate_layer (gint32 image_ID,
+ gint x,
+ gint y)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 layer_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-image-pick-correlate-layer",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, x,
+ GIMP_PDB_INT32, y,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ layer_ID = return_vals[1].data.d_layer;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return layer_ID;
+}
+
+/**
+ * gimp_image_add_layer:
+ * @image_ID: The image.
+ * @layer_ID: The layer.
+ * @position: The layer position.
+ *
+ * Deprecated: Use gimp_image_insert_layer() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_add_layer (gint32 image_ID,
+ gint32 layer_ID,
+ gint position)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-add-layer",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_INT32, position,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_insert_layer:
+ * @image_ID: The image.
+ * @layer_ID: The layer.
+ * @parent_ID: The parent layer.
+ * @position: The layer position.
+ *
+ * Add the specified layer to the image.
+ *
+ * This procedure adds the specified layer to the image at the given
+ * position. If the specified parent is a valid layer group (See
+ * gimp_item_is_group() and gimp_layer_group_new()) then the layer is
+ * added inside the group. If the parent is 0, the layer is added
+ * inside the main stack, outside of any group. The position argument
+ * specifies the location of the layer inside the stack (or the group,
+ * if a valid parent was supplied), starting from the top (0) and
+ * increasing. If the position is specified as -1 and the parent is
+ * specified as 0, then the layer is inserted above the active layer,
+ * or inside the group if the active layer is a layer group. The layer
+ * type must be compatible with the image base type.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_insert_layer (gint32 image_ID,
+ gint32 layer_ID,
+ gint32 parent_ID,
+ gint position)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-insert-layer",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_LAYER, parent_ID,
+ GIMP_PDB_INT32, position,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_remove_layer:
+ * @image_ID: The image.
+ * @layer_ID: The layer.
+ *
+ * Remove the specified layer from the image.
+ *
+ * This procedure removes the specified layer from the image. If the
+ * layer doesn't exist, an error is returned. If there are no layers
+ * left in the image, this call will fail. If this layer is the last
+ * layer remaining, the image will become empty and have no active
+ * layer.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_remove_layer (gint32 image_ID,
+ gint32 layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-remove-layer",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_freeze_layers:
+ * @image_ID: The image.
+ *
+ * Freeze the image's layer list.
+ *
+ * This procedure freezes the layer list of the image, suppressing any
+ * updates to the Layers dialog in response to changes to the image's
+ * layers. This can significantly improve performance while applying
+ * changes affecting the layer list.
+ *
+ * Each call to gimp_image_freeze_layers() should be matched by a
+ * corresponding call to gimp_image_thaw_layers(), undoing its effects.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10.2
+ **/
+gboolean
+gimp_image_freeze_layers (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-freeze-layers",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_thaw_layers:
+ * @image_ID: The image.
+ *
+ * Thaw the image's layer list.
+ *
+ * This procedure thaws the layer list of the image, re-enabling
+ * updates to the Layers dialog.
+ *
+ * This procedure should match a corresponding call to
+ * gimp_image_freeze_layers().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10.2
+ **/
+gboolean
+gimp_image_thaw_layers (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-thaw-layers",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_add_channel:
+ * @image_ID: The image.
+ * @channel_ID: The channel.
+ * @position: The channel position.
+ *
+ * Deprecated: Use gimp_image_insert_channel() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_add_channel (gint32 image_ID,
+ gint32 channel_ID,
+ gint position)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-add-channel",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_CHANNEL, channel_ID,
+ GIMP_PDB_INT32, position,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_insert_channel:
+ * @image_ID: The image.
+ * @channel_ID: The channel.
+ * @parent_ID: The parent channel.
+ * @position: The channel position.
+ *
+ * Add the specified channel to the image.
+ *
+ * This procedure adds the specified channel to the image at the given
+ * position. Since channel groups are not currently supported, the
+ * parent argument must always be 0. The position argument specifies
+ * the location of the channel inside the stack, starting from the top
+ * (0) and increasing. If the position is specified as -1, then the
+ * channel is inserted above the active channel.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_insert_channel (gint32 image_ID,
+ gint32 channel_ID,
+ gint32 parent_ID,
+ gint position)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-insert-channel",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_CHANNEL, channel_ID,
+ GIMP_PDB_CHANNEL, parent_ID,
+ GIMP_PDB_INT32, position,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_remove_channel:
+ * @image_ID: The image.
+ * @channel_ID: The channel.
+ *
+ * Remove the specified channel from the image.
+ *
+ * This procedure removes the specified channel from the image. If the
+ * channel doesn't exist, an error is returned.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_remove_channel (gint32 image_ID,
+ gint32 channel_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-remove-channel",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_CHANNEL, channel_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_freeze_channels:
+ * @image_ID: The image.
+ *
+ * Freeze the image's channel list.
+ *
+ * This procedure freezes the channel list of the image, suppressing
+ * any updates to the Channels dialog in response to changes to the
+ * image's channels. This can significantly improve performance while
+ * applying changes affecting the channel list.
+ *
+ * Each call to gimp_image_freeze_channels() should be matched by a
+ * corresponding call to gimp_image_thaw_channels(), undoing its
+ * effects.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10.2
+ **/
+gboolean
+gimp_image_freeze_channels (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-freeze-channels",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_thaw_channels:
+ * @image_ID: The image.
+ *
+ * Thaw the image's channel list.
+ *
+ * This procedure thaws the channel list of the image, re-enabling
+ * updates to the Channels dialog.
+ *
+ * This procedure should match a corresponding call to
+ * gimp_image_freeze_channels().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10.2
+ **/
+gboolean
+gimp_image_thaw_channels (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-thaw-channels",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_add_vectors:
+ * @image_ID: The image.
+ * @vectors_ID: The vectors object.
+ * @position: The vectors objects position.
+ *
+ * Deprecated: Use gimp_image_insert_vectors() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_add_vectors (gint32 image_ID,
+ gint32 vectors_ID,
+ gint position)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-add-vectors",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_VECTORS, vectors_ID,
+ GIMP_PDB_INT32, position,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_insert_vectors:
+ * @image_ID: The image.
+ * @vectors_ID: The vectors.
+ * @parent_ID: The parent vectors.
+ * @position: The vectors position.
+ *
+ * Add the specified vectors to the image.
+ *
+ * This procedure adds the specified vectors to the image at the given
+ * position. Since vectors groups are not currently supported, the
+ * parent argument must always be 0. The position argument specifies
+ * the location of the vectors inside the stack, starting from the top
+ * (0) and increasing. If the position is specified as -1, then the
+ * vectors is inserted above the active vectors.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_insert_vectors (gint32 image_ID,
+ gint32 vectors_ID,
+ gint32 parent_ID,
+ gint position)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-insert-vectors",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_VECTORS, vectors_ID,
+ GIMP_PDB_VECTORS, parent_ID,
+ GIMP_PDB_INT32, position,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_remove_vectors:
+ * @image_ID: The image.
+ * @vectors_ID: The vectors object.
+ *
+ * Remove the specified path from the image.
+ *
+ * This procedure removes the specified path from the image. If the
+ * path doesn't exist, an error is returned.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_image_remove_vectors (gint32 image_ID,
+ gint32 vectors_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-remove-vectors",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_VECTORS, vectors_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_freeze_vectors:
+ * @image_ID: The image.
+ *
+ * Freeze the image's vectors list.
+ *
+ * This procedure freezes the vectors list of the image, suppressing
+ * any updates to the Paths dialog in response to changes to the
+ * image's vectors. This can significantly improve performance while
+ * applying changes affecting the vectors list.
+ *
+ * Each call to gimp_image_freeze_vectors() should be matched by a
+ * corresponding call to gimp_image_thaw_vectors(), undoing its
+ * effects.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10.2
+ **/
+gboolean
+gimp_image_freeze_vectors (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-freeze-vectors",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_thaw_vectors:
+ * @image_ID: The image.
+ *
+ * Thaw the image's vectors list.
+ *
+ * This procedure thaws the vectors list of the image, re-enabling
+ * updates to the Paths dialog.
+ *
+ * This procedure should match a corresponding call to
+ * gimp_image_freeze_vectors().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10.2
+ **/
+gboolean
+gimp_image_thaw_vectors (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-thaw-vectors",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_get_item_position:
+ * @image_ID: The image.
+ * @item_ID: The item.
+ *
+ * Returns the position of the item in its level of its item tree.
+ *
+ * This procedure determines the position of the specified item in its
+ * level in its item tree in the image. If the item doesn't exist in
+ * the image, or the item is not part of an item tree, an error is
+ * returned.
+ *
+ * Returns: The position of the item in its level in the item tree.
+ *
+ * Since: 2.8
+ **/
+gint
+gimp_image_get_item_position (gint32 image_ID,
+ gint32 item_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint position = 0;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-item-position",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ position = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return position;
+}
+
+/**
+ * gimp_image_raise_item:
+ * @image_ID: The image.
+ * @item_ID: The item to raise.
+ *
+ * Raise the specified item in its level in its item tree
+ *
+ * This procedure raises the specified item one step in the item tree.
+ * The procedure call will fail if there is no item above it.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_image_raise_item (gint32 image_ID,
+ gint32 item_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-raise-item",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_lower_item:
+ * @image_ID: The image.
+ * @item_ID: The item to lower.
+ *
+ * Lower the specified item in its level in its item tree
+ *
+ * This procedure lowers the specified item one step in the item tree.
+ * The procedure call will fail if there is no item below it.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_image_lower_item (gint32 image_ID,
+ gint32 item_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-lower-item",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_raise_item_to_top:
+ * @image_ID: The image.
+ * @item_ID: The item to raise to top.
+ *
+ * Raise the specified item to the top of its level in its item tree
+ *
+ * This procedure raises the specified item to top of its level in the
+ * item tree. It will not move the item if there is no item above it.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_image_raise_item_to_top (gint32 image_ID,
+ gint32 item_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-raise-item-to-top",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_lower_item_to_bottom:
+ * @image_ID: The image.
+ * @item_ID: The item to lower to bottom.
+ *
+ * Lower the specified item to the bottom of its level in its item tree
+ *
+ * This procedure lowers the specified item to bottom of its level in
+ * the item tree. It will not move the layer if there is no layer below
+ * it.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_image_lower_item_to_bottom (gint32 image_ID,
+ gint32 item_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-lower-item-to-bottom",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_reorder_item:
+ * @image_ID: The image.
+ * @item_ID: The item to reorder.
+ * @parent_ID: The new parent item.
+ * @position: The new position of the item.
+ *
+ * Reorder the specified item within its item tree
+ *
+ * This procedure reorders the specified item within its item tree.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_image_reorder_item (gint32 image_ID,
+ gint32 item_ID,
+ gint32 parent_ID,
+ gint position)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-reorder-item",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_ITEM, parent_ID,
+ GIMP_PDB_INT32, position,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_flatten:
+ * @image_ID: The image.
+ *
+ * Flatten all visible layers into a single layer. Discard all
+ * invisible layers.
+ *
+ * This procedure combines the visible layers in a manner analogous to
+ * merging with the CLIP_TO_IMAGE merge type. Non-visible layers are
+ * discarded, and the resulting image is stripped of its alpha channel.
+ *
+ * Returns: The resulting layer.
+ **/
+gint32
+gimp_image_flatten (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 layer_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-image-flatten",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ layer_ID = return_vals[1].data.d_layer;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return layer_ID;
+}
+
+/**
+ * gimp_image_merge_visible_layers:
+ * @image_ID: The image.
+ * @merge_type: The type of merge.
+ *
+ * Merge the visible image layers into one.
+ *
+ * This procedure combines the visible layers into a single layer using
+ * the specified merge type. A merge type of EXPAND_AS_NECESSARY
+ * expands the final layer to encompass the areas of the visible
+ * layers. A merge type of CLIP_TO_IMAGE clips the final layer to the
+ * extents of the image. A merge type of CLIP_TO_BOTTOM_LAYER clips the
+ * final layer to the size of the bottommost layer.
+ *
+ * Returns: The resulting layer.
+ **/
+gint32
+gimp_image_merge_visible_layers (gint32 image_ID,
+ GimpMergeType merge_type)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 layer_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-image-merge-visible-layers",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, merge_type,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ layer_ID = return_vals[1].data.d_layer;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return layer_ID;
+}
+
+/**
+ * gimp_image_merge_down:
+ * @image_ID: The image.
+ * @merge_layer_ID: The layer to merge down from.
+ * @merge_type: The type of merge.
+ *
+ * Merge the layer passed and the first visible layer below.
+ *
+ * This procedure combines the passed layer and the first visible layer
+ * below it using the specified merge type. A merge type of
+ * EXPAND_AS_NECESSARY expands the final layer to encompass the areas
+ * of the visible layers. A merge type of CLIP_TO_IMAGE clips the final
+ * layer to the extents of the image. A merge type of
+ * CLIP_TO_BOTTOM_LAYER clips the final layer to the size of the
+ * bottommost layer.
+ *
+ * Returns: The resulting layer.
+ **/
+gint32
+gimp_image_merge_down (gint32 image_ID,
+ gint32 merge_layer_ID,
+ GimpMergeType merge_type)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 layer_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-image-merge-down",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_LAYER, merge_layer_ID,
+ GIMP_PDB_INT32, merge_type,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ layer_ID = return_vals[1].data.d_layer;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return layer_ID;
+}
+
+/**
+ * gimp_image_merge_layer_group:
+ * @image_ID: The image.
+ * @layer_group_ID: The layer group to merge.
+ *
+ * Merge the passed layer group's layers into one normal layer.
+ *
+ * This procedure combines the layers of the passed layer group into a
+ * single normal layer, replacing the group.
+ *
+ * Returns: The resulting layer.
+ *
+ * Since: 2.10.14
+ **/
+gint32
+gimp_image_merge_layer_group (gint32 image_ID,
+ gint32 layer_group_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 layer_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-image-merge-layer-group",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_LAYER, layer_group_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ layer_ID = return_vals[1].data.d_layer;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return layer_ID;
+}
+
+/**
+ * _gimp_image_get_colormap:
+ * @image_ID: The image.
+ * @num_bytes: Number of bytes in the colormap array.
+ *
+ * Returns the image's colormap
+ *
+ * This procedure returns an actual pointer to the image's colormap, as
+ * well as the number of bytes contained in the colormap. The actual
+ * number of colors in the transmitted colormap will be 'num-bytes' /
+ * 3. If the image is not in Indexed color mode, no colormap is
+ * returned.
+ *
+ * Returns: The image's colormap. The returned value must be freed with
+ * g_free().
+ **/
+guint8 *
+_gimp_image_get_colormap (gint32 image_ID,
+ gint *num_bytes)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ guint8 *colormap = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-colormap",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ *num_bytes = 0;
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ *num_bytes = return_vals[1].data.d_int32;
+ colormap = g_new (guint8, *num_bytes);
+ memcpy (colormap,
+ return_vals[2].data.d_int8array,
+ *num_bytes * sizeof (guint8));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return colormap;
+}
+
+/**
+ * _gimp_image_set_colormap:
+ * @image_ID: The image.
+ * @num_bytes: Number of bytes in the colormap array.
+ * @colormap: The new colormap values.
+ *
+ * Sets the entries in the image's colormap.
+ *
+ * This procedure sets the entries in the specified image's colormap.
+ * The number of entries is specified by the 'num-bytes' parameter and
+ * corresponds to the number of INT8 triples that must be contained in
+ * the 'colormap' array. The actual number of colors in the transmitted
+ * colormap is 'num-bytes' / 3.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+_gimp_image_set_colormap (gint32 image_ID,
+ gint num_bytes,
+ const guint8 *colormap)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-set-colormap",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, num_bytes,
+ GIMP_PDB_INT8ARRAY, colormap,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * _gimp_image_get_metadata:
+ * @image_ID: The image.
+ *
+ * Returns the image's metadata.
+ *
+ * Returns exif/iptc/xmp metadata from the image.
+ *
+ * Returns: The exif/ptc/xmp metadata as a string.
+ **/
+gchar *
+_gimp_image_get_metadata (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *metadata_string = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-metadata",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ metadata_string = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return metadata_string;
+}
+
+/**
+ * _gimp_image_set_metadata:
+ * @image_ID: The image.
+ * @metadata_string: The exif/ptc/xmp metadata as a string.
+ *
+ * Set the image's metadata.
+ *
+ * Sets exif/iptc/xmp metadata on the image.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+_gimp_image_set_metadata (gint32 image_ID,
+ const gchar *metadata_string)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-set-metadata",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_STRING, metadata_string,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_clean_all:
+ * @image_ID: The image.
+ *
+ * Set the image dirty count to 0.
+ *
+ * This procedure sets the specified image's dirty count to 0, allowing
+ * operations to occur without having a 'dirtied' image. This is
+ * especially useful for creating and loading images which should not
+ * initially be considered dirty, even though layers must be created,
+ * filled, and installed in the image. Note that save plug-ins must NOT
+ * call this function themselves after saving the image.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_clean_all (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-clean-all",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_is_dirty:
+ * @image_ID: The image.
+ *
+ * Checks if the image has unsaved changes.
+ *
+ * This procedure checks the specified image's dirty count to see if it
+ * needs to be saved. Note that saving the image does not automatically
+ * set the dirty count to 0, you need to call gimp_image_clean_all()
+ * after calling a save procedure to make the image clean.
+ *
+ * Returns: TRUE if the image has unsaved changes.
+ **/
+gboolean
+gimp_image_is_dirty (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean dirty = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-image-is-dirty",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ dirty = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return dirty;
+}
+
+/**
+ * _gimp_image_thumbnail:
+ * @image_ID: The image.
+ * @width: The requested thumbnail width.
+ * @height: The requested thumbnail height.
+ * @actual_width: The previews width.
+ * @actual_height: The previews height.
+ * @bpp: The previews bpp.
+ * @thumbnail_data_count: The number of bytes in thumbnail data.
+ * @thumbnail_data: The thumbnail data.
+ *
+ * Get a thumbnail of an image.
+ *
+ * This function gets data from which a thumbnail of an image preview
+ * can be created. Maximum x or y dimension is 1024 pixels. The pixels
+ * are returned in RGB[A] or GRAY[A] format. The bpp return value gives
+ * the number of bits per pixel in the image.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+_gimp_image_thumbnail (gint32 image_ID,
+ gint width,
+ gint height,
+ gint *actual_width,
+ gint *actual_height,
+ gint *bpp,
+ gint *thumbnail_data_count,
+ guint8 **thumbnail_data)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-thumbnail",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, width,
+ GIMP_PDB_INT32, height,
+ GIMP_PDB_END);
+
+ *actual_width = 0;
+ *actual_height = 0;
+ *bpp = 0;
+ *thumbnail_data_count = 0;
+ *thumbnail_data = NULL;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *actual_width = return_vals[1].data.d_int32;
+ *actual_height = return_vals[2].data.d_int32;
+ *bpp = return_vals[3].data.d_int32;
+ *thumbnail_data_count = return_vals[4].data.d_int32;
+ *thumbnail_data = g_new (guint8, *thumbnail_data_count);
+ memcpy (*thumbnail_data,
+ return_vals[5].data.d_int8array,
+ *thumbnail_data_count * sizeof (guint8));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_get_active_layer:
+ * @image_ID: The image.
+ *
+ * Returns the specified image's active layer.
+ *
+ * If there is an active layer, its ID will be returned, otherwise, -1.
+ * If a channel is currently active, then no layer will be. If a layer
+ * mask is active, then this will return the associated layer.
+ *
+ * Returns: The active layer.
+ **/
+gint32
+gimp_image_get_active_layer (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 active_layer_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-active-layer",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ active_layer_ID = return_vals[1].data.d_layer;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return active_layer_ID;
+}
+
+/**
+ * gimp_image_set_active_layer:
+ * @image_ID: The image.
+ * @active_layer_ID: The new image active layer.
+ *
+ * Sets the specified image's active layer.
+ *
+ * If the layer exists, it is set as the active layer in the image. Any
+ * previous active layer or channel is set to inactive. An exception is
+ * a previously existing floating selection, in which case this
+ * procedure will return an execution error.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_set_active_layer (gint32 image_ID,
+ gint32 active_layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-set-active-layer",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_LAYER, active_layer_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_get_active_channel:
+ * @image_ID: The image.
+ *
+ * Returns the specified image's active channel.
+ *
+ * If there is an active channel, this will return the channel ID,
+ * otherwise, -1.
+ *
+ * Returns: The active channel.
+ **/
+gint32
+gimp_image_get_active_channel (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 active_channel_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-active-channel",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ active_channel_ID = return_vals[1].data.d_channel;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return active_channel_ID;
+}
+
+/**
+ * gimp_image_set_active_channel:
+ * @image_ID: The image.
+ * @active_channel_ID: The new image active channel.
+ *
+ * Sets the specified image's active channel.
+ *
+ * If the channel exists, it is set as the active channel in the image.
+ * Any previous active channel or layer is set to inactive. An
+ * exception is a previously existing floating selection, in which case
+ * this procedure will return an execution error.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_set_active_channel (gint32 image_ID,
+ gint32 active_channel_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-set-active-channel",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_CHANNEL, active_channel_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_get_active_vectors:
+ * @image_ID: The image.
+ *
+ * Returns the specified image's active vectors.
+ *
+ * If there is an active path, its ID will be returned, otherwise, -1.
+ *
+ * Returns: The active vectors.
+ **/
+gint32
+gimp_image_get_active_vectors (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 active_vectors_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-active-vectors",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ active_vectors_ID = return_vals[1].data.d_vectors;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return active_vectors_ID;
+}
+
+/**
+ * gimp_image_set_active_vectors:
+ * @image_ID: The image.
+ * @active_vectors_ID: The new image active vectors.
+ *
+ * Sets the specified image's active vectors.
+ *
+ * If the path exists, it is set as the active path in the image.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_set_active_vectors (gint32 image_ID,
+ gint32 active_vectors_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-set-active-vectors",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_VECTORS, active_vectors_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_get_selection:
+ * @image_ID: The image.
+ *
+ * Returns the specified image's selection.
+ *
+ * This will always return a valid ID for a selection -- which is
+ * represented as a channel internally.
+ *
+ * Returns: The selection channel.
+ **/
+gint32
+gimp_image_get_selection (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 selection_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-selection",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ selection_ID = return_vals[1].data.d_selection;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return selection_ID;
+}
+
+/**
+ * gimp_image_get_component_active:
+ * @image_ID: The image.
+ * @component: The image component.
+ *
+ * Returns if the specified image's image component is active.
+ *
+ * This procedure returns if the specified image's image component
+ * (i.e. Red, Green, Blue intensity channels in an RGB image) is active
+ * or inactive -- whether or not it can be modified. If the specified
+ * component is not valid for the image type, an error is returned.
+ *
+ * Returns: Component is active.
+ **/
+gboolean
+gimp_image_get_component_active (gint32 image_ID,
+ GimpChannelType component)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean active = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-component-active",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, component,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ active = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return active;
+}
+
+/**
+ * gimp_image_set_component_active:
+ * @image_ID: The image.
+ * @component: The image component.
+ * @active: Component is active.
+ *
+ * Sets if the specified image's image component is active.
+ *
+ * This procedure sets if the specified image's image component (i.e.
+ * Red, Green, Blue intensity channels in an RGB image) is active or
+ * inactive -- whether or not it can be modified. If the specified
+ * component is not valid for the image type, an error is returned.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_set_component_active (gint32 image_ID,
+ GimpChannelType component,
+ gboolean active)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-set-component-active",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, component,
+ GIMP_PDB_INT32, active,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_get_component_visible:
+ * @image_ID: The image.
+ * @component: The image component.
+ *
+ * Returns if the specified image's image component is visible.
+ *
+ * This procedure returns if the specified image's image component
+ * (i.e. Red, Green, Blue intensity channels in an RGB image) is
+ * visible or invisible -- whether or not it can be seen. If the
+ * specified component is not valid for the image type, an error is
+ * returned.
+ *
+ * Returns: Component is visible.
+ **/
+gboolean
+gimp_image_get_component_visible (gint32 image_ID,
+ GimpChannelType component)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean visible = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-component-visible",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, component,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ visible = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return visible;
+}
+
+/**
+ * gimp_image_set_component_visible:
+ * @image_ID: The image.
+ * @component: The image component.
+ * @visible: Component is visible.
+ *
+ * Sets if the specified image's image component is visible.
+ *
+ * This procedure sets if the specified image's image component (i.e.
+ * Red, Green, Blue intensity channels in an RGB image) is visible or
+ * invisible -- whether or not it can be seen. If the specified
+ * component is not valid for the image type, an error is returned.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_set_component_visible (gint32 image_ID,
+ GimpChannelType component,
+ gboolean visible)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-set-component-visible",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, component,
+ GIMP_PDB_INT32, visible,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_get_filename:
+ * @image_ID: The image.
+ *
+ * Returns the specified image's filename.
+ *
+ * This procedure returns the specified image's filename in the
+ * filesystem encoding. The image has a filename only if it was loaded
+ * or imported from a file or has since been saved or exported.
+ * Otherwise, this function returns %NULL. See also
+ * gimp_image_get_uri().
+ *
+ * Returns: The filename. The returned value must be freed with
+ * g_free().
+ **/
+gchar *
+gimp_image_get_filename (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *filename = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-filename",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ filename = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return filename;
+}
+
+/**
+ * gimp_image_set_filename:
+ * @image_ID: The image.
+ * @filename: The new image filename.
+ *
+ * Sets the specified image's filename.
+ *
+ * This procedure sets the specified image's filename. The filename
+ * should be in the filesystem encoding.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_set_filename (gint32 image_ID,
+ const gchar *filename)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-set-filename",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_STRING, filename,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_get_uri:
+ * @image_ID: The image.
+ *
+ * Returns the URI for the specified image.
+ *
+ * This procedure returns the URI associated with the specified image.
+ * The image has an URI only if it was loaded or imported from a file
+ * or has since been saved or exported. Otherwise, this function
+ * returns %NULL. See also gimp-image-get-imported-uri to get the URI
+ * of the current file if it was imported from a non-GIMP file format
+ * and not yet saved, or gimp-image-get-exported-uri if the image has
+ * been exported to a non-GIMP file format.
+ *
+ * Returns: The URI. The returned value must be freed with g_free().
+ *
+ * Since: 2.8
+ **/
+gchar *
+gimp_image_get_uri (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *uri = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-uri",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ uri = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return uri;
+}
+
+/**
+ * gimp_image_get_xcf_uri:
+ * @image_ID: The image.
+ *
+ * Returns the XCF URI for the specified image.
+ *
+ * This procedure returns the XCF URI associated with the image. If
+ * there is no such URI, this procedure returns %NULL.
+ *
+ * Returns: The imported URI. The returned value must be freed with
+ * g_free().
+ *
+ * Since: 2.8
+ **/
+gchar *
+gimp_image_get_xcf_uri (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *uri = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-xcf-uri",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ uri = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return uri;
+}
+
+/**
+ * gimp_image_get_imported_uri:
+ * @image_ID: The image.
+ *
+ * Returns the imported URI for the specified image.
+ *
+ * This procedure returns the URI associated with the specified image
+ * if the image was imported from a non-native Gimp format. If the
+ * image was not imported, or has since been saved in the native Gimp
+ * format, this procedure returns %NULL.
+ *
+ * Returns: The imported URI. The returned value must be freed with
+ * g_free().
+ *
+ * Since: 2.8
+ **/
+gchar *
+gimp_image_get_imported_uri (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *uri = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-imported-uri",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ uri = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return uri;
+}
+
+/**
+ * gimp_image_get_exported_uri:
+ * @image_ID: The image.
+ *
+ * Returns the exported URI for the specified image.
+ *
+ * This procedure returns the URI associated with the specified image
+ * if the image was exported a non-native GIMP format. If the image was
+ * not exported, this procedure returns %NULL.
+ *
+ * Returns: The exported URI. The returned value must be freed with
+ * g_free().
+ *
+ * Since: 2.8
+ **/
+gchar *
+gimp_image_get_exported_uri (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *uri = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-exported-uri",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ uri = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return uri;
+}
+
+/**
+ * gimp_image_get_name:
+ * @image_ID: The image.
+ *
+ * Returns the specified image's name.
+ *
+ * This procedure returns the image's name. If the image has a filename
+ * or an URI, then the returned name contains the filename's or URI's
+ * base name (the last component of the path). Otherwise it is the
+ * translated string \"Untitled\". The returned name is formatted like
+ * the image name in the image window title, it may contain '[]',
+ * '(imported)' etc. and should only be used to label user interface
+ * elements. Never use it to construct filenames.
+ *
+ * Returns: The name. The returned value must be freed with g_free().
+ **/
+gchar *
+gimp_image_get_name (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-name",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ name = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return name;
+}
+
+/**
+ * gimp_image_get_resolution:
+ * @image_ID: The image.
+ * @xresolution: The resolution in the x-axis, in dots per inch.
+ * @yresolution: The resolution in the y-axis, in dots per inch.
+ *
+ * Returns the specified image's resolution.
+ *
+ * This procedure returns the specified image's resolution in dots per
+ * inch. This value is independent of any of the layers in this image.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_get_resolution (gint32 image_ID,
+ gdouble *xresolution,
+ gdouble *yresolution)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-resolution",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ *xresolution = 0.0;
+ *yresolution = 0.0;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *xresolution = return_vals[1].data.d_float;
+ *yresolution = return_vals[2].data.d_float;
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_set_resolution:
+ * @image_ID: The image.
+ * @xresolution: The new image resolution in the x-axis, in dots per inch.
+ * @yresolution: The new image resolution in the y-axis, in dots per inch.
+ *
+ * Sets the specified image's resolution.
+ *
+ * This procedure sets the specified image's resolution in dots per
+ * inch. This value is independent of any of the layers in this image.
+ * No scaling or resizing is performed.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_set_resolution (gint32 image_ID,
+ gdouble xresolution,
+ gdouble yresolution)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-set-resolution",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_FLOAT, xresolution,
+ GIMP_PDB_FLOAT, yresolution,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_get_unit:
+ * @image_ID: The image.
+ *
+ * Returns the specified image's unit.
+ *
+ * This procedure returns the specified image's unit. This value is
+ * independent of any of the layers in this image. See the
+ * gimp_unit_*() procedure definitions for the valid range of unit IDs
+ * and a description of the unit system.
+ *
+ * Returns: The unit.
+ **/
+GimpUnit
+gimp_image_get_unit (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpUnit unit = 0;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-unit",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ unit = return_vals[1].data.d_unit;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return unit;
+}
+
+/**
+ * gimp_image_set_unit:
+ * @image_ID: The image.
+ * @unit: The new image unit.
+ *
+ * Sets the specified image's unit.
+ *
+ * This procedure sets the specified image's unit. No scaling or
+ * resizing is performed. This value is independent of any of the
+ * layers in this image. See the gimp_unit_*() procedure definitions
+ * for the valid range of unit IDs and a description of the unit
+ * system.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_set_unit (gint32 image_ID,
+ GimpUnit unit)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-set-unit",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, unit,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_get_tattoo_state:
+ * @image_ID: The image.
+ *
+ * Returns the tattoo state associated with the image.
+ *
+ * This procedure returns the tattoo state of the image. Use only by
+ * save/load plug-ins that wish to preserve an images tattoo state.
+ * Using this function at other times will produce unexpected results.
+ *
+ * Returns: The tattoo state.
+ **/
+gint
+gimp_image_get_tattoo_state (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint tattoo_state = 0;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-tattoo-state",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ tattoo_state = return_vals[1].data.d_tattoo;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return tattoo_state;
+}
+
+/**
+ * gimp_image_set_tattoo_state:
+ * @image_ID: The image.
+ * @tattoo_state: The new image tattoo state.
+ *
+ * Set the tattoo state associated with the image.
+ *
+ * This procedure sets the tattoo state of the image. Use only by
+ * save/load plug-ins that wish to preserve an images tattoo state.
+ * Using this function at other times will produce unexpected results.
+ * A full check of uniqueness of states in layers, channels and paths
+ * will be performed by this procedure and a execution failure will be
+ * returned if this fails. A failure will also be returned if the new
+ * tattoo state value is less than the maximum tattoo value from all of
+ * the tattoos from the paths, layers and channels. After the image
+ * data has been loaded and all the tattoos have been set then this is
+ * the last procedure that should be called. If effectively does a
+ * status check on the tattoo values that have been set to make sure
+ * that all is OK.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_set_tattoo_state (gint32 image_ID,
+ gint tattoo_state)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-set-tattoo-state",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, tattoo_state,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_get_layer_by_tattoo:
+ * @image_ID: The image.
+ * @tattoo: The tattoo of the layer to find.
+ *
+ * Find a layer with a given tattoo in an image.
+ *
+ * This procedure returns the layer with the given tattoo in the
+ * specified image.
+ *
+ * Returns: The layer with the specified tattoo.
+ **/
+gint32
+gimp_image_get_layer_by_tattoo (gint32 image_ID,
+ gint tattoo)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 layer_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-layer-by-tattoo",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, tattoo,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ layer_ID = return_vals[1].data.d_layer;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return layer_ID;
+}
+
+/**
+ * gimp_image_get_channel_by_tattoo:
+ * @image_ID: The image.
+ * @tattoo: The tattoo of the channel to find.
+ *
+ * Find a channel with a given tattoo in an image.
+ *
+ * This procedure returns the channel with the given tattoo in the
+ * specified image.
+ *
+ * Returns: The channel with the specified tattoo.
+ **/
+gint32
+gimp_image_get_channel_by_tattoo (gint32 image_ID,
+ gint tattoo)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 channel_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-channel-by-tattoo",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, tattoo,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ channel_ID = return_vals[1].data.d_channel;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return channel_ID;
+}
+
+/**
+ * gimp_image_get_vectors_by_tattoo:
+ * @image_ID: The image.
+ * @tattoo: The tattoo of the vectors to find.
+ *
+ * Find a vectors with a given tattoo in an image.
+ *
+ * This procedure returns the vectors with the given tattoo in the
+ * specified image.
+ *
+ * Returns: The vectors with the specified tattoo.
+ *
+ * Since: 2.6
+ **/
+gint32
+gimp_image_get_vectors_by_tattoo (gint32 image_ID,
+ gint tattoo)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 vectors_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-vectors-by-tattoo",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, tattoo,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ vectors_ID = return_vals[1].data.d_vectors;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return vectors_ID;
+}
+
+/**
+ * gimp_image_get_layer_by_name:
+ * @image_ID: The image.
+ * @name: The name of the layer to find.
+ *
+ * Find a layer with a given name in an image.
+ *
+ * This procedure returns the layer with the given name in the
+ * specified image.
+ *
+ * Returns: The layer with the specified name.
+ *
+ * Since: 2.8
+ **/
+gint32
+gimp_image_get_layer_by_name (gint32 image_ID,
+ const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 layer_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-layer-by-name",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ layer_ID = return_vals[1].data.d_layer;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return layer_ID;
+}
+
+/**
+ * gimp_image_get_channel_by_name:
+ * @image_ID: The image.
+ * @name: The name of the channel to find.
+ *
+ * Find a channel with a given name in an image.
+ *
+ * This procedure returns the channel with the given name in the
+ * specified image.
+ *
+ * Returns: The channel with the specified name.
+ *
+ * Since: 2.8
+ **/
+gint32
+gimp_image_get_channel_by_name (gint32 image_ID,
+ const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 channel_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-channel-by-name",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ channel_ID = return_vals[1].data.d_channel;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return channel_ID;
+}
+
+/**
+ * gimp_image_get_vectors_by_name:
+ * @image_ID: The image.
+ * @name: The name of the vectors to find.
+ *
+ * Find a vectors with a given name in an image.
+ *
+ * This procedure returns the vectors with the given name in the
+ * specified image.
+ *
+ * Returns: The vectors with the specified name.
+ *
+ * Since: 2.8
+ **/
+gint32
+gimp_image_get_vectors_by_name (gint32 image_ID,
+ const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 vectors_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-vectors-by-name",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ vectors_ID = return_vals[1].data.d_vectors;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return vectors_ID;
+}
+
+/**
+ * gimp_image_attach_parasite:
+ * @image_ID: The image.
+ * @parasite: The parasite to attach to an image.
+ *
+ * Add a parasite to an image.
+ *
+ * This procedure attaches a parasite to an image. It has no return
+ * values.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_image_attach_parasite (gint32 image_ID,
+ const GimpParasite *parasite)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-attach-parasite",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_PARASITE, parasite,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_detach_parasite:
+ * @image_ID: The image.
+ * @name: The name of the parasite to detach from an image.
+ *
+ * Removes a parasite from an image.
+ *
+ * This procedure detaches a parasite from an image. It has no return
+ * values.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_image_detach_parasite (gint32 image_ID,
+ const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-detach-parasite",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_get_parasite:
+ * @image_ID: The image.
+ * @name: The name of the parasite to find.
+ *
+ * Look up a parasite in an image
+ *
+ * Finds and returns the parasite that was previously attached to an
+ * image.
+ *
+ * Returns: The found parasite.
+ *
+ * Since: 2.8
+ **/
+GimpParasite *
+gimp_image_get_parasite (gint32 image_ID,
+ const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpParasite *parasite = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-parasite",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ parasite = gimp_parasite_copy (&return_vals[1].data.d_parasite);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return parasite;
+}
+
+/**
+ * gimp_image_get_parasite_list:
+ * @image_ID: The image.
+ * @num_parasites: The number of attached parasites.
+ *
+ * List all parasites.
+ *
+ * Returns a list of all currently attached parasites.
+ *
+ * Returns: The names of currently attached parasites. The returned
+ * value must be freed with g_strfreev().
+ *
+ * Since: 2.8
+ **/
+gchar **
+gimp_image_get_parasite_list (gint32 image_ID,
+ gint *num_parasites)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar **parasites = NULL;
+ gint i;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-parasite-list",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ *num_parasites = 0;
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ *num_parasites = return_vals[1].data.d_int32;
+ if (*num_parasites > 0)
+ {
+ parasites = g_new0 (gchar *, *num_parasites + 1);
+ for (i = 0; i < *num_parasites; i++)
+ parasites[i] = g_strdup (return_vals[2].data.d_stringarray[i]);
+ }
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return parasites;
+}
diff --git a/libgimp/gimpimage_pdb.h b/libgimp/gimpimage_pdb.h
new file mode 100644
index 0000000..71f4776
--- /dev/null
+++ b/libgimp/gimpimage_pdb.h
@@ -0,0 +1,214 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpimage_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_IMAGE_PDB_H__
+#define __GIMP_IMAGE_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_image_is_valid (gint32 image_ID);
+gint* gimp_image_list (gint *num_images);
+gint32 gimp_image_new (gint width,
+ gint height,
+ GimpImageBaseType type);
+gint32 gimp_image_new_with_precision (gint width,
+ gint height,
+ GimpImageBaseType type,
+ GimpPrecision precision);
+gint32 gimp_image_duplicate (gint32 image_ID);
+gboolean gimp_image_delete (gint32 image_ID);
+GimpImageBaseType gimp_image_base_type (gint32 image_ID);
+GimpPrecision gimp_image_get_precision (gint32 image_ID);
+GimpLayerMode gimp_image_get_default_new_layer_mode (gint32 image_ID);
+gint gimp_image_width (gint32 image_ID);
+gint gimp_image_height (gint32 image_ID);
+GIMP_DEPRECATED_FOR(gimp_drawable_free_shadow)
+gboolean gimp_image_free_shadow (gint32 image_ID);
+gint* gimp_image_get_layers (gint32 image_ID,
+ gint *num_layers);
+gint* gimp_image_get_channels (gint32 image_ID,
+ gint *num_channels);
+gint* gimp_image_get_vectors (gint32 image_ID,
+ gint *num_vectors);
+gint32 gimp_image_get_active_drawable (gint32 image_ID);
+gboolean gimp_image_unset_active_channel (gint32 image_ID);
+gint32 gimp_image_get_floating_sel (gint32 image_ID);
+gint32 gimp_image_floating_sel_attached_to (gint32 image_ID);
+gboolean gimp_image_pick_color (gint32 image_ID,
+ gint32 drawable_ID,
+ gdouble x,
+ gdouble y,
+ gboolean sample_merged,
+ gboolean sample_average,
+ gdouble average_radius,
+ GimpRGB *color);
+gint32 gimp_image_pick_correlate_layer (gint32 image_ID,
+ gint x,
+ gint y);
+GIMP_DEPRECATED_FOR(gimp_image_insert_layer)
+gboolean gimp_image_add_layer (gint32 image_ID,
+ gint32 layer_ID,
+ gint position);
+gboolean gimp_image_insert_layer (gint32 image_ID,
+ gint32 layer_ID,
+ gint32 parent_ID,
+ gint position);
+gboolean gimp_image_remove_layer (gint32 image_ID,
+ gint32 layer_ID);
+gboolean gimp_image_freeze_layers (gint32 image_ID);
+gboolean gimp_image_thaw_layers (gint32 image_ID);
+GIMP_DEPRECATED_FOR(gimp_image_insert_channel)
+gboolean gimp_image_add_channel (gint32 image_ID,
+ gint32 channel_ID,
+ gint position);
+gboolean gimp_image_insert_channel (gint32 image_ID,
+ gint32 channel_ID,
+ gint32 parent_ID,
+ gint position);
+gboolean gimp_image_remove_channel (gint32 image_ID,
+ gint32 channel_ID);
+gboolean gimp_image_freeze_channels (gint32 image_ID);
+gboolean gimp_image_thaw_channels (gint32 image_ID);
+GIMP_DEPRECATED_FOR(gimp_image_insert_vectors)
+gboolean gimp_image_add_vectors (gint32 image_ID,
+ gint32 vectors_ID,
+ gint position);
+gboolean gimp_image_insert_vectors (gint32 image_ID,
+ gint32 vectors_ID,
+ gint32 parent_ID,
+ gint position);
+gboolean gimp_image_remove_vectors (gint32 image_ID,
+ gint32 vectors_ID);
+gboolean gimp_image_freeze_vectors (gint32 image_ID);
+gboolean gimp_image_thaw_vectors (gint32 image_ID);
+gint gimp_image_get_item_position (gint32 image_ID,
+ gint32 item_ID);
+gboolean gimp_image_raise_item (gint32 image_ID,
+ gint32 item_ID);
+gboolean gimp_image_lower_item (gint32 image_ID,
+ gint32 item_ID);
+gboolean gimp_image_raise_item_to_top (gint32 image_ID,
+ gint32 item_ID);
+gboolean gimp_image_lower_item_to_bottom (gint32 image_ID,
+ gint32 item_ID);
+gboolean gimp_image_reorder_item (gint32 image_ID,
+ gint32 item_ID,
+ gint32 parent_ID,
+ gint position);
+gint32 gimp_image_flatten (gint32 image_ID);
+gint32 gimp_image_merge_visible_layers (gint32 image_ID,
+ GimpMergeType merge_type);
+gint32 gimp_image_merge_down (gint32 image_ID,
+ gint32 merge_layer_ID,
+ GimpMergeType merge_type);
+gint32 gimp_image_merge_layer_group (gint32 image_ID,
+ gint32 layer_group_ID);
+G_GNUC_INTERNAL guint8* _gimp_image_get_colormap (gint32 image_ID,
+ gint *num_bytes);
+G_GNUC_INTERNAL gboolean _gimp_image_set_colormap (gint32 image_ID,
+ gint num_bytes,
+ const guint8 *colormap);
+G_GNUC_INTERNAL gchar* _gimp_image_get_metadata (gint32 image_ID);
+G_GNUC_INTERNAL gboolean _gimp_image_set_metadata (gint32 image_ID,
+ const gchar *metadata_string);
+gboolean gimp_image_clean_all (gint32 image_ID);
+gboolean gimp_image_is_dirty (gint32 image_ID);
+G_GNUC_INTERNAL gboolean _gimp_image_thumbnail (gint32 image_ID,
+ gint width,
+ gint height,
+ gint *actual_width,
+ gint *actual_height,
+ gint *bpp,
+ gint *thumbnail_data_count,
+ guint8 **thumbnail_data);
+gint32 gimp_image_get_active_layer (gint32 image_ID);
+gboolean gimp_image_set_active_layer (gint32 image_ID,
+ gint32 active_layer_ID);
+gint32 gimp_image_get_active_channel (gint32 image_ID);
+gboolean gimp_image_set_active_channel (gint32 image_ID,
+ gint32 active_channel_ID);
+gint32 gimp_image_get_active_vectors (gint32 image_ID);
+gboolean gimp_image_set_active_vectors (gint32 image_ID,
+ gint32 active_vectors_ID);
+gint32 gimp_image_get_selection (gint32 image_ID);
+gboolean gimp_image_get_component_active (gint32 image_ID,
+ GimpChannelType component);
+gboolean gimp_image_set_component_active (gint32 image_ID,
+ GimpChannelType component,
+ gboolean active);
+gboolean gimp_image_get_component_visible (gint32 image_ID,
+ GimpChannelType component);
+gboolean gimp_image_set_component_visible (gint32 image_ID,
+ GimpChannelType component,
+ gboolean visible);
+gchar* gimp_image_get_filename (gint32 image_ID);
+gboolean gimp_image_set_filename (gint32 image_ID,
+ const gchar *filename);
+gchar* gimp_image_get_uri (gint32 image_ID);
+gchar* gimp_image_get_xcf_uri (gint32 image_ID);
+gchar* gimp_image_get_imported_uri (gint32 image_ID);
+gchar* gimp_image_get_exported_uri (gint32 image_ID);
+gchar* gimp_image_get_name (gint32 image_ID);
+gboolean gimp_image_get_resolution (gint32 image_ID,
+ gdouble *xresolution,
+ gdouble *yresolution);
+gboolean gimp_image_set_resolution (gint32 image_ID,
+ gdouble xresolution,
+ gdouble yresolution);
+GimpUnit gimp_image_get_unit (gint32 image_ID);
+gboolean gimp_image_set_unit (gint32 image_ID,
+ GimpUnit unit);
+gint gimp_image_get_tattoo_state (gint32 image_ID);
+gboolean gimp_image_set_tattoo_state (gint32 image_ID,
+ gint tattoo_state);
+gint32 gimp_image_get_layer_by_tattoo (gint32 image_ID,
+ gint tattoo);
+gint32 gimp_image_get_channel_by_tattoo (gint32 image_ID,
+ gint tattoo);
+gint32 gimp_image_get_vectors_by_tattoo (gint32 image_ID,
+ gint tattoo);
+gint32 gimp_image_get_layer_by_name (gint32 image_ID,
+ const gchar *name);
+gint32 gimp_image_get_channel_by_name (gint32 image_ID,
+ const gchar *name);
+gint32 gimp_image_get_vectors_by_name (gint32 image_ID,
+ const gchar *name);
+gboolean gimp_image_attach_parasite (gint32 image_ID,
+ const GimpParasite *parasite);
+gboolean gimp_image_detach_parasite (gint32 image_ID,
+ const gchar *name);
+GimpParasite* gimp_image_get_parasite (gint32 image_ID,
+ const gchar *name);
+gchar** gimp_image_get_parasite_list (gint32 image_ID,
+ gint *num_parasites);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_IMAGE_PDB_H__ */
diff --git a/libgimp/gimpimagecolorprofile.c b/libgimp/gimpimagecolorprofile.c
new file mode 100644
index 0000000..0968445
--- /dev/null
+++ b/libgimp/gimpimagecolorprofile.c
@@ -0,0 +1,173 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpimagecolorprofile.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * gimp_image_get_color_profile:
+ * @image_ID: The image.
+ *
+ * Returns the image's color profile
+ *
+ * This procedure returns the image's color profile, or NULL if the
+ * image has no color profile assigned.
+ *
+ * Returns: The image's color profile. The returned value
+ * must be freed with g_object_unref().
+ *
+ * Since: 2.10
+ **/
+GimpColorProfile *
+gimp_image_get_color_profile (gint32 image_ID)
+{
+ guint8 *data;
+ gint length;
+
+ data = _gimp_image_get_color_profile (image_ID, &length);
+
+ if (data)
+ {
+ GimpColorProfile *profile;
+
+ profile = gimp_color_profile_new_from_icc_profile (data, length, NULL);
+ g_free (data);
+
+ return profile;
+ }
+
+ return NULL;
+}
+
+/**
+ * gimp_image_set_color_profile:
+ * @image_ID: The image.
+ * @profile: A #GimpColorProfile, or %NULL.
+ *
+ * Sets the image's color profile
+ *
+ * This procedure sets the image's color profile.
+ *
+ * Returns: %TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_image_set_color_profile (gint32 image_ID,
+ GimpColorProfile *profile)
+{
+ const guint8 *data = NULL;
+ gint length = 0;
+
+ g_return_val_if_fail (profile == NULL || GIMP_IS_COLOR_PROFILE (profile),
+ FALSE);
+
+ if (profile)
+ {
+ gsize l;
+
+ data = gimp_color_profile_get_icc_profile (profile, &l);
+ length = l;
+ }
+
+ return _gimp_image_set_color_profile (image_ID, length, data);
+}
+
+/**
+ * gimp_image_get_effective_color_profile:
+ * @image_ID: The image.
+ *
+ * Returns the color profile that is used for the image.
+ *
+ * This procedure returns the color profile that is actually used for
+ * this image, which is the profile returned by
+ * gimp_image_get_color_profile() if the image has a profile assigned,
+ * or the default RGB profile from preferences if no profile is
+ * assigned to the image. If there is no default RGB profile configured
+ * in preferences either, a generated default RGB profile is returned.
+ *
+ * Returns: The color profile. The returned value
+ * must be freed with g_object_unref().
+ *
+ * Since: 2.10
+ **/
+GimpColorProfile *
+gimp_image_get_effective_color_profile (gint32 image_ID)
+{
+ guint8 *data;
+ gint length;
+
+ data = _gimp_image_get_effective_color_profile (image_ID, &length);
+
+ if (data)
+ {
+ GimpColorProfile *profile;
+
+ profile = gimp_color_profile_new_from_icc_profile (data, length, NULL);
+ g_free (data);
+
+ return profile;
+ }
+
+ return NULL;
+}
+
+/**
+ * gimp_image_convert_color_profile:
+ * @image_ID: The image.
+ * @profile: The color profile to convert to.
+ * @intent: Rendering intent.
+ * @bpc: Black point compensation.
+ *
+ * Convert the image's layers to a color profile
+ *
+ * This procedure converts from the image's color profile (or the
+ * default RGB profile if none is set) to the given color profile. Only
+ * RGB color profiles are accepted.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_image_convert_color_profile (gint32 image_ID,
+ GimpColorProfile *profile,
+ GimpColorRenderingIntent intent,
+ gboolean bpc)
+{
+ const guint8 *data = NULL;
+ gint length = 0;
+
+ g_return_val_if_fail (profile == NULL || GIMP_IS_COLOR_PROFILE (profile),
+ FALSE);
+
+ if (profile)
+ {
+ gsize l;
+
+ data = gimp_color_profile_get_icc_profile (profile, &l);
+ length = l;
+ }
+
+ return _gimp_image_convert_color_profile (image_ID, length, data,
+ intent, bpc);
+}
diff --git a/libgimp/gimpimagecolorprofile.h b/libgimp/gimpimagecolorprofile.h
new file mode 100644
index 0000000..15defcc
--- /dev/null
+++ b/libgimp/gimpimagecolorprofile.h
@@ -0,0 +1,47 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpimagecolorprofile.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_IMAGE_COLOR_PROFILE_H__
+#define __GIMP_IMAGE_COLOR_PROFILE_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+GimpColorProfile * gimp_image_get_color_profile (gint32 image_ID);
+gboolean gimp_image_set_color_profile (gint32 image_ID,
+ GimpColorProfile *profile);
+
+GimpColorProfile * gimp_image_get_effective_color_profile (gint32 image_ID);
+
+gboolean gimp_image_convert_color_profile (gint32 image_ID,
+ GimpColorProfile *profile,
+ GimpColorRenderingIntent intent,
+ gboolean bpc);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_IMAGE_COLOR_PROFILE_H__ */
diff --git a/libgimp/gimpimagecolorprofile_pdb.c b/libgimp/gimpimagecolorprofile_pdb.c
new file mode 100644
index 0000000..ba55760
--- /dev/null
+++ b/libgimp/gimpimagecolorprofile_pdb.c
@@ -0,0 +1,292 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpimagecolorprofile_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpimagecolorprofile
+ * @title: gimpimagecolorprofile
+ * @short_description: Operations on an image's color profile.
+ *
+ * Operations on an image's color profile.
+ **/
+
+
+/**
+ * _gimp_image_get_color_profile:
+ * @image_ID: The image.
+ * @num_bytes: Number of bytes in the color_profile array.
+ *
+ * Returns the image's color profile
+ *
+ * This procedure returns the image's color profile, or NULL if the
+ * image has no color profile assigned.
+ *
+ * Returns: The image's serialized color profile. The returned value
+ * must be freed with g_free().
+ *
+ * Since: 2.10
+ **/
+guint8 *
+_gimp_image_get_color_profile (gint32 image_ID,
+ gint *num_bytes)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ guint8 *profile_data = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-color-profile",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ *num_bytes = 0;
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ *num_bytes = return_vals[1].data.d_int32;
+ profile_data = g_new (guint8, *num_bytes);
+ memcpy (profile_data,
+ return_vals[2].data.d_int8array,
+ *num_bytes * sizeof (guint8));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return profile_data;
+}
+
+/**
+ * _gimp_image_get_effective_color_profile:
+ * @image_ID: The image.
+ * @num_bytes: Number of bytes in the color_profile array.
+ *
+ * Returns the color profile that is used for the image
+ *
+ * This procedure returns the color profile that is actually used for
+ * this image, which is the profile returned by
+ * gimp_image_get_color_profile() if the image has a profile assigned,
+ * or a generated default RGB or grayscale profile, according to the
+ * image's type.
+ *
+ * Returns: The image's serialized color profile. The returned value
+ * must be freed with g_free().
+ *
+ * Since: 2.10
+ **/
+guint8 *
+_gimp_image_get_effective_color_profile (gint32 image_ID,
+ gint *num_bytes)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ guint8 *profile_data = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-effective-color-profile",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ *num_bytes = 0;
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ *num_bytes = return_vals[1].data.d_int32;
+ profile_data = g_new (guint8, *num_bytes);
+ memcpy (profile_data,
+ return_vals[2].data.d_int8array,
+ *num_bytes * sizeof (guint8));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return profile_data;
+}
+
+/**
+ * _gimp_image_set_color_profile:
+ * @image_ID: The image.
+ * @num_bytes: Number of bytes in the color_profile array.
+ * @color_profile: The new serialized color profile.
+ *
+ * Sets the image's color profile
+ *
+ * This procedure sets the image's color profile, or unsets it if NULL
+ * is passed as 'color_profile'. This procedure does no color
+ * conversion.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+_gimp_image_set_color_profile (gint32 image_ID,
+ gint num_bytes,
+ const guint8 *color_profile)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-set-color-profile",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, num_bytes,
+ GIMP_PDB_INT8ARRAY, color_profile,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_set_color_profile_from_file:
+ * @image_ID: The image.
+ * @uri: The URI of the file containing the new color profile.
+ *
+ * Sets the image's color profile from an ICC file
+ *
+ * This procedure sets the image's color profile from a file containing
+ * an ICC profile, or unsets it if NULL is passed as 'uri'. This
+ * procedure does no color conversion.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_image_set_color_profile_from_file (gint32 image_ID,
+ const gchar *uri)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-set-color-profile-from-file",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_STRING, uri,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * _gimp_image_convert_color_profile:
+ * @image_ID: The image.
+ * @num_bytes: Number of bytes in the color_profile array.
+ * @color_profile: The serialized color profile.
+ * @intent: Rendering intent.
+ * @bpc: Black point compensation.
+ *
+ * Convert the image's layers to a color profile
+ *
+ * This procedure converts from the image's color profile (or the
+ * default RGB or grayscale profile if none is set) to the given color
+ * profile. Only RGB and grayscale color profiles are accepted,
+ * according to the image's type.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+_gimp_image_convert_color_profile (gint32 image_ID,
+ gint num_bytes,
+ const guint8 *color_profile,
+ GimpColorRenderingIntent intent,
+ gboolean bpc)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-convert-color-profile",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, num_bytes,
+ GIMP_PDB_INT8ARRAY, color_profile,
+ GIMP_PDB_INT32, intent,
+ GIMP_PDB_INT32, bpc,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_convert_color_profile_from_file:
+ * @image_ID: The image.
+ * @uri: The URI of the file containing the new color profile.
+ * @intent: Rendering intent.
+ * @bpc: Black point compensation.
+ *
+ * Convert the image's layers to a color profile
+ *
+ * This procedure converts from the image's color profile (or the
+ * default RGB or grayscale profile if none is set) to an ICC profile
+ * specified by 'uri'. Only RGB and grayscale color profiles are
+ * accepted, according to the image's type.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_image_convert_color_profile_from_file (gint32 image_ID,
+ const gchar *uri,
+ GimpColorRenderingIntent intent,
+ gboolean bpc)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-convert-color-profile-from-file",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_STRING, uri,
+ GIMP_PDB_INT32, intent,
+ GIMP_PDB_INT32, bpc,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimpimagecolorprofile_pdb.h b/libgimp/gimpimagecolorprofile_pdb.h
new file mode 100644
index 0000000..4a76cbf
--- /dev/null
+++ b/libgimp/gimpimagecolorprofile_pdb.h
@@ -0,0 +1,57 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpimagecolorprofile_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_IMAGE_COLOR_PROFILE_PDB_H__
+#define __GIMP_IMAGE_COLOR_PROFILE_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+G_GNUC_INTERNAL guint8* _gimp_image_get_color_profile (gint32 image_ID,
+ gint *num_bytes);
+G_GNUC_INTERNAL guint8* _gimp_image_get_effective_color_profile (gint32 image_ID,
+ gint *num_bytes);
+G_GNUC_INTERNAL gboolean _gimp_image_set_color_profile (gint32 image_ID,
+ gint num_bytes,
+ const guint8 *color_profile);
+gboolean gimp_image_set_color_profile_from_file (gint32 image_ID,
+ const gchar *uri);
+G_GNUC_INTERNAL gboolean _gimp_image_convert_color_profile (gint32 image_ID,
+ gint num_bytes,
+ const guint8 *color_profile,
+ GimpColorRenderingIntent intent,
+ gboolean bpc);
+gboolean gimp_image_convert_color_profile_from_file (gint32 image_ID,
+ const gchar *uri,
+ GimpColorRenderingIntent intent,
+ gboolean bpc);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_IMAGE_COLOR_PROFILE_PDB_H__ */
diff --git a/libgimp/gimpimagecombobox.c b/libgimp/gimpimagecombobox.c
new file mode 100644
index 0000000..99aee7e
--- /dev/null
+++ b/libgimp/gimpimagecombobox.c
@@ -0,0 +1,272 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpimagecombobox.c
+ * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "gimp.h"
+
+#include "gimpuitypes.h"
+#include "gimpimagecombobox.h"
+#include "gimppixbuf.h"
+
+
+/**
+ * SECTION: gimpimagecombobox
+ * @title: GimpImageComboBox
+ * @short_description: A widget providing a popup menu of images.
+ *
+ * A widget providing a popup menu of images.
+ **/
+
+
+#define THUMBNAIL_SIZE 24
+#define WIDTH_REQUEST 200
+
+
+typedef struct _GimpImageComboBoxClass GimpImageComboBoxClass;
+
+struct _GimpImageComboBox
+{
+ GimpIntComboBox parent_instance;
+
+ GimpImageConstraintFunc constraint;
+ gpointer data;
+};
+
+struct _GimpImageComboBoxClass
+{
+ GimpIntComboBoxClass parent_class;
+};
+
+
+static void gimp_image_combo_box_populate (GimpImageComboBox *combo_box);
+static void gimp_image_combo_box_model_add (GtkListStore *store,
+ gint num_images,
+ gint32 *images,
+ GimpImageConstraintFunc constraint,
+ gpointer data);
+
+static void gimp_image_combo_box_drag_data_received (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time);
+
+static void gimp_image_combo_box_changed (GimpImageComboBox *combo_box);
+
+
+static const GtkTargetEntry target = { "application/x-gimp-image-id", 0 };
+
+
+G_DEFINE_TYPE (GimpImageComboBox, gimp_image_combo_box, GIMP_TYPE_INT_COMBO_BOX)
+
+
+static void
+gimp_image_combo_box_class_init (GimpImageComboBoxClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ widget_class->drag_data_received = gimp_image_combo_box_drag_data_received;
+}
+
+static void
+gimp_image_combo_box_init (GimpImageComboBox *combo_box)
+{
+ gtk_drag_dest_set (GTK_WIDGET (combo_box),
+ GTK_DEST_DEFAULT_HIGHLIGHT |
+ GTK_DEST_DEFAULT_MOTION |
+ GTK_DEST_DEFAULT_DROP,
+ &target, 1,
+ GDK_ACTION_COPY);
+}
+
+/**
+ * gimp_image_combo_box_new:
+ * @constraint: a #GimpImageConstraintFunc or %NULL
+ * @data: a pointer that is passed to @constraint
+ *
+ * Creates a new #GimpIntComboBox filled with all currently opened
+ * images. If a @constraint function is specified, it is called for
+ * each image and only if the function returns %TRUE, the image is
+ * added to the combobox.
+ *
+ * You should use gimp_int_combo_box_connect() to initialize and
+ * connect the combo. Use gimp_int_combo_box_set_active() to get the
+ * active image ID and gimp_int_combo_box_get_active() to retrieve the
+ * ID of the selected image.
+ *
+ * Return value: a new #GimpIntComboBox.
+ *
+ * Since: 2.2
+ **/
+GtkWidget *
+gimp_image_combo_box_new (GimpImageConstraintFunc constraint,
+ gpointer data)
+{
+ GimpImageComboBox *combo_box;
+
+ combo_box = g_object_new (GIMP_TYPE_IMAGE_COMBO_BOX,
+ "width-request", WIDTH_REQUEST,
+ "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
+ NULL);
+
+ combo_box->constraint = constraint;
+ combo_box->data = data;
+
+ gimp_image_combo_box_populate (combo_box);
+
+ g_signal_connect (combo_box, "changed",
+ G_CALLBACK (gimp_image_combo_box_changed),
+ NULL);
+
+ return GTK_WIDGET (combo_box);
+}
+
+static void
+gimp_image_combo_box_populate (GimpImageComboBox *combo_box)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gint32 *images;
+ gint num_images;
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));
+
+ images = gimp_image_list (&num_images);
+
+ gimp_image_combo_box_model_add (GTK_LIST_STORE (model),
+ num_images, images,
+ combo_box->constraint,
+ combo_box->data);
+
+ g_free (images);
+
+ if (gtk_tree_model_get_iter_first (model, &iter))
+ gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_box), &iter);
+}
+
+static void
+gimp_image_combo_box_model_add (GtkListStore *store,
+ gint num_images,
+ gint32 *images,
+ GimpImageConstraintFunc constraint,
+ gpointer data)
+{
+ GtkTreeIter iter;
+ gint i;
+
+ for (i = 0; i < num_images; i++)
+ {
+ if (! constraint || (* constraint) (images[i], data))
+ {
+ gchar *image_name = gimp_image_get_name (images[i]);
+ gchar *label;
+ GdkPixbuf *thumb;
+
+ label = g_strdup_printf ("%s-%d", image_name, images[i]);
+
+ g_free (image_name);
+
+ thumb = gimp_image_get_thumbnail (images[i],
+ THUMBNAIL_SIZE, THUMBNAIL_SIZE,
+ GIMP_PIXBUF_SMALL_CHECKS);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ GIMP_INT_STORE_VALUE, images[i],
+ GIMP_INT_STORE_LABEL, label,
+ GIMP_INT_STORE_PIXBUF, thumb,
+ -1);
+
+ if (thumb)
+ g_object_unref (thumb);
+
+ g_free (label);
+ }
+ }
+}
+
+static void
+gimp_image_combo_box_drag_data_received (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time)
+{
+ gint length = gtk_selection_data_get_length (selection);
+ gchar *str;
+
+ if (gtk_selection_data_get_format (selection) != 8 || length < 1)
+ {
+ g_warning ("%s: received invalid image ID data", G_STRFUNC);
+ return;
+ }
+
+ str = g_strndup ((const gchar *) gtk_selection_data_get_data (selection),
+ length);
+
+ if (g_utf8_validate (str, -1, NULL))
+ {
+ gint pid;
+ gint ID;
+
+ if (sscanf (str, "%i:%i", &pid, &ID) == 2 &&
+ pid == gimp_getpid ())
+ {
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (widget), ID);
+ }
+ }
+
+ g_free (str);
+}
+
+static void
+gimp_image_combo_box_changed (GimpImageComboBox *combo_box)
+{
+ gint image_ID;
+
+ if (gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo_box),
+ &image_ID))
+ {
+ if (! gimp_image_is_valid (image_ID))
+ {
+ GtkTreeModel *model;
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));
+
+ g_signal_stop_emission_by_name (combo_box, "changed");
+
+ gtk_list_store_clear (GTK_LIST_STORE (model));
+ gimp_image_combo_box_populate (combo_box);
+ }
+ }
+}
diff --git a/libgimp/gimpimagecombobox.h b/libgimp/gimpimagecombobox.h
new file mode 100644
index 0000000..146d0da
--- /dev/null
+++ b/libgimp/gimpimagecombobox.h
@@ -0,0 +1,51 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpimagecombobox.h
+ * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_UI_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimpui.h> can be included directly."
+#endif
+
+#ifndef __GIMP_IMAGE_COMBO_BOX_H__
+#define __GIMP_IMAGE_COMBO_BOX_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+#define GIMP_TYPE_IMAGE_COMBO_BOX (gimp_image_combo_box_get_type ())
+#define GIMP_IMAGE_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_IMAGE_COMBO_BOX, GimpImageComboBox))
+#define GIMP_IS_IMAGE_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_IMAGE_COMBO_BOX)
+
+
+typedef gboolean (* GimpImageConstraintFunc) (gint32 image_id,
+ gpointer data);
+
+
+GType gimp_image_combo_box_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_image_combo_box_new (GimpImageConstraintFunc constraint,
+ gpointer data);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_IMAGE_COMBO_BOX_H__ */
diff --git a/libgimp/gimpimageconvert_pdb.c b/libgimp/gimpimageconvert_pdb.c
new file mode 100644
index 0000000..df72667
--- /dev/null
+++ b/libgimp/gimpimageconvert_pdb.c
@@ -0,0 +1,233 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpimageconvert_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpimageconvert
+ * @title: gimpimageconvert
+ * @short_description: Conversions between RGB, indexed, and grayscale modes.
+ *
+ * Conversions between RGB, indexed, and grayscale modes.
+ **/
+
+
+/**
+ * gimp_image_convert_rgb:
+ * @image_ID: The image.
+ *
+ * Convert specified image to RGB color
+ *
+ * This procedure converts the specified image to RGB color. This
+ * process requires an image in Grayscale or Indexed color mode. No
+ * image content is lost in this process aside from the colormap for an
+ * indexed image.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_convert_rgb (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-convert-rgb",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_convert_grayscale:
+ * @image_ID: The image.
+ *
+ * Convert specified image to grayscale
+ *
+ * This procedure converts the specified image to grayscale. This
+ * process requires an image in RGB or Indexed color mode.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_convert_grayscale (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-convert-grayscale",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_convert_indexed:
+ * @image_ID: The image.
+ * @dither_type: The dither type to use.
+ * @palette_type: The type of palette to use.
+ * @num_cols: The number of colors to quantize to, ignored unless (palette_type == GIMP_CONVERT_PALETTE_GENERATE).
+ * @alpha_dither: Dither transparency to fake partial opacity.
+ * @remove_unused: Remove unused or duplicate color entries from final palette, ignored if (palette_type == GIMP_CONVERT_PALETTE_GENERATE).
+ * @palette: The name of the custom palette to use, ignored unless (palette_type == GIMP_CONVERT_PALETTE_CUSTOM).
+ *
+ * Convert specified image to and Indexed image
+ *
+ * This procedure converts the specified image to 'indexed' color. This
+ * process requires an image in RGB or Grayscale mode. The
+ * 'palette_type' specifies what kind of palette to use, A type of '0'
+ * means to use an optimal palette of 'num_cols' generated from the
+ * colors in the image. A type of '1' means to re-use the previous
+ * palette (not currently implemented). A type of '2' means to use the
+ * so-called WWW-optimized palette. Type '3' means to use only black
+ * and white colors. A type of '4' means to use a palette from the gimp
+ * palettes directories. The 'dither type' specifies what kind of
+ * dithering to use. '0' means no dithering, '1' means standard
+ * Floyd-Steinberg error diffusion, '2' means Floyd-Steinberg error
+ * diffusion with reduced bleeding, '3' means dithering based on pixel
+ * location ('Fixed' dithering).
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_convert_indexed (gint32 image_ID,
+ GimpConvertDitherType dither_type,
+ GimpConvertPaletteType palette_type,
+ gint num_cols,
+ gboolean alpha_dither,
+ gboolean remove_unused,
+ const gchar *palette)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-convert-indexed",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, dither_type,
+ GIMP_PDB_INT32, palette_type,
+ GIMP_PDB_INT32, num_cols,
+ GIMP_PDB_INT32, alpha_dither,
+ GIMP_PDB_INT32, remove_unused,
+ GIMP_PDB_STRING, palette,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_convert_set_dither_matrix:
+ * @width: Width of the matrix (0 to reset to default matrix).
+ * @height: Height of the matrix (0 to reset to default matrix).
+ * @matrix_length: The length of 'matrix'.
+ * @matrix: The matrix -- all values must be >= 1.
+ *
+ * Set dither matrix for conversion to indexed
+ *
+ * This procedure sets the dither matrix used when converting images to
+ * INDEXED mode with positional dithering.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_image_convert_set_dither_matrix (gint width,
+ gint height,
+ gint matrix_length,
+ const guint8 *matrix)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-convert-set-dither-matrix",
+ &nreturn_vals,
+ GIMP_PDB_INT32, width,
+ GIMP_PDB_INT32, height,
+ GIMP_PDB_INT32, matrix_length,
+ GIMP_PDB_INT8ARRAY, matrix,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_convert_precision:
+ * @image_ID: The image.
+ * @precision: The new precision.
+ *
+ * Convert the image to the specified precision
+ *
+ * This procedure converts the image to the specified precision. Note
+ * that indexed images cannot be converted and are always in
+ * GIMP_PRECISION_U8.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_image_convert_precision (gint32 image_ID,
+ GimpPrecision precision)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-convert-precision",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, precision,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimpimageconvert_pdb.h b/libgimp/gimpimageconvert_pdb.h
new file mode 100644
index 0000000..6bbcf0c
--- /dev/null
+++ b/libgimp/gimpimageconvert_pdb.h
@@ -0,0 +1,54 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpimageconvert_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_IMAGE_CONVERT_PDB_H__
+#define __GIMP_IMAGE_CONVERT_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_image_convert_rgb (gint32 image_ID);
+gboolean gimp_image_convert_grayscale (gint32 image_ID);
+gboolean gimp_image_convert_indexed (gint32 image_ID,
+ GimpConvertDitherType dither_type,
+ GimpConvertPaletteType palette_type,
+ gint num_cols,
+ gboolean alpha_dither,
+ gboolean remove_unused,
+ const gchar *palette);
+gboolean gimp_image_convert_set_dither_matrix (gint width,
+ gint height,
+ gint matrix_length,
+ const guint8 *matrix);
+gboolean gimp_image_convert_precision (gint32 image_ID,
+ GimpPrecision precision);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_IMAGE_CONVERT_PDB_H__ */
diff --git a/libgimp/gimpimagegrid_pdb.c b/libgimp/gimpimagegrid_pdb.c
new file mode 100644
index 0000000..c207acb
--- /dev/null
+++ b/libgimp/gimpimagegrid_pdb.c
@@ -0,0 +1,408 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpimagegrid_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpimagegrid
+ * @title: gimpimagegrid
+ * @short_description: Functions manuipulating an image's grid.
+ *
+ * Functions manuipulating an image's grid.
+ **/
+
+
+/**
+ * gimp_image_grid_get_spacing:
+ * @image_ID: The image.
+ * @xspacing: The image's grid horizontal spacing.
+ * @yspacing: The image's grid vertical spacing.
+ *
+ * Gets the spacing of an image's grid.
+ *
+ * This procedure retrieves the horizontal and vertical spacing of an
+ * image's grid. It takes the image as parameter.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_image_grid_get_spacing (gint32 image_ID,
+ gdouble *xspacing,
+ gdouble *yspacing)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-grid-get-spacing",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ *xspacing = 0.0;
+ *yspacing = 0.0;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *xspacing = return_vals[1].data.d_float;
+ *yspacing = return_vals[2].data.d_float;
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_grid_set_spacing:
+ * @image_ID: The image.
+ * @xspacing: The image's grid horizontal spacing.
+ * @yspacing: The image's grid vertical spacing.
+ *
+ * Sets the spacing of an image's grid.
+ *
+ * This procedure sets the horizontal and vertical spacing of an
+ * image's grid.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_image_grid_set_spacing (gint32 image_ID,
+ gdouble xspacing,
+ gdouble yspacing)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-grid-set-spacing",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_FLOAT, xspacing,
+ GIMP_PDB_FLOAT, yspacing,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_grid_get_offset:
+ * @image_ID: The image.
+ * @xoffset: The image's grid horizontal offset.
+ * @yoffset: The image's grid vertical offset.
+ *
+ * Gets the offset of an image's grid.
+ *
+ * This procedure retrieves the horizontal and vertical offset of an
+ * image's grid. It takes the image as parameter.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_image_grid_get_offset (gint32 image_ID,
+ gdouble *xoffset,
+ gdouble *yoffset)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-grid-get-offset",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ *xoffset = 0.0;
+ *yoffset = 0.0;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *xoffset = return_vals[1].data.d_float;
+ *yoffset = return_vals[2].data.d_float;
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_grid_set_offset:
+ * @image_ID: The image.
+ * @xoffset: The image's grid horizontal offset.
+ * @yoffset: The image's grid vertical offset.
+ *
+ * Sets the offset of an image's grid.
+ *
+ * This procedure sets the horizontal and vertical offset of an image's
+ * grid.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_image_grid_set_offset (gint32 image_ID,
+ gdouble xoffset,
+ gdouble yoffset)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-grid-set-offset",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_FLOAT, xoffset,
+ GIMP_PDB_FLOAT, yoffset,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_grid_get_foreground_color:
+ * @image_ID: The image.
+ * @fgcolor: The image's grid foreground color.
+ *
+ * Sets the foreground color of an image's grid.
+ *
+ * This procedure gets the foreground color of an image's grid.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_image_grid_get_foreground_color (gint32 image_ID,
+ GimpRGB *fgcolor)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-grid-get-foreground-color",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ *fgcolor = return_vals[1].data.d_color;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_grid_set_foreground_color:
+ * @image_ID: The image.
+ * @fgcolor: The new foreground color.
+ *
+ * Gets the foreground color of an image's grid.
+ *
+ * This procedure sets the foreground color of an image's grid.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_image_grid_set_foreground_color (gint32 image_ID,
+ const GimpRGB *fgcolor)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-grid-set-foreground-color",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_COLOR, fgcolor,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_grid_get_background_color:
+ * @image_ID: The image.
+ * @bgcolor: The image's grid background color.
+ *
+ * Sets the background color of an image's grid.
+ *
+ * This procedure gets the background color of an image's grid.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_image_grid_get_background_color (gint32 image_ID,
+ GimpRGB *bgcolor)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-grid-get-background-color",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ *bgcolor = return_vals[1].data.d_color;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_grid_set_background_color:
+ * @image_ID: The image.
+ * @bgcolor: The new background color.
+ *
+ * Gets the background color of an image's grid.
+ *
+ * This procedure sets the background color of an image's grid.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_image_grid_set_background_color (gint32 image_ID,
+ const GimpRGB *bgcolor)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-grid-set-background-color",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_COLOR, bgcolor,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_grid_get_style:
+ * @image_ID: The image.
+ *
+ * Gets the style of an image's grid.
+ *
+ * This procedure retrieves the style of an image's grid.
+ *
+ * Returns: The image's grid style.
+ *
+ * Since: 2.4
+ **/
+GimpGridStyle
+gimp_image_grid_get_style (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpGridStyle style = 0;
+
+ return_vals = gimp_run_procedure ("gimp-image-grid-get-style",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ style = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return style;
+}
+
+/**
+ * gimp_image_grid_set_style:
+ * @image_ID: The image.
+ * @style: The image's grid style.
+ *
+ * Sets the style unit of an image's grid.
+ *
+ * This procedure sets the style of an image's grid. It takes the image
+ * and the new style as parameters.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_image_grid_set_style (gint32 image_ID,
+ GimpGridStyle style)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-grid-set-style",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, style,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimpimagegrid_pdb.h b/libgimp/gimpimagegrid_pdb.h
new file mode 100644
index 0000000..f39a88a
--- /dev/null
+++ b/libgimp/gimpimagegrid_pdb.h
@@ -0,0 +1,62 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpimagegrid_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_IMAGE_GRID_PDB_H__
+#define __GIMP_IMAGE_GRID_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_image_grid_get_spacing (gint32 image_ID,
+ gdouble *xspacing,
+ gdouble *yspacing);
+gboolean gimp_image_grid_set_spacing (gint32 image_ID,
+ gdouble xspacing,
+ gdouble yspacing);
+gboolean gimp_image_grid_get_offset (gint32 image_ID,
+ gdouble *xoffset,
+ gdouble *yoffset);
+gboolean gimp_image_grid_set_offset (gint32 image_ID,
+ gdouble xoffset,
+ gdouble yoffset);
+gboolean gimp_image_grid_get_foreground_color (gint32 image_ID,
+ GimpRGB *fgcolor);
+gboolean gimp_image_grid_set_foreground_color (gint32 image_ID,
+ const GimpRGB *fgcolor);
+gboolean gimp_image_grid_get_background_color (gint32 image_ID,
+ GimpRGB *bgcolor);
+gboolean gimp_image_grid_set_background_color (gint32 image_ID,
+ const GimpRGB *bgcolor);
+GimpGridStyle gimp_image_grid_get_style (gint32 image_ID);
+gboolean gimp_image_grid_set_style (gint32 image_ID,
+ GimpGridStyle style);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_IMAGE_GRID_PDB_H__ */
diff --git a/libgimp/gimpimageguides_pdb.c b/libgimp/gimpimageguides_pdb.c
new file mode 100644
index 0000000..252f970
--- /dev/null
+++ b/libgimp/gimpimageguides_pdb.c
@@ -0,0 +1,243 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpimageguides_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpimageguides
+ * @title: gimpimageguides
+ * @short_description: Functions for manipulating an image's guides.
+ *
+ * Functions for manipulating an image's guides.
+ **/
+
+
+/**
+ * gimp_image_add_hguide:
+ * @image_ID: The image.
+ * @yposition: The guide's y-offset from top of image.
+ *
+ * Add a horizontal guide to an image.
+ *
+ * This procedure adds a horizontal guide to an image. It takes the
+ * input image and the y-position of the new guide as parameters. It
+ * returns the guide ID of the new guide.
+ *
+ * Returns: The new guide.
+ **/
+gint32
+gimp_image_add_hguide (gint32 image_ID,
+ gint yposition)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 guide_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-image-add-hguide",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, yposition,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ guide_ID = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return guide_ID;
+}
+
+/**
+ * gimp_image_add_vguide:
+ * @image_ID: The image.
+ * @xposition: The guide's x-offset from left of image.
+ *
+ * Add a vertical guide to an image.
+ *
+ * This procedure adds a vertical guide to an image. It takes the input
+ * image and the x-position of the new guide as parameters. It returns
+ * the guide ID of the new guide.
+ *
+ * Returns: The new guide.
+ **/
+gint32
+gimp_image_add_vguide (gint32 image_ID,
+ gint xposition)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 guide_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-image-add-vguide",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, xposition,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ guide_ID = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return guide_ID;
+}
+
+/**
+ * gimp_image_delete_guide:
+ * @image_ID: The image.
+ * @guide_ID: The ID of the guide to be removed.
+ *
+ * Deletes a guide from an image.
+ *
+ * This procedure takes an image and a guide ID as input and removes
+ * the specified guide from the specified image.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_delete_guide (gint32 image_ID,
+ gint32 guide_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-delete-guide",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, guide_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_find_next_guide:
+ * @image_ID: The image.
+ * @guide_ID: The ID of the current guide (0 if first invocation).
+ *
+ * Find next guide on an image.
+ *
+ * This procedure takes an image and a guide ID as input and finds the
+ * guide ID of the successor of the given guide ID in the image's guide
+ * list. If the supplied guide ID is 0, the procedure will return the
+ * first Guide. The procedure will return 0 if given the final guide ID
+ * as an argument or the image has no guides.
+ *
+ * Returns: The next guide's ID.
+ **/
+gint32
+gimp_image_find_next_guide (gint32 image_ID,
+ gint32 guide_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 next_guide_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-image-find-next-guide",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, guide_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ next_guide_ID = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return next_guide_ID;
+}
+
+/**
+ * gimp_image_get_guide_orientation:
+ * @image_ID: The image.
+ * @guide_ID: The guide.
+ *
+ * Get orientation of a guide on an image.
+ *
+ * This procedure takes an image and a guide ID as input and returns
+ * the orientations of the guide.
+ *
+ * Returns: The guide's orientation.
+ **/
+GimpOrientationType
+gimp_image_get_guide_orientation (gint32 image_ID,
+ gint32 guide_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpOrientationType orientation = GIMP_ORIENTATION_UNKNOWN;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-guide-orientation",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, guide_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ orientation = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return orientation;
+}
+
+/**
+ * gimp_image_get_guide_position:
+ * @image_ID: The image.
+ * @guide_ID: The guide.
+ *
+ * Get position of a guide on an image.
+ *
+ * This procedure takes an image and a guide ID as input and returns
+ * the position of the guide relative to the top or left of the image.
+ *
+ * Returns: The guide's position relative to top or left of image.
+ **/
+gint
+gimp_image_get_guide_position (gint32 image_ID,
+ gint32 guide_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint position = G_MININT /* GIMP_GUIDE_POSITION_UNDEFINED */;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-guide-position",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, guide_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ position = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return position;
+}
diff --git a/libgimp/gimpimageguides_pdb.h b/libgimp/gimpimageguides_pdb.h
new file mode 100644
index 0000000..2f52ded
--- /dev/null
+++ b/libgimp/gimpimageguides_pdb.h
@@ -0,0 +1,51 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpimageguides_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_IMAGE_GUIDES_PDB_H__
+#define __GIMP_IMAGE_GUIDES_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gint32 gimp_image_add_hguide (gint32 image_ID,
+ gint yposition);
+gint32 gimp_image_add_vguide (gint32 image_ID,
+ gint xposition);
+gboolean gimp_image_delete_guide (gint32 image_ID,
+ gint32 guide_ID);
+gint32 gimp_image_find_next_guide (gint32 image_ID,
+ gint32 guide_ID);
+GimpOrientationType gimp_image_get_guide_orientation (gint32 image_ID,
+ gint32 guide_ID);
+gint gimp_image_get_guide_position (gint32 image_ID,
+ gint32 guide_ID);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_IMAGE_GUIDES_PDB_H__ */
diff --git a/libgimp/gimpimagemetadata.c b/libgimp/gimpimagemetadata.c
new file mode 100644
index 0000000..5adb20d
--- /dev/null
+++ b/libgimp/gimpimagemetadata.c
@@ -0,0 +1,1204 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2000 Peter Mattis and Spencer Kimball
+ *
+ * gimpimagemetadata.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <sys/time.h>
+
+#include <gtk/gtk.h>
+#include <gexiv2/gexiv2.h>
+
+#include "gimp.h"
+#include "gimpui.h"
+#include "gimpimagemetadata.h"
+
+#include "libgimp-intl.h"
+
+
+typedef struct
+{
+ gchar *tag;
+ gint type;
+} XmpStructs;
+
+static gchar * gimp_image_metadata_interpret_comment (gchar *comment);
+
+static void gimp_image_metadata_rotate (gint32 image_ID,
+ GExiv2Orientation orientation);
+static GdkPixbuf * gimp_image_metadata_rotate_pixbuf (GdkPixbuf *pixbuf,
+ GExiv2Orientation orientation);
+static void gimp_image_metadata_rotate_query (gint32 image_ID,
+ const gchar *mime_type,
+ GimpMetadata *metadata,
+ gboolean interactive);
+static gboolean gimp_image_metadata_rotate_dialog (gint32 image_ID,
+ GExiv2Orientation orientation,
+ const gchar *parasite_name);
+
+
+/* public functions */
+
+/**
+ * gimp_image_metadata_load_prepare:
+ * @image_ID: The image
+ * @mime_type: The loaded file's mime-type
+ * @file: The file to load the metadata from
+ * @error: Return location for error
+ *
+ * Loads and returns metadata from @file to be passed into
+ * gimp_image_metadata_load_finish().
+ *
+ * Returns: The file's metadata.
+ *
+ * Since: 2.10
+ */
+GimpMetadata *
+gimp_image_metadata_load_prepare (gint32 image_ID,
+ const gchar *mime_type,
+ GFile *file,
+ GError **error)
+{
+ GimpMetadata *metadata;
+
+ g_return_val_if_fail (image_ID > 0, NULL);
+ g_return_val_if_fail (mime_type != NULL, NULL);
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ metadata = gimp_metadata_load_from_file (file, error);
+
+ return metadata;
+}
+
+static gchar *
+gimp_image_metadata_interpret_comment (gchar *comment)
+{
+ /* Exiv2 can return unwanted text at the start of a comment
+ * taken from Exif.Photo.UserComment since 0.27.3.
+ * Let's remove that part and return NULL if there
+ * is nothing else left as comment. */
+
+ if (comment && g_str_has_prefix (comment, "charset=Ascii "))
+ {
+ gchar *real_comment;
+
+ /* Skip "charset=Ascii " (length 14) to find the real comment */
+ real_comment = comment + 14;
+ if (real_comment[0] == '\0' ||
+ ! g_strcmp0 (real_comment, "binary comment"))
+ {
+ g_free (comment);
+ return NULL;
+ }
+ else
+ {
+ real_comment = g_strdup (real_comment);
+ g_free (comment);
+ return real_comment;
+ }
+ }
+
+ return comment;
+}
+
+/**
+ * gimp_image_metadata_load_finish:
+ * @image_ID: The image
+ * @mime_type: The loaded file's mime-type
+ * @metadata: The metadata to set on the image
+ * @flags: Flags to specify what of the metadata to apply to the image
+ * @interactive: Whether this function is allowed to query info with dialogs
+ *
+ * Applies the @metadata previously loaded with
+ * gimp_image_metadata_load_prepare() to the image, taking into account
+ * the passed @flags.
+ *
+ * Since: 2.10
+ */
+void
+gimp_image_metadata_load_finish (gint32 image_ID,
+ const gchar *mime_type,
+ GimpMetadata *metadata,
+ GimpMetadataLoadFlags flags,
+ gboolean interactive)
+{
+ g_return_if_fail (image_ID > 0);
+ g_return_if_fail (mime_type != NULL);
+ g_return_if_fail (GEXIV2_IS_METADATA (metadata));
+
+ if (flags & GIMP_METADATA_LOAD_COMMENT)
+ {
+ gchar *comment;
+
+ comment = gexiv2_metadata_get_tag_interpreted_string (GEXIV2_METADATA (metadata),
+ "Exif.Photo.UserComment");
+ comment = gimp_image_metadata_interpret_comment (comment);
+
+ if (! comment)
+ comment = gexiv2_metadata_get_tag_interpreted_string (GEXIV2_METADATA (metadata),
+ "Exif.Image.ImageDescription");
+
+ if (comment)
+ {
+ GimpParasite *parasite;
+
+ parasite = gimp_parasite_new ("gimp-comment",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (comment) + 1,
+ comment);
+ g_free (comment);
+
+ gimp_image_attach_parasite (image_ID, parasite);
+ gimp_parasite_free (parasite);
+ }
+ }
+
+ if (flags & GIMP_METADATA_LOAD_RESOLUTION)
+ {
+ gdouble xres;
+ gdouble yres;
+ GimpUnit unit;
+
+ if (gimp_metadata_get_resolution (metadata, &xres, &yres, &unit))
+ {
+ gimp_image_set_resolution (image_ID, xres, yres);
+ gimp_image_set_unit (image_ID, unit);
+ }
+ }
+
+ if (flags & GIMP_METADATA_LOAD_ORIENTATION)
+ {
+ gimp_image_metadata_rotate_query (image_ID, mime_type,
+ metadata, interactive);
+ /* Drop the orientation metadata in all cases, whether you rotated
+ * or not. See commit 8dcf258ffc on master.
+ */
+ gexiv2_metadata_set_orientation (GEXIV2_METADATA (metadata),
+ GEXIV2_ORIENTATION_NORMAL);
+ }
+
+ if (flags & GIMP_METADATA_LOAD_COLORSPACE)
+ {
+ GimpColorProfile *profile = gimp_image_get_color_profile (image_ID);
+
+ /* only look for colorspace information from metadata if the
+ * image didn't contain an embedded color profile
+ */
+ if (! profile)
+ {
+ GimpMetadataColorspace colorspace;
+
+ colorspace = gimp_metadata_get_colorspace (metadata);
+
+ switch (colorspace)
+ {
+ case GIMP_METADATA_COLORSPACE_UNSPECIFIED:
+ case GIMP_METADATA_COLORSPACE_UNCALIBRATED:
+ case GIMP_METADATA_COLORSPACE_SRGB:
+ /* use sRGB, a NULL profile will do the right thing */
+ break;
+
+ case GIMP_METADATA_COLORSPACE_ADOBERGB:
+ profile = gimp_color_profile_new_rgb_adobe ();
+ break;
+ }
+
+ if (profile)
+ gimp_image_set_color_profile (image_ID, profile);
+ }
+
+ if (profile)
+ g_object_unref (profile);
+ }
+
+ gimp_image_set_metadata (image_ID, metadata);
+}
+
+/**
+ * gimp_image_metadata_save_prepare:
+ * @image_ID: The image
+ * @mime_type: The saved file's mime-type
+ * @suggested_flags: Suggested default values for the @flags passed to
+ * gimp_image_metadata_save_finish()
+ *
+ * Gets the image metadata for saving it using
+ * gimp_image_metadata_save_finish().
+ *
+ * The @suggested_flags are determined from what kind of metadata
+ * (Exif, XMP, ...) is actually present in the image and the preferences
+ * for metadata exporting.
+ * The calling application may still update @available_flags, for
+ * instance to follow the settings from a previous export in the same
+ * session, or a previous export of the same image. But it should not
+ * override the preferences without a good reason since it is a data
+ * leak.
+ *
+ * The suggested value for GIMP_METADATA_SAVE_THUMBNAIL is determined by
+ * whether there was a thumbnail in the previously imported image.
+ *
+ * Returns: The image's metadata, prepared for saving.
+ *
+ * Since: 2.10
+ */
+GimpMetadata *
+gimp_image_metadata_save_prepare (gint32 image_ID,
+ const gchar *mime_type,
+ GimpMetadataSaveFlags *suggested_flags)
+{
+ GimpMetadata *metadata;
+
+ g_return_val_if_fail (image_ID > 0, NULL);
+ g_return_val_if_fail (mime_type != NULL, NULL);
+ g_return_val_if_fail (suggested_flags != NULL, NULL);
+
+ *suggested_flags = GIMP_METADATA_SAVE_ALL;
+
+ metadata = gimp_image_get_metadata (image_ID);
+
+ if (metadata)
+ {
+ GDateTime *datetime;
+ const GimpParasite *comment_parasite;
+ const gchar *comment = NULL;
+ gint image_width;
+ gint image_height;
+ gdouble xres;
+ gdouble yres;
+ gchar buffer[32];
+ gchar *datetime_buf = NULL;
+ GExiv2Metadata *g2metadata = GEXIV2_METADATA (metadata);
+
+ image_width = gimp_image_width (image_ID);
+ image_height = gimp_image_height (image_ID);
+
+ datetime = g_date_time_new_now_local ();
+
+ comment_parasite = gimp_image_get_parasite (image_ID, "gimp-comment");
+ if (comment_parasite)
+ comment = gimp_parasite_data (comment_parasite);
+
+ /* Exif */
+
+ if (! gimp_export_exif () ||
+ ! gexiv2_metadata_has_exif (g2metadata))
+ *suggested_flags &= ~GIMP_METADATA_SAVE_EXIF;
+
+ if (comment)
+ {
+ gexiv2_metadata_set_tag_string (g2metadata,
+ "Exif.Photo.UserComment",
+ comment);
+ gexiv2_metadata_set_tag_string (g2metadata,
+ "Exif.Image.ImageDescription",
+ comment);
+ }
+
+ g_snprintf (buffer, sizeof (buffer),
+ "%d:%02d:%02d %02d:%02d:%02d",
+ g_date_time_get_year (datetime),
+ g_date_time_get_month (datetime),
+ g_date_time_get_day_of_month (datetime),
+ g_date_time_get_hour (datetime),
+ g_date_time_get_minute (datetime),
+ g_date_time_get_second (datetime));
+ gexiv2_metadata_set_tag_string (g2metadata,
+ "Exif.Image.DateTime",
+ buffer);
+
+ gexiv2_metadata_set_tag_string (g2metadata,
+ "Exif.Image.Software",
+ PACKAGE_STRING);
+
+ gimp_metadata_set_pixel_size (metadata,
+ image_width, image_height);
+
+ gimp_image_get_resolution (image_ID, &xres, &yres);
+ gimp_metadata_set_resolution (metadata, xres, yres,
+ gimp_image_get_unit (image_ID));
+
+ /* XMP */
+
+ if (! gimp_export_xmp () ||
+ ! gexiv2_metadata_has_xmp (g2metadata))
+ *suggested_flags &= ~GIMP_METADATA_SAVE_XMP;
+
+ gexiv2_metadata_set_tag_string (g2metadata,
+ "Xmp.dc.Format",
+ mime_type);
+
+ /* XMP uses datetime in ISO 8601 format */
+ datetime_buf = g_date_time_format (datetime, "%Y:%m:%dT%T\%:z");
+
+ gexiv2_metadata_set_tag_string (g2metadata,
+ "Xmp.xmp.ModifyDate",
+ datetime_buf);
+ gexiv2_metadata_set_tag_string (g2metadata,
+ "Xmp.xmp.MetadataDate",
+ datetime_buf);
+
+ if (! g_strcmp0 (mime_type, "image/tiff"))
+ {
+ /* TIFF specific XMP data */
+
+ g_snprintf (buffer, sizeof (buffer), "%d", image_width);
+ gexiv2_metadata_set_tag_string (g2metadata,
+ "Xmp.tiff.ImageWidth",
+ buffer);
+
+ g_snprintf (buffer, sizeof (buffer), "%d", image_height);
+ gexiv2_metadata_set_tag_string (g2metadata,
+ "Xmp.tiff.ImageLength",
+ buffer);
+
+ gexiv2_metadata_set_tag_string (g2metadata,
+ "Xmp.tiff.DateTime",
+ datetime_buf);
+ }
+
+ /* IPTC */
+
+ if (! gimp_export_iptc () ||
+ ! gexiv2_metadata_has_iptc (g2metadata))
+ *suggested_flags &= ~GIMP_METADATA_SAVE_IPTC;
+
+ g_free (datetime_buf);
+ g_date_time_unref (datetime);
+
+ /* EXIF Thumbnail */
+
+ if (gexiv2_metadata_has_exif (g2metadata))
+ {
+ gchar *value;
+
+ /* Check a required tag for a thumbnail to be present. */
+ value = gexiv2_metadata_get_tag_string (g2metadata,
+ "Exif.Thumbnail.ImageLength");
+
+ if (! value)
+ *suggested_flags &= ~GIMP_METADATA_SAVE_THUMBNAIL;
+ else
+ g_free (value);
+ }
+ else
+ {
+ *suggested_flags &= ~GIMP_METADATA_SAVE_THUMBNAIL;
+ }
+ }
+
+ /* Thumbnail */
+
+ if (FALSE /* FIXME if (original image had a thumbnail) */)
+ *suggested_flags &= ~GIMP_METADATA_SAVE_THUMBNAIL;
+
+ /* Color profile */
+
+ if (! gimp_export_color_profile ())
+ *suggested_flags &= ~GIMP_METADATA_SAVE_COLOR_PROFILE;
+
+ return metadata;
+}
+
+static const gchar *
+gimp_fix_xmp_tag (const gchar *tag)
+{
+ gchar *substring;
+
+ /* Due to problems using /Iptc4xmpExt namespace (/iptcExt is used
+ * instead by Exiv2) we replace all occurrences with /iptcExt which
+ * is valid but less common. Not doing so would cause saving xmp
+ * metadata to fail. This has to be done after getting the values
+ * from the source metadata since that source uses the original
+ * tag names and would otherwise return NULL as value.
+ * /Iptc4xmpExt length = 12
+ * /iptcExt length = 8
+ */
+
+ substring = strstr (tag, "/Iptc4xmpExt");
+ while (substring)
+ {
+ gint len_tag = strlen (tag);
+ gint len_end;
+
+ len_end = len_tag - (substring - tag) - 12;
+ strncpy (substring, "/iptcExt", 8);
+ substring += 8;
+ /* Using memmove: we have overlapping source and dest */
+ memmove (substring, substring+4, len_end);
+ substring[len_end] = '\0';
+ g_debug ("Fixed tag value: %s", tag);
+
+ /* Multiple occurrences are possible: e.g.:
+ * Xmp.iptcExt.ImageRegion[3]/Iptc4xmpExt:RegionBoundary/Iptc4xmpExt:rbVertices[1]/Iptc4xmpExt:rbX
+ */
+ substring = strstr (tag, "/Iptc4xmpExt");
+ }
+ return tag;
+}
+
+static void
+gimp_image_metadata_copy_tag (GExiv2Metadata *src,
+ GExiv2Metadata *dest,
+ const gchar *tag)
+{
+ gchar **values = gexiv2_metadata_get_tag_multiple (src, tag);
+
+ if (values)
+ {
+ gchar *temp_tag;
+
+ /* Xmp always seems to return multiple values */
+ if (g_str_has_prefix (tag, "Xmp."))
+ temp_tag = (gchar *) gimp_fix_xmp_tag (g_strdup (tag));
+ else
+ temp_tag = g_strdup (tag);
+
+ g_debug ("Copy multi tag %s, first value: %s", temp_tag, values[0]);
+ gexiv2_metadata_set_tag_multiple (dest, temp_tag, (const gchar **) values);
+ g_free (temp_tag);
+ g_strfreev (values);
+ }
+ else
+ {
+ gchar *value = gexiv2_metadata_get_tag_string (src, tag);
+
+ if (value)
+ {
+ g_debug ("Copy tag %s, value: %s", tag, value);
+ gexiv2_metadata_set_tag_string (dest, tag, value);
+ g_free (value);
+ }
+ }
+}
+
+static gint
+gimp_natural_sort_compare (gconstpointer left,
+ gconstpointer right)
+{
+ gint compare;
+ gchar *left_key = g_utf8_collate_key_for_filename ((gchar *) left, -1);
+ gchar *right_key = g_utf8_collate_key_for_filename ((gchar *) right, -1);
+
+ compare = g_strcmp0 (left_key, right_key);
+ g_free (left_key);
+ g_free (right_key);
+
+ return compare;
+}
+
+static GList*
+gimp_image_metadata_convert_tags_to_list (gchar **xmp_tags)
+{
+ GList *list = NULL;
+ gint i;
+
+ for (i = 0; xmp_tags[i] != NULL; i++)
+ {
+ g_debug ("Tag: %s, tag type: %s", xmp_tags[i], gexiv2_metadata_get_tag_type(xmp_tags[i]));
+ list = g_list_prepend (list, xmp_tags[i]);
+ }
+ return list;
+}
+
+static GExiv2StructureType
+gimp_image_metadata_get_xmp_struct_type (const gchar *tag)
+{
+ g_debug ("Struct type for tag: %s, type: %s", tag, gexiv2_metadata_get_tag_type (tag));
+
+ if (! g_strcmp0 (gexiv2_metadata_get_tag_type (tag), "XmpSeq"))
+ {
+ return GEXIV2_STRUCTURE_XA_SEQ;
+ }
+
+ return GEXIV2_STRUCTURE_XA_BAG;
+}
+
+static void
+gimp_image_metadata_set_xmp_structs (GList *xmp_list,
+ GExiv2Metadata *metadata)
+{
+ GList *list;
+ gchar *prev_one = NULL;
+ gchar *prev_two = NULL;
+
+ for (list = xmp_list; list != NULL; list = list->next)
+ {
+ gchar **tag_split;
+
+ /*
+ * Most tags with structs have only one struct part, like:
+ * Xmp.xmpMM.History[1]...
+ * However there are also Xmp tags that have two
+ * structs in one tag, e.g.:
+ * Xmp.crs.GradientBasedCorrections[1]/crs:CorrectionMasks[1]...
+ */
+ tag_split = g_strsplit ((gchar *) list->data, "[1]", 3);
+ /* Check if there are at least two parts but don't catch xxx[2]/yyy[1]/zzz */
+ if (tag_split && tag_split[1] && ! strstr (tag_split[0], "["))
+ {
+ if (! prev_one || strcmp (tag_split[0], prev_one) != 0)
+ {
+ GExiv2StructureType type;
+
+ g_free (prev_one);
+ prev_one = g_strdup (tag_split[0]);
+
+ type = gimp_image_metadata_get_xmp_struct_type (gimp_fix_xmp_tag (tag_split[0]));
+ gexiv2_metadata_set_xmp_tag_struct (GEXIV2_METADATA (metadata),
+ prev_one, type);
+ }
+ if (tag_split[2] && (!prev_two || strcmp (tag_split[1], prev_two) != 0))
+ {
+ gchar *second_struct;
+ GExiv2StructureType type;
+
+ g_free (prev_two);
+ prev_two = g_strdup (tag_split[1]);
+ second_struct = g_strdup_printf ("%s[1]%s", prev_one, gimp_fix_xmp_tag(prev_two));
+
+ type = gimp_image_metadata_get_xmp_struct_type (gimp_fix_xmp_tag (tag_split[1]));
+ gexiv2_metadata_set_xmp_tag_struct (GEXIV2_METADATA (metadata),
+ second_struct, type);
+ g_free (second_struct);
+ }
+ }
+
+ g_strfreev (tag_split);
+ }
+ g_free (prev_one);
+ g_free (prev_two);
+}
+
+/**
+ * gimp_image_metadata_save_finish:
+ * @image_ID: The image
+ * @mime_type: The saved file's mime-type
+ * @metadata: The metadata to set on the image
+ * @flags: Flags to specify what of the metadata to save
+ * @file: The file to load the metadata from
+ * @error: Return location for error message
+ *
+ * Saves the @metadata retrieved from the image with
+ * gimp_image_metadata_save_prepare() to @file, taking into account
+ * the passed @flags.
+ *
+ * Return value: Whether the save was successful.
+ *
+ * Since: 2.10
+ */
+gboolean
+gimp_image_metadata_save_finish (gint32 image_ID,
+ const gchar *mime_type,
+ GimpMetadata *metadata,
+ GimpMetadataSaveFlags flags,
+ GFile *file,
+ GError **error)
+{
+ GimpMetadata *new_metadata;
+ GExiv2Metadata *new_g2metadata;
+ gboolean support_exif;
+ gboolean support_xmp;
+ gboolean support_iptc;
+ gboolean success = FALSE;
+ gint i;
+
+ g_return_val_if_fail (image_ID > 0, FALSE);
+ g_return_val_if_fail (mime_type != NULL, FALSE);
+ g_return_val_if_fail (GEXIV2_IS_METADATA (metadata), FALSE);
+ g_return_val_if_fail (G_IS_FILE (file), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ if (! (flags & (GIMP_METADATA_SAVE_EXIF |
+ GIMP_METADATA_SAVE_XMP |
+ GIMP_METADATA_SAVE_IPTC |
+ GIMP_METADATA_SAVE_THUMBNAIL)))
+ return TRUE;
+
+ /* read metadata from saved file */
+ new_metadata = gimp_metadata_load_from_file (file, error);
+ new_g2metadata = GEXIV2_METADATA (new_metadata);
+
+ if (! new_metadata)
+ return FALSE;
+
+ support_exif = gexiv2_metadata_get_supports_exif (new_g2metadata);
+ support_xmp = gexiv2_metadata_get_supports_xmp (new_g2metadata);
+ support_iptc = gexiv2_metadata_get_supports_iptc (new_g2metadata);
+
+ if ((flags & GIMP_METADATA_SAVE_EXIF) && support_exif)
+ {
+ gchar **exif_data = gexiv2_metadata_get_exif_tags (GEXIV2_METADATA (metadata));
+
+ for (i = 0; exif_data[i] != NULL; i++)
+ {
+ if (! gexiv2_metadata_has_tag (new_g2metadata, exif_data[i]) &&
+ gimp_metadata_is_tag_supported (exif_data[i], mime_type))
+ {
+ gimp_image_metadata_copy_tag (GEXIV2_METADATA (metadata),
+ new_g2metadata,
+ exif_data[i]);
+ }
+ }
+
+ g_strfreev (exif_data);
+ }
+
+ if ((flags & GIMP_METADATA_SAVE_XMP) && support_xmp)
+ {
+ gchar **xmp_data;
+ struct timeval timer_usec;
+ gint64 timestamp_usec;
+ gchar ts[128];
+ GList *xmp_list = NULL;
+ GList *list;
+
+ gettimeofday (&timer_usec, NULL);
+ timestamp_usec = ((gint64) timer_usec.tv_sec) * 1000000ll +
+ (gint64) timer_usec.tv_usec;
+ g_snprintf (ts, sizeof (ts), "%" G_GINT64_FORMAT, timestamp_usec);
+
+ gimp_metadata_add_xmp_history (metadata, "");
+
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
+ "Xmp.GIMP.TimeStamp",
+ ts);
+
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
+ "Xmp.xmp.CreatorTool",
+ N_("GIMP 2.10"));
+
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
+ "Xmp.GIMP.Version",
+ GIMP_VERSION);
+
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
+ "Xmp.GIMP.API",
+ GIMP_API_VERSION);
+
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
+ "Xmp.GIMP.Platform",
+#if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
+ "Windows"
+#elif defined(__linux__)
+ "Linux"
+#elif defined(__APPLE__) && defined(__MACH__)
+ "Mac OS"
+#elif defined(unix) || defined(__unix__) || defined(__unix)
+ "Unix"
+#else
+ "Unknown"
+#endif
+ );
+
+ xmp_data = gexiv2_metadata_get_xmp_tags (GEXIV2_METADATA (metadata));
+
+ xmp_list = gimp_image_metadata_convert_tags_to_list (xmp_data);
+ xmp_list = g_list_sort (xmp_list, (GCompareFunc) gimp_natural_sort_compare);
+ gimp_image_metadata_set_xmp_structs (xmp_list, new_g2metadata);
+
+ for (list = xmp_list; list != NULL; list = list->next)
+ {
+ if (! gexiv2_metadata_has_tag (new_g2metadata, (gchar *) list->data) &&
+ gimp_metadata_is_tag_supported ((gchar *) list->data, mime_type))
+ {
+ gimp_image_metadata_copy_tag (GEXIV2_METADATA (metadata),
+ new_g2metadata,
+ (gchar *) list->data);
+ }
+ else
+ g_debug ("Ignored tag: %s", (gchar *) list->data);
+ }
+
+ g_list_free (xmp_list);
+ g_strfreev (xmp_data);
+ }
+
+ if ((flags & GIMP_METADATA_SAVE_IPTC) && support_iptc)
+ {
+ gchar **iptc_data = gexiv2_metadata_get_iptc_tags (GEXIV2_METADATA (metadata));
+
+ for (i = 0; iptc_data[i] != NULL; i++)
+ {
+ if (! gexiv2_metadata_has_tag (new_g2metadata, iptc_data[i]) &&
+ gimp_metadata_is_tag_supported (iptc_data[i], mime_type))
+ {
+ gimp_image_metadata_copy_tag (GEXIV2_METADATA (metadata),
+ new_g2metadata,
+ iptc_data[i]);
+ }
+ }
+
+ g_strfreev (iptc_data);
+ }
+
+ if (flags & GIMP_METADATA_SAVE_THUMBNAIL && support_exif)
+ {
+ GdkPixbuf *thumb_pixbuf;
+ gchar *thumb_buffer;
+ gint image_width;
+ gint image_height;
+ gsize count;
+ gint thumbw;
+ gint thumbh;
+
+#define EXIF_THUMBNAIL_SIZE 256
+
+ image_width = gimp_image_width (image_ID);
+ image_height = gimp_image_height (image_ID);
+
+ if (image_width > image_height)
+ {
+ thumbw = EXIF_THUMBNAIL_SIZE;
+ thumbh = EXIF_THUMBNAIL_SIZE * image_height / image_width;
+ }
+ else
+ {
+ thumbh = EXIF_THUMBNAIL_SIZE;
+ thumbw = EXIF_THUMBNAIL_SIZE * image_width / image_height;
+ }
+
+ thumb_pixbuf = gimp_image_get_thumbnail (image_ID, thumbw, thumbh,
+ GIMP_PIXBUF_KEEP_ALPHA);
+
+ if (gdk_pixbuf_save_to_buffer (thumb_pixbuf, &thumb_buffer, &count,
+ "jpeg", NULL,
+ "quality", "75",
+ NULL))
+ {
+ gchar buffer[32];
+
+ gexiv2_metadata_set_exif_thumbnail_from_buffer (new_g2metadata,
+ (guchar *) thumb_buffer,
+ count);
+
+ g_snprintf (buffer, sizeof (buffer), "%d", thumbw);
+ gexiv2_metadata_set_tag_string (new_g2metadata,
+ "Exif.Thumbnail.ImageWidth",
+ buffer);
+
+ g_snprintf (buffer, sizeof (buffer), "%d", thumbh);
+ gexiv2_metadata_set_tag_string (new_g2metadata,
+ "Exif.Thumbnail.ImageLength",
+ buffer);
+
+ gexiv2_metadata_set_tag_string (new_g2metadata,
+ "Exif.Thumbnail.BitsPerSample",
+ "8 8 8");
+ gexiv2_metadata_set_tag_string (new_g2metadata,
+ "Exif.Thumbnail.SamplesPerPixel",
+ "3");
+ gexiv2_metadata_set_tag_string (new_g2metadata,
+ "Exif.Thumbnail.PhotometricInterpretation",
+ "6"); /* old jpeg */
+ gexiv2_metadata_set_tag_string (new_g2metadata,
+ "Exif.Thumbnail.NewSubfileType",
+ "1"); /* reduced resolution image */
+
+ g_free (thumb_buffer);
+ }
+
+ g_object_unref (thumb_pixbuf);
+ }
+ else
+ {
+ /* Remove Thumbnail */
+ gexiv2_metadata_erase_exif_thumbnail (new_g2metadata);
+ }
+
+ if (flags & GIMP_METADATA_SAVE_COLOR_PROFILE)
+ {
+ /* nothing to do, but if we ever need to modify metadata based
+ * on the exported color profile, this is probably the place to
+ * add it
+ */
+ }
+
+ success = gimp_metadata_save_to_file (new_metadata, file, error);
+
+ g_object_unref (new_metadata);
+
+ return success;
+}
+
+gint32
+gimp_image_metadata_load_thumbnail (GFile *file,
+ GError **error)
+{
+ GimpMetadata *metadata;
+ GInputStream *input_stream;
+ GdkPixbuf *pixbuf;
+ guint8 *thumbnail_buffer;
+ gint thumbnail_size;
+ gint32 image_ID = -1;
+
+ g_return_val_if_fail (G_IS_FILE (file), -1);
+ g_return_val_if_fail (error == NULL || *error == NULL, -1);
+
+ metadata = gimp_metadata_load_from_file (file, error);
+ if (! metadata)
+ return -1;
+
+ if (! gexiv2_metadata_get_exif_thumbnail (GEXIV2_METADATA (metadata),
+ &thumbnail_buffer,
+ &thumbnail_size))
+ {
+ g_object_unref (metadata);
+ return -1;
+ }
+
+ input_stream = g_memory_input_stream_new_from_data (thumbnail_buffer,
+ thumbnail_size,
+ (GDestroyNotify) g_free);
+ pixbuf = gdk_pixbuf_new_from_stream (input_stream, NULL, error);
+ g_object_unref (input_stream);
+
+ if (pixbuf)
+ {
+ gint32 layer_ID;
+
+ image_ID = gimp_image_new (gdk_pixbuf_get_width (pixbuf),
+ gdk_pixbuf_get_height (pixbuf),
+ GIMP_RGB);
+ gimp_image_undo_disable (image_ID);
+
+ layer_ID = gimp_layer_new_from_pixbuf (image_ID, _("Background"),
+ pixbuf,
+ 100.0,
+ gimp_image_get_default_new_layer_mode (image_ID),
+ 0.0, 0.0);
+ g_object_unref (pixbuf);
+
+ gimp_image_insert_layer (image_ID, layer_ID, -1, 0);
+
+ gimp_image_metadata_rotate (image_ID,
+ gexiv2_metadata_get_orientation (GEXIV2_METADATA (metadata)));
+ }
+
+ g_object_unref (metadata);
+
+ return image_ID;
+}
+
+
+/* private functions */
+
+static void
+gimp_image_metadata_rotate (gint32 image_ID,
+ GExiv2Orientation orientation)
+{
+ switch (orientation)
+ {
+ case GEXIV2_ORIENTATION_UNSPECIFIED:
+ case GEXIV2_ORIENTATION_NORMAL: /* standard orientation, do nothing */
+ break;
+
+ case GEXIV2_ORIENTATION_HFLIP:
+ gimp_image_flip (image_ID, GIMP_ORIENTATION_HORIZONTAL);
+ break;
+
+ case GEXIV2_ORIENTATION_ROT_180:
+ gimp_image_rotate (image_ID, GIMP_ROTATE_180);
+ break;
+
+ case GEXIV2_ORIENTATION_VFLIP:
+ gimp_image_flip (image_ID, GIMP_ORIENTATION_VERTICAL);
+ break;
+
+ case GEXIV2_ORIENTATION_ROT_90_HFLIP: /* flipped diagonally around '\' */
+ gimp_image_rotate (image_ID, GIMP_ROTATE_90);
+ gimp_image_flip (image_ID, GIMP_ORIENTATION_HORIZONTAL);
+ break;
+
+ case GEXIV2_ORIENTATION_ROT_90: /* 90 CW */
+ gimp_image_rotate (image_ID, GIMP_ROTATE_90);
+ break;
+
+ case GEXIV2_ORIENTATION_ROT_90_VFLIP: /* flipped diagonally around '/' */
+ gimp_image_rotate (image_ID, GIMP_ROTATE_90);
+ gimp_image_flip (image_ID, GIMP_ORIENTATION_VERTICAL);
+ break;
+
+ case GEXIV2_ORIENTATION_ROT_270: /* 90 CCW */
+ gimp_image_rotate (image_ID, GIMP_ROTATE_270);
+ break;
+
+ default: /* shouldn't happen */
+ break;
+ }
+}
+
+static GdkPixbuf *
+gimp_image_metadata_rotate_pixbuf (GdkPixbuf *pixbuf,
+ GExiv2Orientation orientation)
+{
+ GdkPixbuf *rotated = NULL;
+ GdkPixbuf *temp;
+
+ switch (orientation)
+ {
+ case GEXIV2_ORIENTATION_UNSPECIFIED:
+ case GEXIV2_ORIENTATION_NORMAL: /* standard orientation, do nothing */
+ rotated = g_object_ref (pixbuf);
+ break;
+
+ case GEXIV2_ORIENTATION_HFLIP:
+ rotated = gdk_pixbuf_flip (pixbuf, TRUE);
+ break;
+
+ case GEXIV2_ORIENTATION_ROT_180:
+ rotated = gdk_pixbuf_rotate_simple (pixbuf, GDK_PIXBUF_ROTATE_UPSIDEDOWN);
+ break;
+
+ case GEXIV2_ORIENTATION_VFLIP:
+ rotated = gdk_pixbuf_flip (pixbuf, FALSE);
+ break;
+
+ case GEXIV2_ORIENTATION_ROT_90_HFLIP: /* flipped diagonally around '\' */
+ temp = gdk_pixbuf_rotate_simple (pixbuf, GDK_PIXBUF_ROTATE_CLOCKWISE);
+ rotated = gdk_pixbuf_flip (temp, TRUE);
+ g_object_unref (temp);
+ break;
+
+ case GEXIV2_ORIENTATION_ROT_90: /* 90 CW */
+ rotated = gdk_pixbuf_rotate_simple (pixbuf, GDK_PIXBUF_ROTATE_CLOCKWISE);
+ break;
+
+ case GEXIV2_ORIENTATION_ROT_90_VFLIP: /* flipped diagonally around '/' */
+ temp = gdk_pixbuf_rotate_simple (pixbuf, GDK_PIXBUF_ROTATE_CLOCKWISE);
+ rotated = gdk_pixbuf_flip (temp, FALSE);
+ g_object_unref (temp);
+ break;
+
+ case GEXIV2_ORIENTATION_ROT_270: /* 90 CCW */
+ rotated = gdk_pixbuf_rotate_simple (pixbuf, GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE);
+ break;
+
+ default: /* shouldn't happen */
+ break;
+ }
+
+ return rotated;
+}
+
+static void
+gimp_image_metadata_rotate_query (gint32 image_ID,
+ const gchar *mime_type,
+ GimpMetadata *metadata,
+ gboolean interactive)
+{
+ GimpParasite *parasite;
+ gchar *parasite_name;
+ GExiv2Orientation orientation;
+ gboolean query = interactive;
+
+ orientation = gexiv2_metadata_get_orientation (GEXIV2_METADATA (metadata));
+
+ if (orientation <= GEXIV2_ORIENTATION_NORMAL ||
+ orientation > GEXIV2_ORIENTATION_MAX)
+ return;
+
+ parasite_name = g_strdup_printf ("gimp-metadata-exif-rotate(%s)", mime_type);
+
+ parasite = gimp_get_parasite (parasite_name);
+
+ if (parasite)
+ {
+ if (strncmp (gimp_parasite_data (parasite), "yes",
+ gimp_parasite_data_size (parasite)) == 0)
+ {
+ query = FALSE;
+ }
+ else if (strncmp (gimp_parasite_data (parasite), "no",
+ gimp_parasite_data_size (parasite)) == 0)
+ {
+ gimp_parasite_free (parasite);
+ g_free (parasite_name);
+ return;
+ }
+
+ gimp_parasite_free (parasite);
+ }
+
+ if (query && ! gimp_image_metadata_rotate_dialog (image_ID,
+ orientation,
+ parasite_name))
+ {
+ g_free (parasite_name);
+ return;
+ }
+
+ g_free (parasite_name);
+
+ gimp_image_metadata_rotate (image_ID, orientation);
+ gexiv2_metadata_set_orientation (GEXIV2_METADATA (metadata),
+ GEXIV2_ORIENTATION_NORMAL);
+}
+
+static gboolean
+gimp_image_metadata_rotate_dialog (gint32 image_ID,
+ GExiv2Orientation orientation,
+ const gchar *parasite_name)
+{
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *vbox;
+ GtkWidget *label;
+ GtkWidget *toggle;
+ GdkPixbuf *pixbuf;
+ gchar *name;
+ gchar *title;
+ gint response;
+
+ name = gimp_image_get_name (image_ID);
+ title = g_strdup_printf (_("Rotate %s?"), name);
+ g_free (name);
+
+ dialog = gimp_dialog_new (title, "gimp-metadata-rotate-dialog",
+ NULL, 0, NULL, NULL,
+
+ _("_Keep Original"), GTK_RESPONSE_CANCEL,
+ _("_Rotate"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ g_free (title);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, FALSE, FALSE, 0);
+ gtk_widget_show (main_vbox);
+
+#define THUMBNAIL_SIZE 128
+
+ pixbuf = gimp_image_get_thumbnail (image_ID,
+ THUMBNAIL_SIZE, THUMBNAIL_SIZE,
+ GIMP_PIXBUF_SMALL_CHECKS);
+
+ if (pixbuf)
+ {
+ GdkPixbuf *rotated;
+ GtkWidget *hbox;
+ GtkWidget *image;
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
+ gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ label = gtk_label_new (_("Original"));
+ gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_MIDDLE);
+ gimp_label_set_attributes (GTK_LABEL (label),
+ PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
+ -1);
+ gtk_box_pack_end (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ image = gtk_image_new_from_pixbuf (pixbuf);
+ gtk_box_pack_end (GTK_BOX (vbox), image, FALSE, FALSE, 0);
+ gtk_widget_show (image);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ label = gtk_label_new (_("Rotated"));
+ gimp_label_set_attributes (GTK_LABEL (label),
+ PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
+ -1);
+ gtk_box_pack_end (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ rotated = gimp_image_metadata_rotate_pixbuf (pixbuf, orientation);
+ g_object_unref (pixbuf);
+
+ image = gtk_image_new_from_pixbuf (rotated);
+ g_object_unref (rotated);
+
+ gtk_box_pack_end (GTK_BOX (vbox), image, FALSE, FALSE, 0);
+ gtk_widget_show (image);
+ }
+
+ label = g_object_new (GTK_TYPE_LABEL,
+ "label", _("This image contains Exif orientation "
+ "metadata."),
+ "wrap", TRUE,
+ "justify", GTK_JUSTIFY_LEFT,
+ "xalign", 0.0,
+ "yalign", 0.5,
+ NULL);
+ gimp_label_set_attributes (GTK_LABEL (label),
+ PANGO_ATTR_SCALE, PANGO_SCALE_LARGE,
+ PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD,
+ -1);
+ /* eek */
+ gtk_widget_set_size_request (GTK_WIDGET (label),
+ 2 * THUMBNAIL_SIZE + 12, -1);
+ gtk_box_pack_start (GTK_BOX (main_vbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ label = g_object_new (GTK_TYPE_LABEL,
+ "label", _("Would you like to rotate the image?"),
+ "wrap", TRUE,
+ "justify", GTK_JUSTIFY_LEFT,
+ "xalign", 0.0,
+ "yalign", 0.5,
+ NULL);
+ /* eek */
+ gtk_widget_set_size_request (GTK_WIDGET (label),
+ 2 * THUMBNAIL_SIZE + 12, -1);
+ gtk_box_pack_start (GTK_BOX (main_vbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("_Don't ask me again"));
+ gtk_box_pack_end (GTK_BOX (main_vbox), toggle, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), FALSE);
+ gtk_widget_show (toggle);
+
+ response = gimp_dialog_run (GIMP_DIALOG (dialog));
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (toggle)))
+ {
+ GimpParasite *parasite;
+ const gchar *str = (response == GTK_RESPONSE_OK) ? "yes" : "no";
+
+ parasite = gimp_parasite_new (parasite_name,
+ GIMP_PARASITE_PERSISTENT,
+ strlen (str), str);
+ gimp_attach_parasite (parasite);
+ gimp_parasite_free (parasite);
+ }
+
+ gtk_widget_destroy (dialog);
+
+ return (response == GTK_RESPONSE_OK);
+}
diff --git a/libgimp/gimpimagemetadata.h b/libgimp/gimpimagemetadata.h
new file mode 100644
index 0000000..e8de269
--- /dev/null
+++ b/libgimp/gimpimagemetadata.h
@@ -0,0 +1,61 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2000 Peter Mattis and Spencer Kimball
+ *
+ * gimpimagemetadata.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_UI_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimpui.h> can be included directly."
+#endif
+
+#ifndef __GIMP_IMAGE_METADATA_H__
+#define __GIMP_IMAGE_METADATA_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+GimpMetadata * gimp_image_metadata_load_prepare (gint32 image_ID,
+ const gchar *mime_type,
+ GFile *file,
+ GError **error);
+void gimp_image_metadata_load_finish (gint32 image_ID,
+ const gchar *mime_type,
+ GimpMetadata *metadata,
+ GimpMetadataLoadFlags flags,
+ gboolean interactive);
+
+GimpMetadata * gimp_image_metadata_save_prepare (gint32 image_ID,
+ const gchar *mime_type,
+ GimpMetadataSaveFlags *suggested_flags);
+gboolean gimp_image_metadata_save_finish (gint32 image_ID,
+ const gchar *mime_type,
+ GimpMetadata *metadata,
+ GimpMetadataSaveFlags flags,
+ GFile *file,
+ GError **error);
+
+
+/* this is experimental API, to be finished for 2.10 */
+
+gint32 gimp_image_metadata_load_thumbnail (GFile *file,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* __GIMP_IMAGE_METADATA_H__ */
diff --git a/libgimp/gimpimagesamplepoints_pdb.c b/libgimp/gimpimagesamplepoints_pdb.c
new file mode 100644
index 0000000..5a8135b
--- /dev/null
+++ b/libgimp/gimpimagesamplepoints_pdb.c
@@ -0,0 +1,192 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpimagesamplepoints_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpimagesamplepoints
+ * @title: gimpimagesamplepoints
+ * @short_description: Functions for manipulating an image's sample points.
+ *
+ * Functions for manipulating an image's sample points.
+ **/
+
+
+/**
+ * gimp_image_add_sample_point:
+ * @image_ID: The image.
+ * @position_x: The guide'sample points x-offset from left of image.
+ * @position_y: The guide'sample points y-offset from top of image.
+ *
+ * Add a sample point to an image.
+ *
+ * This procedure adds a sample point to an image. It takes the input
+ * image and the position of the new sample points as parameters. It
+ * returns the sample point ID of the new sample point.
+ *
+ * Returns: The new sample point.
+ *
+ * Since: 2.10
+ **/
+gint32
+gimp_image_add_sample_point (gint32 image_ID,
+ gint position_x,
+ gint position_y)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 sample_point_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-image-add-sample-point",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, position_x,
+ GIMP_PDB_INT32, position_y,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ sample_point_ID = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return sample_point_ID;
+}
+
+/**
+ * gimp_image_delete_sample_point:
+ * @image_ID: The image.
+ * @sample_point_ID: The ID of the sample point to be removed.
+ *
+ * Deletes a sample point from an image.
+ *
+ * This procedure takes an image and a sample point ID as input and
+ * removes the specified sample point from the specified image.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_image_delete_sample_point (gint32 image_ID,
+ gint32 sample_point_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-delete-sample-point",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, sample_point_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_find_next_sample_point:
+ * @image_ID: The image.
+ * @sample_point_ID: The ID of the current sample point (0 if first invocation).
+ *
+ * Find next sample point on an image.
+ *
+ * This procedure takes an image and a sample point ID as input and
+ * finds the sample point ID of the successor of the given sample point
+ * ID in the image's sample point list. If the supplied sample point ID
+ * is 0, the procedure will return the first sample point. The
+ * procedure will return 0 if given the final sample point ID as an
+ * argument or the image has no sample points.
+ *
+ * Returns: The next sample point's ID.
+ *
+ * Since: 2.10
+ **/
+gint32
+gimp_image_find_next_sample_point (gint32 image_ID,
+ gint32 sample_point_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 next_sample_point_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-image-find-next-sample-point",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, sample_point_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ next_sample_point_ID = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return next_sample_point_ID;
+}
+
+/**
+ * gimp_image_get_sample_point_position:
+ * @image_ID: The image.
+ * @sample_point_ID: The guide.
+ * @position_y: The sample points's position relative to top of image.
+ *
+ * Get position of a sample point on an image.
+ *
+ * This procedure takes an image and a sample point ID as input and
+ * returns the position of the sample point relative to the top and
+ * left of the image.
+ *
+ * Returns: The sample points's position relative to top of image.
+ *
+ * Since: 2.10
+ **/
+gint
+gimp_image_get_sample_point_position (gint32 image_ID,
+ gint32 sample_point_ID,
+ gint *position_y)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint position_x = G_MININT;
+
+ return_vals = gimp_run_procedure ("gimp-image-get-sample-point-position",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, sample_point_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ position_x = return_vals[1].data.d_int32;
+ *position_y = return_vals[2].data.d_int32;
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return position_x;
+}
diff --git a/libgimp/gimpimagesamplepoints_pdb.h b/libgimp/gimpimagesamplepoints_pdb.h
new file mode 100644
index 0000000..a1daa69
--- /dev/null
+++ b/libgimp/gimpimagesamplepoints_pdb.h
@@ -0,0 +1,49 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpimagesamplepoints_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_IMAGE_SAMPLE_POINTS_PDB_H__
+#define __GIMP_IMAGE_SAMPLE_POINTS_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gint32 gimp_image_add_sample_point (gint32 image_ID,
+ gint position_x,
+ gint position_y);
+gboolean gimp_image_delete_sample_point (gint32 image_ID,
+ gint32 sample_point_ID);
+gint32 gimp_image_find_next_sample_point (gint32 image_ID,
+ gint32 sample_point_ID);
+gint gimp_image_get_sample_point_position (gint32 image_ID,
+ gint32 sample_point_ID,
+ gint *position_y);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_IMAGE_SAMPLE_POINTS_PDB_H__ */
diff --git a/libgimp/gimpimageselect_pdb.c b/libgimp/gimpimageselect_pdb.c
new file mode 100644
index 0000000..cc3d431
--- /dev/null
+++ b/libgimp/gimpimageselect_pdb.c
@@ -0,0 +1,417 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpimageselect_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpimageselect
+ * @title: gimpimageselect
+ * @short_description: Modify the image's selection.
+ *
+ * Functions to modify the image's selection.
+ **/
+
+
+/**
+ * gimp_image_select_color:
+ * @image_ID: The affected image.
+ * @operation: The selection operation.
+ * @drawable_ID: The affected drawable.
+ * @color: The color to select.
+ *
+ * Create a selection by selecting all pixels (in the specified
+ * drawable) with the same (or similar) color to that specified.
+ *
+ * This tool creates a selection over the specified image. A by-color
+ * selection is determined by the supplied color under the constraints
+ * of the current context settings. Essentially, all pixels (in the
+ * drawable) that have color sufficiently close to the specified color
+ * (as determined by the threshold and criterion context values) are
+ * included in the selection. To select transparent regions, the color
+ * specified must also have minimum alpha.
+ *
+ * This procedure is affected by the following context setters:
+ * gimp_context_set_antialias(), gimp_context_set_feather(),
+ * gimp_context_set_feather_radius(), gimp_context_set_sample_merged(),
+ * gimp_context_set_sample_criterion(),
+ * gimp_context_set_sample_threshold(),
+ * gimp_context_set_sample_transparent().
+ *
+ * In the case of a merged sampling, the supplied drawable is ignored.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_image_select_color (gint32 image_ID,
+ GimpChannelOps operation,
+ gint32 drawable_ID,
+ const GimpRGB *color)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-select-color",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, operation,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_COLOR, color,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_select_contiguous_color:
+ * @image_ID: The affected image.
+ * @operation: The selection operation.
+ * @drawable_ID: The affected drawable.
+ * @x: x coordinate of initial seed fill point: (image coordinates).
+ * @y: y coordinate of initial seed fill point: (image coordinates).
+ *
+ * Create a selection by selecting all pixels around specified
+ * coordinates with the same (or similar) color to that at the
+ * coordinates.
+ *
+ * This tool creates a contiguous selection over the specified image. A
+ * contiguous color selection is determined by a seed fill under the
+ * constraints of the current context settings. Essentially, the color
+ * at the specified coordinates (in the drawable) is measured and the
+ * selection expands outwards from that point to any adjacent pixels
+ * which are not significantly different (as determined by the
+ * threshold and criterion context settings). This process continues
+ * until no more expansion is possible. If antialiasing is turned on,
+ * the final selection mask will contain intermediate values based on
+ * close misses to the threshold bar at pixels along the seed fill
+ * boundary.
+ *
+ * This procedure is affected by the following context setters:
+ * gimp_context_set_antialias(), gimp_context_set_feather(),
+ * gimp_context_set_feather_radius(), gimp_context_set_sample_merged(),
+ * gimp_context_set_sample_criterion(),
+ * gimp_context_set_sample_threshold(),
+ * gimp_context_set_sample_transparent(),
+ * gimp_context_set_diagonal_neighbors().
+ *
+ * In the case of a merged sampling, the supplied drawable is ignored.
+ * If the sample is merged, the specified coordinates are relative to
+ * the image origin; otherwise, they are relative to the drawable's
+ * origin.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_image_select_contiguous_color (gint32 image_ID,
+ GimpChannelOps operation,
+ gint32 drawable_ID,
+ gdouble x,
+ gdouble y)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-select-contiguous-color",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, operation,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_FLOAT, x,
+ GIMP_PDB_FLOAT, y,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_select_rectangle:
+ * @image_ID: The image.
+ * @operation: The selection operation.
+ * @x: x coordinate of upper-left corner of rectangle.
+ * @y: y coordinate of upper-left corner of rectangle.
+ * @width: The width of the rectangle.
+ * @height: The height of the rectangle.
+ *
+ * Create a rectangular selection over the specified image;
+ *
+ * This tool creates a rectangular selection over the specified image.
+ * The rectangular region can be either added to, subtracted from, or
+ * replace the contents of the previous selection mask.
+ *
+ * This procedure is affected by the following context setters:
+ * gimp_context_set_feather(), gimp_context_set_feather_radius().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_image_select_rectangle (gint32 image_ID,
+ GimpChannelOps operation,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-select-rectangle",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, operation,
+ GIMP_PDB_FLOAT, x,
+ GIMP_PDB_FLOAT, y,
+ GIMP_PDB_FLOAT, width,
+ GIMP_PDB_FLOAT, height,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_select_round_rectangle:
+ * @image_ID: The image.
+ * @operation: The selection operation.
+ * @x: x coordinate of upper-left corner of rectangle.
+ * @y: y coordinate of upper-left corner of rectangle.
+ * @width: The width of the rectangle.
+ * @height: The height of the rectangle.
+ * @corner_radius_x: The corner radius in X direction.
+ * @corner_radius_y: The corner radius in Y direction.
+ *
+ * Create a rectangular selection with round corners over the specified
+ * image;
+ *
+ * This tool creates a rectangular selection with round corners over
+ * the specified image. The rectangular region can be either added to,
+ * subtracted from, or replace the contents of the previous selection
+ * mask.
+ *
+ * This procedure is affected by the following context setters:
+ * gimp_context_set_antialias(), gimp_context_set_feather(),
+ * gimp_context_set_feather_radius().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_image_select_round_rectangle (gint32 image_ID,
+ GimpChannelOps operation,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ gdouble corner_radius_x,
+ gdouble corner_radius_y)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-select-round-rectangle",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, operation,
+ GIMP_PDB_FLOAT, x,
+ GIMP_PDB_FLOAT, y,
+ GIMP_PDB_FLOAT, width,
+ GIMP_PDB_FLOAT, height,
+ GIMP_PDB_FLOAT, corner_radius_x,
+ GIMP_PDB_FLOAT, corner_radius_y,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_select_ellipse:
+ * @image_ID: The image.
+ * @operation: The selection operation.
+ * @x: x coordinate of upper-left corner of ellipse bounding box.
+ * @y: y coordinate of upper-left corner of ellipse bounding box.
+ * @width: The width of the ellipse.
+ * @height: The height of the ellipse.
+ *
+ * Create an elliptical selection over the specified image.
+ *
+ * This tool creates an elliptical selection over the specified image.
+ * The elliptical region can be either added to, subtracted from, or
+ * replace the contents of the previous selection mask.
+ *
+ * This procedure is affected by the following context setters:
+ * gimp_context_set_antialias(), gimp_context_set_feather(),
+ * gimp_context_set_feather_radius().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_image_select_ellipse (gint32 image_ID,
+ GimpChannelOps operation,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-select-ellipse",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, operation,
+ GIMP_PDB_FLOAT, x,
+ GIMP_PDB_FLOAT, y,
+ GIMP_PDB_FLOAT, width,
+ GIMP_PDB_FLOAT, height,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_select_polygon:
+ * @image_ID: The image.
+ * @operation: The selection operation.
+ * @num_segs: Number of points (count 1 coordinate as two points).
+ * @segs: Array of points: { p1.x, p1.y, p2.x, p2.y, ..., pn.x, pn.y}.
+ *
+ * Create a polygonal selection over the specified image.
+ *
+ * This tool creates a polygonal selection over the specified image.
+ * The polygonal region can be either added to, subtracted from, or
+ * replace the contents of the previous selection mask. The polygon is
+ * specified through an array of floating point numbers and its length.
+ * The length of array must be 2n, where n is the number of points.
+ * Each point is defined by 2 floating point values which correspond to
+ * the x and y coordinates. If the final point does not connect to the
+ * starting point, a connecting segment is automatically added.
+ *
+ * This procedure is affected by the following context setters:
+ * gimp_context_set_antialias(), gimp_context_set_feather(),
+ * gimp_context_set_feather_radius().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_image_select_polygon (gint32 image_ID,
+ GimpChannelOps operation,
+ gint num_segs,
+ const gdouble *segs)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-select-polygon",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, operation,
+ GIMP_PDB_INT32, num_segs,
+ GIMP_PDB_FLOATARRAY, segs,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_select_item:
+ * @image_ID: The image.
+ * @operation: The desired operation with current selection.
+ * @item_ID: The item to render to the selection.
+ *
+ * Transforms the specified item into a selection
+ *
+ * This procedure renders the item's outline into the current selection
+ * of the image the item belongs to. What exactly the item's outline is
+ * depends on the item type: for layers, it's the layer's alpha
+ * channel, for vectors the vector's shape.
+ *
+ * This procedure is affected by the following context setters:
+ * gimp_context_set_antialias(), gimp_context_set_feather(),
+ * gimp_context_set_feather_radius().
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_image_select_item (gint32 image_ID,
+ GimpChannelOps operation,
+ gint32 item_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-select-item",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, operation,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimpimageselect_pdb.h b/libgimp/gimpimageselect_pdb.h
new file mode 100644
index 0000000..a7e5db8
--- /dev/null
+++ b/libgimp/gimpimageselect_pdb.h
@@ -0,0 +1,75 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpimageselect_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_IMAGE_SELECT_PDB_H__
+#define __GIMP_IMAGE_SELECT_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_image_select_color (gint32 image_ID,
+ GimpChannelOps operation,
+ gint32 drawable_ID,
+ const GimpRGB *color);
+gboolean gimp_image_select_contiguous_color (gint32 image_ID,
+ GimpChannelOps operation,
+ gint32 drawable_ID,
+ gdouble x,
+ gdouble y);
+gboolean gimp_image_select_rectangle (gint32 image_ID,
+ GimpChannelOps operation,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height);
+gboolean gimp_image_select_round_rectangle (gint32 image_ID,
+ GimpChannelOps operation,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ gdouble corner_radius_x,
+ gdouble corner_radius_y);
+gboolean gimp_image_select_ellipse (gint32 image_ID,
+ GimpChannelOps operation,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height);
+gboolean gimp_image_select_polygon (gint32 image_ID,
+ GimpChannelOps operation,
+ gint num_segs,
+ const gdouble *segs);
+gboolean gimp_image_select_item (gint32 image_ID,
+ GimpChannelOps operation,
+ gint32 item_ID);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_IMAGE_SELECT_PDB_H__ */
diff --git a/libgimp/gimpimagetransform_pdb.c b/libgimp/gimpimagetransform_pdb.c
new file mode 100644
index 0000000..1be6a83
--- /dev/null
+++ b/libgimp/gimpimagetransform_pdb.c
@@ -0,0 +1,303 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpimagetransform_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpimagetransform
+ * @title: gimpimagetransform
+ * @short_description: Transformations on images.
+ *
+ * Operations to scale, resize, crop, flip and rotate images.
+ **/
+
+
+/**
+ * gimp_image_resize:
+ * @image_ID: The image.
+ * @new_width: New image width.
+ * @new_height: New image height.
+ * @offx: x offset between upper left corner of old and new images: (new - old).
+ * @offy: y offset between upper left corner of old and new images: (new - old).
+ *
+ * Resize the image to the specified extents.
+ *
+ * This procedure resizes the image so that it's new width and height
+ * are equal to the supplied parameters. Offsets are also provided
+ * which describe the position of the previous image's content. All
+ * channels within the image are resized according to the specified
+ * parameters; this includes the image selection mask. All layers
+ * within the image are repositioned according to the specified
+ * offsets.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_resize (gint32 image_ID,
+ gint new_width,
+ gint new_height,
+ gint offx,
+ gint offy)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-resize",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, new_width,
+ GIMP_PDB_INT32, new_height,
+ GIMP_PDB_INT32, offx,
+ GIMP_PDB_INT32, offy,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_resize_to_layers:
+ * @image_ID: The image.
+ *
+ * Resize the image to fit all layers.
+ *
+ * This procedure resizes the image to the bounding box of all layers
+ * of the image. All channels within the image are resized to the new
+ * size; this includes the image selection mask. All layers within the
+ * image are repositioned to the new image area.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_image_resize_to_layers (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-resize-to-layers",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_scale:
+ * @image_ID: The image.
+ * @new_width: New image width.
+ * @new_height: New image height.
+ *
+ * Scale the image using the default interpolation method.
+ *
+ * This procedure scales the image so that its new width and height are
+ * equal to the supplied parameters. All layers and channels within the
+ * image are scaled according to the specified parameters; this
+ * includes the image selection mask. The interpolation method used can
+ * be set with gimp_context_set_interpolation().
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_scale (gint32 image_ID,
+ gint new_width,
+ gint new_height)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-scale",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, new_width,
+ GIMP_PDB_INT32, new_height,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_scale_full:
+ * @image_ID: The image.
+ * @new_width: New image width.
+ * @new_height: New image height.
+ * @interpolation: Type of interpolation.
+ *
+ * Deprecated: Use gimp_image_scale() instead.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.6
+ **/
+gboolean
+gimp_image_scale_full (gint32 image_ID,
+ gint new_width,
+ gint new_height,
+ GimpInterpolationType interpolation)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-scale-full",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, new_width,
+ GIMP_PDB_INT32, new_height,
+ GIMP_PDB_INT32, interpolation,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_crop:
+ * @image_ID: The image.
+ * @new_width: New image width: (0 < new_width <= width).
+ * @new_height: New image height: (0 < new_height <= height).
+ * @offx: X offset: (0 <= offx <= (width - new_width)).
+ * @offy: Y offset: (0 <= offy <= (height - new_height)).
+ *
+ * Crop the image to the specified extents.
+ *
+ * This procedure crops the image so that it's new width and height are
+ * equal to the supplied parameters. Offsets are also provided which
+ * describe the position of the previous image's content. All channels
+ * and layers within the image are cropped to the new image extents;
+ * this includes the image selection mask. If any parameters are out of
+ * range, an error is returned.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_crop (gint32 image_ID,
+ gint new_width,
+ gint new_height,
+ gint offx,
+ gint offy)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-crop",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, new_width,
+ GIMP_PDB_INT32, new_height,
+ GIMP_PDB_INT32, offx,
+ GIMP_PDB_INT32, offy,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_flip:
+ * @image_ID: The image.
+ * @flip_type: Type of flip.
+ *
+ * Flips the image horizontally or vertically.
+ *
+ * This procedure flips (mirrors) the image.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_flip (gint32 image_ID,
+ GimpOrientationType flip_type)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-flip",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, flip_type,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_rotate:
+ * @image_ID: The image.
+ * @rotate_type: Angle of rotation.
+ *
+ * Rotates the image by the specified degrees.
+ *
+ * This procedure rotates the image.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_rotate (gint32 image_ID,
+ GimpRotationType rotate_type)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-rotate",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, rotate_type,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimpimagetransform_pdb.h b/libgimp/gimpimagetransform_pdb.h
new file mode 100644
index 0000000..02ec951
--- /dev/null
+++ b/libgimp/gimpimagetransform_pdb.h
@@ -0,0 +1,62 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpimagetransform_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_IMAGE_TRANSFORM_PDB_H__
+#define __GIMP_IMAGE_TRANSFORM_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_image_resize (gint32 image_ID,
+ gint new_width,
+ gint new_height,
+ gint offx,
+ gint offy);
+gboolean gimp_image_resize_to_layers (gint32 image_ID);
+gboolean gimp_image_scale (gint32 image_ID,
+ gint new_width,
+ gint new_height);
+GIMP_DEPRECATED_FOR(gimp_image_scale)
+gboolean gimp_image_scale_full (gint32 image_ID,
+ gint new_width,
+ gint new_height,
+ GimpInterpolationType interpolation);
+gboolean gimp_image_crop (gint32 image_ID,
+ gint new_width,
+ gint new_height,
+ gint offx,
+ gint offy);
+gboolean gimp_image_flip (gint32 image_ID,
+ GimpOrientationType flip_type);
+gboolean gimp_image_rotate (gint32 image_ID,
+ GimpRotationType rotate_type);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_IMAGE_TRANSFORM_PDB_H__ */
diff --git a/libgimp/gimpimageundo_pdb.c b/libgimp/gimpimageundo_pdb.c
new file mode 100644
index 0000000..ec76524
--- /dev/null
+++ b/libgimp/gimpimageundo_pdb.c
@@ -0,0 +1,276 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpimageundo_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpimageundo
+ * @title: gimpimageundo
+ * @short_description: Control of image undo/redo.
+ *
+ * Control of image undo/redo.
+ **/
+
+
+/**
+ * gimp_image_undo_group_start:
+ * @image_ID: The ID of the image in which to open an undo group.
+ *
+ * Starts a group undo.
+ *
+ * This function is used to start a group undo--necessary for logically
+ * combining two or more undo operations into a single operation. This
+ * call must be used in conjunction with a gimp_image_undo_group_end()
+ * call.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_undo_group_start (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-undo-group-start",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_undo_group_end:
+ * @image_ID: The ID of the image in which to close an undo group.
+ *
+ * Finish a group undo.
+ *
+ * This function must be called once for each
+ * gimp_image_undo_group_start() call that is made.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_image_undo_group_end (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-image-undo-group-end",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_image_undo_is_enabled:
+ * @image_ID: The image.
+ *
+ * Check if the image's undo stack is enabled.
+ *
+ * This procedure checks if the image's undo stack is currently enabled
+ * or disabled. This is useful when several plug-ins or scripts call
+ * each other and want to check if their caller has already used
+ * gimp_image_undo_disable() or gimp_image_undo_freeze().
+ *
+ * Returns: TRUE if undo is enabled for this image.
+ **/
+gboolean
+gimp_image_undo_is_enabled (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean enabled = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-image-undo-is-enabled",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ enabled = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return enabled;
+}
+
+/**
+ * gimp_image_undo_disable:
+ * @image_ID: The image.
+ *
+ * Disable the image's undo stack.
+ *
+ * This procedure disables the image's undo stack, allowing subsequent
+ * operations to ignore their undo steps. This is generally called in
+ * conjunction with gimp_image_undo_enable() to temporarily disable an
+ * image undo stack. This is advantageous because saving undo steps can
+ * be time and memory intensive.
+ *
+ * Returns: TRUE if the image undo has been disabled.
+ **/
+gboolean
+gimp_image_undo_disable (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean disabled = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-image-undo-disable",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ disabled = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return disabled;
+}
+
+/**
+ * gimp_image_undo_enable:
+ * @image_ID: The image.
+ *
+ * Enable the image's undo stack.
+ *
+ * This procedure enables the image's undo stack, allowing subsequent
+ * operations to store their undo steps. This is generally called in
+ * conjunction with gimp_image_undo_disable() to temporarily disable an
+ * image undo stack.
+ *
+ * Returns: TRUE if the image undo has been enabled.
+ **/
+gboolean
+gimp_image_undo_enable (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean enabled = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-image-undo-enable",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ enabled = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return enabled;
+}
+
+/**
+ * gimp_image_undo_freeze:
+ * @image_ID: The image.
+ *
+ * Freeze the image's undo stack.
+ *
+ * This procedure freezes the image's undo stack, allowing subsequent
+ * operations to ignore their undo steps. This is generally called in
+ * conjunction with gimp_image_undo_thaw() to temporarily disable an
+ * image undo stack. This is advantageous because saving undo steps can
+ * be time and memory intensive. gimp_image_undo_freeze() /
+ * gimp_image_undo_thaw() and gimp_image_undo_disable() /
+ * gimp_image_undo_enable() differ in that the former does not free up
+ * all undo steps when undo is thawed, so is more suited to interactive
+ * in-situ previews. It is important in this case that the image is
+ * back to the same state it was frozen in before thawing, else 'undo'
+ * behaviour is undefined.
+ *
+ * Returns: TRUE if the image undo has been frozen.
+ **/
+gboolean
+gimp_image_undo_freeze (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean frozen = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-image-undo-freeze",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ frozen = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return frozen;
+}
+
+/**
+ * gimp_image_undo_thaw:
+ * @image_ID: The image.
+ *
+ * Thaw the image's undo stack.
+ *
+ * This procedure thaws the image's undo stack, allowing subsequent
+ * operations to store their undo steps. This is generally called in
+ * conjunction with gimp_image_undo_freeze() to temporarily freeze an
+ * image undo stack. gimp_image_undo_thaw() does NOT free the undo
+ * stack as gimp_image_undo_enable() does, so is suited for situations
+ * where one wishes to leave the undo stack in the same state in which
+ * one found it despite non-destructively playing with the image in the
+ * meantime. An example would be in-situ plug-in previews. Balancing
+ * freezes and thaws and ensuring image consistency is the
+ * responsibility of the caller.
+ *
+ * Returns: TRUE if the image undo has been thawed.
+ **/
+gboolean
+gimp_image_undo_thaw (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean thawed = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-image-undo-thaw",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ thawed = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return thawed;
+}
diff --git a/libgimp/gimpimageundo_pdb.h b/libgimp/gimpimageundo_pdb.h
new file mode 100644
index 0000000..fc2bf5e
--- /dev/null
+++ b/libgimp/gimpimageundo_pdb.h
@@ -0,0 +1,46 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpimageundo_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_IMAGE_UNDO_PDB_H__
+#define __GIMP_IMAGE_UNDO_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_image_undo_group_start (gint32 image_ID);
+gboolean gimp_image_undo_group_end (gint32 image_ID);
+gboolean gimp_image_undo_is_enabled (gint32 image_ID);
+gboolean gimp_image_undo_disable (gint32 image_ID);
+gboolean gimp_image_undo_enable (gint32 image_ID);
+gboolean gimp_image_undo_freeze (gint32 image_ID);
+gboolean gimp_image_undo_thaw (gint32 image_ID);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_IMAGE_UNDO_PDB_H__ */
diff --git a/libgimp/gimpitem_pdb.c b/libgimp/gimpitem_pdb.c
new file mode 100644
index 0000000..b5ab9e2
--- /dev/null
+++ b/libgimp/gimpitem_pdb.c
@@ -0,0 +1,1152 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpitem_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpitem
+ * @title: gimpitem
+ * @short_description: Functions to manipulate items.
+ *
+ * Functions to manipulate items.
+ **/
+
+
+/**
+ * gimp_item_is_valid:
+ * @item_ID: The item to check.
+ *
+ * Returns TRUE if the item is valid.
+ *
+ * This procedure checks if the given item ID is valid and refers to an
+ * existing item.
+ *
+ * Returns: Whether the item ID is valid.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_item_is_valid (gint32 item_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean valid = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-item-is-valid",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ valid = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return valid;
+}
+
+/**
+ * gimp_item_get_image:
+ * @item_ID: The item.
+ *
+ * Returns the item's image.
+ *
+ * This procedure returns the item's image.
+ *
+ * Returns: The item's image.
+ *
+ * Since: 2.8
+ **/
+gint32
+gimp_item_get_image (gint32 item_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 image_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-item-get-image",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ image_ID = return_vals[1].data.d_image;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return image_ID;
+}
+
+/**
+ * gimp_item_delete:
+ * @item_ID: The item to delete.
+ *
+ * Delete a item.
+ *
+ * This procedure deletes the specified item. This must not be done if
+ * the image containing this item was already deleted or if the item
+ * was already removed from the image. The only case in which this
+ * procedure is useful is if you want to get rid of a item which has
+ * not yet been added to an image.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_item_delete (gint32 item_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-item-delete",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_item_is_drawable:
+ * @item_ID: The item.
+ *
+ * Returns whether the item is a drawable.
+ *
+ * This procedure returns TRUE if the specified item is a drawable.
+ *
+ * Returns: TRUE if the item is a drawable, FALSE otherwise.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_item_is_drawable (gint32 item_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean drawable = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-item-is-drawable",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ drawable = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return drawable;
+}
+
+/**
+ * gimp_item_is_layer:
+ * @item_ID: The item.
+ *
+ * Returns whether the item is a layer.
+ *
+ * This procedure returns TRUE if the specified item is a layer.
+ *
+ * Returns: TRUE if the item is a layer, FALSE otherwise.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_item_is_layer (gint32 item_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean layer = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-item-is-layer",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ layer = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return layer;
+}
+
+/**
+ * gimp_item_is_text_layer:
+ * @item_ID: The item.
+ *
+ * Returns whether the item is a text layer.
+ *
+ * This procedure returns TRUE if the specified item is a text layer.
+ *
+ * Returns: TRUE if the item is a text layer, FALSE otherwise.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_item_is_text_layer (gint32 item_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean text_layer = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-item-is-text-layer",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ text_layer = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return text_layer;
+}
+
+/**
+ * gimp_item_is_channel:
+ * @item_ID: The item.
+ *
+ * Returns whether the item is a channel.
+ *
+ * This procedure returns TRUE if the specified item is a channel.
+ *
+ * Returns: TRUE if the item is a channel, FALSE otherwise.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_item_is_channel (gint32 item_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean channel = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-item-is-channel",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ channel = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return channel;
+}
+
+/**
+ * gimp_item_is_layer_mask:
+ * @item_ID: The item.
+ *
+ * Returns whether the item is a layer mask.
+ *
+ * This procedure returns TRUE if the specified item is a layer mask.
+ *
+ * Returns: TRUE if the item is a layer mask, FALSE otherwise.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_item_is_layer_mask (gint32 item_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean layer_mask = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-item-is-layer-mask",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ layer_mask = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return layer_mask;
+}
+
+/**
+ * gimp_item_is_selection:
+ * @item_ID: The item.
+ *
+ * Returns whether the item is a selection.
+ *
+ * This procedure returns TRUE if the specified item is a selection.
+ *
+ * Returns: TRUE if the item is a selection, FALSE otherwise.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_item_is_selection (gint32 item_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean selection = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-item-is-selection",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ selection = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return selection;
+}
+
+/**
+ * gimp_item_is_vectors:
+ * @item_ID: The item.
+ *
+ * Returns whether the item is a vectors.
+ *
+ * This procedure returns TRUE if the specified item is a vectors.
+ *
+ * Returns: TRUE if the item is a vectors, FALSE otherwise.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_item_is_vectors (gint32 item_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean vectors = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-item-is-vectors",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ vectors = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return vectors;
+}
+
+/**
+ * gimp_item_is_group:
+ * @item_ID: The item.
+ *
+ * Returns whether the item is a group item.
+ *
+ * This procedure returns TRUE if the specified item is a group item
+ * which can have children.
+ *
+ * Returns: TRUE if the item is a group, FALSE otherwise.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_item_is_group (gint32 item_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean group = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-item-is-group",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ group = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return group;
+}
+
+/**
+ * gimp_item_get_parent:
+ * @item_ID: The item.
+ *
+ * Returns the item's parent item.
+ *
+ * This procedure returns the item's parent item, if any.
+ *
+ * Returns: The item's parent item.
+ *
+ * Since: 2.8
+ **/
+gint32
+gimp_item_get_parent (gint32 item_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 parent_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-item-get-parent",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ parent_ID = return_vals[1].data.d_item;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return parent_ID;
+}
+
+/**
+ * gimp_item_get_children:
+ * @item_ID: The item.
+ * @num_children: The item's number of children.
+ *
+ * Returns the item's list of children.
+ *
+ * This procedure returns the list of items which are children of the
+ * specified item. The order is topmost to bottommost.
+ *
+ * Returns: The item's list of children.
+ *
+ * Since: 2.8
+ **/
+gint *
+gimp_item_get_children (gint32 item_ID,
+ gint *num_children)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint *child_ids = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-item-get-children",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ *num_children = 0;
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ *num_children = return_vals[1].data.d_int32;
+ child_ids = g_new (gint32, *num_children);
+ memcpy (child_ids,
+ return_vals[2].data.d_int32array,
+ *num_children * sizeof (gint32));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return child_ids;
+}
+
+/**
+ * gimp_item_get_expanded:
+ * @item_ID: The item.
+ *
+ * Returns whether the item is expanded.
+ *
+ * This procedure returns TRUE if the specified item is expanded.
+ *
+ * Returns: TRUE if the item is expanded, FALSE otherwise.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_item_get_expanded (gint32 item_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean expanded = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-item-get-expanded",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ expanded = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return expanded;
+}
+
+/**
+ * gimp_item_set_expanded:
+ * @item_ID: The item.
+ * @expanded: TRUE to expand the item, FALSE to collapse the item.
+ *
+ * Sets the expanded state of the item.
+ *
+ * This procedure expands or collapses the item.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_item_set_expanded (gint32 item_ID,
+ gboolean expanded)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-item-set-expanded",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_INT32, expanded,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_item_get_name:
+ * @item_ID: The item.
+ *
+ * Get the name of the specified item.
+ *
+ * This procedure returns the specified item's name.
+ *
+ * Returns: The item name.
+ *
+ * Since: 2.8
+ **/
+gchar *
+gimp_item_get_name (gint32 item_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-item-get-name",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ name = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return name;
+}
+
+/**
+ * gimp_item_set_name:
+ * @item_ID: The item.
+ * @name: The new item name.
+ *
+ * Set the name of the specified item.
+ *
+ * This procedure sets the specified item's name.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_item_set_name (gint32 item_ID,
+ const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-item-set-name",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_item_get_visible:
+ * @item_ID: The item.
+ *
+ * Get the visibility of the specified item.
+ *
+ * This procedure returns the specified item's visibility.
+ *
+ * Returns: The item visibility.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_item_get_visible (gint32 item_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean visible = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-item-get-visible",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ visible = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return visible;
+}
+
+/**
+ * gimp_item_set_visible:
+ * @item_ID: The item.
+ * @visible: The new item visibility.
+ *
+ * Set the visibility of the specified item.
+ *
+ * This procedure sets the specified item's visibility.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_item_set_visible (gint32 item_ID,
+ gboolean visible)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-item-set-visible",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_INT32, visible,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_item_get_linked:
+ * @item_ID: The item.
+ *
+ * Get the linked state of the specified item.
+ *
+ * This procedure returns the specified item's linked state.
+ *
+ * Returns: The item linked state (for moves).
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_item_get_linked (gint32 item_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean linked = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-item-get-linked",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ linked = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return linked;
+}
+
+/**
+ * gimp_item_set_linked:
+ * @item_ID: The item.
+ * @linked: The new item linked state.
+ *
+ * Set the linked state of the specified item.
+ *
+ * This procedure sets the specified item's linked state.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_item_set_linked (gint32 item_ID,
+ gboolean linked)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-item-set-linked",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_INT32, linked,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_item_get_lock_content:
+ * @item_ID: The item.
+ *
+ * Get the 'lock content' state of the specified item.
+ *
+ * This procedure returns the specified item's lock content state.
+ *
+ * Returns: Whether the item's contents are locked.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_item_get_lock_content (gint32 item_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean lock_content = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-item-get-lock-content",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ lock_content = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return lock_content;
+}
+
+/**
+ * gimp_item_set_lock_content:
+ * @item_ID: The item.
+ * @lock_content: The new item 'lock content' state.
+ *
+ * Set the 'lock content' state of the specified item.
+ *
+ * This procedure sets the specified item's lock content state.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_item_set_lock_content (gint32 item_ID,
+ gboolean lock_content)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-item-set-lock-content",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_INT32, lock_content,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_item_get_lock_position:
+ * @item_ID: The item.
+ *
+ * Get the 'lock position' state of the specified item.
+ *
+ * This procedure returns the specified item's lock position state.
+ *
+ * Returns: Whether the item's position is locked.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_item_get_lock_position (gint32 item_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean lock_position = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-item-get-lock-position",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ lock_position = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return lock_position;
+}
+
+/**
+ * gimp_item_set_lock_position:
+ * @item_ID: The item.
+ * @lock_position: The new item 'lock position' state.
+ *
+ * Set the 'lock position' state of the specified item.
+ *
+ * This procedure sets the specified item's lock position state.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_item_set_lock_position (gint32 item_ID,
+ gboolean lock_position)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-item-set-lock-position",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_INT32, lock_position,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_item_get_color_tag:
+ * @item_ID: The item.
+ *
+ * Get the color tag of the specified item.
+ *
+ * This procedure returns the specified item's color tag.
+ *
+ * Returns: The item's color tag.
+ *
+ * Since: 2.10
+ **/
+GimpColorTag
+gimp_item_get_color_tag (gint32 item_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpColorTag color_tag = 0;
+
+ return_vals = gimp_run_procedure ("gimp-item-get-color-tag",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ color_tag = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return color_tag;
+}
+
+/**
+ * gimp_item_set_color_tag:
+ * @item_ID: The item.
+ * @color_tag: The new item color tag.
+ *
+ * Set the color tag of the specified item.
+ *
+ * This procedure sets the specified item's color tag.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_item_set_color_tag (gint32 item_ID,
+ GimpColorTag color_tag)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-item-set-color-tag",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_INT32, color_tag,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_item_get_tattoo:
+ * @item_ID: The item.
+ *
+ * Get the tattoo of the specified item.
+ *
+ * This procedure returns the specified item's tattoo. A tattoo is a
+ * unique and permanent identifier attached to a item that can be used
+ * to uniquely identify a item within an image even between sessions.
+ *
+ * Returns: The item tattoo.
+ *
+ * Since: 2.8
+ **/
+gint
+gimp_item_get_tattoo (gint32 item_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint tattoo = 0;
+
+ return_vals = gimp_run_procedure ("gimp-item-get-tattoo",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ tattoo = return_vals[1].data.d_tattoo;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return tattoo;
+}
+
+/**
+ * gimp_item_set_tattoo:
+ * @item_ID: The item.
+ * @tattoo: The new item tattoo.
+ *
+ * Set the tattoo of the specified item.
+ *
+ * This procedure sets the specified item's tattoo. A tattoo is a
+ * unique and permanent identifier attached to a item that can be used
+ * to uniquely identify a item within an image even between sessions.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_item_set_tattoo (gint32 item_ID,
+ gint tattoo)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-item-set-tattoo",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_INT32, tattoo,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_item_attach_parasite:
+ * @item_ID: The item.
+ * @parasite: The parasite to attach to the item.
+ *
+ * Add a parasite to an item.
+ *
+ * This procedure attaches a parasite to an item. It has no return
+ * values.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_item_attach_parasite (gint32 item_ID,
+ const GimpParasite *parasite)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-item-attach-parasite",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_PARASITE, parasite,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_item_detach_parasite:
+ * @item_ID: The item.
+ * @name: The name of the parasite to detach from the item.
+ *
+ * Removes a parasite from an item.
+ *
+ * This procedure detaches a parasite from an item. It has no return
+ * values.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_item_detach_parasite (gint32 item_ID,
+ const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-item-detach-parasite",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_item_get_parasite:
+ * @item_ID: The item.
+ * @name: The name of the parasite to find.
+ *
+ * Look up a parasite in an item
+ *
+ * Finds and returns the parasite that is attached to an item.
+ *
+ * Returns: The found parasite.
+ *
+ * Since: 2.8
+ **/
+GimpParasite *
+gimp_item_get_parasite (gint32 item_ID,
+ const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpParasite *parasite = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-item-get-parasite",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ parasite = gimp_parasite_copy (&return_vals[1].data.d_parasite);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return parasite;
+}
+
+/**
+ * gimp_item_get_parasite_list:
+ * @item_ID: The item.
+ * @num_parasites: The number of attached parasites.
+ *
+ * List all parasites.
+ *
+ * Returns a list of all parasites currently attached the an item.
+ *
+ * Returns: The names of currently attached parasites. The returned
+ * value must be freed with g_strfreev().
+ *
+ * Since: 2.8
+ **/
+gchar **
+gimp_item_get_parasite_list (gint32 item_ID,
+ gint *num_parasites)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar **parasites = NULL;
+ gint i;
+
+ return_vals = gimp_run_procedure ("gimp-item-get-parasite-list",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ *num_parasites = 0;
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ *num_parasites = return_vals[1].data.d_int32;
+ if (*num_parasites > 0)
+ {
+ parasites = g_new0 (gchar *, *num_parasites + 1);
+ for (i = 0; i < *num_parasites; i++)
+ parasites[i] = g_strdup (return_vals[2].data.d_stringarray[i]);
+ }
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return parasites;
+}
diff --git a/libgimp/gimpitem_pdb.h b/libgimp/gimpitem_pdb.h
new file mode 100644
index 0000000..f9c2158
--- /dev/null
+++ b/libgimp/gimpitem_pdb.h
@@ -0,0 +1,85 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpitem_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_ITEM_PDB_H__
+#define __GIMP_ITEM_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_item_is_valid (gint32 item_ID);
+gint32 gimp_item_get_image (gint32 item_ID);
+gboolean gimp_item_delete (gint32 item_ID);
+gboolean gimp_item_is_drawable (gint32 item_ID);
+gboolean gimp_item_is_layer (gint32 item_ID);
+gboolean gimp_item_is_text_layer (gint32 item_ID);
+gboolean gimp_item_is_channel (gint32 item_ID);
+gboolean gimp_item_is_layer_mask (gint32 item_ID);
+gboolean gimp_item_is_selection (gint32 item_ID);
+gboolean gimp_item_is_vectors (gint32 item_ID);
+gboolean gimp_item_is_group (gint32 item_ID);
+gint32 gimp_item_get_parent (gint32 item_ID);
+gint* gimp_item_get_children (gint32 item_ID,
+ gint *num_children);
+gboolean gimp_item_get_expanded (gint32 item_ID);
+gboolean gimp_item_set_expanded (gint32 item_ID,
+ gboolean expanded);
+gchar* gimp_item_get_name (gint32 item_ID);
+gboolean gimp_item_set_name (gint32 item_ID,
+ const gchar *name);
+gboolean gimp_item_get_visible (gint32 item_ID);
+gboolean gimp_item_set_visible (gint32 item_ID,
+ gboolean visible);
+gboolean gimp_item_get_linked (gint32 item_ID);
+gboolean gimp_item_set_linked (gint32 item_ID,
+ gboolean linked);
+gboolean gimp_item_get_lock_content (gint32 item_ID);
+gboolean gimp_item_set_lock_content (gint32 item_ID,
+ gboolean lock_content);
+gboolean gimp_item_get_lock_position (gint32 item_ID);
+gboolean gimp_item_set_lock_position (gint32 item_ID,
+ gboolean lock_position);
+GimpColorTag gimp_item_get_color_tag (gint32 item_ID);
+gboolean gimp_item_set_color_tag (gint32 item_ID,
+ GimpColorTag color_tag);
+gint gimp_item_get_tattoo (gint32 item_ID);
+gboolean gimp_item_set_tattoo (gint32 item_ID,
+ gint tattoo);
+gboolean gimp_item_attach_parasite (gint32 item_ID,
+ const GimpParasite *parasite);
+gboolean gimp_item_detach_parasite (gint32 item_ID,
+ const gchar *name);
+GimpParasite* gimp_item_get_parasite (gint32 item_ID,
+ const gchar *name);
+gchar** gimp_item_get_parasite_list (gint32 item_ID,
+ gint *num_parasites);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_ITEM_PDB_H__ */
diff --git a/libgimp/gimpitemcombobox.c b/libgimp/gimpitemcombobox.c
new file mode 100644
index 0000000..07d5007
--- /dev/null
+++ b/libgimp/gimpitemcombobox.c
@@ -0,0 +1,589 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpitemcombobox.c
+ * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
+ * Copyright (C) 2006 Simon Budig <simon@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "gimp.h"
+
+#include "gimpuitypes.h"
+#include "gimpitemcombobox.h"
+#include "gimppixbuf.h"
+
+
+/**
+ * SECTION: gimpitemcombobox
+ * @title: GimpItemComboBox
+ * @short_description: Widgets providing popup menus of items.
+ *
+ * Widgets providing popup menus of items (layers, channels,
+ * drawables, vectors).
+ **/
+
+
+#define THUMBNAIL_SIZE 24
+#define WIDTH_REQUEST 200
+
+
+#define GET_PRIVATE(obj) (g_object_get_data (G_OBJECT (obj), "gimp-item-combo-box-private"))
+
+
+typedef struct _GimpItemComboBoxPrivate GimpItemComboBoxPrivate;
+
+struct _GimpItemComboBoxPrivate
+{
+ GimpItemConstraintFunc constraint;
+ gpointer data;
+};
+
+typedef struct _GimpDrawableComboBoxClass GimpDrawableComboBoxClass;
+typedef struct _GimpChannelComboBoxClass GimpChannelComboBoxClass;
+typedef struct _GimpLayerComboBoxClass GimpLayerComboBoxClass;
+typedef struct _GimpVectorsComboBoxClass GimpVectorsComboBoxClass;
+
+struct _GimpDrawableComboBox
+{
+ GimpIntComboBox parent_instance;
+};
+
+struct _GimpDrawableComboBoxClass
+{
+ GimpIntComboBoxClass parent_class;
+};
+
+struct _GimpChannelComboBox
+{
+ GimpIntComboBox parent_instance;
+};
+
+struct _GimpChannelComboBoxClass
+{
+ GimpIntComboBoxClass parent_class;
+};
+
+struct _GimpLayerComboBox
+{
+ GimpIntComboBox parent_instance;
+};
+
+struct _GimpLayerComboBoxClass
+{
+ GimpIntComboBoxClass parent_class;
+};
+
+struct _GimpVectorsComboBox
+{
+ GimpIntComboBox parent_instance;
+};
+
+struct _GimpVectorsComboBoxClass
+{
+ GimpIntComboBoxClass parent_class;
+};
+
+
+static GtkWidget * gimp_item_combo_box_new (GType type,
+ GimpItemConstraintFunc constraint,
+ gpointer data);
+
+static void gimp_item_combo_box_populate (GimpIntComboBox *combo_box);
+static void gimp_item_combo_box_model_add (GimpIntComboBox *combo_box,
+ GtkListStore *store,
+ gint32 image,
+ gint num_items,
+ gint32 *items,
+ gint tree_level);
+
+static void gimp_item_combo_box_drag_data_received (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time);
+
+static void gimp_item_combo_box_changed (GimpIntComboBox *combo_box);
+
+
+static const GtkTargetEntry targets[] =
+{
+ { "application/x-gimp-channel-id", 0 },
+ { "application/x-gimp-layer-id", 0 },
+ { "application/x-gimp-vectors-id", 0 }
+};
+
+
+G_DEFINE_TYPE (GimpDrawableComboBox, gimp_drawable_combo_box,
+ GIMP_TYPE_INT_COMBO_BOX)
+
+static void
+gimp_drawable_combo_box_class_init (GimpDrawableComboBoxClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ widget_class->drag_data_received = gimp_item_combo_box_drag_data_received;
+}
+
+static void
+gimp_drawable_combo_box_init (GimpDrawableComboBox *combo_box)
+{
+ gtk_drag_dest_set (GTK_WIDGET (combo_box),
+ GTK_DEST_DEFAULT_HIGHLIGHT |
+ GTK_DEST_DEFAULT_MOTION |
+ GTK_DEST_DEFAULT_DROP,
+ targets, 2,
+ GDK_ACTION_COPY);
+
+ g_object_set_data_full (G_OBJECT (combo_box), "gimp-item-combo-box-private",
+ g_new0 (GimpItemComboBoxPrivate, 1),
+ (GDestroyNotify) g_free);
+}
+
+/**
+ * gimp_drawable_combo_box_new:
+ * @constraint: a #GimpDrawableConstraintFunc or %NULL
+ * @data: a pointer that is passed to @constraint
+ *
+ * Creates a new #GimpIntComboBox filled with all currently opened
+ * drawables. If a @constraint function is specified, it is called for
+ * each drawable and only if the function returns %TRUE, the drawable
+ * is added to the combobox.
+ *
+ * You should use gimp_int_combo_box_connect() to initialize and connect
+ * the combo. Use gimp_int_combo_box_set_active() to get the active
+ * drawable ID and gimp_int_combo_box_get_active() to retrieve the ID
+ * of the selected drawable.
+ *
+ * Return value: a new #GimpIntComboBox.
+ *
+ * Since: 2.2
+ **/
+GtkWidget *
+gimp_drawable_combo_box_new (GimpDrawableConstraintFunc constraint,
+ gpointer data)
+{
+ return gimp_item_combo_box_new (GIMP_TYPE_DRAWABLE_COMBO_BOX,
+ constraint, data);
+}
+
+
+G_DEFINE_TYPE (GimpChannelComboBox, gimp_channel_combo_box,
+ GIMP_TYPE_INT_COMBO_BOX)
+
+static void
+gimp_channel_combo_box_class_init (GimpChannelComboBoxClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ widget_class->drag_data_received = gimp_item_combo_box_drag_data_received;
+}
+
+static void
+gimp_channel_combo_box_init (GimpChannelComboBox *combo_box)
+{
+ gtk_drag_dest_set (GTK_WIDGET (combo_box),
+ GTK_DEST_DEFAULT_HIGHLIGHT |
+ GTK_DEST_DEFAULT_MOTION |
+ GTK_DEST_DEFAULT_DROP,
+ targets, 1,
+ GDK_ACTION_COPY);
+
+ g_object_set_data_full (G_OBJECT (combo_box), "gimp-item-combo-box-private",
+ g_new0 (GimpItemComboBoxPrivate, 1),
+ (GDestroyNotify) g_free);
+}
+
+/**
+ * gimp_channel_combo_box_new:
+ * @constraint: a #GimpDrawableConstraintFunc or %NULL
+ * @data: a pointer that is passed to @constraint
+ *
+ * Creates a new #GimpIntComboBox filled with all currently opened
+ * channels. See gimp_drawable_combo_box_new() for more information.
+ *
+ * Return value: a new #GimpIntComboBox.
+ *
+ * Since: 2.2
+ **/
+GtkWidget *
+gimp_channel_combo_box_new (GimpDrawableConstraintFunc constraint,
+ gpointer data)
+{
+ return gimp_item_combo_box_new (GIMP_TYPE_CHANNEL_COMBO_BOX,
+ constraint, data);
+}
+
+
+G_DEFINE_TYPE (GimpLayerComboBox, gimp_layer_combo_box,
+ GIMP_TYPE_INT_COMBO_BOX)
+
+static void
+gimp_layer_combo_box_class_init (GimpLayerComboBoxClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ widget_class->drag_data_received = gimp_item_combo_box_drag_data_received;
+}
+
+static void
+gimp_layer_combo_box_init (GimpLayerComboBox *combo_box)
+{
+ gtk_drag_dest_set (GTK_WIDGET (combo_box),
+ GTK_DEST_DEFAULT_HIGHLIGHT |
+ GTK_DEST_DEFAULT_MOTION |
+ GTK_DEST_DEFAULT_DROP,
+ targets + 1, 1,
+ GDK_ACTION_COPY);
+
+ g_object_set_data_full (G_OBJECT (combo_box), "gimp-item-combo-box-private",
+ g_new0 (GimpItemComboBoxPrivate, 1),
+ (GDestroyNotify) g_free);
+}
+
+/**
+ * gimp_layer_combo_box_new:
+ * @constraint: a #GimpDrawableConstraintFunc or %NULL
+ * @data: a pointer that is passed to @constraint
+ *
+ * Creates a new #GimpIntComboBox filled with all currently opened
+ * layers. See gimp_drawable_combo_box_new() for more information.
+ *
+ * Return value: a new #GimpIntComboBox.
+ *
+ * Since: 2.2
+ **/
+GtkWidget *
+gimp_layer_combo_box_new (GimpDrawableConstraintFunc constraint,
+ gpointer data)
+{
+ return gimp_item_combo_box_new (GIMP_TYPE_LAYER_COMBO_BOX,
+ constraint, data);
+}
+
+
+G_DEFINE_TYPE (GimpVectorsComboBox, gimp_vectors_combo_box,
+ GIMP_TYPE_INT_COMBO_BOX)
+
+static void
+gimp_vectors_combo_box_class_init (GimpVectorsComboBoxClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ widget_class->drag_data_received = gimp_item_combo_box_drag_data_received;
+}
+
+static void
+gimp_vectors_combo_box_init (GimpVectorsComboBox *combo_box)
+{
+ gtk_drag_dest_set (GTK_WIDGET (combo_box),
+ GTK_DEST_DEFAULT_HIGHLIGHT |
+ GTK_DEST_DEFAULT_MOTION |
+ GTK_DEST_DEFAULT_DROP,
+ targets + 2, 1,
+ GDK_ACTION_COPY);
+
+ g_object_set_data_full (G_OBJECT (combo_box), "gimp-item-combo-box-private",
+ g_new0 (GimpItemComboBoxPrivate, 1),
+ (GDestroyNotify) g_free);
+}
+
+
+/**
+ * gimp_vectors_combo_box_new:
+ * @constraint: a #GimpVectorsConstraintFunc or %NULL
+ * @data: a pointer that is passed to @constraint
+ *
+ * Creates a new #GimpIntComboBox filled with all currently opened
+ * vectors objects. If a @constraint function is specified, it is called for
+ * each vectors object and only if the function returns %TRUE, the vectors
+ * object is added to the combobox.
+ *
+ * You should use gimp_int_combo_box_connect() to initialize and connect
+ * the combo. Use gimp_int_combo_box_set_active() to set the active
+ * vectors ID and gimp_int_combo_box_get_active() to retrieve the ID
+ * of the selected vectors object.
+ *
+ * Return value: a new #GimpIntComboBox.
+ *
+ * Since: 2.4
+ **/
+GtkWidget *
+gimp_vectors_combo_box_new (GimpVectorsConstraintFunc constraint,
+ gpointer data)
+{
+ return gimp_item_combo_box_new (GIMP_TYPE_VECTORS_COMBO_BOX,
+ constraint, data);
+}
+
+
+static GtkWidget *
+gimp_item_combo_box_new (GType type,
+ GimpItemConstraintFunc constraint,
+ gpointer data)
+{
+ GimpIntComboBox *combo_box;
+ GimpItemComboBoxPrivate *private;
+
+ combo_box = g_object_new (type,
+ "width-request", WIDTH_REQUEST,
+ "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
+ NULL);
+
+ private = GET_PRIVATE (combo_box);
+
+ private->constraint = constraint;
+ private->data = data;
+
+ gimp_item_combo_box_populate (combo_box);
+
+ g_signal_connect (combo_box, "changed",
+ G_CALLBACK (gimp_item_combo_box_changed),
+ NULL);
+
+ return GTK_WIDGET (combo_box);
+}
+
+static void
+gimp_item_combo_box_populate (GimpIntComboBox *combo_box)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gint32 *images;
+ gint num_images;
+ gint i;
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));
+
+ images = gimp_image_list (&num_images);
+
+ for (i = 0; i < num_images; i++)
+ {
+ gint32 *items;
+ gint num_items;
+
+ if (GIMP_IS_DRAWABLE_COMBO_BOX (combo_box) ||
+ GIMP_IS_LAYER_COMBO_BOX (combo_box))
+ {
+ items = gimp_image_get_layers (images[i], &num_items);
+ gimp_item_combo_box_model_add (combo_box, GTK_LIST_STORE (model),
+ images[i],
+ num_items, items, 0);
+ g_free (items);
+ }
+
+ if (GIMP_IS_DRAWABLE_COMBO_BOX (combo_box) ||
+ GIMP_IS_CHANNEL_COMBO_BOX (combo_box))
+ {
+ items = gimp_image_get_channels (images[i], &num_items);
+ gimp_item_combo_box_model_add (combo_box, GTK_LIST_STORE (model),
+ images[i],
+ num_items, items, 0);
+ g_free (items);
+ }
+
+ if (GIMP_IS_VECTORS_COMBO_BOX (combo_box))
+ {
+ items = gimp_image_get_vectors (images[i], &num_items);
+ gimp_item_combo_box_model_add (combo_box, GTK_LIST_STORE (model),
+ images[i],
+ num_items, items, 0);
+ g_free (items);
+ }
+ }
+
+ g_free (images);
+
+ if (gtk_tree_model_get_iter_first (model, &iter))
+ gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_box), &iter);
+}
+
+static void
+gimp_item_combo_box_model_add (GimpIntComboBox *combo_box,
+ GtkListStore *store,
+ gint32 image,
+ gint num_items,
+ gint32 *items,
+ gint tree_level)
+{
+ GimpItemComboBoxPrivate *private = GET_PRIVATE (combo_box);
+ GtkTreeIter iter;
+ gint i;
+ gchar *indent;
+
+ if (tree_level > 0)
+ {
+ indent = g_new (gchar, tree_level + 2);
+ memset (indent, '-', tree_level);
+ indent[tree_level] = ' ';
+ indent[tree_level + 1] = '\0';
+ }
+ else
+ {
+ indent = g_strdup ("");
+ }
+
+ for (i = 0; i < num_items; i++)
+ {
+ if (! private->constraint ||
+ (* private->constraint) (image, items[i], private->data))
+ {
+ gchar *image_name = gimp_image_get_name (image);
+ gchar *item_name = gimp_item_get_name (items[i]);
+ gchar *label;
+ GdkPixbuf *thumb;
+
+ label = g_strdup_printf ("%s%s-%d / %s-%d",
+ indent, image_name, image,
+ item_name, items[i]);
+
+ g_free (item_name);
+ g_free (image_name);
+
+ if (GIMP_IS_VECTORS_COMBO_BOX (combo_box))
+ thumb = NULL;
+ else
+ thumb = gimp_drawable_get_thumbnail (items[i],
+ THUMBNAIL_SIZE, THUMBNAIL_SIZE,
+ GIMP_PIXBUF_SMALL_CHECKS);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ GIMP_INT_STORE_VALUE, items[i],
+ GIMP_INT_STORE_LABEL, label,
+ GIMP_INT_STORE_PIXBUF, thumb,
+ -1);
+
+ if (thumb)
+ g_object_unref (thumb);
+
+ g_free (label);
+ }
+
+ if (gimp_item_is_group (items[i]))
+ {
+ gint32 *children;
+ gint n_children;
+
+ children = gimp_item_get_children (items[i], &n_children);
+ gimp_item_combo_box_model_add (combo_box, store,
+ image,
+ n_children, children,
+ tree_level + 1);
+ g_free (children);
+ }
+ }
+
+ g_free (indent);
+}
+
+static void
+gimp_item_combo_box_drag_data_received (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time)
+{
+ gint length = gtk_selection_data_get_length (selection);
+ gchar *str;
+
+ if (gtk_selection_data_get_format (selection) != 8 || length < 1)
+ {
+ g_warning ("%s: received invalid item ID data", G_STRFUNC);
+ return;
+ }
+
+ str = g_strndup ((const gchar *) gtk_selection_data_get_data (selection),
+ length);
+
+ if (g_utf8_validate (str, -1, NULL))
+ {
+ gint pid;
+ gint ID;
+
+ if (sscanf (str, "%i:%i", &pid, &ID) == 2 &&
+ pid == gimp_getpid ())
+ {
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (widget), ID);
+ }
+ }
+
+ g_free (str);
+}
+
+static gboolean
+gimp_item_combo_box_remove_items (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ gint item_ID;
+ GList **remove = data;
+
+ gtk_tree_model_get (model, iter,
+ GIMP_INT_STORE_VALUE, &item_ID,
+ -1);
+
+ if (item_ID > 0)
+ *remove = g_list_prepend (*remove, g_memdup (iter, sizeof (GtkTreeIter)));
+
+ return FALSE;
+}
+
+static void
+gimp_item_combo_box_changed (GimpIntComboBox *combo_box)
+{
+ gint item_ID;
+
+ if (gimp_int_combo_box_get_active (combo_box, &item_ID))
+ {
+ if (item_ID > 0 && ! gimp_item_is_valid (item_ID))
+ {
+ GtkTreeModel *model;
+ GList *remove = NULL;
+ GList *list;
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));
+
+ g_signal_stop_emission_by_name (combo_box, "changed");
+
+ gtk_tree_model_foreach (model,
+ gimp_item_combo_box_remove_items,
+ &remove);
+
+ for (list = remove; list; list = g_list_next (list))
+ gtk_list_store_remove (GTK_LIST_STORE (model), list->data);
+
+ g_list_free_full (remove, (GDestroyNotify) g_free);
+
+ gimp_item_combo_box_populate (combo_box);
+ }
+ }
+}
diff --git a/libgimp/gimpitemcombobox.h b/libgimp/gimpitemcombobox.h
new file mode 100644
index 0000000..794846d
--- /dev/null
+++ b/libgimp/gimpitemcombobox.h
@@ -0,0 +1,77 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpitemcombobox.h
+ * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
+ * Copyright (C) 2006 Simon Budig <simon@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_UI_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimpui.h> can be included directly."
+#endif
+
+#ifndef __GIMP_ITEM_COMBO_BOX_H__
+#define __GIMP_ITEM_COMBO_BOX_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+#define GIMP_TYPE_DRAWABLE_COMBO_BOX (gimp_drawable_combo_box_get_type ())
+#define GIMP_DRAWABLE_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_DRAWABLE_COMBO_BOX, GimpDrawableComboBox))
+#define GIMP_IS_DRAWABLE_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_DRAWABLE_COMBO_BOX))
+
+#define GIMP_TYPE_CHANNEL_COMBO_BOX (gimp_channel_combo_box_get_type ())
+#define GIMP_CHANNEL_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CHANNEL_COMBO_BOX, GimpChannelComboBox))
+#define GIMP_IS_CHANNEL_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CHANNEL_COMBO_BOX))
+
+#define GIMP_TYPE_LAYER_COMBO_BOX (gimp_layer_combo_box_get_type ())
+#define GIMP_LAYER_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_LAYER_COMBO_BOX, GimpLayerComboBox))
+#define GIMP_IS_LAYER_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_LAYER_COMBO_BOX))
+
+#define GIMP_TYPE_VECTORS_COMBO_BOX (gimp_vectors_combo_box_get_type ())
+#define GIMP_VECTORS_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_VECTORS_COMBO_BOX, GimpVectorsComboBox))
+#define GIMP_IS_VECTORS_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_VECTORS_COMBO_BOX))
+
+
+typedef gboolean (* GimpItemConstraintFunc) (gint32 image_id,
+ gint32 item_id,
+ gpointer data);
+
+typedef GimpItemConstraintFunc GimpVectorsConstraintFunc;
+typedef GimpItemConstraintFunc GimpDrawableConstraintFunc;
+
+
+GType gimp_drawable_combo_box_get_type (void) G_GNUC_CONST;
+GType gimp_channel_combo_box_get_type (void) G_GNUC_CONST;
+GType gimp_layer_combo_box_get_type (void) G_GNUC_CONST;
+GType gimp_vectors_combo_box_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_drawable_combo_box_new (GimpDrawableConstraintFunc constraint,
+ gpointer data);
+GtkWidget * gimp_channel_combo_box_new (GimpDrawableConstraintFunc constraint,
+ gpointer data);
+GtkWidget * gimp_layer_combo_box_new (GimpDrawableConstraintFunc constraint,
+ gpointer data);
+GtkWidget * gimp_vectors_combo_box_new (GimpVectorsConstraintFunc constraint,
+ gpointer data);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_ITEM_COMBO_BOX_H__ */
diff --git a/libgimp/gimpitemtransform_pdb.c b/libgimp/gimpitemtransform_pdb.c
new file mode 100644
index 0000000..c811bef
--- /dev/null
+++ b/libgimp/gimpitemtransform_pdb.c
@@ -0,0 +1,697 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpitemtransform_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpitemtransform
+ * @title: gimpitemtransform
+ * @short_description: Functions to perform transformations on items.
+ *
+ * Functions to perform transformations on items.
+ **/
+
+
+/**
+ * gimp_item_transform_translate:
+ * @item_ID: The item.
+ * @off_x: Offset in x direction.
+ * @off_y: Offset in y direction.
+ *
+ * Translate the item by the specified offsets.
+ *
+ * This procedure translates the item by the amounts specified in the
+ * off_x and off_y arguments. These can be negative, and are considered
+ * offsets from the current position. The offsets will be rounded to
+ * the nearest pixel unless the item is a path.
+ *
+ * If the item is attached to an image and has its linked flag set to
+ * TRUE, all additional items contained in the image which have the
+ * linked flag set to TRUE will also be translated by the specified
+ * offsets.
+ *
+ * Returns: The translated item.
+ *
+ * Since: 2.10
+ **/
+gint32
+gimp_item_transform_translate (gint32 item_ID,
+ gdouble off_x,
+ gdouble off_y)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_item_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-item-transform-translate",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_FLOAT, off_x,
+ GIMP_PDB_FLOAT, off_y,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_item_ID = return_vals[1].data.d_item;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_item_ID;
+}
+
+/**
+ * gimp_item_transform_flip_simple:
+ * @item_ID: The affected item.
+ * @flip_type: Type of flip.
+ * @auto_center: Whether to automatically position the axis in the selection center.
+ * @axis: coord. of flip axis.
+ *
+ * Flip the specified item either vertically or horizontally.
+ *
+ * This procedure flips the specified item.
+ *
+ * If a selection exists and the item is a drawable, the portion of the
+ * drawable which lies under the selection is cut from the drawable and
+ * made into a floating selection which is then flipped. If auto_center
+ * is set to TRUE, the flip is around the selection's center.
+ * Otherwise, the coordinate of the axis needs to be specified. The
+ * return value is the ID of the flipped floating selection.
+ *
+ * If there is no selection or the item is not a drawable, the entire
+ * item will be flipped around its center if auto_center is set to
+ * TRUE, otherwise the coordinate of the axis needs to be specified.
+ * Additionally, if the item has its linked flag set to TRUE, all
+ * additional items contained in the image which have the linked flag
+ * set to TRUE will also be flipped around the same axis. The return
+ * value will be equal to the item ID supplied as input.
+ *
+ * This procedure is affected by the following context setters:
+ * gimp_context_set_transform_resize().
+ *
+ * Returns: The flipped item.
+ *
+ * Since: 2.2
+ **/
+gint32
+gimp_item_transform_flip_simple (gint32 item_ID,
+ GimpOrientationType flip_type,
+ gboolean auto_center,
+ gdouble axis)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_item_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-item-transform-flip-simple",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_INT32, flip_type,
+ GIMP_PDB_INT32, auto_center,
+ GIMP_PDB_FLOAT, axis,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_item_ID = return_vals[1].data.d_item;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_item_ID;
+}
+
+/**
+ * gimp_item_transform_flip:
+ * @item_ID: The affected item.
+ * @x0: horz. coord. of one end of axis.
+ * @y0: vert. coord. of one end of axis.
+ * @x1: horz. coord. of other end of axis.
+ * @y1: vert. coord. of other end of axis.
+ *
+ * Flip the specified item around a given line.
+ *
+ * This procedure flips the specified item.
+ *
+ * If a selection exists and the item is a drawable, the portion of the
+ * drawable which lies under the selection is cut from the drawable and
+ * made into a floating selection which is then flipped. The axis to
+ * flip around is specified by specifying two points from that line.
+ * The return value is the ID of the flipped floating selection.
+ *
+ * If there is no selection or the item is not a drawable, the entire
+ * item will be flipped around the specified axis. Additionally, if the
+ * item has its linked flag set to TRUE, all additional items contained
+ * in the image which have the linked flag set to TRUE will also be
+ * flipped around the same axis. The return value will be equal to the
+ * item ID supplied as input.
+ *
+ * This procedure is affected by the following context setters:
+ * gimp_context_set_interpolation(),
+ * gimp_context_set_transform_direction(),
+ * gimp_context_set_transform_resize().
+ *
+ * Returns: The flipped item.
+ *
+ * Since: 2.8
+ **/
+gint32
+gimp_item_transform_flip (gint32 item_ID,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_item_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-item-transform-flip",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_FLOAT, x0,
+ GIMP_PDB_FLOAT, y0,
+ GIMP_PDB_FLOAT, x1,
+ GIMP_PDB_FLOAT, y1,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_item_ID = return_vals[1].data.d_item;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_item_ID;
+}
+
+/**
+ * gimp_item_transform_perspective:
+ * @item_ID: The affected item.
+ * @x0: The new x coordinate of upper-left corner of original bounding box.
+ * @y0: The new y coordinate of upper-left corner of original bounding box.
+ * @x1: The new x coordinate of upper-right corner of original bounding box.
+ * @y1: The new y coordinate of upper-right corner of original bounding box.
+ * @x2: The new x coordinate of lower-left corner of original bounding box.
+ * @y2: The new y coordinate of lower-left corner of original bounding box.
+ * @x3: The new x coordinate of lower-right corner of original bounding box.
+ * @y3: The new y coordinate of lower-right corner of original bounding box.
+ *
+ * Perform a possibly non-affine transformation on the specified item.
+ *
+ * This procedure performs a possibly non-affine transformation on the
+ * specified item by allowing the corners of the original bounding box
+ * to be arbitrarily remapped to any values.
+ *
+ * The 4 coordinates specify the new locations of each corner of the
+ * original bounding box. By specifying these values, any affine
+ * transformation (rotation, scaling, translation) can be affected.
+ * Additionally, these values can be specified such that the resulting
+ * transformed item will appear to have been projected via a
+ * perspective transform.
+ *
+ * If a selection exists and the item is a drawable, the portion of the
+ * drawable which lies under the selection is cut from the drawable and
+ * made into a floating selection which is then transformed as
+ * specified. The return value is the ID of the transformed floating
+ * selection.
+ *
+ * If there is no selection or the item is not a drawable, the entire
+ * item will be transformed according to the specified mapping.
+ * Additionally, if the item has its linked flag set to TRUE, all
+ * additional items contained in the image which have the linked flag
+ * set to TRUE will also be transformed the same way. The return value
+ * will be equal to the item ID supplied as input.
+ *
+ * This procedure is affected by the following context setters:
+ * gimp_context_set_interpolation(),
+ * gimp_context_set_transform_direction(),
+ * gimp_context_set_transform_resize().
+ *
+ * Returns: The transformed item.
+ *
+ * Since: 2.8
+ **/
+gint32
+gimp_item_transform_perspective (gint32 item_ID,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2,
+ gdouble x3,
+ gdouble y3)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_item_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-item-transform-perspective",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_FLOAT, x0,
+ GIMP_PDB_FLOAT, y0,
+ GIMP_PDB_FLOAT, x1,
+ GIMP_PDB_FLOAT, y1,
+ GIMP_PDB_FLOAT, x2,
+ GIMP_PDB_FLOAT, y2,
+ GIMP_PDB_FLOAT, x3,
+ GIMP_PDB_FLOAT, y3,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_item_ID = return_vals[1].data.d_item;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_item_ID;
+}
+
+/**
+ * gimp_item_transform_rotate_simple:
+ * @item_ID: The affected item.
+ * @rotate_type: Type of rotation.
+ * @auto_center: Whether to automatically rotate around the selection center.
+ * @center_x: The hor. coordinate of the center of rotation.
+ * @center_y: The vert. coordinate of the center of rotation.
+ *
+ * Rotate the specified item about given coordinates through the
+ * specified angle.
+ *
+ * This function rotates the specified item.
+ *
+ * If a selection exists and the item is a drawable, the portion of the
+ * drawable which lies under the selection is cut from the drawable and
+ * made into a floating selection which is then rotated by the
+ * specified amount. If auto_center is set to TRUE, the rotation is
+ * around the selection's center. Otherwise, the coordinate of the
+ * center point needs to be specified. The return value is the ID of
+ * the rotated floating selection.
+ *
+ * If there is no selection or the item is not a drawable, the entire
+ * item will be rotated around its center if auto_center is set to
+ * TRUE, otherwise the coordinate of the center point needs to be
+ * specified. Additionally, if the item has its linked flag set to
+ * TRUE, all additional items contained in the image which have the
+ * linked flag set to TRUE will also be rotated around the same center
+ * point. The return value will be equal to the item ID supplied as
+ * input.
+ *
+ * This procedure is affected by the following context setters:
+ * gimp_context_set_transform_resize().
+ *
+ * Returns: The rotated item.
+ *
+ * Since: 2.8
+ **/
+gint32
+gimp_item_transform_rotate_simple (gint32 item_ID,
+ GimpRotationType rotate_type,
+ gboolean auto_center,
+ gdouble center_x,
+ gdouble center_y)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_item_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-item-transform-rotate-simple",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_INT32, rotate_type,
+ GIMP_PDB_INT32, auto_center,
+ GIMP_PDB_FLOAT, center_x,
+ GIMP_PDB_FLOAT, center_y,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_item_ID = return_vals[1].data.d_item;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_item_ID;
+}
+
+/**
+ * gimp_item_transform_rotate:
+ * @item_ID: The affected item.
+ * @angle: The angle of rotation (radians).
+ * @auto_center: Whether to automatically rotate around the selection center.
+ * @center_x: The hor. coordinate of the center of rotation.
+ * @center_y: The vert. coordinate of the center of rotation.
+ *
+ * Rotate the specified item about given coordinates through the
+ * specified angle.
+ *
+ * This function rotates the specified item.
+ *
+ * If a selection exists and the item is a drawable, the portion of the
+ * drawable which lies under the selection is cut from the drawable and
+ * made into a floating selection which is then rotated by the
+ * specified amount. If auto_center is set to TRUE, the rotation is
+ * around the selection's center. Otherwise, the coordinate of the
+ * center point needs to be specified. The return value is the ID of
+ * the rotated floating selection.
+ *
+ * If there is no selection or the item is not a drawable, the entire
+ * item will be rotated around its center if auto_center is set to
+ * TRUE, otherwise the coordinate of the center point needs to be
+ * specified. Additionally, if the item has its linked flag set to
+ * TRUE, all additional items contained in the image which have the
+ * linked flag set to TRUE will also be rotated around the same center
+ * point. The return value will be equal to the item ID supplied as
+ * input.
+ *
+ * This procedure is affected by the following context setters:
+ * gimp_context_set_interpolation(),
+ * gimp_context_set_transform_direction(),
+ * gimp_context_set_transform_resize().
+ *
+ * Returns: The rotated item.
+ *
+ * Since: 2.8
+ **/
+gint32
+gimp_item_transform_rotate (gint32 item_ID,
+ gdouble angle,
+ gboolean auto_center,
+ gdouble center_x,
+ gdouble center_y)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_item_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-item-transform-rotate",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_FLOAT, angle,
+ GIMP_PDB_INT32, auto_center,
+ GIMP_PDB_FLOAT, center_x,
+ GIMP_PDB_FLOAT, center_y,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_item_ID = return_vals[1].data.d_item;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_item_ID;
+}
+
+/**
+ * gimp_item_transform_scale:
+ * @item_ID: The affected item.
+ * @x0: The new x coordinate of the upper-left corner of the scaled region.
+ * @y0: The new y coordinate of the upper-left corner of the scaled region.
+ * @x1: The new x coordinate of the lower-right corner of the scaled region.
+ * @y1: The new y coordinate of the lower-right corner of the scaled region.
+ *
+ * Scale the specified item.
+ *
+ * This procedure scales the specified item.
+ *
+ * The 2 coordinates specify the new locations of the top-left and
+ * bottom-roght corners of the original bounding box.
+ *
+ * If a selection exists and the item is a drawable, the portion of the
+ * drawable which lies under the selection is cut from the drawable and
+ * made into a floating selection which is then scaled as specified.
+ * The return value is the ID of the scaled floating selection.
+ *
+ * If there is no selection or the item is not a drawable, the entire
+ * item will be scaled according to the specified coordinates.
+ * Additionally, if the item has its linked flag set to TRUE, all
+ * additional items contained in the image which have the linked flag
+ * set to TRUE will also be scaled the same way. The return value will
+ * be equal to the item ID supplied as input.
+ *
+ * This procedure is affected by the following context setters:
+ * gimp_context_set_interpolation(),
+ * gimp_context_set_transform_direction(),
+ * gimp_context_set_transform_resize().
+ *
+ * Returns: The scaled item.
+ *
+ * Since: 2.8
+ **/
+gint32
+gimp_item_transform_scale (gint32 item_ID,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_item_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-item-transform-scale",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_FLOAT, x0,
+ GIMP_PDB_FLOAT, y0,
+ GIMP_PDB_FLOAT, x1,
+ GIMP_PDB_FLOAT, y1,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_item_ID = return_vals[1].data.d_item;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_item_ID;
+}
+
+/**
+ * gimp_item_transform_shear:
+ * @item_ID: The affected item.
+ * @shear_type: Type of shear.
+ * @magnitude: The magnitude of the shear.
+ *
+ * Shear the specified item about its center by the specified
+ * magnitude.
+ *
+ * This procedure shears the specified item.
+ *
+ * The shear type parameter indicates whether the shear will be applied
+ * horizontally or vertically. The magnitude can be either positive or
+ * negative and indicates the extent (in pixels) to shear by.
+ *
+ * If a selection exists and the item is a drawable, the portion of the
+ * drawable which lies under the selection is cut from the drawable and
+ * made into a floating selection which is then sheared as specified.
+ * The return value is the ID of the sheared floating selection.
+ *
+ * If there is no selection or the item is not a drawable, the entire
+ * item will be sheared according to the specified parameters.
+ * Additionally, if the item has its linked flag set to TRUE, all
+ * additional items contained in the image which have the linked flag
+ * set to TRUE will also be sheared the same way. The return value will
+ * be equal to the item ID supplied as input.
+ *
+ * This procedure is affected by the following context setters:
+ * gimp_context_set_interpolation(),
+ * gimp_context_set_transform_direction(),
+ * gimp_context_set_transform_resize().
+ *
+ * Returns: The sheared item.
+ *
+ * Since: 2.8
+ **/
+gint32
+gimp_item_transform_shear (gint32 item_ID,
+ GimpOrientationType shear_type,
+ gdouble magnitude)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_item_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-item-transform-shear",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_INT32, shear_type,
+ GIMP_PDB_FLOAT, magnitude,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_item_ID = return_vals[1].data.d_item;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_item_ID;
+}
+
+/**
+ * gimp_item_transform_2d:
+ * @item_ID: The affected item.
+ * @source_x: X coordinate of the transformation center.
+ * @source_y: Y coordinate of the transformation center.
+ * @scale_x: Amount to scale in x direction.
+ * @scale_y: Amount to scale in y direction.
+ * @angle: The angle of rotation (radians).
+ * @dest_x: X coordinate of where the center goes.
+ * @dest_y: Y coordinate of where the center goes.
+ *
+ * Transform the specified item in 2d.
+ *
+ * This procedure transforms the specified item.
+ *
+ * The transformation is done by scaling by the x and y scale factors
+ * about the point (source_x, source_y), then rotating around the same
+ * point, then translating that point to the new position (dest_x,
+ * dest_y).
+ *
+ * If a selection exists and the item is a drawable, the portion of the
+ * drawable which lies under the selection is cut from the drawable and
+ * made into a floating selection which is then transformed as
+ * specified. The return value is the ID of the transformed floating
+ * selection.
+ *
+ * If there is no selection or the item is not a drawable, the entire
+ * item will be transformed according to the specified parameters.
+ * Additionally, if the item has its linked flag set to TRUE, all
+ * additional items contained in the image which have the linked flag
+ * set to TRUE will also be transformed the same way. The return value
+ * will be equal to the item ID supplied as input.
+ *
+ * This procedure is affected by the following context setters:
+ * gimp_context_set_interpolation(),
+ * gimp_context_set_transform_direction(),
+ * gimp_context_set_transform_resize().
+ *
+ * Returns: The transformed item.
+ *
+ * Since: 2.8
+ **/
+gint32
+gimp_item_transform_2d (gint32 item_ID,
+ gdouble source_x,
+ gdouble source_y,
+ gdouble scale_x,
+ gdouble scale_y,
+ gdouble angle,
+ gdouble dest_x,
+ gdouble dest_y)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_item_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-item-transform-2d",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_FLOAT, source_x,
+ GIMP_PDB_FLOAT, source_y,
+ GIMP_PDB_FLOAT, scale_x,
+ GIMP_PDB_FLOAT, scale_y,
+ GIMP_PDB_FLOAT, angle,
+ GIMP_PDB_FLOAT, dest_x,
+ GIMP_PDB_FLOAT, dest_y,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_item_ID = return_vals[1].data.d_item;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_item_ID;
+}
+
+/**
+ * gimp_item_transform_matrix:
+ * @item_ID: The affected item.
+ * @coeff_0_0: coefficient (0,0) of the transformation matrix.
+ * @coeff_0_1: coefficient (0,1) of the transformation matrix.
+ * @coeff_0_2: coefficient (0,2) of the transformation matrix.
+ * @coeff_1_0: coefficient (1,0) of the transformation matrix.
+ * @coeff_1_1: coefficient (1,1) of the transformation matrix.
+ * @coeff_1_2: coefficient (1,2) of the transformation matrix.
+ * @coeff_2_0: coefficient (2,0) of the transformation matrix.
+ * @coeff_2_1: coefficient (2,1) of the transformation matrix.
+ * @coeff_2_2: coefficient (2,2) of the transformation matrix.
+ *
+ * Transform the specified item in 2d.
+ *
+ * This procedure transforms the specified item.
+ *
+ * The transformation is done by assembling a 3x3 matrix from the
+ * coefficients passed.
+ *
+ * If a selection exists and the item is a drawable, the portion of the
+ * drawable which lies under the selection is cut from the drawable and
+ * made into a floating selection which is then transformed as
+ * specified. The return value is the ID of the transformed floating
+ * selection.
+ *
+ * If there is no selection or the item is not a drawable, the entire
+ * item will be transformed according to the specified matrix.
+ * Additionally, if the item has its linked flag set to TRUE, all
+ * additional items contained in the image which have the linked flag
+ * set to TRUE will also be transformed the same way. The return value
+ * will be equal to the item ID supplied as input.
+ *
+ * This procedure is affected by the following context setters:
+ * gimp_context_set_interpolation(),
+ * gimp_context_set_transform_direction(),
+ * gimp_context_set_transform_resize().
+ *
+ * Returns: The transformed item.
+ *
+ * Since: 2.8
+ **/
+gint32
+gimp_item_transform_matrix (gint32 item_ID,
+ gdouble coeff_0_0,
+ gdouble coeff_0_1,
+ gdouble coeff_0_2,
+ gdouble coeff_1_0,
+ gdouble coeff_1_1,
+ gdouble coeff_1_2,
+ gdouble coeff_2_0,
+ gdouble coeff_2_1,
+ gdouble coeff_2_2)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_item_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-item-transform-matrix",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_FLOAT, coeff_0_0,
+ GIMP_PDB_FLOAT, coeff_0_1,
+ GIMP_PDB_FLOAT, coeff_0_2,
+ GIMP_PDB_FLOAT, coeff_1_0,
+ GIMP_PDB_FLOAT, coeff_1_1,
+ GIMP_PDB_FLOAT, coeff_1_2,
+ GIMP_PDB_FLOAT, coeff_2_0,
+ GIMP_PDB_FLOAT, coeff_2_1,
+ GIMP_PDB_FLOAT, coeff_2_2,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_item_ID = return_vals[1].data.d_item;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_item_ID;
+}
diff --git a/libgimp/gimpitemtransform_pdb.h b/libgimp/gimpitemtransform_pdb.h
new file mode 100644
index 0000000..18cb9ed
--- /dev/null
+++ b/libgimp/gimpitemtransform_pdb.h
@@ -0,0 +1,96 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpitemtransform_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_ITEM_TRANSFORM_PDB_H__
+#define __GIMP_ITEM_TRANSFORM_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gint32 gimp_item_transform_translate (gint32 item_ID,
+ gdouble off_x,
+ gdouble off_y);
+gint32 gimp_item_transform_flip_simple (gint32 item_ID,
+ GimpOrientationType flip_type,
+ gboolean auto_center,
+ gdouble axis);
+gint32 gimp_item_transform_flip (gint32 item_ID,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1);
+gint32 gimp_item_transform_perspective (gint32 item_ID,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2,
+ gdouble x3,
+ gdouble y3);
+gint32 gimp_item_transform_rotate_simple (gint32 item_ID,
+ GimpRotationType rotate_type,
+ gboolean auto_center,
+ gdouble center_x,
+ gdouble center_y);
+gint32 gimp_item_transform_rotate (gint32 item_ID,
+ gdouble angle,
+ gboolean auto_center,
+ gdouble center_x,
+ gdouble center_y);
+gint32 gimp_item_transform_scale (gint32 item_ID,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1);
+gint32 gimp_item_transform_shear (gint32 item_ID,
+ GimpOrientationType shear_type,
+ gdouble magnitude);
+gint32 gimp_item_transform_2d (gint32 item_ID,
+ gdouble source_x,
+ gdouble source_y,
+ gdouble scale_x,
+ gdouble scale_y,
+ gdouble angle,
+ gdouble dest_x,
+ gdouble dest_y);
+gint32 gimp_item_transform_matrix (gint32 item_ID,
+ gdouble coeff_0_0,
+ gdouble coeff_0_1,
+ gdouble coeff_0_2,
+ gdouble coeff_1_0,
+ gdouble coeff_1_1,
+ gdouble coeff_1_2,
+ gdouble coeff_2_0,
+ gdouble coeff_2_1,
+ gdouble coeff_2_2);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_ITEM_TRANSFORM_PDB_H__ */
diff --git a/libgimp/gimplayer.c b/libgimp/gimplayer.c
new file mode 100644
index 0000000..a3f58fd
--- /dev/null
+++ b/libgimp/gimplayer.c
@@ -0,0 +1,415 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2000 Peter Mattis and Spencer Kimball
+ *
+ * gimplayer.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#define GIMP_DISABLE_DEPRECATION_WARNINGS
+
+#include "gimp.h"
+
+
+/**
+ * gimp_layer_new:
+ * @image_ID: The image to which to add the layer.
+ * @name: The layer name.
+ * @width: The layer width.
+ * @height: The layer height.
+ * @type: The layer type.
+ * @opacity: The layer opacity.
+ * @mode: The layer combination mode.
+ *
+ * Create a new layer.
+ *
+ * This procedure creates a new layer with the specified width, height,
+ * and type. Name, opacity, and mode are also supplied parameters. The
+ * new layer still needs to be added to the image, as this is not
+ * automatic. Add the new layer with the gimp_image_insert_layer()
+ * command. Other attributes such as layer mask modes, and offsets
+ * should be set with explicit procedure calls.
+ *
+ * Returns: The newly created layer.
+ */
+gint32
+gimp_layer_new (gint32 image_ID,
+ const gchar *name,
+ gint width,
+ gint height,
+ GimpImageType type,
+ gdouble opacity,
+ GimpLayerMode mode)
+{
+ return _gimp_layer_new (image_ID,
+ width,
+ height,
+ type,
+ name,
+ opacity,
+ mode);
+}
+
+/**
+ * gimp_layer_copy:
+ * @layer_ID: The layer to copy.
+ *
+ * Copy a layer.
+ *
+ * This procedure copies the specified layer and returns the copy. The
+ * newly copied layer is for use within the original layer's image. It
+ * should not be subsequently added to any other image.
+ *
+ * Returns: The newly copied layer.
+ */
+gint32
+gimp_layer_copy (gint32 layer_ID)
+{
+ return _gimp_layer_copy (layer_ID, FALSE);
+}
+
+/**
+ * gimp_layer_new_from_pixbuf:
+ * @image_ID: The RGB image to which to add the layer.
+ * @name: The layer name.
+ * @pixbuf: A GdkPixbuf.
+ * @opacity: The layer opacity.
+ * @mode: The layer combination mode.
+ * @progress_start: start of progress
+ * @progress_end: end of progress
+ *
+ * Create a new layer from a %GdkPixbuf.
+ *
+ * This procedure creates a new layer from the given %GdkPixbuf. The
+ * image has to be an RGB image and just like with gimp_layer_new()
+ * you will still need to add the layer to it.
+ *
+ * If you pass @progress_end > @progress_start to this function,
+ * gimp_progress_update() will be called for. You have to call
+ * gimp_progress_init() beforehand then.
+ *
+ * Returns: The newly created layer.
+ *
+ * Since: 2.4
+ */
+gint32
+gimp_layer_new_from_pixbuf (gint32 image_ID,
+ const gchar *name,
+ GdkPixbuf *pixbuf,
+ gdouble opacity,
+ GimpLayerMode mode,
+ gdouble progress_start,
+ gdouble progress_end)
+{
+ gint32 layer;
+ gint width;
+ gint height;
+ gint bpp;
+ gdouble range = progress_end - progress_start;
+
+ g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), -1);
+
+ if (gimp_image_base_type (image_ID) != GIMP_RGB)
+ {
+ g_warning ("gimp_layer_new_from_pixbuf() needs an RGB image");
+ return -1;
+ }
+
+ if (gdk_pixbuf_get_colorspace (pixbuf) != GDK_COLORSPACE_RGB)
+ {
+ g_warning ("gimp_layer_new_from_pixbuf() assumes that GdkPixbuf is RGB");
+ return -1;
+ }
+
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+ bpp = gdk_pixbuf_get_n_channels (pixbuf);
+
+ layer = gimp_layer_new (image_ID, name, width, height,
+ bpp == 3 ? GIMP_RGB_IMAGE : GIMP_RGBA_IMAGE,
+ opacity, mode);
+
+ if (layer == -1)
+ return -1;
+
+ if (gimp_plugin_precision_enabled ())
+ {
+ GeglBuffer *dest_buffer;
+
+ dest_buffer = gimp_drawable_get_buffer (layer);
+
+ gegl_buffer_set (dest_buffer, GEGL_RECTANGLE (0, 0, width, height), 0,
+ gimp_pixbuf_get_format (pixbuf),
+ gdk_pixbuf_get_pixels (pixbuf),
+ gdk_pixbuf_get_rowstride (pixbuf));
+
+ g_object_unref (dest_buffer);
+ }
+ else
+ {
+ GimpDrawable *drawable;
+ GimpPixelRgn rgn;
+ gpointer pr;
+ const guchar *pixels;
+ gint rowstride;
+ guint done = 0;
+ guint count = 0;
+
+ drawable = gimp_drawable_get (layer);
+
+ gimp_pixel_rgn_init (&rgn, drawable, 0, 0, width, height, TRUE, FALSE);
+
+ g_assert (bpp == rgn.bpp);
+
+ rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+ pixels = gdk_pixbuf_get_pixels (pixbuf);
+
+ for (pr = gimp_pixel_rgns_register (1, &rgn);
+ pr != NULL;
+ pr = gimp_pixel_rgns_process (pr))
+ {
+ const guchar *src = pixels + rgn.y * rowstride + rgn.x * bpp;
+ guchar *dest = rgn.data;
+ gint y;
+
+ for (y = 0; y < rgn.h; y++)
+ {
+ memcpy (dest, src, rgn.w * rgn.bpp);
+
+ src += rowstride;
+ dest += rgn.rowstride;
+ }
+
+ if (range > 0.0)
+ {
+ done += rgn.h * rgn.w;
+
+ if (count++ % 32 == 0)
+ gimp_progress_update (progress_start +
+ (gdouble) done / (width * height) * range);
+ }
+ }
+
+ gimp_drawable_detach (drawable);
+ }
+
+ if (range > 0.0)
+ gimp_progress_update (progress_end);
+
+ return layer;
+}
+
+/**
+ * gimp_layer_new_from_surface:
+ * @image_ID: The RGB image to which to add the layer.
+ * @name: The layer name.
+ * @surface: A Cairo image surface.
+ * @progress_start: start of progress
+ * @progress_end: end of progress
+ *
+ * Create a new layer from a #cairo_surface_t.
+ *
+ * This procedure creates a new layer from the given
+ * #cairo_surface_t. The image has to be an RGB image and just like
+ * with gimp_layer_new() you will still need to add the layer to it.
+ *
+ * If you pass @progress_end > @progress_start to this function,
+ * gimp_progress_update() will be called for. You have to call
+ * gimp_progress_init() beforehand then.
+ *
+ * Returns: The newly created layer.
+ *
+ * Since: 2.8
+ */
+gint32
+gimp_layer_new_from_surface (gint32 image_ID,
+ const gchar *name,
+ cairo_surface_t *surface,
+ gdouble progress_start,
+ gdouble progress_end)
+{
+ gint32 layer;
+ gint width;
+ gint height;
+ cairo_format_t format;
+ gdouble range = progress_end - progress_start;
+
+ g_return_val_if_fail (surface != NULL, -1);
+ g_return_val_if_fail (cairo_surface_get_type (surface) ==
+ CAIRO_SURFACE_TYPE_IMAGE, -1);
+
+ if (gimp_image_base_type (image_ID) != GIMP_RGB)
+ {
+ g_warning ("gimp_layer_new_from_surface() needs an RGB image");
+ return -1;
+ }
+
+ width = cairo_image_surface_get_width (surface);
+ height = cairo_image_surface_get_height (surface);
+ format = cairo_image_surface_get_format (surface);
+
+ if (format != CAIRO_FORMAT_ARGB32 &&
+ format != CAIRO_FORMAT_RGB24)
+ {
+ g_warning ("gimp_layer_new_from_surface() assumes that surface is RGB");
+ return -1;
+ }
+
+ layer = gimp_layer_new (image_ID, name, width, height,
+ format == CAIRO_FORMAT_RGB24 ?
+ GIMP_RGB_IMAGE : GIMP_RGBA_IMAGE,
+ 100.0,
+ gimp_image_get_default_new_layer_mode (image_ID));
+
+ if (layer == -1)
+ return -1;
+
+ if (gimp_plugin_precision_enabled ())
+ {
+ GeglBuffer *src_buffer;
+ GeglBuffer *dest_buffer;
+
+ src_buffer = gimp_cairo_surface_create_buffer (surface);
+ dest_buffer = gimp_drawable_get_buffer (layer);
+
+ gegl_buffer_copy (src_buffer, NULL, GEGL_ABYSS_NONE,
+ dest_buffer, NULL);
+
+ g_object_unref (src_buffer);
+ g_object_unref (dest_buffer);
+ }
+ else
+ {
+ GimpDrawable *drawable;
+ GimpPixelRgn rgn;
+ const guchar *pixels;
+ gpointer pr;
+ gsize rowstride;
+ guint count = 0;
+ guint done = 0;
+
+ drawable = gimp_drawable_get (layer);
+
+ gimp_pixel_rgn_init (&rgn, drawable, 0, 0, width, height, TRUE, FALSE);
+
+ rowstride = cairo_image_surface_get_stride (surface);
+ pixels = cairo_image_surface_get_data (surface);
+
+ for (pr = gimp_pixel_rgns_register (1, &rgn);
+ pr != NULL;
+ pr = gimp_pixel_rgns_process (pr))
+ {
+ const guchar *src = pixels + rgn.y * rowstride + rgn.x * 4;
+ guchar *dest = rgn.data;
+ gint y;
+
+ switch (format)
+ {
+ case CAIRO_FORMAT_RGB24:
+ for (y = 0; y < rgn.h; y++)
+ {
+ const guchar *s = src;
+ guchar *d = dest;
+ gint w = rgn.w;
+
+ while (w--)
+ {
+ GIMP_CAIRO_RGB24_GET_PIXEL (s, d[0], d[1], d[2]);
+
+ s += 4;
+ d += 3;
+ }
+
+ src += rowstride;
+ dest += rgn.rowstride;
+ }
+ break;
+
+ case CAIRO_FORMAT_ARGB32:
+ for (y = 0; y < rgn.h; y++)
+ {
+ const guchar *s = src;
+ guchar *d = dest;
+ gint w = rgn.w;
+
+ while (w--)
+ {
+ GIMP_CAIRO_ARGB32_GET_PIXEL (s, d[0], d[1], d[2], d[3]);
+
+ s += 4;
+ d += 4;
+ }
+
+ src += rowstride;
+ dest += rgn.rowstride;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (range > 0.0)
+ {
+ done += rgn.h * rgn.w;
+
+ if (count++ % 32 == 0)
+ gimp_progress_update (progress_start +
+ (gdouble) done / (width * height) * range);
+ }
+ }
+
+ gimp_drawable_detach (drawable);
+ }
+
+ if (range > 0.0)
+ gimp_progress_update (progress_end);
+
+ return layer;
+}
+
+/**
+ * gimp_layer_get_preserve_trans:
+ * @layer_ID: The layer.
+ *
+ * This procedure is deprecated! Use gimp_layer_get_lock_alpha() instead.
+ *
+ * Returns: The layer's preserve transperancy setting.
+ */
+gboolean
+gimp_layer_get_preserve_trans (gint32 layer_ID)
+{
+ return gimp_layer_get_lock_alpha (layer_ID);
+}
+
+/**
+ * gimp_layer_set_preserve_trans:
+ * @layer_ID: The layer.
+ * @preserve_trans: The new layer's preserve transperancy setting.
+ *
+ * This procedure is deprecated! Use gimp_layer_set_lock_alpha() instead.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+gimp_layer_set_preserve_trans (gint32 layer_ID,
+ gboolean preserve_trans)
+{
+ return gimp_layer_set_lock_alpha (layer_ID, preserve_trans);
+}
diff --git a/libgimp/gimplayer.h b/libgimp/gimplayer.h
new file mode 100644
index 0000000..f1d006c
--- /dev/null
+++ b/libgimp/gimplayer.h
@@ -0,0 +1,63 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2000 Peter Mattis and Spencer Kimball
+ *
+ * gimplayer.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_LAYER_H__
+#define __GIMP_LAYER_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gint32 gimp_layer_new (gint32 image_ID,
+ const gchar *name,
+ gint width,
+ gint height,
+ GimpImageType type,
+ gdouble opacity,
+ GimpLayerMode mode);
+gint32 gimp_layer_copy (gint32 layer_ID);
+
+gint32 gimp_layer_new_from_pixbuf (gint32 image_ID,
+ const gchar *name,
+ GdkPixbuf *pixbuf,
+ gdouble opacity,
+ GimpLayerMode mode,
+ gdouble progress_start,
+ gdouble progress_end);
+gint32 gimp_layer_new_from_surface (gint32 image_ID,
+ const gchar *name,
+ cairo_surface_t *surface,
+ gdouble progress_start,
+ gdouble progress_end);
+
+GIMP_DEPRECATED_FOR(gimp_layer_get_lock_alpha)
+gboolean gimp_layer_get_preserve_trans (gint32 layer_ID);
+GIMP_DEPRECATED_FOR(gimp_layer_set_lock_alpha)
+gboolean gimp_layer_set_preserve_trans (gint32 layer_ID,
+ gboolean preserve_trans);
+
+G_END_DECLS
+
+#endif /* __GIMP_LAYER_H__ */
diff --git a/libgimp/gimplayer_pdb.c b/libgimp/gimplayer_pdb.c
new file mode 100644
index 0000000..7cd2d82
--- /dev/null
+++ b/libgimp/gimplayer_pdb.c
@@ -0,0 +1,1354 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimplayer_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimplayer
+ * @title: gimplayer
+ * @short_description: Operations on a single layer.
+ *
+ * Operations on a single layer.
+ **/
+
+
+/**
+ * _gimp_layer_new:
+ * @image_ID: The image to which to add the layer.
+ * @width: The layer width.
+ * @height: The layer height.
+ * @type: The layer type.
+ * @name: The layer name.
+ * @opacity: The layer opacity.
+ * @mode: The layer combination mode.
+ *
+ * Create a new layer.
+ *
+ * This procedure creates a new layer with the specified width, height,
+ * and type. Name, opacity, and mode are also supplied parameters. The
+ * new layer still needs to be added to the image, as this is not
+ * automatic. Add the new layer with the gimp_image_insert_layer()
+ * command. Other attributes such as layer mask modes, and offsets
+ * should be set with explicit procedure calls.
+ *
+ * Returns: The newly created layer.
+ **/
+gint32
+_gimp_layer_new (gint32 image_ID,
+ gint width,
+ gint height,
+ GimpImageType type,
+ const gchar *name,
+ gdouble opacity,
+ GimpLayerMode mode)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 layer_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-layer-new",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, width,
+ GIMP_PDB_INT32, height,
+ GIMP_PDB_INT32, type,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_FLOAT, opacity,
+ GIMP_PDB_INT32, mode,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ layer_ID = return_vals[1].data.d_layer;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return layer_ID;
+}
+
+/**
+ * gimp_layer_new_from_visible:
+ * @image_ID: The source image from where the content is copied.
+ * @dest_image_ID: The destination image to which to add the layer.
+ * @name: The layer name.
+ *
+ * Create a new layer from what is visible in an image.
+ *
+ * This procedure creates a new layer from what is visible in the given
+ * image. The new layer still needs to be added to the destination
+ * image, as this is not automatic. Add the new layer with the
+ * gimp_image_insert_layer() command. Other attributes such as layer
+ * mask modes, and offsets should be set with explicit procedure calls.
+ *
+ * Returns: The newly created layer.
+ *
+ * Since: 2.6
+ **/
+gint32
+gimp_layer_new_from_visible (gint32 image_ID,
+ gint32 dest_image_ID,
+ const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 layer_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-layer-new-from-visible",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_IMAGE, dest_image_ID,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ layer_ID = return_vals[1].data.d_layer;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return layer_ID;
+}
+
+/**
+ * gimp_layer_new_from_drawable:
+ * @drawable_ID: The source drawable from where the new layer is copied.
+ * @dest_image_ID: The destination image to which to add the layer.
+ *
+ * Create a new layer by copying an existing drawable.
+ *
+ * This procedure creates a new layer as a copy of the specified
+ * drawable. The new layer still needs to be added to the image, as
+ * this is not automatic. Add the new layer with the
+ * gimp_image_insert_layer() command. Other attributes such as layer
+ * mask modes, and offsets should be set with explicit procedure calls.
+ *
+ * Returns: The newly copied layer.
+ **/
+gint32
+gimp_layer_new_from_drawable (gint32 drawable_ID,
+ gint32 dest_image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 layer_copy_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-layer-new-from-drawable",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_IMAGE, dest_image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ layer_copy_ID = return_vals[1].data.d_layer;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return layer_copy_ID;
+}
+
+/**
+ * gimp_layer_group_new:
+ * @image_ID: The image to which to add the layer group.
+ *
+ * Create a new layer group.
+ *
+ * This procedure creates a new layer group. Attributes such as layer
+ * mode and opacity should be set with explicit procedure calls. Add
+ * the new layer group (which is a kind of layer) with the
+ * gimp_image_insert_layer() command.
+ * Other procedures useful with layer groups:
+ * gimp_image_reorder_item(), gimp_item_get_parent(),
+ * gimp_item_get_children(), gimp_item_is_group().
+ *
+ * Returns: The newly created layer group.
+ *
+ * Since: 2.8
+ **/
+gint32
+gimp_layer_group_new (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 layer_group_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-layer-group-new",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ layer_group_ID = return_vals[1].data.d_layer;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return layer_group_ID;
+}
+
+/**
+ * _gimp_layer_copy:
+ * @layer_ID: The layer to copy.
+ * @add_alpha: Add an alpha channel to the copied layer.
+ *
+ * Copy a layer.
+ *
+ * This procedure copies the specified layer and returns the copy. The
+ * newly copied layer is for use within the original layer's image. It
+ * should not be subsequently added to any other image. The copied
+ * layer can optionally have an added alpha channel. This is useful if
+ * the background layer in an image is being copied and added to the
+ * same image.
+ *
+ * Returns: The newly copied layer.
+ **/
+gint32
+_gimp_layer_copy (gint32 layer_ID,
+ gboolean add_alpha)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 layer_copy_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-layer-copy",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_INT32, add_alpha,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ layer_copy_ID = return_vals[1].data.d_layer;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return layer_copy_ID;
+}
+
+/**
+ * gimp_layer_add_alpha:
+ * @layer_ID: The layer.
+ *
+ * Add an alpha channel to the layer if it doesn't already have one.
+ *
+ * This procedure adds an additional component to the specified layer
+ * if it does not already possess an alpha channel. An alpha channel
+ * makes it possible to clear and erase to transparency, instead of the
+ * background color. This transforms layers of type RGB to RGBA, GRAY
+ * to GRAYA, and INDEXED to INDEXEDA.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_layer_add_alpha (gint32 layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-layer-add-alpha",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_layer_flatten:
+ * @layer_ID: The layer.
+ *
+ * Remove the alpha channel from the layer if it has one.
+ *
+ * This procedure removes the alpha channel from a layer, blending all
+ * (partially) transparent pixels in the layer against the background
+ * color. This transforms layers of type RGBA to RGB, GRAYA to GRAY,
+ * and INDEXEDA to INDEXED.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_layer_flatten (gint32 layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-layer-flatten",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_layer_scale:
+ * @layer_ID: The layer.
+ * @new_width: New layer width.
+ * @new_height: New layer height.
+ * @local_origin: Use a local origin (as opposed to the image origin).
+ *
+ * Scale the layer using the default interpolation method.
+ *
+ * This procedure scales the layer so that its new width and height are
+ * equal to the supplied parameters. The 'local-origin' parameter
+ * specifies whether to scale from the center of the layer, or from the
+ * image origin. This operation only works if the layer has been added
+ * to an image. The interpolation method used can be set with
+ * gimp_context_set_interpolation().
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_layer_scale (gint32 layer_ID,
+ gint new_width,
+ gint new_height,
+ gboolean local_origin)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-layer-scale",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_INT32, new_width,
+ GIMP_PDB_INT32, new_height,
+ GIMP_PDB_INT32, local_origin,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_layer_scale_full:
+ * @layer_ID: The layer.
+ * @new_width: New layer width.
+ * @new_height: New layer height.
+ * @local_origin: Use a local origin (as opposed to the image origin).
+ * @interpolation: Type of interpolation.
+ *
+ * Deprecated: Use gimp_layer_scale() instead.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.6
+ **/
+gboolean
+gimp_layer_scale_full (gint32 layer_ID,
+ gint new_width,
+ gint new_height,
+ gboolean local_origin,
+ GimpInterpolationType interpolation)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-layer-scale-full",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_INT32, new_width,
+ GIMP_PDB_INT32, new_height,
+ GIMP_PDB_INT32, local_origin,
+ GIMP_PDB_INT32, interpolation,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_layer_resize:
+ * @layer_ID: The layer.
+ * @new_width: New layer width.
+ * @new_height: New layer height.
+ * @offx: x offset between upper left corner of old and new layers: (old - new).
+ * @offy: y offset between upper left corner of old and new layers: (old - new).
+ *
+ * Resize the layer to the specified extents.
+ *
+ * This procedure resizes the layer so that its new width and height
+ * are equal to the supplied parameters. Offsets are also provided
+ * which describe the position of the previous layer's content. This
+ * operation only works if the layer has been added to an image.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_layer_resize (gint32 layer_ID,
+ gint new_width,
+ gint new_height,
+ gint offx,
+ gint offy)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-layer-resize",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_INT32, new_width,
+ GIMP_PDB_INT32, new_height,
+ GIMP_PDB_INT32, offx,
+ GIMP_PDB_INT32, offy,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_layer_resize_to_image_size:
+ * @layer_ID: The layer to resize.
+ *
+ * Resize a layer to the image size.
+ *
+ * This procedure resizes the layer so that it's new width and height
+ * are equal to the width and height of its image container.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_layer_resize_to_image_size (gint32 layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-layer-resize-to-image-size",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_layer_translate:
+ * @layer_ID: The layer.
+ * @offx: Offset in x direction.
+ * @offy: Offset in y direction.
+ *
+ * Translate the layer by the specified offsets.
+ *
+ * This procedure translates the layer by the amounts specified in the
+ * x and y arguments. These can be negative, and are considered offsets
+ * from the current position. This command only works if the layer has
+ * been added to an image. All additional layers contained in the image
+ * which have the linked flag set to TRUE w ill also be translated by
+ * the specified offsets.
+ *
+ * Deprecated: Use gimp_item_transform_translate() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_layer_translate (gint32 layer_ID,
+ gint offx,
+ gint offy)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-layer-translate",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_INT32, offx,
+ GIMP_PDB_INT32, offy,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_layer_set_offsets:
+ * @layer_ID: The layer.
+ * @offx: Offset in x direction.
+ * @offy: Offset in y direction.
+ *
+ * Set the layer offsets.
+ *
+ * This procedure sets the offsets for the specified layer. The offsets
+ * are relative to the image origin and can be any values. This
+ * operation is valid only on layers which have been added to an image.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_layer_set_offsets (gint32 layer_ID,
+ gint offx,
+ gint offy)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-layer-set-offsets",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_INT32, offx,
+ GIMP_PDB_INT32, offy,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_layer_create_mask:
+ * @layer_ID: The layer to which to add the mask.
+ * @mask_type: The type of mask.
+ *
+ * Create a layer mask for the specified layer.
+ *
+ * This procedure creates a layer mask for the specified layer.
+ * Layer masks serve as an additional alpha channel for a layer.
+ * Different types of masks are allowed for initialisation:
+ * - white mask (leaves the layer fully visible);
+ * - black mask (gives the layer complete transparency);
+ * - the layer's alpha channel (either a copy, or a transfer, which
+ * leaves the layer fully visible, but which may be more useful than a
+ * white mask);
+ * - the current selection;
+ * - a grayscale copy of the layer;
+ * - or a copy of the active channel.
+ *
+ * The layer mask still needs to be added to the layer. This can be
+ * done with a call to gimp_layer_add_mask().
+ *
+ * gimp_layer_create_mask() will fail if there are no active channels
+ * on the image, when called with 'ADD-CHANNEL-MASK'. It will return a
+ * black mask when called with 'ADD-ALPHA-MASK' or
+ * 'ADD-ALPHA-TRANSFER-MASK' on a layer with no alpha channels, or with
+ * 'ADD-SELECTION-MASK' when there is no selection on the image.
+ *
+ * Returns: The newly created mask.
+ **/
+gint32
+gimp_layer_create_mask (gint32 layer_ID,
+ GimpAddMaskType mask_type)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 mask_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-layer-create-mask",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_INT32, mask_type,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ mask_ID = return_vals[1].data.d_layer_mask;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return mask_ID;
+}
+
+/**
+ * gimp_layer_get_mask:
+ * @layer_ID: The layer.
+ *
+ * Get the specified layer's mask if it exists.
+ *
+ * This procedure returns the specified layer's mask, or -1 if none
+ * exists.
+ *
+ * Returns: The layer mask.
+ **/
+gint32
+gimp_layer_get_mask (gint32 layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 mask_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-layer-get-mask",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ mask_ID = return_vals[1].data.d_layer_mask;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return mask_ID;
+}
+
+/**
+ * gimp_layer_from_mask:
+ * @mask_ID: Mask for which to return the layer.
+ *
+ * Get the specified mask's layer.
+ *
+ * This procedure returns the specified mask's layer , or -1 if none
+ * exists.
+ *
+ * Returns: The mask's layer.
+ *
+ * Since: 2.2
+ **/
+gint32
+gimp_layer_from_mask (gint32 mask_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 layer_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-layer-from-mask",
+ &nreturn_vals,
+ GIMP_PDB_CHANNEL, mask_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ layer_ID = return_vals[1].data.d_layer;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return layer_ID;
+}
+
+/**
+ * gimp_layer_add_mask:
+ * @layer_ID: The layer to receive the mask.
+ * @mask_ID: The mask to add to the layer.
+ *
+ * Add a layer mask to the specified layer.
+ *
+ * This procedure adds a layer mask to the specified layer. Layer masks
+ * serve as an additional alpha channel for a layer. This procedure
+ * will fail if a number of prerequisites aren't met. The layer cannot
+ * already have a layer mask. The specified mask must exist and have
+ * the same dimensions as the layer. The layer must have been created
+ * for use with the specified image and the mask must have been created
+ * with the procedure 'gimp-layer-create-mask'.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_layer_add_mask (gint32 layer_ID,
+ gint32 mask_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-layer-add-mask",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_CHANNEL, mask_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_layer_remove_mask:
+ * @layer_ID: The layer from which to remove mask.
+ * @mode: Removal mode.
+ *
+ * Remove the specified layer mask from the layer.
+ *
+ * This procedure removes the specified layer mask from the layer. If
+ * the mask doesn't exist, an error is returned.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_layer_remove_mask (gint32 layer_ID,
+ GimpMaskApplyMode mode)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-layer-remove-mask",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_INT32, mode,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_layer_is_floating_sel:
+ * @layer_ID: The layer.
+ *
+ * Is the specified layer a floating selection?
+ *
+ * This procedure returns whether the layer is a floating selection.
+ * Floating selections are special cases of layers which are attached
+ * to a specific drawable.
+ *
+ * Returns: TRUE if the layer is a floating selection.
+ **/
+gboolean
+gimp_layer_is_floating_sel (gint32 layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean is_floating_sel = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-layer-is-floating-sel",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ is_floating_sel = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return is_floating_sel;
+}
+
+/**
+ * gimp_layer_get_lock_alpha:
+ * @layer_ID: The layer.
+ *
+ * Get the lock alpha channel setting of the specified layer.
+ *
+ * This procedure returns the specified layer's lock alpha channel
+ * setting.
+ *
+ * Returns: The layer's lock alpha channel setting.
+ **/
+gboolean
+gimp_layer_get_lock_alpha (gint32 layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean lock_alpha = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-layer-get-lock-alpha",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ lock_alpha = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return lock_alpha;
+}
+
+/**
+ * gimp_layer_set_lock_alpha:
+ * @layer_ID: The layer.
+ * @lock_alpha: The new layer's lock alpha channel setting.
+ *
+ * Set the lock alpha channel setting of the specified layer.
+ *
+ * This procedure sets the specified layer's lock alpha channel
+ * setting.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_layer_set_lock_alpha (gint32 layer_ID,
+ gboolean lock_alpha)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-layer-set-lock-alpha",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_INT32, lock_alpha,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_layer_get_apply_mask:
+ * @layer_ID: The layer.
+ *
+ * Get the apply mask setting of the specified layer.
+ *
+ * This procedure returns the specified layer's apply mask setting. If
+ * the value is TRUE, then the layer mask for this layer is currently
+ * being composited with the layer's alpha channel.
+ *
+ * Returns: The layer's apply mask setting.
+ **/
+gboolean
+gimp_layer_get_apply_mask (gint32 layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean apply_mask = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-layer-get-apply-mask",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ apply_mask = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return apply_mask;
+}
+
+/**
+ * gimp_layer_set_apply_mask:
+ * @layer_ID: The layer.
+ * @apply_mask: The new layer's apply mask setting.
+ *
+ * Set the apply mask setting of the specified layer.
+ *
+ * This procedure sets the specified layer's apply mask setting. This
+ * controls whether the layer's mask is currently affecting the alpha
+ * channel. If there is no layer mask, this function will return an
+ * error.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_layer_set_apply_mask (gint32 layer_ID,
+ gboolean apply_mask)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-layer-set-apply-mask",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_INT32, apply_mask,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_layer_get_show_mask:
+ * @layer_ID: The layer.
+ *
+ * Get the show mask setting of the specified layer.
+ *
+ * This procedure returns the specified layer's show mask setting. This
+ * controls whether the layer or its mask is visible. TRUE indicates
+ * that the mask should be visible. If the layer has no mask, then this
+ * function returns an error.
+ *
+ * Returns: The layer's show mask setting.
+ **/
+gboolean
+gimp_layer_get_show_mask (gint32 layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean show_mask = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-layer-get-show-mask",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ show_mask = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return show_mask;
+}
+
+/**
+ * gimp_layer_set_show_mask:
+ * @layer_ID: The layer.
+ * @show_mask: The new layer's show mask setting.
+ *
+ * Set the show mask setting of the specified layer.
+ *
+ * This procedure sets the specified layer's show mask setting. This
+ * controls whether the layer or its mask is visible. TRUE indicates
+ * that the mask should be visible. If there is no layer mask, this
+ * function will return an error.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_layer_set_show_mask (gint32 layer_ID,
+ gboolean show_mask)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-layer-set-show-mask",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_INT32, show_mask,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_layer_get_edit_mask:
+ * @layer_ID: The layer.
+ *
+ * Get the edit mask setting of the specified layer.
+ *
+ * This procedure returns the specified layer's edit mask setting. If
+ * the value is TRUE, then the layer mask for this layer is currently
+ * active, and not the layer.
+ *
+ * Returns: The layer's edit mask setting.
+ **/
+gboolean
+gimp_layer_get_edit_mask (gint32 layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean edit_mask = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-layer-get-edit-mask",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ edit_mask = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return edit_mask;
+}
+
+/**
+ * gimp_layer_set_edit_mask:
+ * @layer_ID: The layer.
+ * @edit_mask: The new layer's edit mask setting.
+ *
+ * Set the edit mask setting of the specified layer.
+ *
+ * This procedure sets the specified layer's edit mask setting. This
+ * controls whether the layer or it's mask is currently active for
+ * editing. If the specified layer has no layer mask, then this
+ * procedure will return an error.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_layer_set_edit_mask (gint32 layer_ID,
+ gboolean edit_mask)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-layer-set-edit-mask",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_INT32, edit_mask,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_layer_get_opacity:
+ * @layer_ID: The layer.
+ *
+ * Get the opacity of the specified layer.
+ *
+ * This procedure returns the specified layer's opacity.
+ *
+ * Returns: The layer opacity.
+ **/
+gdouble
+gimp_layer_get_opacity (gint32 layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble opacity = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-layer-get-opacity",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ opacity = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return opacity;
+}
+
+/**
+ * gimp_layer_set_opacity:
+ * @layer_ID: The layer.
+ * @opacity: The new layer opacity.
+ *
+ * Set the opacity of the specified layer.
+ *
+ * This procedure sets the specified layer's opacity.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_layer_set_opacity (gint32 layer_ID,
+ gdouble opacity)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-layer-set-opacity",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_FLOAT, opacity,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_layer_get_mode:
+ * @layer_ID: The layer.
+ *
+ * Get the combination mode of the specified layer.
+ *
+ * This procedure returns the specified layer's combination mode.
+ *
+ * Returns: The layer combination mode.
+ **/
+GimpLayerMode
+gimp_layer_get_mode (gint32 layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpLayerMode mode = 0;
+
+ return_vals = gimp_run_procedure ("gimp-layer-get-mode",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ mode = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return mode;
+}
+
+/**
+ * gimp_layer_set_mode:
+ * @layer_ID: The layer.
+ * @mode: The new layer combination mode.
+ *
+ * Set the combination mode of the specified layer.
+ *
+ * This procedure sets the specified layer's combination mode.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_layer_set_mode (gint32 layer_ID,
+ GimpLayerMode mode)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-layer-set-mode",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_INT32, mode,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_layer_get_blend_space:
+ * @layer_ID: The layer.
+ *
+ * Get the blend space of the specified layer.
+ *
+ * This procedure returns the specified layer's blend space.
+ *
+ * Returns: The layer blend space.
+ *
+ * Since: 2.10
+ **/
+GimpLayerColorSpace
+gimp_layer_get_blend_space (gint32 layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpLayerColorSpace blend_space = 0;
+
+ return_vals = gimp_run_procedure ("gimp-layer-get-blend-space",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ blend_space = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return blend_space;
+}
+
+/**
+ * gimp_layer_set_blend_space:
+ * @layer_ID: The layer.
+ * @blend_space: The new layer blend space.
+ *
+ * Set the blend space of the specified layer.
+ *
+ * This procedure sets the specified layer's blend space.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_layer_set_blend_space (gint32 layer_ID,
+ GimpLayerColorSpace blend_space)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-layer-set-blend-space",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_INT32, blend_space,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_layer_get_composite_space:
+ * @layer_ID: The layer.
+ *
+ * Get the composite space of the specified layer.
+ *
+ * This procedure returns the specified layer's composite space.
+ *
+ * Returns: The layer composite space.
+ *
+ * Since: 2.10
+ **/
+GimpLayerColorSpace
+gimp_layer_get_composite_space (gint32 layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpLayerColorSpace composite_space = 0;
+
+ return_vals = gimp_run_procedure ("gimp-layer-get-composite-space",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ composite_space = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return composite_space;
+}
+
+/**
+ * gimp_layer_set_composite_space:
+ * @layer_ID: The layer.
+ * @composite_space: The new layer composite space.
+ *
+ * Set the composite space of the specified layer.
+ *
+ * This procedure sets the specified layer's composite space.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_layer_set_composite_space (gint32 layer_ID,
+ GimpLayerColorSpace composite_space)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-layer-set-composite-space",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_INT32, composite_space,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_layer_get_composite_mode:
+ * @layer_ID: The layer.
+ *
+ * Get the composite mode of the specified layer.
+ *
+ * This procedure returns the specified layer's composite mode.
+ *
+ * Returns: The layer composite mode.
+ *
+ * Since: 2.10
+ **/
+GimpLayerCompositeMode
+gimp_layer_get_composite_mode (gint32 layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpLayerCompositeMode composite_mode = 0;
+
+ return_vals = gimp_run_procedure ("gimp-layer-get-composite-mode",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ composite_mode = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return composite_mode;
+}
+
+/**
+ * gimp_layer_set_composite_mode:
+ * @layer_ID: The layer.
+ * @composite_mode: The new layer composite mode.
+ *
+ * Set the composite mode of the specified layer.
+ *
+ * This procedure sets the specified layer's composite mode.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_layer_set_composite_mode (gint32 layer_ID,
+ GimpLayerCompositeMode composite_mode)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-layer-set-composite-mode",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_INT32, composite_mode,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimplayer_pdb.h b/libgimp/gimplayer_pdb.h
new file mode 100644
index 0000000..79e34ad
--- /dev/null
+++ b/libgimp/gimplayer_pdb.h
@@ -0,0 +1,115 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimplayer_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_LAYER_PDB_H__
+#define __GIMP_LAYER_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+G_GNUC_INTERNAL gint32 _gimp_layer_new (gint32 image_ID,
+ gint width,
+ gint height,
+ GimpImageType type,
+ const gchar *name,
+ gdouble opacity,
+ GimpLayerMode mode);
+gint32 gimp_layer_new_from_visible (gint32 image_ID,
+ gint32 dest_image_ID,
+ const gchar *name);
+gint32 gimp_layer_new_from_drawable (gint32 drawable_ID,
+ gint32 dest_image_ID);
+gint32 gimp_layer_group_new (gint32 image_ID);
+G_GNUC_INTERNAL gint32 _gimp_layer_copy (gint32 layer_ID,
+ gboolean add_alpha);
+gboolean gimp_layer_add_alpha (gint32 layer_ID);
+gboolean gimp_layer_flatten (gint32 layer_ID);
+gboolean gimp_layer_scale (gint32 layer_ID,
+ gint new_width,
+ gint new_height,
+ gboolean local_origin);
+GIMP_DEPRECATED_FOR(gimp_layer_scale)
+gboolean gimp_layer_scale_full (gint32 layer_ID,
+ gint new_width,
+ gint new_height,
+ gboolean local_origin,
+ GimpInterpolationType interpolation);
+gboolean gimp_layer_resize (gint32 layer_ID,
+ gint new_width,
+ gint new_height,
+ gint offx,
+ gint offy);
+gboolean gimp_layer_resize_to_image_size (gint32 layer_ID);
+GIMP_DEPRECATED_FOR(gimp_item_transform_translate)
+gboolean gimp_layer_translate (gint32 layer_ID,
+ gint offx,
+ gint offy);
+gboolean gimp_layer_set_offsets (gint32 layer_ID,
+ gint offx,
+ gint offy);
+gint32 gimp_layer_create_mask (gint32 layer_ID,
+ GimpAddMaskType mask_type);
+gint32 gimp_layer_get_mask (gint32 layer_ID);
+gint32 gimp_layer_from_mask (gint32 mask_ID);
+gboolean gimp_layer_add_mask (gint32 layer_ID,
+ gint32 mask_ID);
+gboolean gimp_layer_remove_mask (gint32 layer_ID,
+ GimpMaskApplyMode mode);
+gboolean gimp_layer_is_floating_sel (gint32 layer_ID);
+gboolean gimp_layer_get_lock_alpha (gint32 layer_ID);
+gboolean gimp_layer_set_lock_alpha (gint32 layer_ID,
+ gboolean lock_alpha);
+gboolean gimp_layer_get_apply_mask (gint32 layer_ID);
+gboolean gimp_layer_set_apply_mask (gint32 layer_ID,
+ gboolean apply_mask);
+gboolean gimp_layer_get_show_mask (gint32 layer_ID);
+gboolean gimp_layer_set_show_mask (gint32 layer_ID,
+ gboolean show_mask);
+gboolean gimp_layer_get_edit_mask (gint32 layer_ID);
+gboolean gimp_layer_set_edit_mask (gint32 layer_ID,
+ gboolean edit_mask);
+gdouble gimp_layer_get_opacity (gint32 layer_ID);
+gboolean gimp_layer_set_opacity (gint32 layer_ID,
+ gdouble opacity);
+GimpLayerMode gimp_layer_get_mode (gint32 layer_ID);
+gboolean gimp_layer_set_mode (gint32 layer_ID,
+ GimpLayerMode mode);
+GimpLayerColorSpace gimp_layer_get_blend_space (gint32 layer_ID);
+gboolean gimp_layer_set_blend_space (gint32 layer_ID,
+ GimpLayerColorSpace blend_space);
+GimpLayerColorSpace gimp_layer_get_composite_space (gint32 layer_ID);
+gboolean gimp_layer_set_composite_space (gint32 layer_ID,
+ GimpLayerColorSpace composite_space);
+GimpLayerCompositeMode gimp_layer_get_composite_mode (gint32 layer_ID);
+gboolean gimp_layer_set_composite_mode (gint32 layer_ID,
+ GimpLayerCompositeMode composite_mode);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_LAYER_PDB_H__ */
diff --git a/libgimp/gimpmenu.c b/libgimp/gimpmenu.c
new file mode 100644
index 0000000..7f788d5
--- /dev/null
+++ b/libgimp/gimpmenu.c
@@ -0,0 +1,509 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpmenu.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "gimp.h"
+
+#include "gimppixbuf.h"
+
+#undef GIMP_DISABLE_DEPRECATED
+#include "gimpmenu.h"
+
+#include "libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpmenu
+ * @title: gimpmenu
+ * @short_description: Menus for selecting images, layers, channels
+ * and drawables.
+ *
+ * Menus for selecting images, layers, channels and drawables.
+ **/
+
+
+#define MENU_THUMBNAIL_WIDTH 24
+#define MENU_THUMBNAIL_HEIGHT 24
+
+
+/* local function prototypes */
+
+static GtkWidget * gimp_menu_make_menu (GimpMenuCallback callback,
+ gpointer data);
+static GtkWidget * gimp_menu_add_item (GtkWidget *menu,
+ const gchar *image_name,
+ const gchar *drawable_name,
+ gint32 any_ID);
+static GtkWidget * gimp_menu_add_empty (GtkWidget *menu);
+static GtkWidget * gimp_menu_make_preview (gint32 any_ID,
+ gboolean is_image,
+ gint width,
+ gint height);
+static void gimp_menu_callback (GtkWidget *widget,
+ gpointer any_ID);
+
+
+/* public functions */
+
+/**
+ * gimp_image_menu_new:
+ * @constraint: a function to filter the menu contents
+ * @callback: the callback to call when an image is selected
+ * @data: the callback's user_data
+ * @active_image: an image to preselect
+ *
+ * Deprecated: Use gimp_image_combo_box_new() instead.
+ *
+ * Returns: the image menu.
+ */
+GtkWidget *
+gimp_image_menu_new (GimpConstraintFunc constraint,
+ GimpMenuCallback callback,
+ gpointer data,
+ gint32 active_image)
+{
+ GtkWidget *menu;
+ gchar *name;
+ gchar *label;
+ gint32 *images;
+ gint32 image = -1;
+ gint n_images;
+ gint i, k;
+
+ g_return_val_if_fail (callback != NULL, NULL);
+
+ menu = gimp_menu_make_menu (callback, data);
+
+ images = gimp_image_list (&n_images);
+
+ for (i = 0, k = 0; i < n_images; i++)
+ if (! constraint || (* constraint) (images[i], -1, data))
+ {
+ name = gimp_image_get_name (images[i]);
+ label = g_strdup_printf ("%s-%d", name, images[i]);
+ g_free (name);
+
+ gimp_menu_add_item (menu, label, NULL, images[i]);
+
+ g_free (label);
+
+ if (images[i] == active_image)
+ {
+ image = active_image;
+ gtk_menu_set_active (GTK_MENU (menu), k);
+ }
+ else if (image == -1)
+ {
+ image = images[i];
+ }
+
+ k += 1;
+ }
+
+ if (k == 0)
+ gimp_menu_add_empty (menu);
+
+ (* callback) (image, data);
+
+ g_free (images);
+
+ return menu;
+}
+
+/**
+ * gimp_layer_menu_new:
+ * @constraint: a function to filter the menu contents
+ * @callback: the callback to call when a channel is selected
+ * @data: the callback's user_data
+ * @active_layer: a layer to preselect
+ *
+ * Deprecated: Use gimp_layer_combo_box_new() instead.
+ *
+ * Returns: the layer menu.
+ */
+GtkWidget *
+gimp_layer_menu_new (GimpConstraintFunc constraint,
+ GimpMenuCallback callback,
+ gpointer data,
+ gint32 active_layer)
+{
+ GtkWidget *menu;
+ gchar *image_label;
+ gint32 *images;
+ gint32 *layers;
+ gint32 layer = -1;
+ gint n_images;
+ gint n_layers;
+ gint i, j, k;
+
+ g_return_val_if_fail (callback != NULL, NULL);
+
+ menu = gimp_menu_make_menu (callback, data);
+
+ images = gimp_image_list (&n_images);
+
+ for (i = 0, k = 0; i < n_images; i++)
+ if (! constraint || (* constraint) (images[i], -1, data))
+ {
+ gchar *name;
+
+ name = gimp_image_get_name (images[i]);
+ image_label = g_strdup_printf ("%s-%d", name, images[i]);
+ g_free (name);
+
+ layers = gimp_image_get_layers (images[i], &n_layers);
+
+ for (j = 0; j < n_layers; j++)
+ if (! constraint || (* constraint) (images[i], layers[j], data))
+ {
+ name = gimp_item_get_name (layers[j]);
+ gimp_menu_add_item (menu, image_label, name, layers[j]);
+ g_free (name);
+
+ if (layers[j] == active_layer)
+ {
+ layer = active_layer;
+ gtk_menu_set_active (GTK_MENU (menu), k);
+ }
+ else if (layer == -1)
+ {
+ layer = layers[j];
+ }
+
+ k += 1;
+ }
+
+ g_free (image_label);
+ g_free (layers);
+ }
+
+ g_free (images);
+
+ if (k == 0)
+ gimp_menu_add_empty (menu);
+
+ (* callback) (layer, data);
+
+ return menu;
+}
+
+/**
+ * gimp_channel_menu_new:
+ * @constraint: a function to filter the menu contents
+ * @callback: the callback to call when a channel is selected
+ * @data: the callback's user_data
+ * @active_channel: a channel to preselect
+ *
+ * Deprecated: Use gimp_channel_combo_box_new() instead.
+ *
+ * Returns: the channel menu.
+ */
+GtkWidget *
+gimp_channel_menu_new (GimpConstraintFunc constraint,
+ GimpMenuCallback callback,
+ gpointer data,
+ gint32 active_channel)
+{
+ GtkWidget *menu;
+ gchar *image_label;
+ gint32 *images;
+ gint32 *channels;
+ gint32 channel;
+ gint n_images;
+ gint n_channels;
+ gint i, j, k;
+
+ g_return_val_if_fail (callback != NULL, NULL);
+
+ menu = gimp_menu_make_menu (callback, data);
+
+ channel = -1;
+
+ images = gimp_image_list (&n_images);
+
+ for (i = 0, k = 0; i < n_images; i++)
+ if (! constraint || (* constraint) (images[i], -1, data))
+ {
+ gchar *name;
+
+ name = gimp_image_get_name (images[i]);
+ image_label = g_strdup_printf ("%s-%d", name, images[i]);
+ g_free (name);
+
+ channels = gimp_image_get_channels (images[i], &n_channels);
+
+ for (j = 0; j < n_channels; j++)
+ if (! constraint || (* constraint) (images[i], channels[j], data))
+ {
+ name = gimp_item_get_name (channels[j]);
+ gimp_menu_add_item (menu, image_label, name, channels[j]);
+ g_free (name);
+
+ if (channels[j] == active_channel)
+ {
+ channel = active_channel;
+ gtk_menu_set_active (GTK_MENU (menu), k);
+ }
+ else if (channel == -1)
+ {
+ channel = channels[j];
+ }
+
+ k += 1;
+ }
+
+ g_free (image_label);
+ g_free (channels);
+ }
+
+ g_free (images);
+
+ if (k == 0)
+ gimp_menu_add_empty (menu);
+
+ (* callback) (channel, data);
+
+ return menu;
+}
+
+/**
+ * gimp_drawable_menu_new:
+ * @constraint: a function to filter the menu contents
+ * @callback: the callback to call when a channel is selected
+ * @data: the callback's user_data
+ * @active_drawable: a drawable to preselect
+ *
+ * Deprecated: Use gimp_drawable_combo_box_new() instead.
+ *
+ * Returns: the drawable menu.
+ */
+GtkWidget *
+gimp_drawable_menu_new (GimpConstraintFunc constraint,
+ GimpMenuCallback callback,
+ gpointer data,
+ gint32 active_drawable)
+{
+ GtkWidget *menu;
+ gchar *name;
+ gchar *image_label;
+ gint32 *images;
+ gint32 *layers;
+ gint32 *channels;
+ gint32 drawable;
+ gint n_images;
+ gint n_layers;
+ gint n_channels;
+ gint i, j, k;
+
+ menu = gimp_menu_make_menu (callback, data);
+
+ drawable = -1;
+
+ images = gimp_image_list (&n_images);
+
+ for (i = 0, k = 0; i < n_images; i++)
+ if (! constraint || (* constraint) (images[i], -1, data))
+ {
+ name = gimp_image_get_name (images[i]);
+ image_label = g_strdup_printf ("%s-%d", name, images[i]);
+ g_free (name);
+
+ layers = gimp_image_get_layers (images[i], &n_layers);
+ channels = gimp_image_get_channels (images[i], &n_channels);
+
+ for (j = 0; j < n_layers; j++)
+ if (! constraint || (* constraint) (images[i], layers[j], data))
+ {
+ name = gimp_item_get_name (layers[j]);
+ gimp_menu_add_item (menu, image_label, name, layers[j]);
+ g_free (name);
+
+ if (layers[j] == active_drawable)
+ {
+ drawable = active_drawable;
+ gtk_menu_set_active (GTK_MENU (menu), k);
+ }
+ else if (drawable == -1)
+ {
+ drawable = layers[j];
+ }
+
+ k += 1;
+ }
+
+ for (j = 0; j < n_channels; j++)
+ if (! constraint || (* constraint) (images[i], channels[j], data))
+ {
+ name = gimp_item_get_name (channels[j]);
+ gimp_menu_add_item (menu, image_label, name, channels[j]);
+ g_free (name);
+
+ if (channels[j] == active_drawable)
+ {
+ drawable = active_drawable;
+ gtk_menu_set_active (GTK_MENU (menu), k);
+ }
+ else if (drawable == -1)
+ {
+ drawable = channels[j];
+ }
+
+ k += 1;
+ }
+
+ g_free (image_label);
+ g_free (layers);
+ g_free (channels);
+ }
+
+ g_free (images);
+
+ if (k == 0)
+ gimp_menu_add_empty (menu);
+
+ (* callback) (drawable, data);
+
+ return menu;
+}
+
+
+/* private functions */
+
+static GtkWidget *
+gimp_menu_make_menu (GimpMenuCallback callback,
+ gpointer data)
+{
+ GtkWidget *menu;
+
+ menu = gtk_menu_new ();
+ g_object_set_data (G_OBJECT (menu), "gimp-menu-callback", callback);
+ g_object_set_data (G_OBJECT (menu), "gimp-menu-callback-data", data);
+
+ return menu;
+}
+
+static GtkWidget *
+gimp_menu_add_item (GtkWidget *menu,
+ const gchar *image_name,
+ const gchar *drawable_name,
+ gint32 any_ID)
+{
+ GtkWidget *menuitem;
+ GtkWidget *hbox;
+ GtkWidget *vbox;
+ GtkWidget *preview;
+ GtkWidget *label;
+ gchar *str;
+
+ if (drawable_name)
+ str = g_strdup_printf ("%s/%s-%d", image_name, drawable_name, any_ID);
+ else
+ str = g_strdup (image_name);
+
+ menuitem = gtk_menu_item_new ();
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+ gtk_widget_show (menuitem);
+
+ g_signal_connect (menuitem, "activate",
+ G_CALLBACK (gimp_menu_callback),
+ GINT_TO_POINTER (any_ID));
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+ gtk_container_add (GTK_CONTAINER (menuitem), hbox);
+ gtk_widget_show (hbox);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ preview = gimp_menu_make_preview (any_ID, drawable_name == NULL,
+ MENU_THUMBNAIL_WIDTH,
+ MENU_THUMBNAIL_HEIGHT);
+ gtk_box_pack_start (GTK_BOX (vbox), preview, TRUE, TRUE, 0);
+ gtk_widget_show (preview);
+
+ label = gtk_label_new (str);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ g_free (str);
+
+ return menuitem;
+}
+
+static GtkWidget *
+gimp_menu_add_empty (GtkWidget *menu)
+{
+ GtkWidget *menuitem;
+
+ menuitem = gtk_menu_item_new_with_label (_("(Empty)"));
+ gtk_widget_set_sensitive (menuitem, FALSE);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+ gtk_widget_show (menuitem);
+
+ return menuitem;
+}
+
+static GtkWidget *
+gimp_menu_make_preview (gint32 any_ID,
+ gboolean is_image,
+ gint width,
+ gint height)
+{
+ GtkWidget *image;
+ GdkPixbuf *pixbuf;
+
+ if (is_image)
+ pixbuf = gimp_image_get_thumbnail (any_ID,
+ width, height,
+ GIMP_PIXBUF_SMALL_CHECKS);
+ else
+ pixbuf = gimp_drawable_get_thumbnail (any_ID,
+ width, height,
+ GIMP_PIXBUF_SMALL_CHECKS);
+
+ image = gtk_image_new_from_pixbuf (pixbuf);
+
+ g_object_unref (pixbuf);
+
+ return image;
+}
+
+static void
+gimp_menu_callback (GtkWidget *widget,
+ gpointer any_ID)
+{
+ GtkWidget *parent = gtk_widget_get_parent (widget);
+ GimpMenuCallback callback;
+ gpointer callback_data;
+
+ callback = (GimpMenuCallback) g_object_get_data (G_OBJECT (parent),
+ "gimp-menu-callback");
+ callback_data = g_object_get_data (G_OBJECT (parent),
+ "gimp-menu-callback-data");
+
+ (* callback) (GPOINTER_TO_INT (any_ID), callback_data);
+}
diff --git a/libgimp/gimpmenu.h b/libgimp/gimpmenu.h
new file mode 100644
index 0000000..bb4648a
--- /dev/null
+++ b/libgimp/gimpmenu.h
@@ -0,0 +1,68 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpmenu.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_UI_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimpui.h> can be included directly."
+#endif
+
+#ifndef __GIMP_MENU_H__
+#define __GIMP_MENU_H__
+
+/* These functions are deprecated and should not be used in newly
+ * written code.
+ */
+
+G_BEGIN_DECLS
+
+#ifndef GIMP_DISABLE_DEPRECATED
+
+typedef gboolean (* GimpConstraintFunc) (gint32 image_id,
+ gint32 drawable_id,
+ gpointer data);
+typedef void (* GimpMenuCallback) (gint32 any_id,
+ gpointer data);
+
+
+GIMP_DEPRECATED_FOR(gimp_image_combo_box_new)
+GtkWidget * gimp_image_menu_new (GimpConstraintFunc constraint,
+ GimpMenuCallback callback,
+ gpointer data,
+ gint32 active_image);
+GIMP_DEPRECATED_FOR(gimp_layer_combo_box_new)
+GtkWidget * gimp_layer_menu_new (GimpConstraintFunc constraint,
+ GimpMenuCallback callback,
+ gpointer data,
+ gint32 active_layer);
+GIMP_DEPRECATED_FOR(gimp_channel_combo_box_new)
+GtkWidget * gimp_channel_menu_new (GimpConstraintFunc constraint,
+ GimpMenuCallback callback,
+ gpointer data,
+ gint32 active_channel);
+GIMP_DEPRECATED_FOR(gimp_drawable_combo_box_new)
+GtkWidget * gimp_drawable_menu_new (GimpConstraintFunc constraint,
+ GimpMenuCallback callback,
+ gpointer data,
+ gint32 active_drawable);
+
+#endif /* GIMP_DISABLE_DEPRECATED */
+
+G_END_DECLS
+
+#endif /* __GIMP_MENU_H__ */
diff --git a/libgimp/gimpmessage_pdb.c b/libgimp/gimpmessage_pdb.c
new file mode 100644
index 0000000..3dce5f3
--- /dev/null
+++ b/libgimp/gimpmessage_pdb.c
@@ -0,0 +1,126 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpmessage_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpmessage
+ * @title: gimpmessage
+ * @short_description: Display a dialog box with a message.
+ *
+ * Display a dialog box with a message.
+ **/
+
+
+/**
+ * gimp_message:
+ * @message: Message to display in the dialog.
+ *
+ * Displays a dialog box with a message.
+ *
+ * Displays a dialog box with a message. Useful for status or error
+ * reporting. The message must be in UTF-8 encoding.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_message (const gchar *message)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-message",
+ &nreturn_vals,
+ GIMP_PDB_STRING, message,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_message_get_handler:
+ *
+ * Returns the current state of where warning messages are displayed.
+ *
+ * This procedure returns the way g_message warnings are displayed.
+ * They can be shown in a dialog box or printed on the console where
+ * gimp was started.
+ *
+ * Returns: The current handler type.
+ **/
+GimpMessageHandlerType
+gimp_message_get_handler (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpMessageHandlerType handler = 0;
+
+ return_vals = gimp_run_procedure ("gimp-message-get-handler",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ handler = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return handler;
+}
+
+/**
+ * gimp_message_set_handler:
+ * @handler: The new handler type.
+ *
+ * Controls where warning messages are displayed.
+ *
+ * This procedure controls how g_message warnings are displayed. They
+ * can be shown in a dialog box or printed on the console where gimp
+ * was started.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_message_set_handler (GimpMessageHandlerType handler)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-message-set-handler",
+ &nreturn_vals,
+ GIMP_PDB_INT32, handler,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimpmessage_pdb.h b/libgimp/gimpmessage_pdb.h
new file mode 100644
index 0000000..a23fe59
--- /dev/null
+++ b/libgimp/gimpmessage_pdb.h
@@ -0,0 +1,42 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpmessage_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_MESSAGE_PDB_H__
+#define __GIMP_MESSAGE_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_message (const gchar *message);
+GimpMessageHandlerType gimp_message_get_handler (void);
+gboolean gimp_message_set_handler (GimpMessageHandlerType handler);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_MESSAGE_PDB_H__ */
diff --git a/libgimp/gimppainttools_pdb.c b/libgimp/gimppainttools_pdb.c
new file mode 100644
index 0000000..f03b43a
--- /dev/null
+++ b/libgimp/gimppainttools_pdb.c
@@ -0,0 +1,775 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimppainttools_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimppainttools
+ * @title: gimppainttools
+ * @short_description: Access to toolbox paint tools.
+ *
+ * Functions giving access to toolbox paint tools.
+ **/
+
+
+/**
+ * gimp_airbrush:
+ * @drawable_ID: The affected drawable.
+ * @pressure: The pressure of the airbrush strokes.
+ * @num_strokes: Number of stroke control points (count each coordinate as 2 points).
+ * @strokes: Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }.
+ *
+ * Paint in the current brush with varying pressure. Paint application
+ * is time-dependent.
+ *
+ * This tool simulates the use of an airbrush. Paint pressure
+ * represents the relative intensity of the paint application. High
+ * pressure results in a thicker layer of paint while low pressure
+ * results in a thinner layer.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_airbrush (gint32 drawable_ID,
+ gdouble pressure,
+ gint num_strokes,
+ const gdouble *strokes)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-airbrush",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_FLOAT, pressure,
+ GIMP_PDB_INT32, num_strokes,
+ GIMP_PDB_FLOATARRAY, strokes,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_airbrush_default:
+ * @drawable_ID: The affected drawable.
+ * @num_strokes: Number of stroke control points (count each coordinate as 2 points).
+ * @strokes: Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }.
+ *
+ * Paint in the current brush with varying pressure. Paint application
+ * is time-dependent.
+ *
+ * This tool simulates the use of an airbrush. It is similar to
+ * gimp_airbrush() except that the pressure is derived from the
+ * airbrush tools options box. It the option has not been set the
+ * default for the option will be used.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_airbrush_default (gint32 drawable_ID,
+ gint num_strokes,
+ const gdouble *strokes)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-airbrush-default",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, num_strokes,
+ GIMP_PDB_FLOATARRAY, strokes,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_clone:
+ * @drawable_ID: The affected drawable.
+ * @src_drawable_ID: The source drawable.
+ * @clone_type: The type of clone.
+ * @src_x: The x coordinate in the source image.
+ * @src_y: The y coordinate in the source image.
+ * @num_strokes: Number of stroke control points (count each coordinate as 2 points).
+ * @strokes: Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }.
+ *
+ * Clone from the source to the dest drawable using the current brush
+ *
+ * This tool clones (copies) from the source drawable starting at the
+ * specified source coordinates to the dest drawable. If the
+ * \"clone_type\" argument is set to PATTERN-CLONE, then the current
+ * pattern is used as the source and the \"src_drawable\" argument is
+ * ignored. Pattern cloning assumes a tileable pattern and mods the sum
+ * of the src coordinates and subsequent stroke offsets with the width
+ * and height of the pattern. For image cloning, if the sum of the src
+ * coordinates and subsequent stroke offsets exceeds the extents of the
+ * src drawable, then no paint is transferred. The clone tool is
+ * capable of transforming between any image types including
+ * RGB-&gt;Indexed--although converting from any type to indexed is
+ * significantly slower.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_clone (gint32 drawable_ID,
+ gint32 src_drawable_ID,
+ GimpCloneType clone_type,
+ gdouble src_x,
+ gdouble src_y,
+ gint num_strokes,
+ const gdouble *strokes)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-clone",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_DRAWABLE, src_drawable_ID,
+ GIMP_PDB_INT32, clone_type,
+ GIMP_PDB_FLOAT, src_x,
+ GIMP_PDB_FLOAT, src_y,
+ GIMP_PDB_INT32, num_strokes,
+ GIMP_PDB_FLOATARRAY, strokes,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_clone_default:
+ * @drawable_ID: The affected drawable.
+ * @num_strokes: Number of stroke control points (count each coordinate as 2 points).
+ * @strokes: Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }.
+ *
+ * Clone from the source to the dest drawable using the current brush
+ *
+ * This tool clones (copies) from the source drawable starting at the
+ * specified source coordinates to the dest drawable. This function
+ * performs exactly the same as the gimp_clone() function except that
+ * the tools arguments are obtained from the clones option dialog. It
+ * this dialog has not been activated then the dialogs default values
+ * will be used.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_clone_default (gint32 drawable_ID,
+ gint num_strokes,
+ const gdouble *strokes)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-clone-default",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, num_strokes,
+ GIMP_PDB_FLOATARRAY, strokes,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_convolve:
+ * @drawable_ID: The affected drawable.
+ * @pressure: The pressure.
+ * @convolve_type: Convolve type.
+ * @num_strokes: Number of stroke control points (count each coordinate as 2 points).
+ * @strokes: Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }.
+ *
+ * Convolve (Blur, Sharpen) using the current brush.
+ *
+ * This tool convolves the specified drawable with either a sharpening
+ * or blurring kernel. The pressure parameter controls the magnitude of
+ * the operation. Like the paintbrush, this tool linearly interpolates
+ * between the specified stroke coordinates.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_convolve (gint32 drawable_ID,
+ gdouble pressure,
+ GimpConvolveType convolve_type,
+ gint num_strokes,
+ const gdouble *strokes)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-convolve",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_FLOAT, pressure,
+ GIMP_PDB_INT32, convolve_type,
+ GIMP_PDB_INT32, num_strokes,
+ GIMP_PDB_FLOATARRAY, strokes,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_convolve_default:
+ * @drawable_ID: The affected drawable.
+ * @num_strokes: Number of stroke control points (count each coordinate as 2 points).
+ * @strokes: Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }.
+ *
+ * Convolve (Blur, Sharpen) using the current brush.
+ *
+ * This tool convolves the specified drawable with either a sharpening
+ * or blurring kernel. This function performs exactly the same as the
+ * gimp_convolve() function except that the tools arguments are
+ * obtained from the convolve option dialog. It this dialog has not
+ * been activated then the dialogs default values will be used.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_convolve_default (gint32 drawable_ID,
+ gint num_strokes,
+ const gdouble *strokes)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-convolve-default",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, num_strokes,
+ GIMP_PDB_FLOATARRAY, strokes,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_dodgeburn:
+ * @drawable_ID: The affected drawable.
+ * @exposure: The exposure of the strokes.
+ * @dodgeburn_type: The type either dodge or burn.
+ * @dodgeburn_mode: The mode.
+ * @num_strokes: Number of stroke control points (count each coordinate as 2 points).
+ * @strokes: Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }.
+ *
+ * Dodgeburn image with varying exposure.
+ *
+ * Dodgeburn. More details here later.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_dodgeburn (gint32 drawable_ID,
+ gdouble exposure,
+ GimpDodgeBurnType dodgeburn_type,
+ GimpTransferMode dodgeburn_mode,
+ gint num_strokes,
+ const gdouble *strokes)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-dodgeburn",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_FLOAT, exposure,
+ GIMP_PDB_INT32, dodgeburn_type,
+ GIMP_PDB_INT32, dodgeburn_mode,
+ GIMP_PDB_INT32, num_strokes,
+ GIMP_PDB_FLOATARRAY, strokes,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_dodgeburn_default:
+ * @drawable_ID: The affected drawable.
+ * @num_strokes: Number of stroke control points (count each coordinate as 2 points).
+ * @strokes: Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }.
+ *
+ * Dodgeburn image with varying exposure. This is the same as the
+ * gimp_dodgeburn() function except that the exposure, type and mode
+ * are taken from the tools option dialog. If the dialog has not been
+ * activated then the defaults as used by the dialog will be used.
+ *
+ * Dodgeburn. More details here later.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_dodgeburn_default (gint32 drawable_ID,
+ gint num_strokes,
+ const gdouble *strokes)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-dodgeburn-default",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, num_strokes,
+ GIMP_PDB_FLOATARRAY, strokes,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_eraser:
+ * @drawable_ID: The affected drawable.
+ * @num_strokes: Number of stroke control points (count each coordinate as 2 points).
+ * @strokes: Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }.
+ * @hardness: How to apply the brush.
+ * @method: The paint method to use.
+ *
+ * Erase using the current brush.
+ *
+ * This tool erases using the current brush mask. If the specified
+ * drawable contains an alpha channel, then the erased pixels will
+ * become transparent. Otherwise, the eraser tool replaces the contents
+ * of the drawable with the background color. Like paintbrush, this
+ * tool linearly interpolates between the specified stroke coordinates.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_eraser (gint32 drawable_ID,
+ gint num_strokes,
+ const gdouble *strokes,
+ GimpBrushApplicationMode hardness,
+ GimpPaintApplicationMode method)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-eraser",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, num_strokes,
+ GIMP_PDB_FLOATARRAY, strokes,
+ GIMP_PDB_INT32, hardness,
+ GIMP_PDB_INT32, method,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_eraser_default:
+ * @drawable_ID: The affected drawable.
+ * @num_strokes: Number of stroke control points (count each coordinate as 2 points).
+ * @strokes: Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }.
+ *
+ * Erase using the current brush.
+ *
+ * This tool erases using the current brush mask. This function
+ * performs exactly the same as the gimp_eraser() function except that
+ * the tools arguments are obtained from the eraser option dialog. It
+ * this dialog has not been activated then the dialogs default values
+ * will be used.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_eraser_default (gint32 drawable_ID,
+ gint num_strokes,
+ const gdouble *strokes)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-eraser-default",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, num_strokes,
+ GIMP_PDB_FLOATARRAY, strokes,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_heal:
+ * @drawable_ID: The affected drawable.
+ * @src_drawable_ID: The source drawable.
+ * @src_x: The x coordinate in the source image.
+ * @src_y: The y coordinate in the source image.
+ * @num_strokes: Number of stroke control points (count each coordinate as 2 points).
+ * @strokes: Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }.
+ *
+ * Heal from the source to the dest drawable using the current brush
+ *
+ * This tool heals the source drawable starting at the specified source
+ * coordinates to the dest drawable. For image healing, if the sum of
+ * the src coordinates and subsequent stroke offsets exceeds the
+ * extents of the src drawable, then no paint is transferred. The
+ * healing tool is capable of transforming between any image types
+ * except RGB-&gt;Indexed.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_heal (gint32 drawable_ID,
+ gint32 src_drawable_ID,
+ gdouble src_x,
+ gdouble src_y,
+ gint num_strokes,
+ const gdouble *strokes)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-heal",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_DRAWABLE, src_drawable_ID,
+ GIMP_PDB_FLOAT, src_x,
+ GIMP_PDB_FLOAT, src_y,
+ GIMP_PDB_INT32, num_strokes,
+ GIMP_PDB_FLOATARRAY, strokes,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_heal_default:
+ * @drawable_ID: The affected drawable.
+ * @num_strokes: Number of stroke control points (count each coordinate as 2 points).
+ * @strokes: Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }.
+ *
+ * Heal from the source to the dest drawable using the current brush
+ *
+ * This tool heals from the source drawable starting at the specified
+ * source coordinates to the dest drawable. This function performs
+ * exactly the same as the gimp_heal() function except that the tools
+ * arguments are obtained from the healing option dialog. It this
+ * dialog has not been activated then the dialogs default values will
+ * be used.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_heal_default (gint32 drawable_ID,
+ gint num_strokes,
+ const gdouble *strokes)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-heal-default",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, num_strokes,
+ GIMP_PDB_FLOATARRAY, strokes,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_paintbrush:
+ * @drawable_ID: The affected drawable.
+ * @fade_out: Fade out parameter.
+ * @num_strokes: Number of stroke control points (count each coordinate as 2 points).
+ * @strokes: Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }.
+ * @method: The paint method to use.
+ * @gradient_length: Length of gradient to draw.
+ *
+ * Paint in the current brush with optional fade out parameter and pull
+ * colors from a gradient.
+ *
+ * This tool is the standard paintbrush. It draws linearly interpolated
+ * lines through the specified stroke coordinates. It operates on the
+ * specified drawable in the foreground color with the active brush.
+ * The 'fade-out' parameter is measured in pixels and allows the brush
+ * stroke to linearly fall off. The pressure is set to the maximum at
+ * the beginning of the stroke. As the distance of the stroke nears the
+ * fade-out value, the pressure will approach zero. The gradient-length
+ * is the distance to spread the gradient over. It is measured in
+ * pixels. If the gradient-length is 0, no gradient is used.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_paintbrush (gint32 drawable_ID,
+ gdouble fade_out,
+ gint num_strokes,
+ const gdouble *strokes,
+ GimpPaintApplicationMode method,
+ gdouble gradient_length)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-paintbrush",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_FLOAT, fade_out,
+ GIMP_PDB_INT32, num_strokes,
+ GIMP_PDB_FLOATARRAY, strokes,
+ GIMP_PDB_INT32, method,
+ GIMP_PDB_FLOAT, gradient_length,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_paintbrush_default:
+ * @drawable_ID: The affected drawable.
+ * @num_strokes: Number of stroke control points (count each coordinate as 2 points).
+ * @strokes: Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }.
+ *
+ * Paint in the current brush. The fade out parameter and pull colors
+ * from a gradient parameter are set from the paintbrush options
+ * dialog. If this dialog has not been activated then the dialog
+ * defaults will be used.
+ *
+ * This tool is similar to the standard paintbrush. It draws linearly
+ * interpolated lines through the specified stroke coordinates. It
+ * operates on the specified drawable in the foreground color with the
+ * active brush. The 'fade-out' parameter is measured in pixels and
+ * allows the brush stroke to linearly fall off (value obtained from
+ * the option dialog). The pressure is set to the maximum at the
+ * beginning of the stroke. As the distance of the stroke nears the
+ * fade-out value, the pressure will approach zero. The gradient-length
+ * (value obtained from the option dialog) is the distance to spread
+ * the gradient over. It is measured in pixels. If the gradient-length
+ * is 0, no gradient is used.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_paintbrush_default (gint32 drawable_ID,
+ gint num_strokes,
+ const gdouble *strokes)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-paintbrush-default",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, num_strokes,
+ GIMP_PDB_FLOATARRAY, strokes,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_pencil:
+ * @drawable_ID: The affected drawable.
+ * @num_strokes: Number of stroke control points (count each coordinate as 2 points).
+ * @strokes: Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }.
+ *
+ * Paint in the current brush without sub-pixel sampling.
+ *
+ * This tool is the standard pencil. It draws linearly interpolated
+ * lines through the specified stroke coordinates. It operates on the
+ * specified drawable in the foreground color with the active brush.
+ * The brush mask is treated as though it contains only black and white
+ * values. Any value below half is treated as black; any above half, as
+ * white.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_pencil (gint32 drawable_ID,
+ gint num_strokes,
+ const gdouble *strokes)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-pencil",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, num_strokes,
+ GIMP_PDB_FLOATARRAY, strokes,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_smudge:
+ * @drawable_ID: The affected drawable.
+ * @pressure: The pressure of the smudge strokes.
+ * @num_strokes: Number of stroke control points (count each coordinate as 2 points).
+ * @strokes: Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }.
+ *
+ * Smudge image with varying pressure.
+ *
+ * This tool simulates a smudge using the current brush. High pressure
+ * results in a greater smudge of paint while low pressure results in a
+ * lesser smudge.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_smudge (gint32 drawable_ID,
+ gdouble pressure,
+ gint num_strokes,
+ const gdouble *strokes)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-smudge",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_FLOAT, pressure,
+ GIMP_PDB_INT32, num_strokes,
+ GIMP_PDB_FLOATARRAY, strokes,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_smudge_default:
+ * @drawable_ID: The affected drawable.
+ * @num_strokes: Number of stroke control points (count each coordinate as 2 points).
+ * @strokes: Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ..., sn.x, sn.y }.
+ *
+ * Smudge image with varying pressure.
+ *
+ * This tool simulates a smudge using the current brush. It behaves
+ * exactly the same as gimp_smudge() except that the pressure value is
+ * taken from the smudge tool options or the options default if the
+ * tools option dialog has not been activated.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_smudge_default (gint32 drawable_ID,
+ gint num_strokes,
+ const gdouble *strokes)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-smudge-default",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, num_strokes,
+ GIMP_PDB_FLOATARRAY, strokes,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimppainttools_pdb.h b/libgimp/gimppainttools_pdb.h
new file mode 100644
index 0000000..3782fbd
--- /dev/null
+++ b/libgimp/gimppainttools_pdb.h
@@ -0,0 +1,109 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimppainttools_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PAINT_TOOLS_PDB_H__
+#define __GIMP_PAINT_TOOLS_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_airbrush (gint32 drawable_ID,
+ gdouble pressure,
+ gint num_strokes,
+ const gdouble *strokes);
+gboolean gimp_airbrush_default (gint32 drawable_ID,
+ gint num_strokes,
+ const gdouble *strokes);
+gboolean gimp_clone (gint32 drawable_ID,
+ gint32 src_drawable_ID,
+ GimpCloneType clone_type,
+ gdouble src_x,
+ gdouble src_y,
+ gint num_strokes,
+ const gdouble *strokes);
+gboolean gimp_clone_default (gint32 drawable_ID,
+ gint num_strokes,
+ const gdouble *strokes);
+gboolean gimp_convolve (gint32 drawable_ID,
+ gdouble pressure,
+ GimpConvolveType convolve_type,
+ gint num_strokes,
+ const gdouble *strokes);
+gboolean gimp_convolve_default (gint32 drawable_ID,
+ gint num_strokes,
+ const gdouble *strokes);
+gboolean gimp_dodgeburn (gint32 drawable_ID,
+ gdouble exposure,
+ GimpDodgeBurnType dodgeburn_type,
+ GimpTransferMode dodgeburn_mode,
+ gint num_strokes,
+ const gdouble *strokes);
+gboolean gimp_dodgeburn_default (gint32 drawable_ID,
+ gint num_strokes,
+ const gdouble *strokes);
+gboolean gimp_eraser (gint32 drawable_ID,
+ gint num_strokes,
+ const gdouble *strokes,
+ GimpBrushApplicationMode hardness,
+ GimpPaintApplicationMode method);
+gboolean gimp_eraser_default (gint32 drawable_ID,
+ gint num_strokes,
+ const gdouble *strokes);
+gboolean gimp_heal (gint32 drawable_ID,
+ gint32 src_drawable_ID,
+ gdouble src_x,
+ gdouble src_y,
+ gint num_strokes,
+ const gdouble *strokes);
+gboolean gimp_heal_default (gint32 drawable_ID,
+ gint num_strokes,
+ const gdouble *strokes);
+gboolean gimp_paintbrush (gint32 drawable_ID,
+ gdouble fade_out,
+ gint num_strokes,
+ const gdouble *strokes,
+ GimpPaintApplicationMode method,
+ gdouble gradient_length);
+gboolean gimp_paintbrush_default (gint32 drawable_ID,
+ gint num_strokes,
+ const gdouble *strokes);
+gboolean gimp_pencil (gint32 drawable_ID,
+ gint num_strokes,
+ const gdouble *strokes);
+gboolean gimp_smudge (gint32 drawable_ID,
+ gdouble pressure,
+ gint num_strokes,
+ const gdouble *strokes);
+gboolean gimp_smudge_default (gint32 drawable_ID,
+ gint num_strokes,
+ const gdouble *strokes);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PAINT_TOOLS_PDB_H__ */
diff --git a/libgimp/gimppalette.c b/libgimp/gimppalette.c
new file mode 100644
index 0000000..ad1519d
--- /dev/null
+++ b/libgimp/gimppalette.c
@@ -0,0 +1,130 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimppalette.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gimp.h"
+
+/**
+ * gimp_palette_get_foreground:
+ * @foreground: The foreground color.
+ *
+ * Get the current GIMP foreground color.
+ *
+ * This procedure retrieves the current GIMP foreground color. The
+ * foreground color is used in a variety of tools such as paint tools,
+ * blending, and bucket fill.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+gimp_palette_get_foreground (GimpRGB *foreground)
+{
+ return gimp_context_get_foreground (foreground);
+}
+
+/**
+ * gimp_palette_get_background:
+ * @background: The background color.
+ *
+ * Get the current GIMP background color.
+ *
+ * This procedure retrieves the current GIMP background color. The
+ * background color is used in a variety of tools such as blending,
+ * erasing (with non-alpha images), and image filling.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+gimp_palette_get_background (GimpRGB *background)
+{
+ return gimp_context_get_background (background);
+}
+
+/**
+ * gimp_palette_set_foreground:
+ * @foreground: The foreground color.
+ *
+ * Set the current GIMP foreground color.
+ *
+ * This procedure sets the current GIMP foreground color. After this is
+ * set, operations which use foreground such as paint tools, blending,
+ * and bucket fill will use the new value.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+gimp_palette_set_foreground (const GimpRGB *foreground)
+{
+ return gimp_context_set_foreground (foreground);
+}
+
+/**
+ * gimp_palette_set_background:
+ * @background: The background color.
+ *
+ * Set the current GIMP background color.
+ *
+ * This procedure sets the current GIMP background color. After this is
+ * set, operations which use background such as blending, filling
+ * images, clearing, and erasing (in non-alpha images) will use the new
+ * value.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+gimp_palette_set_background (const GimpRGB *background)
+{
+ return gimp_context_set_background (background);
+}
+
+/**
+ * gimp_palette_set_default_colors:
+ *
+ * Set the current GIMP foreground and background colors to black and
+ * white.
+ *
+ * This procedure sets the current GIMP foreground and background
+ * colors to their initial default values, black and white.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+gimp_palette_set_default_colors (void)
+{
+ return gimp_context_set_default_colors ();
+}
+
+/**
+ * gimp_palette_swap_colors:
+ *
+ * Swap the current GIMP foreground and background colors.
+ *
+ * This procedure swaps the current GIMP foreground and background
+ * colors, so that the new foreground color becomes the old background
+ * color and vice versa.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+gimp_palette_swap_colors (void)
+{
+ return gimp_context_swap_colors ();
+}
diff --git a/libgimp/gimppalette.h b/libgimp/gimppalette.h
new file mode 100644
index 0000000..4e6269f
--- /dev/null
+++ b/libgimp/gimppalette.h
@@ -0,0 +1,47 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimppalette.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PALETTE_H__
+#define __GIMP_PALETTE_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+GIMP_DEPRECATED_FOR(gimp_context_get_foreground)
+gboolean gimp_palette_get_foreground (GimpRGB *foreground);
+GIMP_DEPRECATED_FOR(gimp_context_get_background)
+gboolean gimp_palette_get_background (GimpRGB *background);
+GIMP_DEPRECATED_FOR(gimp_context_set_foreground)
+gboolean gimp_palette_set_foreground (const GimpRGB *foreground);
+GIMP_DEPRECATED_FOR(gimp_context_set_background)
+gboolean gimp_palette_set_background (const GimpRGB *background);
+GIMP_DEPRECATED_FOR(gimp_context_set_default_colors)
+gboolean gimp_palette_set_default_colors (void);
+GIMP_DEPRECATED_FOR(gimp_context_swap_colors)
+gboolean gimp_palette_swap_colors (void);
+
+G_END_DECLS
+
+#endif /* __GIMP_PALETTE_H__ */
diff --git a/libgimp/gimppalette_pdb.c b/libgimp/gimppalette_pdb.c
new file mode 100644
index 0000000..926ef30
--- /dev/null
+++ b/libgimp/gimppalette_pdb.c
@@ -0,0 +1,591 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimppalette_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimppalette
+ * @title: gimppalette
+ * @short_description: Functions operating on a single palette.
+ *
+ * Functions operating on a single palette.
+ **/
+
+
+/**
+ * gimp_palette_new:
+ * @name: The requested name of the new palette.
+ *
+ * Creates a new palette
+ *
+ * This procedure creates a new, uninitialized palette
+ *
+ * Returns: The actual new palette name.
+ *
+ * Since: 2.2
+ **/
+gchar *
+gimp_palette_new (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *actual_name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-palette-new",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ actual_name = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return actual_name;
+}
+
+/**
+ * gimp_palette_duplicate:
+ * @name: The palette name.
+ *
+ * Duplicates a palette
+ *
+ * This procedure creates an identical palette by a different name
+ *
+ * Returns: The name of the palette's copy.
+ *
+ * Since: 2.2
+ **/
+gchar *
+gimp_palette_duplicate (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *copy_name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-palette-duplicate",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ copy_name = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return copy_name;
+}
+
+/**
+ * gimp_palette_rename:
+ * @name: The palette name.
+ * @new_name: The new name of the palette.
+ *
+ * Rename a palette
+ *
+ * This procedure renames a palette
+ *
+ * Returns: The actual new name of the palette.
+ *
+ * Since: 2.2
+ **/
+gchar *
+gimp_palette_rename (const gchar *name,
+ const gchar *new_name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *actual_name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-palette-rename",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_STRING, new_name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ actual_name = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return actual_name;
+}
+
+/**
+ * gimp_palette_delete:
+ * @name: The palette name.
+ *
+ * Deletes a palette
+ *
+ * This procedure deletes a palette
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_palette_delete (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-palette-delete",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_palette_is_editable:
+ * @name: The palette name.
+ *
+ * Tests if palette can be edited
+ *
+ * Returns TRUE if you have permission to change the palette
+ *
+ * Returns: TRUE if the palette can be edited.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_palette_is_editable (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean editable = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-palette-is-editable",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ editable = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return editable;
+}
+
+/**
+ * gimp_palette_get_info:
+ * @name: The palette name.
+ * @num_colors: The number of colors in the palette.
+ *
+ * Retrieve information about the specified palette.
+ *
+ * This procedure retrieves information about the specified palette.
+ * This includes the name, and the number of colors.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_palette_get_info (const gchar *name,
+ gint *num_colors)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-palette-get-info",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ *num_colors = 0;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ *num_colors = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_palette_get_colors:
+ * @name: The palette name.
+ * @num_colors: Length of the colors array.
+ *
+ * Gets all colors from the specified palette.
+ *
+ * This procedure retrieves all color entries of the specified palette.
+ *
+ * Returns: The colors in the palette.
+ *
+ * Since: 2.6
+ **/
+GimpRGB *
+gimp_palette_get_colors (const gchar *name,
+ gint *num_colors)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpRGB *colors = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-palette-get-colors",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ *num_colors = 0;
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ *num_colors = return_vals[1].data.d_int32;
+ colors = g_new (GimpRGB, *num_colors);
+ memcpy (colors,
+ return_vals[2].data.d_colorarray,
+ *num_colors * sizeof (GimpRGB));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return colors;
+}
+
+/**
+ * gimp_palette_get_columns:
+ * @name: The palette name.
+ *
+ * Retrieves the number of columns to use to display this palette
+ *
+ * This procedures retrieves the preferred number of columns to use
+ * when the palette is being displayed.
+ *
+ * Returns: The number of columns used to display this palette.
+ *
+ * Since: 2.4
+ **/
+gint
+gimp_palette_get_columns (const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint num_columns = 0;
+
+ return_vals = gimp_run_procedure ("gimp-palette-get-columns",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ num_columns = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return num_columns;
+}
+
+/**
+ * gimp_palette_set_columns:
+ * @name: The palette name.
+ * @columns: The new number of columns.
+ *
+ * Sets the number of columns to use when displaying the palette
+ *
+ * This procedures controls how many colors are shown per row when the
+ * palette is being displayed. This value can only be changed if the
+ * palette is writable. The maximum allowed value is 64.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_palette_set_columns (const gchar *name,
+ gint columns)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-palette-set-columns",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, columns,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_palette_add_entry:
+ * @name: The palette name.
+ * @entry_name: The name of the entry.
+ * @color: The new entry's color color.
+ * @entry_num: The index of the added entry.
+ *
+ * Adds a palette entry to the specified palette.
+ *
+ * This procedure adds an entry to the specified palette. It returns an
+ * error if the entry palette does not exist.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_palette_add_entry (const gchar *name,
+ const gchar *entry_name,
+ const GimpRGB *color,
+ gint *entry_num)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-palette-add-entry",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_STRING, entry_name,
+ GIMP_PDB_COLOR, color,
+ GIMP_PDB_END);
+
+ *entry_num = 0;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ *entry_num = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_palette_delete_entry:
+ * @name: The palette name.
+ * @entry_num: The index of the added entry.
+ *
+ * Deletes a palette entry from the specified palette.
+ *
+ * This procedure deletes an entry from the specified palette. It
+ * returns an error if the entry palette does not exist.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_palette_delete_entry (const gchar *name,
+ gint entry_num)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-palette-delete-entry",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, entry_num,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_palette_entry_get_color:
+ * @name: The palette name.
+ * @entry_num: The entry to retrieve.
+ * @color: The color requested.
+ *
+ * Gets the specified palette entry from the specified palette.
+ *
+ * This procedure retrieves the color of the zero-based entry specified
+ * for the specified palette. It returns an error if the entry does not
+ * exist.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_palette_entry_get_color (const gchar *name,
+ gint entry_num,
+ GimpRGB *color)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-palette-entry-get-color",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, entry_num,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ *color = return_vals[1].data.d_color;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_palette_entry_set_color:
+ * @name: The palette name.
+ * @entry_num: The entry to retrieve.
+ * @color: The new color.
+ *
+ * Sets the specified palette entry in the specified palette.
+ *
+ * This procedure sets the color of the zero-based entry specified for
+ * the specified palette. It returns an error if the entry does not
+ * exist.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_palette_entry_set_color (const gchar *name,
+ gint entry_num,
+ const GimpRGB *color)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-palette-entry-set-color",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, entry_num,
+ GIMP_PDB_COLOR, color,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_palette_entry_get_name:
+ * @name: The palette name.
+ * @entry_num: The entry to retrieve.
+ * @entry_name: The name requested.
+ *
+ * Gets the specified palette entry from the specified palette.
+ *
+ * This procedure retrieves the name of the zero-based entry specified
+ * for the specified palette. It returns an error if the entry does not
+ * exist.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_palette_entry_get_name (const gchar *name,
+ gint entry_num,
+ gchar **entry_name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-palette-entry-get-name",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, entry_num,
+ GIMP_PDB_END);
+
+ *entry_name = NULL;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ *entry_name = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_palette_entry_set_name:
+ * @name: The palette name.
+ * @entry_num: The entry to retrieve.
+ * @entry_name: The new name.
+ *
+ * Sets the specified palette entry in the specified palette.
+ *
+ * This procedure sets the name of the zero-based entry specified for
+ * the specified palette. It returns an error if the entry does not
+ * exist.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_palette_entry_set_name (const gchar *name,
+ gint entry_num,
+ const gchar *entry_name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-palette-entry-set-name",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, entry_num,
+ GIMP_PDB_STRING, entry_name,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimppalette_pdb.h b/libgimp/gimppalette_pdb.h
new file mode 100644
index 0000000..d9dd379
--- /dev/null
+++ b/libgimp/gimppalette_pdb.h
@@ -0,0 +1,70 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimppalette_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PALETTE_PDB_H__
+#define __GIMP_PALETTE_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gchar* gimp_palette_new (const gchar *name);
+gchar* gimp_palette_duplicate (const gchar *name);
+gchar* gimp_palette_rename (const gchar *name,
+ const gchar *new_name);
+gboolean gimp_palette_delete (const gchar *name);
+gboolean gimp_palette_is_editable (const gchar *name);
+gboolean gimp_palette_get_info (const gchar *name,
+ gint *num_colors);
+GimpRGB* gimp_palette_get_colors (const gchar *name,
+ gint *num_colors);
+gint gimp_palette_get_columns (const gchar *name);
+gboolean gimp_palette_set_columns (const gchar *name,
+ gint columns);
+gboolean gimp_palette_add_entry (const gchar *name,
+ const gchar *entry_name,
+ const GimpRGB *color,
+ gint *entry_num);
+gboolean gimp_palette_delete_entry (const gchar *name,
+ gint entry_num);
+gboolean gimp_palette_entry_get_color (const gchar *name,
+ gint entry_num,
+ GimpRGB *color);
+gboolean gimp_palette_entry_set_color (const gchar *name,
+ gint entry_num,
+ const GimpRGB *color);
+gboolean gimp_palette_entry_get_name (const gchar *name,
+ gint entry_num,
+ gchar **entry_name);
+gboolean gimp_palette_entry_set_name (const gchar *name,
+ gint entry_num,
+ const gchar *entry_name);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PALETTE_PDB_H__ */
diff --git a/libgimp/gimppalettemenu.c b/libgimp/gimppalettemenu.c
new file mode 100644
index 0000000..e013d1b
--- /dev/null
+++ b/libgimp/gimppalettemenu.c
@@ -0,0 +1,148 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppalettemenu.c
+ * Copyright (C) 2004 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "gimp.h"
+
+#include "gimpuitypes.h"
+#include "gimppalettemenu.h"
+#include "gimppaletteselectbutton.h"
+
+
+/**
+ * SECTION: gimppalettemenu
+ * @title: gimppalettemenu
+ * @short_description: A widget for selecting palettes.
+ *
+ * A widget for selecting palettes.
+ **/
+
+
+typedef struct
+{
+ GimpRunPaletteCallback callback;
+ gpointer data;
+} CompatCallbackData;
+
+
+static void compat_callback (GimpPaletteSelectButton *palette_button,
+ const gchar *palette_name,
+ gboolean dialog_closing,
+ CompatCallbackData *data);
+static void compat_callback_data_free (CompatCallbackData *data);
+
+
+/**
+ * gimp_palette_select_widget_new:
+ * @title: Title of the dialog to use or %NULL to use the default title.
+ * @palette_name: Initial palette name.
+ * @callback: A function to call when the selected palette changes.
+ * @data: A pointer to arbitrary data to be used in the call to @callback.
+ *
+ * Creates a new #GtkWidget that completely controls the selection of
+ * a palette. This widget is suitable for placement in a table in a
+ * plug-in dialog.
+ *
+ * Returns: A #GtkWidget that you can use in your UI.
+ *
+ * Since: 2.2
+ */
+GtkWidget *
+gimp_palette_select_widget_new (const gchar *title,
+ const gchar *palette_name,
+ GimpRunPaletteCallback callback,
+ gpointer data)
+{
+ GtkWidget *palette_button;
+ CompatCallbackData *compat_data;
+
+ g_return_val_if_fail (callback != NULL, NULL);
+
+ palette_button = gimp_palette_select_button_new (title, palette_name);
+
+ compat_data = g_slice_new (CompatCallbackData);
+
+ compat_data->callback = callback;
+ compat_data->data = data;
+
+ g_signal_connect_data (palette_button, "palette-set",
+ G_CALLBACK (compat_callback),
+ compat_data,
+ (GClosureNotify) compat_callback_data_free, 0);
+
+ return palette_button;
+}
+
+/**
+ * gimp_palette_select_widget_close:
+ * @widget: A palette select widget.
+ *
+ * Closes the popup window associated with @widget.
+ *
+ * Since: 2.2
+ */
+void
+gimp_palette_select_widget_close (GtkWidget *widget)
+{
+ g_return_if_fail (widget != NULL);
+
+ gimp_select_button_close_popup (GIMP_SELECT_BUTTON (widget));
+}
+
+/**
+ * gimp_palette_select_widget_set:
+ * @widget: A palette select widget.
+ * @palette_name: Palette name to set; %NULL means no change.
+ *
+ * Sets the current palette for the palette select widget. Calls the
+ * callback function if one was supplied in the call to
+ * gimp_palette_select_widget_new().
+ *
+ * Since: 2.2
+ */
+void
+gimp_palette_select_widget_set (GtkWidget *widget,
+ const gchar *palette_name)
+{
+ g_return_if_fail (widget != NULL);
+
+ gimp_palette_select_button_set_palette (GIMP_PALETTE_SELECT_BUTTON (widget),
+ palette_name);
+}
+
+
+static void
+compat_callback (GimpPaletteSelectButton *palette_button,
+ const gchar *palette_name,
+ gboolean dialog_closing,
+ CompatCallbackData *data)
+{
+ data->callback (palette_name, dialog_closing, data->data);
+}
+
+static void
+compat_callback_data_free (CompatCallbackData *data)
+{
+ g_slice_free (CompatCallbackData, data);
+}
diff --git a/libgimp/gimppalettemenu.h b/libgimp/gimppalettemenu.h
new file mode 100644
index 0000000..af0e5f9
--- /dev/null
+++ b/libgimp/gimppalettemenu.h
@@ -0,0 +1,49 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppalettemenu.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_UI_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimpui.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PALETTE_MENU_H__
+#define __GIMP_PALETTE_MENU_H__
+
+/* These functions are deprecated and should not be used in newly
+ * written code.
+ */
+
+G_BEGIN_DECLS
+
+GIMP_DEPRECATED_FOR(gimp_gradient_select_button_new)
+GtkWidget * gimp_palette_select_widget_new (const gchar *title,
+ const gchar *palette_name,
+ GimpRunPaletteCallback callback,
+ gpointer data);
+
+GIMP_DEPRECATED_FOR(gimp_select_button_close_popup)
+void gimp_palette_select_widget_close (GtkWidget *widget);
+GIMP_DEPRECATED_FOR(gimp_gradient_select_button_set_gradient)
+void gimp_palette_select_widget_set (GtkWidget *widget,
+ const gchar *palette_name);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PALETTE_MENU_H__ */
diff --git a/libgimp/gimppalettes.c b/libgimp/gimppalettes.c
new file mode 100644
index 0000000..f5e479e
--- /dev/null
+++ b/libgimp/gimppalettes.c
@@ -0,0 +1,38 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimppalettes.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gimp.h"
+#include "gimppalettes.h"
+
+/**
+ * gimp_palettes_set_palette:
+ * @name: The palette name.
+ *
+ * This procedure is deprecated! Use gimp_context_set_palette() instead.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+gimp_palettes_set_palette (const gchar *name)
+{
+ return gimp_context_set_palette (name);
+}
diff --git a/libgimp/gimppalettes.h b/libgimp/gimppalettes.h
new file mode 100644
index 0000000..54d9d9b
--- /dev/null
+++ b/libgimp/gimppalettes.h
@@ -0,0 +1,37 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimppalettes.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PALETTES_H__
+#define __GIMP_PALETTES_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+GIMP_DEPRECATED_FOR(gimp_context_set_gradient)
+gboolean gimp_palettes_set_palette (const gchar *name);
+
+G_END_DECLS
+
+#endif /* __GIMP_PALETTES_H__ */
diff --git a/libgimp/gimppalettes_pdb.c b/libgimp/gimppalettes_pdb.c
new file mode 100644
index 0000000..80a1c23
--- /dev/null
+++ b/libgimp/gimppalettes_pdb.c
@@ -0,0 +1,178 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimppalettes_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimppalettes
+ * @title: gimppalettes
+ * @short_description: Operations related to palettes.
+ *
+ * Operations related to palettes.
+ **/
+
+
+/**
+ * gimp_palettes_refresh:
+ *
+ * Refreshes current palettes. This function always succeeds.
+ *
+ * This procedure retrieves all palettes currently in the user's
+ * palette path and updates the palette dialogs accordingly.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_palettes_refresh (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-palettes-refresh",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_palettes_get_list:
+ * @filter: An optional regular expression used to filter the list.
+ * @num_palettes: The number of palettes in the list.
+ *
+ * Retrieves a list of all of the available palettes
+ *
+ * This procedure returns a complete listing of available palettes.
+ * Each name returned can be used as input to the command
+ * gimp_context_set_palette().
+ *
+ * Returns: The list of palette names. The returned value must be freed
+ * with g_strfreev().
+ **/
+gchar **
+gimp_palettes_get_list (const gchar *filter,
+ gint *num_palettes)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar **palette_list = NULL;
+ gint i;
+
+ return_vals = gimp_run_procedure ("gimp-palettes-get-list",
+ &nreturn_vals,
+ GIMP_PDB_STRING, filter,
+ GIMP_PDB_END);
+
+ *num_palettes = 0;
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ *num_palettes = return_vals[1].data.d_int32;
+ if (*num_palettes > 0)
+ {
+ palette_list = g_new0 (gchar *, *num_palettes + 1);
+ for (i = 0; i < *num_palettes; i++)
+ palette_list[i] = g_strdup (return_vals[2].data.d_stringarray[i]);
+ }
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return palette_list;
+}
+
+/**
+ * gimp_palettes_get_palette:
+ * @num_colors: The palette num_colors.
+ *
+ * Deprecated: Use gimp_context_get_palette() instead.
+ *
+ * Returns: The palette name.
+ **/
+gchar *
+gimp_palettes_get_palette (gint *num_colors)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-palettes-get-palette",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ name = g_strdup (return_vals[1].data.d_string);
+ *num_colors = return_vals[2].data.d_int32;
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return name;
+}
+
+/**
+ * gimp_palettes_get_palette_entry:
+ * @name: The palette name (\"\" means currently active palette).
+ * @entry_num: The entry to retrieve.
+ * @num_colors: The palette num_colors.
+ * @color: The color requested.
+ *
+ * Deprecated: Use gimp_palette_entry_get_color() instead.
+ *
+ * Returns: The palette name.
+ **/
+gchar *
+gimp_palettes_get_palette_entry (const gchar *name,
+ gint entry_num,
+ gint *num_colors,
+ GimpRGB *color)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *actual_name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-palettes-get-palette-entry",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, entry_num,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ actual_name = g_strdup (return_vals[1].data.d_string);
+ *num_colors = return_vals[2].data.d_int32;
+ *color = return_vals[3].data.d_color;
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return actual_name;
+}
diff --git a/libgimp/gimppalettes_pdb.h b/libgimp/gimppalettes_pdb.h
new file mode 100644
index 0000000..ad4d9a3
--- /dev/null
+++ b/libgimp/gimppalettes_pdb.h
@@ -0,0 +1,49 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimppalettes_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PALETTES_PDB_H__
+#define __GIMP_PALETTES_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_palettes_refresh (void);
+gchar** gimp_palettes_get_list (const gchar *filter,
+ gint *num_palettes);
+GIMP_DEPRECATED_FOR(gimp_context_get_palette)
+gchar* gimp_palettes_get_palette (gint *num_colors);
+GIMP_DEPRECATED_FOR(gimp_palette_entry_get_color)
+gchar* gimp_palettes_get_palette_entry (const gchar *name,
+ gint entry_num,
+ gint *num_colors,
+ GimpRGB *color);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PALETTES_PDB_H__ */
diff --git a/libgimp/gimppaletteselect.c b/libgimp/gimppaletteselect.c
new file mode 100644
index 0000000..c6ddaaf
--- /dev/null
+++ b/libgimp/gimppaletteselect.c
@@ -0,0 +1,212 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppaletteselect.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+typedef struct
+{
+ gchar *palette_callback;
+ guint idle_id;
+ gchar *palette_name;
+ gint num_colors;
+ GimpRunPaletteCallback callback;
+ gboolean closing;
+ gpointer data;
+} GimpPaletteData;
+
+
+/* local function prototypes */
+
+static void gimp_palette_data_free (GimpPaletteData *data);
+
+static void gimp_temp_palette_run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+static gboolean gimp_temp_palette_run_idle (GimpPaletteData *palette_data);
+
+
+/* private variables */
+
+static GHashTable *gimp_palette_select_ht = NULL;
+
+
+/* public functions */
+
+const gchar *
+gimp_palette_select_new (const gchar *title,
+ const gchar *palette_name,
+ GimpRunPaletteCallback callback,
+ gpointer data)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_STRING, "str", "String" },
+ { GIMP_PDB_INT32, "num-colors", "Number of colors" },
+ { GIMP_PDB_INT32, "dialog-status", "If the dialog was closing "
+ "[0 = No, 1 = Yes]" }
+ };
+
+ gchar *palette_callback = gimp_procedural_db_temp_name ();
+
+ gimp_install_temp_proc (palette_callback,
+ "Temporary palette popup callback procedure",
+ "",
+ "",
+ "",
+ "",
+ NULL,
+ "",
+ GIMP_TEMPORARY,
+ G_N_ELEMENTS (args), 0,
+ args, NULL,
+ gimp_temp_palette_run);
+
+ if (gimp_palettes_popup (palette_callback, title, palette_name))
+ {
+ GimpPaletteData *palette_data;
+
+ gimp_extension_enable (); /* Allow callbacks to be watched */
+
+ /* Now add to hash table so we can find it again */
+ if (! gimp_palette_select_ht)
+ {
+ gimp_palette_select_ht =
+ g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free,
+ (GDestroyNotify) gimp_palette_data_free);
+ }
+
+ palette_data = g_slice_new0 (GimpPaletteData);
+
+ palette_data->palette_callback = palette_callback;
+ palette_data->callback = callback;
+ palette_data->data = data;
+
+ g_hash_table_insert (gimp_palette_select_ht,
+ palette_callback, palette_data);
+
+ return palette_callback;
+ }
+
+ gimp_uninstall_temp_proc (palette_callback);
+ g_free (palette_callback);
+
+ return NULL;
+}
+
+void
+gimp_palette_select_destroy (const gchar *palette_callback)
+{
+ GimpPaletteData *palette_data;
+
+ g_return_if_fail (palette_callback != NULL);
+ g_return_if_fail (gimp_palette_select_ht != NULL);
+
+ palette_data = g_hash_table_lookup (gimp_palette_select_ht, palette_callback);
+
+ if (! palette_data)
+ {
+ g_warning ("Can't find internal palette data");
+ return;
+ }
+
+ if (palette_data->idle_id)
+ g_source_remove (palette_data->idle_id);
+
+ g_free (palette_data->palette_name);
+
+ if (palette_data->palette_callback)
+ gimp_palettes_close_popup (palette_data->palette_callback);
+
+ gimp_uninstall_temp_proc (palette_callback);
+
+ g_hash_table_remove (gimp_palette_select_ht, palette_callback);
+}
+
+
+/* private functions */
+
+static void
+gimp_palette_data_free (GimpPaletteData *data)
+{
+ g_slice_free (GimpPaletteData, data);
+}
+
+static void
+gimp_temp_palette_run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpPaletteData *palette_data;
+
+ palette_data = g_hash_table_lookup (gimp_palette_select_ht, name);
+
+ if (! palette_data)
+ {
+ g_warning ("Can't find internal palette data");
+ }
+ else
+ {
+ g_free (palette_data->palette_name);
+
+ palette_data->palette_name = g_strdup (param[0].data.d_string);
+ palette_data->num_colors = param[1].data.d_int32;
+ palette_data->closing = param[2].data.d_int32;
+
+ if (! palette_data->idle_id)
+ palette_data->idle_id = g_idle_add ((GSourceFunc) gimp_temp_palette_run_idle,
+ palette_data);
+ }
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_SUCCESS;
+}
+
+static gboolean
+gimp_temp_palette_run_idle (GimpPaletteData *palette_data)
+{
+ palette_data->idle_id = 0;
+
+ if (palette_data->callback)
+ palette_data->callback (palette_data->palette_name,
+ palette_data->closing,
+ palette_data->data);
+
+ if (palette_data->closing)
+ {
+ gchar *palette_callback = palette_data->palette_callback;
+
+ palette_data->palette_callback = NULL;
+ gimp_palette_select_destroy (palette_callback);
+ }
+
+ return FALSE;
+}
diff --git a/libgimp/gimppaletteselect.h b/libgimp/gimppaletteselect.h
new file mode 100644
index 0000000..3e4b6c9
--- /dev/null
+++ b/libgimp/gimppaletteselect.h
@@ -0,0 +1,45 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppaletteselect.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PALETTE_SELECT_H__
+#define __GIMP_PALETTE_SELECT_H__
+
+G_BEGIN_DECLS
+
+
+typedef void (* GimpRunPaletteCallback) (const gchar *palette_name,
+ gboolean dialog_closing,
+ gpointer user_data);
+
+
+const gchar * gimp_palette_select_new (const gchar *title,
+ const gchar *palette_name,
+ GimpRunPaletteCallback callback,
+ gpointer data);
+void gimp_palette_select_destroy (const gchar *palette_callback);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PALETTE_SELECT_H__ */
diff --git a/libgimp/gimppaletteselect_pdb.c b/libgimp/gimppaletteselect_pdb.c
new file mode 100644
index 0000000..0e64e1b
--- /dev/null
+++ b/libgimp/gimppaletteselect_pdb.c
@@ -0,0 +1,131 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimppaletteselect_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimppaletteselect
+ * @title: gimppaletteselect
+ * @short_description: Functions providing a palette selection dialog.
+ *
+ * Functions providing a palette selection dialog.
+ **/
+
+
+/**
+ * gimp_palettes_popup:
+ * @palette_callback: The callback PDB proc to call when palette selection is made.
+ * @popup_title: Title of the palette selection dialog.
+ * @initial_palette: The name of the palette to set as the first selected.
+ *
+ * Invokes the Gimp palette selection.
+ *
+ * This procedure opens the palette selection dialog.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_palettes_popup (const gchar *palette_callback,
+ const gchar *popup_title,
+ const gchar *initial_palette)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-palettes-popup",
+ &nreturn_vals,
+ GIMP_PDB_STRING, palette_callback,
+ GIMP_PDB_STRING, popup_title,
+ GIMP_PDB_STRING, initial_palette,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_palettes_close_popup:
+ * @palette_callback: The name of the callback registered for this pop-up.
+ *
+ * Close the palette selection dialog.
+ *
+ * This procedure closes an opened palette selection dialog.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_palettes_close_popup (const gchar *palette_callback)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-palettes-close-popup",
+ &nreturn_vals,
+ GIMP_PDB_STRING, palette_callback,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_palettes_set_popup:
+ * @palette_callback: The name of the callback registered for this pop-up.
+ * @palette_name: The name of the palette to set as selected.
+ *
+ * Sets the current palette in a palette selection dialog.
+ *
+ * Sets the current palette in a palette selection dialog.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_palettes_set_popup (const gchar *palette_callback,
+ const gchar *palette_name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-palettes-set-popup",
+ &nreturn_vals,
+ GIMP_PDB_STRING, palette_callback,
+ GIMP_PDB_STRING, palette_name,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimppaletteselect_pdb.h b/libgimp/gimppaletteselect_pdb.h
new file mode 100644
index 0000000..221ec4b
--- /dev/null
+++ b/libgimp/gimppaletteselect_pdb.h
@@ -0,0 +1,45 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimppaletteselect_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PALETTE_SELECT_PDB_H__
+#define __GIMP_PALETTE_SELECT_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_palettes_popup (const gchar *palette_callback,
+ const gchar *popup_title,
+ const gchar *initial_palette);
+gboolean gimp_palettes_close_popup (const gchar *palette_callback);
+gboolean gimp_palettes_set_popup (const gchar *palette_callback,
+ const gchar *palette_name);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PALETTE_SELECT_PDB_H__ */
diff --git a/libgimp/gimppaletteselectbutton.c b/libgimp/gimppaletteselectbutton.c
new file mode 100644
index 0000000..ad4957f
--- /dev/null
+++ b/libgimp/gimppaletteselectbutton.c
@@ -0,0 +1,488 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppaletteselectbutton.c
+ * Copyright (C) 2004 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "gimp.h"
+
+#include "gimpuitypes.h"
+#include "gimppaletteselectbutton.h"
+#include "gimpuimarshal.h"
+
+#include "libgimp-intl.h"
+
+
+/**
+ * SECTION: gimppaletteselectbutton
+ * @title: GimpPaletteSelect
+ * @short_description: A button which pops up a palette select dialog.
+ *
+ * A button which pops up a palette select dialog.
+ **/
+
+
+#define GIMP_PALETTE_SELECT_BUTTON_GET_PRIVATE(obj) ((GimpPaletteSelectButtonPrivate *) gimp_palette_select_button_get_instance_private ((GimpPaletteSelectButton *) (obj)))
+
+typedef struct _GimpPaletteSelectButtonPrivate GimpPaletteSelectButtonPrivate;
+
+struct _GimpPaletteSelectButtonPrivate
+{
+ gchar *title;
+
+ gchar *palette_name; /* Local copy */
+
+ GtkWidget *inside;
+ GtkWidget *label;
+};
+
+enum
+{
+ PALETTE_SET,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_TITLE,
+ PROP_PALETTE_NAME
+};
+
+
+/* local function prototypes */
+
+static void gimp_palette_select_button_finalize (GObject *object);
+
+static void gimp_palette_select_button_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_palette_select_button_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_palette_select_button_clicked (GimpPaletteSelectButton *button);
+
+static void gimp_palette_select_button_callback (const gchar *palette_name,
+ gboolean dialog_closing,
+ gpointer user_data);
+
+static void gimp_palette_select_drag_data_received (GimpPaletteSelectButton *button,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time);
+
+static GtkWidget * gimp_palette_select_button_create_inside (GimpPaletteSelectButton *palette_button);
+
+
+static const GtkTargetEntry target = { "application/x-gimp-palette-name", 0 };
+
+static guint palette_button_signals[LAST_SIGNAL] = { 0 };
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpPaletteSelectButton, gimp_palette_select_button,
+ GIMP_TYPE_SELECT_BUTTON)
+
+
+static void
+gimp_palette_select_button_class_init (GimpPaletteSelectButtonClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpSelectButtonClass *select_button_class = GIMP_SELECT_BUTTON_CLASS (klass);
+
+ object_class->finalize = gimp_palette_select_button_finalize;
+ object_class->set_property = gimp_palette_select_button_set_property;
+ object_class->get_property = gimp_palette_select_button_get_property;
+
+ select_button_class->select_destroy = gimp_palette_select_destroy;
+
+ klass->palette_set = NULL;
+
+ /**
+ * GimpPaletteSelectButton:title:
+ *
+ * The title to be used for the palette selection popup dialog.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class, PROP_TITLE,
+ g_param_spec_string ("title",
+ "Title",
+ "The title to be used for the palette selection popup dialog",
+ _("Palette Selection"),
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * GimpPaletteSelectButton:palette-name:
+ *
+ * The name of the currently selected palette.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class, PROP_PALETTE_NAME,
+ g_param_spec_string ("palette-name",
+ "Palette name",
+ "The name of the currently selected palette",
+ NULL,
+ GIMP_PARAM_READWRITE));
+
+ /**
+ * GimpPaletteSelectButton::palette-set:
+ * @widget: the object which received the signal.
+ * @palette_name: the name of the currently selected palette.
+ * @dialog_closing: whether the dialog was closed or not.
+ *
+ * The ::palette-set signal is emitted when the user selects a palette.
+ *
+ * Since: 2.4
+ */
+ palette_button_signals[PALETTE_SET] =
+ g_signal_new ("palette-set",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpPaletteSelectButtonClass, palette_set),
+ NULL, NULL,
+ _gimpui_marshal_VOID__STRING_BOOLEAN,
+ G_TYPE_NONE, 2,
+ G_TYPE_STRING,
+ G_TYPE_BOOLEAN);
+}
+
+static void
+gimp_palette_select_button_init (GimpPaletteSelectButton *button)
+{
+ GimpPaletteSelectButtonPrivate *priv;
+
+ priv = GIMP_PALETTE_SELECT_BUTTON_GET_PRIVATE (button);
+
+ priv->palette_name = NULL;
+
+ priv->inside = gimp_palette_select_button_create_inside (button);
+ gtk_container_add (GTK_CONTAINER (button), priv->inside);
+}
+
+/**
+ * gimp_palette_select_button_new:
+ * @title: Title of the dialog to use or %NULL to use the default title.
+ * @palette_name: Initial palette name.
+ *
+ * Creates a new #GtkWidget that completely controls the selection of
+ * a palette. This widget is suitable for placement in a table in a
+ * plug-in dialog.
+ *
+ * Returns: A #GtkWidget that you can use in your UI.
+ *
+ * Since: 2.4
+ */
+GtkWidget *
+gimp_palette_select_button_new (const gchar *title,
+ const gchar *palette_name)
+{
+ GtkWidget *button;
+
+ if (title)
+ button = g_object_new (GIMP_TYPE_PALETTE_SELECT_BUTTON,
+ "title", title,
+ "palette-name", palette_name,
+ NULL);
+ else
+ button = g_object_new (GIMP_TYPE_PALETTE_SELECT_BUTTON,
+ "palette-name", palette_name,
+ NULL);
+
+ return button;
+}
+
+/**
+ * gimp_palette_select_button_get_palette:
+ * @button: A #GimpPaletteSelectButton
+ *
+ * Retrieves the name of currently selected palette.
+ *
+ * Returns: an internal copy of the palette name which must not be freed.
+ *
+ * Since: 2.4
+ */
+const gchar *
+gimp_palette_select_button_get_palette (GimpPaletteSelectButton *button)
+{
+ GimpPaletteSelectButtonPrivate *priv;
+
+ g_return_val_if_fail (GIMP_IS_PALETTE_SELECT_BUTTON (button), NULL);
+
+ priv = GIMP_PALETTE_SELECT_BUTTON_GET_PRIVATE (button);
+ return priv->palette_name;
+}
+
+/**
+ * gimp_palette_select_button_set_palette:
+ * @button: A #GimpPaletteSelectButton
+ * @palette_name: Palette name to set; %NULL means no change.
+ *
+ * Sets the current palette for the palette select button.
+ *
+ * Since: 2.4
+ */
+void
+gimp_palette_select_button_set_palette (GimpPaletteSelectButton *button,
+ const gchar *palette_name)
+{
+ GimpSelectButton *select_button;
+
+ g_return_if_fail (GIMP_IS_PALETTE_SELECT_BUTTON (button));
+
+ select_button = GIMP_SELECT_BUTTON (button);
+
+ if (select_button->temp_callback)
+ {
+ gimp_palettes_set_popup (select_button->temp_callback, palette_name);
+ }
+ else
+ {
+ gchar *name;
+ gint num_colors;
+
+ if (palette_name && *palette_name)
+ name = g_strdup (palette_name);
+ else
+ name = gimp_context_get_palette ();
+
+ if (gimp_palette_get_info (name, &num_colors))
+ gimp_palette_select_button_callback (name, FALSE, button);
+
+ g_free (name);
+ }
+}
+
+
+/* private functions */
+
+static void
+gimp_palette_select_button_finalize (GObject *object)
+{
+ GimpPaletteSelectButtonPrivate *priv;
+
+ priv = GIMP_PALETTE_SELECT_BUTTON_GET_PRIVATE (object);
+
+ g_clear_pointer (&priv->palette_name, g_free);
+ g_clear_pointer (&priv->title, g_free);
+
+ G_OBJECT_CLASS (gimp_palette_select_button_parent_class)->finalize (object);
+}
+
+static void
+gimp_palette_select_button_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpPaletteSelectButton *button;
+ GimpPaletteSelectButtonPrivate *priv;
+
+ button = GIMP_PALETTE_SELECT_BUTTON (object);
+ priv = GIMP_PALETTE_SELECT_BUTTON_GET_PRIVATE (button);
+
+ switch (property_id)
+ {
+ case PROP_TITLE:
+ priv->title = g_value_dup_string (value);
+ break;
+ case PROP_PALETTE_NAME:
+ gimp_palette_select_button_set_palette (button,
+ g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_palette_select_button_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpPaletteSelectButton *button;
+ GimpPaletteSelectButtonPrivate *priv;
+
+ button = GIMP_PALETTE_SELECT_BUTTON (object);
+ priv = GIMP_PALETTE_SELECT_BUTTON_GET_PRIVATE (button);
+
+ switch (property_id)
+ {
+ case PROP_TITLE:
+ g_value_set_string (value, priv->title);
+ break;
+ case PROP_PALETTE_NAME:
+ g_value_set_string (value, priv->palette_name);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_palette_select_button_callback (const gchar *palette_name,
+ gboolean dialog_closing,
+ gpointer user_data)
+{
+ GimpPaletteSelectButton *button;
+ GimpPaletteSelectButtonPrivate *priv;
+ GimpSelectButton *select_button;
+
+ button = GIMP_PALETTE_SELECT_BUTTON (user_data);
+
+ priv = GIMP_PALETTE_SELECT_BUTTON_GET_PRIVATE (button);
+ select_button = GIMP_SELECT_BUTTON (button);
+
+ g_free (priv->palette_name);
+ priv->palette_name = g_strdup (palette_name);
+
+ gtk_label_set_text (GTK_LABEL (priv->label), palette_name);
+
+ if (dialog_closing)
+ select_button->temp_callback = NULL;
+
+ g_signal_emit (button, palette_button_signals[PALETTE_SET], 0,
+ palette_name, dialog_closing);
+ g_object_notify (G_OBJECT (button), "palette-name");
+}
+
+static void
+gimp_palette_select_button_clicked (GimpPaletteSelectButton *button)
+{
+ GimpPaletteSelectButtonPrivate *priv;
+ GimpSelectButton *select_button;
+
+ priv = GIMP_PALETTE_SELECT_BUTTON_GET_PRIVATE (button);
+ select_button = GIMP_SELECT_BUTTON (button);
+
+ if (select_button->temp_callback)
+ {
+ /* calling gimp_palettes_set_popup() raises the dialog */
+ gimp_palettes_set_popup (select_button->temp_callback,
+ priv->palette_name);
+ }
+ else
+ {
+ select_button->temp_callback =
+ gimp_palette_select_new (priv->title, priv->palette_name,
+ gimp_palette_select_button_callback,
+ button);
+ }
+}
+
+static void
+gimp_palette_select_drag_data_received (GimpPaletteSelectButton *button,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time)
+{
+ gint length = gtk_selection_data_get_length (selection);
+ gchar *str;
+
+ if (gtk_selection_data_get_format (selection) != 8 || length < 1)
+ {
+ g_warning ("%s: received invalid palette data", G_STRFUNC);
+ return;
+ }
+
+ str = g_strndup ((const gchar *) gtk_selection_data_get_data (selection),
+ length);
+
+ if (g_utf8_validate (str, -1, NULL))
+ {
+ gint pid;
+ gpointer unused;
+ gint name_offset = 0;
+
+ if (sscanf (str, "%i:%p:%n", &pid, &unused, &name_offset) >= 2 &&
+ pid == gimp_getpid () && name_offset > 0)
+ {
+ gchar *name = str + name_offset;
+
+ gimp_palette_select_button_set_palette (button, name);
+ }
+ }
+
+ g_free (str);
+}
+
+static GtkWidget *
+gimp_palette_select_button_create_inside (GimpPaletteSelectButton *palette_button)
+{
+ GtkWidget *button;
+ GtkWidget *hbox;
+ GtkWidget *image;
+ GimpPaletteSelectButtonPrivate *priv;
+
+ priv = GIMP_PALETTE_SELECT_BUTTON_GET_PRIVATE (palette_button);
+
+ gtk_widget_push_composite_child ();
+
+ button = gtk_button_new ();
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+ gtk_container_add (GTK_CONTAINER (button), hbox);
+
+ image = gtk_image_new_from_icon_name (GIMP_ICON_PALETTE,
+ GTK_ICON_SIZE_BUTTON);
+ gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
+
+ priv->label = gtk_label_new (priv->palette_name);
+ gtk_box_pack_start (GTK_BOX (hbox), priv->label, TRUE, TRUE, 4);
+
+ gtk_widget_show_all (button);
+
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (gimp_palette_select_button_clicked),
+ palette_button);
+
+ gtk_drag_dest_set (GTK_WIDGET (button),
+ GTK_DEST_DEFAULT_HIGHLIGHT |
+ GTK_DEST_DEFAULT_MOTION |
+ GTK_DEST_DEFAULT_DROP,
+ &target, 1,
+ GDK_ACTION_COPY);
+
+ g_signal_connect_swapped (button, "drag-data-received",
+ G_CALLBACK (gimp_palette_select_drag_data_received),
+ palette_button);
+
+ gtk_widget_pop_composite_child ();
+
+ return button;
+}
diff --git a/libgimp/gimppaletteselectbutton.h b/libgimp/gimppaletteselectbutton.h
new file mode 100644
index 0000000..15d411a
--- /dev/null
+++ b/libgimp/gimppaletteselectbutton.h
@@ -0,0 +1,79 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppaletteselectbutton.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_UI_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimpui.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PALETTE_SELECT_BUTTON_H__
+#define __GIMP_PALETTE_SELECT_BUTTON_H__
+
+#include <libgimp/gimpselectbutton.h>
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+#define GIMP_TYPE_PALETTE_SELECT_BUTTON (gimp_palette_select_button_get_type ())
+#define GIMP_PALETTE_SELECT_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_PALETTE_SELECT_BUTTON, GimpPaletteSelectButton))
+#define GIMP_PALETTE_SELECT_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_PALETTE_SELECT_BUTTON, GimpPaletteSelectButtonClass))
+#define GIMP_IS_PALETTE_SELECT_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_PALETTE_SELECT_BUTTON))
+#define GIMP_IS_PALETTE_SELECT_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_PALETTE_SELECT_BUTTON))
+#define GIMP_PALETTE_SELECT_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_PALETTE_SELECT_BUTTON, GimpPaletteSelectButtonClass))
+
+
+typedef struct _GimpPaletteSelectButtonClass GimpPaletteSelectButtonClass;
+
+struct _GimpPaletteSelectButton
+{
+ GimpSelectButton parent_instance;
+};
+
+struct _GimpPaletteSelectButtonClass
+{
+ GimpSelectButtonClass parent_class;
+
+ /* palette_set signal is emitted when palette is chosen */
+ void (* palette_set) (GimpPaletteSelectButton *button,
+ const gchar *palette_name,
+ gboolean dialog_closing);
+
+ /* Padding for future expansion */
+ void (*_gimp_reserved1) (void);
+ void (*_gimp_reserved2) (void);
+ void (*_gimp_reserved3) (void);
+ void (*_gimp_reserved4) (void);
+};
+
+
+GType gimp_palette_select_button_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_palette_select_button_new (const gchar *title,
+ const gchar *palette_name);
+
+const gchar * gimp_palette_select_button_get_palette (GimpPaletteSelectButton *button);
+void gimp_palette_select_button_set_palette (GimpPaletteSelectButton *button,
+ const gchar *palette_name);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PALETTE_SELECT_BUTTON_H__ */
diff --git a/libgimp/gimppaths_pdb.c b/libgimp/gimppaths_pdb.c
new file mode 100644
index 0000000..01ee26a
--- /dev/null
+++ b/libgimp/gimppaths_pdb.c
@@ -0,0 +1,560 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimppaths_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimppaths
+ * @title: gimppaths
+ * @short_description: Deprecated operations related to paths.
+ *
+ * Deprecated operations related to paths.
+ **/
+
+
+/**
+ * gimp_path_list:
+ * @image_ID: The image to list the paths from.
+ * @num_paths: The number of paths returned.
+ *
+ * Deprecated: Use gimp_image_get_vectors() instead.
+ *
+ * Returns: List of the paths belonging to this image. The returned
+ * value must be freed with g_strfreev().
+ **/
+gchar **
+gimp_path_list (gint32 image_ID,
+ gint *num_paths)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar **path_list = NULL;
+ gint i;
+
+ return_vals = gimp_run_procedure ("gimp-path-list",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ *num_paths = 0;
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ *num_paths = return_vals[1].data.d_int32;
+ if (*num_paths > 0)
+ {
+ path_list = g_new0 (gchar *, *num_paths + 1);
+ for (i = 0; i < *num_paths; i++)
+ path_list[i] = g_strdup (return_vals[2].data.d_stringarray[i]);
+ }
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return path_list;
+}
+
+/**
+ * gimp_path_get_current:
+ * @image_ID: The image to get the current path from.
+ *
+ * Deprecated: Use gimp_image_get_active_vectors() instead.
+ *
+ * Returns: The name of the current path.
+ **/
+gchar *
+gimp_path_get_current (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-path-get-current",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ name = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return name;
+}
+
+/**
+ * gimp_path_set_current:
+ * @image_ID: The image in which a path will become current.
+ * @name: The name of the path to make current.
+ *
+ * Deprecated: Use gimp_image_set_active_vectors() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_path_set_current (gint32 image_ID,
+ const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-path-set-current",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_path_delete:
+ * @image_ID: The image to delete the path from.
+ * @name: The name of the path to delete.
+ *
+ * Deprecated: Use gimp_image_remove_vectors() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_path_delete (gint32 image_ID,
+ const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-path-delete",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_path_get_points:
+ * @image_ID: The image to list the paths from.
+ * @name: The name of the path whose points should be listed.
+ * @path_closed: Return if the path is closed. (0 = path open, 1 = path closed).
+ * @num_path_point_details: The number of points returned. Each point is made up of (x, y, pnt_type) of floats.
+ * @points_pairs: The points in the path represented as 3 floats. The first is the x pos, next is the y pos, last is the type of the pnt. The type field is dependent on the path type. For beziers (type 1 paths) the type can either be (1.0 = BEZIER_ANCHOR, 2.0 = BEZIER_CONTROL, 3.0 = BEZIER_MOVE). Note all points are returned in pixel resolution.
+ *
+ * Deprecated: Use gimp_vectors_stroke_get_points() instead.
+ *
+ * Returns: The type of the path. Currently only one type (1 = Bezier)
+ * is supported.
+ **/
+gint
+gimp_path_get_points (gint32 image_ID,
+ const gchar *name,
+ gint *path_closed,
+ gint *num_path_point_details,
+ gdouble **points_pairs)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint path_type = 0;
+
+ return_vals = gimp_run_procedure ("gimp-path-get-points",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ *num_path_point_details = 0;
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ path_type = return_vals[1].data.d_int32;
+ *path_closed = return_vals[2].data.d_int32;
+ *num_path_point_details = return_vals[3].data.d_int32;
+ *points_pairs = g_new (gdouble, *num_path_point_details);
+ memcpy (*points_pairs,
+ return_vals[4].data.d_floatarray,
+ *num_path_point_details * sizeof (gdouble));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return path_type;
+}
+
+/**
+ * gimp_path_set_points:
+ * @image_ID: The image to set the paths in.
+ * @name: The name of the path to create. If it exists then a unique name will be created - query the list of paths if you want to make sure that the name of the path you create is unique. This will be set as the current path.
+ * @ptype: The type of the path. Currently only one type (1 = Bezier) is supported.
+ * @num_path_points: The number of elements in the array, i.e. the number of points in the path * 3. Each point is made up of (x, y, type) of floats. Currently only the creation of bezier curves is allowed. The type parameter must be set to (1) to indicate a BEZIER type curve. Note that for BEZIER curves, points must be given in the following order: ACCACCAC... If the path is not closed the last control point is missed off. Points consist of three control points (control/anchor/control) so for a curve that is not closed there must be at least two points passed (2 x,y pairs). If (num_path_points/3) % 3 = 0 then the path is assumed to be closed and the points are ACCACCACCACC.
+ * @points_pairs: The points in the path represented as 3 floats. The first is the x pos, next is the y pos, last is the type of the pnt. The type field is dependent on the path type. For beziers (type 1 paths) the type can either be (1.0 = BEZIER_ANCHOR, 2.0 = BEZIER_CONTROL, 3.0= BEZIER_MOVE). Note all points are returned in pixel resolution.
+ *
+ * Deprecated: Use gimp_vectors_stroke_new_from_points() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_path_set_points (gint32 image_ID,
+ const gchar *name,
+ gint ptype,
+ gint num_path_points,
+ const gdouble *points_pairs)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-path-set-points",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, ptype,
+ GIMP_PDB_INT32, num_path_points,
+ GIMP_PDB_FLOATARRAY, points_pairs,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_path_stroke_current:
+ * @image_ID: The image which contains the path to stroke.
+ *
+ * Deprecated: Use gimp_edit_stroke_vectors() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_path_stroke_current (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-path-stroke-current",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_path_get_point_at_dist:
+ * @image_ID: The image the paths belongs to.
+ * @distance: The distance along the path.
+ * @y_point: The y position of the point.
+ * @slope: The slope (dy / dx) at the specified point.
+ *
+ * Deprecated: Use gimp_vectors_stroke_get_point_at_dist() instead.
+ *
+ * Returns: The x position of the point.
+ **/
+gint
+gimp_path_get_point_at_dist (gint32 image_ID,
+ gdouble distance,
+ gint *y_point,
+ gdouble *slope)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint x_point = 0;
+
+ return_vals = gimp_run_procedure ("gimp-path-get-point-at-dist",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_FLOAT, distance,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ x_point = return_vals[1].data.d_int32;
+ *y_point = return_vals[2].data.d_int32;
+ *slope = return_vals[3].data.d_float;
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return x_point;
+}
+
+/**
+ * gimp_path_get_tattoo:
+ * @image_ID: The image.
+ * @name: The name of the path whose tattoo should be obtained.
+ *
+ * Deprecated: Use gimp_vectors_get_tattoo() instead.
+ *
+ * Returns: The tattoo associated with the named path.
+ **/
+gint
+gimp_path_get_tattoo (gint32 image_ID,
+ const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint tattoo = 0;
+
+ return_vals = gimp_run_procedure ("gimp-path-get-tattoo",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ tattoo = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return tattoo;
+}
+
+/**
+ * gimp_path_set_tattoo:
+ * @image_ID: The image.
+ * @name: the name of the path whose tattoo should be set.
+ * @tattovalue: The tattoo associated with the name path. Only values returned from 'path_get_tattoo' should be used here.
+ *
+ * Deprecated: Use gimp_vectors_set_tattoo() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_path_set_tattoo (gint32 image_ID,
+ const gchar *name,
+ gint tattovalue)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-path-set-tattoo",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, tattovalue,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_get_path_by_tattoo:
+ * @image_ID: The image.
+ * @tattoo: The tattoo of the required path.
+ *
+ * Deprecated: Use gimp_image_get_vectors_by_tattoo() instead.
+ *
+ * Returns: The name of the path with the specified tattoo.
+ **/
+gchar *
+gimp_get_path_by_tattoo (gint32 image_ID,
+ gint tattoo)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-get-path-by-tattoo",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, tattoo,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ name = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return name;
+}
+
+/**
+ * gimp_path_get_locked:
+ * @image_ID: The image.
+ * @name: The name of the path whose locked status should be obtained.
+ *
+ * Deprecated: Use gimp_vectors_get_linked() instead.
+ *
+ * Returns: TRUE if the path is locked, FALSE otherwise.
+ **/
+gboolean
+gimp_path_get_locked (gint32 image_ID,
+ const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean locked = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-path-get-locked",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ locked = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return locked;
+}
+
+/**
+ * gimp_path_set_locked:
+ * @image_ID: The image.
+ * @name: the name of the path whose locked status should be set.
+ * @locked: Whether the path is locked.
+ *
+ * Deprecated: Use gimp_vectors_set_linked() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_path_set_locked (gint32 image_ID,
+ const gchar *name,
+ gboolean locked)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-path-set-locked",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, locked,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_path_to_selection:
+ * @image_ID: The image.
+ * @name: The name of the path which should be made into selection.
+ * @op: The desired operation with current selection.
+ * @antialias: Antialias selection.
+ * @feather: Feather selection.
+ * @feather_radius_x: Feather radius x.
+ * @feather_radius_y: Feather radius y.
+ *
+ * Deprecated: Use gimp_vectors_to_selection() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_path_to_selection (gint32 image_ID,
+ const gchar *name,
+ GimpChannelOps op,
+ gboolean antialias,
+ gboolean feather,
+ gdouble feather_radius_x,
+ gdouble feather_radius_y)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-path-to-selection",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_INT32, op,
+ GIMP_PDB_INT32, antialias,
+ GIMP_PDB_INT32, feather,
+ GIMP_PDB_FLOAT, feather_radius_x,
+ GIMP_PDB_FLOAT, feather_radius_y,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_path_import:
+ * @image_ID: The image.
+ * @filename: The name of the SVG file to import.
+ * @merge: Merge paths into a single vectors object.
+ * @scale: Scale the SVG to image dimensions.
+ *
+ * Deprecated: Use gimp_vectors_import_from_file() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_path_import (gint32 image_ID,
+ const gchar *filename,
+ gboolean merge,
+ gboolean scale)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-path-import",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_STRING, filename,
+ GIMP_PDB_INT32, merge,
+ GIMP_PDB_INT32, scale,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimppaths_pdb.h b/libgimp/gimppaths_pdb.h
new file mode 100644
index 0000000..62df9a6
--- /dev/null
+++ b/libgimp/gimppaths_pdb.h
@@ -0,0 +1,99 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimppaths_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PATHS_PDB_H__
+#define __GIMP_PATHS_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+GIMP_DEPRECATED_FOR(gimp_image_get_vectors)
+gchar** gimp_path_list (gint32 image_ID,
+ gint *num_paths);
+GIMP_DEPRECATED_FOR(gimp_image_get_active_vectors)
+gchar* gimp_path_get_current (gint32 image_ID);
+GIMP_DEPRECATED_FOR(gimp_image_set_active_vectors)
+gboolean gimp_path_set_current (gint32 image_ID,
+ const gchar *name);
+GIMP_DEPRECATED_FOR(gimp_image_remove_vectors)
+gboolean gimp_path_delete (gint32 image_ID,
+ const gchar *name);
+GIMP_DEPRECATED_FOR(gimp_vectors_stroke_get_points)
+gint gimp_path_get_points (gint32 image_ID,
+ const gchar *name,
+ gint *path_closed,
+ gint *num_path_point_details,
+ gdouble **points_pairs);
+GIMP_DEPRECATED_FOR(gimp_vectors_stroke_new_from_points)
+gboolean gimp_path_set_points (gint32 image_ID,
+ const gchar *name,
+ gint ptype,
+ gint num_path_points,
+ const gdouble *points_pairs);
+GIMP_DEPRECATED_FOR(gimp_edit_stroke_vectors)
+gboolean gimp_path_stroke_current (gint32 image_ID);
+GIMP_DEPRECATED_FOR(gimp_vectors_stroke_get_point_at_dist)
+gint gimp_path_get_point_at_dist (gint32 image_ID,
+ gdouble distance,
+ gint *y_point,
+ gdouble *slope);
+GIMP_DEPRECATED_FOR(gimp_vectors_get_tattoo)
+gint gimp_path_get_tattoo (gint32 image_ID,
+ const gchar *name);
+GIMP_DEPRECATED_FOR(gimp_vectors_set_tattoo)
+gboolean gimp_path_set_tattoo (gint32 image_ID,
+ const gchar *name,
+ gint tattovalue);
+GIMP_DEPRECATED_FOR(gimp_image_get_vectors_by_tattoo)
+gchar* gimp_get_path_by_tattoo (gint32 image_ID,
+ gint tattoo);
+GIMP_DEPRECATED_FOR(gimp_vectors_get_linked)
+gboolean gimp_path_get_locked (gint32 image_ID,
+ const gchar *name);
+GIMP_DEPRECATED_FOR(gimp_vectors_set_linked)
+gboolean gimp_path_set_locked (gint32 image_ID,
+ const gchar *name,
+ gboolean locked);
+GIMP_DEPRECATED_FOR(gimp_vectors_to_selection)
+gboolean gimp_path_to_selection (gint32 image_ID,
+ const gchar *name,
+ GimpChannelOps op,
+ gboolean antialias,
+ gboolean feather,
+ gdouble feather_radius_x,
+ gdouble feather_radius_y);
+GIMP_DEPRECATED_FOR(gimp_vectors_import_from_file)
+gboolean gimp_path_import (gint32 image_ID,
+ const gchar *filename,
+ gboolean merge,
+ gboolean scale);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PATHS_PDB_H__ */
diff --git a/libgimp/gimppattern_pdb.c b/libgimp/gimppattern_pdb.c
new file mode 100644
index 0000000..827b70f
--- /dev/null
+++ b/libgimp/gimppattern_pdb.c
@@ -0,0 +1,147 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimppattern_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimppattern
+ * @title: gimppattern
+ * @short_description: Functions operating on a single pattern.
+ *
+ * Functions operating on a single pattern.
+ **/
+
+
+/**
+ * gimp_pattern_get_info:
+ * @name: The pattern name.
+ * @width: The pattern width.
+ * @height: The pattern height.
+ * @bpp: The pattern bpp.
+ *
+ * Retrieve information about the specified pattern.
+ *
+ * This procedure retrieves information about the specified pattern.
+ * This includes the pattern extents (width and height).
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_pattern_get_info (const gchar *name,
+ gint *width,
+ gint *height,
+ gint *bpp)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-pattern-get-info",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ *width = 0;
+ *height = 0;
+ *bpp = 0;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *width = return_vals[1].data.d_int32;
+ *height = return_vals[2].data.d_int32;
+ *bpp = return_vals[3].data.d_int32;
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_pattern_get_pixels:
+ * @name: The pattern name.
+ * @width: The pattern width.
+ * @height: The pattern height.
+ * @bpp: The pattern bpp.
+ * @num_color_bytes: Number of pattern bytes.
+ * @color_bytes: The pattern data.
+ *
+ * Retrieve information about the specified pattern (including pixels).
+ *
+ * This procedure retrieves information about the specified. This
+ * includes the pattern extents (width and height), its bpp and its
+ * pixel data.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_pattern_get_pixels (const gchar *name,
+ gint *width,
+ gint *height,
+ gint *bpp,
+ gint *num_color_bytes,
+ guint8 **color_bytes)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-pattern-get-pixels",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ *width = 0;
+ *height = 0;
+ *bpp = 0;
+ *num_color_bytes = 0;
+ *color_bytes = NULL;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *width = return_vals[1].data.d_int32;
+ *height = return_vals[2].data.d_int32;
+ *bpp = return_vals[3].data.d_int32;
+ *num_color_bytes = return_vals[4].data.d_int32;
+ *color_bytes = g_new (guint8, *num_color_bytes);
+ memcpy (*color_bytes,
+ return_vals[5].data.d_int8array,
+ *num_color_bytes * sizeof (guint8));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimppattern_pdb.h b/libgimp/gimppattern_pdb.h
new file mode 100644
index 0000000..7145798
--- /dev/null
+++ b/libgimp/gimppattern_pdb.h
@@ -0,0 +1,49 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimppattern_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PATTERN_PDB_H__
+#define __GIMP_PATTERN_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_pattern_get_info (const gchar *name,
+ gint *width,
+ gint *height,
+ gint *bpp);
+gboolean gimp_pattern_get_pixels (const gchar *name,
+ gint *width,
+ gint *height,
+ gint *bpp,
+ gint *num_color_bytes,
+ guint8 **color_bytes);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PATTERN_PDB_H__ */
diff --git a/libgimp/gimppatternmenu.c b/libgimp/gimppatternmenu.c
new file mode 100644
index 0000000..995ff05
--- /dev/null
+++ b/libgimp/gimppatternmenu.c
@@ -0,0 +1,151 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppatternmenu.c
+ * Copyright (C) 1998 Andy Thomas
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "gimp.h"
+
+#include "gimpuitypes.h"
+#include "gimppatternmenu.h"
+#include "gimppatternselectbutton.h"
+
+
+/**
+ * SECTION: gimppatternmenu
+ * @title: gimppatternmenu
+ * @short_description: A widget for selecting patterns.
+ *
+ * A widget for selecting patterns.
+ **/
+
+
+typedef struct
+{
+ GimpRunPatternCallback callback;
+ gpointer data;
+} CompatCallbackData;
+
+
+static void compat_callback (GimpPatternSelectButton *pattern_button,
+ const gchar *pattern_name,
+ gint width,
+ gint height,
+ gint bytes,
+ const guchar *mask_data,
+ gboolean dialog_closing,
+ CompatCallbackData *data);
+static void compat_callback_data_free (CompatCallbackData *data);
+
+
+/**
+ * gimp_pattern_select_widget_new:
+ * @title: Title of the dialog to use or %NULL to use the default title.
+ * @pattern_name: Initial pattern name or %NULL to use current selection.
+ * @callback: A function to call when the selected pattern changes.
+ * @data: A pointer to arbitrary data to be used in the call to @callback.
+ *
+ * Creates a new #GtkWidget that completely controls the selection of
+ * a pattern. This widget is suitable for placement in a table in a
+ * plug-in dialog.
+ *
+ * Returns: A #GtkWidget that you can use in your UI.
+ */
+GtkWidget *
+gimp_pattern_select_widget_new (const gchar *title,
+ const gchar *pattern_name,
+ GimpRunPatternCallback callback,
+ gpointer data)
+{
+ GtkWidget *pattern_button;
+ CompatCallbackData *compat_data;
+
+ g_return_val_if_fail (callback != NULL, NULL);
+
+ pattern_button = gimp_pattern_select_button_new (title, pattern_name);
+
+ compat_data = g_slice_new (CompatCallbackData);
+
+ compat_data->callback = callback;
+ compat_data->data = data;
+
+ g_signal_connect_data (pattern_button, "pattern-set",
+ G_CALLBACK (compat_callback),
+ compat_data,
+ (GClosureNotify) compat_callback_data_free, 0);
+
+ return pattern_button;
+}
+
+/**
+ * gimp_pattern_select_widget_close:
+ * @widget: A pattern select widget.
+ *
+ * Closes the popup window associated with @widget.
+ */
+void
+gimp_pattern_select_widget_close (GtkWidget *widget)
+{
+ g_return_if_fail (widget != NULL);
+
+ gimp_select_button_close_popup (GIMP_SELECT_BUTTON (widget));
+}
+
+/**
+ * gimp_pattern_select_widget_set:
+ * @widget: A pattern select widget.
+ * @pattern_name: Pattern name to set. NULL means no change.
+ *
+ * Sets the current pattern for the pattern select widget. Calls the
+ * callback function if one was supplied in the call to
+ * gimp_pattern_select_widget_new().
+ */
+void
+gimp_pattern_select_widget_set (GtkWidget *widget,
+ const gchar *pattern_name)
+{
+ g_return_if_fail (widget != NULL);
+
+ gimp_pattern_select_button_set_pattern (GIMP_PATTERN_SELECT_BUTTON (widget),
+ pattern_name);
+}
+
+
+static void
+compat_callback (GimpPatternSelectButton *pattern_button,
+ const gchar *pattern_name,
+ gint width,
+ gint height,
+ gint bpp,
+ const guchar *mask_data,
+ gboolean dialog_closing,
+ CompatCallbackData *data)
+{
+ data->callback (pattern_name, width, height, bpp, mask_data,
+ dialog_closing, data->data);
+}
+
+static void
+compat_callback_data_free (CompatCallbackData *data)
+{
+ g_slice_free (CompatCallbackData, data);
+}
diff --git a/libgimp/gimppatternmenu.h b/libgimp/gimppatternmenu.h
new file mode 100644
index 0000000..7a25099
--- /dev/null
+++ b/libgimp/gimppatternmenu.h
@@ -0,0 +1,49 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppatternmenu.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_UI_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimpui.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PATTERN_MENU_H__
+#define __GIMP_PATTERN_MENU_H__
+
+/* These functions are deprecated and should not be used in newly
+ * written code.
+ */
+
+G_BEGIN_DECLS
+
+GIMP_DEPRECATED_FOR(gimp_pattern_select_button_new)
+GtkWidget * gimp_pattern_select_widget_new (const gchar *title,
+ const gchar *pattern_name,
+ GimpRunPatternCallback callback,
+ gpointer data);
+
+GIMP_DEPRECATED_FOR(gimp_select_button_close_popup)
+void gimp_pattern_select_widget_close (GtkWidget *widget);
+GIMP_DEPRECATED_FOR(gimp_pattern_select_button_set_brush)
+void gimp_pattern_select_widget_set (GtkWidget *widget,
+ const gchar *pattern_name);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PATTERN_MENU_H__ */
diff --git a/libgimp/gimppatterns.c b/libgimp/gimppatterns.c
new file mode 100644
index 0000000..9638b6b
--- /dev/null
+++ b/libgimp/gimppatterns.c
@@ -0,0 +1,40 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimppatterns.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gimp.h"
+#include "gimppatterns.h"
+
+/**
+ * gimp_patterns_set_pattern:
+ * @name: The pattern name.
+ *
+ * This procedure is deprecated! Use gimp_context_set_pattern() instead.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+gimp_patterns_set_pattern (const gchar *name)
+{
+ return gimp_context_set_pattern (name);
+}
diff --git a/libgimp/gimppatterns.h b/libgimp/gimppatterns.h
new file mode 100644
index 0000000..5433576
--- /dev/null
+++ b/libgimp/gimppatterns.h
@@ -0,0 +1,37 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimppatterns.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PATTERNS_H__
+#define __GIMP_PATTERNS_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+GIMP_DEPRECATED_FOR(gimp_context_set_pattern)
+gboolean gimp_patterns_set_pattern (const gchar *name);
+
+G_END_DECLS
+
+#endif /* __GIMP_PATTERNS_H__ */
diff --git a/libgimp/gimppatterns_pdb.c b/libgimp/gimppatterns_pdb.c
new file mode 100644
index 0000000..e30579e
--- /dev/null
+++ b/libgimp/gimppatterns_pdb.c
@@ -0,0 +1,194 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimppatterns_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimppatterns
+ * @title: gimppatterns
+ * @short_description: Functions relating to patterns.
+ *
+ * Functions relating to patterns.
+ **/
+
+
+/**
+ * gimp_patterns_refresh:
+ *
+ * Refresh current patterns. This function always succeeds.
+ *
+ * This procedure retrieves all patterns currently in the user's
+ * pattern path and updates all pattern dialogs accordingly.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_patterns_refresh (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-patterns-refresh",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_patterns_get_list:
+ * @filter: An optional regular expression used to filter the list.
+ * @num_patterns: The number of patterns in the pattern list.
+ *
+ * Retrieve a complete listing of the available patterns.
+ *
+ * This procedure returns a complete listing of available GIMP
+ * patterns. Each name returned can be used as input to the
+ * gimp_context_set_pattern().
+ *
+ * Returns: The list of pattern names. The returned value must be freed
+ * with g_strfreev().
+ **/
+gchar **
+gimp_patterns_get_list (const gchar *filter,
+ gint *num_patterns)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar **pattern_list = NULL;
+ gint i;
+
+ return_vals = gimp_run_procedure ("gimp-patterns-get-list",
+ &nreturn_vals,
+ GIMP_PDB_STRING, filter,
+ GIMP_PDB_END);
+
+ *num_patterns = 0;
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ *num_patterns = return_vals[1].data.d_int32;
+ if (*num_patterns > 0)
+ {
+ pattern_list = g_new0 (gchar *, *num_patterns + 1);
+ for (i = 0; i < *num_patterns; i++)
+ pattern_list[i] = g_strdup (return_vals[2].data.d_stringarray[i]);
+ }
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return pattern_list;
+}
+
+/**
+ * gimp_patterns_get_pattern:
+ * @width: The pattern width.
+ * @height: The pattern height.
+ *
+ * Deprecated: Use gimp_context_get_pattern() instead.
+ *
+ * Returns: The pattern name.
+ **/
+gchar *
+gimp_patterns_get_pattern (gint *width,
+ gint *height)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-patterns-get-pattern",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ name = g_strdup (return_vals[1].data.d_string);
+ *width = return_vals[2].data.d_int32;
+ *height = return_vals[3].data.d_int32;
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return name;
+}
+
+/**
+ * gimp_patterns_get_pattern_data:
+ * @name: The pattern name (\"\" means currently active pattern).
+ * @width: The pattern width.
+ * @height: The pattern height.
+ * @mask_bpp: Pattern bytes per pixel.
+ * @length: Length of pattern mask data.
+ * @mask_data: The pattern mask data.
+ *
+ * Deprecated: Use gimp_pattern_get_pixels() instead.
+ *
+ * Returns: The pattern name.
+ **/
+gchar *
+gimp_patterns_get_pattern_data (const gchar *name,
+ gint *width,
+ gint *height,
+ gint *mask_bpp,
+ gint *length,
+ guint8 **mask_data)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *actual_name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-patterns-get-pattern-data",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ *length = 0;
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ actual_name = g_strdup (return_vals[1].data.d_string);
+ *width = return_vals[2].data.d_int32;
+ *height = return_vals[3].data.d_int32;
+ *mask_bpp = return_vals[4].data.d_int32;
+ *length = return_vals[5].data.d_int32;
+ *mask_data = g_new (guint8, *length);
+ memcpy (*mask_data,
+ return_vals[6].data.d_int8array,
+ *length * sizeof (guint8));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return actual_name;
+}
diff --git a/libgimp/gimppatterns_pdb.h b/libgimp/gimppatterns_pdb.h
new file mode 100644
index 0000000..a339a88
--- /dev/null
+++ b/libgimp/gimppatterns_pdb.h
@@ -0,0 +1,52 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimppatterns_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PATTERNS_PDB_H__
+#define __GIMP_PATTERNS_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_patterns_refresh (void);
+gchar** gimp_patterns_get_list (const gchar *filter,
+ gint *num_patterns);
+GIMP_DEPRECATED_FOR(gimp_context_get_pattern)
+gchar* gimp_patterns_get_pattern (gint *width,
+ gint *height);
+GIMP_DEPRECATED_FOR(gimp_pattern_get_pixels)
+gchar* gimp_patterns_get_pattern_data (const gchar *name,
+ gint *width,
+ gint *height,
+ gint *mask_bpp,
+ gint *length,
+ guint8 **mask_data);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PATTERNS_PDB_H__ */
diff --git a/libgimp/gimppatternselect.c b/libgimp/gimppatternselect.c
new file mode 100644
index 0000000..6cb2783
--- /dev/null
+++ b/libgimp/gimppatternselect.c
@@ -0,0 +1,230 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppatternselect.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+typedef struct
+{
+ gchar *pattern_callback;
+ guint idle_id;
+ gchar *pattern_name;
+ gint width;
+ gint height;
+ gint bytes;
+ guchar *pattern_mask_data;
+ GimpRunPatternCallback callback;
+ gboolean closing;
+ gpointer data;
+} GimpPatternData;
+
+
+/* local function prototypes */
+
+static void gimp_pattern_data_free (GimpPatternData *data);
+
+static void gimp_temp_pattern_run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+static gboolean gimp_temp_pattern_run_idle (GimpPatternData *pattern_data);
+
+
+/* private variables */
+
+static GHashTable *gimp_pattern_select_ht = NULL;
+
+
+/* public functions */
+
+const gchar *
+gimp_pattern_select_new (const gchar *title,
+ const gchar *pattern_name,
+ GimpRunPatternCallback callback,
+ gpointer data)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_STRING, "str", "String" },
+ { GIMP_PDB_INT32, "mask-width", "Pattern width" },
+ { GIMP_PDB_INT32, "mask-height", "Pattern height" },
+ { GIMP_PDB_INT32, "mask-bpp", "Pattern bytes per pixel" },
+ { GIMP_PDB_INT32, "mask-len", "Length of pattern mask data" },
+ { GIMP_PDB_INT8ARRAY,"mask-data", "The pattern mask data" },
+ { GIMP_PDB_INT32, "dialog-status", "If the dialog was closing "
+ "[0 = No, 1 = Yes]" }
+ };
+
+ gchar *pattern_callback = gimp_procedural_db_temp_name ();
+
+ gimp_install_temp_proc (pattern_callback,
+ "Temporary pattern popup callback procedure",
+ "",
+ "",
+ "",
+ "",
+ NULL,
+ "",
+ GIMP_TEMPORARY,
+ G_N_ELEMENTS (args), 0,
+ args, NULL,
+ gimp_temp_pattern_run);
+
+ if (gimp_patterns_popup (pattern_callback, title, pattern_name))
+ {
+ GimpPatternData *pattern_data;
+
+ gimp_extension_enable (); /* Allow callbacks to be watched */
+
+ /* Now add to hash table so we can find it again */
+ if (! gimp_pattern_select_ht)
+ {
+ gimp_pattern_select_ht =
+ g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free,
+ (GDestroyNotify) gimp_pattern_data_free);
+ }
+
+ pattern_data = g_slice_new0 (GimpPatternData);
+
+ pattern_data->pattern_callback = pattern_callback;
+ pattern_data->callback = callback;
+ pattern_data->data = data;
+
+ g_hash_table_insert (gimp_pattern_select_ht,
+ pattern_callback, pattern_data);
+
+ return pattern_callback;
+ }
+
+ gimp_uninstall_temp_proc (pattern_callback);
+ g_free (pattern_callback);
+
+ return NULL;
+}
+
+void
+gimp_pattern_select_destroy (const gchar *pattern_callback)
+{
+ GimpPatternData *pattern_data;
+
+ g_return_if_fail (pattern_callback != NULL);
+ g_return_if_fail (gimp_pattern_select_ht != NULL);
+
+ pattern_data = g_hash_table_lookup (gimp_pattern_select_ht,
+ pattern_callback);
+
+ if (! pattern_data)
+ {
+ g_warning ("Can't find internal pattern data");
+ return;
+ }
+
+ if (pattern_data->idle_id)
+ g_source_remove (pattern_data->idle_id);
+
+ g_free (pattern_data->pattern_name);
+ g_free (pattern_data->pattern_mask_data);
+
+ if (pattern_data->pattern_callback)
+ gimp_patterns_close_popup (pattern_data->pattern_callback);
+
+ gimp_uninstall_temp_proc (pattern_callback);
+
+ g_hash_table_remove (gimp_pattern_select_ht, pattern_callback);
+}
+
+
+/* private functions */
+
+static void
+gimp_pattern_data_free (GimpPatternData *data)
+{
+ g_slice_free (GimpPatternData, data);
+}
+
+static void
+gimp_temp_pattern_run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpPatternData *pattern_data;
+
+ pattern_data = g_hash_table_lookup (gimp_pattern_select_ht, name);
+
+ if (! pattern_data)
+ {
+ g_warning ("Can't find internal pattern data");
+ }
+ else
+ {
+ g_free (pattern_data->pattern_name);
+ g_free (pattern_data->pattern_mask_data);
+
+ pattern_data->pattern_name = g_strdup (param[0].data.d_string);
+ pattern_data->width = param[1].data.d_int32;
+ pattern_data->height = param[2].data.d_int32;
+ pattern_data->bytes = param[3].data.d_int32;
+ pattern_data->pattern_mask_data = g_memdup (param[5].data.d_int8array,
+ param[4].data.d_int32);
+ pattern_data->closing = param[6].data.d_int32;
+
+ if (! pattern_data->idle_id)
+ pattern_data->idle_id = g_idle_add ((GSourceFunc) gimp_temp_pattern_run_idle,
+ pattern_data);
+ }
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_SUCCESS;
+}
+
+static gboolean
+gimp_temp_pattern_run_idle (GimpPatternData *pattern_data)
+{
+ pattern_data->idle_id = 0;
+
+ if (pattern_data->callback)
+ pattern_data->callback (pattern_data->pattern_name,
+ pattern_data->width,
+ pattern_data->height,
+ pattern_data->bytes,
+ pattern_data->pattern_mask_data,
+ pattern_data->closing,
+ pattern_data->data);
+
+ if (pattern_data->closing)
+ {
+ gchar *pattern_callback = pattern_data->pattern_callback;
+
+ pattern_data->pattern_callback = NULL;
+ gimp_pattern_select_destroy (pattern_callback);
+ }
+
+ return FALSE;
+}
diff --git a/libgimp/gimppatternselect.h b/libgimp/gimppatternselect.h
new file mode 100644
index 0000000..66a91b7
--- /dev/null
+++ b/libgimp/gimppatternselect.h
@@ -0,0 +1,49 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppatternselect.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PATTERN_SELECT_H__
+#define __GIMP_PATTERN_SELECT_H__
+
+G_BEGIN_DECLS
+
+
+typedef void (* GimpRunPatternCallback) (const gchar *pattern_name,
+ gint width,
+ gint height,
+ gint bpp,
+ const guchar *mask_data,
+ gboolean dialog_closing,
+ gpointer user_data);
+
+
+const gchar * gimp_pattern_select_new (const gchar *title,
+ const gchar *pattern_name,
+ GimpRunPatternCallback callback,
+ gpointer data);
+void gimp_pattern_select_destroy (const gchar *pattern_callback);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PATTERN_SELECT_H__ */
diff --git a/libgimp/gimppatternselect_pdb.c b/libgimp/gimppatternselect_pdb.c
new file mode 100644
index 0000000..12ea47f
--- /dev/null
+++ b/libgimp/gimppatternselect_pdb.c
@@ -0,0 +1,131 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimppatternselect_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimppatternselect
+ * @title: gimppatternselect
+ * @short_description: Functions providing a pattern selection dialog.
+ *
+ * Functions providing a pattern selection dialog.
+ **/
+
+
+/**
+ * gimp_patterns_popup:
+ * @pattern_callback: The callback PDB proc to call when pattern selection is made.
+ * @popup_title: Title of the pattern selection dialog.
+ * @initial_pattern: The name of the pattern to set as the first selected.
+ *
+ * Invokes the Gimp pattern selection.
+ *
+ * This procedure opens the pattern selection dialog.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_patterns_popup (const gchar *pattern_callback,
+ const gchar *popup_title,
+ const gchar *initial_pattern)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-patterns-popup",
+ &nreturn_vals,
+ GIMP_PDB_STRING, pattern_callback,
+ GIMP_PDB_STRING, popup_title,
+ GIMP_PDB_STRING, initial_pattern,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_patterns_close_popup:
+ * @pattern_callback: The name of the callback registered for this pop-up.
+ *
+ * Close the pattern selection dialog.
+ *
+ * This procedure closes an opened pattern selection dialog.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_patterns_close_popup (const gchar *pattern_callback)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-patterns-close-popup",
+ &nreturn_vals,
+ GIMP_PDB_STRING, pattern_callback,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_patterns_set_popup:
+ * @pattern_callback: The name of the callback registered for this pop-up.
+ * @pattern_name: The name of the pattern to set as selected.
+ *
+ * Sets the current pattern in a pattern selection dialog.
+ *
+ * Sets the current pattern in a pattern selection dialog.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_patterns_set_popup (const gchar *pattern_callback,
+ const gchar *pattern_name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-patterns-set-popup",
+ &nreturn_vals,
+ GIMP_PDB_STRING, pattern_callback,
+ GIMP_PDB_STRING, pattern_name,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimppatternselect_pdb.h b/libgimp/gimppatternselect_pdb.h
new file mode 100644
index 0000000..50a0428
--- /dev/null
+++ b/libgimp/gimppatternselect_pdb.h
@@ -0,0 +1,45 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimppatternselect_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PATTERN_SELECT_PDB_H__
+#define __GIMP_PATTERN_SELECT_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_patterns_popup (const gchar *pattern_callback,
+ const gchar *popup_title,
+ const gchar *initial_pattern);
+gboolean gimp_patterns_close_popup (const gchar *pattern_callback);
+gboolean gimp_patterns_set_popup (const gchar *pattern_callback,
+ const gchar *pattern_name);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PATTERN_SELECT_PDB_H__ */
diff --git a/libgimp/gimppatternselectbutton.c b/libgimp/gimppatternselectbutton.c
new file mode 100644
index 0000000..af37fad
--- /dev/null
+++ b/libgimp/gimppatternselectbutton.c
@@ -0,0 +1,730 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppatternselectbutton.c
+ * Copyright (C) 1998 Andy Thomas
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "gimp.h"
+
+#include "gimpuitypes.h"
+#include "gimppatternselectbutton.h"
+#include "gimpuimarshal.h"
+
+#include "libgimp-intl.h"
+
+
+/**
+ * SECTION: gimppatternselectbutton
+ * @title: GimpPatternSelectButton
+ * @short_description: A button which pops up a pattern select dialog.
+ *
+ * A button which pops up a pattern select dialog.
+ **/
+
+
+#define CELL_SIZE 20
+
+
+#define GIMP_PATTERN_SELECT_BUTTON_GET_PRIVATE(obj) ((GimpPatternSelectButtonPrivate *) gimp_pattern_select_button_get_instance_private ((GimpPatternSelectButton *) (obj)))
+
+typedef struct _GimpPatternSelectButtonPrivate GimpPatternSelectButtonPrivate;
+
+struct _GimpPatternSelectButtonPrivate
+{
+ gchar *title;
+
+ gchar *pattern_name; /* Local copy */
+ gint width;
+ gint height;
+ gint bytes;
+ guchar *mask_data; /* local copy */
+
+ GtkWidget *inside;
+ GtkWidget *preview;
+ GtkWidget *popup;
+};
+
+enum
+{
+ PATTERN_SET,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_TITLE,
+ PROP_PATTERN_NAME
+};
+
+
+/* local function prototypes */
+
+static void gimp_pattern_select_button_finalize (GObject *object);
+
+static void gimp_pattern_select_button_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_pattern_select_button_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_pattern_select_button_clicked (GimpPatternSelectButton *button);
+
+static void gimp_pattern_select_button_callback (const gchar *pattern_name,
+ gint width,
+ gint height,
+ gint bytes,
+ const guchar *mask_data,
+ gboolean dialog_closing,
+ gpointer user_data);
+
+static void gimp_pattern_select_preview_resize (GimpPatternSelectButton *button);
+static gboolean gimp_pattern_select_preview_events (GtkWidget *widget,
+ GdkEvent *event,
+ GimpPatternSelectButton *button);
+static void gimp_pattern_select_preview_update (GtkWidget *preview,
+ gint width,
+ gint height,
+ gint bytes,
+ const guchar *mask_data);
+
+static void gimp_pattern_select_button_open_popup (GimpPatternSelectButton *button,
+ gint x,
+ gint y);
+static void gimp_pattern_select_button_close_popup (GimpPatternSelectButton *button);
+
+static void gimp_pattern_select_drag_data_received (GimpPatternSelectButton *button,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time);
+
+static GtkWidget * gimp_pattern_select_button_create_inside (GimpPatternSelectButton *button);
+
+
+static const GtkTargetEntry target = { "application/x-gimp-pattern-name", 0 };
+
+static guint pattern_button_signals[LAST_SIGNAL] = { 0 };
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpPatternSelectButton, gimp_pattern_select_button,
+ GIMP_TYPE_SELECT_BUTTON)
+
+static void
+gimp_pattern_select_button_class_init (GimpPatternSelectButtonClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpSelectButtonClass *select_button_class = GIMP_SELECT_BUTTON_CLASS (klass);
+
+ object_class->finalize = gimp_pattern_select_button_finalize;
+ object_class->set_property = gimp_pattern_select_button_set_property;
+ object_class->get_property = gimp_pattern_select_button_get_property;
+
+ select_button_class->select_destroy = gimp_pattern_select_destroy;
+
+ klass->pattern_set = NULL;
+
+ /**
+ * GimpPatternSelectButton:title:
+ *
+ * The title to be used for the pattern selection popup dialog.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class, PROP_TITLE,
+ g_param_spec_string ("title",
+ "Title",
+ "The title to be used for the pattern selection popup dialog",
+ _("Pattern Selection"),
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * GimpPatternSelectButton:pattern-name:
+ *
+ * The name of the currently selected pattern.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class, PROP_PATTERN_NAME,
+ g_param_spec_string ("pattern-name",
+ "Pattern name",
+ "The name of the currently selected pattern",
+ NULL,
+ GIMP_PARAM_READWRITE));
+
+ /**
+ * GimpPatternSelectButton::pattern-set:
+ * @widget: the object which received the signal.
+ * @pattern_name: the name of the currently selected pattern.
+ * @width: width of the pattern
+ * @height: height of the pattern
+ * @bpp: bpp of the pattern
+ * @mask_data: pattern mask data
+ * @dialog_closing: whether the dialog was closed or not.
+ *
+ * The ::pattern-set signal is emitted when the user selects a pattern.
+ *
+ * Since: 2.4
+ */
+ pattern_button_signals[PATTERN_SET] =
+ g_signal_new ("pattern-set",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpPatternSelectButtonClass, pattern_set),
+ NULL, NULL,
+ _gimpui_marshal_VOID__STRING_INT_INT_INT_POINTER_BOOLEAN,
+ G_TYPE_NONE, 6,
+ G_TYPE_STRING,
+ G_TYPE_INT,
+ G_TYPE_INT,
+ G_TYPE_INT,
+ G_TYPE_POINTER,
+ G_TYPE_BOOLEAN);
+}
+
+static void
+gimp_pattern_select_button_init (GimpPatternSelectButton *button)
+{
+ GimpPatternSelectButtonPrivate *priv;
+ gint mask_data_size;
+
+ priv = GIMP_PATTERN_SELECT_BUTTON_GET_PRIVATE (button);
+
+ priv->pattern_name = gimp_context_get_pattern ();
+ gimp_pattern_get_pixels (priv->pattern_name,
+ &priv->width,
+ &priv->height,
+ &priv->bytes,
+ &mask_data_size,
+ &priv->mask_data);
+
+ priv->inside = gimp_pattern_select_button_create_inside (button);
+ gtk_container_add (GTK_CONTAINER (button), priv->inside);
+
+ priv->popup = NULL;
+}
+
+/**
+ * gimp_pattern_select_button_new:
+ * @title: Title of the dialog to use or %NULL to use the default title.
+ * @pattern_name: Initial pattern name or %NULL to use current selection.
+ *
+ * Creates a new #GtkWidget that completely controls the selection of
+ * a pattern. This widget is suitable for placement in a table in a
+ * plug-in dialog.
+ *
+ * Returns: A #GtkWidget that you can use in your UI.
+ *
+ * Since: 2.4
+ */
+GtkWidget *
+gimp_pattern_select_button_new (const gchar *title,
+ const gchar *pattern_name)
+{
+ GtkWidget *button;
+
+ if (title)
+ button = g_object_new (GIMP_TYPE_PATTERN_SELECT_BUTTON,
+ "title", title,
+ "pattern-name", pattern_name,
+ NULL);
+ else
+ button = g_object_new (GIMP_TYPE_PATTERN_SELECT_BUTTON,
+ "pattern-name", pattern_name,
+ NULL);
+
+ return button;
+}
+
+/**
+ * gimp_pattern_select_button_get_pattern:
+ * @button: A #GimpPatternSelectButton
+ *
+ * Retrieves the name of currently selected pattern.
+ *
+ * Returns: an internal copy of the pattern name which must not be freed.
+ *
+ * Since: 2.4
+ */
+const gchar *
+gimp_pattern_select_button_get_pattern (GimpPatternSelectButton *button)
+{
+ GimpPatternSelectButtonPrivate *priv;
+
+ g_return_val_if_fail (GIMP_IS_PATTERN_SELECT_BUTTON (button), NULL);
+
+ priv = GIMP_PATTERN_SELECT_BUTTON_GET_PRIVATE (button);
+ return priv->pattern_name;
+}
+
+/**
+ * gimp_pattern_select_button_set_pattern:
+ * @button: A #GimpPatternSelectButton
+ * @pattern_name: Pattern name to set; %NULL means no change.
+ *
+ * Sets the current pattern for the pattern select button.
+ *
+ * Since: 2.4
+ */
+void
+gimp_pattern_select_button_set_pattern (GimpPatternSelectButton *button,
+ const gchar *pattern_name)
+{
+ GimpSelectButton *select_button;
+
+ g_return_if_fail (GIMP_IS_PATTERN_SELECT_BUTTON (button));
+
+ select_button = GIMP_SELECT_BUTTON (button);
+
+ if (select_button->temp_callback)
+ {
+ gimp_patterns_set_popup (select_button->temp_callback, pattern_name);
+ }
+ else
+ {
+ gchar *name;
+ gint width;
+ gint height;
+ gint bytes;
+ gint mask_data_size;
+ guint8 *mask_data;
+
+ if (pattern_name && *pattern_name)
+ name = g_strdup (pattern_name);
+ else
+ name = gimp_context_get_pattern ();
+
+ if (gimp_pattern_get_pixels (name,
+ &width,
+ &height,
+ &bytes,
+ &mask_data_size,
+ &mask_data))
+ {
+ gimp_pattern_select_button_callback (name,
+ width, height, bytes, mask_data,
+ FALSE, button);
+
+ g_free (mask_data);
+ }
+
+ g_free (name);
+ }
+}
+
+
+/* private functions */
+
+static void
+gimp_pattern_select_button_finalize (GObject *object)
+{
+ GimpPatternSelectButtonPrivate *priv;
+
+ priv = GIMP_PATTERN_SELECT_BUTTON_GET_PRIVATE (object);
+
+ g_clear_pointer (&priv->pattern_name, g_free);
+ g_clear_pointer (&priv->mask_data, g_free);
+ g_clear_pointer (&priv->title, g_free);
+
+ G_OBJECT_CLASS (gimp_pattern_select_button_parent_class)->finalize (object);
+}
+
+static void
+gimp_pattern_select_button_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpPatternSelectButton *button;
+ GimpPatternSelectButtonPrivate *priv;
+
+ button = GIMP_PATTERN_SELECT_BUTTON (object);
+ priv = GIMP_PATTERN_SELECT_BUTTON_GET_PRIVATE (button);
+
+ switch (property_id)
+ {
+ case PROP_TITLE:
+ priv->title = g_value_dup_string (value);
+ break;
+ case PROP_PATTERN_NAME:
+ gimp_pattern_select_button_set_pattern (button,
+ g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_pattern_select_button_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpPatternSelectButton *button;
+ GimpPatternSelectButtonPrivate *priv;
+
+ button = GIMP_PATTERN_SELECT_BUTTON (object);
+ priv = GIMP_PATTERN_SELECT_BUTTON_GET_PRIVATE (button);
+
+ switch (property_id)
+ {
+ case PROP_TITLE:
+ g_value_set_string (value, priv->title);
+ break;
+ case PROP_PATTERN_NAME:
+ g_value_set_string (value, priv->pattern_name);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_pattern_select_button_callback (const gchar *pattern_name,
+ gint width,
+ gint height,
+ gint bytes,
+ const guchar *mask_data,
+ gboolean dialog_closing,
+ gpointer user_data)
+{
+ GimpPatternSelectButton *button;
+ GimpPatternSelectButtonPrivate *priv;
+ GimpSelectButton *select_button;
+
+ button = GIMP_PATTERN_SELECT_BUTTON (user_data);
+
+ priv = GIMP_PATTERN_SELECT_BUTTON_GET_PRIVATE (button);
+ select_button = GIMP_SELECT_BUTTON (button);
+
+ g_free (priv->pattern_name);
+ g_free (priv->mask_data);
+
+ priv->pattern_name = g_strdup (pattern_name);
+ priv->width = width;
+ priv->height = height;
+ priv->bytes = bytes;
+ priv->mask_data = g_memdup (mask_data, width * height * bytes);
+
+ gimp_pattern_select_preview_update (priv->preview,
+ width, height, bytes, mask_data);
+
+ if (dialog_closing)
+ select_button->temp_callback = NULL;
+
+ g_signal_emit (button, pattern_button_signals[PATTERN_SET], 0,
+ pattern_name, width, height, bytes, dialog_closing);
+ g_object_notify (G_OBJECT (button), "pattern-name");
+}
+
+static void
+gimp_pattern_select_button_clicked (GimpPatternSelectButton *button)
+{
+ GimpPatternSelectButtonPrivate *priv;
+ GimpSelectButton *select_button;
+
+ priv = GIMP_PATTERN_SELECT_BUTTON_GET_PRIVATE (button);
+ select_button = GIMP_SELECT_BUTTON (button);
+
+ if (select_button->temp_callback)
+ {
+ /* calling gimp_patterns_set_popup() raises the dialog */
+ gimp_patterns_set_popup (select_button->temp_callback,
+ priv->pattern_name);
+ }
+ else
+ {
+ select_button->temp_callback =
+ gimp_pattern_select_new (priv->title, priv->pattern_name,
+ gimp_pattern_select_button_callback,
+ button);
+ }
+}
+
+static void
+gimp_pattern_select_preview_resize (GimpPatternSelectButton *button)
+{
+ GimpPatternSelectButtonPrivate *priv;
+
+ priv = GIMP_PATTERN_SELECT_BUTTON_GET_PRIVATE (button);
+
+ if (priv->width > 0 && priv->height > 0)
+ gimp_pattern_select_preview_update (priv->preview,
+ priv->width,
+ priv->height,
+ priv->bytes,
+ priv->mask_data);
+}
+
+static gboolean
+gimp_pattern_select_preview_events (GtkWidget *widget,
+ GdkEvent *event,
+ GimpPatternSelectButton *button)
+{
+ GimpPatternSelectButtonPrivate *priv;
+ GdkEventButton *bevent;
+
+ priv = GIMP_PATTERN_SELECT_BUTTON_GET_PRIVATE (button);
+
+ if (priv->mask_data)
+ {
+ switch (event->type)
+ {
+ case GDK_BUTTON_PRESS:
+ bevent = (GdkEventButton *) event;
+
+ if (bevent->button == 1)
+ {
+ gtk_grab_add (widget);
+ gimp_pattern_select_button_open_popup (button,
+ bevent->x, bevent->y);
+ }
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ bevent = (GdkEventButton *) event;
+
+ if (bevent->button == 1)
+ {
+ gtk_grab_remove (widget);
+ gimp_pattern_select_button_close_popup (button);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+gimp_pattern_select_preview_update (GtkWidget *preview,
+ gint width,
+ gint height,
+ gint bytes,
+ const guchar *mask_data)
+{
+ GimpImageType type;
+
+ switch (bytes)
+ {
+ case 1: type = GIMP_GRAY_IMAGE; break;
+ case 2: type = GIMP_GRAYA_IMAGE; break;
+ case 3: type = GIMP_RGB_IMAGE; break;
+ case 4: type = GIMP_RGBA_IMAGE; break;
+ default:
+ return;
+ }
+
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview),
+ 0, 0, width, height,
+ type,
+ mask_data,
+ width * bytes);
+}
+
+static void
+gimp_pattern_select_button_open_popup (GimpPatternSelectButton *button,
+ gint x,
+ gint y)
+{
+ GimpPatternSelectButtonPrivate *priv;
+ GtkWidget *frame;
+ GtkWidget *preview;
+ GdkScreen *screen;
+ gint x_org;
+ gint y_org;
+ gint scr_w;
+ gint scr_h;
+
+ priv = GIMP_PATTERN_SELECT_BUTTON_GET_PRIVATE (button);
+
+ if (priv->popup)
+ gimp_pattern_select_button_close_popup (button);
+
+ if (priv->width <= CELL_SIZE && priv->height <= CELL_SIZE)
+ return;
+
+ screen = gtk_widget_get_screen (GTK_WIDGET (button));
+
+ priv->popup = gtk_window_new (GTK_WINDOW_POPUP);
+ gtk_window_set_type_hint (GTK_WINDOW (priv->popup), GDK_WINDOW_TYPE_HINT_DND);
+ gtk_window_set_screen (GTK_WINDOW (priv->popup), screen);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
+ gtk_container_add (GTK_CONTAINER (priv->popup), frame);
+ gtk_widget_show (frame);
+
+ preview = gimp_preview_area_new ();
+ gtk_widget_set_size_request (preview, priv->width, priv->height);
+ gtk_container_add (GTK_CONTAINER (frame), preview);
+ gtk_widget_show (preview);
+
+ /* decide where to put the popup */
+ gdk_window_get_origin (gtk_widget_get_window (priv->preview),
+ &x_org, &y_org);
+
+ scr_w = gdk_screen_get_width (screen);
+ scr_h = gdk_screen_get_height (screen);
+
+ x = x_org + x - (priv->width / 2);
+ y = y_org + y - (priv->height / 2);
+ x = (x < 0) ? 0 : x;
+ y = (y < 0) ? 0 : y;
+ x = (x + priv->width > scr_w) ? scr_w - priv->width : x;
+ y = (y + priv->height > scr_h) ? scr_h - priv->height : y;
+
+ gtk_window_move (GTK_WINDOW (priv->popup), x, y);
+
+ gtk_widget_show (priv->popup);
+
+ /* Draw the pattern */
+ gimp_pattern_select_preview_update (preview,
+ priv->width,
+ priv->height,
+ priv->bytes,
+ priv->mask_data);
+}
+
+static void
+gimp_pattern_select_button_close_popup (GimpPatternSelectButton *button)
+{
+ GimpPatternSelectButtonPrivate *priv;
+
+ priv = GIMP_PATTERN_SELECT_BUTTON_GET_PRIVATE (button);
+
+ if (priv->popup)
+ {
+ gtk_widget_destroy (priv->popup);
+ priv->popup = NULL;
+ }
+}
+
+static void
+gimp_pattern_select_drag_data_received (GimpPatternSelectButton *button,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time)
+{
+ gint length = gtk_selection_data_get_length (selection);
+ gchar *str;
+
+ if (gtk_selection_data_get_format (selection) != 8 || length < 1)
+ {
+ g_warning ("Received invalid pattern data!");
+ return;
+ }
+
+ str = g_strndup ((const gchar *) gtk_selection_data_get_data (selection),
+ length);
+
+ if (g_utf8_validate (str, -1, NULL))
+ {
+ gint pid;
+ gpointer unused;
+ gint name_offset = 0;
+
+ if (sscanf (str, "%i:%p:%n", &pid, &unused, &name_offset) >= 2 &&
+ pid == gimp_getpid () && name_offset > 0)
+ {
+ gchar *name = str + name_offset;
+
+ gimp_pattern_select_button_set_pattern (button, name);
+ }
+ }
+
+ g_free (str);
+}
+
+static GtkWidget *
+gimp_pattern_select_button_create_inside (GimpPatternSelectButton *pattern_button)
+{
+ GtkWidget *hbox;
+ GtkWidget *frame;
+ GtkWidget *button;
+ GimpPatternSelectButtonPrivate *priv;
+
+ priv = GIMP_PATTERN_SELECT_BUTTON_GET_PRIVATE (pattern_button);
+
+ gtk_widget_push_composite_child ();
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
+
+ priv->preview = gimp_preview_area_new ();
+ gtk_widget_add_events (priv->preview,
+ GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
+ gtk_widget_set_size_request (priv->preview, CELL_SIZE, CELL_SIZE);
+ gtk_container_add (GTK_CONTAINER (frame), priv->preview);
+
+ g_signal_connect_swapped (priv->preview, "size-allocate",
+ G_CALLBACK (gimp_pattern_select_preview_resize),
+ pattern_button);
+ g_signal_connect (priv->preview, "event",
+ G_CALLBACK (gimp_pattern_select_preview_events),
+ pattern_button);
+
+ gtk_drag_dest_set (GTK_WIDGET (priv->preview),
+ GTK_DEST_DEFAULT_HIGHLIGHT |
+ GTK_DEST_DEFAULT_MOTION |
+ GTK_DEST_DEFAULT_DROP,
+ &target, 1,
+ GDK_ACTION_COPY);
+
+ g_signal_connect_swapped (priv->preview, "drag-data-received",
+ G_CALLBACK (gimp_pattern_select_drag_data_received),
+ pattern_button);
+
+ button = gtk_button_new_with_mnemonic (_("_Browse..."));
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (gimp_pattern_select_button_clicked),
+ pattern_button);
+
+ gtk_widget_show_all (hbox);
+
+ gtk_widget_pop_composite_child ();
+
+ return hbox;
+}
diff --git a/libgimp/gimppatternselectbutton.h b/libgimp/gimppatternselectbutton.h
new file mode 100644
index 0000000..934a402
--- /dev/null
+++ b/libgimp/gimppatternselectbutton.h
@@ -0,0 +1,83 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppatternselectbutton.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_UI_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimpui.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PATTERN_SELECT_BUTTON_H__
+#define __GIMP_PATTERN_SELECT_BUTTON_H__
+
+#include <libgimp/gimpselectbutton.h>
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+#define GIMP_TYPE_PATTERN_SELECT_BUTTON (gimp_pattern_select_button_get_type ())
+#define GIMP_PATTERN_SELECT_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_PATTERN_SELECT_BUTTON, GimpPatternSelectButton))
+#define GIMP_PATTERN_SELECT_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_PATTERN_SELECT_BUTTON, GimpPatternSelectButtonClass))
+#define GIMP_IS_PATTERN_SELECT_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_PATTERN_SELECT_BUTTON))
+#define GIMP_IS_PATTERN_SELECT_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_PATTERN_SELECT_BUTTON))
+#define GIMP_PATTERN_SELECT_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_PATTERN_SELECT_BUTTON, GimpPatternSelectButtonClass))
+
+
+typedef struct _GimpPatternSelectButtonClass GimpPatternSelectButtonClass;
+
+struct _GimpPatternSelectButton
+{
+ GimpSelectButton parent_instance;
+};
+
+struct _GimpPatternSelectButtonClass
+{
+ GimpSelectButtonClass parent_class;
+
+ /* pattern_set signal is emitted when pattern is chosen */
+ void (* pattern_set) (GimpPatternSelectButton *button,
+ const gchar *pattern_name,
+ gint width,
+ gint height,
+ gint bpp,
+ const guchar *mask_data,
+ gboolean dialog_closing);
+
+ /* Padding for future expansion */
+ void (*_gimp_reserved1) (void);
+ void (*_gimp_reserved2) (void);
+ void (*_gimp_reserved3) (void);
+ void (*_gimp_reserved4) (void);
+};
+
+
+GType gimp_pattern_select_button_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_pattern_select_button_new (const gchar *title,
+ const gchar *pattern_name);
+
+const gchar * gimp_pattern_select_button_get_pattern (GimpPatternSelectButton *button);
+void gimp_pattern_select_button_set_pattern (GimpPatternSelectButton *button,
+ const gchar *pattern_name);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PATTERN_SELECT_BUTTON_H__ */
diff --git a/libgimp/gimppixbuf.c b/libgimp/gimppixbuf.c
new file mode 100644
index 0000000..bd3cb46
--- /dev/null
+++ b/libgimp/gimppixbuf.c
@@ -0,0 +1,302 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppixbuf.c
+ * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gimp.h"
+
+#include "gimppixbuf.h"
+
+
+/**
+ * SECTION: gimppixbuf
+ * @title: gimppixbuf
+ * @short_description: Get a thumbnail pixbuf for a drawable or image.
+ *
+ * Get a thumbnail pixbuf for a drawable or image.
+ **/
+
+
+static GdkPixbuf * gimp_pixbuf_from_data (guchar *data,
+ gint width,
+ gint height,
+ gint bpp,
+ GimpPixbufTransparency alpha);
+
+
+/**
+ * gimp_image_get_thumbnail:
+ * @image_ID: the image ID
+ * @width: the requested thumbnail width (<= 1024 pixels)
+ * @height: the requested thumbnail height (<= 1024 pixels)
+ * @alpha: how to handle an alpha channel
+ *
+ * Retrieves a thumbnail pixbuf for the image identified by @image_ID.
+ * The thumbnail will be not larger than the requested size.
+ *
+ * Return value: a new #GdkPixbuf
+ *
+ * Since: 2.2
+ **/
+GdkPixbuf *
+gimp_image_get_thumbnail (gint32 image_ID,
+ gint width,
+ gint height,
+ GimpPixbufTransparency alpha)
+{
+ gint thumb_width = width;
+ gint thumb_height = height;
+ gint thumb_bpp;
+ guchar *data;
+
+ g_return_val_if_fail (width > 0 && width <= 1024, NULL);
+ g_return_val_if_fail (height > 0 && height <= 1024, NULL);
+
+ data = gimp_image_get_thumbnail_data (image_ID,
+ &thumb_width,
+ &thumb_height,
+ &thumb_bpp);
+ if (data)
+ return gimp_pixbuf_from_data (data,
+ thumb_width, thumb_height, thumb_bpp,
+ alpha);
+ else
+ return NULL;
+}
+
+/**
+ * gimp_drawable_get_thumbnail:
+ * @drawable_ID: the drawable ID
+ * @width: the requested thumbnail width (<= 1024 pixels)
+ * @height: the requested thumbnail height (<= 1024 pixels)
+ * @alpha: how to handle an alpha channel
+ *
+ * Retrieves a thumbnail pixbuf for the drawable identified by
+ * @drawable_ID. The thumbnail will be not larger than the requested
+ * size.
+ *
+ * Return value: a new #GdkPixbuf
+ *
+ * Since: 2.2
+ **/
+GdkPixbuf *
+gimp_drawable_get_thumbnail (gint32 drawable_ID,
+ gint width,
+ gint height,
+ GimpPixbufTransparency alpha)
+{
+ gint thumb_width = width;
+ gint thumb_height = height;
+ gint thumb_bpp;
+ guchar *data;
+
+ g_return_val_if_fail (width > 0 && width <= 1024, NULL);
+ g_return_val_if_fail (height > 0 && height <= 1024, NULL);
+
+ data = gimp_drawable_get_thumbnail_data (drawable_ID,
+ &thumb_width,
+ &thumb_height,
+ &thumb_bpp);
+
+ if (data)
+ return gimp_pixbuf_from_data (data,
+ thumb_width, thumb_height, thumb_bpp,
+ alpha);
+
+ return NULL;
+}
+
+/**
+ * gimp_drawable_get_sub_thumbnail:
+ * @drawable_ID: the drawable ID
+ * @src_x: the x coordinate of the area
+ * @src_y: the y coordinate of the area
+ * @src_width: the width of the area
+ * @src_height: the height of the area
+ * @dest_width: the requested thumbnail width (<= 1024 pixels)
+ * @dest_height: the requested thumbnail height (<= 1024 pixels)
+ * @alpha: how to handle an alpha channel
+ *
+ * Retrieves a thumbnail pixbuf for the drawable identified by
+ * @drawable_ID. The thumbnail will be not larger than the requested
+ * size.
+ *
+ * Return value: a new #GdkPixbuf
+ *
+ * Since: 2.2
+ **/
+GdkPixbuf *
+gimp_drawable_get_sub_thumbnail (gint32 drawable_ID,
+ gint src_x,
+ gint src_y,
+ gint src_width,
+ gint src_height,
+ gint dest_width,
+ gint dest_height,
+ GimpPixbufTransparency alpha)
+{
+ gint thumb_width = dest_width;
+ gint thumb_height = dest_height;
+ gint thumb_bpp;
+ guchar *data;
+
+ g_return_val_if_fail (src_x >= 0, NULL);
+ g_return_val_if_fail (src_y >= 0, NULL);
+ g_return_val_if_fail (src_width > 0, NULL);
+ g_return_val_if_fail (src_height > 0, NULL);
+ g_return_val_if_fail (dest_width > 0 && dest_width <= 1024, NULL);
+ g_return_val_if_fail (dest_height > 0 && dest_height <= 1024, NULL);
+
+ data = gimp_drawable_get_sub_thumbnail_data (drawable_ID,
+ src_x, src_y,
+ src_width, src_height,
+ &thumb_width,
+ &thumb_height,
+ &thumb_bpp);
+
+ if (data)
+ return gimp_pixbuf_from_data (data,
+ thumb_width, thumb_height, thumb_bpp,
+ alpha);
+
+ return NULL;
+}
+
+
+/*
+ * The data that is passed to this function is either freed here or
+ * owned by the returned pixbuf.
+ */
+static GdkPixbuf *
+gimp_pixbuf_from_data (guchar *data,
+ gint width,
+ gint height,
+ gint bpp,
+ GimpPixbufTransparency alpha)
+{
+ GdkPixbuf *pixbuf;
+
+ switch (bpp)
+ {
+ case 1:
+ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, width, height);
+ {
+ guchar *src = data;
+ guchar *pixels = gdk_pixbuf_get_pixels (pixbuf);
+ gint rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+ gint y;
+
+ for (y = 0; y < height; y++)
+ {
+ guchar *dest = pixels;
+ gint x;
+
+ for (x = 0; x < width; x++, src += 1, dest += 3)
+ {
+ dest[0] = dest[1] = dest[2] = src[0];
+ }
+
+ pixels += rowstride;
+ }
+
+ g_free (data);
+ }
+ bpp = 3;
+ break;
+
+ case 2:
+ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height);
+ {
+ guchar *src = data;
+ guchar *pixels = gdk_pixbuf_get_pixels (pixbuf);
+ gint rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+ gint y;
+
+ for (y = 0; y < height; y++)
+ {
+ guchar *dest = pixels;
+ gint x;
+
+ for (x = 0; x < width; x++, src += 2, dest += 4)
+ {
+ dest[0] = dest[1] = dest[2] = src[0];
+ dest[3] = src[1];
+ }
+
+ pixels += rowstride;
+ }
+
+ g_free (data);
+ }
+ bpp = 4;
+ break;
+
+ case 3:
+ pixbuf = gdk_pixbuf_new_from_data (data,
+ GDK_COLORSPACE_RGB, FALSE, 8,
+ width, height, width * bpp,
+ (GdkPixbufDestroyNotify) g_free, NULL);
+ break;
+
+ case 4:
+ pixbuf = gdk_pixbuf_new_from_data (data,
+ GDK_COLORSPACE_RGB, TRUE, 8,
+ width, height, width * bpp,
+ (GdkPixbufDestroyNotify) g_free, NULL);
+ break;
+
+ default:
+ g_return_val_if_reached (NULL);
+ return NULL;
+ }
+
+ if (bpp == 4)
+ {
+ GdkPixbuf *tmp;
+ gint check_size = 0;
+
+ switch (alpha)
+ {
+ case GIMP_PIXBUF_KEEP_ALPHA:
+ return pixbuf;
+
+ case GIMP_PIXBUF_SMALL_CHECKS:
+ check_size = GIMP_CHECK_SIZE_SM;
+ break;
+
+ case GIMP_PIXBUF_LARGE_CHECKS:
+ check_size = GIMP_CHECK_SIZE;
+ break;
+ }
+
+ tmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, width, height);
+
+ gdk_pixbuf_composite_color (pixbuf, tmp,
+ 0, 0, width, height, 0, 0, 1.0, 1.0,
+ GDK_INTERP_NEAREST, 255,
+ 0, 0, check_size, 0x66666666, 0x99999999);
+
+ g_object_unref (pixbuf);
+ pixbuf = tmp;
+ }
+
+ return pixbuf;
+}
diff --git a/libgimp/gimppixbuf.h b/libgimp/gimppixbuf.h
new file mode 100644
index 0000000..b734784
--- /dev/null
+++ b/libgimp/gimppixbuf.h
@@ -0,0 +1,70 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppixbuf.h
+ * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __LIBGIMP_GIMP_PIXBUF_H__
+#define __LIBGIMP_GIMP_PIXBUF_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+/**
+ * GimpPixbufTransparency:
+ * @GIMP_PIXBUF_KEEP_ALPHA: Create a pixbuf with alpha
+ * @GIMP_PIXBUF_SMALL_CHECKS: Show transparency as small checks
+ * @GIMP_PIXBUF_LARGE_CHECKS: Show transparency as large checks
+ *
+ * How to deal with transparency when creating thubnail pixbufs from
+ * images and drawables.
+ **/
+typedef enum
+{
+ GIMP_PIXBUF_KEEP_ALPHA,
+ GIMP_PIXBUF_SMALL_CHECKS,
+ GIMP_PIXBUF_LARGE_CHECKS
+} GimpPixbufTransparency;
+
+
+GdkPixbuf * gimp_image_get_thumbnail (gint32 image_ID,
+ gint width,
+ gint height,
+ GimpPixbufTransparency alpha);
+GdkPixbuf * gimp_drawable_get_thumbnail (gint32 drawable_ID,
+ gint width,
+ gint height,
+ GimpPixbufTransparency alpha);
+GdkPixbuf * gimp_drawable_get_sub_thumbnail (gint32 drawable_ID,
+ gint src_x,
+ gint src_y,
+ gint src_width,
+ gint src_height,
+ gint dest_width,
+ gint dest_height,
+ GimpPixbufTransparency alpha);
+
+G_END_DECLS
+
+#endif /* __LIBGIMP_GIMP_PIXBUF_H__ */
diff --git a/libgimp/gimppixelfetcher.c b/libgimp/gimppixelfetcher.c
new file mode 100644
index 0000000..c71fdca
--- /dev/null
+++ b/libgimp/gimppixelfetcher.c
@@ -0,0 +1,334 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppixelfetcher.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#define GIMP_DISABLE_DEPRECATION_WARNINGS
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimppixelfetcher
+ * @title: gimppixelfetcher
+ * @short_description: Functions for operating on pixel regions.
+ *
+ * These functions provide neighbourhood-based algorithms which get
+ * dramatically slower on region boundaries, to the point where a
+ * special treatment for neighbourhoods which are completely inside a
+ * tile is called for. It hides the special treatment of tile borders,
+ * making plug-in code more readable and shorter.
+ **/
+
+
+struct _GimpPixelFetcher
+{
+ gint col, row;
+ gint img_width;
+ gint img_height;
+ gint sel_x1, sel_y1, sel_x2, sel_y2;
+ gint img_bpp;
+ gint tile_width, tile_height;
+ guchar bg_color[4];
+ GimpPixelFetcherEdgeMode mode;
+ GimpDrawable *drawable;
+ GimpTile *tile;
+ gboolean tile_dirty;
+ gboolean shadow;
+};
+
+
+/* local function prototypes */
+
+static guchar * gimp_pixel_fetcher_provide_tile (GimpPixelFetcher *pf,
+ gint x,
+ gint y);
+
+
+/* public functions */
+
+/**
+ * gimp_pixel_fetcher_new:
+ * @drawable: the #GimpDrawable the new region will be attached to.
+ * @shadow: a #gboolean indicating whether the region is attached to
+ * the shadow tiles or the real @drawable tiles.
+ *
+ * Initialize a pixel region from the drawable.
+ *
+ * Return value: a pointer to a #GimpPixelRgn structure (or NULL).
+ **/
+GimpPixelFetcher *
+gimp_pixel_fetcher_new (GimpDrawable *drawable,
+ gboolean shadow)
+{
+ GimpPixelFetcher *pf;
+ gint width;
+ gint height;
+ gint bpp;
+
+ g_return_val_if_fail (drawable != NULL, NULL);
+
+ width = gimp_drawable_width (drawable->drawable_id);
+ height = gimp_drawable_height (drawable->drawable_id);
+ bpp = gimp_drawable_bpp (drawable->drawable_id);
+
+ g_return_val_if_fail (width > 0 && height > 0 && bpp > 0, NULL);
+
+ pf = g_slice_new0 (GimpPixelFetcher);
+
+ gimp_drawable_mask_bounds (drawable->drawable_id,
+ &pf->sel_x1, &pf->sel_y1,
+ &pf->sel_x2, &pf->sel_y2);
+
+ pf->col = -1;
+ pf->row = -1;
+ pf->img_width = width;
+ pf->img_height = height;
+ pf->img_bpp = bpp;
+ pf->tile_width = gimp_tile_width ();
+ pf->tile_height = gimp_tile_height ();
+ pf->bg_color[0] = 0;
+ pf->bg_color[1] = 0;
+ pf->bg_color[2] = 0;
+ pf->bg_color[3] = 255;
+ pf->mode = GIMP_PIXEL_FETCHER_EDGE_NONE;
+ pf->drawable = drawable;
+ pf->tile = NULL;
+ pf->tile_dirty = FALSE;
+ pf->shadow = shadow;
+
+ return pf;
+}
+
+/**
+ * gimp_pixel_fetcher_destroy:
+ * @pf: a pointer to a previously initialized #GimpPixelFetcher.
+ *
+ * Close a previously initialized pixel region.
+ **/
+void
+gimp_pixel_fetcher_destroy (GimpPixelFetcher *pf)
+{
+ g_return_if_fail (pf != NULL);
+
+ if (pf->tile)
+ gimp_tile_unref (pf->tile, pf->tile_dirty);
+
+ g_slice_free (GimpPixelFetcher, pf);
+}
+
+/**
+ * gimp_pixel_fetcher_set_edge_mode:
+ * @pf: a pointer to a previously initialized #GimpPixelFetcher.
+ * @mode: the new edge mode from #GimpPixelFetcherEdgeMode.
+ *
+ * Change the edge mode of a previously initialized pixel region.
+ **/
+void
+gimp_pixel_fetcher_set_edge_mode (GimpPixelFetcher *pf,
+ GimpPixelFetcherEdgeMode mode)
+{
+ g_return_if_fail (pf != NULL);
+
+ pf->mode = mode;
+}
+
+/**
+ * gimp_pixel_fetcher_set_bg_color:
+ * @pf: a pointer to a previously initialized #GimpPixelFetcher.
+ * @color: the color to be used as bg color.
+ *
+ * Change the background color of a previously initialized pixel region.
+ **/
+void
+gimp_pixel_fetcher_set_bg_color (GimpPixelFetcher *pf,
+ const GimpRGB *color)
+{
+ g_return_if_fail (pf != NULL);
+ g_return_if_fail (color != NULL);
+
+ switch (pf->img_bpp)
+ {
+ case 2: pf->bg_color[1] = 255;
+ case 1:
+ pf->bg_color[0] = gimp_rgb_luminance_uchar (color);
+ break;
+
+ case 4: pf->bg_color[3] = 255;
+ case 3:
+ gimp_rgb_get_uchar (color,
+ pf->bg_color, pf->bg_color + 1, pf->bg_color + 2);
+ break;
+ }
+}
+
+/**
+ * gimp_pixel_fetcher_get_pixel:
+ * @pf: a pointer to a previously initialized #GimpPixelFetcher.
+ * @x: the x coordinate of the pixel to get.
+ * @y: the y coordinate of the pixel to get.
+ * @pixel: the memory location where to return the pixel.
+ *
+ * Get a pixel from the pixel region.
+ **/
+void
+gimp_pixel_fetcher_get_pixel (GimpPixelFetcher *pf,
+ gint x,
+ gint y,
+ guchar *pixel)
+{
+ guchar *p;
+ gint i;
+
+ g_return_if_fail (pf != NULL);
+ g_return_if_fail (pixel != NULL);
+
+ if (pf->mode == GIMP_PIXEL_FETCHER_EDGE_NONE &&
+ (x < pf->sel_x1 || x >= pf->sel_x2 ||
+ y < pf->sel_y1 || y >= pf->sel_y2))
+ {
+ return;
+ }
+
+ if (x < 0 || x >= pf->img_width ||
+ y < 0 || y >= pf->img_height)
+ {
+ switch (pf->mode)
+ {
+ case GIMP_PIXEL_FETCHER_EDGE_WRAP:
+ if (x < 0 || x >= pf->img_width)
+ {
+ x %= pf->img_width;
+
+ if (x < 0)
+ x += pf->img_width;
+ }
+
+ if (y < 0 || y >= pf->img_height)
+ {
+ y %= pf->img_height;
+
+ if (y < 0)
+ y += pf->img_height;
+ }
+ break;
+
+ case GIMP_PIXEL_FETCHER_EDGE_SMEAR:
+ x = CLAMP (x, 0, pf->img_width - 1);
+ y = CLAMP (y, 0, pf->img_height - 1);
+ break;
+
+ case GIMP_PIXEL_FETCHER_EDGE_BLACK:
+ for (i = 0; i < pf->img_bpp; i++)
+ pixel[i] = 0;
+ return;
+
+ case GIMP_PIXEL_FETCHER_EDGE_BACKGROUND:
+ for (i = 0; i < pf->img_bpp; i++)
+ pixel[i] = pf->bg_color[i];
+ return;
+
+ default:
+ return;
+ }
+ }
+
+ p = gimp_pixel_fetcher_provide_tile (pf, x, y);
+
+ i = pf->img_bpp;
+
+ do
+ {
+ *pixel++ = *p++;
+ }
+ while (--i);
+}
+
+/**
+ * gimp_pixel_fetcher_put_pixel:
+ * @pf: a pointer to a previously initialized #GimpPixelFetcher.
+ * @x: the x coordinate of the pixel to set.
+ * @y: the y coordinate of the pixel to set.
+ * @pixel: the pixel to set.
+ *
+ * Set a pixel in the pixel region.
+ **/
+void
+gimp_pixel_fetcher_put_pixel (GimpPixelFetcher *pf,
+ gint x,
+ gint y,
+ const guchar *pixel)
+{
+ guchar *p;
+ gint i;
+
+ g_return_if_fail (pf != NULL);
+ g_return_if_fail (pixel != NULL);
+
+ if (x < pf->sel_x1 || x >= pf->sel_x2 ||
+ y < pf->sel_y1 || y >= pf->sel_y2)
+ {
+ return;
+ }
+
+ p = gimp_pixel_fetcher_provide_tile (pf, x, y);
+
+ i = pf->img_bpp;
+
+ do
+ {
+ *p++ = *pixel++;
+ }
+ while (--i);
+
+ pf->tile_dirty = TRUE;
+}
+
+
+/* private functions */
+
+static guchar *
+gimp_pixel_fetcher_provide_tile (GimpPixelFetcher *pf,
+ gint x,
+ gint y)
+{
+ gint col, row;
+ gint coloff, rowoff;
+
+ col = x / pf->tile_width;
+ coloff = x % pf->tile_width;
+ row = y / pf->tile_height;
+ rowoff = y % pf->tile_height;
+
+ if ((col != pf->col) || (row != pf->row) || (pf->tile == NULL))
+ {
+ if (pf->tile != NULL)
+ gimp_tile_unref (pf->tile, pf->tile_dirty);
+
+ pf->tile = gimp_drawable_get_tile (pf->drawable, pf->shadow, row, col);
+ pf->tile_dirty = FALSE;
+ gimp_tile_ref (pf->tile);
+
+ pf->col = col;
+ pf->row = row;
+ }
+
+ return pf->tile->data + pf->img_bpp * (pf->tile->ewidth * rowoff + coloff);
+}
diff --git a/libgimp/gimppixelfetcher.h b/libgimp/gimppixelfetcher.h
new file mode 100644
index 0000000..f493613
--- /dev/null
+++ b/libgimp/gimppixelfetcher.h
@@ -0,0 +1,76 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppixelfetcher.h
+ * Contains all kinds of miscellaneous routines factored out from different
+ * plug-ins. They stay here until their API has crystalized a bit and we can
+ * put them into the file where they belong (Maurits Rijk
+ * <lpeek.mrijk@consunet.nl> if you want to blame someone for this mess)
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PIXEL_FETCHER_H__
+#define __GIMP_PIXEL_FETCHER_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+typedef enum
+{
+ GIMP_PIXEL_FETCHER_EDGE_NONE,
+ GIMP_PIXEL_FETCHER_EDGE_WRAP,
+ GIMP_PIXEL_FETCHER_EDGE_SMEAR,
+ GIMP_PIXEL_FETCHER_EDGE_BLACK,
+ GIMP_PIXEL_FETCHER_EDGE_BACKGROUND
+} GimpPixelFetcherEdgeMode;
+
+
+typedef struct _GimpPixelFetcher GimpPixelFetcher;
+
+
+GIMP_DEPRECATED
+GimpPixelFetcher * gimp_pixel_fetcher_new (GimpDrawable *drawable,
+ gboolean shadow);
+GIMP_DEPRECATED
+void gimp_pixel_fetcher_destroy (GimpPixelFetcher *pf);
+
+GIMP_DEPRECATED
+void gimp_pixel_fetcher_set_edge_mode (GimpPixelFetcher *pf,
+ GimpPixelFetcherEdgeMode mode);
+GIMP_DEPRECATED
+void gimp_pixel_fetcher_set_bg_color (GimpPixelFetcher *pf,
+ const GimpRGB *color);
+
+GIMP_DEPRECATED
+void gimp_pixel_fetcher_get_pixel (GimpPixelFetcher *pf,
+ gint x,
+ gint y,
+ guchar *pixel);
+GIMP_DEPRECATED
+void gimp_pixel_fetcher_put_pixel (GimpPixelFetcher *pf,
+ gint x,
+ gint y,
+ const guchar *pixel);
+
+G_END_DECLS
+
+#endif /* __GIMP_PIXEL_FETCHER_H__ */
diff --git a/libgimp/gimppixelrgn.c b/libgimp/gimppixelrgn.c
new file mode 100644
index 0000000..c553089
--- /dev/null
+++ b/libgimp/gimppixelrgn.c
@@ -0,0 +1,965 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppixelrgn.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdarg.h>
+
+#define GIMP_DISABLE_DEPRECATION_WARNINGS
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimppixelrgn
+ * @title: gimppixelrgn
+ * @short_description: Functions for operating on pixel regions.
+ *
+ * Functions for operating on pixel regions. These functions provide
+ * fast ways of accessing and modifying portions of a drawable.
+ **/
+
+
+#define TILE_WIDTH gimp_tile_width()
+#define TILE_HEIGHT gimp_tile_height()
+
+
+typedef struct _GimpPixelRgnHolder GimpPixelRgnHolder;
+typedef struct _GimpPixelRgnIterator GimpPixelRgnIterator;
+
+struct _GimpPixelRgnHolder
+{
+ GimpPixelRgn *pr;
+ guchar *original_data;
+ gint startx;
+ gint starty;
+ gint count;
+};
+
+struct _GimpPixelRgnIterator
+{
+ GSList *pixel_regions;
+ gint region_width;
+ gint region_height;
+ gint portion_width;
+ gint portion_height;
+ gint process_count;
+};
+
+
+static gint gimp_get_portion_width (GimpPixelRgnIterator *pri);
+static gint gimp_get_portion_height (GimpPixelRgnIterator *pri);
+static gpointer gimp_pixel_rgns_configure (GimpPixelRgnIterator *pri);
+static void gimp_pixel_rgn_configure (GimpPixelRgnHolder *prh,
+ GimpPixelRgnIterator *pri);
+
+/**
+ * gimp_pixel_rgn_init:
+ * @pr: a pointer to a #GimpPixelRgn variable.
+ * @drawable: the #GimpDrawable the new region will be attached to.
+ * @x: the x coordinate of the top-left pixel of the region in the
+ * @drawable.
+ * @y: the y coordinate of the top-left pixel of the region in the
+ * @drawable.
+ * @width: the width of the region.
+ * @height: the height of the region.
+ * @dirty: a #gboolean indicating whether the @drawable should be marked
+ * as "dirty".
+ * @shadow: a #gboolean indicating whether the region is attached to the
+ * shadow tiles or the real @drawable tiles.
+ *
+ * Initialize the pixel region pointed by @pr with the specified parameters.
+ *
+ * The @dirty and @shadow flags can be used as follows:
+ *
+ * - @dirty = FALSE, @shadow = FALSE: the region will be used to read
+ * the actual drawable datas. This
+ * is useful for save plug-ins or for
+ * filters.
+ *
+ * - @dirty = FALSE, @shadow = TRUE: the region will be used to read the
+ * shadow tiles. This is used in
+ * some filter plug-ins which operate
+ * in two passes such as gaussian
+ * blur. The first pass reads the
+ * actual drawable data and writes to
+ * the shadow tiles, and the second
+ * one reads from and writes to the
+ * shadow tiles.
+ *
+ * - @dirty = TRUE, @shadow = TRUE: the region will be used to write to
+ * the shadow tiles. It is common
+ * practice to write to the shadow
+ * tiles and then use
+ * gimp_drawable_merge_shadow() to
+ * merge the changes from the shadow
+ * tiles using the current selection
+ * as a mask.
+ *
+ * - @dirty = TRUE, @shadow = FALSE: the region will be used to directly
+ * change the drawable content. Don't
+ * do this, since this could prevent
+ * the Undo-System from working as
+ * expected.
+ **/
+void
+gimp_pixel_rgn_init (GimpPixelRgn *pr,
+ GimpDrawable *drawable,
+ gint x,
+ gint y,
+ gint width,
+ gint height,
+ gboolean dirty,
+ gboolean shadow)
+{
+ g_return_if_fail (pr != NULL);
+ g_return_if_fail (drawable != NULL);
+ g_return_if_fail (x >= 0 && x + width <= drawable->width);
+ g_return_if_fail (y >= 0 && y + height <= drawable->height);
+
+ pr->data = NULL;
+ pr->drawable = drawable;
+ pr->bpp = drawable->bpp;
+ pr->rowstride = 0;
+ pr->x = x;
+ pr->y = y;
+ pr->w = width;
+ pr->h = height;
+ pr->dirty = dirty;
+ pr->shadow = shadow;
+}
+
+/**
+ * gimp_pixel_rgn_resize:
+ * @pr: a pointer to a previously initialized #GimpPixelRgn.
+ * @x: the x coordinate of the new position of the region's
+ * top-left corner.
+ * @y: the y coordinate of the new position of the region's
+ * top-left corner.
+ * @width: the new width of the region.
+ * @height: the new height of the region.
+ *
+ * Change the position and size of a previously initialized pixel region.
+ **/
+void
+gimp_pixel_rgn_resize (GimpPixelRgn *pr,
+ gint x,
+ gint y,
+ gint width,
+ gint height)
+{
+ g_return_if_fail (pr != NULL && pr->drawable != NULL);
+ g_return_if_fail (x >= 0 && x + width <= pr->drawable->width);
+ g_return_if_fail (y >= 0 && y + height <= pr->drawable->height);
+
+ pr->x = x;
+ pr->y = y;
+ pr->w = width;
+ pr->h = height;
+}
+
+/**
+ * gimp_pixel_rgn_get_pixel:
+ * @pr: a pointer to a previously initialized #GimpPixelRgn.
+ * @buf: a pointer to an array of #guchar
+ * @x: the x coordinate of the wanted pixel (relative to the drawable)
+ * @y: the y coordinate of the wanted pixel (relative to the drawable)
+ *
+ * Fill the buffer pointed by @buf with the value of the pixel at (@x, @y)
+ * in the region @pr. @buf should be large enough to hold the pixel value
+ * (1 #guchar for an indexed or grayscale drawable, 2 #guchar for
+ * indexed with alpha or grayscale with alpha drawable, 3 #guchar for
+ * rgb drawable and 4 #guchar for rgb with alpha drawable.
+ **/
+void
+gimp_pixel_rgn_get_pixel (GimpPixelRgn *pr,
+ guchar *buf,
+ gint x,
+ gint y)
+{
+ GimpTile *tile;
+ const guchar *tile_data;
+ gint b;
+
+ g_return_if_fail (pr != NULL && pr->drawable != NULL);
+ g_return_if_fail (x >= 0 && x < pr->drawable->width);
+ g_return_if_fail (y >= 0 && y < pr->drawable->height);
+
+ tile = gimp_drawable_get_tile2 (pr->drawable, pr->shadow, x, y);
+ gimp_tile_ref (tile);
+
+ tile_data = (tile->data +
+ tile->bpp * (tile->ewidth * (y % TILE_HEIGHT) + (x % TILE_WIDTH)));
+
+ for (b = 0; b < tile->bpp; b++)
+ *buf++ = *tile_data++;
+
+ gimp_tile_unref (tile, FALSE);
+}
+
+/**
+ * gimp_pixel_rgn_get_row:
+ * @pr: a pointer to a previously initialized #GimpPixelRgn.
+ * @buf: a pointer to an array of #guchar
+ * @x: the x coordinate of the first pixel (relative to the drawable).
+ * @y: the y coordinate of the first pixel (relative to the drawable).
+ * @width: the number of pixels to get.
+ *
+ * Get several pixels of a region in a row. This function fills the buffer
+ * @buf with the values of the pixels from (@x, @y) to (@x+@width-1, @y).
+ * @buf should be large enough to hold all these values.
+ **/
+void
+gimp_pixel_rgn_get_row (GimpPixelRgn *pr,
+ guchar *buf,
+ gint x,
+ gint y,
+ gint width)
+{
+ gint end;
+
+ g_return_if_fail (pr != NULL && pr->drawable != NULL);
+ g_return_if_fail (buf != NULL);
+ g_return_if_fail (x >= 0 && x + width <= pr->drawable->width);
+ g_return_if_fail (y >= 0 && y < pr->drawable->height);
+ g_return_if_fail (width >= 0);
+
+ end = x + width;
+
+ while (x < end)
+ {
+ GimpTile *tile;
+ const guchar *tile_data;
+ gint inc, min;
+ gint boundary;
+
+ tile = gimp_drawable_get_tile2 (pr->drawable, pr->shadow, x, y);
+ gimp_tile_ref (tile);
+
+ tile_data = (tile->data +
+ tile->bpp * (tile->ewidth * (y % TILE_HEIGHT) + (x % TILE_WIDTH)));
+
+ boundary = x + (tile->ewidth - (x % TILE_WIDTH));
+
+ min = MIN (end, boundary);
+ inc = tile->bpp * (min - x);
+
+ memcpy (buf, tile_data, inc);
+
+ x = min;
+ buf += inc;
+
+ gimp_tile_unref (tile, FALSE);
+ }
+}
+
+/**
+ * gimp_pixel_rgn_get_col:
+ * @pr: a pointer to a previously initialized #GimpPixelRgn.
+ * @buf: a pointer to an array of #guchar
+ * @x: the x coordinate of the first pixel (relative to the drawable).
+ * @y: the y coordinate of the first pixel (relative to the drawable).
+ * @height: the number of pixels to get.
+ *
+ * Get several pixels of a region's column. This function fills the buffer
+ * @buf with the values of the pixels from (@x, @y) to (@x, @y+@height-1).
+ * @buf should be large enough to hold all these values.
+ *
+ **/
+void
+gimp_pixel_rgn_get_col (GimpPixelRgn *pr,
+ guchar *buf,
+ gint x,
+ gint y,
+ gint height)
+{
+ gint end;
+
+ g_return_if_fail (pr != NULL && pr->drawable != NULL);
+ g_return_if_fail (buf != NULL);
+ g_return_if_fail (x >= 0 && x < pr->drawable->width);
+ g_return_if_fail (y >= 0 && y + height <= pr->drawable->height);
+ g_return_if_fail (height >= 0);
+
+ end = y + height;
+
+ while (y < end)
+ {
+ GimpTile *tile;
+ const guchar *tile_data;
+ gint inc;
+ gint boundary;
+ gint b;
+
+ tile = gimp_drawable_get_tile2 (pr->drawable, pr->shadow, x, y);
+ gimp_tile_ref (tile);
+
+ tile_data = (tile->data +
+ tile->bpp * (tile->ewidth * (y % TILE_HEIGHT) + (x % TILE_WIDTH)));
+
+ boundary = y + (tile->eheight - (y % TILE_HEIGHT));
+ inc = tile->bpp * tile->ewidth;
+
+ for ( ; y < end && y < boundary; y++)
+ {
+ for (b = 0; b < tile->bpp; b++)
+ *buf++ = tile_data[b];
+
+ tile_data += inc;
+ }
+
+ gimp_tile_unref (tile, FALSE);
+ }
+}
+
+/**
+ * gimp_pixel_rgn_get_rect:
+ * @pr: a pointer to a previously initialized #GimpPixelRgn.
+ * @buf: a pointer to an array of #guchar
+ * @x: the x coordinate of the first pixel (relative to the drawable).
+ * @y: the y coordinate of the first pixel (relative to the drawable).
+ * @width: the width of the rectangle.
+ * @height: the height of the rectangle.
+ *
+ * Get all the pixel values from the rectangle defined by @x, @y, @width and
+ * @height. This function fills the buffer @buf with the values of the pixels
+ * from (@x, @y) to (@x+@width-1, @y+@height-1).
+ * @buf should be large enough to hold all these values (@width*@height*bpp).
+ **/
+void
+gimp_pixel_rgn_get_rect (GimpPixelRgn *pr,
+ guchar *buf,
+ gint x,
+ gint y,
+ gint width,
+ gint height)
+{
+ gulong bufstride;
+ gint xstart, ystart;
+ gint xend, yend;
+ gint xboundary;
+ gint yboundary;
+ gint xstep, ystep;
+ gint ty, bpp;
+
+ g_return_if_fail (pr != NULL && pr->drawable != NULL);
+ g_return_if_fail (buf != NULL);
+ g_return_if_fail (x >= 0 && x + width <= pr->drawable->width);
+ g_return_if_fail (y >= 0 && y + height <= pr->drawable->height);
+ g_return_if_fail (width >= 0);
+ g_return_if_fail (height >= 0);
+
+ bpp = pr->bpp;
+ bufstride = bpp * width;
+
+ xstart = x;
+ ystart = y;
+ xend = x + width;
+ yend = y + height;
+ ystep = 0;
+
+ while (y < yend)
+ {
+ x = xstart;
+
+ while (x < xend)
+ {
+ GimpTile *tile;
+
+ tile = gimp_drawable_get_tile2 (pr->drawable, pr->shadow, x, y);
+ gimp_tile_ref (tile);
+
+ xstep = tile->ewidth - (x % TILE_WIDTH);
+ ystep = tile->eheight - (y % TILE_HEIGHT);
+ xboundary = x + xstep;
+ yboundary = y + ystep;
+ xboundary = MIN (xboundary, xend);
+ yboundary = MIN (yboundary, yend);
+
+ for (ty = y; ty < yboundary; ty++)
+ {
+ const guchar *src;
+ guchar *dest;
+
+ src = (tile->data +
+ tile->bpp * (tile->ewidth * (ty % TILE_HEIGHT) + (x % TILE_WIDTH)));
+ dest = buf + bufstride * (ty - ystart) + bpp * (x - xstart);
+
+ memcpy (dest, src, (xboundary - x) * bpp);
+ }
+
+ gimp_tile_unref (tile, FALSE);
+ x += xstep;
+ }
+
+ y += ystep;
+ }
+}
+
+/**
+ * gimp_pixel_rgn_set_pixel:
+ * @pr: a pointer to a previously initialized #GimpPixelRgn.
+ * @buf: a pointer to an array of #guchar.
+ * @x: the x coordinate of the pixel (relative to the drawable).
+ * @y: the y coordinate of the pixel (relative to the drawable).
+ *
+ * Set the pixel at (@x, @y) to the values from @buf.
+ **/
+void
+gimp_pixel_rgn_set_pixel (GimpPixelRgn *pr,
+ const guchar *buf,
+ gint x,
+ gint y)
+{
+ GimpTile *tile;
+ guchar *tile_data;
+ gint b;
+
+ g_return_if_fail (pr != NULL && pr->drawable != NULL);
+ g_return_if_fail (buf != NULL);
+ g_return_if_fail (x >= 0 && x < pr->drawable->width);
+ g_return_if_fail (y >= 0 && y < pr->drawable->height);
+
+ tile = gimp_drawable_get_tile2 (pr->drawable, pr->shadow, x, y);
+ gimp_tile_ref (tile);
+
+ tile_data = tile->data + tile->bpp * (tile->ewidth *
+ (y % TILE_HEIGHT) + (x % TILE_WIDTH));
+
+ for (b = 0; b < tile->bpp; b++)
+ *tile_data++ = *buf++;
+
+ gimp_tile_unref (tile, TRUE);
+}
+
+/**
+ * gimp_pixel_rgn_set_row:
+ * @pr: a pointer to a previously initialized #GimpPixelRgn.
+ * @buf: a pointer to an array of #guchar
+ * @x: the x coordinate of the first pixel (relative to the drawable).
+ * @y: the y coordinate of the first pixel (relative to the drawable).
+ * @width: the number of pixels to set.
+ *
+ * Set several pixels of a region in a row. This function draws the pixels
+ * from (@x, @y) to (@x+@width-1, @y) using the values of the buffer @buf.
+ * @buf should be large enough to hold all these values.
+ **/
+void
+gimp_pixel_rgn_set_row (GimpPixelRgn *pr,
+ const guchar *buf,
+ gint x,
+ gint y,
+ gint width)
+{
+ GimpTile *tile;
+ guchar *tile_data;
+ gint inc, min;
+ gint end;
+ gint boundary;
+
+ g_return_if_fail (pr != NULL && pr->drawable != NULL);
+ g_return_if_fail (buf != NULL);
+ g_return_if_fail (x >= 0 && x + width <= pr->drawable->width);
+ g_return_if_fail (y >= 0 && y < pr->drawable->height);
+ g_return_if_fail (width >= 0);
+
+ end = x + width;
+
+ while (x < end)
+ {
+ tile = gimp_drawable_get_tile2 (pr->drawable, pr->shadow, x, y);
+ gimp_tile_ref (tile);
+
+ tile_data = (tile->data +
+ tile->bpp * (tile->ewidth * (y % TILE_HEIGHT) + (x % TILE_WIDTH)));
+
+ boundary = x + (tile->ewidth - (x % TILE_WIDTH));
+
+ min = MIN (end, boundary);
+ inc = tile->bpp * (min - x);
+
+ memcpy (tile_data, buf, inc);
+
+ x = min;
+ buf += inc;
+
+ gimp_tile_unref (tile, TRUE);
+ }
+}
+
+/**
+ * gimp_pixel_rgn_set_col:
+ * @pr: a pointer to a previously initialized #GimpPixelRgn.
+ * @buf: a pointer to an array of #guchar
+ * @x: the x coordinate of the first pixel (relative to the drawable).
+ * @y: the y coordinate of the first pixel (relative to the drawable).
+ * @height: the number of pixels to set.
+ *
+ * Set several pixels of a region's column. This function draws the pixels
+ * from (@x, @y) to (@x, @y+@height-1) using the values from the buffer @buf.
+ * @buf should be large enough to hold all these values.
+ **/
+void
+gimp_pixel_rgn_set_col (GimpPixelRgn *pr,
+ const guchar *buf,
+ gint x,
+ gint y,
+ gint height)
+{
+ gint end;
+
+ g_return_if_fail (pr != NULL && pr->drawable != NULL);
+ g_return_if_fail (buf != NULL);
+ g_return_if_fail (x >= 0 && x < pr->drawable->width);
+ g_return_if_fail (y >= 0 && y + height <= pr->drawable->height);
+ g_return_if_fail (height >= 0);
+
+ end = y + height;
+
+ while (y < end)
+ {
+ GimpTile *tile;
+ guchar *tile_data;
+ gint inc;
+ gint boundary;
+
+ tile = gimp_drawable_get_tile2 (pr->drawable, pr->shadow, x, y);
+ gimp_tile_ref (tile);
+
+ tile_data = (tile->data +
+ tile->bpp * (tile->ewidth * (y % TILE_HEIGHT) + (x % TILE_WIDTH)));
+
+ boundary = y + (tile->eheight - (y % TILE_HEIGHT));
+ inc = tile->bpp * tile->ewidth;
+
+ for ( ; y < end && y < boundary; y++)
+ {
+ gint b;
+
+ for (b = 0; b < tile->bpp; b++)
+ tile_data[b] = *buf++;
+
+ tile_data += inc;
+ }
+
+ gimp_tile_unref (tile, TRUE);
+ }
+}
+
+/**
+ * gimp_pixel_rgn_set_rect:
+ * @pr: a pointer to a previously initialized #GimpPixelRgn.
+ * @buf: a pointer to an array of #guchar
+ * @x: the x coordinate of the first pixel (relative to the drawable).
+ * @y: the y coordinate of the first pixel (relative to the drawable).
+ * @width: the width of the rectangle.
+ * @height: the height of the rectangle.
+ *
+ * Set all the pixel of the rectangle defined by @x, @y, @width and
+ * @height. This function draws the rectangle from (@x, @y) to
+ * (@x+@width-1, @y+@height-1), using the pixel values from the buffer @buf.
+ * @buf should be large enough to hold all these values (@width*@height*bpp).
+ **/
+void
+gimp_pixel_rgn_set_rect (GimpPixelRgn *pr,
+ const guchar *buf,
+ gint x,
+ gint y,
+ gint width,
+ gint height)
+{
+ gulong bufstride;
+ gint xstart, ystart;
+ gint xend, yend;
+ gint xboundary;
+ gint yboundary;
+ gint xstep, ystep;
+ gint ty, bpp;
+
+ g_return_if_fail (pr != NULL && pr->drawable != NULL);
+ g_return_if_fail (buf != NULL);
+ g_return_if_fail (x >= 0 && x + width <= pr->drawable->width);
+ g_return_if_fail (y >= 0 && y + height <= pr->drawable->height);
+ g_return_if_fail (width >= 0);
+ g_return_if_fail (height >= 0);
+
+ bpp = pr->bpp;
+ bufstride = bpp * width;
+
+ xstart = x;
+ ystart = y;
+ xend = x + width;
+ yend = y + height;
+ ystep = 0;
+
+ while (y < yend)
+ {
+ x = xstart;
+
+ while (x < xend)
+ {
+ GimpTile *tile;
+
+ tile = gimp_drawable_get_tile2 (pr->drawable, pr->shadow, x, y);
+ gimp_tile_ref (tile);
+
+ xstep = tile->ewidth - (x % TILE_WIDTH);
+ ystep = tile->eheight - (y % TILE_HEIGHT);
+ xboundary = x + xstep;
+ yboundary = y + ystep;
+ xboundary = MIN (xboundary, xend);
+ yboundary = MIN (yboundary, yend);
+
+ for (ty = y; ty < yboundary; ty++)
+ {
+ const guchar *src;
+ guchar *dest;
+
+ src = buf + bufstride * (ty - ystart) + bpp * (x - xstart);
+ dest = tile->data + tile->bpp * (tile->ewidth *
+ (ty % TILE_HEIGHT) + (x % TILE_WIDTH));
+
+ memcpy (dest, src, (xboundary - x) * bpp);
+ }
+
+ gimp_tile_unref (tile, TRUE);
+ x += xstep;
+ }
+
+ y += ystep;
+ }
+}
+
+/**
+ * gimp_pixel_rgns_register2:
+ * @nrgns: the number of regions to register.
+ * @prs: an array of @nrgns pointers to initialized #GimpPixelRgn.
+ *
+ * It takes a number of initialized regions of the same size and provides a
+ * pixel region iterator the iterator can be used to iterate over the
+ * registered pixel regions. While iterating the registered pixel regions will
+ * cover subsets of the original pixel regions, chosen for optimized access to
+ * the image data.
+ *
+ * Note that the given regions themselves are changed by this function, so
+ * they are resized to the first subsets.
+ *
+ * This function has to be used together with gimp_pixel_rgns_process in a loop.
+ *
+ * Returns: a #gpointer to a regions iterator.
+ **/
+gpointer
+gimp_pixel_rgns_register2 (gint nrgns,
+ GimpPixelRgn **prs)
+{
+ GimpPixelRgnIterator *pri;
+ gboolean found;
+
+ g_return_val_if_fail (nrgns > 0, NULL);
+ g_return_val_if_fail (prs != NULL, NULL);
+
+ pri = g_slice_new0 (GimpPixelRgnIterator);
+
+ found = FALSE;
+ while (nrgns --)
+ {
+ GimpPixelRgn *pr = prs[nrgns];
+ GimpPixelRgnHolder *prh = g_slice_new0 (GimpPixelRgnHolder);
+
+ prh->pr = pr;
+
+ if (pr != NULL)
+ {
+ /* If there is a defined value for data, make sure tiles is NULL */
+ if (pr->data)
+ pr->drawable = NULL;
+
+ prh->original_data = pr->data;
+ prh->startx = pr->x;
+ prh->starty = pr->y;
+ prh->pr->process_count = 0;
+
+ if (! found)
+ {
+ found = TRUE;
+ pri->region_width = pr->w;
+ pri->region_height = pr->h;
+ }
+ }
+
+ /* Add the pixel Rgn holder to the list */
+ pri->pixel_regions = g_slist_prepend (pri->pixel_regions, prh);
+ }
+
+ return gimp_pixel_rgns_configure (pri);
+}
+
+/**
+ * gimp_pixel_rgns_register:
+ * @nrgns: the number of regions to register.
+ * @...: @nrgns pointers to #GimpPixelRgn.
+ *
+ * This is the varargs version of #gimp_pixel_rgns_register2.
+ *
+ * Returns: a #gpointer to a regions iterator.
+ **/
+gpointer
+gimp_pixel_rgns_register (gint nrgns,
+ ...)
+{
+ GimpPixelRgn **prs;
+ gint n;
+ va_list ap;
+
+ g_return_val_if_fail (nrgns > 0, NULL);
+
+ prs = g_newa (GimpPixelRgn *, nrgns);
+
+ va_start (ap, nrgns);
+
+ for (n = nrgns; n--; )
+ prs[n] = va_arg (ap, GimpPixelRgn *);
+
+ va_end (ap);
+
+ return gimp_pixel_rgns_register2 (nrgns, prs);
+}
+
+/**
+ * gimp_pixel_rgns_process:
+ * @pri_ptr: a regions iterator returned by #gimp_pixel_rgns_register,
+ * #gimp_pixel_rgns_register2 or #gimp_pixel_rgns_process.
+ *
+ * This function update the regions registered previously with one of the
+ * #gimp_pixel_rgns_register* functions to their next tile.
+ *
+ * Returns: a #gpointer to a new regions iterator or #NULL if there isn't
+ * any tiles left.
+ **/
+gpointer
+gimp_pixel_rgns_process (gpointer pri_ptr)
+{
+ GimpPixelRgnIterator *pri;
+ GSList *list;
+
+ g_return_val_if_fail (pri_ptr != NULL, NULL);
+
+ pri = (GimpPixelRgnIterator*) pri_ptr;
+ pri->process_count++;
+
+ /* Unref all referenced tiles and increment the offsets */
+
+ for (list = pri->pixel_regions; list; list = list->next)
+ {
+ GimpPixelRgnHolder *prh = list->data;
+
+ if ((prh->pr != NULL) && (prh->pr->process_count != pri->process_count))
+ {
+ /* This eliminates the possibility of incrementing the
+ * same region twice
+ */
+ prh->pr->process_count++;
+
+ /* Unref the last referenced tile if the underlying region
+ * is a tile manager
+ */
+ if (prh->pr->drawable)
+ {
+ GimpTile *tile = gimp_drawable_get_tile2 (prh->pr->drawable,
+ prh->pr->shadow,
+ prh->pr->x,
+ prh->pr->y);
+ gimp_tile_unref (tile, prh->pr->dirty);
+ }
+
+ prh->pr->x += pri->portion_width;
+
+ if ((prh->pr->x - prh->startx) >= pri->region_width)
+ {
+ prh->pr->x = prh->startx;
+ prh->pr->y += pri->portion_height;
+ }
+ }
+ }
+
+ return gimp_pixel_rgns_configure (pri);
+}
+
+
+static gint
+gimp_get_portion_width (GimpPixelRgnIterator *pri)
+{
+ GSList *list;
+ gint min_width = G_MAXINT;
+ gint width;
+
+ /* Find the minimum width to the next vertical tile (in the case of
+ * a tile manager) or to the end of the pixel region (in the case of
+ * no tile manager)
+ */
+
+ for (list = pri->pixel_regions; list; list = list->next)
+ {
+ GimpPixelRgnHolder *prh = list->data;
+
+ if (prh->pr)
+ {
+ /* Check if we're past the point of no return */
+ if ((prh->pr->x - prh->startx) >= pri->region_width)
+ return 0;
+
+ if (prh->pr->drawable)
+ {
+ width = TILE_WIDTH - (prh->pr->x % TILE_WIDTH);
+ width = CLAMP (width,
+ 0,
+ (pri->region_width - (prh->pr->x - prh->startx)));
+ }
+ else
+ {
+ width = (pri->region_width - (prh->pr->x - prh->startx));
+ }
+
+ if (width < min_width)
+ min_width = width;
+ }
+ }
+
+ return min_width;
+}
+
+static gint
+gimp_get_portion_height (GimpPixelRgnIterator *pri)
+{
+ GSList *list;
+ gint min_height = G_MAXINT;
+ gint height;
+
+ /* Find the minimum height to the next vertical tile (in the case of
+ * a tile manager) or to the end of the pixel region (in the case of
+ * no tile manager)
+ */
+
+ for (list = pri->pixel_regions; list; list = list->next)
+ {
+ GimpPixelRgnHolder *prh = list->data;
+
+ if (prh->pr)
+ {
+ /* Check if we're past the point of no return */
+ if ((prh->pr->y - prh->starty) >= pri->region_height)
+ return 0;
+
+ if (prh->pr->drawable)
+ {
+ height = TILE_HEIGHT - (prh->pr->y % TILE_HEIGHT);
+ height = CLAMP (height,
+ 0,
+ (pri->region_height - (prh->pr->y - prh->starty)));
+ }
+ else
+ {
+ height = (pri->region_height - (prh->pr->y - prh->starty));
+ }
+
+ if (height < min_height)
+ min_height = height;
+ }
+ }
+
+ return min_height;
+}
+
+static gpointer
+gimp_pixel_rgns_configure (GimpPixelRgnIterator *pri)
+{
+ GSList *list;
+
+ /* Determine the portion width and height */
+ pri->portion_width = gimp_get_portion_width (pri);
+ pri->portion_height = gimp_get_portion_height (pri);
+
+ if (pri->portion_width == 0 ||
+ pri->portion_height == 0)
+ {
+ /* free the pixel regions list */
+ for (list = pri->pixel_regions; list; list = list->next)
+ g_slice_free (GimpPixelRgnHolder, list->data);
+
+ g_slist_free (pri->pixel_regions);
+ g_slice_free (GimpPixelRgnIterator, pri);
+
+ return NULL;
+ }
+
+ pri->process_count++;
+
+ for (list = pri->pixel_regions; list; list = list->next)
+ {
+ GimpPixelRgnHolder *prh = list->data;
+
+ if ((prh->pr != NULL) && (prh->pr->process_count != pri->process_count))
+ {
+ prh->pr->process_count++;
+ gimp_pixel_rgn_configure (prh, pri);
+ }
+ }
+
+ return pri;
+}
+
+static void
+gimp_pixel_rgn_configure (GimpPixelRgnHolder *prh,
+ GimpPixelRgnIterator *pri)
+{
+ /* Configure the rowstride and data pointer for the pixel region
+ * based on the current offsets into the region and whether the
+ * region is represented by a tile manager or not
+ */
+ if (prh->pr->drawable)
+ {
+ GimpTile *tile;
+ gint offx;
+ gint offy;
+
+ tile = gimp_drawable_get_tile2 (prh->pr->drawable,
+ prh->pr->shadow,
+ prh->pr->x,
+ prh->pr->y);
+ gimp_tile_ref (tile);
+
+ offx = prh->pr->x % TILE_WIDTH;
+ offy = prh->pr->y % TILE_HEIGHT;
+
+ prh->pr->rowstride = tile->ewidth * prh->pr->bpp;
+ prh->pr->data = (tile->data +
+ offy * prh->pr->rowstride + offx * prh->pr->bpp);
+ }
+ else
+ {
+ prh->pr->data = (prh->original_data +
+ prh->pr->y * prh->pr->rowstride +
+ prh->pr->x * prh->pr->bpp);
+ }
+
+ prh->pr->w = pri->portion_width;
+ prh->pr->h = pri->portion_height;
+}
diff --git a/libgimp/gimppixelrgn.h b/libgimp/gimppixelrgn.h
new file mode 100644
index 0000000..8fb4c4c
--- /dev/null
+++ b/libgimp/gimppixelrgn.h
@@ -0,0 +1,122 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppixelrgn.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PIXEL_RGN_H__
+#define __GIMP_PIXEL_RGN_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+struct _GimpPixelRgn
+{
+ guchar *data; /* pointer to region data */
+ GimpDrawable *drawable; /* pointer to drawable */
+ gint bpp; /* bytes per pixel */
+ gint rowstride; /* bytes per pixel row */
+ gint x, y; /* origin */
+ gint w, h; /* width and height of region */
+ guint dirty : 1; /* will this region be dirtied? */
+ guint shadow : 1; /* will this region use the shadow or normal tiles */
+ gint process_count; /* used internally */
+};
+
+
+GIMP_DEPRECATED_FOR(gimp_drawable_get_buffer)
+void gimp_pixel_rgn_init (GimpPixelRgn *pr,
+ GimpDrawable *drawable,
+ gint x,
+ gint y,
+ gint width,
+ gint height,
+ gint dirty,
+ gint shadow);
+GIMP_DEPRECATED
+void gimp_pixel_rgn_resize (GimpPixelRgn *pr,
+ gint x,
+ gint y,
+ gint width,
+ gint height);
+GIMP_DEPRECATED_FOR(gegl_buffer_sample)
+void gimp_pixel_rgn_get_pixel (GimpPixelRgn *pr,
+ guchar *buf,
+ gint x,
+ gint y);
+GIMP_DEPRECATED_FOR(gegl_buffer_get)
+void gimp_pixel_rgn_get_row (GimpPixelRgn *pr,
+ guchar *buf,
+ gint x,
+ gint y,
+ gint width);
+GIMP_DEPRECATED_FOR(gegl_buffer_get)
+void gimp_pixel_rgn_get_col (GimpPixelRgn *pr,
+ guchar *buf,
+ gint x,
+ gint y,
+ gint height);
+GIMP_DEPRECATED_FOR(gegl_buffer_get)
+void gimp_pixel_rgn_get_rect (GimpPixelRgn *pr,
+ guchar *buf,
+ gint x,
+ gint y,
+ gint width,
+ gint height);
+GIMP_DEPRECATED_FOR(gegl_buffer_set)
+void gimp_pixel_rgn_set_pixel (GimpPixelRgn *pr,
+ const guchar *buf,
+ gint x,
+ gint y);
+GIMP_DEPRECATED_FOR(gegl_buffer_set)
+void gimp_pixel_rgn_set_row (GimpPixelRgn *pr,
+ const guchar *buf,
+ gint x,
+ gint y,
+ gint width);
+GIMP_DEPRECATED_FOR(gegl_buffer_set)
+void gimp_pixel_rgn_set_col (GimpPixelRgn *pr,
+ const guchar *buf,
+ gint x,
+ gint y,
+ gint height);
+GIMP_DEPRECATED_FOR(gegl_buffer_set)
+void gimp_pixel_rgn_set_rect (GimpPixelRgn *pr,
+ const guchar *buf,
+ gint x,
+ gint y,
+ gint width,
+ gint height);
+GIMP_DEPRECATED_FOR(gegl_buffer_iterator_new)
+gpointer gimp_pixel_rgns_register (gint nrgns,
+ ...);
+GIMP_DEPRECATED_FOR(gegl_buffer_iterator_new)
+gpointer gimp_pixel_rgns_register2 (gint nrgns,
+ GimpPixelRgn **prs);
+GIMP_DEPRECATED_FOR(gegl_buffer_iterator_next)
+gpointer gimp_pixel_rgns_process (gpointer pri_ptr);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PIXEL_RGN_H__ */
diff --git a/libgimp/gimpplugin.c b/libgimp/gimpplugin.c
new file mode 100644
index 0000000..64611f9
--- /dev/null
+++ b/libgimp/gimpplugin.c
@@ -0,0 +1,71 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpplugin.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gimp.h"
+
+
+/**
+ * gimp_plug_in_error_quark:
+ *
+ * Generic #GQuark error domain for plug-ins. Plug-ins are welcome to
+ * create their own domain when they want to handle advanced error
+ * handling. Often, you just want to pass an error message to the core.
+ * This domain can be used for such simple usage.
+ *
+ * See #GError for information on error domains.
+ */
+G_DEFINE_QUARK (gimp-plug-in-error-quark, gimp_plug_in_error)
+
+
+gboolean
+gimp_plugin_icon_register (const gchar *procedure_name,
+ GimpIconType icon_type,
+ const guint8 *icon_data)
+{
+ gint icon_data_length;
+
+ g_return_val_if_fail (procedure_name != NULL, FALSE);
+ g_return_val_if_fail (icon_data != NULL, FALSE);
+
+ switch (icon_type)
+ {
+ case GIMP_ICON_TYPE_ICON_NAME:
+ case GIMP_ICON_TYPE_IMAGE_FILE:
+ icon_data_length = strlen ((const gchar *) icon_data) + 1;
+ break;
+
+ case GIMP_ICON_TYPE_INLINE_PIXBUF:
+ g_return_val_if_fail (g_ntohl (*((gint32 *) icon_data)) == 0x47646b50,
+ FALSE);
+
+ icon_data_length = g_ntohl (*((gint32 *) (icon_data + 4)));
+ break;
+
+ default:
+ g_return_val_if_reached (FALSE);
+ }
+
+ return _gimp_plugin_icon_register (procedure_name,
+ icon_type, icon_data_length, icon_data);
+}
diff --git a/libgimp/gimpplugin.h b/libgimp/gimpplugin.h
new file mode 100644
index 0000000..434f947
--- /dev/null
+++ b/libgimp/gimpplugin.h
@@ -0,0 +1,44 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpplugin.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PLUG_IN_H__
+#define __GIMP_PLUG_IN_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+#define GIMP_PLUG_IN_ERROR (gimp_plug_in_error_quark ())
+
+
+GQuark gimp_plug_in_error_quark (void);
+
+gboolean gimp_plugin_icon_register (const gchar *procedure_name,
+ GimpIconType icon_type,
+ const guint8 *icon_data);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PLUG_IN_H__ */
diff --git a/libgimp/gimpplugin_pdb.c b/libgimp/gimpplugin_pdb.c
new file mode 100644
index 0000000..0cf6766
--- /dev/null
+++ b/libgimp/gimpplugin_pdb.c
@@ -0,0 +1,357 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpplugin_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpplugin
+ * @title: gimpplugin
+ * @short_description: Functions useful for plug-ins, e.g. registration and progress indicators.
+ *
+ * Functions useful for plug-ins, e.g. registration and progress
+ * indicators.
+ **/
+
+
+/**
+ * gimp_plugin_domain_register:
+ * @domain_name: The name of the textdomain (must be unique).
+ * @domain_path: The absolute path to the compiled message catalog (may be NULL).
+ *
+ * Registers a textdomain for localisation.
+ *
+ * This procedure adds a textdomain to the list of domains Gimp
+ * searches for strings when translating its menu entries. There is no
+ * need to call this function for plug-ins that have their strings
+ * included in the 'gimp-std-plugins' domain as that is used by
+ * default. If the compiled message catalog is not in the standard
+ * location, you may specify an absolute path to another location. This
+ * procedure can only be called in the query function of a plug-in and
+ * it has to be called before any procedure is installed.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_plugin_domain_register (const gchar *domain_name,
+ const gchar *domain_path)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-plugin-domain-register",
+ &nreturn_vals,
+ GIMP_PDB_STRING, domain_name,
+ GIMP_PDB_STRING, domain_path,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_plugin_help_register:
+ * @domain_name: The XML namespace of the plug-in's help pages.
+ * @domain_uri: The root URI of the plug-in's help pages.
+ *
+ * Register a help path for a plug-in.
+ *
+ * This procedure registers user documentation for the calling plug-in
+ * with the GIMP help system. The domain_uri parameter points to the
+ * root directory where the plug-in help is installed. For each
+ * supported language there should be a file called 'gimp-help.xml'
+ * that maps the help IDs to the actual help files.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_plugin_help_register (const gchar *domain_name,
+ const gchar *domain_uri)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-plugin-help-register",
+ &nreturn_vals,
+ GIMP_PDB_STRING, domain_name,
+ GIMP_PDB_STRING, domain_uri,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_plugin_menu_register:
+ * @procedure_name: The procedure for which to install the menu path.
+ * @menu_path: The procedure's additional menu path.
+ *
+ * Register an additional menu path for a plug-in procedure.
+ *
+ * This procedure installs an additional menu entry for the given
+ * procedure.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_plugin_menu_register (const gchar *procedure_name,
+ const gchar *menu_path)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-plugin-menu-register",
+ &nreturn_vals,
+ GIMP_PDB_STRING, procedure_name,
+ GIMP_PDB_STRING, menu_path,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_plugin_menu_branch_register:
+ * @menu_path: The sub-menu's menu path.
+ * @menu_name: The name of the sub-menu.
+ *
+ * Register a sub-menu.
+ *
+ * This procedure installs a sub-menu which does not belong to any
+ * procedure. The menu-name should be the untranslated menu label. GIMP
+ * will look up the translation in the textdomain registered for the
+ * plug-in.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_plugin_menu_branch_register (const gchar *menu_path,
+ const gchar *menu_name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-plugin-menu-branch-register",
+ &nreturn_vals,
+ GIMP_PDB_STRING, menu_path,
+ GIMP_PDB_STRING, menu_name,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * _gimp_plugin_icon_register:
+ * @procedure_name: The procedure for which to install the icon.
+ * @icon_type: The type of the icon.
+ * @icon_data_length: The length of 'icon-data'.
+ * @icon_data: The procedure's icon. The format depends on the 'icon_type' parameter.
+ *
+ * Register an icon for a plug-in procedure.
+ *
+ * This procedure installs an icon for the given procedure.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+_gimp_plugin_icon_register (const gchar *procedure_name,
+ GimpIconType icon_type,
+ gint icon_data_length,
+ const guint8 *icon_data)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-plugin-icon-register",
+ &nreturn_vals,
+ GIMP_PDB_STRING, procedure_name,
+ GIMP_PDB_INT32, icon_type,
+ GIMP_PDB_INT32, icon_data_length,
+ GIMP_PDB_INT8ARRAY, icon_data,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_plugin_set_pdb_error_handler:
+ * @handler: Who is responsible for handling procedure call errors.
+ *
+ * Sets an error handler for procedure calls.
+ *
+ * This procedure changes the way that errors in procedure calls are
+ * handled. By default GIMP will raise an error dialog if a procedure
+ * call made by a plug-in fails. Using this procedure the plug-in can
+ * change this behavior. If the error handler is set to
+ * %GIMP_PDB_ERROR_HANDLER_PLUGIN, then the plug-in is responsible for
+ * calling gimp_get_pdb_error() and handling the error whenever one if
+ * its procedure calls fails. It can do this by displaying the error
+ * message or by forwarding it in its own return values.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.6
+ **/
+gboolean
+gimp_plugin_set_pdb_error_handler (GimpPDBErrorHandler handler)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-plugin-set-pdb-error-handler",
+ &nreturn_vals,
+ GIMP_PDB_INT32, handler,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_plugin_get_pdb_error_handler:
+ *
+ * Retrieves the active error handler for procedure calls.
+ *
+ * This procedure retrieves the currently active error handler for
+ * procedure calls made by the calling plug-in. See
+ * gimp_plugin_set_pdb_error_handler() for details.
+ *
+ * Returns: Who is responsible for handling procedure call errors.
+ *
+ * Since: 2.6
+ **/
+GimpPDBErrorHandler
+gimp_plugin_get_pdb_error_handler (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpPDBErrorHandler handler = 0;
+
+ return_vals = gimp_run_procedure ("gimp-plugin-get-pdb-error-handler",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ handler = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return handler;
+}
+
+/**
+ * gimp_plugin_enable_precision:
+ *
+ * Switches this plug-in to using the real bit depth of drawables.
+ *
+ * Switches this plug-in to using the real bit depth of drawables. This
+ * setting can only be enabled, and not disabled again during the
+ * lifetime of the plug-in. Using gimp_drawable_get_buffer(),
+ * gimp_drawable_get_shadow_buffer() or gimp_drawable_get_format() will
+ * automatically call this function.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_plugin_enable_precision (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-plugin-enable-precision",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_plugin_precision_enabled:
+ *
+ * Whether this plug-in is using the real bit depth of drawables.
+ *
+ * Returns whether this plug-in is using the real bit depth of
+ * drawables, which can be more than 8 bits per channel.
+ *
+ * Returns: Whether precision is enabled.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_plugin_precision_enabled (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean enabled = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-plugin-precision-enabled",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ enabled = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return enabled;
+}
diff --git a/libgimp/gimpplugin_pdb.h b/libgimp/gimpplugin_pdb.h
new file mode 100644
index 0000000..97b83d8
--- /dev/null
+++ b/libgimp/gimpplugin_pdb.h
@@ -0,0 +1,55 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpplugin_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PLUG_IN_PDB_H__
+#define __GIMP_PLUG_IN_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_plugin_domain_register (const gchar *domain_name,
+ const gchar *domain_path);
+gboolean gimp_plugin_help_register (const gchar *domain_name,
+ const gchar *domain_uri);
+gboolean gimp_plugin_menu_register (const gchar *procedure_name,
+ const gchar *menu_path);
+gboolean gimp_plugin_menu_branch_register (const gchar *menu_path,
+ const gchar *menu_name);
+G_GNUC_INTERNAL gboolean _gimp_plugin_icon_register (const gchar *procedure_name,
+ GimpIconType icon_type,
+ gint icon_data_length,
+ const guint8 *icon_data);
+gboolean gimp_plugin_set_pdb_error_handler (GimpPDBErrorHandler handler);
+GimpPDBErrorHandler gimp_plugin_get_pdb_error_handler (void);
+gboolean gimp_plugin_enable_precision (void);
+gboolean gimp_plugin_precision_enabled (void);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PLUG_IN_PDB_H__ */
diff --git a/libgimp/gimpprocbrowserdialog.c b/libgimp/gimpprocbrowserdialog.c
new file mode 100644
index 0000000..dbb5b21
--- /dev/null
+++ b/libgimp/gimpprocbrowserdialog.c
@@ -0,0 +1,546 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpprocbrowserdialog.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "gimp.h"
+
+#include "gimpuitypes.h"
+#include "gimpprocbrowserdialog.h"
+#include "gimpprocview.h"
+
+#include "libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpprocbrowserdialog
+ * @title: GimpProcBrowserDialog
+ * @short_description: The dialog for the procedure and plugin browsers.
+ *
+ * The dialog for the procedure and plugin browsers.
+ **/
+
+
+#define DBL_LIST_WIDTH 250
+#define DBL_WIDTH (DBL_LIST_WIDTH + 400)
+#define DBL_HEIGHT 250
+
+
+enum
+{
+ SELECTION_CHANGED,
+ ROW_ACTIVATED,
+ LAST_SIGNAL
+};
+
+typedef enum
+{
+ SEARCH_TYPE_ALL,
+ SEARCH_TYPE_NAME,
+ SEARCH_TYPE_BLURB,
+ SEARCH_TYPE_HELP,
+ SEARCH_TYPE_AUTHOR,
+ SEARCH_TYPE_COPYRIGHT,
+ SEARCH_TYPE_DATE,
+ SEARCH_TYPE_PROC_TYPE
+} SearchType;
+
+enum
+{
+ COLUMN_PROC_NAME,
+ N_COLUMNS
+};
+
+
+static void browser_selection_changed (GtkTreeSelection *sel,
+ GimpProcBrowserDialog *dialog);
+static void browser_row_activated (GtkTreeView *treeview,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ GimpProcBrowserDialog *dialog);
+static void browser_show_procedure (GimpProcBrowserDialog *dialog,
+ const gchar *proc_name);
+static void browser_search (GimpBrowser *browser,
+ const gchar *query_text,
+ gint search_type,
+ GimpProcBrowserDialog *dialog);
+
+
+G_DEFINE_TYPE (GimpProcBrowserDialog, gimp_proc_browser_dialog,
+ GIMP_TYPE_DIALOG)
+
+#define parent_class gimp_proc_browser_dialog_parent_class
+
+static guint dialog_signals[LAST_SIGNAL] = { 0, };
+
+
+static void
+gimp_proc_browser_dialog_class_init (GimpProcBrowserDialogClass *klass)
+{
+ /**
+ * GimpProcBrowserDialog::selection-changed:
+ * @dialog: the object that received the signal
+ *
+ * Emitted when the selection in the contained #GtkTreeView changes.
+ */
+ dialog_signals[SELECTION_CHANGED] =
+ g_signal_new ("selection-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GimpProcBrowserDialogClass,
+ selection_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ /**
+ * GimpProcBrowserDialog::row-activated:
+ * @dialog: the object that received the signal
+ *
+ * Emitted when one of the rows in the contained #GtkTreeView is activated.
+ */
+ dialog_signals[ROW_ACTIVATED] =
+ g_signal_new ("row-activated",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GimpProcBrowserDialogClass,
+ row_activated),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ klass->selection_changed = NULL;
+ klass->row_activated = NULL;
+}
+
+static void
+gimp_proc_browser_dialog_init (GimpProcBrowserDialog *dialog)
+{
+ GtkWidget *scrolled_window;
+ GtkCellRenderer *renderer;
+ GtkTreeSelection *selection;
+ GtkWidget *parent;
+
+ dialog->browser = gimp_browser_new ();
+ gimp_browser_add_search_types (GIMP_BROWSER (dialog->browser),
+ _("by name"), SEARCH_TYPE_NAME,
+ _("by description"), SEARCH_TYPE_BLURB,
+ _("by help"), SEARCH_TYPE_HELP,
+ _("by author"), SEARCH_TYPE_AUTHOR,
+ _("by copyright"), SEARCH_TYPE_COPYRIGHT,
+ _("by date"), SEARCH_TYPE_DATE,
+ _("by type"), SEARCH_TYPE_PROC_TYPE,
+ NULL);
+ gtk_container_set_border_width (GTK_CONTAINER (dialog->browser), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ dialog->browser, TRUE, TRUE, 0);
+ gtk_widget_show (dialog->browser);
+
+ g_signal_connect (dialog->browser, "search",
+ G_CALLBACK (browser_search),
+ dialog);
+
+ /* list : list in a scrolled_win */
+
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_SHADOW_IN);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_ALWAYS);
+ gtk_box_pack_start (GTK_BOX (GIMP_BROWSER (dialog->browser)->left_vbox),
+ scrolled_window, TRUE, TRUE, 0);
+ gtk_widget_show (scrolled_window);
+
+ dialog->tree_view = gtk_tree_view_new ();
+
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_cell_renderer_text_set_fixed_height_from_font
+ (GTK_CELL_RENDERER_TEXT (renderer), 1);
+
+ gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (dialog->tree_view),
+ -1, NULL,
+ renderer,
+ "text", 0,
+ NULL);
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (dialog->tree_view), FALSE);
+
+ g_signal_connect (dialog->tree_view, "row_activated",
+ G_CALLBACK (browser_row_activated),
+ dialog);
+
+ gtk_widget_set_size_request (dialog->tree_view, DBL_LIST_WIDTH, DBL_HEIGHT);
+ gtk_container_add (GTK_CONTAINER (scrolled_window), dialog->tree_view);
+ gtk_widget_show (dialog->tree_view);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->tree_view));
+
+ g_signal_connect (selection, "changed",
+ G_CALLBACK (browser_selection_changed),
+ dialog);
+
+ parent = gtk_widget_get_parent (GIMP_BROWSER (dialog->browser)->right_vbox);
+ parent = gtk_widget_get_parent (parent);
+
+ gtk_widget_set_size_request (parent, DBL_WIDTH - DBL_LIST_WIDTH, -1);
+}
+
+
+/* public functions */
+
+/**
+ * gimp_proc_browser_dialog_new:
+ * @title: The dialog's title.
+ * @role: The dialog's role, see gtk_window_set_role().
+ * @help_func: The function which will be called if the user presses "F1".
+ * @help_id: The help_id which will be passed to @help_func.
+ * @...: A %NULL-terminated list destribing the action_area buttons.
+ *
+ * Create a new #GimpProcBrowserDialog.
+ *
+ * Return Value: a newly created #GimpProcBrowserDialog.
+ *
+ * Since: 2.4
+ **/
+GtkWidget *
+gimp_proc_browser_dialog_new (const gchar *title,
+ const gchar *role,
+ GimpHelpFunc help_func,
+ const gchar *help_id,
+ ...)
+{
+ GimpProcBrowserDialog *dialog;
+ va_list args;
+
+ va_start (args, help_id);
+
+ dialog = g_object_new (GIMP_TYPE_PROC_BROWSER_DIALOG,
+ "title", title,
+ "role", role,
+ "help-func", help_func,
+ "help-id", help_id,
+ NULL);
+
+ gimp_dialog_add_buttons_valist (GIMP_DIALOG (dialog), args);
+
+ va_end (args);
+
+ /* first search (all procedures) */
+ browser_search (GIMP_BROWSER (dialog->browser), "", SEARCH_TYPE_ALL, dialog);
+
+ return GTK_WIDGET (dialog);
+}
+
+/**
+ * gimp_proc_browser_dialog_get_selected:
+ * @dialog: a #GimpProcBrowserDialog
+ *
+ * Retrieves the name of the currently selected procedure.
+ *
+ * Return Value: The name of the selected procedure of %NULL if no
+ * procedure is selected.
+ *
+ * Since: 2.4
+ **/
+gchar *
+gimp_proc_browser_dialog_get_selected (GimpProcBrowserDialog *dialog)
+{
+ GtkTreeSelection *sel;
+ GtkTreeIter iter;
+
+ g_return_val_if_fail (GIMP_IS_PROC_BROWSER_DIALOG (dialog), NULL);
+
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->tree_view));
+
+ if (gtk_tree_selection_get_selected (sel, NULL, &iter))
+ {
+ gchar *proc_name;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (dialog->store), &iter,
+ COLUMN_PROC_NAME, &proc_name,
+ -1);
+
+ return proc_name;
+ }
+
+ return NULL;
+}
+
+
+/* private functions */
+
+static void
+browser_selection_changed (GtkTreeSelection *sel,
+ GimpProcBrowserDialog *dialog)
+{
+ GtkTreeIter iter;
+
+ if (gtk_tree_selection_get_selected (sel, NULL, &iter))
+ {
+ gchar *proc_name;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (dialog->store), &iter,
+ COLUMN_PROC_NAME, &proc_name,
+ -1);
+ browser_show_procedure (dialog, proc_name);
+ g_free (proc_name);
+ }
+
+ g_signal_emit (dialog, dialog_signals[SELECTION_CHANGED], 0);
+}
+
+static void
+browser_row_activated (GtkTreeView *treeview,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ GimpProcBrowserDialog *dialog)
+{
+ g_signal_emit (dialog, dialog_signals[ROW_ACTIVATED], 0);
+}
+
+static void
+browser_show_procedure (GimpProcBrowserDialog *dialog,
+ const gchar *proc_name)
+{
+ gchar *proc_blurb;
+ gchar *proc_help;
+ gchar *proc_author;
+ gchar *proc_copyright;
+ gchar *proc_date;
+ GimpPDBProcType proc_type;
+ gint n_params;
+ gint n_return_vals;
+ GimpParamDef *params;
+ GimpParamDef *return_vals;
+
+ gimp_procedural_db_proc_info (proc_name,
+ &proc_blurb,
+ &proc_help,
+ &proc_author,
+ &proc_copyright,
+ &proc_date,
+ &proc_type,
+ &n_params,
+ &n_return_vals,
+ &params,
+ &return_vals);
+
+ gimp_browser_set_widget (GIMP_BROWSER (dialog->browser),
+ gimp_proc_view_new (proc_name,
+ NULL,
+ proc_blurb,
+ proc_help,
+ proc_author,
+ proc_copyright,
+ proc_date,
+ proc_type,
+ n_params,
+ n_return_vals,
+ params,
+ return_vals));
+
+ g_free (proc_blurb);
+ g_free (proc_help);
+ g_free (proc_author);
+ g_free (proc_copyright);
+ g_free (proc_date);
+
+ gimp_destroy_paramdefs (params, n_params);
+ gimp_destroy_paramdefs (return_vals, n_return_vals);
+}
+
+static void
+browser_search (GimpBrowser *browser,
+ const gchar *query_text,
+ gint search_type,
+ GimpProcBrowserDialog *dialog)
+{
+ gchar **proc_list;
+ gint num_procs;
+ gchar *str;
+ GRegex *regex;
+
+ /* first check if the query is a valid regex */
+ regex = g_regex_new (query_text, 0, 0, NULL);
+
+ if (! regex)
+ {
+ gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->tree_view), NULL);
+ dialog->store = NULL;
+
+ gimp_browser_show_message (browser, _("No matches"));
+
+ gtk_label_set_text (GTK_LABEL (browser->count_label),
+ _("Search term invalid or incomplete"));
+ return;
+ }
+
+ g_regex_unref (regex);
+
+ switch (search_type)
+ {
+ case SEARCH_TYPE_ALL:
+ gimp_browser_show_message (browser, _("Searching"));
+
+ gimp_procedural_db_query (".*", ".*", ".*", ".*", ".*", ".*", ".*",
+ &num_procs, &proc_list);
+ break;
+
+ case SEARCH_TYPE_NAME:
+ {
+ GString *query = g_string_new ("");
+ const gchar *q = query_text;
+
+ gimp_browser_show_message (browser, _("Searching by name"));
+
+ while (*q)
+ {
+ if ((*q == '_') || (*q == '-'))
+ g_string_append (query, "-");
+ else
+ g_string_append_c (query, *q);
+
+ q++;
+ }
+
+ gimp_procedural_db_query (query->str,
+ ".*", ".*", ".*", ".*", ".*", ".*",
+ &num_procs, &proc_list);
+
+ g_string_free (query, TRUE);
+ }
+ break;
+
+ case SEARCH_TYPE_BLURB:
+ gimp_browser_show_message (browser, _("Searching by description"));
+
+ gimp_procedural_db_query (".*", query_text, ".*", ".*", ".*", ".*", ".*",
+ &num_procs, &proc_list);
+ break;
+
+ case SEARCH_TYPE_HELP:
+ gimp_browser_show_message (browser, _("Searching by help"));
+
+ gimp_procedural_db_query (".*", ".*", query_text, ".*", ".*", ".*", ".*",
+ &num_procs, &proc_list);
+ break;
+
+ case SEARCH_TYPE_AUTHOR:
+ gimp_browser_show_message (browser, _("Searching by author"));
+
+ gimp_procedural_db_query (".*", ".*", ".*", query_text, ".*", ".*", ".*",
+ &num_procs, &proc_list);
+ break;
+
+ case SEARCH_TYPE_COPYRIGHT:
+ gimp_browser_show_message (browser, _("Searching by copyright"));
+
+ gimp_procedural_db_query (".*", ".*", ".*", ".*", query_text, ".*", ".*",
+ &num_procs, &proc_list);
+ break;
+
+ case SEARCH_TYPE_DATE:
+ gimp_browser_show_message (browser, _("Searching by date"));
+
+ gimp_procedural_db_query (".*", ".*", ".*", ".*", ".*", query_text, ".*",
+ &num_procs, &proc_list);
+ break;
+
+ case SEARCH_TYPE_PROC_TYPE:
+ gimp_browser_show_message (browser, _("Searching by type"));
+
+ gimp_procedural_db_query (".*", ".*", ".*", ".*", ".*", ".*", query_text,
+ &num_procs, &proc_list);
+ break;
+ }
+
+ if (! query_text || strlen (query_text) == 0)
+ {
+ str = g_strdup_printf (dngettext (GETTEXT_PACKAGE "-libgimp",
+ "%d procedure",
+ "%d procedures",
+ num_procs), num_procs);
+ }
+ else
+ {
+ switch (num_procs)
+ {
+ case 0:
+ str = g_strdup (_("No matches for your query"));
+ break;
+ default:
+ str = g_strdup_printf (dngettext (GETTEXT_PACKAGE "-libgimp",
+ "%d procedure matches your query",
+ "%d procedures match your query",
+ num_procs), num_procs);
+ break;
+ }
+ }
+
+ gtk_label_set_text (GTK_LABEL (browser->count_label), str);
+ g_free (str);
+
+ if (num_procs > 0)
+ {
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ gint i;
+
+ dialog->store = gtk_list_store_new (N_COLUMNS,
+ G_TYPE_STRING);
+ gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->tree_view),
+ GTK_TREE_MODEL (dialog->store));
+ g_object_unref (dialog->store);
+
+ for (i = 0; i < num_procs; i++)
+ {
+ gtk_list_store_append (dialog->store, &iter);
+ gtk_list_store_set (dialog->store, &iter,
+ COLUMN_PROC_NAME, proc_list[i],
+ -1);
+ }
+
+ g_strfreev (proc_list);
+
+ gtk_tree_view_columns_autosize (GTK_TREE_VIEW (dialog->tree_view));
+
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (dialog->store),
+ COLUMN_PROC_NAME,
+ GTK_SORT_ASCENDING);
+
+ gtk_tree_model_get_iter_first (GTK_TREE_MODEL (dialog->store), &iter);
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->tree_view));
+ gtk_tree_selection_select_iter (selection, &iter);
+ }
+ else
+ {
+ gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->tree_view), NULL);
+ dialog->store = NULL;
+
+ gimp_browser_show_message (browser, _("No matches"));
+ }
+}
diff --git a/libgimp/gimpprocbrowserdialog.h b/libgimp/gimpprocbrowserdialog.h
new file mode 100644
index 0000000..cf096c3
--- /dev/null
+++ b/libgimp/gimpprocbrowserdialog.h
@@ -0,0 +1,82 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpprocbrowserdialog.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_UI_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimpui.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PROC_BROWSER_DIALOG_H__
+#define __GIMP_PROC_BROWSER_DIALOG_H__
+
+G_BEGIN_DECLS
+
+
+/* For information look into the C source or the html documentation */
+
+
+#define GIMP_TYPE_PROC_BROWSER_DIALOG (gimp_proc_browser_dialog_get_type ())
+#define GIMP_PROC_BROWSER_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_PROC_BROWSER_DIALOG, GimpProcBrowserDialog))
+#define GIMP_PROC_BROWSER_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_PROC_BROWSER_DIALOG, GimpProcBrowserDialogClass))
+#define GIMP_IS_PROC_BROWSER_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_PROC_BROWSER_DIALOG))
+#define GIMP_IS_PROC_BROWSER_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_PROC_BROWSER_DIALOG))
+#define GIMP_PROC_BROWSER_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_PROC_BROWSER_DIALOG, GimpProcBrowserDialogClass))
+
+
+typedef struct _GimpProcBrowserDialogClass GimpProcBrowserDialogClass;
+
+struct _GimpProcBrowserDialog
+{
+ GimpDialog parent_instance;
+
+ GtkWidget *browser;
+
+ GtkListStore *store;
+ GtkWidget *tree_view;
+};
+
+struct _GimpProcBrowserDialogClass
+{
+ GimpDialogClass parent_class;
+
+ void (* selection_changed) (GimpProcBrowserDialog *dialog);
+ void (* row_activated) (GimpProcBrowserDialog *dialog);
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_proc_browser_dialog_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_proc_browser_dialog_new (const gchar *title,
+ const gchar *role,
+ GimpHelpFunc help_func,
+ const gchar *help_id,
+ ...) G_GNUC_NULL_TERMINATED;
+
+gchar * gimp_proc_browser_dialog_get_selected (GimpProcBrowserDialog *dialog);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PROC_BROWSER_DIALOG_H__ */
diff --git a/libgimp/gimpproceduraldb.c b/libgimp/gimpproceduraldb.c
new file mode 100644
index 0000000..4605af1
--- /dev/null
+++ b/libgimp/gimpproceduraldb.c
@@ -0,0 +1,173 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2000 Peter Mattis and Spencer Kimball
+ *
+ * gimpproceduraldb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h> /* memcmp */
+
+#include "gimp.h"
+
+/**
+ * gimp_procedural_db_proc_info:
+ * @procedure: The procedure name.
+ * @blurb: A short blurb.
+ * @help: Detailed procedure help.
+ * @author: Author(s) of the procedure.
+ * @copyright: The copyright.
+ * @date: Copyright date.
+ * @proc_type: The procedure type.
+ * @num_args: The number of input arguments.
+ * @num_values: The number of return values.
+ * @args: The input arguments.
+ * @return_vals: The return values.
+ *
+ * Queries the procedural database for information on the specified
+ * procedure.
+ *
+ * This procedure returns information on the specified procedure. A
+ * short blurb, detailed help, author(s), copyright information,
+ * procedure type, number of input, and number of return values are
+ * returned. Additionally this function returns specific information
+ * about each input argument and return value.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+gimp_procedural_db_proc_info (const gchar *procedure,
+ gchar **blurb,
+ gchar **help,
+ gchar **author,
+ gchar **copyright,
+ gchar **date,
+ GimpPDBProcType *proc_type,
+ gint *num_args,
+ gint *num_values,
+ GimpParamDef **args,
+ GimpParamDef **return_vals)
+{
+ gint i;
+ gboolean success = TRUE;
+
+ success = _gimp_procedural_db_proc_info (procedure,
+ blurb,
+ help,
+ author,
+ copyright,
+ date,
+ proc_type,
+ num_args,
+ num_values);
+
+ if (success)
+ {
+ *args = g_new (GimpParamDef, *num_args);
+ *return_vals = g_new (GimpParamDef, *num_values);
+
+ for (i = 0; i < *num_args; i++)
+ {
+ if (! gimp_procedural_db_proc_arg (procedure,
+ i,
+ &(*args)[i].type,
+ &(*args)[i].name,
+ &(*args)[i].description))
+ {
+ g_free (*args);
+ g_free (*return_vals);
+
+ return FALSE;
+ }
+ }
+
+ for (i = 0; i < *num_values; i++)
+ {
+ if (! gimp_procedural_db_proc_val (procedure,
+ i,
+ &(*return_vals)[i].type,
+ &(*return_vals)[i].name,
+ &(*return_vals)[i].description))
+ {
+ g_free (*args);
+ g_free (*return_vals);
+
+ return FALSE;
+ }
+ }
+ }
+
+ return success;
+}
+
+/**
+ * gimp_procedural_db_get_data:
+ * @identifier: The identifier associated with data.
+ * @data: A byte array containing data.
+ *
+ * Returns data associated with the specified identifier.
+ *
+ * This procedure returns any data which may have been associated with
+ * the specified identifier. The data is copied into the given memory
+ * location.
+ *
+ * Returns: TRUE on success, FALSE if no data has been associated with
+ * the identifier
+ */
+gboolean
+gimp_procedural_db_get_data (const gchar *identifier,
+ gpointer data)
+{
+ gint size;
+ guint8 *hack;
+ gboolean success;
+
+ success = _gimp_procedural_db_get_data (identifier,
+ &size,
+ &hack);
+ if (hack)
+ {
+ memcpy (data, (gconstpointer) hack, size * sizeof (guint8));
+ g_free (hack);
+ }
+
+ return success;
+}
+
+/**
+ * gimp_procedural_db_set_data:
+ * @identifier: The identifier associated with data.
+ * @data: A byte array containing data.
+ * @bytes: The number of bytes in the data
+ *
+ * Associates the specified identifier with the supplied data.
+ *
+ * This procedure associates the supplied data with the provided
+ * identifier. The data may be subsequently retrieved by a call to
+ * 'procedural-db-get-data'.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+gimp_procedural_db_set_data (const gchar *identifier,
+ gconstpointer data,
+ guint32 bytes)
+{
+ return _gimp_procedural_db_set_data (identifier,
+ bytes,
+ data);
+}
diff --git a/libgimp/gimpproceduraldb.h b/libgimp/gimpproceduraldb.h
new file mode 100644
index 0000000..70e6005
--- /dev/null
+++ b/libgimp/gimpproceduraldb.h
@@ -0,0 +1,53 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2000 Peter Mattis and Spencer Kimball
+ *
+ * gimpproceduraldb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PROCEDURAL_DB_H__
+#define __GIMP_PROCEDURAL_DB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_procedural_db_proc_info (const gchar *procedure,
+ gchar **blurb,
+ gchar **help,
+ gchar **author,
+ gchar **copyright,
+ gchar **date,
+ GimpPDBProcType *proc_type,
+ gint *num_args,
+ gint *num_values,
+ GimpParamDef **args,
+ GimpParamDef **return_vals);
+gboolean gimp_procedural_db_get_data (const gchar *identifier,
+ gpointer data);
+gboolean gimp_procedural_db_set_data (const gchar *identifier,
+ gconstpointer data,
+ guint32 bytes);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PROCEDURAL_DB_H__ */
diff --git a/libgimp/gimpproceduraldb_pdb.c b/libgimp/gimpproceduraldb_pdb.c
new file mode 100644
index 0000000..32f47cf
--- /dev/null
+++ b/libgimp/gimpproceduraldb_pdb.c
@@ -0,0 +1,501 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpproceduraldb_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpproceduraldb
+ * @title: gimpproceduraldb
+ * @short_description: Functions for querying and changing procedural database (PDB) entries.
+ *
+ * Functions for querying and changing procedural database (PDB)
+ * entries.
+ **/
+
+
+/**
+ * gimp_procedural_db_temp_name:
+ *
+ * Generates a unique temporary PDB name.
+ *
+ * This procedure generates a temporary PDB entry name that is
+ * guaranteed to be unique.
+ *
+ * Returns: A unique temporary name for a temporary PDB entry.
+ **/
+gchar *
+gimp_procedural_db_temp_name (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *temp_name = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-procedural-db-temp-name",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ temp_name = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return temp_name;
+}
+
+/**
+ * gimp_procedural_db_dump:
+ * @filename: The dump filename.
+ *
+ * Dumps the current contents of the procedural database
+ *
+ * This procedure dumps the contents of the procedural database to the
+ * specified file. The file will contain all of the information
+ * provided for each registered procedure.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_procedural_db_dump (const gchar *filename)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-procedural-db-dump",
+ &nreturn_vals,
+ GIMP_PDB_STRING, filename,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_procedural_db_query:
+ * @name: The regex for procedure name.
+ * @blurb: The regex for procedure blurb.
+ * @help: The regex for procedure help.
+ * @author: The regex for procedure author.
+ * @copyright: The regex for procedure copyright.
+ * @date: The regex for procedure date.
+ * @proc_type: The regex for procedure type: { 'Internal GIMP procedure', 'GIMP Plug-in', 'GIMP Extension', 'Temporary Procedure' }.
+ * @num_matches: The number of matching procedures.
+ * @procedure_names: The list of procedure names.
+ *
+ * Queries the procedural database for its contents using regular
+ * expression matching.
+ *
+ * This procedure queries the contents of the procedural database. It
+ * is supplied with seven arguments matching procedures on { name,
+ * blurb, help, author, copyright, date, procedure type}. This is
+ * accomplished using regular expression matching. For instance, to
+ * find all procedures with \"jpeg\" listed in the blurb, all seven
+ * arguments can be supplied as \".*\", except for the second, which
+ * can be supplied as \".*jpeg.*\". There are two return arguments for
+ * this procedure. The first is the number of procedures matching the
+ * query. The second is a concatenated list of procedure names
+ * corresponding to those matching the query. If no matching entries
+ * are found, then the returned string is NULL and the number of
+ * entries is 0.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_procedural_db_query (const gchar *name,
+ const gchar *blurb,
+ const gchar *help,
+ const gchar *author,
+ const gchar *copyright,
+ const gchar *date,
+ const gchar *proc_type,
+ gint *num_matches,
+ gchar ***procedure_names)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+ gint i;
+
+ return_vals = gimp_run_procedure ("gimp-procedural-db-query",
+ &nreturn_vals,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_STRING, blurb,
+ GIMP_PDB_STRING, help,
+ GIMP_PDB_STRING, author,
+ GIMP_PDB_STRING, copyright,
+ GIMP_PDB_STRING, date,
+ GIMP_PDB_STRING, proc_type,
+ GIMP_PDB_END);
+
+ *num_matches = 0;
+ *procedure_names = NULL;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *num_matches = return_vals[1].data.d_int32;
+ if (*num_matches > 0)
+ {
+ *procedure_names = g_new0 (gchar *, *num_matches + 1);
+ for (i = 0; i < *num_matches; i++)
+ (*procedure_names)[i] = g_strdup (return_vals[2].data.d_stringarray[i]);
+ }
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_procedural_db_proc_exists:
+ * @procedure_name: The procedure name.
+ *
+ * Checks if the specified procedure exists in the procedural database
+ *
+ * This procedure checks if the specified procedure is registered in
+ * the procedural database.
+ *
+ * Returns: Whether a procedure of that name is registered.
+ *
+ * Since: 2.6
+ **/
+gboolean
+gimp_procedural_db_proc_exists (const gchar *procedure_name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean exists = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-procedural-db-proc-exists",
+ &nreturn_vals,
+ GIMP_PDB_STRING, procedure_name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ exists = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return exists;
+}
+
+/**
+ * _gimp_procedural_db_proc_info:
+ * @procedure_name: The procedure name.
+ * @blurb: A short blurb.
+ * @help: Detailed procedure help.
+ * @author: Author(s) of the procedure.
+ * @copyright: The copyright.
+ * @date: Copyright date.
+ * @proc_type: The procedure type.
+ * @num_args: The number of input arguments.
+ * @num_values: The number of return values.
+ *
+ * Queries the procedural database for information on the specified
+ * procedure.
+ *
+ * This procedure returns information on the specified procedure. A
+ * short blurb, detailed help, author(s), copyright information,
+ * procedure type, number of input, and number of return values are
+ * returned. For specific information on each input argument and return
+ * value, use the gimp_procedural_db_proc_arg() and
+ * gimp_procedural_db_proc_val() procedures.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+_gimp_procedural_db_proc_info (const gchar *procedure_name,
+ gchar **blurb,
+ gchar **help,
+ gchar **author,
+ gchar **copyright,
+ gchar **date,
+ GimpPDBProcType *proc_type,
+ gint *num_args,
+ gint *num_values)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-procedural-db-proc-info",
+ &nreturn_vals,
+ GIMP_PDB_STRING, procedure_name,
+ GIMP_PDB_END);
+
+ *blurb = NULL;
+ *help = NULL;
+ *author = NULL;
+ *copyright = NULL;
+ *date = NULL;
+ *proc_type = 0;
+ *num_args = 0;
+ *num_values = 0;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *blurb = g_strdup (return_vals[1].data.d_string);
+ *help = g_strdup (return_vals[2].data.d_string);
+ *author = g_strdup (return_vals[3].data.d_string);
+ *copyright = g_strdup (return_vals[4].data.d_string);
+ *date = g_strdup (return_vals[5].data.d_string);
+ *proc_type = return_vals[6].data.d_int32;
+ *num_args = return_vals[7].data.d_int32;
+ *num_values = return_vals[8].data.d_int32;
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_procedural_db_proc_arg:
+ * @procedure_name: The procedure name.
+ * @arg_num: The argument number.
+ * @arg_type: The type of argument.
+ * @arg_name: The name of the argument.
+ * @arg_desc: A description of the argument.
+ *
+ * Queries the procedural database for information on the specified
+ * procedure's argument.
+ *
+ * This procedure returns information on the specified procedure's
+ * argument. The argument type, name, and a description are retrieved.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_procedural_db_proc_arg (const gchar *procedure_name,
+ gint arg_num,
+ GimpPDBArgType *arg_type,
+ gchar **arg_name,
+ gchar **arg_desc)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-procedural-db-proc-arg",
+ &nreturn_vals,
+ GIMP_PDB_STRING, procedure_name,
+ GIMP_PDB_INT32, arg_num,
+ GIMP_PDB_END);
+
+ *arg_type = 0;
+ *arg_name = NULL;
+ *arg_desc = NULL;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *arg_type = return_vals[1].data.d_int32;
+ *arg_name = g_strdup (return_vals[2].data.d_string);
+ *arg_desc = g_strdup (return_vals[3].data.d_string);
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_procedural_db_proc_val:
+ * @procedure_name: The procedure name.
+ * @val_num: The return value number.
+ * @val_type: The type of return value.
+ * @val_name: The name of the return value.
+ * @val_desc: A description of the return value.
+ *
+ * Queries the procedural database for information on the specified
+ * procedure's return value.
+ *
+ * This procedure returns information on the specified procedure's
+ * return value. The return value type, name, and a description are
+ * retrieved.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_procedural_db_proc_val (const gchar *procedure_name,
+ gint val_num,
+ GimpPDBArgType *val_type,
+ gchar **val_name,
+ gchar **val_desc)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-procedural-db-proc-val",
+ &nreturn_vals,
+ GIMP_PDB_STRING, procedure_name,
+ GIMP_PDB_INT32, val_num,
+ GIMP_PDB_END);
+
+ *val_type = 0;
+ *val_name = NULL;
+ *val_desc = NULL;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *val_type = return_vals[1].data.d_int32;
+ *val_name = g_strdup (return_vals[2].data.d_string);
+ *val_desc = g_strdup (return_vals[3].data.d_string);
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * _gimp_procedural_db_get_data:
+ * @identifier: The identifier associated with data.
+ * @bytes: The number of bytes in the data.
+ * @data: A byte array containing data.
+ *
+ * Returns data associated with the specified identifier.
+ *
+ * This procedure returns any data which may have been associated with
+ * the specified identifier. The data is a variable length array of
+ * bytes. If no data has been associated with the identifier, an error
+ * is returned.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+_gimp_procedural_db_get_data (const gchar *identifier,
+ gint *bytes,
+ guint8 **data)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-procedural-db-get-data",
+ &nreturn_vals,
+ GIMP_PDB_STRING, identifier,
+ GIMP_PDB_END);
+
+ *bytes = 0;
+ *data = NULL;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *bytes = return_vals[1].data.d_int32;
+ *data = g_new (guint8, *bytes);
+ memcpy (*data,
+ return_vals[2].data.d_int8array,
+ *bytes * sizeof (guint8));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_procedural_db_get_data_size:
+ * @identifier: The identifier associated with data.
+ *
+ * Returns size of data associated with the specified identifier.
+ *
+ * This procedure returns the size of any data which may have been
+ * associated with the specified identifier. If no data has been
+ * associated with the identifier, an error is returned.
+ *
+ * Returns: The number of bytes in the data.
+ **/
+gint
+gimp_procedural_db_get_data_size (const gchar *identifier)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint bytes = 0;
+
+ return_vals = gimp_run_procedure ("gimp-procedural-db-get-data-size",
+ &nreturn_vals,
+ GIMP_PDB_STRING, identifier,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ bytes = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return bytes;
+}
+
+/**
+ * _gimp_procedural_db_set_data:
+ * @identifier: The identifier associated with data.
+ * @bytes: The number of bytes in the data.
+ * @data: A byte array containing data.
+ *
+ * Associates the specified identifier with the supplied data.
+ *
+ * This procedure associates the supplied data with the provided
+ * identifier. The data may be subsequently retrieved by a call to
+ * 'procedural-db-get-data'.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+_gimp_procedural_db_set_data (const gchar *identifier,
+ gint bytes,
+ const guint8 *data)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-procedural-db-set-data",
+ &nreturn_vals,
+ GIMP_PDB_STRING, identifier,
+ GIMP_PDB_INT32, bytes,
+ GIMP_PDB_INT8ARRAY, data,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimpproceduraldb_pdb.h b/libgimp/gimpproceduraldb_pdb.h
new file mode 100644
index 0000000..45b4c7b
--- /dev/null
+++ b/libgimp/gimpproceduraldb_pdb.h
@@ -0,0 +1,77 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpproceduraldb_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PROCEDURAL_DB_PDB_H__
+#define __GIMP_PROCEDURAL_DB_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gchar* gimp_procedural_db_temp_name (void);
+gboolean gimp_procedural_db_dump (const gchar *filename);
+gboolean gimp_procedural_db_query (const gchar *name,
+ const gchar *blurb,
+ const gchar *help,
+ const gchar *author,
+ const gchar *copyright,
+ const gchar *date,
+ const gchar *proc_type,
+ gint *num_matches,
+ gchar ***procedure_names);
+gboolean gimp_procedural_db_proc_exists (const gchar *procedure_name);
+G_GNUC_INTERNAL gboolean _gimp_procedural_db_proc_info (const gchar *procedure_name,
+ gchar **blurb,
+ gchar **help,
+ gchar **author,
+ gchar **copyright,
+ gchar **date,
+ GimpPDBProcType *proc_type,
+ gint *num_args,
+ gint *num_values);
+gboolean gimp_procedural_db_proc_arg (const gchar *procedure_name,
+ gint arg_num,
+ GimpPDBArgType *arg_type,
+ gchar **arg_name,
+ gchar **arg_desc);
+gboolean gimp_procedural_db_proc_val (const gchar *procedure_name,
+ gint val_num,
+ GimpPDBArgType *val_type,
+ gchar **val_name,
+ gchar **val_desc);
+G_GNUC_INTERNAL gboolean _gimp_procedural_db_get_data (const gchar *identifier,
+ gint *bytes,
+ guint8 **data);
+gint gimp_procedural_db_get_data_size (const gchar *identifier);
+G_GNUC_INTERNAL gboolean _gimp_procedural_db_set_data (const gchar *identifier,
+ gint bytes,
+ const guint8 *data);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PROCEDURAL_DB_PDB_H__ */
diff --git a/libgimp/gimpprocview.c b/libgimp/gimpprocview.c
new file mode 100644
index 0000000..fab2e6b
--- /dev/null
+++ b/libgimp/gimpprocview.c
@@ -0,0 +1,339 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpprocview.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * dbbrowser_utils.c
+ * 0.08 26th sept 97 by Thomas NOEL <thomas@minet.net>
+ *
+ * 98/12/13 Sven Neumann <sven@gimp.org> : added help display
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "gimp.h"
+
+#include "gimpuitypes.h"
+#include "gimpprocview.h"
+
+#include "libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpprocview
+ * @title: GimpProcView
+ * @short_description: A widget showing information about a PDB procedure.
+ *
+ * A widget showing information about a PDB procedure, mainly for the
+ * procedure and plug-in browsers.
+ **/
+
+
+/* local function prototypes */
+
+static GtkWidget * gimp_proc_view_create_params (const GimpParamDef *params,
+ gint n_params,
+ GtkSizeGroup *name_group,
+ GtkSizeGroup *type_group,
+ GtkSizeGroup *desc_group);
+
+
+/* public functions */
+
+
+/**
+ * gimp_proc_view_new:
+ * @name:
+ * @menu_path:
+ * @blurb:
+ * @help:
+ * @author:
+ * @copyright:
+ * @date:
+ * @type:
+ * @n_params:
+ * @n_return_vals:
+ * @params:
+ * @return_vals:
+ *
+ * Return value: a new widget providing a view on a GIMP procedure
+ *
+ * Since: 2.4
+ **/
+GtkWidget *
+gimp_proc_view_new (const gchar *name,
+ const gchar *menu_path,
+ const gchar *blurb,
+ const gchar *help,
+ const gchar *author,
+ const gchar *copyright,
+ const gchar *date,
+ GimpPDBProcType type,
+ gint n_params,
+ gint n_return_vals,
+ const GimpParamDef *params,
+ const GimpParamDef *return_vals)
+{
+ GtkWidget *main_vbox;
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GtkWidget *table;
+ GtkWidget *label;
+ GtkSizeGroup *name_group;
+ GtkSizeGroup *type_group;
+ GtkSizeGroup *desc_group;
+ const gchar *type_str;
+ gint row;
+
+ if (blurb && strlen (blurb) < 2) blurb = NULL;
+ if (help && strlen (help) < 2) help = NULL;
+ if (author && strlen (author) < 2) author = NULL;
+ if (date && strlen (date) < 2) date = NULL;
+ if (copyright && strlen (copyright) < 2) copyright = NULL;
+
+ if (blurb && help && ! strcmp (blurb, help))
+ help = NULL;
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+
+ /* show the name */
+
+ frame = gimp_frame_new (name);
+ label = gtk_frame_get_label_widget (GTK_FRAME (frame));
+ gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 8);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ if (! gimp_enum_get_value (GIMP_TYPE_PDB_PROC_TYPE, type,
+ NULL, NULL, &type_str, NULL))
+ type_str = "UNKNOWN";
+
+ label = gtk_label_new (type_str);
+ gimp_label_set_attributes (GTK_LABEL (label),
+ PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
+ -1);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ if (menu_path)
+ {
+ label = gtk_label_new_with_mnemonic (menu_path);
+ gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+ }
+
+ if (blurb)
+ {
+ label = gtk_label_new (blurb);
+ gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+ }
+
+ name_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+ type_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+ desc_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+ /* in parameters */
+ if (n_params)
+ {
+ frame = gimp_frame_new (_("Parameters"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gimp_proc_view_create_params (params, n_params,
+ name_group, type_group, desc_group);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+ }
+
+ /* out parameters */
+ if (n_return_vals)
+ {
+ frame = gimp_frame_new (_("Return Values"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gimp_proc_view_create_params (return_vals, n_return_vals,
+ name_group, type_group, desc_group);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+ }
+
+ if (! help && ! author && ! date && ! copyright)
+ return main_vbox;
+
+ frame = gimp_frame_new (_("Additional Information"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 8);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ /* show the help */
+ if (help)
+ {
+ label = gtk_label_new (help);
+ gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+ }
+
+ /* show the author & the copyright */
+
+ if (! author && ! date && ! copyright)
+ return main_vbox;
+
+ table = gtk_table_new (0, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 4);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ row = 0;
+
+ if (author)
+ {
+ label = gtk_label_new (author);
+ gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_label_set_yalign (GTK_LABEL (label), 0.0);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, row++,
+ _("Author:"), 0.0, 0.0,
+ label, 3, FALSE);
+ }
+
+ if (date)
+ {
+ label = gtk_label_new (date);
+ gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_label_set_yalign (GTK_LABEL (label), 0.0);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, row++,
+ _("Date:"), 0.0, 0.0,
+ label, 3, FALSE);
+ }
+
+ if (copyright)
+ {
+ label = gtk_label_new (copyright);
+ gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_label_set_yalign (GTK_LABEL (label), 0.0);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, row++,
+ _("Copyright:"), 0.0, 0.0,
+ label, 3, FALSE);
+ }
+
+ return main_vbox;
+}
+
+
+/* private functions */
+
+static GtkWidget *
+gimp_proc_view_create_params (const GimpParamDef *params,
+ gint n_params,
+ GtkSizeGroup *name_group,
+ GtkSizeGroup *type_group,
+ GtkSizeGroup *desc_group)
+{
+ GtkWidget *table;
+ gint i;
+
+ table = gtk_table_new (n_params, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 4);
+
+ for (i = 0; i < n_params; i++)
+ {
+ GtkWidget *label;
+ const gchar *type;
+ gchar *upper;
+
+ /* name */
+ label = gtk_label_new (params[i].name);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_label_set_yalign (GTK_LABEL (label), 0.0);
+ gtk_size_group_add_widget (name_group, label);
+ gtk_table_attach (GTK_TABLE (table), label,
+ 0, 1, i, i + 1, GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ /* type */
+ if (! gimp_enum_get_value (GIMP_TYPE_PDB_ARG_TYPE, params[i].type,
+ NULL, &type, NULL, NULL))
+ upper = g_strdup ("UNKNOWN");
+ else
+ upper = g_ascii_strup (type, -1);
+
+ label = gtk_label_new (upper);
+ g_free (upper);
+
+ gimp_label_set_attributes (GTK_LABEL (label),
+ PANGO_ATTR_FAMILY, "monospace",
+ PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
+ -1);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_label_set_yalign (GTK_LABEL (label), 0.0);
+ gtk_size_group_add_widget (type_group, label);
+ gtk_table_attach (GTK_TABLE (table), label,
+ 1, 2, i, i + 1, GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ /* description */
+ label = gtk_label_new (params[i].description);
+ gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_label_set_yalign (GTK_LABEL (label), 0.0);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_size_group_add_widget (desc_group, label);
+ gtk_table_attach (GTK_TABLE (table), label,
+ 2, 3, i, i + 1, GTK_SHRINK | GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+ }
+
+ return table;
+}
diff --git a/libgimp/gimpprocview.h b/libgimp/gimpprocview.h
new file mode 100644
index 0000000..7499297
--- /dev/null
+++ b/libgimp/gimpprocview.h
@@ -0,0 +1,50 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpprocview.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_UI_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimpui.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PROC_VIEW_H__
+#define __GIMP_PROC_VIEW_H__
+
+G_BEGIN_DECLS
+
+
+/* For information look into the C source or the html documentation */
+
+
+GtkWidget * gimp_proc_view_new (const gchar *name,
+ const gchar *menu_path,
+ const gchar *blurb,
+ const gchar *help,
+ const gchar *author,
+ const gchar *copyright,
+ const gchar *date,
+ GimpPDBProcType type,
+ gint n_params,
+ gint n_return_vals,
+ const GimpParamDef *params,
+ const GimpParamDef *return_vals);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PROC_VIEW_H__ */
diff --git a/libgimp/gimpprogress.c b/libgimp/gimpprogress.c
new file mode 100644
index 0000000..58846c4
--- /dev/null
+++ b/libgimp/gimpprogress.c
@@ -0,0 +1,452 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpprogress.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#undef GIMP_DISABLE_DEPRECATED
+#include "gimpprogress.h"
+#define GIMP_DISABLE_DEPRECATED
+
+#include "gimp.h"
+
+
+typedef struct
+{
+ gchar *progress_callback;
+ GimpProgressVtable vtable;
+ gpointer data;
+} GimpProgressData;
+
+
+/* local function prototypes */
+
+static void gimp_progress_data_free (GimpProgressData *data);
+
+static void gimp_temp_progress_run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+
+/* private variables */
+
+static GHashTable * gimp_progress_ht = NULL;
+static gdouble gimp_progress_current = 0.0;
+static const gdouble gimp_progress_step = (1.0 / 256.0);
+
+
+/* public functions */
+
+/**
+ * gimp_progress_install:
+ * @start_callback: the function to call when progress starts
+ * @end_callback: the function to call when progress finishes
+ * @text_callback: the function to call to change the text
+ * @value_callback: the function to call to change the value
+ * @user_data: a pointer that is returned when uninstalling the progress
+ *
+ * Note that since GIMP 2.4, @value_callback can be called with
+ * negative values. This is triggered by calls to gimp_progress_pulse().
+ * The callback should then implement a progress indicating business,
+ * e.g. by calling gtk_progress_bar_pulse().
+ *
+ * Return value: the name of the temporary procedure that's been installed
+ *
+ * Since: 2.2
+ **/
+const gchar *
+gimp_progress_install (GimpProgressStartCallback start_callback,
+ GimpProgressEndCallback end_callback,
+ GimpProgressTextCallback text_callback,
+ GimpProgressValueCallback value_callback,
+ gpointer user_data)
+{
+ GimpProgressVtable vtable = { 0, };
+
+ g_return_val_if_fail (start_callback != NULL, NULL);
+ g_return_val_if_fail (end_callback != NULL, NULL);
+ g_return_val_if_fail (text_callback != NULL, NULL);
+ g_return_val_if_fail (value_callback != NULL, NULL);
+
+ vtable.start = start_callback;
+ vtable.end = end_callback;
+ vtable.set_text = text_callback;
+ vtable.set_value = value_callback;
+
+ return gimp_progress_install_vtable (&vtable, user_data);
+}
+
+/**
+ * gimp_progress_install_vtable:
+ * @vtable: a pointer to a @GimpProgressVtable.
+ * @user_data: a pointer that is passed as user_data to all vtable functions.
+ *
+ * Return value: the name of the temporary procedure that's been installed
+ *
+ * Since: 2.4
+ **/
+const gchar *
+gimp_progress_install_vtable (const GimpProgressVtable *vtable,
+ gpointer user_data)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "command", "" },
+ { GIMP_PDB_STRING, "text", "" },
+ { GIMP_PDB_FLOAT, "value", "" }
+ };
+
+ static const GimpParamDef values[] =
+ {
+ { GIMP_PDB_FLOAT, "value", "" }
+ };
+
+ gchar *progress_callback;
+
+ g_return_val_if_fail (vtable != NULL, NULL);
+ g_return_val_if_fail (vtable->start != NULL, NULL);
+ g_return_val_if_fail (vtable->end != NULL, NULL);
+ g_return_val_if_fail (vtable->set_text != NULL, NULL);
+ g_return_val_if_fail (vtable->set_value != NULL, NULL);
+
+ progress_callback = gimp_procedural_db_temp_name ();
+
+ gimp_install_temp_proc (progress_callback,
+ "Temporary progress callback procedure",
+ "",
+ "",
+ "",
+ "",
+ NULL,
+ "",
+ GIMP_TEMPORARY,
+ G_N_ELEMENTS (args), G_N_ELEMENTS (values),
+ args, values,
+ gimp_temp_progress_run);
+
+ if (_gimp_progress_install (progress_callback))
+ {
+ GimpProgressData *progress_data;
+
+ gimp_extension_enable (); /* Allow callbacks to be watched */
+
+ /* Now add to hash table so we can find it again */
+ if (! gimp_progress_ht)
+ {
+ gimp_progress_ht =
+ g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free,
+ (GDestroyNotify) gimp_progress_data_free);
+ }
+
+ progress_data = g_slice_new0 (GimpProgressData);
+
+ progress_data->progress_callback = progress_callback;
+ progress_data->vtable.start = vtable->start;
+ progress_data->vtable.end = vtable->end;
+ progress_data->vtable.set_text = vtable->set_text;
+ progress_data->vtable.set_value = vtable->set_value;
+ progress_data->vtable.pulse = vtable->pulse;
+ progress_data->vtable.get_window = vtable->get_window;
+ progress_data->data = user_data;
+
+ g_hash_table_insert (gimp_progress_ht, progress_callback, progress_data);
+
+ return progress_callback;
+ }
+
+ gimp_uninstall_temp_proc (progress_callback);
+ g_free (progress_callback);
+
+ return NULL;
+}
+
+/**
+ * gimp_progress_uninstall:
+ * @progress_callback: the name of the temporary procedure to uninstall
+ *
+ * Uninstalls a temporary progress procedure that was installed using
+ * gimp_progress_install().
+ *
+ * Return value: the @user_data that was passed to gimp_progress_install().
+ *
+ * Since: 2.2
+ **/
+gpointer
+gimp_progress_uninstall (const gchar *progress_callback)
+{
+ GimpProgressData *progress_data;
+ gpointer user_data;
+
+ g_return_val_if_fail (progress_callback != NULL, NULL);
+ g_return_val_if_fail (gimp_progress_ht != NULL, NULL);
+
+ progress_data = g_hash_table_lookup (gimp_progress_ht, progress_callback);
+
+ if (! progress_data)
+ {
+ g_warning ("Can't find internal progress data");
+ return NULL;
+ }
+
+ _gimp_progress_uninstall (progress_callback);
+ gimp_uninstall_temp_proc (progress_callback);
+
+ user_data = progress_data->data;
+
+ g_hash_table_remove (gimp_progress_ht, progress_callback);
+
+ return user_data;
+}
+
+
+/**
+ * gimp_progress_init:
+ * @message: Message to use in the progress dialog.
+ *
+ * Initializes the progress bar for the current plug-in.
+ *
+ * Initializes the progress bar for the current plug-in. It is only
+ * valid to call this procedure from a plug-in.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+gimp_progress_init (const gchar *message)
+{
+ gimp_progress_current = 0.0;
+
+ return _gimp_progress_init (message, gimp_default_display ());
+}
+
+/**
+ * gimp_progress_init_printf:
+ * @format: a standard printf() format string
+ * @...: arguments for @format
+ *
+ * Initializes the progress bar for the current plug-in.
+ *
+ * Initializes the progress bar for the current plug-in. It is only
+ * valid to call this procedure from a plug-in.
+ *
+ * Returns: %TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_progress_init_printf (const gchar *format,
+ ...)
+{
+ gchar *text;
+ gboolean retval;
+ va_list args;
+
+ g_return_val_if_fail (format != NULL, FALSE);
+
+ va_start (args, format);
+ text = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ retval = gimp_progress_init (text);
+
+ g_free (text);
+
+ return retval;
+}
+
+/**
+ * gimp_progress_set_text_printf:
+ * @format: a standard printf() format string
+ * @...: arguments for @format
+ *
+ * Changes the text in the progress bar for the current plug-in.
+ *
+ * This function changes the text in the progress bar for the current
+ * plug-in. Unlike gimp_progress_init() it does not change the
+ * displayed value.
+ *
+ * Returns: %TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_progress_set_text_printf (const gchar *format,
+ ...)
+{
+ gchar *text;
+ gboolean retval;
+ va_list args;
+
+ g_return_val_if_fail (format != NULL, FALSE);
+
+ va_start (args, format);
+ text = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ retval = gimp_progress_set_text (text);
+
+ g_free (text);
+
+ return retval;
+}
+
+/**
+ * gimp_progress_update:
+ * @percentage: Percentage of progress completed (in the range from 0.0 to 1.0).
+ *
+ * Updates the progress bar for the current plug-in.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+gimp_progress_update (gdouble percentage)
+{
+ gboolean changed;
+
+ if (percentage <= 0.0)
+ {
+ changed = (gimp_progress_current != 0.0);
+ percentage = 0.0;
+ }
+ else if (percentage >= 1.0)
+ {
+ changed = (gimp_progress_current != 1.0);
+ percentage = 1.0;
+ }
+ else
+ {
+ changed =
+ (fabs (gimp_progress_current - percentage) > gimp_progress_step);
+
+#ifdef GIMP_UNSTABLE
+ if (! changed)
+ {
+ static gboolean warned = FALSE;
+ static gint count = 0;
+
+ count++;
+
+ if (count > 3 && ! warned)
+ {
+ g_printerr ("%s is updating the progress too often\n",
+ g_get_prgname ());
+ warned = TRUE;
+ }
+ }
+#endif
+ }
+
+ /* Suppress the update if the change was only marginal. */
+ if (! changed)
+ return TRUE;
+
+ gimp_progress_current = percentage;
+
+ return _gimp_progress_update (gimp_progress_current);
+}
+
+
+/* private functions */
+
+static void
+gimp_progress_data_free (GimpProgressData *data)
+{
+ g_slice_free (GimpProgressData, data);
+}
+
+static void
+gimp_temp_progress_run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpProgressData *progress_data;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_SUCCESS;
+
+ progress_data = g_hash_table_lookup (gimp_progress_ht, name);
+
+ if (! progress_data)
+ {
+ g_warning ("Can't find internal progress data");
+
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ else
+ {
+ GimpProgressCommand command = param[0].data.d_int32;
+
+ switch (command)
+ {
+ case GIMP_PROGRESS_COMMAND_START:
+ progress_data->vtable.start (param[1].data.d_string,
+ param[2].data.d_float != 0.0,
+ progress_data->data);
+ break;
+
+ case GIMP_PROGRESS_COMMAND_END:
+ progress_data->vtable.end (progress_data->data);
+ break;
+
+ case GIMP_PROGRESS_COMMAND_SET_TEXT:
+ progress_data->vtable.set_text (param[1].data.d_string,
+ progress_data->data);
+ break;
+
+ case GIMP_PROGRESS_COMMAND_SET_VALUE:
+ progress_data->vtable.set_value (param[2].data.d_float,
+ progress_data->data);
+ break;
+
+ case GIMP_PROGRESS_COMMAND_PULSE:
+ if (progress_data->vtable.pulse)
+ progress_data->vtable.pulse (progress_data->data);
+ else
+ progress_data->vtable.set_value (-1, progress_data->data);
+ break;
+
+ case GIMP_PROGRESS_COMMAND_GET_WINDOW:
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_FLOAT;
+
+ if (progress_data->vtable.get_window)
+ values[1].data.d_float =
+ (gdouble) progress_data->vtable.get_window (progress_data->data);
+ else
+ values[1].data.d_float = 0;
+ break;
+
+ default:
+ values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
+ break;
+ }
+ }
+}
diff --git a/libgimp/gimpprogress.h b/libgimp/gimpprogress.h
new file mode 100644
index 0000000..3808f05
--- /dev/null
+++ b/libgimp/gimpprogress.h
@@ -0,0 +1,94 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpprogress.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PROGRESS_H__
+#define __GIMP_PROGRESS_H__
+
+G_BEGIN_DECLS
+
+
+typedef struct _GimpProgressVtable GimpProgressVtable;
+
+struct _GimpProgressVtable
+{
+ void (* start) (const gchar *message,
+ gboolean cancelable,
+ gpointer user_data);
+ void (* end) (gpointer user_data);
+ void (* set_text) (const gchar *message,
+ gpointer user_data);
+ void (* set_value) (gdouble percentage,
+ gpointer user_data);
+ void (* pulse) (gpointer user_data);
+
+ guint32 (* get_window) (gpointer user_data);
+
+ /* Padding for future expansion. Must be initialized with NULL! */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+ void (* _gimp_reserved5) (void);
+ void (* _gimp_reserved6) (void);
+ void (* _gimp_reserved7) (void);
+ void (* _gimp_reserved8) (void);
+};
+
+
+const gchar * gimp_progress_install_vtable (const GimpProgressVtable *vtable,
+ gpointer user_data);
+gpointer gimp_progress_uninstall (const gchar *progress_callback);
+
+gboolean gimp_progress_init (const gchar *message);
+gboolean gimp_progress_init_printf (const gchar *format,
+ ...) G_GNUC_PRINTF (1, 2);
+
+gboolean gimp_progress_set_text_printf (const gchar *format,
+ ...) G_GNUC_PRINTF (1, 2);
+
+gboolean gimp_progress_update (gdouble percentage);
+
+
+#ifndef GIMP_DISABLE_DEPRECATED
+typedef void (* GimpProgressStartCallback) (const gchar *message,
+ gboolean cancelable,
+ gpointer user_data);
+typedef void (* GimpProgressEndCallback) (gpointer user_data);
+typedef void (* GimpProgressTextCallback) (const gchar *message,
+ gpointer user_data);
+typedef void (* GimpProgressValueCallback) (gdouble percentage,
+ gpointer user_data);
+
+GIMP_DEPRECATED_FOR(gimp_progress_install_vtable)
+const gchar * gimp_progress_install (GimpProgressStartCallback start_callback,
+ GimpProgressEndCallback end_callback,
+ GimpProgressTextCallback text_callback,
+ GimpProgressValueCallback value_callback,
+ gpointer user_data);
+#endif /* GIMP_DISABLE_DEPRECATED */
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PROGRESS_H__ */
diff --git a/libgimp/gimpprogress_pdb.c b/libgimp/gimpprogress_pdb.c
new file mode 100644
index 0000000..a35df17
--- /dev/null
+++ b/libgimp/gimpprogress_pdb.c
@@ -0,0 +1,324 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpprogress_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpprogress
+ * @title: gimpprogress
+ * @short_description: Functions for embedding the progress bar into a plug-in's GUI.
+ *
+ * Functions for embedding the progress bar into a plug-in's GUI.
+ **/
+
+
+/**
+ * _gimp_progress_init:
+ * @message: Message to use in the progress dialog.
+ * @gdisplay_ID: GimpDisplay to update progressbar in, or -1 for a separate window.
+ *
+ * Initializes the progress bar for the current plug-in.
+ *
+ * Initializes the progress bar for the current plug-in. It is only
+ * valid to call this procedure from a plug-in.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+_gimp_progress_init (const gchar *message,
+ gint32 gdisplay_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-progress-init",
+ &nreturn_vals,
+ GIMP_PDB_STRING, message,
+ GIMP_PDB_DISPLAY, gdisplay_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * _gimp_progress_update:
+ * @percentage: Percentage of progress completed which must be between 0.0 and 1.0.
+ *
+ * Updates the progress bar for the current plug-in.
+ *
+ * Updates the progress bar for the current plug-in. It is only valid
+ * to call this procedure from a plug-in.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+_gimp_progress_update (gdouble percentage)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-progress-update",
+ &nreturn_vals,
+ GIMP_PDB_FLOAT, percentage,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_progress_pulse:
+ *
+ * Pulses the progress bar for the current plug-in.
+ *
+ * Updates the progress bar for the current plug-in. It is only valid
+ * to call this procedure from a plug-in. Use this function instead of
+ * gimp_progress_update() if you cannot tell how much progress has been
+ * made. This usually causes the the progress bar to enter \"activity
+ * mode\", where a block bounces back and forth.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_progress_pulse (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-progress-pulse",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_progress_set_text:
+ * @message: Message to use in the progress dialog.
+ *
+ * Changes the text in the progress bar for the current plug-in.
+ *
+ * This function changes the text in the progress bar for the current
+ * plug-in. Unlike gimp_progress_init() it does not change the
+ * displayed value.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_progress_set_text (const gchar *message)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-progress-set-text",
+ &nreturn_vals,
+ GIMP_PDB_STRING, message,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_progress_end:
+ *
+ * Ends the progress bar for the current plug-in.
+ *
+ * Ends the progress display for the current plug-in. Most plug-ins
+ * don't need to call this, they just exit when the work is done. It is
+ * only valid to call this procedure from a plug-in.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_progress_end (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-progress-end",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_progress_get_window_handle:
+ *
+ * Returns the native window ID of the toplevel window this plug-in's
+ * progress is displayed in.
+ *
+ * This function returns the native window ID of the toplevel window
+ * this plug-in\'s progress is displayed in.
+ *
+ * Returns: The progress bar's toplevel window.
+ *
+ * Since: 2.2
+ **/
+gint
+gimp_progress_get_window_handle (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint window = 0;
+
+ return_vals = gimp_run_procedure ("gimp-progress-get-window-handle",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ window = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return window;
+}
+
+/**
+ * _gimp_progress_install:
+ * @progress_callback: The callback PDB proc to call.
+ *
+ * Installs a progress callback for the current plug-in.
+ *
+ * This function installs a temporary PDB procedure which will handle
+ * all progress calls made by this plug-in and any procedure it calls.
+ * Calling this function multiple times simply replaces the old
+ * progress callbacks.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+_gimp_progress_install (const gchar *progress_callback)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-progress-install",
+ &nreturn_vals,
+ GIMP_PDB_STRING, progress_callback,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * _gimp_progress_uninstall:
+ * @progress_callback: The name of the callback registered for this progress.
+ *
+ * Uninstalls the progress callback for the current plug-in.
+ *
+ * This function uninstalls any progress callback installed with
+ * gimp_progress_install() before.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+_gimp_progress_uninstall (const gchar *progress_callback)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-progress-uninstall",
+ &nreturn_vals,
+ GIMP_PDB_STRING, progress_callback,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_progress_cancel:
+ * @progress_callback: The name of the callback registered for this progress.
+ *
+ * Cancels a running progress.
+ *
+ * This function cancels the currently running progress.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_progress_cancel (const gchar *progress_callback)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-progress-cancel",
+ &nreturn_vals,
+ GIMP_PDB_STRING, progress_callback,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimpprogress_pdb.h b/libgimp/gimpprogress_pdb.h
new file mode 100644
index 0000000..010533a
--- /dev/null
+++ b/libgimp/gimpprogress_pdb.h
@@ -0,0 +1,49 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpprogress_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PROGRESS_PDB_H__
+#define __GIMP_PROGRESS_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+G_GNUC_INTERNAL gboolean _gimp_progress_init (const gchar *message,
+ gint32 gdisplay_ID);
+G_GNUC_INTERNAL gboolean _gimp_progress_update (gdouble percentage);
+gboolean gimp_progress_pulse (void);
+gboolean gimp_progress_set_text (const gchar *message);
+gboolean gimp_progress_end (void);
+gint gimp_progress_get_window_handle (void);
+G_GNUC_INTERNAL gboolean _gimp_progress_install (const gchar *progress_callback);
+G_GNUC_INTERNAL gboolean _gimp_progress_uninstall (const gchar *progress_callback);
+gboolean gimp_progress_cancel (const gchar *progress_callback);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PROGRESS_PDB_H__ */
diff --git a/libgimp/gimpprogressbar.c b/libgimp/gimpprogressbar.c
new file mode 100644
index 0000000..f989d2a
--- /dev/null
+++ b/libgimp/gimpprogressbar.c
@@ -0,0 +1,230 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpprogressbar.c
+ * Copyright (C) 2004 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#ifdef GDK_WINDOWING_WIN32
+#include <gdk/gdkwin32.h>
+#endif
+
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#endif
+
+#include "gimpuitypes.h"
+
+#include "gimp.h"
+
+#include "gimpprogressbar.h"
+
+
+/**
+ * SECTION: gimpprogressbar
+ * @title: GimpProgressBar
+ * @short_description: A widget providing a progress bar.
+ *
+ * A widget providing a progress bar that automatically redirects any
+ * progress calls to itself.
+ **/
+
+
+static void gimp_progress_bar_dispose (GObject *object);
+
+static void gimp_progress_bar_start (const gchar *message,
+ gboolean cancelable,
+ gpointer user_data);
+static void gimp_progress_bar_end (gpointer user_data);
+static void gimp_progress_bar_set_text (const gchar *message,
+ gpointer user_data);
+static void gimp_progress_bar_set_value (gdouble percentage,
+ gpointer user_data);
+static void gimp_progress_bar_pulse (gpointer user_data);
+static guint32 gimp_progress_bar_get_window (gpointer user_data);
+
+
+G_DEFINE_TYPE (GimpProgressBar, gimp_progress_bar, GTK_TYPE_PROGRESS_BAR)
+
+#define parent_class gimp_progress_bar_parent_class
+
+
+static void
+gimp_progress_bar_class_init (GimpProgressBarClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = gimp_progress_bar_dispose;
+}
+
+static void
+gimp_progress_bar_init (GimpProgressBar *bar)
+{
+ GimpProgressVtable vtable = { 0, };
+
+ gtk_progress_bar_set_text (GTK_PROGRESS_BAR (bar), " ");
+ gtk_progress_bar_set_ellipsize (GTK_PROGRESS_BAR (bar), PANGO_ELLIPSIZE_END);
+
+ vtable.start = gimp_progress_bar_start;
+ vtable.end = gimp_progress_bar_end;
+ vtable.set_text = gimp_progress_bar_set_text;
+ vtable.set_value = gimp_progress_bar_set_value;
+ vtable.pulse = gimp_progress_bar_pulse;
+ vtable.get_window = gimp_progress_bar_get_window;
+
+ bar->progress_callback = gimp_progress_install_vtable (&vtable, bar);
+}
+
+static void
+gimp_progress_bar_dispose (GObject *object)
+{
+ GimpProgressBar *bar = GIMP_PROGRESS_BAR (object);
+
+ if (bar->progress_callback)
+ {
+ gimp_progress_uninstall (bar->progress_callback);
+ bar->progress_callback = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gimp_progress_bar_start (const gchar *message,
+ gboolean cancelable,
+ gpointer user_data)
+{
+ GimpProgressBar *bar = GIMP_PROGRESS_BAR (user_data);
+
+ gtk_progress_bar_set_text (GTK_PROGRESS_BAR (bar), message ? message : " ");
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (bar), 0.0);
+
+ if (gtk_widget_is_drawable (GTK_WIDGET (bar)))
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+}
+
+static void
+gimp_progress_bar_end (gpointer user_data)
+{
+ GimpProgressBar *bar = GIMP_PROGRESS_BAR (user_data);
+
+ gtk_progress_bar_set_text (GTK_PROGRESS_BAR (bar), " ");
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (bar), 0.0);
+
+ if (gtk_widget_is_drawable (GTK_WIDGET (bar)))
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+}
+
+static void
+gimp_progress_bar_set_text (const gchar *message,
+ gpointer user_data)
+{
+ GimpProgressBar *bar = GIMP_PROGRESS_BAR (user_data);
+
+ gtk_progress_bar_set_text (GTK_PROGRESS_BAR (bar), message ? message : " ");
+
+ if (gtk_widget_is_drawable (GTK_WIDGET (bar)))
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+}
+
+static void
+gimp_progress_bar_set_value (gdouble percentage,
+ gpointer user_data)
+{
+ GimpProgressBar *bar = GIMP_PROGRESS_BAR (user_data);
+
+ if (percentage >= 0.0)
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (bar), percentage);
+ else
+ gtk_progress_bar_pulse (GTK_PROGRESS_BAR (bar));
+
+ if (gtk_widget_is_drawable (GTK_WIDGET (bar)))
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+}
+
+static void
+gimp_progress_bar_pulse (gpointer user_data)
+{
+ GimpProgressBar *bar = GIMP_PROGRESS_BAR (user_data);
+
+ gtk_progress_bar_pulse (GTK_PROGRESS_BAR (bar));
+
+ if (gtk_widget_is_drawable (GTK_WIDGET (bar)))
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+}
+
+static guint32
+gimp_window_get_native_id (GtkWindow *window)
+{
+ g_return_val_if_fail (GTK_IS_WINDOW (window), 0);
+
+#ifdef GDK_NATIVE_WINDOW_POINTER
+#ifdef __GNUC__
+#warning gimp_window_get_native() unimplementable for the target windowing system
+#endif
+#endif
+
+#ifdef GDK_WINDOWING_WIN32
+ if (window && gtk_widget_get_realized (GTK_WIDGET (window)))
+ return GDK_WINDOW_HWND (gtk_widget_get_window (GTK_WIDGET (window)));
+#endif
+
+#ifdef GDK_WINDOWING_X11
+ if (window && gtk_widget_get_realized (GTK_WIDGET (window)))
+ return GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (window)));
+#endif
+
+ return 0;
+}
+
+static guint32
+gimp_progress_bar_get_window (gpointer user_data)
+{
+ GimpProgressBar *bar = GIMP_PROGRESS_BAR (user_data);
+ GtkWidget *toplevel;
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (bar));
+
+ if (GTK_IS_WINDOW (toplevel))
+ return gimp_window_get_native_id (GTK_WINDOW (toplevel));
+
+ return 0;
+}
+
+/**
+ * gimp_progress_bar_new:
+ *
+ * Creates a new #GimpProgressBar widget.
+ *
+ * Return value: the new widget.
+ *
+ * Since: 2.2
+ **/
+GtkWidget *
+gimp_progress_bar_new (void)
+{
+ return g_object_new (GIMP_TYPE_PROGRESS_BAR, NULL);
+}
diff --git a/libgimp/gimpprogressbar.h b/libgimp/gimpprogressbar.h
new file mode 100644
index 0000000..3af45f2
--- /dev/null
+++ b/libgimp/gimpprogressbar.h
@@ -0,0 +1,69 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpprogressbar.h
+ * Copyright (C) 2004 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_UI_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimpui.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PROGRESS_BAR_H__
+#define __GIMP_PROGRESS_BAR_H__
+
+G_BEGIN_DECLS
+
+
+#define GIMP_TYPE_PROGRESS_BAR (gimp_progress_bar_get_type ())
+#define GIMP_PROGRESS_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_PROGRESS_BAR, GimpProgressBar))
+#define GIMP_PROGRESS_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_PROGRESS_BAR, GimpProgressBarClass))
+#define GIMP_IS_PROGRESS_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_PROGRESS_BAR))
+#define GIMP_IS_PROGRESS_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_PROGRESS_BAR))
+#define GIMP_PROGRESS_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_PROGRESS_BAR, GimpProgressBarClass))
+
+
+typedef struct _GimpProgressBarClass GimpProgressBarClass;
+
+struct _GimpProgressBar
+{
+ GtkProgressBar parent_instance;
+
+ const gchar *progress_callback;
+ gboolean cancelable;
+};
+
+struct _GimpProgressBarClass
+{
+ GtkProgressBarClass parent_class;
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_progress_bar_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_progress_bar_new (void);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PROGRESS_BAR_H__ */
diff --git a/libgimp/gimpregioniterator.c b/libgimp/gimpregioniterator.c
new file mode 100644
index 0000000..ad866c5
--- /dev/null
+++ b/libgimp/gimpregioniterator.c
@@ -0,0 +1,397 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpregioniterator.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+
+#define GIMP_DISABLE_DEPRECATION_WARNINGS
+
+#include "gimp.h"
+#include "gimpregioniterator.h"
+
+
+/**
+ * SECTION: gimpregioniterator
+ * @title: gimpregioniterator
+ * @short_description: Functions to traverse a pixel regions.
+ *
+ * The GimpRgnIterator functions provide a variety of common ways to
+ * traverse a PixelRegion, using a pre-defined function pointer per
+ * pixel.
+ **/
+
+
+struct _GimpRgnIterator
+{
+ GimpDrawable *drawable;
+ gint x1;
+ gint y1;
+ gint x2;
+ gint y2;
+};
+
+
+static void gimp_rgn_iterator_iter_single (GimpRgnIterator *iter,
+ GimpPixelRgn *srcPR,
+ GimpRgnFuncSrc func,
+ gpointer data);
+static void gimp_rgn_render_row (const guchar *src,
+ guchar *dest,
+ gint col,
+ gint bpp,
+ GimpRgnFunc2 func,
+ gpointer data);
+static void gimp_rgn_render_region (const GimpPixelRgn *srcPR,
+ const GimpPixelRgn *destPR,
+ GimpRgnFunc2 func,
+ gpointer data);
+
+
+/**
+ * gimp_rgn_iterator_new:
+ * @drawable: a #GimpDrawable
+ * @unused: ignored
+ *
+ * Creates a new #GimpRgnIterator for @drawable. The #GimpRunMode
+ * parameter is ignored. Use gimp_rgn_iterator_free() to free this
+ * iterator.
+ *
+ * Return value: a newly allocated #GimpRgnIterator.
+ **/
+GimpRgnIterator *
+gimp_rgn_iterator_new (GimpDrawable *drawable,
+ GimpRunMode unused)
+{
+ GimpRgnIterator *iter;
+
+ g_return_val_if_fail (drawable != NULL, NULL);
+
+ iter = g_slice_new (GimpRgnIterator);
+
+ iter->drawable = drawable;
+
+ gimp_drawable_mask_bounds (drawable->drawable_id,
+ &iter->x1, &iter->y1,
+ &iter->x2, &iter->y2);
+
+ return iter;
+}
+
+/**
+ * gimp_rgn_iterator_free:
+ * @iter: a #GimpRgnIterator
+ *
+ * Frees the resources allocated for @iter.
+ **/
+void
+gimp_rgn_iterator_free (GimpRgnIterator *iter)
+{
+ g_return_if_fail (iter != NULL);
+
+ g_slice_free (GimpRgnIterator, iter);
+}
+
+void
+gimp_rgn_iterator_src (GimpRgnIterator *iter,
+ GimpRgnFuncSrc func,
+ gpointer data)
+{
+ GimpPixelRgn srcPR;
+
+ g_return_if_fail (iter != NULL);
+
+ gimp_pixel_rgn_init (&srcPR, iter->drawable,
+ iter->x1, iter->y1,
+ iter->x2 - iter->x1, iter->y2 - iter->y1,
+ FALSE, FALSE);
+ gimp_rgn_iterator_iter_single (iter, &srcPR, func, data);
+}
+
+void
+gimp_rgn_iterator_src_dest (GimpRgnIterator *iter,
+ GimpRgnFuncSrcDest func,
+ gpointer data)
+{
+ GimpPixelRgn srcPR, destPR;
+ gint x1, y1, x2, y2;
+ gint bpp;
+ gint count;
+ gpointer pr;
+ gint total_area;
+ gint area_so_far;
+
+ g_return_if_fail (iter != NULL);
+
+ x1 = iter->x1;
+ y1 = iter->y1;
+ x2 = iter->x2;
+ y2 = iter->y2;
+
+ total_area = (x2 - x1) * (y2 - y1);
+ area_so_far = 0;
+
+ gimp_pixel_rgn_init (&srcPR, iter->drawable, x1, y1, x2 - x1, y2 - y1,
+ FALSE, FALSE);
+ gimp_pixel_rgn_init (&destPR, iter->drawable, x1, y1, x2 - x1, y2 - y1,
+ TRUE, TRUE);
+
+ bpp = srcPR.bpp;
+
+ for (pr = gimp_pixel_rgns_register (2, &srcPR, &destPR), count = 0;
+ pr != NULL;
+ pr = gimp_pixel_rgns_process (pr), count++)
+ {
+ const guchar *src = srcPR.data;
+ guchar *dest = destPR.data;
+ gint y;
+
+ for (y = srcPR.y; y < srcPR.y + srcPR.h; y++)
+ {
+ const guchar *s = src;
+ guchar *d = dest;
+ gint x;
+
+ for (x = srcPR.x; x < srcPR.x + srcPR.w; x++)
+ {
+ func (x, y, s, d, bpp, data);
+
+ s += bpp;
+ d += bpp;
+ }
+
+ src += srcPR.rowstride;
+ dest += destPR.rowstride;
+ }
+
+ area_so_far += srcPR.w * srcPR.h;
+
+ if ((count % 16) == 0)
+ gimp_progress_update ((gdouble) area_so_far / (gdouble) total_area);
+ }
+
+ gimp_drawable_flush (iter->drawable);
+ gimp_drawable_merge_shadow (iter->drawable->drawable_id, TRUE);
+ gimp_drawable_update (iter->drawable->drawable_id,
+ x1, y1, x2 - x1, y2 - y1);
+}
+
+void
+gimp_rgn_iterator_dest (GimpRgnIterator *iter,
+ GimpRgnFuncDest func,
+ gpointer data)
+{
+ GimpPixelRgn destPR;
+
+ g_return_if_fail (iter != NULL);
+
+ gimp_pixel_rgn_init (&destPR, iter->drawable,
+ iter->x1, iter->y1,
+ iter->x2 - iter->x1, iter->y2 - iter->y1,
+ TRUE, TRUE);
+ gimp_rgn_iterator_iter_single (iter, &destPR, (GimpRgnFuncSrc) func, data);
+
+ /* update the processed region */
+ gimp_drawable_flush (iter->drawable);
+ gimp_drawable_merge_shadow (iter->drawable->drawable_id, TRUE);
+ gimp_drawable_update (iter->drawable->drawable_id,
+ iter->x1, iter->y1,
+ iter->x2 - iter->x1, iter->y2 - iter->y1);
+}
+
+
+void
+gimp_rgn_iterate1 (GimpDrawable *drawable,
+ GimpRunMode unused,
+ GimpRgnFunc1 func,
+ gpointer data)
+{
+ GimpPixelRgn srcPR;
+ gint x1, y1, x2, y2;
+ gpointer pr;
+ gint total_area;
+ gint area_so_far;
+ gint count;
+
+ g_return_if_fail (drawable != NULL);
+
+ gimp_drawable_mask_bounds (drawable->drawable_id, &x1, &y1, &x2, &y2);
+
+ total_area = (x2 - x1) * (y2 - y1);
+ area_so_far = 0;
+
+ if (total_area <= 0)
+ return;
+
+ gimp_pixel_rgn_init (&srcPR, drawable,
+ x1, y1, (x2 - x1), (y2 - y1), FALSE, FALSE);
+
+ for (pr = gimp_pixel_rgns_register (1, &srcPR), count = 0;
+ pr != NULL;
+ pr = gimp_pixel_rgns_process (pr), count++)
+ {
+ const guchar *src = srcPR.data;
+ gint y;
+
+ for (y = 0; y < srcPR.h; y++)
+ {
+ const guchar *s = src;
+ gint x;
+
+ for (x = 0; x < srcPR.w; x++)
+ {
+ func (s, srcPR.bpp, data);
+ s += srcPR.bpp;
+ }
+
+ src += srcPR.rowstride;
+ }
+
+ area_so_far += srcPR.w * srcPR.h;
+
+ if ((count % 16) == 0)
+ gimp_progress_update ((gdouble) area_so_far / (gdouble) total_area);
+ }
+}
+
+void
+gimp_rgn_iterate2 (GimpDrawable *drawable,
+ GimpRunMode unused,
+ GimpRgnFunc2 func,
+ gpointer data)
+{
+ GimpPixelRgn srcPR, destPR;
+ gint x1, y1, x2, y2;
+ gpointer pr;
+ gint total_area;
+ gint area_so_far;
+ gint count;
+
+ g_return_if_fail (drawable != NULL);
+
+ gimp_drawable_mask_bounds (drawable->drawable_id, &x1, &y1, &x2, &y2);
+
+ total_area = (x2 - x1) * (y2 - y1);
+ area_so_far = 0;
+
+ if (total_area <= 0)
+ return;
+
+ /* Initialize the pixel regions. */
+ gimp_pixel_rgn_init (&srcPR, drawable, x1, y1, (x2 - x1), (y2 - y1),
+ FALSE, FALSE);
+ gimp_pixel_rgn_init (&destPR, drawable, x1, y1, (x2 - x1), (y2 - y1),
+ TRUE, TRUE);
+
+ for (pr = gimp_pixel_rgns_register (2, &srcPR, &destPR), count = 0;
+ pr != NULL;
+ pr = gimp_pixel_rgns_process (pr), count++)
+ {
+ gimp_rgn_render_region (&srcPR, &destPR, func, data);
+
+ area_so_far += srcPR.w * srcPR.h;
+
+ if ((count % 16) == 0)
+ gimp_progress_update ((gdouble) area_so_far / (gdouble) total_area);
+ }
+
+ /* update the processed region */
+ gimp_drawable_flush (drawable);
+ gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
+ gimp_drawable_update (drawable->drawable_id, x1, y1, (x2 - x1), (y2 - y1));
+}
+
+static void
+gimp_rgn_iterator_iter_single (GimpRgnIterator *iter,
+ GimpPixelRgn *srcPR,
+ GimpRgnFuncSrc func,
+ gpointer data)
+{
+ gpointer pr;
+ gint total_area;
+ gint area_so_far;
+ gint count;
+
+ total_area = (iter->x2 - iter->x1) * (iter->y2 - iter->y1);
+ area_so_far = 0;
+
+ for (pr = gimp_pixel_rgns_register (1, srcPR), count = 0;
+ pr != NULL;
+ pr = gimp_pixel_rgns_process (pr), count++)
+ {
+ const guchar *src = srcPR->data;
+ gint y;
+
+ for (y = srcPR->y; y < srcPR->y + srcPR->h; y++)
+ {
+ const guchar *s = src;
+ gint x;
+
+ for (x = srcPR->x; x < srcPR->x + srcPR->w; x++)
+ {
+ func (x, y, s, srcPR->bpp, data);
+ s += srcPR->bpp;
+ }
+
+ src += srcPR->rowstride;
+ }
+
+ area_so_far += srcPR->w * srcPR->h;
+
+ if ((count % 16) == 0)
+ gimp_progress_update ((gdouble) area_so_far / (gdouble) total_area);
+ }
+}
+
+static void
+gimp_rgn_render_row (const guchar *src,
+ guchar *dest,
+ gint col, /* row width in pixels */
+ gint bpp,
+ GimpRgnFunc2 func,
+ gpointer data)
+{
+ while (col--)
+ {
+ func (src, dest, bpp, data);
+
+ src += bpp;
+ dest += bpp;
+ }
+}
+
+static void
+gimp_rgn_render_region (const GimpPixelRgn *srcPR,
+ const GimpPixelRgn *destPR,
+ GimpRgnFunc2 func,
+ gpointer data)
+{
+ const guchar *src = srcPR->data;
+ guchar *dest = destPR->data;
+ gint row;
+
+ for (row = 0; row < srcPR->h; row++)
+ {
+ gimp_rgn_render_row (src, dest, srcPR->w, srcPR->bpp, func, data);
+
+ src += srcPR->rowstride;
+ dest += destPR->rowstride;
+ }
+}
diff --git a/libgimp/gimpregioniterator.h b/libgimp/gimpregioniterator.h
new file mode 100644
index 0000000..68ebb9f
--- /dev/null
+++ b/libgimp/gimpregioniterator.h
@@ -0,0 +1,95 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpregioniterator.h
+ * Contains all kinds of miscellaneous routines factored out from different
+ * plug-ins. They stay here until their API has crystalized a bit and we can
+ * put them into the file where they belong (Maurits Rijk
+ * <lpeek.mrijk@consunet.nl> if you want to blame someone for this mess)
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_REGION_ITERATOR_H__
+#define __GIMP_REGION_ITERATOR_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+typedef struct _GimpRgnIterator GimpRgnIterator;
+
+typedef void (* GimpRgnFunc1) (const guchar *src,
+ gint bpp,
+ gpointer data);
+typedef void (* GimpRgnFunc2) (const guchar *src,
+ guchar *dest,
+ gint bpp,
+ gpointer data);
+typedef void (* GimpRgnFuncSrc) (gint x,
+ gint y,
+ const guchar *src,
+ gint bpp,
+ gpointer data);
+typedef void (* GimpRgnFuncDest) (gint x,
+ gint y,
+ guchar *dest,
+ gint bpp,
+ gpointer data);
+typedef void (* GimpRgnFuncSrcDest) (gint x,
+ gint y,
+ const guchar *src,
+ guchar *dest,
+ gint bpp,
+ gpointer data);
+
+GIMP_DEPRECATED_FOR(GeglBufferIterator)
+GimpRgnIterator * gimp_rgn_iterator_new (GimpDrawable *drawable,
+ GimpRunMode unused);
+GIMP_DEPRECATED_FOR(GeglBufferIterator)
+void gimp_rgn_iterator_free (GimpRgnIterator *iter);
+GIMP_DEPRECATED_FOR(GeglBufferIterator)
+void gimp_rgn_iterator_src (GimpRgnIterator *iter,
+ GimpRgnFuncSrc func,
+ gpointer data);
+GIMP_DEPRECATED_FOR(GeglBufferIterator)
+void gimp_rgn_iterator_dest (GimpRgnIterator *iter,
+ GimpRgnFuncDest func,
+ gpointer data);
+GIMP_DEPRECATED_FOR(GeglBufferIterator)
+void gimp_rgn_iterator_src_dest (GimpRgnIterator *iter,
+ GimpRgnFuncSrcDest func,
+ gpointer data);
+
+
+GIMP_DEPRECATED_FOR(GeglBufferIterator)
+void gimp_rgn_iterate1 (GimpDrawable *drawable,
+ GimpRunMode unused,
+ GimpRgnFunc1 func,
+ gpointer data);
+
+GIMP_DEPRECATED_FOR(GeglBufferIterator)
+void gimp_rgn_iterate2 (GimpDrawable *drawable,
+ GimpRunMode unused,
+ GimpRgnFunc2 func,
+ gpointer data);
+
+G_END_DECLS
+
+#endif /* __GIMP_REGION_ITERATOR_H__ */
diff --git a/libgimp/gimpselectbutton.c b/libgimp/gimpselectbutton.c
new file mode 100644
index 0000000..33d743c
--- /dev/null
+++ b/libgimp/gimpselectbutton.c
@@ -0,0 +1,95 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpselectbutton.c
+ * Copyright (C) 2003 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "gimp.h"
+
+#include "gimpuitypes.h"
+#include "gimpselectbutton.h"
+
+
+/**
+ * SECTION: gimpselectbutton
+ * @title: GimpSelectButton
+ * @short_description: The base class of the data select buttons.
+ *
+ * The base class of the brush, pattern, gradient, palette and font
+ * select buttons.
+ **/
+
+
+/* local function prototypes */
+
+static void gimp_select_button_dispose (GObject *object);
+
+
+G_DEFINE_TYPE (GimpSelectButton, gimp_select_button, GTK_TYPE_BOX)
+
+static void
+gimp_select_button_class_init (GimpSelectButtonClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = gimp_select_button_dispose;
+}
+
+static void
+gimp_select_button_init (GimpSelectButton *select_button)
+{
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (select_button),
+ GTK_ORIENTATION_HORIZONTAL);
+
+ select_button->temp_callback = NULL;
+}
+
+static void
+gimp_select_button_dispose (GObject *object)
+{
+ gimp_select_button_close_popup (GIMP_SELECT_BUTTON (object));
+
+ G_OBJECT_CLASS (gimp_select_button_parent_class)->dispose (object);
+}
+
+/**
+ * gimp_select_button_close_popup:
+ * @button: A #GimpSelectButton
+ *
+ * Closes the popup window associated with @button.
+ *
+ * Since: 2.4
+ */
+void
+gimp_select_button_close_popup (GimpSelectButton *button)
+{
+ g_return_if_fail (GIMP_IS_SELECT_BUTTON (button));
+
+ if (button->temp_callback)
+ {
+ GimpSelectButtonClass *klass = GIMP_SELECT_BUTTON_GET_CLASS (button);
+
+ klass->select_destroy (button->temp_callback);
+
+ button->temp_callback = NULL;
+ }
+}
diff --git a/libgimp/gimpselectbutton.h b/libgimp/gimpselectbutton.h
new file mode 100644
index 0000000..2c8b335
--- /dev/null
+++ b/libgimp/gimpselectbutton.h
@@ -0,0 +1,76 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpselectbutton.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_UI_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimpui.h> can be included directly."
+#endif
+
+#ifndef __GIMP_SELECT_BUTTON_H__
+#define __GIMP_SELECT_BUTTON_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+#define GIMP_TYPE_SELECT_BUTTON (gimp_select_button_get_type ())
+#define GIMP_SELECT_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_SELECT_BUTTON, GimpSelectButton))
+#define GIMP_SELECT_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_SELECT_BUTTON, GimpSelectButtonClass))
+#define GIMP_IS_SELECT_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_SELECT_BUTTON))
+#define GIMP_IS_SELECT_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_SELECT_BUTTON))
+#define GIMP_SELECT_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_SELECT_BUTTON, GimpSelectButtonClass))
+
+
+typedef struct _GimpSelectButtonClass GimpSelectButtonClass;
+
+struct _GimpSelectButton
+{
+ GtkBox parent_instance;
+
+ const gchar *temp_callback;
+};
+
+struct _GimpSelectButtonClass
+{
+ GtkBoxClass parent_class;
+
+ gchar *default_title;
+
+ void (*select_destroy) (const gchar *callback);
+
+ /* Padding for future expansion */
+ void (*_gimp_reserved1) (void);
+ void (*_gimp_reserved2) (void);
+ void (*_gimp_reserved3) (void);
+ void (*_gimp_reserved4) (void);
+ void (*_gimp_reserved5) (void);
+ void (*_gimp_reserved6) (void);
+ void (*_gimp_reserved7) (void);
+};
+
+
+GType gimp_select_button_get_type (void) G_GNUC_CONST;
+
+void gimp_select_button_close_popup (GimpSelectButton *button);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_SELECT_BUTTON_H__ */
diff --git a/libgimp/gimpselection.c b/libgimp/gimpselection.c
new file mode 100644
index 0000000..c8e1bbb
--- /dev/null
+++ b/libgimp/gimpselection.c
@@ -0,0 +1,66 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2000 Peter Mattis and Spencer Kimball
+ *
+ * gimpselection.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gimp.h"
+
+/**
+ * gimp_selection_float:
+ * @image_ID: ignored
+ * @drawable_ID: The drawable from which to float selection.
+ * @offx: x offset for translation.
+ * @offy: y offset for translation.
+ *
+ * Float the selection from the specified drawable with initial offsets
+ * as specified.
+ *
+ * This procedure determines the region of the specified drawable that
+ * lies beneath the current selection. The region is then cut from the
+ * drawable and the resulting data is made into a new layer which is
+ * instantiated as a floating selection. The offsets allow initial
+ * positioning of the new floating selection.
+ *
+ * Returns: The floated layer.
+ */
+gint32
+gimp_selection_float (gint32 image_ID,
+ gint32 drawable_ID,
+ gint offx,
+ gint offy)
+{
+ return _gimp_selection_float (drawable_ID,
+ offx,
+ offy);
+}
+
+/**
+ * gimp_selection_clear:
+ * @image_ID: The image.
+ *
+ * This procedure is deprecated! Use gimp_selection_none() instead.
+ *
+ * Returns: TRUE on success.
+ */
+gboolean
+gimp_selection_clear (gint32 image_ID)
+{
+ return gimp_selection_none (image_ID);
+}
diff --git a/libgimp/gimpselection.h b/libgimp/gimpselection.h
new file mode 100644
index 0000000..6a95572
--- /dev/null
+++ b/libgimp/gimpselection.h
@@ -0,0 +1,45 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2000 Peter Mattis and Spencer Kimball
+ *
+ * gimpselection.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_SELECTION_H__
+#define __GIMP_SELECTION_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gint32 gimp_selection_float (gint32 image_ID,
+ gint32 drawable_ID,
+ gint offx,
+ gint offy);
+
+GIMP_DEPRECATED_FOR(gimp_selection_none)
+gboolean gimp_selection_clear (gint32 image_ID);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_SELECTION_H__ */
+
diff --git a/libgimp/gimpselection_pdb.c b/libgimp/gimpselection_pdb.c
new file mode 100644
index 0000000..de159c3
--- /dev/null
+++ b/libgimp/gimpselection_pdb.c
@@ -0,0 +1,650 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpselection_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpselection
+ * @title: gimpselection
+ * @short_description: Functions for manipulating selections.
+ *
+ * Functions for manipulating selections.
+ **/
+
+
+/**
+ * gimp_selection_bounds:
+ * @image_ID: The image.
+ * @non_empty: TRUE if there is a selection.
+ * @x1: x coordinate of upper left corner of selection bounds.
+ * @y1: y coordinate of upper left corner of selection bounds.
+ * @x2: x coordinate of lower right corner of selection bounds.
+ * @y2: y coordinate of lower right corner of selection bounds.
+ *
+ * Find the bounding box of the current selection.
+ *
+ * This procedure returns whether there is a selection for the
+ * specified image. If there is one, the upper left and lower right
+ * corners of the bounding box are returned. These coordinates are
+ * relative to the image. Please note that the pixel specified by the
+ * lower right coordinate of the bounding box is not part of the
+ * selection. The selection ends at the upper left corner of this
+ * pixel. This means the width of the selection can be calculated as
+ * (x2 - x1), its height as (y2 - y1).
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_selection_bounds (gint32 image_ID,
+ gboolean *non_empty,
+ gint *x1,
+ gint *y1,
+ gint *x2,
+ gint *y2)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-selection-bounds",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ *non_empty = FALSE;
+ *x1 = 0;
+ *y1 = 0;
+ *x2 = 0;
+ *y2 = 0;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *non_empty = return_vals[1].data.d_int32;
+ *x1 = return_vals[2].data.d_int32;
+ *y1 = return_vals[3].data.d_int32;
+ *x2 = return_vals[4].data.d_int32;
+ *y2 = return_vals[5].data.d_int32;
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_selection_value:
+ * @image_ID: The image.
+ * @x: x coordinate of value.
+ * @y: y coordinate of value.
+ *
+ * Find the value of the selection at the specified coordinates.
+ *
+ * This procedure returns the value of the selection at the specified
+ * coordinates. If the coordinates lie out of bounds, 0 is returned.
+ *
+ * Returns: Value of the selection.
+ **/
+gint
+gimp_selection_value (gint32 image_ID,
+ gint x,
+ gint y)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint value = 0;
+
+ return_vals = gimp_run_procedure ("gimp-selection-value",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, x,
+ GIMP_PDB_INT32, y,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ value = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return value;
+}
+
+/**
+ * gimp_selection_is_empty:
+ * @image_ID: The image.
+ *
+ * Determine whether the selection is empty.
+ *
+ * This procedure returns TRUE if the selection for the specified image
+ * is empty.
+ *
+ * Returns: Is the selection empty?
+ **/
+gboolean
+gimp_selection_is_empty (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean is_empty = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-selection-is-empty",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ is_empty = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return is_empty;
+}
+
+/**
+ * gimp_selection_translate:
+ * @image_ID: The image.
+ * @offx: x offset for translation.
+ * @offy: y offset for translation.
+ *
+ * Translate the selection by the specified offsets.
+ *
+ * This procedure actually translates the selection for the specified
+ * image by the specified offsets. Regions that are translated from
+ * beyond the bounds of the image are set to empty. Valid regions of
+ * the selection which are translated beyond the bounds of the image
+ * because of this call are lost.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_selection_translate (gint32 image_ID,
+ gint offx,
+ gint offy)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-selection-translate",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, offx,
+ GIMP_PDB_INT32, offy,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * _gimp_selection_float:
+ * @drawable_ID: The drawable from which to float selection.
+ * @offx: x offset for translation.
+ * @offy: y offset for translation.
+ *
+ * Float the selection from the specified drawable with initial offsets
+ * as specified.
+ *
+ * This procedure determines the region of the specified drawable that
+ * lies beneath the current selection. The region is then cut from the
+ * drawable and the resulting data is made into a new layer which is
+ * instantiated as a floating selection. The offsets allow initial
+ * positioning of the new floating selection.
+ *
+ * Returns: The floated layer.
+ **/
+gint32
+_gimp_selection_float (gint32 drawable_ID,
+ gint offx,
+ gint offy)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 layer_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-selection-float",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, offx,
+ GIMP_PDB_INT32, offy,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ layer_ID = return_vals[1].data.d_layer;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return layer_ID;
+}
+
+/**
+ * gimp_selection_invert:
+ * @image_ID: The image.
+ *
+ * Invert the selection mask.
+ *
+ * This procedure inverts the selection mask. For every pixel in the
+ * selection channel, its new value is calculated as (255 - old-value).
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_selection_invert (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-selection-invert",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_selection_sharpen:
+ * @image_ID: The image.
+ *
+ * Sharpen the selection mask.
+ *
+ * This procedure sharpens the selection mask. For every pixel in the
+ * selection channel, if the value is &gt; 127, the new pixel is
+ * assigned a value of 255. This removes any \"anti-aliasing\" that
+ * might exist in the selection mask's boundary.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_selection_sharpen (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-selection-sharpen",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_selection_all:
+ * @image_ID: The image.
+ *
+ * Select all of the image.
+ *
+ * This procedure sets the selection mask to completely encompass the
+ * image. Every pixel in the selection channel is set to 255.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_selection_all (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-selection-all",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_selection_none:
+ * @image_ID: The image.
+ *
+ * Deselect the entire image.
+ *
+ * This procedure deselects the entire image. Every pixel in the
+ * selection channel is set to 0.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_selection_none (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-selection-none",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_selection_feather:
+ * @image_ID: The image.
+ * @radius: Radius of feather (in pixels).
+ *
+ * Feather the image's selection
+ *
+ * This procedure feathers the selection. Feathering is implemented
+ * using a gaussian blur.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_selection_feather (gint32 image_ID,
+ gdouble radius)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-selection-feather",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_FLOAT, radius,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_selection_border:
+ * @image_ID: The image.
+ * @radius: Radius of border (in pixels).
+ *
+ * Border the image's selection
+ *
+ * This procedure borders the selection. Bordering creates a new
+ * selection which is defined along the boundary of the previous
+ * selection at every point within the specified radius.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_selection_border (gint32 image_ID,
+ gint radius)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-selection-border",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, radius,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_selection_grow:
+ * @image_ID: The image.
+ * @steps: Steps of grow (in pixels).
+ *
+ * Grow the image's selection
+ *
+ * This procedure grows the selection. Growing involves expanding the
+ * boundary in all directions by the specified pixel amount.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_selection_grow (gint32 image_ID,
+ gint steps)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-selection-grow",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, steps,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_selection_shrink:
+ * @image_ID: The image.
+ * @steps: Steps of shrink (in pixels).
+ *
+ * Shrink the image's selection
+ *
+ * This procedure shrinks the selection. Shrinking involves trimming
+ * the existing selection boundary on all sides by the specified number
+ * of pixels.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_selection_shrink (gint32 image_ID,
+ gint steps)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-selection-shrink",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, steps,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_selection_flood:
+ * @image_ID: The image.
+ *
+ * Remove holes from the image's selection
+ *
+ * This procedure removes holes from the selection, that can come from
+ * selecting a patchy area with the Fuzzy Select Tool. In technical
+ * terms this procedure floods the selection. See the Algorithms page
+ * in the developer wiki for details.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_selection_flood (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-selection-flood",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_selection_layer_alpha:
+ * @layer_ID: Layer with alpha.
+ *
+ * Deprecated: Use gimp_image_select_item() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_selection_layer_alpha (gint32 layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-selection-layer-alpha",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_selection_load:
+ * @channel_ID: The channel.
+ *
+ * Deprecated: Use gimp_image_select_item() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_selection_load (gint32 channel_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-selection-load",
+ &nreturn_vals,
+ GIMP_PDB_CHANNEL, channel_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_selection_save:
+ * @image_ID: The image.
+ *
+ * Copy the selection mask to a new channel.
+ *
+ * This procedure copies the selection mask and stores the content in a
+ * new channel. The new channel is automatically inserted into the
+ * image's list of channels.
+ *
+ * Returns: The new channel.
+ **/
+gint32
+gimp_selection_save (gint32 image_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 channel_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-selection-save",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ channel_ID = return_vals[1].data.d_channel;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return channel_ID;
+}
+
+/**
+ * gimp_selection_combine:
+ * @channel_ID: The channel.
+ * @operation: The selection operation.
+ *
+ * Deprecated: Use gimp_image_select_item() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_selection_combine (gint32 channel_ID,
+ GimpChannelOps operation)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-selection-combine",
+ &nreturn_vals,
+ GIMP_PDB_CHANNEL, channel_ID,
+ GIMP_PDB_INT32, operation,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimpselection_pdb.h b/libgimp/gimpselection_pdb.h
new file mode 100644
index 0000000..8440859
--- /dev/null
+++ b/libgimp/gimpselection_pdb.h
@@ -0,0 +1,76 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpselection_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_SELECTION_PDB_H__
+#define __GIMP_SELECTION_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_selection_bounds (gint32 image_ID,
+ gboolean *non_empty,
+ gint *x1,
+ gint *y1,
+ gint *x2,
+ gint *y2);
+gint gimp_selection_value (gint32 image_ID,
+ gint x,
+ gint y);
+gboolean gimp_selection_is_empty (gint32 image_ID);
+gboolean gimp_selection_translate (gint32 image_ID,
+ gint offx,
+ gint offy);
+G_GNUC_INTERNAL gint32 _gimp_selection_float (gint32 drawable_ID,
+ gint offx,
+ gint offy);
+gboolean gimp_selection_invert (gint32 image_ID);
+gboolean gimp_selection_sharpen (gint32 image_ID);
+gboolean gimp_selection_all (gint32 image_ID);
+gboolean gimp_selection_none (gint32 image_ID);
+gboolean gimp_selection_feather (gint32 image_ID,
+ gdouble radius);
+gboolean gimp_selection_border (gint32 image_ID,
+ gint radius);
+gboolean gimp_selection_grow (gint32 image_ID,
+ gint steps);
+gboolean gimp_selection_shrink (gint32 image_ID,
+ gint steps);
+gboolean gimp_selection_flood (gint32 image_ID);
+GIMP_DEPRECATED_FOR(gimp_image_select_item)
+gboolean gimp_selection_layer_alpha (gint32 layer_ID);
+GIMP_DEPRECATED_FOR(gimp_image_select_item)
+gboolean gimp_selection_load (gint32 channel_ID);
+gint32 gimp_selection_save (gint32 image_ID);
+GIMP_DEPRECATED_FOR(gimp_image_select_item)
+gboolean gimp_selection_combine (gint32 channel_ID,
+ GimpChannelOps operation);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_SELECTION_PDB_H__ */
diff --git a/libgimp/gimpselectiontools_pdb.c b/libgimp/gimpselectiontools_pdb.c
new file mode 100644
index 0000000..1d5a324
--- /dev/null
+++ b/libgimp/gimpselectiontools_pdb.c
@@ -0,0 +1,461 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpselectiontools_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpselectiontools
+ * @title: gimpselectiontools
+ * @short_description: Access to toolbox selection tools.
+ *
+ * Functions giving access to toolbox selection tools.
+ **/
+
+
+/**
+ * gimp_by_color_select:
+ * @drawable_ID: The affected drawable.
+ * @color: The color to select.
+ * @threshold: Threshold in intensity levels.
+ * @operation: The selection operation.
+ * @antialias: Antialiasing.
+ * @feather: Feather option for selections.
+ * @feather_radius: Radius for feather operation.
+ * @sample_merged: Use the composite image, not the drawable.
+ *
+ * Deprecated: Use gimp_image_select_color() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_by_color_select (gint32 drawable_ID,
+ const GimpRGB *color,
+ gint threshold,
+ GimpChannelOps operation,
+ gboolean antialias,
+ gboolean feather,
+ gdouble feather_radius,
+ gboolean sample_merged)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-by-color-select",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_COLOR, color,
+ GIMP_PDB_INT32, threshold,
+ GIMP_PDB_INT32, operation,
+ GIMP_PDB_INT32, antialias,
+ GIMP_PDB_INT32, feather,
+ GIMP_PDB_FLOAT, feather_radius,
+ GIMP_PDB_INT32, sample_merged,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_by_color_select_full:
+ * @drawable_ID: The affected drawable.
+ * @color: The color to select.
+ * @threshold: Threshold in intensity levels.
+ * @operation: The selection operation.
+ * @antialias: Antialiasing.
+ * @feather: Feather option for selections.
+ * @feather_radius_x: Radius for feather operation in X direction.
+ * @feather_radius_y: Radius for feather operation in Y direction.
+ * @sample_merged: Use the composite image, not the drawable.
+ * @select_transparent: Whether to consider transparent pixels for selection. If TRUE, transparency is considered as a unique selectable color.
+ * @select_criterion: The criterion used to determine color similarity. SELECT_CRITERION_COMPOSITE is the standard choice.
+ *
+ * Deprecated: Use gimp_image_select_color() instead.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_by_color_select_full (gint32 drawable_ID,
+ const GimpRGB *color,
+ gint threshold,
+ GimpChannelOps operation,
+ gboolean antialias,
+ gboolean feather,
+ gdouble feather_radius_x,
+ gdouble feather_radius_y,
+ gboolean sample_merged,
+ gboolean select_transparent,
+ GimpSelectCriterion select_criterion)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-by-color-select-full",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_COLOR, color,
+ GIMP_PDB_INT32, threshold,
+ GIMP_PDB_INT32, operation,
+ GIMP_PDB_INT32, antialias,
+ GIMP_PDB_INT32, feather,
+ GIMP_PDB_FLOAT, feather_radius_x,
+ GIMP_PDB_FLOAT, feather_radius_y,
+ GIMP_PDB_INT32, sample_merged,
+ GIMP_PDB_INT32, select_transparent,
+ GIMP_PDB_INT32, select_criterion,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_ellipse_select:
+ * @image_ID: The image.
+ * @x: x coordinate of upper-left corner of ellipse bounding box.
+ * @y: y coordinate of upper-left corner of ellipse bounding box.
+ * @width: The width of the ellipse.
+ * @height: The height of the ellipse.
+ * @operation: The selection operation.
+ * @antialias: Antialiasing.
+ * @feather: Feather option for selections.
+ * @feather_radius: Radius for feather operation.
+ *
+ * Deprecated: Use gimp_image_select_ellipse() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_ellipse_select (gint32 image_ID,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ GimpChannelOps operation,
+ gboolean antialias,
+ gboolean feather,
+ gdouble feather_radius)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-ellipse-select",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_FLOAT, x,
+ GIMP_PDB_FLOAT, y,
+ GIMP_PDB_FLOAT, width,
+ GIMP_PDB_FLOAT, height,
+ GIMP_PDB_INT32, operation,
+ GIMP_PDB_INT32, antialias,
+ GIMP_PDB_INT32, feather,
+ GIMP_PDB_FLOAT, feather_radius,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_free_select:
+ * @image_ID: The image.
+ * @num_segs: Number of points (count 1 coordinate as two points).
+ * @segs: Array of points: { p1.x, p1.y, p2.x, p2.y, ..., pn.x, pn.y}.
+ * @operation: The selection operation.
+ * @antialias: Antialiasing.
+ * @feather: Feather option for selections.
+ * @feather_radius: Radius for feather operation.
+ *
+ * Deprecated: Use gimp_image_select_polygon() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_free_select (gint32 image_ID,
+ gint num_segs,
+ const gdouble *segs,
+ GimpChannelOps operation,
+ gboolean antialias,
+ gboolean feather,
+ gdouble feather_radius)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-free-select",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_INT32, num_segs,
+ GIMP_PDB_FLOATARRAY, segs,
+ GIMP_PDB_INT32, operation,
+ GIMP_PDB_INT32, antialias,
+ GIMP_PDB_INT32, feather,
+ GIMP_PDB_FLOAT, feather_radius,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_fuzzy_select:
+ * @drawable_ID: The affected drawable.
+ * @x: x coordinate of initial seed fill point: (image coordinates).
+ * @y: y coordinate of initial seed fill point: (image coordinates).
+ * @threshold: Threshold in intensity levels.
+ * @operation: The selection operation.
+ * @antialias: Antialiasing.
+ * @feather: Feather option for selections.
+ * @feather_radius: Radius for feather operation.
+ * @sample_merged: Use the composite image, not the drawable.
+ *
+ * Deprecated: Use gimp_image_select_contiguous_color() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_fuzzy_select (gint32 drawable_ID,
+ gdouble x,
+ gdouble y,
+ gint threshold,
+ GimpChannelOps operation,
+ gboolean antialias,
+ gboolean feather,
+ gdouble feather_radius,
+ gboolean sample_merged)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-fuzzy-select",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_FLOAT, x,
+ GIMP_PDB_FLOAT, y,
+ GIMP_PDB_INT32, threshold,
+ GIMP_PDB_INT32, operation,
+ GIMP_PDB_INT32, antialias,
+ GIMP_PDB_INT32, feather,
+ GIMP_PDB_FLOAT, feather_radius,
+ GIMP_PDB_INT32, sample_merged,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_fuzzy_select_full:
+ * @drawable_ID: The affected drawable.
+ * @x: x coordinate of initial seed fill point: (image coordinates).
+ * @y: y coordinate of initial seed fill point: (image coordinates).
+ * @threshold: Threshold in intensity levels.
+ * @operation: The selection operation.
+ * @antialias: Antialiasing.
+ * @feather: Feather option for selections.
+ * @feather_radius_x: Radius for feather operation in X direction.
+ * @feather_radius_y: Radius for feather operation in Y direction.
+ * @sample_merged: Use the composite image, not the drawable.
+ * @select_transparent: Whether to consider transparent pixels for selection. If TRUE, transparency is considered as a unique selectable color.
+ * @select_criterion: The criterion used to determine color similarity. SELECT_CRITERION_COMPOSITE is the standard choice.
+ *
+ * Deprecated: Use gimp_image_select_contiguous_color() instead.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_fuzzy_select_full (gint32 drawable_ID,
+ gdouble x,
+ gdouble y,
+ gint threshold,
+ GimpChannelOps operation,
+ gboolean antialias,
+ gboolean feather,
+ gdouble feather_radius_x,
+ gdouble feather_radius_y,
+ gboolean sample_merged,
+ gboolean select_transparent,
+ GimpSelectCriterion select_criterion)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-fuzzy-select-full",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_FLOAT, x,
+ GIMP_PDB_FLOAT, y,
+ GIMP_PDB_INT32, threshold,
+ GIMP_PDB_INT32, operation,
+ GIMP_PDB_INT32, antialias,
+ GIMP_PDB_INT32, feather,
+ GIMP_PDB_FLOAT, feather_radius_x,
+ GIMP_PDB_FLOAT, feather_radius_y,
+ GIMP_PDB_INT32, sample_merged,
+ GIMP_PDB_INT32, select_transparent,
+ GIMP_PDB_INT32, select_criterion,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_rect_select:
+ * @image_ID: The image.
+ * @x: x coordinate of upper-left corner of rectangle.
+ * @y: y coordinate of upper-left corner of rectangle.
+ * @width: The width of the rectangle.
+ * @height: The height of the rectangle.
+ * @operation: The selection operation.
+ * @feather: Feather option for selections.
+ * @feather_radius: Radius for feather operation.
+ *
+ * Deprecated: Use gimp_image_select_rectangle() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_rect_select (gint32 image_ID,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ GimpChannelOps operation,
+ gboolean feather,
+ gdouble feather_radius)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-rect-select",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_FLOAT, x,
+ GIMP_PDB_FLOAT, y,
+ GIMP_PDB_FLOAT, width,
+ GIMP_PDB_FLOAT, height,
+ GIMP_PDB_INT32, operation,
+ GIMP_PDB_INT32, feather,
+ GIMP_PDB_FLOAT, feather_radius,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_round_rect_select:
+ * @image_ID: The image.
+ * @x: x coordinate of upper-left corner of rectangle.
+ * @y: y coordinate of upper-left corner of rectangle.
+ * @width: The width of the rectangle.
+ * @height: The height of the rectangle.
+ * @corner_radius_x: The corner radius in X direction.
+ * @corner_radius_y: The corner radius in Y direction.
+ * @operation: The selection operation.
+ * @antialias: Antialiasing.
+ * @feather: Feather option for selections.
+ * @feather_radius_x: Radius for feather operation in X direction.
+ * @feather_radius_y: Radius for feather operation in Y direction.
+ *
+ * Deprecated: Use gimp_image_select_round_rectangle() instead.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_round_rect_select (gint32 image_ID,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ gdouble corner_radius_x,
+ gdouble corner_radius_y,
+ GimpChannelOps operation,
+ gboolean antialias,
+ gboolean feather,
+ gdouble feather_radius_x,
+ gdouble feather_radius_y)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-round-rect-select",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_FLOAT, x,
+ GIMP_PDB_FLOAT, y,
+ GIMP_PDB_FLOAT, width,
+ GIMP_PDB_FLOAT, height,
+ GIMP_PDB_FLOAT, corner_radius_x,
+ GIMP_PDB_FLOAT, corner_radius_y,
+ GIMP_PDB_INT32, operation,
+ GIMP_PDB_INT32, antialias,
+ GIMP_PDB_INT32, feather,
+ GIMP_PDB_FLOAT, feather_radius_x,
+ GIMP_PDB_FLOAT, feather_radius_y,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimpselectiontools_pdb.h b/libgimp/gimpselectiontools_pdb.h
new file mode 100644
index 0000000..50c886a
--- /dev/null
+++ b/libgimp/gimpselectiontools_pdb.h
@@ -0,0 +1,123 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpselectiontools_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_SELECTION_TOOLS_PDB_H__
+#define __GIMP_SELECTION_TOOLS_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+GIMP_DEPRECATED_FOR(gimp_image_select_color)
+gboolean gimp_by_color_select (gint32 drawable_ID,
+ const GimpRGB *color,
+ gint threshold,
+ GimpChannelOps operation,
+ gboolean antialias,
+ gboolean feather,
+ gdouble feather_radius,
+ gboolean sample_merged);
+GIMP_DEPRECATED_FOR(gimp_image_select_color)
+gboolean gimp_by_color_select_full (gint32 drawable_ID,
+ const GimpRGB *color,
+ gint threshold,
+ GimpChannelOps operation,
+ gboolean antialias,
+ gboolean feather,
+ gdouble feather_radius_x,
+ gdouble feather_radius_y,
+ gboolean sample_merged,
+ gboolean select_transparent,
+ GimpSelectCriterion select_criterion);
+GIMP_DEPRECATED_FOR(gimp_image_select_ellipse)
+gboolean gimp_ellipse_select (gint32 image_ID,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ GimpChannelOps operation,
+ gboolean antialias,
+ gboolean feather,
+ gdouble feather_radius);
+GIMP_DEPRECATED_FOR(gimp_image_select_polygon)
+gboolean gimp_free_select (gint32 image_ID,
+ gint num_segs,
+ const gdouble *segs,
+ GimpChannelOps operation,
+ gboolean antialias,
+ gboolean feather,
+ gdouble feather_radius);
+GIMP_DEPRECATED_FOR(gimp_image_select_contiguous_color)
+gboolean gimp_fuzzy_select (gint32 drawable_ID,
+ gdouble x,
+ gdouble y,
+ gint threshold,
+ GimpChannelOps operation,
+ gboolean antialias,
+ gboolean feather,
+ gdouble feather_radius,
+ gboolean sample_merged);
+GIMP_DEPRECATED_FOR(gimp_image_select_contiguous_color)
+gboolean gimp_fuzzy_select_full (gint32 drawable_ID,
+ gdouble x,
+ gdouble y,
+ gint threshold,
+ GimpChannelOps operation,
+ gboolean antialias,
+ gboolean feather,
+ gdouble feather_radius_x,
+ gdouble feather_radius_y,
+ gboolean sample_merged,
+ gboolean select_transparent,
+ GimpSelectCriterion select_criterion);
+GIMP_DEPRECATED_FOR(gimp_image_select_rectangle)
+gboolean gimp_rect_select (gint32 image_ID,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ GimpChannelOps operation,
+ gboolean feather,
+ gdouble feather_radius);
+GIMP_DEPRECATED_FOR(gimp_image_select_round_rectangle)
+gboolean gimp_round_rect_select (gint32 image_ID,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ gdouble corner_radius_x,
+ gdouble corner_radius_y,
+ GimpChannelOps operation,
+ gboolean antialias,
+ gboolean feather,
+ gdouble feather_radius_x,
+ gdouble feather_radius_y);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_SELECTION_TOOLS_PDB_H__ */
diff --git a/libgimp/gimptextlayer_pdb.c b/libgimp/gimptextlayer_pdb.c
new file mode 100644
index 0000000..95da141
--- /dev/null
+++ b/libgimp/gimptextlayer_pdb.c
@@ -0,0 +1,1127 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimptextlayer_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimptextlayer
+ * @title: gimptextlayer
+ * @short_description: Functions for querying and manipulating text layers.
+ *
+ * Functions for querying and manipulating text layers.
+ **/
+
+
+/**
+ * gimp_text_layer_new:
+ * @image_ID: The image.
+ * @text: The text to generate (in UTF-8 encoding).
+ * @fontname: The name of the font.
+ * @size: The size of text in either pixels or points.
+ * @unit: The units of specified size.
+ *
+ * Creates a new text layer.
+ *
+ * This procedure creates a new text layer. The arguments are kept as
+ * simple as necessary for the normal case. All text attributes,
+ * however, can be modified with the appropriate
+ * gimp_text_layer_set_*() procedures. The new layer still needs to be
+ * added to the image, as this is not automatic. Add the new layer
+ * using gimp_image_insert_layer().
+ *
+ * Returns: The new text layer.
+ *
+ * Since: 2.6
+ **/
+gint32
+gimp_text_layer_new (gint32 image_ID,
+ const gchar *text,
+ const gchar *fontname,
+ gdouble size,
+ GimpUnit unit)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 layer_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-new",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_STRING, text,
+ GIMP_PDB_STRING, fontname,
+ GIMP_PDB_FLOAT, size,
+ GIMP_PDB_INT32, unit,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ layer_ID = return_vals[1].data.d_layer;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return layer_ID;
+}
+
+/**
+ * gimp_text_layer_get_text:
+ * @layer_ID: The text layer.
+ *
+ * Get the text from a text layer as string.
+ *
+ * This procedure returns the text from a text layer as a string.
+ *
+ * Returns: The text from the specified text layer.
+ *
+ * Since: 2.6
+ **/
+gchar *
+gimp_text_layer_get_text (gint32 layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *text = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-get-text",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ text = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return text;
+}
+
+/**
+ * gimp_text_layer_set_text:
+ * @layer_ID: The text layer.
+ * @text: The new text to set.
+ *
+ * Set the text of a text layer.
+ *
+ * This procedure changes the text of a text layer.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.6
+ **/
+gboolean
+gimp_text_layer_set_text (gint32 layer_ID,
+ const gchar *text)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-set-text",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_STRING, text,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_text_layer_get_markup:
+ * @layer_ID: The text layer.
+ *
+ * Get the markup from a text layer as string.
+ *
+ * This procedure returns the markup of the styles from a text layer.
+ * The markup will be in the form of Pango's markup - See
+ * https://www.pango.org/ for more information about Pango and its
+ * markup. Note: Setting the markup of a text layer using Pango's
+ * markup is not supported for now.
+ *
+ * Returns: The markup which represents the style of the specified text
+ * layer.
+ *
+ * Since: 2.8
+ **/
+gchar *
+gimp_text_layer_get_markup (gint32 layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *markup = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-get-markup",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ markup = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return markup;
+}
+
+/**
+ * gimp_text_layer_get_font:
+ * @layer_ID: The text layer.
+ *
+ * Get the font from a text layer as string.
+ *
+ * This procedure returns the name of the font from a text layer.
+ *
+ * Returns: The font which is used in the specified text layer.
+ *
+ * Since: 2.6
+ **/
+gchar *
+gimp_text_layer_get_font (gint32 layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *font = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-get-font",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ font = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return font;
+}
+
+/**
+ * gimp_text_layer_set_font:
+ * @layer_ID: The text layer.
+ * @font: The new font to use.
+ *
+ * Set the font of a text layer.
+ *
+ * This procedure modifies the font used in the specified text layer.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.6
+ **/
+gboolean
+gimp_text_layer_set_font (gint32 layer_ID,
+ const gchar *font)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-set-font",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_STRING, font,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_text_layer_get_font_size:
+ * @layer_ID: The text layer.
+ * @unit: The unit used for the font size.
+ *
+ * Get the font size from a text layer.
+ *
+ * This procedure returns the size of the font which is used in a text
+ * layer. You will receive the size as a float 'font-size' in 'unit'
+ * units.
+ *
+ * Returns: The font size.
+ *
+ * Since: 2.6
+ **/
+gdouble
+gimp_text_layer_get_font_size (gint32 layer_ID,
+ GimpUnit *unit)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble font_size = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-get-font-size",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ font_size = return_vals[1].data.d_float;
+ *unit = return_vals[2].data.d_unit;
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return font_size;
+}
+
+/**
+ * gimp_text_layer_set_font_size:
+ * @layer_ID: The text layer.
+ * @font_size: The font size.
+ * @unit: The unit to use for the font size.
+ *
+ * Set the font size.
+ *
+ * This procedure changes the font size of a text layer. The size of
+ * your font will be a double 'font-size' of 'unit' units.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.6
+ **/
+gboolean
+gimp_text_layer_set_font_size (gint32 layer_ID,
+ gdouble font_size,
+ GimpUnit unit)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-set-font-size",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_FLOAT, font_size,
+ GIMP_PDB_INT32, unit,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_text_layer_get_antialias:
+ * @layer_ID: The text layer.
+ *
+ * Check if antialiasing is used in the text layer.
+ *
+ * This procedure checks if antialiasing is enabled in the specified
+ * text layer.
+ *
+ * Returns: A flag which is true if antialiasing is used for rendering
+ * the font in the text layer.
+ *
+ * Since: 2.6
+ **/
+gboolean
+gimp_text_layer_get_antialias (gint32 layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean antialias = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-get-antialias",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ antialias = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return antialias;
+}
+
+/**
+ * gimp_text_layer_set_antialias:
+ * @layer_ID: The text layer.
+ * @antialias: Enable/disable antialiasing of the text.
+ *
+ * Enable/disable anti-aliasing in a text layer.
+ *
+ * This procedure enables or disables anti-aliasing of the text in a
+ * text layer.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.6
+ **/
+gboolean
+gimp_text_layer_set_antialias (gint32 layer_ID,
+ gboolean antialias)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-set-antialias",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_INT32, antialias,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_text_layer_get_hint_style:
+ * @layer_ID: The text layer.
+ *
+ * Get information about hinting in the specified text layer.
+ *
+ * This procedure provides information about the hinting that is being
+ * used in a text layer. Hinting can be optimized for fidelity or
+ * contrast or it can be turned entirely off.
+ *
+ * Returns: The hint style used for font outlines.
+ *
+ * Since: 2.8
+ **/
+GimpTextHintStyle
+gimp_text_layer_get_hint_style (gint32 layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpTextHintStyle style = 0;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-get-hint-style",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ style = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return style;
+}
+
+/**
+ * gimp_text_layer_set_hint_style:
+ * @layer_ID: The text layer.
+ * @style: The new hint style.
+ *
+ * Control how font outlines are hinted in a text layer.
+ *
+ * This procedure sets the hint style for font outlines in a text
+ * layer. This controls whether to fit font outlines to the pixel grid,
+ * and if so, whether to optimize for fidelity or contrast.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_text_layer_set_hint_style (gint32 layer_ID,
+ GimpTextHintStyle style)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-set-hint-style",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_INT32, style,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_text_layer_get_kerning:
+ * @layer_ID: The text layer.
+ *
+ * Check if kerning is used in the text layer.
+ *
+ * This procedure checks if kerning is enabled in the specified text
+ * layer.
+ *
+ * Returns: A flag which is true if kerning is used in the text layer.
+ *
+ * Since: 2.6
+ **/
+gboolean
+gimp_text_layer_get_kerning (gint32 layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean kerning = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-get-kerning",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ kerning = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return kerning;
+}
+
+/**
+ * gimp_text_layer_set_kerning:
+ * @layer_ID: The text layer.
+ * @kerning: Enable/disable kerning in the text.
+ *
+ * Enable/disable kerning in a text layer.
+ *
+ * This procedure enables or disables kerning in a text layer.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.6
+ **/
+gboolean
+gimp_text_layer_set_kerning (gint32 layer_ID,
+ gboolean kerning)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-set-kerning",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_INT32, kerning,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_text_layer_get_language:
+ * @layer_ID: The text layer.
+ *
+ * Get the language used in the text layer.
+ *
+ * This procedure returns the language string which is set for the text
+ * in the text layer.
+ *
+ * Returns: The language used in the text layer.
+ *
+ * Since: 2.6
+ **/
+gchar *
+gimp_text_layer_get_language (gint32 layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *language = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-get-language",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ language = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return language;
+}
+
+/**
+ * gimp_text_layer_set_language:
+ * @layer_ID: The text layer.
+ * @language: The new language to use for the text layer.
+ *
+ * Set the language of the text layer.
+ *
+ * This procedure sets the language of the text in text layer. For some
+ * scripts the language has an influence of how the text is rendered.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.6
+ **/
+gboolean
+gimp_text_layer_set_language (gint32 layer_ID,
+ const gchar *language)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-set-language",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_STRING, language,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_text_layer_get_base_direction:
+ * @layer_ID: The text layer.
+ *
+ * Get the base direction used for rendering the text layer.
+ *
+ * This procedure returns the base direction used for rendering the
+ * text in the text layer
+ *
+ * Returns: The based direction used for the text layer.
+ *
+ * Since: 2.6
+ **/
+GimpTextDirection
+gimp_text_layer_get_base_direction (gint32 layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpTextDirection direction = 0;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-get-base-direction",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ direction = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return direction;
+}
+
+/**
+ * gimp_text_layer_set_base_direction:
+ * @layer_ID: The text layer.
+ * @direction: The base direction of the text.
+ *
+ * Set the base direction in the text layer.
+ *
+ * This procedure sets the base direction used in applying the Unicode
+ * bidirectional algorithm when rendering the text.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.6
+ **/
+gboolean
+gimp_text_layer_set_base_direction (gint32 layer_ID,
+ GimpTextDirection direction)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-set-base-direction",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_INT32, direction,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_text_layer_get_justification:
+ * @layer_ID: The text layer.
+ *
+ * Get the text justification information of the text layer.
+ *
+ * This procedure returns the alignment of the lines in the text layer
+ * relative to each other.
+ *
+ * Returns: The justification used in the text layer.
+ *
+ * Since: 2.6
+ **/
+GimpTextJustification
+gimp_text_layer_get_justification (gint32 layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpTextJustification justify = 0;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-get-justification",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ justify = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return justify;
+}
+
+/**
+ * gimp_text_layer_set_justification:
+ * @layer_ID: The text layer.
+ * @justify: The justification for your text.
+ *
+ * Set the justification of the text in a text layer.
+ *
+ * This procedure sets the alignment of the lines in the text layer
+ * relative to each other.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.6
+ **/
+gboolean
+gimp_text_layer_set_justification (gint32 layer_ID,
+ GimpTextJustification justify)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-set-justification",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_INT32, justify,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_text_layer_get_color:
+ * @layer_ID: The text layer.
+ * @color: The color of the text.
+ *
+ * Get the color of the text in a text layer.
+ *
+ * This procedure returns the color of the text in a text layer.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.6
+ **/
+gboolean
+gimp_text_layer_get_color (gint32 layer_ID,
+ GimpRGB *color)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-get-color",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ *color = return_vals[1].data.d_color;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_text_layer_set_color:
+ * @layer_ID: The text layer.
+ * @color: The color to use for the text.
+ *
+ * Set the color of the text in the text layer.
+ *
+ * This procedure sets the text color in the text layer 'layer'.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.6
+ **/
+gboolean
+gimp_text_layer_set_color (gint32 layer_ID,
+ const GimpRGB *color)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-set-color",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_COLOR, color,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_text_layer_get_indent:
+ * @layer_ID: The text layer.
+ *
+ * Get the line indentation of text layer.
+ *
+ * This procedure returns the indentation of the first line in a text
+ * layer.
+ *
+ * Returns: The indentation value of the first line.
+ *
+ * Since: 2.6
+ **/
+gdouble
+gimp_text_layer_get_indent (gint32 layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble indent = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-get-indent",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ indent = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return indent;
+}
+
+/**
+ * gimp_text_layer_set_indent:
+ * @layer_ID: The text layer.
+ * @indent: The indentation for the first line.
+ *
+ * Set the indentation of the first line in a text layer.
+ *
+ * This procedure sets the indentation of the first line in the text
+ * layer.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.6
+ **/
+gboolean
+gimp_text_layer_set_indent (gint32 layer_ID,
+ gdouble indent)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-set-indent",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_FLOAT, indent,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_text_layer_get_line_spacing:
+ * @layer_ID: The text layer.
+ *
+ * Get the spacing between lines of text.
+ *
+ * This procedure returns the line-spacing between lines of text in a
+ * text layer.
+ *
+ * Returns: The line-spacing value.
+ *
+ * Since: 2.6
+ **/
+gdouble
+gimp_text_layer_get_line_spacing (gint32 layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble line_spacing = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-get-line-spacing",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ line_spacing = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return line_spacing;
+}
+
+/**
+ * gimp_text_layer_set_line_spacing:
+ * @layer_ID: The text layer.
+ * @line_spacing: The additional line spacing to use.
+ *
+ * Adjust the line spacing in a text layer.
+ *
+ * This procedure sets the additional spacing used between lines a text
+ * layer.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.6
+ **/
+gboolean
+gimp_text_layer_set_line_spacing (gint32 layer_ID,
+ gdouble line_spacing)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-set-line-spacing",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_FLOAT, line_spacing,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_text_layer_get_letter_spacing:
+ * @layer_ID: The text layer.
+ *
+ * Get the letter spacing used in a text layer.
+ *
+ * This procedure returns the additional spacing between the single
+ * glyphs in a text layer.
+ *
+ * Returns: The letter-spacing value.
+ *
+ * Since: 2.6
+ **/
+gdouble
+gimp_text_layer_get_letter_spacing (gint32 layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble letter_spacing = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-get-letter-spacing",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ letter_spacing = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return letter_spacing;
+}
+
+/**
+ * gimp_text_layer_set_letter_spacing:
+ * @layer_ID: The text layer.
+ * @letter_spacing: The additional letter spacing to use.
+ *
+ * Adjust the letter spacing in a text layer.
+ *
+ * This procedure sets the additional spacing between the single glyphs
+ * in a text layer.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.6
+ **/
+gboolean
+gimp_text_layer_set_letter_spacing (gint32 layer_ID,
+ gdouble letter_spacing)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-set-letter-spacing",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_FLOAT, letter_spacing,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_text_layer_resize:
+ * @layer_ID: The text layer.
+ * @width: The new box width in pixels.
+ * @height: The new box height in pixels.
+ *
+ * Resize the box of a text layer.
+ *
+ * This procedure changes the width and height of a text layer while
+ * keeping it as a text layer and not converting it to a bitmap like
+ * gimp_layer_resize() would do.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_text_layer_resize (gint32 layer_ID,
+ gdouble width,
+ gdouble height)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-resize",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_FLOAT, width,
+ GIMP_PDB_FLOAT, height,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_text_layer_get_hinting:
+ * @layer_ID: The text layer.
+ * @autohint: A flag which is true if the text layer is forced to use the autohinter from FreeType.
+ *
+ * Deprecated: Use gimp_text_layer_get_hint_style() instead.
+ *
+ * Returns: A flag which is true if hinting is used on the font.
+ **/
+gboolean
+gimp_text_layer_get_hinting (gint32 layer_ID,
+ gboolean *autohint)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean hinting = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-get-hinting",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ hinting = return_vals[1].data.d_int32;
+ *autohint = return_vals[2].data.d_int32;
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return hinting;
+}
+
+/**
+ * gimp_text_layer_set_hinting:
+ * @layer_ID: The text layer.
+ * @hinting: Enable/disable the use of hinting on the text.
+ * @autohint: Force the use of the autohinter provided through FreeType.
+ *
+ * Enable/disable the use of hinting in a text layer.
+ *
+ * This procedure enables or disables hinting on the text of a text
+ * layer. If you enable 'auto-hint', FreeType\'s automatic hinter will
+ * be used and hinting information from the font will be ignored.
+ *
+ * Deprecated: Use gimp_text_layer_set_hint_style() instead.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.6
+ **/
+gboolean
+gimp_text_layer_set_hinting (gint32 layer_ID,
+ gboolean hinting,
+ gboolean autohint)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-text-layer-set-hinting",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_INT32, hinting,
+ GIMP_PDB_INT32, autohint,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimptextlayer_pdb.h b/libgimp/gimptextlayer_pdb.h
new file mode 100644
index 0000000..0f286e8
--- /dev/null
+++ b/libgimp/gimptextlayer_pdb.h
@@ -0,0 +1,97 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimptextlayer_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_TEXT_LAYER_PDB_H__
+#define __GIMP_TEXT_LAYER_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gint32 gimp_text_layer_new (gint32 image_ID,
+ const gchar *text,
+ const gchar *fontname,
+ gdouble size,
+ GimpUnit unit);
+gchar* gimp_text_layer_get_text (gint32 layer_ID);
+gboolean gimp_text_layer_set_text (gint32 layer_ID,
+ const gchar *text);
+gchar* gimp_text_layer_get_markup (gint32 layer_ID);
+gchar* gimp_text_layer_get_font (gint32 layer_ID);
+gboolean gimp_text_layer_set_font (gint32 layer_ID,
+ const gchar *font);
+gdouble gimp_text_layer_get_font_size (gint32 layer_ID,
+ GimpUnit *unit);
+gboolean gimp_text_layer_set_font_size (gint32 layer_ID,
+ gdouble font_size,
+ GimpUnit unit);
+gboolean gimp_text_layer_get_antialias (gint32 layer_ID);
+gboolean gimp_text_layer_set_antialias (gint32 layer_ID,
+ gboolean antialias);
+GimpTextHintStyle gimp_text_layer_get_hint_style (gint32 layer_ID);
+gboolean gimp_text_layer_set_hint_style (gint32 layer_ID,
+ GimpTextHintStyle style);
+gboolean gimp_text_layer_get_kerning (gint32 layer_ID);
+gboolean gimp_text_layer_set_kerning (gint32 layer_ID,
+ gboolean kerning);
+gchar* gimp_text_layer_get_language (gint32 layer_ID);
+gboolean gimp_text_layer_set_language (gint32 layer_ID,
+ const gchar *language);
+GimpTextDirection gimp_text_layer_get_base_direction (gint32 layer_ID);
+gboolean gimp_text_layer_set_base_direction (gint32 layer_ID,
+ GimpTextDirection direction);
+GimpTextJustification gimp_text_layer_get_justification (gint32 layer_ID);
+gboolean gimp_text_layer_set_justification (gint32 layer_ID,
+ GimpTextJustification justify);
+gboolean gimp_text_layer_get_color (gint32 layer_ID,
+ GimpRGB *color);
+gboolean gimp_text_layer_set_color (gint32 layer_ID,
+ const GimpRGB *color);
+gdouble gimp_text_layer_get_indent (gint32 layer_ID);
+gboolean gimp_text_layer_set_indent (gint32 layer_ID,
+ gdouble indent);
+gdouble gimp_text_layer_get_line_spacing (gint32 layer_ID);
+gboolean gimp_text_layer_set_line_spacing (gint32 layer_ID,
+ gdouble line_spacing);
+gdouble gimp_text_layer_get_letter_spacing (gint32 layer_ID);
+gboolean gimp_text_layer_set_letter_spacing (gint32 layer_ID,
+ gdouble letter_spacing);
+gboolean gimp_text_layer_resize (gint32 layer_ID,
+ gdouble width,
+ gdouble height);
+GIMP_DEPRECATED_FOR(gimp_text_layer_get_hint_style)
+gboolean gimp_text_layer_get_hinting (gint32 layer_ID,
+ gboolean *autohint);
+GIMP_DEPRECATED_FOR(gimp_text_layer_set_hint_style)
+gboolean gimp_text_layer_set_hinting (gint32 layer_ID,
+ gboolean hinting,
+ gboolean autohint);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_TEXT_LAYER_PDB_H__ */
diff --git a/libgimp/gimptexttool_pdb.c b/libgimp/gimptexttool_pdb.c
new file mode 100644
index 0000000..12fd893
--- /dev/null
+++ b/libgimp/gimptexttool_pdb.c
@@ -0,0 +1,324 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimptexttool_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimptexttool
+ * @title: gimptexttool
+ * @short_description: Functions for controlling the text tool.
+ *
+ * Functions for controlling the text tool.
+ **/
+
+
+/**
+ * gimp_text_fontname:
+ * @image_ID: The image.
+ * @drawable_ID: The affected drawable: (-1 for a new text layer).
+ * @x: The x coordinate for the left of the text bounding box.
+ * @y: The y coordinate for the top of the text bounding box.
+ * @text: The text to generate (in UTF-8 encoding).
+ * @border: The size of the border.
+ * @antialias: Antialiasing.
+ * @size: The size of text in either pixels or points.
+ * @size_type: The units of specified size.
+ * @fontname: The name of the font.
+ *
+ * Add text at the specified location as a floating selection or a new
+ * layer.
+ *
+ * This tool requires a fontname matching an installed PangoFT2 font.
+ * You can specify the fontsize in units of pixels or points, and the
+ * appropriate metric is specified using the size_type argument. The x
+ * and y parameters together control the placement of the new text by
+ * specifying the upper left corner of the text bounding box. If the
+ * specified drawable parameter is valid, the text will be created as a
+ * floating selection attached to the drawable. If the drawable
+ * parameter is not valid (-1), the text will appear as a new layer.
+ * Finally, a border can be specified around the final rendered text.
+ * The border is measured in pixels. Parameter size-type is not used
+ * and is currently ignored. If you need to display a font in points,
+ * divide the size in points by 72.0 and multiply it by the image's
+ * vertical resolution.
+ *
+ * Returns: The new text layer or -1 if no layer was created.
+ **/
+gint32
+gimp_text_fontname (gint32 image_ID,
+ gint32 drawable_ID,
+ gdouble x,
+ gdouble y,
+ const gchar *text,
+ gint border,
+ gboolean antialias,
+ gdouble size,
+ GimpSizeType size_type,
+ const gchar *fontname)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 text_layer_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-text-fontname",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_FLOAT, x,
+ GIMP_PDB_FLOAT, y,
+ GIMP_PDB_STRING, text,
+ GIMP_PDB_INT32, border,
+ GIMP_PDB_INT32, antialias,
+ GIMP_PDB_FLOAT, size,
+ GIMP_PDB_INT32, size_type,
+ GIMP_PDB_STRING, fontname,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ text_layer_ID = return_vals[1].data.d_layer;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return text_layer_ID;
+}
+
+/**
+ * gimp_text_get_extents_fontname:
+ * @text: The text to generate (in UTF-8 encoding).
+ * @size: The size of text in either pixels or points.
+ * @size_type: The units of specified size.
+ * @fontname: The name of the font.
+ * @width: The width of the specified font.
+ * @height: The height of the specified font.
+ * @ascent: The ascent of the specified font.
+ * @descent: The descent of the specified font.
+ *
+ * Get extents of the bounding box for the specified text.
+ *
+ * This tool returns the width and height of a bounding box for the
+ * specified text string with the specified font information. Ascent
+ * and descent for the specified font are returned as well. Parameter
+ * size-type is not used and is currently ignored. If you need to
+ * display a font in points, divide the size in points by 72.0 and
+ * multiply it by the vertical resolution of the image you are taking
+ * into account.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_text_get_extents_fontname (const gchar *text,
+ gdouble size,
+ GimpSizeType size_type,
+ const gchar *fontname,
+ gint *width,
+ gint *height,
+ gint *ascent,
+ gint *descent)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-text-get-extents-fontname",
+ &nreturn_vals,
+ GIMP_PDB_STRING, text,
+ GIMP_PDB_FLOAT, size,
+ GIMP_PDB_INT32, size_type,
+ GIMP_PDB_STRING, fontname,
+ GIMP_PDB_END);
+
+ *width = 0;
+ *height = 0;
+ *ascent = 0;
+ *descent = 0;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *width = return_vals[1].data.d_int32;
+ *height = return_vals[2].data.d_int32;
+ *ascent = return_vals[3].data.d_int32;
+ *descent = return_vals[4].data.d_int32;
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_text:
+ * @image_ID: The image.
+ * @drawable_ID: The affected drawable: (-1 for a new text layer).
+ * @x: The x coordinate for the left of the text bounding box.
+ * @y: The y coordinate for the top of the text bounding box.
+ * @text: The text to generate (in UTF-8 encoding).
+ * @border: The size of the border.
+ * @antialias: Antialiasing.
+ * @size: The size of text in either pixels or points.
+ * @size_type: The units of specified size.
+ * @foundry: The font foundry.
+ * @family: The font family.
+ * @weight: The font weight.
+ * @slant: The font slant.
+ * @set_width: The font set-width.
+ * @spacing: The font spacing.
+ * @registry: The font registry.
+ * @encoding: The font encoding.
+ *
+ * Deprecated: Use gimp_text_fontname() instead.
+ *
+ * Returns: The new text layer or -1 if no layer was created.
+ **/
+gint32
+gimp_text (gint32 image_ID,
+ gint32 drawable_ID,
+ gdouble x,
+ gdouble y,
+ const gchar *text,
+ gint border,
+ gboolean antialias,
+ gdouble size,
+ GimpSizeType size_type,
+ const gchar *foundry,
+ const gchar *family,
+ const gchar *weight,
+ const gchar *slant,
+ const gchar *set_width,
+ const gchar *spacing,
+ const gchar *registry,
+ const gchar *encoding)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 text_layer_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-text",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_FLOAT, x,
+ GIMP_PDB_FLOAT, y,
+ GIMP_PDB_STRING, text,
+ GIMP_PDB_INT32, border,
+ GIMP_PDB_INT32, antialias,
+ GIMP_PDB_FLOAT, size,
+ GIMP_PDB_INT32, size_type,
+ GIMP_PDB_STRING, foundry,
+ GIMP_PDB_STRING, family,
+ GIMP_PDB_STRING, weight,
+ GIMP_PDB_STRING, slant,
+ GIMP_PDB_STRING, set_width,
+ GIMP_PDB_STRING, spacing,
+ GIMP_PDB_STRING, registry,
+ GIMP_PDB_STRING, encoding,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ text_layer_ID = return_vals[1].data.d_layer;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return text_layer_ID;
+}
+
+/**
+ * gimp_text_get_extents:
+ * @text: The text to generate (in UTF-8 encoding).
+ * @size: The size of text in either pixels or points.
+ * @size_type: The units of specified size.
+ * @foundry: The font foundry.
+ * @family: The font family.
+ * @weight: The font weight.
+ * @slant: The font slant.
+ * @set_width: The font set-width.
+ * @spacing: The font spacing.
+ * @registry: The font registry.
+ * @encoding: The font encoding.
+ * @width: The width of the specified font.
+ * @height: The height of the specified font.
+ * @ascent: The ascent of the specified font.
+ * @descent: The descent of the specified font.
+ *
+ * Deprecated: Use gimp_text_get_extents_fontname() instead.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+gimp_text_get_extents (const gchar *text,
+ gdouble size,
+ GimpSizeType size_type,
+ const gchar *foundry,
+ const gchar *family,
+ const gchar *weight,
+ const gchar *slant,
+ const gchar *set_width,
+ const gchar *spacing,
+ const gchar *registry,
+ const gchar *encoding,
+ gint *width,
+ gint *height,
+ gint *ascent,
+ gint *descent)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-text-get-extents",
+ &nreturn_vals,
+ GIMP_PDB_STRING, text,
+ GIMP_PDB_FLOAT, size,
+ GIMP_PDB_INT32, size_type,
+ GIMP_PDB_STRING, foundry,
+ GIMP_PDB_STRING, family,
+ GIMP_PDB_STRING, weight,
+ GIMP_PDB_STRING, slant,
+ GIMP_PDB_STRING, set_width,
+ GIMP_PDB_STRING, spacing,
+ GIMP_PDB_STRING, registry,
+ GIMP_PDB_STRING, encoding,
+ GIMP_PDB_END);
+
+ *width = 0;
+ *height = 0;
+ *ascent = 0;
+ *descent = 0;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *width = return_vals[1].data.d_int32;
+ *height = return_vals[2].data.d_int32;
+ *ascent = return_vals[3].data.d_int32;
+ *descent = return_vals[4].data.d_int32;
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
diff --git a/libgimp/gimptexttool_pdb.h b/libgimp/gimptexttool_pdb.h
new file mode 100644
index 0000000..05d1dfb
--- /dev/null
+++ b/libgimp/gimptexttool_pdb.h
@@ -0,0 +1,91 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimptexttool_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_TEXT_TOOL_PDB_H__
+#define __GIMP_TEXT_TOOL_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gint32 gimp_text_fontname (gint32 image_ID,
+ gint32 drawable_ID,
+ gdouble x,
+ gdouble y,
+ const gchar *text,
+ gint border,
+ gboolean antialias,
+ gdouble size,
+ GimpSizeType size_type,
+ const gchar *fontname);
+gboolean gimp_text_get_extents_fontname (const gchar *text,
+ gdouble size,
+ GimpSizeType size_type,
+ const gchar *fontname,
+ gint *width,
+ gint *height,
+ gint *ascent,
+ gint *descent);
+GIMP_DEPRECATED_FOR(gimp_text_fontname)
+gint32 gimp_text (gint32 image_ID,
+ gint32 drawable_ID,
+ gdouble x,
+ gdouble y,
+ const gchar *text,
+ gint border,
+ gboolean antialias,
+ gdouble size,
+ GimpSizeType size_type,
+ const gchar *foundry,
+ const gchar *family,
+ const gchar *weight,
+ const gchar *slant,
+ const gchar *set_width,
+ const gchar *spacing,
+ const gchar *registry,
+ const gchar *encoding);
+GIMP_DEPRECATED_FOR(gimp_text_get_extents_fontname)
+gboolean gimp_text_get_extents (const gchar *text,
+ gdouble size,
+ GimpSizeType size_type,
+ const gchar *foundry,
+ const gchar *family,
+ const gchar *weight,
+ const gchar *slant,
+ const gchar *set_width,
+ const gchar *spacing,
+ const gchar *registry,
+ const gchar *encoding,
+ gint *width,
+ gint *height,
+ gint *ascent,
+ gint *descent);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_TEXT_TOOL_PDB_H__ */
diff --git a/libgimp/gimptile.c b/libgimp/gimptile.c
new file mode 100644
index 0000000..4570ebb
--- /dev/null
+++ b/libgimp/gimptile.c
@@ -0,0 +1,433 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimptile.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gio/gio.h>
+
+#define GIMP_DISABLE_DEPRECATION_WARNINGS
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpbase/gimpprotocol.h"
+#include "libgimpbase/gimpwire.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimptile
+ * @title: gimptile
+ * @short_description: Functions for working with tiles.
+ *
+ * Functions for working with tiles.
+ **/
+
+
+/* This is the percentage of the maximum cache size that
+ * should be cleared from the cache when an eviction is
+ * necessary.
+ */
+#define FREE_QUANTUM 0.1
+
+
+void gimp_read_expect_msg (GimpWireMessage *msg,
+ gint type);
+
+static void gimp_tile_get (GimpTile *tile);
+static void gimp_tile_put (GimpTile *tile);
+static void gimp_tile_cache_insert (GimpTile *tile);
+static void gimp_tile_cache_flush (GimpTile *tile);
+
+
+/* private variables */
+
+static GHashTable * tile_hash_table = NULL;
+static GList * tile_list_head = NULL;
+static GList * tile_list_tail = NULL;
+static gulong max_tile_size = 0;
+static gulong cur_cache_size = 0;
+static gulong max_cache_size = 0;
+
+
+/* public functions */
+
+void
+gimp_tile_ref (GimpTile *tile)
+{
+ g_return_if_fail (tile != NULL);
+
+ tile->ref_count++;
+
+ if (tile->ref_count == 1)
+ {
+ gimp_tile_get (tile);
+ tile->dirty = FALSE;
+ }
+
+ gimp_tile_cache_insert (tile);
+}
+
+void
+gimp_tile_ref_zero (GimpTile *tile)
+{
+ g_return_if_fail (tile != NULL);
+
+ tile->ref_count++;
+
+ if (tile->ref_count == 1)
+ tile->data = g_new0 (guchar, tile->ewidth * tile->eheight * tile->bpp);
+
+ gimp_tile_cache_insert (tile);
+}
+
+void
+_gimp_tile_ref_nocache (GimpTile *tile,
+ gboolean init)
+{
+ g_return_if_fail (tile != NULL);
+
+ tile->ref_count++;
+
+ if (tile->ref_count == 1)
+ {
+ if (init)
+ {
+ gimp_tile_get (tile);
+ tile->dirty = FALSE;
+ }
+ else
+ {
+ tile->data = g_new (guchar, tile->ewidth * tile->eheight * tile->bpp);
+ }
+ }
+}
+
+void
+gimp_tile_unref (GimpTile *tile,
+ gboolean dirty)
+{
+ g_return_if_fail (tile != NULL);
+ g_return_if_fail (tile->ref_count > 0);
+
+ tile->ref_count--;
+ tile->dirty |= dirty;
+
+ if (tile->ref_count == 0)
+ {
+ gimp_tile_flush (tile);
+ g_free (tile->data);
+ tile->data = NULL;
+ }
+}
+
+void
+gimp_tile_flush (GimpTile *tile)
+{
+ g_return_if_fail (tile != NULL);
+
+ if (tile->data && tile->dirty)
+ {
+ gimp_tile_put (tile);
+ tile->dirty = FALSE;
+ }
+}
+
+/**
+ * gimp_tile_cache_size:
+ * @kilobytes: new cache size in kilobytes
+ *
+ * Sets the size of the tile cache on the plug-in side. The tile cache
+ * is used to reduce the number of tiles exchanged between the GIMP core
+ * and the plug-in. See also gimp_tile_cache_ntiles().
+ **/
+void
+gimp_tile_cache_size (gulong kilobytes)
+{
+ max_cache_size = kilobytes * 1024;
+}
+
+/**
+ * gimp_tile_cache_ntiles:
+ * @ntiles: number of tiles that should fit into the cache
+ *
+ * Sets the size of the tile cache on the plug-in side. This function
+ * is similar to gimp_tile_cache_size() but supports specifying the
+ * number of tiles directly.
+ *
+ * If your plug-in access pixels tile-by-tile, it doesn't need a tile
+ * cache at all. If however the plug-in accesses drawable pixel data
+ * row-by-row, it should set the tile cache large enough to hold the
+ * number of tiles per row. Double this size if your plug-in uses
+ * shadow tiles.
+ **/
+void
+gimp_tile_cache_ntiles (gulong ntiles)
+{
+ gimp_tile_cache_size ((ntiles *
+ gimp_tile_width () *
+ gimp_tile_height () * 4 + 1023) / 1024);
+}
+
+void
+_gimp_tile_cache_flush_drawable (GimpDrawable *drawable)
+{
+ GList *list;
+
+ g_return_if_fail (drawable != NULL);
+
+ list = tile_list_head;
+ while (list)
+ {
+ GimpTile *tile = list->data;
+
+ list = list->next;
+
+ if (tile->drawable == drawable)
+ gimp_tile_cache_flush (tile);
+ }
+}
+
+
+/* private functions */
+
+static void
+gimp_tile_get (GimpTile *tile)
+{
+ extern GIOChannel *_writechannel;
+
+ GPTileReq tile_req;
+ GPTileData *tile_data;
+ GimpWireMessage msg;
+
+ tile_req.drawable_ID = tile->drawable->drawable_id;
+ tile_req.tile_num = tile->tile_num;
+ tile_req.shadow = tile->shadow;
+
+ if (! gp_tile_req_write (_writechannel, &tile_req, NULL))
+ gimp_quit ();
+
+ gimp_read_expect_msg (&msg, GP_TILE_DATA);
+
+ tile_data = msg.data;
+ if (tile_data->drawable_ID != tile->drawable->drawable_id ||
+ tile_data->tile_num != tile->tile_num ||
+ tile_data->shadow != tile->shadow ||
+ tile_data->width != tile->ewidth ||
+ tile_data->height != tile->eheight ||
+ tile_data->bpp != tile->bpp)
+ {
+ g_message ("received tile info did not match computed tile info");
+ gimp_quit ();
+ }
+
+ if (tile_data->use_shm)
+ {
+ tile->data = g_memdup (gimp_shm_addr (),
+ tile->ewidth * tile->eheight * tile->bpp);
+ }
+ else
+ {
+ tile->data = tile_data->data;
+ tile_data->data = NULL;
+ }
+
+ if (! gp_tile_ack_write (_writechannel, NULL))
+ gimp_quit ();
+
+ gimp_wire_destroy (&msg);
+}
+
+static void
+gimp_tile_put (GimpTile *tile)
+{
+ extern GIOChannel *_writechannel;
+
+ GPTileReq tile_req;
+ GPTileData tile_data;
+ GPTileData *tile_info;
+ GimpWireMessage msg;
+
+ tile_req.drawable_ID = -1;
+ tile_req.tile_num = 0;
+ tile_req.shadow = 0;
+
+ if (! gp_tile_req_write (_writechannel, &tile_req, NULL))
+ gimp_quit ();
+
+ gimp_read_expect_msg (&msg, GP_TILE_DATA);
+
+ tile_info = msg.data;
+
+ tile_data.drawable_ID = tile->drawable->drawable_id;
+ tile_data.tile_num = tile->tile_num;
+ tile_data.shadow = tile->shadow;
+ tile_data.bpp = tile->bpp;
+ tile_data.width = tile->ewidth;
+ tile_data.height = tile->eheight;
+ tile_data.use_shm = tile_info->use_shm;
+ tile_data.data = NULL;
+
+ if (tile_info->use_shm)
+ memcpy (gimp_shm_addr (),
+ tile->data,
+ tile->ewidth * tile->eheight * tile->bpp);
+ else
+ tile_data.data = tile->data;
+
+ if (! gp_tile_data_write (_writechannel, &tile_data, NULL))
+ gimp_quit ();
+
+ if (! tile_info->use_shm)
+ tile_data.data = NULL;
+
+ gimp_wire_destroy (&msg);
+
+ gimp_read_expect_msg (&msg, GP_TILE_ACK);
+
+ gimp_wire_destroy (&msg);
+}
+
+/* This function is nearly identical to the function 'tile_cache_insert'
+ * in the file 'tile_cache.c' which is part of the main gimp application.
+ */
+static void
+gimp_tile_cache_insert (GimpTile *tile)
+{
+ GList *list;
+
+ if (!tile_hash_table)
+ {
+ tile_hash_table = g_hash_table_new (g_direct_hash, NULL);
+ max_tile_size = gimp_tile_width () * gimp_tile_height () * 4;
+ }
+
+ /* First check and see if the tile is already
+ * in the cache. In that case we will simply place
+ * it at the end of the tile list to indicate that
+ * it was the most recently accessed tile.
+ */
+ list = g_hash_table_lookup (tile_hash_table, tile);
+
+ if (list)
+ {
+ /* The tile was already in the cache. Place it at
+ * the end of the tile list.
+ */
+
+ /* If the tile is already at the end of the list, we are done */
+ if (list == tile_list_tail)
+ return;
+
+ /* At this point we have at least two elements in our list */
+ g_assert (tile_list_head != tile_list_tail);
+
+ tile_list_head = g_list_remove_link (tile_list_head, list);
+
+ tile_list_tail = g_list_last (g_list_concat (tile_list_tail, list));
+ }
+ else
+ {
+ /* The tile was not in the cache. First check and see
+ * if there is room in the cache. If not then we'll have
+ * to make room first. Note: it might be the case that the
+ * cache is smaller than the size of a tile in which case
+ * it won't be possible to put it in the cache.
+ */
+
+ if ((cur_cache_size + max_tile_size) > max_cache_size)
+ {
+ while (tile_list_head &&
+ (cur_cache_size +
+ max_cache_size * FREE_QUANTUM) > max_cache_size)
+ {
+ gimp_tile_cache_flush ((GimpTile *) tile_list_head->data);
+ }
+
+ if ((cur_cache_size + max_tile_size) > max_cache_size)
+ return;
+ }
+
+ /* Place the tile at the end of the tile list.
+ */
+ tile_list_tail = g_list_append (tile_list_tail, tile);
+
+ if (! tile_list_head)
+ tile_list_head = tile_list_tail;
+
+ tile_list_tail = g_list_last (tile_list_tail);
+
+ /* Add the tiles list node to the tile hash table.
+ */
+ g_hash_table_insert (tile_hash_table, tile, tile_list_tail);
+
+ /* Note the increase in the number of bytes the cache
+ * is referencing.
+ */
+ cur_cache_size += max_tile_size;
+
+ /* Reference the tile so that it won't be returned to
+ * the main gimp application immediately.
+ */
+ tile->ref_count++;
+ }
+}
+
+static void
+gimp_tile_cache_flush (GimpTile *tile)
+{
+ GList *list;
+
+ if (! tile_hash_table)
+ return;
+
+ /* Find where the tile is in the cache.
+ */
+ list = g_hash_table_lookup (tile_hash_table, tile);
+
+ if (list)
+ {
+ /* If the tile is in the cache, then remove it from the
+ * tile list.
+ */
+ if (list == tile_list_tail)
+ tile_list_tail = tile_list_tail->prev;
+
+ tile_list_head = g_list_remove_link (tile_list_head, list);
+
+ if (! tile_list_head)
+ tile_list_tail = NULL;
+
+ /* Remove the tile from the tile hash table.
+ */
+ g_hash_table_remove (tile_hash_table, tile);
+ g_list_free (list);
+
+ /* Note the decrease in the number of bytes the cache
+ * is referencing.
+ */
+ cur_cache_size -= max_tile_size;
+
+ /* Unreference the tile.
+ */
+ gimp_tile_unref (tile, FALSE);
+ }
+}
diff --git a/libgimp/gimptile.h b/libgimp/gimptile.h
new file mode 100644
index 0000000..50b54da
--- /dev/null
+++ b/libgimp/gimptile.h
@@ -0,0 +1,73 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimptile.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_TILE_H__
+#define __GIMP_TILE_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+struct _GimpTile
+{
+ guint ewidth; /* the effective width of the tile */
+ guint eheight; /* the effective height of the tile */
+ guint bpp; /* the bytes per pixel (1, 2, 3 or 4 ) */
+ guint tile_num; /* the number of this tile within the drawable */
+ guint16 ref_count; /* reference count for the tile */
+ guint dirty : 1; /* is the tile dirty? has it been modified? */
+ guint shadow: 1; /* is this a shadow tile */
+ guchar *data; /* the pixel data for the tile */
+ GimpDrawable *drawable; /* the drawable this tile came from */
+};
+
+
+GIMP_DEPRECATED
+void gimp_tile_ref (GimpTile *tile);
+GIMP_DEPRECATED
+void gimp_tile_ref_zero (GimpTile *tile);
+GIMP_DEPRECATED
+void gimp_tile_unref (GimpTile *tile,
+ gboolean dirty);
+GIMP_DEPRECATED
+void gimp_tile_flush (GimpTile *tile);
+
+GIMP_DEPRECATED
+void gimp_tile_cache_size (gulong kilobytes);
+GIMP_DEPRECATED
+void gimp_tile_cache_ntiles (gulong ntiles);
+
+
+/* private function */
+
+G_GNUC_INTERNAL void _gimp_tile_ref_nocache (GimpTile *tile,
+ gboolean init);
+
+G_GNUC_INTERNAL void _gimp_tile_cache_flush_drawable (GimpDrawable *drawable);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_TILE_H__ */
diff --git a/libgimp/gimptilebackendplugin.c b/libgimp/gimptilebackendplugin.c
new file mode 100644
index 0000000..d1767fa
--- /dev/null
+++ b/libgimp/gimptilebackendplugin.c
@@ -0,0 +1,313 @@
+/* gimptilebackendtilemanager.c
+ * Copyright (C) 2012 Øyvind Kolås <pippin@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (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
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <gegl.h>
+
+#define GIMP_DISABLE_DEPRECATION_WARNINGS
+
+#include "gimp.h"
+#include "gimptilebackendplugin.h"
+
+
+#define TILE_WIDTH gimp_tile_width()
+#define TILE_HEIGHT gimp_tile_height()
+
+
+struct _GimpTileBackendPluginPrivate
+{
+ GimpDrawable *drawable;
+ gboolean shadow;
+ gint mul;
+};
+
+
+static gint
+gimp_gegl_tile_mul (void)
+{
+ static gint mul = 1;
+ static gboolean inited = FALSE;
+
+ if (G_LIKELY (inited))
+ return mul;
+
+ inited = TRUE;
+
+ if (g_getenv ("GIMP_GEGL_TILE_MUL"))
+ mul = atoi (g_getenv ("GIMP_GEGL_TILE_MUL"));
+
+ if (mul < 1)
+ mul = 1;
+
+ return mul;
+}
+
+static void gimp_tile_backend_plugin_finalize (GObject *object);
+static gpointer gimp_tile_backend_plugin_command (GeglTileSource *tile_store,
+ GeglTileCommand command,
+ gint x,
+ gint y,
+ gint z,
+ gpointer data);
+
+static void gimp_tile_write_mul (GimpTileBackendPlugin *backend_plugin,
+ gint x,
+ gint y,
+ guchar *source);
+
+static GeglTile * gimp_tile_read_mul (GimpTileBackendPlugin *backend_plugin,
+ gint x,
+ gint y);
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpTileBackendPlugin, _gimp_tile_backend_plugin,
+ GEGL_TYPE_TILE_BACKEND)
+
+#define parent_class _gimp_tile_backend_plugin_parent_class
+
+
+static GMutex backend_plugin_mutex;
+
+
+static void
+_gimp_tile_backend_plugin_class_init (GimpTileBackendPluginClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gimp_tile_backend_plugin_finalize;
+}
+
+static void
+_gimp_tile_backend_plugin_init (GimpTileBackendPlugin *backend)
+{
+ GeglTileSource *source = GEGL_TILE_SOURCE (backend);
+
+ backend->priv = _gimp_tile_backend_plugin_get_instance_private (backend);
+
+ source->command = gimp_tile_backend_plugin_command;
+}
+
+static void
+gimp_tile_backend_plugin_finalize (GObject *object)
+{
+ GimpTileBackendPlugin *backend = GIMP_TILE_BACKEND_PLUGIN (object);
+
+ if (backend->priv->drawable) /* This also causes a flush */
+ gimp_drawable_detach (backend->priv->drawable);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gpointer
+gimp_tile_backend_plugin_command (GeglTileSource *tile_store,
+ GeglTileCommand command,
+ gint x,
+ gint y,
+ gint z,
+ gpointer data)
+{
+ GimpTileBackendPlugin *backend_plugin = GIMP_TILE_BACKEND_PLUGIN (tile_store);
+ gpointer result = NULL;
+
+ switch (command)
+ {
+ case GEGL_TILE_GET:
+ /* TODO: fetch mipmapped tiles directly from gimp, instead of returning
+ * NULL to render them locally
+ */
+ if (z == 0)
+ {
+ g_mutex_lock (&backend_plugin_mutex);
+
+ result = gimp_tile_read_mul (backend_plugin, x, y);
+
+ g_mutex_unlock (&backend_plugin_mutex);
+ }
+ break;
+
+ case GEGL_TILE_SET:
+ /* TODO: actually store mipmapped tiles */
+ if (z == 0)
+ {
+ g_mutex_lock (&backend_plugin_mutex);
+
+ gimp_tile_write_mul (backend_plugin, x, y, gegl_tile_get_data (data));
+
+ g_mutex_unlock (&backend_plugin_mutex);
+ }
+
+ gegl_tile_mark_as_stored (data);
+ break;
+
+ case GEGL_TILE_FLUSH:
+ g_mutex_lock (&backend_plugin_mutex);
+
+ gimp_drawable_flush (backend_plugin->priv->drawable);
+
+ g_mutex_unlock (&backend_plugin_mutex);
+ break;
+
+ default:
+ result = gegl_tile_backend_command (GEGL_TILE_BACKEND (tile_store),
+ command, x, y, z, data);
+ break;
+ }
+
+ return result;
+}
+
+static GeglTile *
+gimp_tile_read_mul (GimpTileBackendPlugin *backend_plugin,
+ gint x,
+ gint y)
+{
+ GimpTileBackendPluginPrivate *priv = backend_plugin->priv;
+ GeglTileBackend *backend = GEGL_TILE_BACKEND (backend_plugin);
+ GeglTile *tile;
+ gint tile_size;
+ gint u, v;
+ gint mul = priv->mul;
+ guchar *tile_data;
+
+ x *= mul;
+ y *= mul;
+
+ tile_size = gegl_tile_backend_get_tile_size (backend);
+ tile = gegl_tile_new (tile_size);
+ tile_data = gegl_tile_get_data (tile);
+
+ for (u = 0; u < mul; u++)
+ {
+ for (v = 0; v < mul; v++)
+ {
+ GimpTile *gimp_tile;
+
+ if (x + u >= priv->drawable->ntile_cols ||
+ y + v >= priv->drawable->ntile_rows)
+ continue;
+
+ gimp_tile = gimp_drawable_get_tile (priv->drawable,
+ priv->shadow,
+ y + v, x + u);
+ _gimp_tile_ref_nocache (gimp_tile, TRUE);
+
+ {
+ gint ewidth = gimp_tile->ewidth;
+ gint eheight = gimp_tile->eheight;
+ gint bpp = gimp_tile->bpp;
+ gint tile_stride = mul * TILE_WIDTH * bpp;
+ gint gimp_tile_stride = ewidth * bpp;
+ gint row;
+
+ for (row = 0; row < eheight; row++)
+ {
+ memcpy (tile_data + (row + TILE_HEIGHT * v) *
+ tile_stride + u * TILE_WIDTH * bpp,
+ ((gchar *) gimp_tile->data) + row * gimp_tile_stride,
+ gimp_tile_stride);
+ }
+ }
+
+ gimp_tile_unref (gimp_tile, FALSE);
+ }
+ }
+
+ return tile;
+}
+
+static void
+gimp_tile_write_mul (GimpTileBackendPlugin *backend_plugin,
+ gint x,
+ gint y,
+ guchar *source)
+{
+ GimpTileBackendPluginPrivate *priv = backend_plugin->priv;
+ gint u, v;
+ gint mul = priv->mul;
+
+ x *= mul;
+ y *= mul;
+
+ for (v = 0; v < mul; v++)
+ {
+ for (u = 0; u < mul; u++)
+ {
+ GimpTile *gimp_tile;
+
+ if (x + u >= priv->drawable->ntile_cols ||
+ y + v >= priv->drawable->ntile_rows)
+ continue;
+
+ gimp_tile = gimp_drawable_get_tile (priv->drawable,
+ priv->shadow,
+ y+v, x+u);
+ _gimp_tile_ref_nocache (gimp_tile, FALSE);
+
+ {
+ gint ewidth = gimp_tile->ewidth;
+ gint eheight = gimp_tile->eheight;
+ gint bpp = gimp_tile->bpp;
+ gint tile_stride = mul * TILE_WIDTH * bpp;
+ gint gimp_tile_stride = ewidth * bpp;
+ gint row;
+
+ for (row = 0; row < eheight; row++)
+ memcpy (((gchar *)gimp_tile->data) + row * gimp_tile_stride,
+ source + (row + v * TILE_HEIGHT) *
+ tile_stride + u * TILE_WIDTH * bpp,
+ gimp_tile_stride);
+ }
+
+ gimp_tile_unref (gimp_tile, TRUE);
+ }
+ }
+}
+
+GeglTileBackend *
+_gimp_tile_backend_plugin_new (GimpDrawable *drawable,
+ gint shadow)
+{
+ GeglTileBackend *backend;
+ GimpTileBackendPlugin *backend_plugin;
+ const Babl *format;
+ gint width = gimp_drawable_width (drawable->drawable_id);
+ gint height = gimp_drawable_height (drawable->drawable_id);
+ gint mul = gimp_gegl_tile_mul ();
+
+ format = gimp_drawable_get_format (drawable->drawable_id);
+
+ backend = g_object_new (GIMP_TYPE_TILE_BACKEND_PLUGIN,
+ "tile-width", TILE_WIDTH * mul,
+ "tile-height", TILE_HEIGHT * mul,
+ "format", format,
+ NULL);
+
+ backend_plugin = GIMP_TILE_BACKEND_PLUGIN (backend);
+
+ backend_plugin->priv->drawable = drawable;
+ backend_plugin->priv->mul = mul;
+ backend_plugin->priv->shadow = shadow;
+
+ gegl_tile_backend_set_extent (backend,
+ GEGL_RECTANGLE (0, 0, width, height));
+
+ return backend;
+}
diff --git a/libgimp/gimptilebackendplugin.h b/libgimp/gimptilebackendplugin.h
new file mode 100644
index 0000000..7ed9fad
--- /dev/null
+++ b/libgimp/gimptilebackendplugin.h
@@ -0,0 +1,59 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimptilebackendtilemanager.h
+ * Copyright (C) 2011 Øyvind Kolås <pippin@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (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
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_TILE_BACKEND_PLUGIN_H__
+#define __GIMP_TILE_BACKEND_PLUGIN_H__
+
+#include <gegl-buffer-backend.h>
+
+G_BEGIN_DECLS
+
+#define GIMP_TYPE_TILE_BACKEND_PLUGIN (_gimp_tile_backend_plugin_get_type ())
+#define GIMP_TILE_BACKEND_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TILE_BACKEND_PLUGIN, GimpTileBackendPlugin))
+#define GIMP_TILE_BACKEND_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TILE_BACKEND_PLUGIN, GimpTileBackendPluginClass))
+#define GIMP_IS_TILE_BACKEND_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TILE_BACKEND_PLUGIN))
+#define GIMP_IS_TILE_BACKEND_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TILE_BACKEND_PLUGIN))
+#define GIMP_TILE_BACKEND_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TILE_BACKEND_PLUGIN, GimpTileBackendPluginClass))
+
+
+typedef struct _GimpTileBackendPlugin GimpTileBackendPlugin;
+typedef struct _GimpTileBackendPluginClass GimpTileBackendPluginClass;
+typedef struct _GimpTileBackendPluginPrivate GimpTileBackendPluginPrivate;
+
+struct _GimpTileBackendPlugin
+{
+ GeglTileBackend parent_instance;
+
+ GimpTileBackendPluginPrivate *priv;
+};
+
+struct _GimpTileBackendPluginClass
+{
+ GeglTileBackendClass parent_class;
+};
+
+GType _gimp_tile_backend_plugin_get_type (void) G_GNUC_CONST;
+
+GeglTileBackend * _gimp_tile_backend_plugin_new (GimpDrawable *drawable,
+ gint shadow);
+
+G_END_DECLS
+
+#endif /* __GIMP_TILE_BACKEND_plugin_H__ */
diff --git a/libgimp/gimptransformtools_pdb.c b/libgimp/gimptransformtools_pdb.c
new file mode 100644
index 0000000..759a768
--- /dev/null
+++ b/libgimp/gimptransformtools_pdb.c
@@ -0,0 +1,287 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimptransformtools_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimptransformtools
+ * @title: gimptransformtools
+ * @short_description: Access to toolbox transform tools.
+ *
+ * Functions giving access to toolbox transform tools.
+ **/
+
+
+/**
+ * gimp_flip:
+ * @drawable_ID: The affected drawable.
+ * @flip_type: Type of flip.
+ *
+ * Deprecated: Use gimp_item_transform_flip_simple() instead.
+ *
+ * Returns: The flipped drawable.
+ **/
+gint32
+gimp_flip (gint32 drawable_ID,
+ GimpOrientationType flip_type)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_drawable_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-flip",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, flip_type,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_drawable_ID = return_vals[1].data.d_drawable;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_drawable_ID;
+}
+
+/**
+ * gimp_perspective:
+ * @drawable_ID: The affected drawable.
+ * @interpolation: Whether to use interpolation.
+ * @x0: The new x coordinate of upper-left corner of original bounding box.
+ * @y0: The new y coordinate of upper-left corner of original bounding box.
+ * @x1: The new x coordinate of upper-right corner of original bounding box.
+ * @y1: The new y coordinate of upper-right corner of original bounding box.
+ * @x2: The new x coordinate of lower-left corner of original bounding box.
+ * @y2: The new y coordinate of lower-left corner of original bounding box.
+ * @x3: The new x coordinate of lower-right corner of original bounding box.
+ * @y3: The new y coordinate of lower-right corner of original bounding box.
+ *
+ * Deprecated: Use gimp_item_transform_perspective() instead.
+ *
+ * Returns: The newly mapped drawable.
+ **/
+gint32
+gimp_perspective (gint32 drawable_ID,
+ gboolean interpolation,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2,
+ gdouble x3,
+ gdouble y3)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_drawable_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-perspective",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, interpolation,
+ GIMP_PDB_FLOAT, x0,
+ GIMP_PDB_FLOAT, y0,
+ GIMP_PDB_FLOAT, x1,
+ GIMP_PDB_FLOAT, y1,
+ GIMP_PDB_FLOAT, x2,
+ GIMP_PDB_FLOAT, y2,
+ GIMP_PDB_FLOAT, x3,
+ GIMP_PDB_FLOAT, y3,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_drawable_ID = return_vals[1].data.d_drawable;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_drawable_ID;
+}
+
+/**
+ * gimp_rotate:
+ * @drawable_ID: The affected drawable.
+ * @interpolation: Whether to use interpolation.
+ * @angle: The angle of rotation (radians).
+ *
+ * Deprecated: Use gimp_item_transform_rotate() instead.
+ *
+ * Returns: The rotated drawable.
+ **/
+gint32
+gimp_rotate (gint32 drawable_ID,
+ gboolean interpolation,
+ gdouble angle)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_drawable_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-rotate",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, interpolation,
+ GIMP_PDB_FLOAT, angle,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_drawable_ID = return_vals[1].data.d_drawable;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_drawable_ID;
+}
+
+/**
+ * gimp_scale:
+ * @drawable_ID: The affected drawable.
+ * @interpolation: Whether to use interpolation.
+ * @x0: The new x coordinate of the upper-left corner of the scaled region.
+ * @y0: The new y coordinate of the upper-left corner of the scaled region.
+ * @x1: The new x coordinate of the lower-right corner of the scaled region.
+ * @y1: The new y coordinate of the lower-right corner of the scaled region.
+ *
+ * Deprecated: Use gimp_item_transform_scale() instead.
+ *
+ * Returns: The scaled drawable.
+ **/
+gint32
+gimp_scale (gint32 drawable_ID,
+ gboolean interpolation,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_drawable_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-scale",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, interpolation,
+ GIMP_PDB_FLOAT, x0,
+ GIMP_PDB_FLOAT, y0,
+ GIMP_PDB_FLOAT, x1,
+ GIMP_PDB_FLOAT, y1,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_drawable_ID = return_vals[1].data.d_drawable;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_drawable_ID;
+}
+
+/**
+ * gimp_shear:
+ * @drawable_ID: The affected drawable.
+ * @interpolation: Whether to use interpolation.
+ * @shear_type: Type of shear.
+ * @magnitude: The magnitude of the shear.
+ *
+ * Deprecated: Use gimp_item_transform_shear() instead.
+ *
+ * Returns: The sheared drawable.
+ **/
+gint32
+gimp_shear (gint32 drawable_ID,
+ gboolean interpolation,
+ GimpOrientationType shear_type,
+ gdouble magnitude)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_drawable_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-shear",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, interpolation,
+ GIMP_PDB_INT32, shear_type,
+ GIMP_PDB_FLOAT, magnitude,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_drawable_ID = return_vals[1].data.d_drawable;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_drawable_ID;
+}
+
+/**
+ * gimp_transform_2d:
+ * @drawable_ID: The affected drawable.
+ * @interpolation: Whether to use interpolation.
+ * @source_x: X coordinate of the transformation center.
+ * @source_y: Y coordinate of the transformation center.
+ * @scale_x: Amount to scale in x direction.
+ * @scale_y: Amount to scale in y direction.
+ * @angle: The angle of rotation (radians).
+ * @dest_x: X coordinate of where the centre goes.
+ * @dest_y: Y coordinate of where the centre goes.
+ *
+ * Deprecated: Use gimp_item_transform_2d() instead.
+ *
+ * Returns: The transformed drawable.
+ **/
+gint32
+gimp_transform_2d (gint32 drawable_ID,
+ gboolean interpolation,
+ gdouble source_x,
+ gdouble source_y,
+ gdouble scale_x,
+ gdouble scale_y,
+ gdouble angle,
+ gdouble dest_x,
+ gdouble dest_y)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 ret_drawable_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-transform-2d",
+ &nreturn_vals,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_INT32, interpolation,
+ GIMP_PDB_FLOAT, source_x,
+ GIMP_PDB_FLOAT, source_y,
+ GIMP_PDB_FLOAT, scale_x,
+ GIMP_PDB_FLOAT, scale_y,
+ GIMP_PDB_FLOAT, angle,
+ GIMP_PDB_FLOAT, dest_x,
+ GIMP_PDB_FLOAT, dest_y,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ ret_drawable_ID = return_vals[1].data.d_drawable;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return ret_drawable_ID;
+}
diff --git a/libgimp/gimptransformtools_pdb.h b/libgimp/gimptransformtools_pdb.h
new file mode 100644
index 0000000..6f53890
--- /dev/null
+++ b/libgimp/gimptransformtools_pdb.h
@@ -0,0 +1,79 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimptransformtools_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_TRANSFORM_TOOLS_PDB_H__
+#define __GIMP_TRANSFORM_TOOLS_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+GIMP_DEPRECATED_FOR(gimp_item_transform_flip_simple)
+gint32 gimp_flip (gint32 drawable_ID,
+ GimpOrientationType flip_type);
+GIMP_DEPRECATED_FOR(gimp_item_transform_perspective)
+gint32 gimp_perspective (gint32 drawable_ID,
+ gboolean interpolation,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2,
+ gdouble x3,
+ gdouble y3);
+GIMP_DEPRECATED_FOR(gimp_item_transform_rotate)
+gint32 gimp_rotate (gint32 drawable_ID,
+ gboolean interpolation,
+ gdouble angle);
+GIMP_DEPRECATED_FOR(gimp_item_transform_scale)
+gint32 gimp_scale (gint32 drawable_ID,
+ gboolean interpolation,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1);
+GIMP_DEPRECATED_FOR(gimp_item_transform_shear)
+gint32 gimp_shear (gint32 drawable_ID,
+ gboolean interpolation,
+ GimpOrientationType shear_type,
+ gdouble magnitude);
+GIMP_DEPRECATED_FOR(gimp_item_transform_2d)
+gint32 gimp_transform_2d (gint32 drawable_ID,
+ gboolean interpolation,
+ gdouble source_x,
+ gdouble source_y,
+ gdouble scale_x,
+ gdouble scale_y,
+ gdouble angle,
+ gdouble dest_x,
+ gdouble dest_y);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_TRANSFORM_TOOLS_PDB_H__ */
diff --git a/libgimp/gimptypes.h b/libgimp/gimptypes.h
new file mode 100644
index 0000000..6866dbe
--- /dev/null
+++ b/libgimp/gimptypes.h
@@ -0,0 +1,82 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimptypes.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_TYPES_H__
+#define __GIMP_TYPES_H__
+
+#include <libgimpbase/gimpbasetypes.h>
+
+G_BEGIN_DECLS
+
+/* For information look into the html documentation */
+
+
+typedef struct _GimpPlugInInfo GimpPlugInInfo;
+typedef struct _GimpTile GimpTile;
+typedef struct _GimpDrawable GimpDrawable;
+typedef struct _GimpPixelRgn GimpPixelRgn;
+typedef struct _GimpParamDef GimpParamDef;
+typedef struct _GimpParamRegion GimpParamRegion;
+typedef union _GimpParamData GimpParamData;
+typedef struct _GimpParam GimpParam;
+
+
+#ifndef GIMP_DISABLE_DEPRECATED
+
+/* This is so ugly it hurts. C++ won't let us have enum GimpLayerMode
+ * in the API where we used to have enum GimpLayerModeEffects, so
+ * typedef and define around to make it happy:
+ */
+typedef GimpLayerMode GimpLayerModeEffects;
+
+#define GIMP_NORMAL_MODE GIMP_LAYER_MODE_NORMAL_LEGACY
+#define GIMP_DISSOLVE_MODE GIMP_LAYER_MODE_DISSOLVE
+#define GIMP_BEHIND_MODE GIMP_LAYER_MODE_BEHIND_LEGACY
+#define GIMP_MULTIPLY_MODE GIMP_LAYER_MODE_MULTIPLY_LEGACY
+#define GIMP_SCREEN_MODE GIMP_LAYER_MODE_SCREEN_LEGACY
+#define GIMP_OVERLAY_MODE GIMP_LAYER_MODE_OVERLAY_LEGACY
+#define GIMP_DIFFERENCE_MODE GIMP_LAYER_MODE_DIFFERENCE_LEGACY
+#define GIMP_ADDITION_MODE GIMP_LAYER_MODE_ADDITION_LEGACY
+#define GIMP_SUBTRACT_MODE GIMP_LAYER_MODE_SUBTRACT_LEGACY
+#define GIMP_DARKEN_ONLY_MODE GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY
+#define GIMP_LIGHTEN_ONLY_MODE GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY
+#define GIMP_HUE_MODE GIMP_LAYER_MODE_HSV_HUE_LEGACY
+#define GIMP_SATURATION_MODE GIMP_LAYER_MODE_HSV_SATURATION_LEGACY
+#define GIMP_COLOR_MODE GIMP_LAYER_MODE_HSL_COLOR_LEGACY
+#define GIMP_VALUE_MODE GIMP_LAYER_MODE_HSV_VALUE_LEGACY
+#define GIMP_DIVIDE_MODE GIMP_LAYER_MODE_DIVIDE_LEGACY
+#define GIMP_DODGE_MODE GIMP_LAYER_MODE_DODGE_LEGACY
+#define GIMP_BURN_MODE GIMP_LAYER_MODE_BURN_LEGACY
+#define GIMP_HARDLIGHT_MODE GIMP_LAYER_MODE_HARDLIGHT_LEGACY
+#define GIMP_SOFTLIGHT_MODE GIMP_LAYER_MODE_SOFTLIGHT_LEGACY
+#define GIMP_GRAIN_EXTRACT_MODE GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY
+#define GIMP_GRAIN_MERGE_MODE GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY
+#define GIMP_COLOR_ERASE_MODE GIMP_LAYER_MODE_COLOR_ERASE_LEGACY
+
+#define GIMP_NO_DITHER GIMP_CONVERT_DITHER_NONE
+#define GIMP_FS_DITHER GIMP_CONVERT_DITHER_FS
+#define GIMP_FSLOWBLEED_DITHER GIMP_CONVERT_DITHER_FS_LOWBLEED
+#define GIMP_FIXED_DITHER GIMP_CONVERT_DITHER_FIXED
+
+#endif /* ! GIMP_DISABLE_DEPRECATED */
+
+G_END_DECLS
+
+#endif /* __GIMP_TYPES_H__ */
diff --git a/libgimp/gimpui.c b/libgimp/gimpui.c
new file mode 100644
index 0000000..04c316a
--- /dev/null
+++ b/libgimp/gimpui.c
@@ -0,0 +1,505 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#ifdef GDK_DISABLE_DEPRECATED
+#undef GDK_DISABLE_DEPRECATED
+#endif
+#include <gtk/gtk.h>
+
+#ifdef GDK_WINDOWING_WIN32
+#include <gdk/gdkwin32.h>
+#endif
+
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#endif
+
+#ifdef GDK_WINDOWING_QUARTZ
+#include <Cocoa/Cocoa.h>
+#endif
+
+#include "gimp.h"
+#include "gimpui.h"
+
+#include "libgimpmodule/gimpmodule.h"
+
+#include "libgimpwidgets/gimpwidgets.h"
+#include "libgimpwidgets/gimpwidgets-private.h"
+
+
+/**
+ * SECTION: gimpui
+ * @title: gimpui
+ * @short_description: Common user interface functions. This header includes
+ * all other GIMP User Interface Library headers.
+ * @see_also: gtk_init(), gdk_set_use_xshm(), gdk_rgb_get_visual(),
+ * gdk_rgb_get_cmap(), gtk_widget_set_default_visual(),
+ * gtk_widget_set_default_colormap(), gtk_preview_set_gamma().
+ *
+ * Common user interface functions. This header includes all other
+ * GIMP User Interface Library headers.
+ **/
+
+
+/* local function prototypes */
+
+static void gimp_ui_help_func (const gchar *help_id,
+ gpointer help_data);
+static void gimp_ensure_modules (void);
+static void gimp_window_transient_realized (GtkWidget *window,
+ GdkWindow *parent);
+static gboolean gimp_window_set_transient_for (GtkWindow *window,
+ GdkWindow *parent);
+
+static void gimp_ui_theme_changed (void);
+static void gimp_ui_fix_pixbuf_style (void);
+static void gimp_ui_draw_pixbuf_layout (GtkStyle *style,
+ GdkWindow *window,
+ GtkStateType state_type,
+ gboolean use_text,
+ GdkRectangle *area,
+ GtkWidget *widget,
+ const gchar *detail,
+ gint x,
+ gint y,
+ PangoLayout *layout);
+#ifdef GDK_WINDOWING_QUARTZ
+static gboolean gimp_osx_focus_window (gpointer);
+#endif
+
+static gboolean gimp_ui_initialized = FALSE;
+
+
+/* public functions */
+
+/**
+ * gimp_ui_init:
+ * @prog_name: The name of the plug-in which will be passed as argv[0] to
+ * gtk_init(). It's a convention to use the name of the
+ * executable and _not_ the PDB procedure name.
+ * @preview: This parameter is unused and exists for historical
+ * reasons only.
+ *
+ * This function initializes GTK+ with gtk_init() and initializes GDK's
+ * image rendering subsystem (GdkRGB) to follow the GIMP main program's
+ * colormap allocation/installation policy.
+ *
+ * It also sets up various other things so that the plug-in user looks
+ * and behaves like the GIMP core. This includes selecting the GTK+
+ * theme and setting up the help system as chosen in the GIMP
+ * preferences. Any plug-in that provides a user interface should call
+ * this function.
+ **/
+void
+gimp_ui_init (const gchar *prog_name,
+ gboolean preview)
+{
+ GdkScreen *screen;
+ const gchar *display_name;
+ gchar *themerc;
+ GFileMonitor *rc_monitor;
+ GFile *file;
+
+ g_return_if_fail (prog_name != NULL);
+
+ if (gimp_ui_initialized)
+ return;
+
+ g_set_prgname (prog_name);
+
+ display_name = gimp_display_name ();
+
+ if (display_name)
+ {
+#if defined (GDK_WINDOWING_X11)
+ g_setenv ("DISPLAY", display_name, TRUE);
+#else
+ g_setenv ("GDK_DISPLAY", display_name, TRUE);
+#endif
+ }
+
+ if (gimp_user_time ())
+ {
+ /* Construct a fake startup ID as we only want to pass the
+ * interaction timestamp, see _gdk_windowing_set_default_display().
+ */
+ gchar *startup_id = g_strdup_printf ("_TIME%u", gimp_user_time ());
+
+ g_setenv ("DESKTOP_STARTUP_ID", startup_id, TRUE);
+ g_free (startup_id);
+ }
+
+ gtk_init (NULL, NULL);
+
+ themerc = gimp_personal_rc_file ("themerc");
+ gtk_rc_parse (themerc);
+
+ file = g_file_new_for_path (themerc);
+ g_free (themerc);
+
+ rc_monitor = g_file_monitor (file, G_FILE_MONITOR_NONE, NULL, NULL);
+ g_object_unref (file);
+
+ g_signal_connect (rc_monitor, "changed",
+ G_CALLBACK (gimp_ui_theme_changed),
+ NULL);
+
+ gdk_set_program_class (gimp_wm_class ());
+
+ screen = gdk_screen_get_default ();
+ gtk_widget_set_default_colormap (gdk_screen_get_rgb_colormap (screen));
+
+ if (gimp_icon_theme_dir ())
+ {
+ file = g_file_new_for_path (gimp_icon_theme_dir ());
+ gimp_icons_set_icon_theme (file);
+ g_object_unref (file);
+ }
+
+ gimp_widgets_init (gimp_ui_help_func,
+ gimp_context_get_foreground,
+ gimp_context_get_background,
+ gimp_ensure_modules);
+
+ if (! gimp_show_tool_tips ())
+ gimp_help_disable_tooltips ();
+
+ gimp_dialogs_show_help_button (gimp_show_help_button ());
+
+#ifdef GDK_WINDOWING_QUARTZ
+ g_idle_add (gimp_osx_focus_window, NULL);
+#endif
+
+ gimp_ui_fix_pixbuf_style ();
+ gimp_ui_initialized = TRUE;
+}
+
+static GdkWindow *
+gimp_ui_get_foreign_window (guint32 window)
+{
+#ifdef GDK_WINDOWING_X11
+ return gdk_x11_window_foreign_new_for_display (gdk_display_get_default (),
+ window);
+#endif
+
+#ifdef GDK_WINDOWING_WIN32
+ return gdk_win32_window_foreign_new_for_display (gdk_display_get_default (),
+ (HWND) (uintptr_t) window);
+#endif
+
+ return NULL;
+}
+
+/**
+ * gimp_ui_get_display_window:
+ * @gdisp_ID: a GimpDisplay ID.
+ *
+ * Returns the #GdkWindow of a display window. The purpose is to allow
+ * to make plug-in dialogs transient to the image display as explained
+ * with gdk_window_set_transient_for().
+ *
+ * You shouldn't have to call this function directly. Use
+ * gimp_window_set_transient_for_display() instead.
+ *
+ * Return value: A reference to a #GdkWindow or %NULL. You should
+ * unref the window using g_object_unref() as soon as
+ * you don't need it any longer.
+ *
+ * Since: 2.4
+ */
+GdkWindow *
+gimp_ui_get_display_window (guint32 gdisp_ID)
+{
+ guint32 window;
+
+ g_return_val_if_fail (gimp_ui_initialized, NULL);
+
+ window = gimp_display_get_window_handle (gdisp_ID);
+ if (window)
+ return gimp_ui_get_foreign_window (window);
+
+ return NULL;
+}
+
+/**
+ * gimp_ui_get_progress_window:
+ *
+ * Returns the #GdkWindow of the window this plug-in's progress bar is
+ * shown in. Use it to make plug-in dialogs transient to this window
+ * as explained with gdk_window_set_transient_for().
+ *
+ * You shouldn't have to call this function directly. Use
+ * gimp_window_set_transient() instead.
+ *
+ * Return value: A reference to a #GdkWindow or %NULL. You should
+ * unref the window using g_object_unref() as soon as
+ * you don't need it any longer.
+ *
+ * Since: 2.4
+ */
+GdkWindow *
+gimp_ui_get_progress_window (void)
+{
+ guint32 window;
+
+ g_return_val_if_fail (gimp_ui_initialized, NULL);
+
+ window = gimp_progress_get_window_handle ();
+ if (window)
+ return gimp_ui_get_foreign_window (window);
+
+ return NULL;
+}
+
+#ifdef GDK_WINDOWING_QUARTZ
+static void
+gimp_window_transient_show (GtkWidget *window)
+{
+ g_signal_handlers_disconnect_by_func (window,
+ gimp_window_transient_show,
+ NULL);
+ [NSApp arrangeInFront: nil];
+}
+#endif
+
+/**
+ * gimp_window_set_transient_for_display:
+ * @window: the #GtkWindow that should become transient
+ * @gdisp_ID: display ID of the image window that should become the parent
+ *
+ * Indicates to the window manager that @window is a transient dialog
+ * associated with the GIMP image window that is identified by it's
+ * display ID. See gdk_window_set_transient_for () for more information.
+ *
+ * Most of the time you will want to use the convenience function
+ * gimp_window_set_transient().
+ *
+ * Since: 2.4
+ */
+void
+gimp_window_set_transient_for_display (GtkWindow *window,
+ guint32 gdisp_ID)
+{
+ g_return_if_fail (gimp_ui_initialized);
+ g_return_if_fail (GTK_IS_WINDOW (window));
+
+ if (! gimp_window_set_transient_for (window,
+ gimp_ui_get_display_window (gdisp_ID)))
+ {
+ /* if setting the window transient failed, at least set
+ * WIN_POS_CENTER, which will center the window on the screen
+ * where the mouse is (see bug #684003).
+ */
+ gtk_window_set_position (window, GTK_WIN_POS_CENTER);
+
+#ifdef GDK_WINDOWING_QUARTZ
+ g_signal_connect (window, "show",
+ G_CALLBACK (gimp_window_transient_show),
+ NULL);
+#endif
+ }
+}
+
+/**
+ * gimp_window_set_transient:
+ * @window: the #GtkWindow that should become transient
+ *
+ * Indicates to the window manager that @window is a transient dialog
+ * associated with the GIMP window that the plug-in has been
+ * started from. See also gimp_window_set_transient_for_display().
+ *
+ * Since: 2.4
+ */
+void
+gimp_window_set_transient (GtkWindow *window)
+{
+ g_return_if_fail (gimp_ui_initialized);
+ g_return_if_fail (GTK_IS_WINDOW (window));
+
+ if (! gimp_window_set_transient_for (window, gimp_ui_get_progress_window ()))
+ {
+ /* see above */
+ gtk_window_set_position (window, GTK_WIN_POS_CENTER);
+
+#ifdef GDK_WINDOWING_QUARTZ
+ g_signal_connect (window, "show",
+ G_CALLBACK (gimp_window_transient_show),
+ NULL);
+#endif
+ }
+}
+
+
+/* private functions */
+
+static void
+gimp_ui_help_func (const gchar *help_id,
+ gpointer help_data)
+{
+ gimp_help (NULL, help_id);
+}
+
+static void
+gimp_ensure_modules (void)
+{
+ static GimpModuleDB *module_db = NULL;
+
+ if (! module_db)
+ {
+ gchar *load_inhibit = gimp_get_module_load_inhibit ();
+ gchar *module_path = gimp_gimprc_query ("module-path");
+
+ module_db = gimp_module_db_new (FALSE);
+
+ gimp_module_db_set_load_inhibit (module_db, load_inhibit);
+ gimp_module_db_load (module_db, module_path);
+
+ g_free (module_path);
+ g_free (load_inhibit);
+ }
+}
+
+static void
+gimp_window_transient_realized (GtkWidget *window,
+ GdkWindow *parent)
+{
+ if (gtk_widget_get_realized (window))
+ gdk_window_set_transient_for (gtk_widget_get_window (window), parent);
+}
+
+static gboolean
+gimp_window_set_transient_for (GtkWindow *window,
+ GdkWindow *parent)
+{
+ gtk_window_set_transient_for (window, NULL);
+
+#ifndef GDK_WINDOWING_WIN32
+ g_signal_handlers_disconnect_matched (window, G_SIGNAL_MATCH_FUNC,
+ 0, 0, NULL,
+ gimp_window_transient_realized,
+ NULL);
+
+ if (! parent)
+ return FALSE;
+
+ if (gtk_widget_get_realized (GTK_WIDGET (window)))
+ gdk_window_set_transient_for (gtk_widget_get_window (GTK_WIDGET (window)),
+ parent);
+
+ g_signal_connect_object (window, "realize",
+ G_CALLBACK (gimp_window_transient_realized),
+ parent, 0);
+ g_object_unref (parent);
+
+ return TRUE;
+#endif
+
+ return FALSE;
+}
+
+static void
+gimp_ui_theme_changed (void)
+{
+ gtk_rc_reparse_all ();
+
+ gimp_ui_fix_pixbuf_style ();
+}
+
+static void
+gimp_ui_fix_pixbuf_style (void)
+{
+ /* Same hack as in app/gui/themes.c, to be removed for GTK+ 3.x */
+
+ static GtkStyleClass *pixbuf_style_class = NULL;
+
+ if (! pixbuf_style_class)
+ {
+ GType type = g_type_from_name ("PixbufStyle");
+
+ if (type)
+ {
+ pixbuf_style_class = g_type_class_ref (type);
+
+ if (pixbuf_style_class)
+ pixbuf_style_class->draw_layout = gimp_ui_draw_pixbuf_layout;
+ }
+ }
+}
+
+static void
+gimp_ui_draw_pixbuf_layout (GtkStyle *style,
+ GdkWindow *window,
+ GtkStateType state_type,
+ gboolean use_text,
+ GdkRectangle *area,
+ GtkWidget *widget,
+ const gchar *detail,
+ gint x,
+ gint y,
+ PangoLayout *layout)
+{
+ GdkGC *gc;
+
+ gc = use_text ? style->text_gc[state_type] : style->fg_gc[state_type];
+
+ if (area)
+ gdk_gc_set_clip_rectangle (gc, area);
+
+ if (state_type == GTK_STATE_INSENSITIVE)
+ {
+ GdkGC *copy = gdk_gc_new (window);
+ GdkGCValues orig;
+ GdkColor fore;
+ guint16 r, g, b;
+
+ gdk_gc_copy (copy, gc);
+ gdk_gc_get_values (gc, &orig);
+
+ r = 0x40 + (((orig.foreground.pixel >> 16) & 0xff) >> 1);
+ g = 0x40 + (((orig.foreground.pixel >> 8) & 0xff) >> 1);
+ b = 0x40 + (((orig.foreground.pixel >> 0) & 0xff) >> 1);
+
+ fore.pixel = (r << 16) | (g << 8) | b;
+ fore.red = r * 257;
+ fore.green = g * 257;
+ fore.blue = b * 257;
+
+ gdk_gc_set_foreground (copy, &fore);
+ gdk_draw_layout (window, copy, x, y, layout);
+
+ g_object_unref (copy);
+ }
+ else
+ {
+ gdk_draw_layout (window, gc, x, y, layout);
+ }
+
+ if (area)
+ gdk_gc_set_clip_rectangle (gc, NULL);
+}
+
+#ifdef GDK_WINDOWING_QUARTZ
+static gboolean
+gimp_osx_focus_window (gpointer user_data)
+{
+ [NSApp activateIgnoringOtherApps:YES];
+ return FALSE;
+}
+#endif
diff --git a/libgimp/gimpui.def b/libgimp/gimpui.def
new file mode 100644
index 0000000..b0ba21a
--- /dev/null
+++ b/libgimp/gimpui.def
@@ -0,0 +1,90 @@
+EXPORTS
+ gimp_aspect_preview_get_type
+ gimp_aspect_preview_new
+ gimp_aspect_preview_new_from_drawable_id
+ gimp_brush_select_button_get_brush
+ gimp_brush_select_button_get_type
+ gimp_brush_select_button_new
+ gimp_brush_select_button_set_brush
+ gimp_brush_select_widget_close
+ gimp_brush_select_widget_new
+ gimp_brush_select_widget_set
+ gimp_channel_combo_box_get_type
+ gimp_channel_combo_box_new
+ gimp_channel_menu_new
+ gimp_drawable_combo_box_get_type
+ gimp_drawable_combo_box_new
+ gimp_drawable_menu_new
+ gimp_drawable_preview_draw_region
+ gimp_drawable_preview_get_drawable
+ gimp_drawable_preview_get_drawable_id
+ gimp_drawable_preview_get_type
+ gimp_drawable_preview_new
+ gimp_drawable_preview_new_from_drawable_id
+ gimp_export_dialog_get_content_area
+ gimp_export_dialog_new
+ gimp_export_image
+ gimp_font_select_button_get_font
+ gimp_font_select_button_get_type
+ gimp_font_select_button_new
+ gimp_font_select_button_set_font
+ gimp_font_select_widget_close
+ gimp_font_select_widget_new
+ gimp_font_select_widget_set
+ gimp_gradient_select_button_get_gradient
+ gimp_gradient_select_button_get_type
+ gimp_gradient_select_button_new
+ gimp_gradient_select_button_set_gradient
+ gimp_gradient_select_widget_close
+ gimp_gradient_select_widget_new
+ gimp_gradient_select_widget_set
+ gimp_image_combo_box_get_type
+ gimp_image_combo_box_new
+ gimp_image_menu_new
+ gimp_image_metadata_load_finish
+ gimp_image_metadata_load_prepare
+ gimp_image_metadata_load_thumbnail
+ gimp_image_metadata_save_finish
+ gimp_image_metadata_save_prepare
+ gimp_layer_combo_box_get_type
+ gimp_layer_combo_box_new
+ gimp_layer_menu_new
+ gimp_palette_select_button_get_palette
+ gimp_palette_select_button_get_type
+ gimp_palette_select_button_new
+ gimp_palette_select_button_set_palette
+ gimp_palette_select_widget_close
+ gimp_palette_select_widget_new
+ gimp_palette_select_widget_set
+ gimp_pattern_select_button_get_pattern
+ gimp_pattern_select_button_get_type
+ gimp_pattern_select_button_new
+ gimp_pattern_select_button_set_pattern
+ gimp_pattern_select_widget_close
+ gimp_pattern_select_widget_new
+ gimp_pattern_select_widget_set
+ gimp_proc_browser_dialog_get_selected
+ gimp_proc_browser_dialog_get_type
+ gimp_proc_browser_dialog_new
+ gimp_proc_view_new
+ gimp_progress_bar_get_type
+ gimp_progress_bar_new
+ gimp_select_button_close_popup
+ gimp_select_button_get_type
+ gimp_ui_get_display_window
+ gimp_ui_get_progress_window
+ gimp_ui_init
+ gimp_vectors_combo_box_get_type
+ gimp_vectors_combo_box_new
+ gimp_window_set_transient
+ gimp_window_set_transient_for_display
+ gimp_zoom_preview_get_drawable
+ gimp_zoom_preview_get_drawable_id
+ gimp_zoom_preview_get_factor
+ gimp_zoom_preview_get_model
+ gimp_zoom_preview_get_source
+ gimp_zoom_preview_get_type
+ gimp_zoom_preview_new
+ gimp_zoom_preview_new_from_drawable_id
+ gimp_zoom_preview_new_with_model
+ gimp_zoom_preview_new_with_model_from_drawable_id
diff --git a/libgimp/gimpui.h b/libgimp/gimpui.h
new file mode 100644
index 0000000..29b722a
--- /dev/null
+++ b/libgimp/gimpui.h
@@ -0,0 +1,72 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_UI_H__
+#define __GIMP_UI_H__
+
+#include <gtk/gtk.h>
+
+#include <libgimpwidgets/gimpwidgets.h>
+
+#define __GIMP_UI_H_INSIDE__
+
+#include <libgimp/gimpuitypes.h>
+
+#include <libgimp/gimpaspectpreview.h>
+#include <libgimp/gimpbrushmenu.h>
+#include <libgimp/gimpbrushselectbutton.h>
+#include <libgimp/gimpdrawablepreview.h>
+#include <libgimp/gimpexport.h>
+#include <libgimp/gimpfontmenu.h>
+#include <libgimp/gimpfontselectbutton.h>
+#include <libgimp/gimpgradientmenu.h>
+#include <libgimp/gimpgradientselectbutton.h>
+#include <libgimp/gimpimagecombobox.h>
+#include <libgimp/gimpimagemetadata.h>
+#include <libgimp/gimpitemcombobox.h>
+#include <libgimp/gimpmenu.h>
+#include <libgimp/gimppalettemenu.h>
+#include <libgimp/gimppaletteselectbutton.h>
+#include <libgimp/gimppatternmenu.h>
+#include <libgimp/gimppatternselectbutton.h>
+#include <libgimp/gimpprocbrowserdialog.h>
+#include <libgimp/gimpprocview.h>
+#include <libgimp/gimpprogressbar.h>
+#include <libgimp/gimpselectbutton.h>
+#include <libgimp/gimpzoompreview.h>
+
+#undef __GIMP_UI_H_INSIDE__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+void gimp_ui_init (const gchar *prog_name,
+ gboolean preview);
+
+GdkWindow * gimp_ui_get_display_window (guint32 gdisp_ID);
+GdkWindow * gimp_ui_get_progress_window (void);
+
+void gimp_window_set_transient_for_display (GtkWindow *window,
+ guint32 gdisp_ID);
+void gimp_window_set_transient (GtkWindow *window);
+
+G_END_DECLS
+
+#endif /* __GIMP_UI_H__ */
diff --git a/libgimp/gimpuimarshal.c b/libgimp/gimpuimarshal.c
new file mode 100644
index 0000000..30f4d38
--- /dev/null
+++ b/libgimp/gimpuimarshal.c
@@ -0,0 +1,261 @@
+/* This file is generated by glib-genmarshal, do not modify it. This code is licensed under the same license as the containing project. Note that it links to GLib, so must comply with the LGPL linking clauses. */
+#include <glib-object.h>
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v) g_value_get_schar (v)
+#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v) g_value_get_int (v)
+#define g_marshal_value_peek_uint(v) g_value_get_uint (v)
+#define g_marshal_value_peek_long(v) g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v) g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v) g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v) g_value_get_flags (v)
+#define g_marshal_value_peek_float(v) g_value_get_float (v)
+#define g_marshal_value_peek_double(v) g_value_get_double (v)
+#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v) g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v) g_value_get_object (v)
+#define g_marshal_value_peek_variant(v) g_value_get_variant (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ * Do not access GValues directly in your code. Instead, use the
+ * g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int
+#define g_marshal_value_peek_char(v) (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v) (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v) (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v) (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v) (v)->data[0].v_float
+#define g_marshal_value_peek_double(v) (v)->data[0].v_double
+#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_variant(v) (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+/* VOID: STRING, BOOLEAN (../../libgimp/gimpuimarshal.list:25) */
+/* Prototype for -Wmissing-prototypes */
+G_BEGIN_DECLS
+extern
+void _gimpui_marshal_VOID__STRING_BOOLEAN (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+G_END_DECLS
+void
+_gimpui_marshal_VOID__STRING_BOOLEAN (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__STRING_BOOLEAN) (gpointer data1,
+ gpointer arg1,
+ gboolean arg2,
+ gpointer data2);
+ GCClosure *cc = (GCClosure *) closure;
+ gpointer data1, data2;
+ GMarshalFunc_VOID__STRING_BOOLEAN callback;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__STRING_BOOLEAN) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_string (param_values + 1),
+ g_marshal_value_peek_boolean (param_values + 2),
+ data2);
+}
+
+/* VOID: STRING, INT, POINTER, BOOLEAN (../../libgimp/gimpuimarshal.list:26) */
+/* Prototype for -Wmissing-prototypes */
+G_BEGIN_DECLS
+extern
+void _gimpui_marshal_VOID__STRING_INT_POINTER_BOOLEAN (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+G_END_DECLS
+void
+_gimpui_marshal_VOID__STRING_INT_POINTER_BOOLEAN (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__STRING_INT_POINTER_BOOLEAN) (gpointer data1,
+ gpointer arg1,
+ gint arg2,
+ gpointer arg3,
+ gboolean arg4,
+ gpointer data2);
+ GCClosure *cc = (GCClosure *) closure;
+ gpointer data1, data2;
+ GMarshalFunc_VOID__STRING_INT_POINTER_BOOLEAN callback;
+
+ g_return_if_fail (n_param_values == 5);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__STRING_INT_POINTER_BOOLEAN) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_string (param_values + 1),
+ g_marshal_value_peek_int (param_values + 2),
+ g_marshal_value_peek_pointer (param_values + 3),
+ g_marshal_value_peek_boolean (param_values + 4),
+ data2);
+}
+
+/* VOID: STRING, INT, INT, INT, POINTER, BOOLEAN (../../libgimp/gimpuimarshal.list:27) */
+/* Prototype for -Wmissing-prototypes */
+G_BEGIN_DECLS
+extern
+void _gimpui_marshal_VOID__STRING_INT_INT_INT_POINTER_BOOLEAN (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+G_END_DECLS
+void
+_gimpui_marshal_VOID__STRING_INT_INT_INT_POINTER_BOOLEAN (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__STRING_INT_INT_INT_POINTER_BOOLEAN) (gpointer data1,
+ gpointer arg1,
+ gint arg2,
+ gint arg3,
+ gint arg4,
+ gpointer arg5,
+ gboolean arg6,
+ gpointer data2);
+ GCClosure *cc = (GCClosure *) closure;
+ gpointer data1, data2;
+ GMarshalFunc_VOID__STRING_INT_INT_INT_POINTER_BOOLEAN callback;
+
+ g_return_if_fail (n_param_values == 7);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__STRING_INT_INT_INT_POINTER_BOOLEAN) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_string (param_values + 1),
+ g_marshal_value_peek_int (param_values + 2),
+ g_marshal_value_peek_int (param_values + 3),
+ g_marshal_value_peek_int (param_values + 4),
+ g_marshal_value_peek_pointer (param_values + 5),
+ g_marshal_value_peek_boolean (param_values + 6),
+ data2);
+}
+
+/* VOID: STRING, DOUBLE, INT, INT, INT, INT, POINTER, BOOLEAN (../../libgimp/gimpuimarshal.list:28) */
+/* Prototype for -Wmissing-prototypes */
+G_BEGIN_DECLS
+extern
+void _gimpui_marshal_VOID__STRING_DOUBLE_INT_INT_INT_INT_POINTER_BOOLEAN (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+G_END_DECLS
+void
+_gimpui_marshal_VOID__STRING_DOUBLE_INT_INT_INT_INT_POINTER_BOOLEAN (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__STRING_DOUBLE_INT_INT_INT_INT_POINTER_BOOLEAN) (gpointer data1,
+ gpointer arg1,
+ gdouble arg2,
+ gint arg3,
+ gint arg4,
+ gint arg5,
+ gint arg6,
+ gpointer arg7,
+ gboolean arg8,
+ gpointer data2);
+ GCClosure *cc = (GCClosure *) closure;
+ gpointer data1, data2;
+ GMarshalFunc_VOID__STRING_DOUBLE_INT_INT_INT_INT_POINTER_BOOLEAN callback;
+
+ g_return_if_fail (n_param_values == 9);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__STRING_DOUBLE_INT_INT_INT_INT_POINTER_BOOLEAN) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_string (param_values + 1),
+ g_marshal_value_peek_double (param_values + 2),
+ g_marshal_value_peek_int (param_values + 3),
+ g_marshal_value_peek_int (param_values + 4),
+ g_marshal_value_peek_int (param_values + 5),
+ g_marshal_value_peek_int (param_values + 6),
+ g_marshal_value_peek_pointer (param_values + 7),
+ g_marshal_value_peek_boolean (param_values + 8),
+ data2);
+}
+
diff --git a/libgimp/gimpuimarshal.h b/libgimp/gimpuimarshal.h
new file mode 100644
index 0000000..3575a79
--- /dev/null
+++ b/libgimp/gimpuimarshal.h
@@ -0,0 +1,48 @@
+/* This file is generated by glib-genmarshal, do not modify it. This code is licensed under the same license as the containing project. Note that it links to GLib, so must comply with the LGPL linking clauses. */
+#ifndef ___GIMPUI_MARSHAL_MARSHAL_H__
+#define ___GIMPUI_MARSHAL_MARSHAL_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/* VOID: STRING, BOOLEAN (../../libgimp/gimpuimarshal.list:25) */
+extern
+void _gimpui_marshal_VOID__STRING_BOOLEAN (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* VOID: STRING, INT, POINTER, BOOLEAN (../../libgimp/gimpuimarshal.list:26) */
+extern
+void _gimpui_marshal_VOID__STRING_INT_POINTER_BOOLEAN (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* VOID: STRING, INT, INT, INT, POINTER, BOOLEAN (../../libgimp/gimpuimarshal.list:27) */
+extern
+void _gimpui_marshal_VOID__STRING_INT_INT_INT_POINTER_BOOLEAN (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* VOID: STRING, DOUBLE, INT, INT, INT, INT, POINTER, BOOLEAN (../../libgimp/gimpuimarshal.list:28) */
+extern
+void _gimpui_marshal_VOID__STRING_DOUBLE_INT_INT_INT_INT_POINTER_BOOLEAN (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+
+G_END_DECLS
+
+#endif /* ___GIMPUI_MARSHAL_MARSHAL_H__ */
diff --git a/libgimp/gimpuimarshal.list b/libgimp/gimpuimarshal.list
new file mode 100644
index 0000000..59ec674
--- /dev/null
+++ b/libgimp/gimpuimarshal.list
@@ -0,0 +1,28 @@
+# see glib-genmarshal(1) for a detailed description of the file format,
+# possible parameter types are:
+# VOID indicates no return type, or no extra
+# parameters. if VOID is used as the parameter
+# list, no additional parameters may be present.
+# BOOLEAN for boolean types (gboolean)
+# CHAR for signed char types (gchar)
+# UCHAR for unsigned char types (guchar)
+# INT for signed integer types (gint)
+# UINT for unsigned integer types (guint)
+# LONG for signed long integer types (glong)
+# ULONG for unsigned long integer types (gulong)
+# ENUM for enumeration types (gint)
+# FLAGS for flag enumeration types (guint)
+# FLOAT for single-precision float types (gfloat)
+# DOUBLE for double-precision float types (gdouble)
+# STRING for string types (gchar*)
+# BOXED for boxed (anonymous but reference counted) types (GBoxed*)
+# POINTER for anonymous pointer types (gpointer)
+# PARAM for GParamSpec or derived types (GParamSpec*)
+# OBJECT for GObject or derived types (GObject*)
+# NONE deprecated alias for VOID
+# BOOL deprecated alias for BOOLEAN
+
+VOID: STRING, BOOLEAN
+VOID: STRING, INT, POINTER, BOOLEAN
+VOID: STRING, INT, INT, INT, POINTER, BOOLEAN
+VOID: STRING, DOUBLE, INT, INT, INT, INT, POINTER, BOOLEAN
diff --git a/libgimp/gimpuitypes.h b/libgimp/gimpuitypes.h
new file mode 100644
index 0000000..06178d1
--- /dev/null
+++ b/libgimp/gimpuitypes.h
@@ -0,0 +1,53 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpuitypes.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_UI_TYPES_H__
+#define __GIMP_UI_TYPES_H__
+
+#include <libgimpwidgets/gimpwidgetstypes.h>
+
+G_BEGIN_DECLS
+
+/* For information look into the html documentation */
+
+
+typedef struct _GimpAspectPreview GimpAspectPreview;
+typedef struct _GimpDrawablePreview GimpDrawablePreview;
+typedef struct _GimpProcBrowserDialog GimpProcBrowserDialog;
+typedef struct _GimpProgressBar GimpProgressBar;
+typedef struct _GimpZoomPreview GimpZoomPreview;
+
+typedef struct _GimpDrawableComboBox GimpDrawableComboBox;
+typedef struct _GimpChannelComboBox GimpChannelComboBox;
+typedef struct _GimpLayerComboBox GimpLayerComboBox;
+typedef struct _GimpVectorsComboBox GimpVectorsComboBox;
+typedef struct _GimpImageComboBox GimpImageComboBox;
+
+typedef struct _GimpSelectButton GimpSelectButton;
+typedef struct _GimpBrushSelectButton GimpBrushSelectButton;
+typedef struct _GimpFontSelectButton GimpFontSelectButton;
+typedef struct _GimpGradientSelectButton GimpGradientSelectButton;
+typedef struct _GimpPaletteSelectButton GimpPaletteSelectButton;
+typedef struct _GimpPatternSelectButton GimpPatternSelectButton;
+
+
+G_END_DECLS
+
+#endif /* __GIMP_UI_TYPES_H__ */
diff --git a/libgimp/gimpunit_pdb.c b/libgimp/gimpunit_pdb.c
new file mode 100644
index 0000000..fff9e12
--- /dev/null
+++ b/libgimp/gimpunit_pdb.c
@@ -0,0 +1,427 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpunit_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpunit
+ * @title: gimpunit
+ * @short_description: Provides a collection of predefined units and functions for creating user-defined units.
+ *
+ * Provides a collection of predefined units and functions for creating
+ * user-defined units.
+ **/
+
+
+/**
+ * _gimp_unit_get_number_of_units:
+ *
+ * Returns the number of units.
+ *
+ * This procedure returns the number of defined units.
+ *
+ * Returns: The number of units.
+ **/
+gint
+_gimp_unit_get_number_of_units (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint num_units = GIMP_UNIT_END;
+
+ return_vals = gimp_run_procedure ("gimp-unit-get-number-of-units",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ num_units = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return num_units;
+}
+
+/**
+ * _gimp_unit_get_number_of_built_in_units:
+ *
+ * Returns the number of built-in units.
+ *
+ * This procedure returns the number of defined units built-in to GIMP.
+ *
+ * Returns: The number of built-in units.
+ **/
+gint
+_gimp_unit_get_number_of_built_in_units (void)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint num_units = GIMP_UNIT_END;
+
+ return_vals = gimp_run_procedure ("gimp-unit-get-number-of-built-in-units",
+ &nreturn_vals,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ num_units = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return num_units;
+}
+
+/**
+ * _gimp_unit_new:
+ * @identifier: The new unit's identifier.
+ * @factor: The new unit's factor.
+ * @digits: The new unit's digits.
+ * @symbol: The new unit's symbol.
+ * @abbreviation: The new unit's abbreviation.
+ * @singular: The new unit's singular form.
+ * @plural: The new unit's plural form.
+ *
+ * Creates a new unit and returns it's integer ID.
+ *
+ * This procedure creates a new unit and returns it's integer ID. Note
+ * that the new unit will have it's deletion flag set to TRUE, so you
+ * will have to set it to FALSE with gimp_unit_set_deletion_flag() to
+ * make it persistent.
+ *
+ * Returns: The new unit's ID.
+ **/
+GimpUnit
+_gimp_unit_new (const gchar *identifier,
+ gdouble factor,
+ gint digits,
+ const gchar *symbol,
+ const gchar *abbreviation,
+ const gchar *singular,
+ const gchar *plural)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpUnit unit_id = GIMP_UNIT_INCH;
+
+ return_vals = gimp_run_procedure ("gimp-unit-new",
+ &nreturn_vals,
+ GIMP_PDB_STRING, identifier,
+ GIMP_PDB_FLOAT, factor,
+ GIMP_PDB_INT32, digits,
+ GIMP_PDB_STRING, symbol,
+ GIMP_PDB_STRING, abbreviation,
+ GIMP_PDB_STRING, singular,
+ GIMP_PDB_STRING, plural,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ unit_id = return_vals[1].data.d_unit;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return unit_id;
+}
+
+/**
+ * _gimp_unit_get_deletion_flag:
+ * @unit_id: The unit's integer ID.
+ *
+ * Returns the deletion flag of the unit.
+ *
+ * This procedure returns the deletion flag of the unit. If this value
+ * is TRUE the unit's definition will not be saved in the user's unitrc
+ * file on gimp exit.
+ *
+ * Returns: The unit's deletion flag.
+ **/
+gboolean
+_gimp_unit_get_deletion_flag (GimpUnit unit_id)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean deletion_flag = FALSE;
+
+ return_vals = gimp_run_procedure ("gimp-unit-get-deletion-flag",
+ &nreturn_vals,
+ GIMP_PDB_INT32, unit_id,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ deletion_flag = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return deletion_flag;
+}
+
+/**
+ * _gimp_unit_set_deletion_flag:
+ * @unit_id: The unit's integer ID.
+ * @deletion_flag: The new deletion flag of the unit.
+ *
+ * Sets the deletion flag of a unit.
+ *
+ * This procedure sets the unit's deletion flag. If the deletion flag
+ * of a unit is TRUE on gimp exit, this unit's definition will not be
+ * saved in the user's unitrc.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+_gimp_unit_set_deletion_flag (GimpUnit unit_id,
+ gboolean deletion_flag)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-unit-set-deletion-flag",
+ &nreturn_vals,
+ GIMP_PDB_INT32, unit_id,
+ GIMP_PDB_INT32, deletion_flag,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * _gimp_unit_get_identifier:
+ * @unit_id: The unit's integer ID.
+ *
+ * Returns the textual identifier of the unit.
+ *
+ * This procedure returns the textual identifier of the unit. For
+ * built-in units it will be the english singular form of the unit's
+ * name. For user-defined units this should equal to the singular form.
+ *
+ * Returns: The unit's textual identifier.
+ **/
+gchar *
+_gimp_unit_get_identifier (GimpUnit unit_id)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *identifier = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-unit-get-identifier",
+ &nreturn_vals,
+ GIMP_PDB_INT32, unit_id,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ identifier = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return identifier;
+}
+
+/**
+ * _gimp_unit_get_factor:
+ * @unit_id: The unit's integer ID.
+ *
+ * Returns the factor of the unit.
+ *
+ * This procedure returns the unit's factor which indicates how many
+ * units make up an inch. Note that asking for the factor of \"pixels\"
+ * will produce an error.
+ *
+ * Returns: The unit's factor.
+ **/
+gdouble
+_gimp_unit_get_factor (GimpUnit unit_id)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble factor = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-unit-get-factor",
+ &nreturn_vals,
+ GIMP_PDB_INT32, unit_id,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ factor = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return factor;
+}
+
+/**
+ * _gimp_unit_get_digits:
+ * @unit_id: The unit's integer ID.
+ *
+ * Returns the number of digits of the unit.
+ *
+ * This procedure returns the number of digits you should provide in
+ * input or output functions to get approximately the same accuracy as
+ * with two digits and inches. Note that asking for the digits of
+ * \"pixels\" will produce an error.
+ *
+ * Returns: The unit's number of digits.
+ **/
+gint
+_gimp_unit_get_digits (GimpUnit unit_id)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint digits = 0;
+
+ return_vals = gimp_run_procedure ("gimp-unit-get-digits",
+ &nreturn_vals,
+ GIMP_PDB_INT32, unit_id,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ digits = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return digits;
+}
+
+/**
+ * _gimp_unit_get_symbol:
+ * @unit_id: The unit's integer ID.
+ *
+ * Returns the symbol of the unit.
+ *
+ * This procedure returns the symbol of the unit (\"''\" for inches).
+ *
+ * Returns: The unit's symbol.
+ **/
+gchar *
+_gimp_unit_get_symbol (GimpUnit unit_id)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *symbol = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-unit-get-symbol",
+ &nreturn_vals,
+ GIMP_PDB_INT32, unit_id,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ symbol = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return symbol;
+}
+
+/**
+ * _gimp_unit_get_abbreviation:
+ * @unit_id: The unit's integer ID.
+ *
+ * Returns the abbreviation of the unit.
+ *
+ * This procedure returns the abbreviation of the unit (\"in\" for
+ * inches).
+ *
+ * Returns: The unit's abbreviation.
+ **/
+gchar *
+_gimp_unit_get_abbreviation (GimpUnit unit_id)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *abbreviation = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-unit-get-abbreviation",
+ &nreturn_vals,
+ GIMP_PDB_INT32, unit_id,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ abbreviation = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return abbreviation;
+}
+
+/**
+ * _gimp_unit_get_singular:
+ * @unit_id: The unit's integer ID.
+ *
+ * Returns the singular form of the unit.
+ *
+ * This procedure returns the singular form of the unit.
+ *
+ * Returns: The unit's singular form.
+ **/
+gchar *
+_gimp_unit_get_singular (GimpUnit unit_id)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *singular = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-unit-get-singular",
+ &nreturn_vals,
+ GIMP_PDB_INT32, unit_id,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ singular = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return singular;
+}
+
+/**
+ * _gimp_unit_get_plural:
+ * @unit_id: The unit's integer ID.
+ *
+ * Returns the plural form of the unit.
+ *
+ * This procedure returns the plural form of the unit.
+ *
+ * Returns: The unit's plural form.
+ **/
+gchar *
+_gimp_unit_get_plural (GimpUnit unit_id)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *plural = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-unit-get-plural",
+ &nreturn_vals,
+ GIMP_PDB_INT32, unit_id,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ plural = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return plural;
+}
diff --git a/libgimp/gimpunit_pdb.h b/libgimp/gimpunit_pdb.h
new file mode 100644
index 0000000..3481649
--- /dev/null
+++ b/libgimp/gimpunit_pdb.h
@@ -0,0 +1,58 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpunit_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_UNIT_PDB_H__
+#define __GIMP_UNIT_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+G_GNUC_INTERNAL gint _gimp_unit_get_number_of_units (void);
+G_GNUC_INTERNAL gint _gimp_unit_get_number_of_built_in_units (void);
+G_GNUC_INTERNAL GimpUnit _gimp_unit_new (const gchar *identifier,
+ gdouble factor,
+ gint digits,
+ const gchar *symbol,
+ const gchar *abbreviation,
+ const gchar *singular,
+ const gchar *plural);
+G_GNUC_INTERNAL gboolean _gimp_unit_get_deletion_flag (GimpUnit unit_id);
+G_GNUC_INTERNAL gboolean _gimp_unit_set_deletion_flag (GimpUnit unit_id,
+ gboolean deletion_flag);
+G_GNUC_INTERNAL gchar* _gimp_unit_get_identifier (GimpUnit unit_id);
+G_GNUC_INTERNAL gdouble _gimp_unit_get_factor (GimpUnit unit_id);
+G_GNUC_INTERNAL gint _gimp_unit_get_digits (GimpUnit unit_id);
+G_GNUC_INTERNAL gchar* _gimp_unit_get_symbol (GimpUnit unit_id);
+G_GNUC_INTERNAL gchar* _gimp_unit_get_abbreviation (GimpUnit unit_id);
+G_GNUC_INTERNAL gchar* _gimp_unit_get_singular (GimpUnit unit_id);
+G_GNUC_INTERNAL gchar* _gimp_unit_get_plural (GimpUnit unit_id);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_UNIT_PDB_H__ */
diff --git a/libgimp/gimpunitcache.c b/libgimp/gimpunitcache.c
new file mode 100644
index 0000000..95bca08
--- /dev/null
+++ b/libgimp/gimpunitcache.c
@@ -0,0 +1,236 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpunitcache.c
+ * Copyright (C) 1999-2000 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "gimpunitcache.h"
+#include "gimpunit_pdb.h"
+
+#include "libgimp-intl.h"
+
+/* internal structures */
+
+typedef struct
+{
+ gdouble factor;
+ gint digits;
+ const gchar *identifier;
+ const gchar *symbol;
+ const gchar *abbreviation;
+ const gchar *singular;
+ const gchar *plural;
+} GimpUnitDef;
+
+
+static GimpUnitDef * gimp_unit_defs = NULL;
+static GimpUnit gimp_units_initialized = 0;
+
+/* not a unit at all but kept here to have the strings in one place
+ */
+static const GimpUnitDef gimp_unit_percent =
+{
+ 0.0, 0, "percent", "%", "%", N_("percent"), N_("percent")
+};
+
+
+static void gimp_unit_def_init (GimpUnitDef *unit_def,
+ GimpUnit unit);
+
+
+static gboolean
+gimp_unit_init (GimpUnit unit)
+{
+ gint i, n;
+
+ if (unit < gimp_units_initialized)
+ return TRUE;
+
+ n = _gimp_unit_get_number_of_units ();
+
+ if (unit >= n)
+ return FALSE;
+
+ gimp_unit_defs = g_renew (GimpUnitDef, gimp_unit_defs, n);
+
+ for (i = gimp_units_initialized; i < n; i++)
+ {
+ gimp_unit_def_init (&gimp_unit_defs[i], i);
+ }
+
+ gimp_units_initialized = n;
+
+ return TRUE;
+}
+
+static void
+gimp_unit_def_init (GimpUnitDef *unit_def,
+ GimpUnit unit)
+{
+ unit_def->factor = _gimp_unit_get_factor (unit);
+ unit_def->digits = _gimp_unit_get_digits (unit);
+ unit_def->identifier = _gimp_unit_get_identifier (unit);
+ unit_def->symbol = _gimp_unit_get_symbol (unit);
+ unit_def->abbreviation = _gimp_unit_get_abbreviation (unit);
+ unit_def->singular = _gimp_unit_get_singular (unit);
+ unit_def->plural = _gimp_unit_get_plural (unit);
+}
+
+gint
+_gimp_unit_cache_get_number_of_units (void)
+{
+ return _gimp_unit_get_number_of_units ();
+}
+
+gint
+_gimp_unit_cache_get_number_of_built_in_units (void)
+{
+ return GIMP_UNIT_END;
+}
+
+GimpUnit
+_gimp_unit_cache_new (gchar *identifier,
+ gdouble factor,
+ gint digits,
+ gchar *symbol,
+ gchar *abbreviation,
+ gchar *singular,
+ gchar *plural)
+{
+ return _gimp_unit_new (identifier,
+ factor,
+ digits,
+ symbol,
+ abbreviation,
+ singular,
+ plural);
+}
+
+gboolean
+_gimp_unit_cache_get_deletion_flag (GimpUnit unit)
+{
+ if (unit < GIMP_UNIT_END)
+ return FALSE;
+
+ return _gimp_unit_get_deletion_flag (unit);
+}
+
+void
+_gimp_unit_cache_set_deletion_flag (GimpUnit unit,
+ gboolean deletion_flag)
+{
+ if (unit < GIMP_UNIT_END)
+ return;
+
+ _gimp_unit_set_deletion_flag (unit,
+ deletion_flag);
+}
+
+gdouble
+_gimp_unit_cache_get_factor (GimpUnit unit)
+{
+ g_return_val_if_fail (unit >= GIMP_UNIT_INCH, 1.0);
+
+ if (unit == GIMP_UNIT_PERCENT)
+ return gimp_unit_percent.factor;
+
+ if (!gimp_unit_init (unit))
+ return 1.0;
+
+ return gimp_unit_defs[unit].factor;
+}
+
+gint
+_gimp_unit_cache_get_digits (GimpUnit unit)
+{
+ g_return_val_if_fail (unit >= GIMP_UNIT_INCH, 0);
+
+ if (unit == GIMP_UNIT_PERCENT)
+ return gimp_unit_percent.digits;
+
+ if (!gimp_unit_init (unit))
+ return 0;
+
+ return gimp_unit_defs[unit].digits;
+}
+
+const gchar *
+_gimp_unit_cache_get_identifier (GimpUnit unit)
+{
+ if (unit == GIMP_UNIT_PERCENT)
+ return gimp_unit_percent.identifier;
+
+ if (!gimp_unit_init (unit))
+ return NULL;
+
+ return gimp_unit_defs[unit].identifier;
+}
+
+const gchar *
+_gimp_unit_cache_get_symbol (GimpUnit unit)
+{
+ if (unit == GIMP_UNIT_PERCENT)
+ return gimp_unit_percent.symbol;
+
+ if (!gimp_unit_init (unit))
+ return NULL;
+
+ return gimp_unit_defs[unit].symbol;
+}
+
+const gchar *
+_gimp_unit_cache_get_abbreviation (GimpUnit unit)
+{
+ if (unit == GIMP_UNIT_PERCENT)
+ return gimp_unit_percent.abbreviation;
+
+ if (!gimp_unit_init (unit))
+ return NULL;
+
+ return gimp_unit_defs[unit].abbreviation;
+}
+
+const gchar *
+_gimp_unit_cache_get_singular (GimpUnit unit)
+{
+ if (unit == GIMP_UNIT_PERCENT)
+ return gettext (gimp_unit_percent.singular);
+
+ if (!gimp_unit_init (unit))
+ return NULL;
+
+ return gettext (gimp_unit_defs[unit].singular);
+}
+
+const gchar *
+_gimp_unit_cache_get_plural (GimpUnit unit)
+{
+ if (unit == GIMP_UNIT_PERCENT)
+ return gettext (gimp_unit_percent.plural);
+
+ if (!gimp_unit_init (unit))
+ return NULL;
+
+ return gettext (gimp_unit_defs[unit].plural);
+}
diff --git a/libgimp/gimpunitcache.h b/libgimp/gimpunitcache.h
new file mode 100644
index 0000000..245127f
--- /dev/null
+++ b/libgimp/gimpunitcache.h
@@ -0,0 +1,52 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpunitcache.c
+ * Copyright (C) 2003 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_UNIT_CACHE_H__
+#define __GIMP_UNIT_CACHE_H__
+
+G_BEGIN_DECLS
+
+
+G_GNUC_INTERNAL gint _gimp_unit_cache_get_number_of_units (void);
+G_GNUC_INTERNAL gint _gimp_unit_cache_get_number_of_built_in_units (void) G_GNUC_CONST;
+
+G_GNUC_INTERNAL GimpUnit _gimp_unit_cache_new (gchar *identifier,
+ gdouble factor,
+ gint digits,
+ gchar *symbol,
+ gchar *abbreviation,
+ gchar *singular,
+ gchar *plural);
+G_GNUC_INTERNAL gboolean _gimp_unit_cache_get_deletion_flag (GimpUnit unit);
+G_GNUC_INTERNAL void _gimp_unit_cache_set_deletion_flag (GimpUnit unit,
+ gboolean deletion_flag);
+G_GNUC_INTERNAL gdouble _gimp_unit_cache_get_factor (GimpUnit unit);
+G_GNUC_INTERNAL gint _gimp_unit_cache_get_digits (GimpUnit unit);
+G_GNUC_INTERNAL const gchar * _gimp_unit_cache_get_identifier (GimpUnit unit);
+G_GNUC_INTERNAL const gchar * _gimp_unit_cache_get_symbol (GimpUnit unit);
+G_GNUC_INTERNAL const gchar * _gimp_unit_cache_get_abbreviation (GimpUnit unit);
+G_GNUC_INTERNAL const gchar * _gimp_unit_cache_get_singular (GimpUnit unit);
+G_GNUC_INTERNAL const gchar * _gimp_unit_cache_get_plural (GimpUnit unit);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_UNIT_CACHE_H__ */
diff --git a/libgimp/gimpvectors.c b/libgimp/gimpvectors.c
new file mode 100644
index 0000000..05105a0
--- /dev/null
+++ b/libgimp/gimpvectors.c
@@ -0,0 +1,271 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpvectors.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gimp.h"
+#include "gimpvectors.h"
+
+
+/**
+ * gimp_vectors_is_valid:
+ * @vectors_ID: The vectors object to check.
+ *
+ * Deprecated: Use gimp_item_is_valid() instead.
+ *
+ * Returns: Whether the vectors ID is valid.
+ *
+ * Since: 2.4
+ */
+gboolean
+gimp_vectors_is_valid (gint32 vectors_ID)
+{
+ return gimp_item_is_valid (vectors_ID);
+}
+
+/**
+ * gimp_vectors_get_image:
+ * @vectors_ID: The vectors object.
+ *
+ * Deprecated: Use gimp_item_get_image() instead.
+ *
+ * Returns: The vectors image.
+ *
+ * Since: 2.4
+ */
+gint32
+gimp_vectors_get_image (gint32 vectors_ID)
+{
+ return gimp_item_get_image (vectors_ID);
+}
+
+/**
+ * gimp_vectors_get_name:
+ * @vectors_ID: The vectors object.
+ *
+ * Deprecated: Use gimp_item_get_name() instead.
+ *
+ * Returns: The name of the vectors object.
+ *
+ * Since: 2.4
+ */
+gchar *
+gimp_vectors_get_name (gint32 vectors_ID)
+{
+ return gimp_item_get_name (vectors_ID);
+}
+
+/**
+ * gimp_vectors_set_name:
+ * @vectors_ID: The vectors object.
+ * @name: the new name of the path.
+ *
+ * Deprecated: Use gimp_item_set_name() instead.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ */
+gboolean
+gimp_vectors_set_name (gint32 vectors_ID,
+ const gchar *name)
+{
+ return gimp_item_set_name (vectors_ID, name);
+}
+
+/**
+ * gimp_vectors_get_visible:
+ * @vectors_ID: The vectors object.
+ *
+ * Deprecated: Use gimp_item_get_visible() instead.
+ *
+ * Returns: TRUE if the path is visible, FALSE otherwise.
+ *
+ * Since: 2.4
+ */
+gboolean
+gimp_vectors_get_visible (gint32 vectors_ID)
+{
+ return gimp_item_get_visible (vectors_ID);
+}
+
+/**
+ * gimp_vectors_set_visible:
+ * @vectors_ID: The vectors object.
+ * @visible: Whether the path is visible.
+ *
+ * Deprecated: Use gimp_item_set_visible() instead.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ */
+gboolean
+gimp_vectors_set_visible (gint32 vectors_ID,
+ gboolean visible)
+{
+ return gimp_item_set_visible (vectors_ID, visible);
+}
+
+/**
+ * gimp_vectors_get_linked:
+ * @vectors_ID: The vectors object.
+ *
+ * Deprecated: Use gimp_item_get_linked() instead.
+ *
+ * Returns: TRUE if the path is linked, FALSE otherwise.
+ *
+ * Since: 2.4
+ */
+gboolean
+gimp_vectors_get_linked (gint32 vectors_ID)
+{
+ return gimp_item_get_linked (vectors_ID);
+}
+
+/**
+ * gimp_vectors_set_linked:
+ * @vectors_ID: The vectors object.
+ * @linked: Whether the path is linked.
+ *
+ * Deprecated: Use gimp_item_set_linked() instead.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ */
+gboolean
+gimp_vectors_set_linked (gint32 vectors_ID,
+ gboolean linked)
+{
+ return gimp_item_set_linked (vectors_ID, linked);
+}
+
+/**
+ * gimp_vectors_get_tattoo:
+ * @vectors_ID: The vectors object.
+ *
+ * Deprecated: Use gimp_item_get_tattoo() instead.
+ *
+ * Returns: The vectors tattoo.
+ *
+ * Since: 2.4
+ */
+gint
+gimp_vectors_get_tattoo (gint32 vectors_ID)
+{
+ return gimp_item_get_tattoo (vectors_ID);
+}
+
+/**
+ * gimp_vectors_set_tattoo:
+ * @vectors_ID: The vectors object.
+ * @tattoo: the new tattoo.
+ *
+ * Deprecated: Use gimp_item_set_tattoo() instead.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ */
+gboolean
+gimp_vectors_set_tattoo (gint32 vectors_ID,
+ gint tattoo)
+{
+ return gimp_item_set_tattoo (vectors_ID, tattoo);
+}
+
+/**
+ * gimp_vectors_parasite_find:
+ * @vectors_ID: The vectors object.
+ * @name: The name of the parasite to find.
+ *
+ * Deprecated: Use gimp_item_get_parasite() instead.
+ *
+ * Returns: The found parasite.
+ *
+ * Since: 2.4
+ **/
+GimpParasite *
+gimp_vectors_parasite_find (gint32 vectors_ID,
+ const gchar *name)
+{
+ return gimp_item_get_parasite (vectors_ID, name);
+}
+
+/**
+ * gimp_vectors_parasite_attach:
+ * @vectors_ID: The vectors object.
+ * @parasite: The parasite to attach to a vectors object.
+ *
+ * Deprecated: Use gimp_item_attach_parasite() instead.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_vectors_parasite_attach (gint32 vectors_ID,
+ const GimpParasite *parasite)
+{
+ return gimp_item_attach_parasite (vectors_ID, parasite);
+}
+
+/**
+ * gimp_vectors_parasite_detach:
+ * @vectors_ID: The vectors object.
+ * @name: The name of the parasite to detach from a vectors object.
+ *
+ * Deprecated: Use gimp_item_detach_parasite() instead.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_vectors_parasite_detach (gint32 vectors_ID,
+ const gchar *name)
+{
+ return gimp_item_detach_parasite (vectors_ID, name);
+}
+
+/**
+ * gimp_vectors_parasite_list:
+ * @vectors_ID: The vectors object.
+ * @num_parasites: The number of attached parasites.
+ * @parasites: The names of currently attached parasites.
+ *
+ * Deprecated: Use gimp_item_get_parasite_list() instead.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_vectors_parasite_list (gint32 vectors_ID,
+ gint *num_parasites,
+ gchar ***parasites)
+{
+ *parasites = gimp_item_get_parasite_list (vectors_ID, num_parasites);
+
+ return *parasites != NULL;
+}
diff --git a/libgimp/gimpvectors.h b/libgimp/gimpvectors.h
new file mode 100644
index 0000000..0608cbc
--- /dev/null
+++ b/libgimp/gimpvectors.h
@@ -0,0 +1,73 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpvectors.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_VECTORS_H__
+#define __GIMP_VECTORS_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+GIMP_DEPRECATED_FOR(gimp_item_is_valid)
+gboolean gimp_vectors_is_valid (gint32 vectors_ID);
+GIMP_DEPRECATED_FOR(gimp_item_get_image)
+gint32 gimp_vectors_get_image (gint32 vectors_ID);
+GIMP_DEPRECATED_FOR(gimp_item_get_name)
+gchar * gimp_vectors_get_name (gint32 vectors_ID);
+GIMP_DEPRECATED_FOR(gimp_item_set_name)
+gboolean gimp_vectors_set_name (gint32 vectors_ID,
+ const gchar *name);
+GIMP_DEPRECATED_FOR(gimp_item_get_visible)
+gboolean gimp_vectors_get_visible (gint32 vectors_ID);
+GIMP_DEPRECATED_FOR(gimp_item_get_visible)
+gboolean gimp_vectors_set_visible (gint32 vectors_ID,
+ gboolean visible);
+GIMP_DEPRECATED_FOR(gimp_item_get_linked)
+gboolean gimp_vectors_get_linked (gint32 vectors_ID);
+GIMP_DEPRECATED_FOR(gimp_item_set_linked)
+gboolean gimp_vectors_set_linked (gint32 vectors_ID,
+ gboolean linked);
+GIMP_DEPRECATED_FOR(gimp_item_get_tattoo)
+gint gimp_vectors_get_tattoo (gint32 vectors_ID);
+GIMP_DEPRECATED_FOR(gimp_item_set_tattoo)
+gboolean gimp_vectors_set_tattoo (gint32 vectors_ID,
+ gint tattoo);
+GIMP_DEPRECATED_FOR(gimp_item_get_parasite)
+GimpParasite * gimp_vectors_parasite_find (gint32 vectors_ID,
+ const gchar *name);
+GIMP_DEPRECATED_FOR(gimp_item_attach_parasite)
+gboolean gimp_vectors_parasite_attach (gint32 vectors_ID,
+ const GimpParasite *parasite);
+GIMP_DEPRECATED_FOR(gimp_item_detach_parasite)
+gboolean gimp_vectors_parasite_detach (gint32 vectors_ID,
+ const gchar *name);
+GIMP_DEPRECATED_FOR(gimp_item_get_parasite_list)
+gboolean gimp_vectors_parasite_list (gint32 vectors_ID,
+ gint *num_parasites,
+ gchar ***parasites);
+
+G_END_DECLS
+
+#endif /* __GIMP_VECTORS_H__ */
diff --git a/libgimp/gimpvectors_pdb.c b/libgimp/gimpvectors_pdb.c
new file mode 100644
index 0000000..94c1d22
--- /dev/null
+++ b/libgimp/gimpvectors_pdb.c
@@ -0,0 +1,1191 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpvectors_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpvectors
+ * @title: gimpvectors
+ * @short_description: Functions for querying and manipulating vectors.
+ *
+ * Functions for querying and manipulating vectors.
+ **/
+
+
+/**
+ * gimp_vectors_new:
+ * @image_ID: The image.
+ * @name: the name of the new vector object.
+ *
+ * Creates a new empty vectors object.
+ *
+ * Creates a new empty vectors object. The vectors object needs to be
+ * added to the image using gimp_image_insert_vectors().
+ *
+ * Returns: the current vector object, 0 if no vector exists in the
+ * image.
+ *
+ * Since: 2.4
+ **/
+gint32
+gimp_vectors_new (gint32 image_ID,
+ const gchar *name)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 vectors_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-vectors-new",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_STRING, name,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ vectors_ID = return_vals[1].data.d_vectors;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return vectors_ID;
+}
+
+/**
+ * gimp_vectors_new_from_text_layer:
+ * @image_ID: The image.
+ * @layer_ID: The text layer.
+ *
+ * Creates a new vectors object from a text layer.
+ *
+ * Creates a new vectors object from a text layer. The vectors object
+ * needs to be added to the image using gimp_image_insert_vectors().
+ *
+ * Returns: The vectors of the text layer.
+ *
+ * Since: 2.6
+ **/
+gint32
+gimp_vectors_new_from_text_layer (gint32 image_ID,
+ gint32 layer_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 vectors_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-vectors-new-from-text-layer",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_LAYER, layer_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ vectors_ID = return_vals[1].data.d_vectors;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return vectors_ID;
+}
+
+/**
+ * gimp_vectors_copy:
+ * @vectors_ID: The vectors object to copy.
+ *
+ * Copy a vectors object.
+ *
+ * This procedure copies the specified vectors object and returns the
+ * copy.
+ *
+ * Returns: The newly copied vectors object.
+ *
+ * Since: 2.6
+ **/
+gint32
+gimp_vectors_copy (gint32 vectors_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 vectors_copy_ID = -1;
+
+ return_vals = gimp_run_procedure ("gimp-vectors-copy",
+ &nreturn_vals,
+ GIMP_PDB_VECTORS, vectors_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ vectors_copy_ID = return_vals[1].data.d_vectors;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return vectors_copy_ID;
+}
+
+/**
+ * gimp_vectors_get_strokes:
+ * @vectors_ID: The vectors object.
+ * @num_strokes: The number of strokes returned.
+ *
+ * List the strokes associated with the passed path.
+ *
+ * Returns an Array with the stroke-IDs associated with the passed
+ * path.
+ *
+ * Returns: List of the strokes belonging to the path.
+ *
+ * Since: 2.4
+ **/
+gint *
+gimp_vectors_get_strokes (gint32 vectors_ID,
+ gint *num_strokes)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint *stroke_ids = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-vectors-get-strokes",
+ &nreturn_vals,
+ GIMP_PDB_VECTORS, vectors_ID,
+ GIMP_PDB_END);
+
+ *num_strokes = 0;
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ *num_strokes = return_vals[1].data.d_int32;
+ stroke_ids = g_new (gint32, *num_strokes);
+ memcpy (stroke_ids,
+ return_vals[2].data.d_int32array,
+ *num_strokes * sizeof (gint32));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return stroke_ids;
+}
+
+/**
+ * gimp_vectors_stroke_get_length:
+ * @vectors_ID: The vectors object.
+ * @stroke_id: The stroke ID.
+ * @precision: The precision used for the approximation.
+ *
+ * Measure the length of the given stroke.
+ *
+ * Measure the length of the given stroke.
+ *
+ * Returns: The length (in pixels) of the given stroke.
+ *
+ * Since: 2.4
+ **/
+gdouble
+gimp_vectors_stroke_get_length (gint32 vectors_ID,
+ gint stroke_id,
+ gdouble precision)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble length = 0.0;
+
+ return_vals = gimp_run_procedure ("gimp-vectors-stroke-get-length",
+ &nreturn_vals,
+ GIMP_PDB_VECTORS, vectors_ID,
+ GIMP_PDB_INT32, stroke_id,
+ GIMP_PDB_FLOAT, precision,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ length = return_vals[1].data.d_float;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return length;
+}
+
+/**
+ * gimp_vectors_stroke_get_point_at_dist:
+ * @vectors_ID: The vectors object.
+ * @stroke_id: The stroke ID.
+ * @dist: The given distance.
+ * @precision: The precision used for the approximation.
+ * @x_point: The x position of the point.
+ * @y_point: The y position of the point.
+ * @slope: The slope (dy / dx) at the specified point.
+ * @valid: Indicator for the validity of the returned data.
+ *
+ * Get point at a specified distance along the stroke.
+ *
+ * This will return the x,y position of a point at a given distance
+ * along the stroke. The distance will be obtained by first digitizing
+ * the curve internally and then walking along the curve. For a closed
+ * stroke the start of the path is the first point on the path that was
+ * created. This might not be obvious. If the stroke is not long
+ * enough, a \"valid\" flag will be FALSE.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_vectors_stroke_get_point_at_dist (gint32 vectors_ID,
+ gint stroke_id,
+ gdouble dist,
+ gdouble precision,
+ gdouble *x_point,
+ gdouble *y_point,
+ gdouble *slope,
+ gboolean *valid)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-vectors-stroke-get-point-at-dist",
+ &nreturn_vals,
+ GIMP_PDB_VECTORS, vectors_ID,
+ GIMP_PDB_INT32, stroke_id,
+ GIMP_PDB_FLOAT, dist,
+ GIMP_PDB_FLOAT, precision,
+ GIMP_PDB_END);
+
+ *x_point = 0.0;
+ *y_point = 0.0;
+ *slope = 0.0;
+ *valid = FALSE;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *x_point = return_vals[1].data.d_float;
+ *y_point = return_vals[2].data.d_float;
+ *slope = return_vals[3].data.d_float;
+ *valid = return_vals[4].data.d_int32;
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_vectors_remove_stroke:
+ * @vectors_ID: The vectors object.
+ * @stroke_id: The stroke ID.
+ *
+ * remove the stroke from a vectors object.
+ *
+ * Remove the stroke from a vectors object.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_vectors_remove_stroke (gint32 vectors_ID,
+ gint stroke_id)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-vectors-remove-stroke",
+ &nreturn_vals,
+ GIMP_PDB_VECTORS, vectors_ID,
+ GIMP_PDB_INT32, stroke_id,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_vectors_stroke_close:
+ * @vectors_ID: The vectors object.
+ * @stroke_id: The stroke ID.
+ *
+ * closes the specified stroke.
+ *
+ * Closes the specified stroke.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_vectors_stroke_close (gint32 vectors_ID,
+ gint stroke_id)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-vectors-stroke-close",
+ &nreturn_vals,
+ GIMP_PDB_VECTORS, vectors_ID,
+ GIMP_PDB_INT32, stroke_id,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_vectors_stroke_translate:
+ * @vectors_ID: The vectors object.
+ * @stroke_id: The stroke ID.
+ * @off_x: Offset in x direction.
+ * @off_y: Offset in y direction.
+ *
+ * translate the given stroke.
+ *
+ * Translate the given stroke.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_vectors_stroke_translate (gint32 vectors_ID,
+ gint stroke_id,
+ gint off_x,
+ gint off_y)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-vectors-stroke-translate",
+ &nreturn_vals,
+ GIMP_PDB_VECTORS, vectors_ID,
+ GIMP_PDB_INT32, stroke_id,
+ GIMP_PDB_INT32, off_x,
+ GIMP_PDB_INT32, off_y,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_vectors_stroke_scale:
+ * @vectors_ID: The vectors object.
+ * @stroke_id: The stroke ID.
+ * @scale_x: Scale factor in x direction.
+ * @scale_y: Scale factor in y direction.
+ *
+ * scales the given stroke.
+ *
+ * Scale the given stroke.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_vectors_stroke_scale (gint32 vectors_ID,
+ gint stroke_id,
+ gdouble scale_x,
+ gdouble scale_y)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-vectors-stroke-scale",
+ &nreturn_vals,
+ GIMP_PDB_VECTORS, vectors_ID,
+ GIMP_PDB_INT32, stroke_id,
+ GIMP_PDB_FLOAT, scale_x,
+ GIMP_PDB_FLOAT, scale_y,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_vectors_stroke_rotate:
+ * @vectors_ID: The vectors object.
+ * @stroke_id: The stroke ID.
+ * @center_x: X coordinate of the rotation center.
+ * @center_y: Y coordinate of the rotation center.
+ * @angle: angle to rotate about.
+ *
+ * rotates the given stroke.
+ *
+ * Rotates the given stroke around given center by angle (in degrees).
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_vectors_stroke_rotate (gint32 vectors_ID,
+ gint stroke_id,
+ gdouble center_x,
+ gdouble center_y,
+ gdouble angle)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-vectors-stroke-rotate",
+ &nreturn_vals,
+ GIMP_PDB_VECTORS, vectors_ID,
+ GIMP_PDB_INT32, stroke_id,
+ GIMP_PDB_FLOAT, center_x,
+ GIMP_PDB_FLOAT, center_y,
+ GIMP_PDB_FLOAT, angle,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_vectors_stroke_flip:
+ * @vectors_ID: The vectors object.
+ * @stroke_id: The stroke ID.
+ * @flip_type: Flip orientation, either vertical or horizontal.
+ * @axis: axis coordinate about which to flip, in pixels.
+ *
+ * flips the given stroke.
+ *
+ * Rotates the given stroke around given center by angle (in degrees).
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_vectors_stroke_flip (gint32 vectors_ID,
+ gint stroke_id,
+ GimpOrientationType flip_type,
+ gdouble axis)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-vectors-stroke-flip",
+ &nreturn_vals,
+ GIMP_PDB_VECTORS, vectors_ID,
+ GIMP_PDB_INT32, stroke_id,
+ GIMP_PDB_INT32, flip_type,
+ GIMP_PDB_FLOAT, axis,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_vectors_stroke_flip_free:
+ * @vectors_ID: The vectors object.
+ * @stroke_id: The stroke ID.
+ * @x1: X coordinate of the first point of the flipping axis.
+ * @y1: Y coordinate of the first point of the flipping axis.
+ * @x2: X coordinate of the second point of the flipping axis.
+ * @y2: Y coordinate of the second point of the flipping axis.
+ *
+ * flips the given stroke about an arbitrary axis.
+ *
+ * Flips the given stroke about an arbitrary axis. Axis is defined by
+ * two coordinates in the image (in pixels), through which the flipping
+ * axis passes.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_vectors_stroke_flip_free (gint32 vectors_ID,
+ gint stroke_id,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-vectors-stroke-flip-free",
+ &nreturn_vals,
+ GIMP_PDB_VECTORS, vectors_ID,
+ GIMP_PDB_INT32, stroke_id,
+ GIMP_PDB_FLOAT, x1,
+ GIMP_PDB_FLOAT, y1,
+ GIMP_PDB_FLOAT, x2,
+ GIMP_PDB_FLOAT, y2,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_vectors_stroke_get_points:
+ * @vectors_ID: The vectors object.
+ * @stroke_id: The stroke ID.
+ * @num_points: The number of floats returned.
+ * @controlpoints: List of the control points for the stroke (x0, y0, x1, y1, ...).
+ * @closed: Whether the stroke is closed or not.
+ *
+ * returns the control points of a stroke.
+ *
+ * returns the control points of a stroke. The interpretation of the
+ * coordinates returned depends on the type of the stroke. For Gimp 2.4
+ * this is always a bezier stroke, where the coordinates are the
+ * control points.
+ *
+ * Returns: type of the stroke (always GIMP_VECTORS_STROKE_TYPE_BEZIER
+ * for now).
+ *
+ * Since: 2.4
+ **/
+GimpVectorsStrokeType
+gimp_vectors_stroke_get_points (gint32 vectors_ID,
+ gint stroke_id,
+ gint *num_points,
+ gdouble **controlpoints,
+ gboolean *closed)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ GimpVectorsStrokeType type = 0;
+
+ return_vals = gimp_run_procedure ("gimp-vectors-stroke-get-points",
+ &nreturn_vals,
+ GIMP_PDB_VECTORS, vectors_ID,
+ GIMP_PDB_INT32, stroke_id,
+ GIMP_PDB_END);
+
+ *num_points = 0;
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ type = return_vals[1].data.d_int32;
+ *num_points = return_vals[2].data.d_int32;
+ *controlpoints = g_new (gdouble, *num_points);
+ memcpy (*controlpoints,
+ return_vals[3].data.d_floatarray,
+ *num_points * sizeof (gdouble));
+ *closed = return_vals[4].data.d_int32;
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return type;
+}
+
+/**
+ * gimp_vectors_stroke_new_from_points:
+ * @vectors_ID: The vectors object.
+ * @type: type of the stroke (always GIMP_VECTORS_STROKE_TYPE_BEZIER for now).
+ * @num_points: The number of elements in the array, i.e. the number of controlpoints in the stroke * 2 (x- and y-coordinate).
+ * @controlpoints: List of the x- and y-coordinates of the control points.
+ * @closed: Whether the stroke is to be closed or not.
+ *
+ * Adds a stroke of a given type to the vectors object.
+ *
+ * Adds a stroke of a given type to the vectors object. The coordinates
+ * of the control points can be specified. For now only strokes of the
+ * type GIMP_VECTORS_STROKE_TYPE_BEZIER are supported. The control
+ * points are specified as a pair of float values for the x- and
+ * y-coordinate. The Bezier stroke type needs a multiple of three
+ * control points. Each Bezier segment endpoint (anchor, A) has two
+ * additional control points (C) associated. They are specified in the
+ * order CACCACCAC...
+ *
+ * Returns: The stroke ID of the newly created stroke.
+ *
+ * Since: 2.4
+ **/
+gint
+gimp_vectors_stroke_new_from_points (gint32 vectors_ID,
+ GimpVectorsStrokeType type,
+ gint num_points,
+ const gdouble *controlpoints,
+ gboolean closed)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint stroke_id = 0;
+
+ return_vals = gimp_run_procedure ("gimp-vectors-stroke-new-from-points",
+ &nreturn_vals,
+ GIMP_PDB_VECTORS, vectors_ID,
+ GIMP_PDB_INT32, type,
+ GIMP_PDB_INT32, num_points,
+ GIMP_PDB_FLOATARRAY, controlpoints,
+ GIMP_PDB_INT32, closed,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ stroke_id = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return stroke_id;
+}
+
+/**
+ * gimp_vectors_stroke_interpolate:
+ * @vectors_ID: The vectors object.
+ * @stroke_id: The stroke ID.
+ * @precision: The precision used for the approximation.
+ * @num_coords: The number of floats returned.
+ * @closed: Whether the stroke is closed or not.
+ *
+ * returns polygonal approximation of the stroke.
+ *
+ * returns polygonal approximation of the stroke.
+ *
+ * Returns: List of the coords along the path (x0, y0, x1, y1, ...).
+ *
+ * Since: 2.4
+ **/
+gdouble *
+gimp_vectors_stroke_interpolate (gint32 vectors_ID,
+ gint stroke_id,
+ gdouble precision,
+ gint *num_coords,
+ gboolean *closed)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gdouble *coords = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-vectors-stroke-interpolate",
+ &nreturn_vals,
+ GIMP_PDB_VECTORS, vectors_ID,
+ GIMP_PDB_INT32, stroke_id,
+ GIMP_PDB_FLOAT, precision,
+ GIMP_PDB_END);
+
+ *num_coords = 0;
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ *num_coords = return_vals[1].data.d_int32;
+ coords = g_new (gdouble, *num_coords);
+ memcpy (coords,
+ return_vals[2].data.d_floatarray,
+ *num_coords * sizeof (gdouble));
+ *closed = return_vals[3].data.d_int32;
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return coords;
+}
+
+/**
+ * gimp_vectors_bezier_stroke_new_moveto:
+ * @vectors_ID: The vectors object.
+ * @x0: The x-coordinate of the moveto.
+ * @y0: The y-coordinate of the moveto.
+ *
+ * Adds a bezier stroke with a single moveto to the vectors object.
+ *
+ * Adds a bezier stroke with a single moveto to the vectors object.
+ *
+ * Returns: The resulting stroke.
+ *
+ * Since: 2.4
+ **/
+gint
+gimp_vectors_bezier_stroke_new_moveto (gint32 vectors_ID,
+ gdouble x0,
+ gdouble y0)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint stroke_id = 0;
+
+ return_vals = gimp_run_procedure ("gimp-vectors-bezier-stroke-new-moveto",
+ &nreturn_vals,
+ GIMP_PDB_VECTORS, vectors_ID,
+ GIMP_PDB_FLOAT, x0,
+ GIMP_PDB_FLOAT, y0,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ stroke_id = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return stroke_id;
+}
+
+/**
+ * gimp_vectors_bezier_stroke_lineto:
+ * @vectors_ID: The vectors object.
+ * @stroke_id: The stroke ID.
+ * @x0: The x-coordinate of the lineto.
+ * @y0: The y-coordinate of the lineto.
+ *
+ * Extends a bezier stroke with a lineto.
+ *
+ * Extends a bezier stroke with a lineto.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_vectors_bezier_stroke_lineto (gint32 vectors_ID,
+ gint stroke_id,
+ gdouble x0,
+ gdouble y0)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-vectors-bezier-stroke-lineto",
+ &nreturn_vals,
+ GIMP_PDB_VECTORS, vectors_ID,
+ GIMP_PDB_INT32, stroke_id,
+ GIMP_PDB_FLOAT, x0,
+ GIMP_PDB_FLOAT, y0,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_vectors_bezier_stroke_conicto:
+ * @vectors_ID: The vectors object.
+ * @stroke_id: The stroke ID.
+ * @x0: The x-coordinate of the control point.
+ * @y0: The y-coordinate of the control point.
+ * @x1: The x-coordinate of the end point.
+ * @y1: The y-coordinate of the end point.
+ *
+ * Extends a bezier stroke with a conic bezier spline.
+ *
+ * Extends a bezier stroke with a conic bezier spline. Actually a cubic
+ * bezier spline gets added that realizes the shape of a conic bezier
+ * spline.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_vectors_bezier_stroke_conicto (gint32 vectors_ID,
+ gint stroke_id,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-vectors-bezier-stroke-conicto",
+ &nreturn_vals,
+ GIMP_PDB_VECTORS, vectors_ID,
+ GIMP_PDB_INT32, stroke_id,
+ GIMP_PDB_FLOAT, x0,
+ GIMP_PDB_FLOAT, y0,
+ GIMP_PDB_FLOAT, x1,
+ GIMP_PDB_FLOAT, y1,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_vectors_bezier_stroke_cubicto:
+ * @vectors_ID: The vectors object.
+ * @stroke_id: The stroke ID.
+ * @x0: The x-coordinate of the first control point.
+ * @y0: The y-coordinate of the first control point.
+ * @x1: The x-coordinate of the second control point.
+ * @y1: The y-coordinate of the second control point.
+ * @x2: The x-coordinate of the end point.
+ * @y2: The y-coordinate of the end point.
+ *
+ * Extends a bezier stroke with a cubic bezier spline.
+ *
+ * Extends a bezier stroke with a cubic bezier spline.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_vectors_bezier_stroke_cubicto (gint32 vectors_ID,
+ gint stroke_id,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-vectors-bezier-stroke-cubicto",
+ &nreturn_vals,
+ GIMP_PDB_VECTORS, vectors_ID,
+ GIMP_PDB_INT32, stroke_id,
+ GIMP_PDB_FLOAT, x0,
+ GIMP_PDB_FLOAT, y0,
+ GIMP_PDB_FLOAT, x1,
+ GIMP_PDB_FLOAT, y1,
+ GIMP_PDB_FLOAT, x2,
+ GIMP_PDB_FLOAT, y2,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_vectors_bezier_stroke_new_ellipse:
+ * @vectors_ID: The vectors object.
+ * @x0: The x-coordinate of the center.
+ * @y0: The y-coordinate of the center.
+ * @radius_x: The radius in x direction.
+ * @radius_y: The radius in y direction.
+ * @angle: The angle the x-axis of the ellipse (radians, counterclockwise).
+ *
+ * Adds a bezier stroke describing an ellipse the vectors object.
+ *
+ * Adds a bezier stroke describing an ellipse the vectors object.
+ *
+ * Returns: The resulting stroke.
+ *
+ * Since: 2.4
+ **/
+gint
+gimp_vectors_bezier_stroke_new_ellipse (gint32 vectors_ID,
+ gdouble x0,
+ gdouble y0,
+ gdouble radius_x,
+ gdouble radius_y,
+ gdouble angle)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint stroke_id = 0;
+
+ return_vals = gimp_run_procedure ("gimp-vectors-bezier-stroke-new-ellipse",
+ &nreturn_vals,
+ GIMP_PDB_VECTORS, vectors_ID,
+ GIMP_PDB_FLOAT, x0,
+ GIMP_PDB_FLOAT, y0,
+ GIMP_PDB_FLOAT, radius_x,
+ GIMP_PDB_FLOAT, radius_y,
+ GIMP_PDB_FLOAT, angle,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ stroke_id = return_vals[1].data.d_int32;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return stroke_id;
+}
+
+/**
+ * gimp_vectors_to_selection:
+ * @vectors_ID: The vectors object to render to the selection.
+ * @operation: The desired operation with current selection.
+ * @antialias: Antialias selection.
+ * @feather: Feather selection.
+ * @feather_radius_x: Feather radius x.
+ * @feather_radius_y: Feather radius y.
+ *
+ * Deprecated: Use gimp_image_select_item() instead.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_vectors_to_selection (gint32 vectors_ID,
+ GimpChannelOps operation,
+ gboolean antialias,
+ gboolean feather,
+ gdouble feather_radius_x,
+ gdouble feather_radius_y)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-vectors-to-selection",
+ &nreturn_vals,
+ GIMP_PDB_VECTORS, vectors_ID,
+ GIMP_PDB_INT32, operation,
+ GIMP_PDB_INT32, antialias,
+ GIMP_PDB_INT32, feather,
+ GIMP_PDB_FLOAT, feather_radius_x,
+ GIMP_PDB_FLOAT, feather_radius_y,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_vectors_import_from_file:
+ * @image_ID: The image.
+ * @filename: The name of the SVG file to import.
+ * @merge: Merge paths into a single vectors object.
+ * @scale: Scale the SVG to image dimensions.
+ * @num_vectors: The number of newly created vectors.
+ * @vectors_ids: The list of newly created vectors.
+ *
+ * Import paths from an SVG file.
+ *
+ * This procedure imports paths from an SVG file. SVG elements other
+ * than paths and basic shapes are ignored.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_vectors_import_from_file (gint32 image_ID,
+ const gchar *filename,
+ gboolean merge,
+ gboolean scale,
+ gint *num_vectors,
+ gint32 **vectors_ids)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-vectors-import-from-file",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_STRING, filename,
+ GIMP_PDB_INT32, merge,
+ GIMP_PDB_INT32, scale,
+ GIMP_PDB_END);
+
+ *num_vectors = 0;
+ *vectors_ids = NULL;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *num_vectors = return_vals[1].data.d_int32;
+ *vectors_ids = g_new (gint32, *num_vectors);
+ memcpy (*vectors_ids,
+ return_vals[2].data.d_int32array,
+ *num_vectors * sizeof (gint32));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_vectors_import_from_string:
+ * @image_ID: The image.
+ * @string: A string that must be a complete and valid SVG document.
+ * @length: Number of bytes in string or -1 if the string is NULL terminated.
+ * @merge: Merge paths into a single vectors object.
+ * @scale: Scale the SVG to image dimensions.
+ * @num_vectors: The number of newly created vectors.
+ * @vectors_ids: The list of newly created vectors.
+ *
+ * Import paths from an SVG string.
+ *
+ * This procedure works like gimp_vectors_import_from_file() but takes
+ * a string rather than reading the SVG from a file. This allows you to
+ * write scripts that generate SVG and feed it to GIMP.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_vectors_import_from_string (gint32 image_ID,
+ const gchar *string,
+ gint length,
+ gboolean merge,
+ gboolean scale,
+ gint *num_vectors,
+ gint32 **vectors_ids)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-vectors-import-from-string",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_STRING, string,
+ GIMP_PDB_INT32, length,
+ GIMP_PDB_INT32, merge,
+ GIMP_PDB_INT32, scale,
+ GIMP_PDB_END);
+
+ *num_vectors = 0;
+ *vectors_ids = NULL;
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ if (success)
+ {
+ *num_vectors = return_vals[1].data.d_int32;
+ *vectors_ids = g_new (gint32, *num_vectors);
+ memcpy (*vectors_ids,
+ return_vals[2].data.d_int32array,
+ *num_vectors * sizeof (gint32));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_vectors_export_to_file:
+ * @image_ID: The image.
+ * @filename: The name of the SVG file to create.
+ * @vectors_ID: The vectors object to be saved, or 0 for all in the image.
+ *
+ * save a path as an SVG file.
+ *
+ * This procedure creates an SVG file to save a Vectors object, that
+ * is, a path. The resulting file can be edited using a vector graphics
+ * application, or later reloaded into GIMP. If you pass 0 as the
+ * 'vectors' argument, then all paths in the image will be exported.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: 2.6
+ **/
+gboolean
+gimp_vectors_export_to_file (gint32 image_ID,
+ const gchar *filename,
+ gint32 vectors_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-vectors-export-to-file",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_STRING, filename,
+ GIMP_PDB_VECTORS, vectors_ID,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
+ * gimp_vectors_export_to_string:
+ * @image_ID: The image.
+ * @vectors_ID: The vectors object to save, or 0 for all in the image.
+ *
+ * Save a path as an SVG string.
+ *
+ * This procedure works like gimp_vectors_export_to_file() but creates
+ * a string rather than a file. The contents are a NUL-terminated
+ * string that holds a complete XML document. If you pass 0 as the
+ * 'vectors' argument, then all paths in the image will be exported.
+ *
+ * Returns: A string whose contents are a complete SVG document.
+ *
+ * Since: 2.6
+ **/
+gchar *
+gimp_vectors_export_to_string (gint32 image_ID,
+ gint32 vectors_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *string = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-vectors-export-to-string",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_VECTORS, vectors_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ string = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return string;
+}
diff --git a/libgimp/gimpvectors_pdb.h b/libgimp/gimpvectors_pdb.h
new file mode 100644
index 0000000..c425185
--- /dev/null
+++ b/libgimp/gimpvectors_pdb.h
@@ -0,0 +1,151 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpvectors_pdb.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_VECTORS_PDB_H__
+#define __GIMP_VECTORS_PDB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gint32 gimp_vectors_new (gint32 image_ID,
+ const gchar *name);
+gint32 gimp_vectors_new_from_text_layer (gint32 image_ID,
+ gint32 layer_ID);
+gint32 gimp_vectors_copy (gint32 vectors_ID);
+gint* gimp_vectors_get_strokes (gint32 vectors_ID,
+ gint *num_strokes);
+gdouble gimp_vectors_stroke_get_length (gint32 vectors_ID,
+ gint stroke_id,
+ gdouble precision);
+gboolean gimp_vectors_stroke_get_point_at_dist (gint32 vectors_ID,
+ gint stroke_id,
+ gdouble dist,
+ gdouble precision,
+ gdouble *x_point,
+ gdouble *y_point,
+ gdouble *slope,
+ gboolean *valid);
+gboolean gimp_vectors_remove_stroke (gint32 vectors_ID,
+ gint stroke_id);
+gboolean gimp_vectors_stroke_close (gint32 vectors_ID,
+ gint stroke_id);
+gboolean gimp_vectors_stroke_translate (gint32 vectors_ID,
+ gint stroke_id,
+ gint off_x,
+ gint off_y);
+gboolean gimp_vectors_stroke_scale (gint32 vectors_ID,
+ gint stroke_id,
+ gdouble scale_x,
+ gdouble scale_y);
+gboolean gimp_vectors_stroke_rotate (gint32 vectors_ID,
+ gint stroke_id,
+ gdouble center_x,
+ gdouble center_y,
+ gdouble angle);
+gboolean gimp_vectors_stroke_flip (gint32 vectors_ID,
+ gint stroke_id,
+ GimpOrientationType flip_type,
+ gdouble axis);
+gboolean gimp_vectors_stroke_flip_free (gint32 vectors_ID,
+ gint stroke_id,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2);
+GimpVectorsStrokeType gimp_vectors_stroke_get_points (gint32 vectors_ID,
+ gint stroke_id,
+ gint *num_points,
+ gdouble **controlpoints,
+ gboolean *closed);
+gint gimp_vectors_stroke_new_from_points (gint32 vectors_ID,
+ GimpVectorsStrokeType type,
+ gint num_points,
+ const gdouble *controlpoints,
+ gboolean closed);
+gdouble* gimp_vectors_stroke_interpolate (gint32 vectors_ID,
+ gint stroke_id,
+ gdouble precision,
+ gint *num_coords,
+ gboolean *closed);
+gint gimp_vectors_bezier_stroke_new_moveto (gint32 vectors_ID,
+ gdouble x0,
+ gdouble y0);
+gboolean gimp_vectors_bezier_stroke_lineto (gint32 vectors_ID,
+ gint stroke_id,
+ gdouble x0,
+ gdouble y0);
+gboolean gimp_vectors_bezier_stroke_conicto (gint32 vectors_ID,
+ gint stroke_id,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1);
+gboolean gimp_vectors_bezier_stroke_cubicto (gint32 vectors_ID,
+ gint stroke_id,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2);
+gint gimp_vectors_bezier_stroke_new_ellipse (gint32 vectors_ID,
+ gdouble x0,
+ gdouble y0,
+ gdouble radius_x,
+ gdouble radius_y,
+ gdouble angle);
+GIMP_DEPRECATED_FOR(gimp_image_select_item)
+gboolean gimp_vectors_to_selection (gint32 vectors_ID,
+ GimpChannelOps operation,
+ gboolean antialias,
+ gboolean feather,
+ gdouble feather_radius_x,
+ gdouble feather_radius_y);
+gboolean gimp_vectors_import_from_file (gint32 image_ID,
+ const gchar *filename,
+ gboolean merge,
+ gboolean scale,
+ gint *num_vectors,
+ gint32 **vectors_ids);
+gboolean gimp_vectors_import_from_string (gint32 image_ID,
+ const gchar *string,
+ gint length,
+ gboolean merge,
+ gboolean scale,
+ gint *num_vectors,
+ gint32 **vectors_ids);
+gboolean gimp_vectors_export_to_file (gint32 image_ID,
+ const gchar *filename,
+ gint32 vectors_ID);
+gchar* gimp_vectors_export_to_string (gint32 image_ID,
+ gint32 vectors_ID);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_VECTORS_PDB_H__ */
diff --git a/libgimp/gimpzoompreview.c b/libgimp/gimpzoompreview.c
new file mode 100644
index 0000000..eb0735a
--- /dev/null
+++ b/libgimp/gimpzoompreview.c
@@ -0,0 +1,1013 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpzoompreview.c
+ * Copyright (C) 2005 David Odin <dindinx@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+/* we use our own deprecated API here */
+#define GIMP_DISABLE_DEPRECATION_WARNINGS
+
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "gimpuitypes.h"
+
+#include "gimp.h"
+
+#include "gimpdrawablepreview.h"
+#include "gimpzoompreview.h"
+
+
+/**
+ * SECTION: gimpzoompreview
+ * @title: GimpZoomPreview
+ * @short_description: A drawable preview with zooming capabilities.
+ *
+ * A drawable preview with zooming capabilities.
+ **/
+
+
+enum
+{
+ PROP_0,
+ PROP_DRAWABLE,
+ PROP_DRAWABLE_ID,
+ PROP_MODEL
+};
+
+
+struct _GimpZoomPreviewPrivate
+{
+ gint32 drawable_ID;
+ GimpDrawable *drawable;
+ GimpZoomModel *model;
+ GdkRectangle extents;
+};
+
+typedef struct
+{
+ gboolean update;
+} PreviewSettings;
+
+
+#define GIMP_ZOOM_PREVIEW_GET_PRIVATE(obj) \
+ ((GimpZoomPreviewPrivate *) ((GimpZoomPreview *) (obj))->priv)
+
+static void gimp_zoom_preview_constructed (GObject *object);
+static void gimp_zoom_preview_finalize (GObject *object);
+static void gimp_zoom_preview_dispose (GObject *object);
+static void gimp_zoom_preview_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_zoom_preview_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_zoom_preview_set_adjustments (GimpZoomPreview *preview,
+ gdouble old_factor,
+ gdouble new_factor);
+static void gimp_zoom_preview_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation,
+ GimpZoomPreview *preview);
+static void gimp_zoom_preview_style_set (GtkWidget *widget,
+ GtkStyle *prev_style);
+static gboolean gimp_zoom_preview_scroll_event (GtkWidget *widget,
+ GdkEventScroll *event,
+ GimpZoomPreview *preview);
+static void gimp_zoom_preview_draw (GimpPreview *preview);
+static void gimp_zoom_preview_draw_buffer (GimpPreview *preview,
+ const guchar *buffer,
+ gint rowstride);
+static void gimp_zoom_preview_draw_thumb (GimpPreview *preview,
+ GimpPreviewArea *area,
+ gint width,
+ gint height);
+static void gimp_zoom_preview_set_cursor (GimpPreview *preview);
+static void gimp_zoom_preview_transform (GimpPreview *preview,
+ gint src_x,
+ gint src_y,
+ gint *dest_x,
+ gint *dest_y);
+static void gimp_zoom_preview_untransform (GimpPreview *preview,
+ gint src_x,
+ gint src_y,
+ gint *dest_x,
+ gint *dest_y);
+
+static void gimp_zoom_preview_set_drawable (GimpZoomPreview *preview,
+ GimpDrawable *drawable);
+static void gimp_zoom_preview_set_drawable_id (GimpZoomPreview *preview,
+ gint32 drawable_ID);
+static void gimp_zoom_preview_set_model (GimpZoomPreview *preview,
+ GimpZoomModel *model);
+
+static void gimp_zoom_preview_get_source_area (GimpPreview *preview,
+ gint *x,
+ gint *y,
+ gint *w,
+ gint *h);
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpZoomPreview, gimp_zoom_preview,
+ GIMP_TYPE_SCROLLED_PREVIEW)
+
+#define parent_class gimp_zoom_preview_parent_class
+
+static gint gimp_zoom_preview_counter = 0;
+
+
+static void
+gimp_zoom_preview_class_init (GimpZoomPreviewClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GimpPreviewClass *preview_class = GIMP_PREVIEW_CLASS (klass);
+
+ object_class->constructed = gimp_zoom_preview_constructed;
+ object_class->finalize = gimp_zoom_preview_finalize;
+ object_class->dispose = gimp_zoom_preview_dispose;
+ object_class->get_property = gimp_zoom_preview_get_property;
+ object_class->set_property = gimp_zoom_preview_set_property;
+
+ widget_class->style_set = gimp_zoom_preview_style_set;
+
+ preview_class->draw = gimp_zoom_preview_draw;
+ preview_class->draw_buffer = gimp_zoom_preview_draw_buffer;
+ preview_class->draw_thumb = gimp_zoom_preview_draw_thumb;
+ preview_class->set_cursor = gimp_zoom_preview_set_cursor;
+ preview_class->transform = gimp_zoom_preview_transform;
+ preview_class->untransform = gimp_zoom_preview_untransform;
+
+ /**
+ * GimpZoomPreview:drawable:
+ *
+ * The drawable the #GimpZoomPreview is attached to.
+ *
+ * Deprecated: use the drawable-id property instead.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class, PROP_DRAWABLE,
+ g_param_spec_pointer ("drawable",
+ "Drawable",
+ "Deprecated: use the drawable-id property instead",
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * GimpZoomPreview:drawable-id:
+ *
+ * The drawable the #GimpZoomPreview is attached to.
+ *
+ * Since: 2.10
+ */
+ g_object_class_install_property (object_class, PROP_DRAWABLE_ID,
+ g_param_spec_int ("drawable-id",
+ "Drawable ID",
+ "The drawable this preview is attached to",
+ -1, G_MAXINT, -1,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * GimpZoomPreview:model:
+ *
+ * The #GimpZoomModel used by this #GimpZoomPreview.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class, PROP_MODEL,
+ g_param_spec_object ("model",
+ "Model",
+ "The zoom preview's GimpZoomModel",
+ GIMP_TYPE_ZOOM_MODEL,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+gimp_zoom_preview_init (GimpZoomPreview *preview)
+{
+ preview->priv = gimp_zoom_preview_get_instance_private (preview);
+
+ g_signal_connect (GIMP_PREVIEW (preview)->area, "size-allocate",
+ G_CALLBACK (gimp_zoom_preview_size_allocate),
+ preview);
+ g_signal_connect (GIMP_PREVIEW (preview)->area, "scroll-event",
+ G_CALLBACK (gimp_zoom_preview_scroll_event),
+ preview);
+
+ g_object_set (GIMP_PREVIEW (preview)->area,
+ "check-size", gimp_check_size (),
+ "check-type", gimp_check_type (),
+ NULL);
+
+ gimp_scrolled_preview_set_policy (GIMP_SCROLLED_PREVIEW (preview),
+ GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS);
+}
+
+static void
+gimp_zoom_preview_constructed (GObject *object)
+{
+ GimpZoomPreviewPrivate *priv = GIMP_ZOOM_PREVIEW_GET_PRIVATE (object);
+ gchar *data_name;
+ PreviewSettings settings;
+
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ data_name = g_strdup_printf ("%s-zoom-preview-%d",
+ g_get_prgname (),
+ gimp_zoom_preview_counter++);
+
+ if (gimp_get_data (data_name, &settings))
+ {
+ gimp_preview_set_update (GIMP_PREVIEW (object), settings.update);
+ }
+
+ g_object_set_data_full (object, "gimp-zoom-preview-data-name",
+ data_name, (GDestroyNotify) g_free);
+
+ if (! priv->model)
+ {
+ GimpZoomModel *model = gimp_zoom_model_new ();
+
+ gimp_zoom_model_set_range (model, 1.0, 256.0);
+ gimp_zoom_preview_set_model (GIMP_ZOOM_PREVIEW (object), model);
+
+ g_object_unref (model);
+ }
+
+ gimp_zoom_preview_set_adjustments (GIMP_ZOOM_PREVIEW (object), 1.0, 1.0);
+}
+
+static void
+gimp_zoom_preview_finalize (GObject *object)
+{
+ GimpZoomPreviewPrivate *priv = GIMP_ZOOM_PREVIEW_GET_PRIVATE (object);
+
+ g_clear_object (&priv->model);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_zoom_preview_dispose (GObject *object)
+{
+ const gchar *data_name = g_object_get_data (G_OBJECT (object),
+ "gimp-zoom-preview-data-name");
+
+ if (data_name)
+ {
+ GimpPreview *preview = GIMP_PREVIEW (object);
+ PreviewSettings settings;
+
+ settings.update = gimp_preview_get_update (preview);
+
+ gimp_set_data (data_name, &settings, sizeof (PreviewSettings));
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gimp_zoom_preview_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpZoomPreview *preview = GIMP_ZOOM_PREVIEW (object);
+
+ switch (property_id)
+ {
+ case PROP_DRAWABLE:
+ g_value_set_pointer (value, gimp_zoom_preview_get_drawable (preview));
+ break;
+
+ case PROP_DRAWABLE_ID:
+ g_value_set_int (value, gimp_zoom_preview_get_drawable_id (preview));
+ break;
+
+ case PROP_MODEL:
+ g_value_set_object (value, gimp_zoom_preview_get_model (preview));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_zoom_preview_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpZoomPreview *preview = GIMP_ZOOM_PREVIEW (object);
+
+ switch (property_id)
+ {
+ case PROP_DRAWABLE:
+ g_return_if_fail (preview->priv->drawable_ID < 1);
+ if (g_value_get_pointer (value))
+ gimp_zoom_preview_set_drawable (preview, g_value_get_pointer (value));
+ break;
+
+ case PROP_DRAWABLE_ID:
+ gimp_zoom_preview_set_drawable_id (preview, g_value_get_int (value));
+ break;
+
+ case PROP_MODEL:
+ gimp_zoom_preview_set_model (preview, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_zoom_preview_set_adjustments (GimpZoomPreview *preview,
+ gdouble old_factor,
+ gdouble new_factor)
+{
+ GimpScrolledPreview *scrolled_preview = GIMP_SCROLLED_PREVIEW (preview);
+ GtkAdjustment *adj;
+ gdouble width;
+ gdouble height;
+ gdouble ratio;
+
+ gimp_scrolled_preview_freeze (scrolled_preview);
+
+ width = GIMP_PREVIEW (preview)->width;
+ height = GIMP_PREVIEW (preview)->height;
+
+ ratio = new_factor / old_factor;
+
+ adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_preview->hscr));
+ gtk_adjustment_configure (adj,
+ (gtk_adjustment_get_value (adj) + width / 2.0) * ratio
+ - width / 2.0,
+ 0,
+ width * new_factor,
+ new_factor,
+ MAX (width / 2.0, new_factor),
+ width);
+
+ adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_preview->vscr));
+ gtk_adjustment_configure (adj,
+ (gtk_adjustment_get_value (adj) + height / 2.0) * ratio
+ - height / 2.0,
+ 0,
+ height * new_factor,
+ new_factor,
+ MAX (height / 2.0, new_factor),
+ height);
+
+ gimp_scrolled_preview_thaw (scrolled_preview);
+}
+
+static void
+gimp_zoom_preview_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation,
+ GimpZoomPreview *preview)
+{
+ gdouble zoom;
+
+ gint width = GIMP_PREVIEW (preview)->xmax - GIMP_PREVIEW (preview)->xmin;
+ gint height = GIMP_PREVIEW (preview)->ymax - GIMP_PREVIEW (preview)->ymin;
+
+ GIMP_PREVIEW (preview)->width = MIN (width, allocation->width);
+ GIMP_PREVIEW (preview)->height = MIN (height, allocation->height);
+
+ zoom = gimp_zoom_model_get_factor (preview->priv->model);
+
+ gimp_zoom_preview_set_adjustments (preview, zoom, zoom);
+}
+
+static void
+gimp_zoom_preview_style_set (GtkWidget *widget,
+ GtkStyle *prev_style)
+{
+ GimpPreview *preview = GIMP_PREVIEW (widget);
+ GimpZoomPreviewPrivate *priv = GIMP_ZOOM_PREVIEW (preview)->priv;
+ gint size;
+ gint width, height;
+ gint x1, y1;
+ gint x2, y2;
+
+ if (GTK_WIDGET_CLASS (parent_class)->style_set)
+ GTK_WIDGET_CLASS (parent_class)->style_set (widget, prev_style);
+
+ gtk_widget_style_get (widget, "size", &size, NULL);
+
+ if (_gimp_drawable_preview_get_bounds (priv->drawable_ID,
+ &x1, &y1, &x2, &y2))
+ {
+ width = x2 - x1;
+ height = y2 - y1;
+ }
+ else
+ {
+ width = gimp_drawable_width (priv->drawable_ID);
+ height = gimp_drawable_height (priv->drawable_ID);
+ }
+
+ if (width > height)
+ {
+ preview->width = MIN (width, size);
+ preview->height = (height * preview->width) / width;
+ }
+ else
+ {
+ preview->height = MIN (height, size);
+ preview->width = (width * preview->height) / height;
+ }
+
+ gtk_widget_set_size_request (preview->area,
+ preview->width, preview->height);
+}
+
+static gboolean
+gimp_zoom_preview_scroll_event (GtkWidget *widget,
+ GdkEventScroll *event,
+ GimpZoomPreview *preview)
+{
+ if (event->state & GDK_CONTROL_MASK)
+ {
+ GimpZoomPreviewPrivate *priv = GIMP_ZOOM_PREVIEW_GET_PRIVATE (preview);
+
+ gimp_scrolled_preview_freeze (GIMP_SCROLLED_PREVIEW (preview));
+
+ switch (event->direction)
+ {
+ case GDK_SCROLL_UP:
+ gimp_zoom_model_zoom (priv->model, GIMP_ZOOM_IN, 0.0);
+ break;
+
+ case GDK_SCROLL_DOWN:
+ gimp_zoom_model_zoom (priv->model, GIMP_ZOOM_OUT, 0.0);
+ break;
+
+ default:
+ break;
+ }
+
+ gimp_scrolled_preview_thaw (GIMP_SCROLLED_PREVIEW (preview));
+ }
+
+ return FALSE;
+}
+
+static void
+gimp_zoom_preview_draw (GimpPreview *preview)
+{
+ GimpZoomPreviewPrivate *priv = GIMP_ZOOM_PREVIEW (preview)->priv;
+ guchar *data;
+ gint width;
+ gint height;
+ gint bpp;
+
+ if (! priv->model)
+ return;
+
+ if (priv->drawable_ID < 1)
+ return;
+
+ data = gimp_zoom_preview_get_source (GIMP_ZOOM_PREVIEW (preview),
+ &width, &height, &bpp);
+
+ if (data)
+ {
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview->area),
+ 0, 0, width, height,
+ gimp_drawable_type (priv->drawable_ID),
+ data, width * bpp);
+ g_free (data);
+ }
+}
+
+static void
+gimp_zoom_preview_draw_buffer (GimpPreview *preview,
+ const guchar *buffer,
+ gint rowstride)
+{
+ GimpZoomPreviewPrivate *priv = GIMP_ZOOM_PREVIEW (preview)->priv;
+ gint32 image_ID;
+
+ image_ID = gimp_item_get_image (priv->drawable_ID);
+
+ if (gimp_selection_is_empty (image_ID))
+ {
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview->area),
+ 0, 0,
+ preview->width, preview->height,
+ gimp_drawable_type (priv->drawable_ID),
+ buffer,
+ rowstride);
+ }
+ else
+ {
+ guchar *sel;
+ guchar *src;
+ gint selection_ID;
+ gint width, height;
+ gint bpp;
+ gint src_x;
+ gint src_y;
+ gint src_width;
+ gint src_height;
+ gint offsx = 0;
+ gint offsy = 0;
+
+ selection_ID = gimp_image_get_selection (image_ID);
+
+ width = preview->width;
+ height = preview->height;
+
+ gimp_zoom_preview_get_source_area (preview,
+ &src_x, &src_y,
+ &src_width, &src_height);
+
+ src = gimp_drawable_get_sub_thumbnail_data (priv->drawable_ID,
+ src_x, src_y,
+ src_width, src_height,
+ &width, &height, &bpp);
+ gimp_drawable_offsets (priv->drawable_ID, &offsx, &offsy);
+ sel = gimp_drawable_get_sub_thumbnail_data (selection_ID,
+ src_x + offsx, src_y + offsy,
+ src_width, src_height,
+ &width, &height, &bpp);
+
+ gimp_preview_area_mask (GIMP_PREVIEW_AREA (preview->area),
+ 0, 0, preview->width, preview->height,
+ gimp_drawable_type (priv->drawable_ID),
+ src, width * gimp_drawable_bpp (priv->drawable_ID),
+ buffer, rowstride,
+ sel, width);
+
+ g_free (sel);
+ g_free (src);
+ }
+
+}
+
+static void
+gimp_zoom_preview_draw_thumb (GimpPreview *preview,
+ GimpPreviewArea *area,
+ gint width,
+ gint height)
+{
+ GimpZoomPreviewPrivate *priv = GIMP_ZOOM_PREVIEW (preview)->priv;
+
+ if (priv->drawable_ID > 0)
+ _gimp_drawable_preview_area_draw_thumb (area, priv->drawable_ID,
+ width, height);
+}
+
+static void
+gimp_zoom_preview_set_cursor (GimpPreview *preview)
+{
+ if (! gtk_widget_get_realized (preview->area))
+ return;
+
+ if (gimp_zoom_preview_get_factor (GIMP_ZOOM_PREVIEW (preview)) > 1.0)
+ {
+ gdk_window_set_cursor (gtk_widget_get_window (preview->area),
+ GIMP_SCROLLED_PREVIEW (preview)->cursor_move);
+ }
+ else
+ {
+ gdk_window_set_cursor (gtk_widget_get_window (preview->area),
+ preview->default_cursor);
+ }
+}
+
+static void
+gimp_zoom_preview_transform (GimpPreview *preview,
+ gint src_x,
+ gint src_y,
+ gint *dest_x,
+ gint *dest_y)
+{
+ GimpZoomPreviewPrivate *priv = GIMP_ZOOM_PREVIEW (preview)->priv;
+
+ gdouble zoom = gimp_zoom_preview_get_factor (GIMP_ZOOM_PREVIEW (preview));
+
+ *dest_x = ((gdouble) (src_x - priv->extents.x) *
+ preview->width / priv->extents.width * zoom) - preview->xoff;
+
+ *dest_y = ((gdouble) (src_y - priv->extents.y) *
+ preview->height / priv->extents.height * zoom) - preview->yoff;
+}
+
+static void
+gimp_zoom_preview_untransform (GimpPreview *preview,
+ gint src_x,
+ gint src_y,
+ gint *dest_x,
+ gint *dest_y)
+{
+ GimpZoomPreviewPrivate *priv = GIMP_ZOOM_PREVIEW (preview)->priv;
+
+ gdouble zoom = gimp_zoom_preview_get_factor (GIMP_ZOOM_PREVIEW (preview));
+
+ *dest_x = (priv->extents.x +
+ ((gdouble) (src_x + preview->xoff) *
+ priv->extents.width / preview->width / zoom));
+
+ *dest_y = (priv->extents.y +
+ ((gdouble) (src_y + preview->yoff) *
+ priv->extents.height / preview->height / zoom));
+}
+
+static void
+gimp_zoom_preview_set_drawable (GimpZoomPreview *preview,
+ GimpDrawable *drawable)
+{
+ g_return_if_fail (preview->priv->drawable == NULL);
+ g_return_if_fail (preview->priv->drawable_ID < 1);
+
+ preview->priv->drawable = drawable;
+
+ gimp_zoom_preview_set_drawable_id (preview, drawable->drawable_id);
+}
+
+static void
+gimp_zoom_preview_set_drawable_id (GimpZoomPreview *preview,
+ gint32 drawable_ID)
+{
+ GimpZoomPreviewPrivate *priv = preview->priv;
+ gint x, y;
+ gint width, height;
+ gint max_width, max_height;
+
+ g_return_if_fail (preview->priv->drawable_ID < 1);
+
+ priv->drawable_ID = drawable_ID;
+
+ if (gimp_drawable_mask_intersect (drawable_ID, &x, &y, &width, &height))
+ {
+ priv->extents.x = x;
+ priv->extents.y = y;
+ }
+ else
+ {
+ width = gimp_drawable_width (drawable_ID);
+ height = gimp_drawable_height (drawable_ID);
+
+ priv->extents.x = 0;
+ priv->extents.y = 0;
+ }
+
+ priv->extents.width = width;
+ priv->extents.height = height;
+
+ if (width > height)
+ {
+ max_width = MIN (width, 512);
+ max_height = (height * max_width) / width;
+ }
+ else
+ {
+ max_height = MIN (height, 512);
+ max_width = (width * max_height) / height;
+ }
+
+ gimp_preview_set_bounds (GIMP_PREVIEW (preview),
+ 0, 0, max_width, max_height);
+
+ g_object_set (GIMP_PREVIEW (preview)->frame,
+ "ratio", (gdouble) width / (gdouble) height,
+ NULL);
+}
+
+static void
+gimp_zoom_preview_set_model (GimpZoomPreview *preview,
+ GimpZoomModel *model)
+{
+ GimpZoomPreviewPrivate *priv = GIMP_ZOOM_PREVIEW_GET_PRIVATE (preview);
+ GtkWidget *button_bar;
+ GtkWidget *button;
+ GtkWidget *box;
+
+ g_return_if_fail (priv->model == NULL);
+
+ if (! model)
+ return;
+
+ priv->model = g_object_ref (model);
+
+ g_signal_connect_swapped (priv->model, "zoomed",
+ G_CALLBACK (gimp_zoom_preview_set_adjustments),
+ preview);
+
+ box = gimp_preview_get_controls (GIMP_PREVIEW (preview));
+ g_return_if_fail (GTK_IS_BOX (box));
+
+ button_bar = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
+ gtk_box_pack_end (GTK_BOX (box), button_bar, FALSE, FALSE, 0);
+ gtk_widget_show (button_bar);
+
+ /* zoom out */
+ button = gimp_zoom_button_new (priv->model,
+ GIMP_ZOOM_OUT, GTK_ICON_SIZE_SMALL_TOOLBAR);
+ gtk_box_pack_start (GTK_BOX (button_bar), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ /* zoom in */
+ button = gimp_zoom_button_new (priv->model,
+ GIMP_ZOOM_IN, GTK_ICON_SIZE_SMALL_TOOLBAR);
+ gtk_box_pack_start (GTK_BOX (button_bar), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+}
+
+static void
+gimp_zoom_preview_get_source_area (GimpPreview *preview,
+ gint *x,
+ gint *y,
+ gint *w,
+ gint *h)
+{
+ GimpZoomPreviewPrivate *priv = GIMP_ZOOM_PREVIEW_GET_PRIVATE (preview);
+ gdouble zoom = gimp_zoom_model_get_factor (priv->model);
+
+ gimp_zoom_preview_untransform (preview, 0, 0, x, y);
+
+ *w = priv->extents.width / zoom;
+ *h = priv->extents.height / zoom;
+}
+
+
+/**
+ * gimp_zoom_preview_new_from_drawable_id:
+ * @drawable_ID: a drawable ID
+ *
+ * Creates a new #GimpZoomPreview widget for @drawable_ID.
+ *
+ * Since: 2.10
+ *
+ * Returns: a new #GimpZoomPreview.
+ **/
+GtkWidget *
+gimp_zoom_preview_new_from_drawable_id (gint32 drawable_ID)
+{
+ g_return_val_if_fail (gimp_item_is_valid (drawable_ID), NULL);
+ g_return_val_if_fail (gimp_item_is_drawable (drawable_ID), NULL);
+
+ return g_object_new (GIMP_TYPE_ZOOM_PREVIEW,
+ "drawable-id", drawable_ID,
+ NULL);
+}
+
+/**
+ * gimp_zoom_preview_new_with_model_from_drawable_id:
+ * @drawable_ID: a drawable ID
+ * @model: a #GimpZoomModel
+ *
+ * Creates a new #GimpZoomPreview widget for @drawable_ID using the
+ * given @model.
+ *
+ * This variant of gimp_zoom_preview_new_from_drawable_id() allows you
+ * to create a preview using an existing zoom model. This may be
+ * useful if for example you want to have two zoom previews that keep
+ * their zoom factor in sync.
+ *
+ * Since: 2.10
+ *
+ * Returns: a new #GimpZoomPreview.
+ **/
+GtkWidget *
+gimp_zoom_preview_new_with_model_from_drawable_id (gint32 drawable_ID,
+ GimpZoomModel *model)
+
+{
+ g_return_val_if_fail (gimp_item_is_valid (drawable_ID), NULL);
+ g_return_val_if_fail (gimp_item_is_drawable (drawable_ID), NULL);
+ g_return_val_if_fail (GIMP_IS_ZOOM_MODEL (model), NULL);
+
+ return g_object_new (GIMP_TYPE_ZOOM_PREVIEW,
+ "drawable-id", drawable_ID,
+ "model", model,
+ NULL);
+}
+
+/**
+ * gimp_zoom_preview_new:
+ * @drawable: a #GimpDrawable
+ *
+ * Creates a new #GimpZoomPreview widget for @drawable.
+ *
+ * Deprecated: 2.10: Use gimp_zoom_preview_new_from_drawable_id() instead.
+ *
+ * Since: 2.4
+ *
+ * Returns: a new #GimpZoomPreview.
+ **/
+GtkWidget *
+gimp_zoom_preview_new (GimpDrawable *drawable)
+{
+ g_return_val_if_fail (drawable != NULL, NULL);
+
+ return g_object_new (GIMP_TYPE_ZOOM_PREVIEW,
+ "drawable", drawable,
+ NULL);
+}
+
+/**
+ * gimp_zoom_preview_new_with_model:
+ * @drawable: a #GimpDrawable
+ * @model: a #GimpZoomModel
+ *
+ * Creates a new #GimpZoomPreview widget for @drawable using the
+ * given @model.
+ *
+ * This variant of gimp_zoom_preview_new() allows you to create a
+ * preview using an existing zoom model. This may be useful if for
+ * example you want to have two zoom previews that keep their zoom
+ * factor in sync.
+ *
+ * Deprecated: 2.10: Use gimp_zoom_preview_new_with_model_from_drawable_id()
+ * instead.
+ *
+ * Since: 2.4
+ *
+ * Returns: a new #GimpZoomPreview.
+ **/
+GtkWidget *
+gimp_zoom_preview_new_with_model (GimpDrawable *drawable,
+ GimpZoomModel *model)
+
+{
+ g_return_val_if_fail (drawable != NULL, NULL);
+ g_return_val_if_fail (GIMP_IS_ZOOM_MODEL (model), NULL);
+
+ return g_object_new (GIMP_TYPE_ZOOM_PREVIEW,
+ "drawable", drawable,
+ "model", model,
+ NULL);
+}
+
+
+/**
+ * gimp_zoom_preview_get_drawable_id:
+ * @preview: a #GimpZoomPreview widget
+ *
+ * Returns the drawable_ID the #GimpZoomPreview is attached to.
+ *
+ * Return Value: the drawable_ID that was passed to
+ * gimp_zoom_preview_new_from_drawable_id().
+ *
+ * Since: 2.10
+ **/
+gint32
+gimp_zoom_preview_get_drawable_id (GimpZoomPreview *preview)
+{
+ g_return_val_if_fail (GIMP_IS_ZOOM_PREVIEW (preview), -1);
+
+ return GIMP_ZOOM_PREVIEW_GET_PRIVATE (preview)->drawable_ID;
+}
+
+/**
+ * gimp_zoom_preview_get_drawable:
+ * @preview: a #GimpZoomPreview widget
+ *
+ * Returns the #GimpDrawable the #GimpZoomPreview is attached to.
+ *
+ * Return Value: the #GimpDrawable that was passed to gimp_zoom_preview_new().
+ *
+ * Deprecated: 2.10: Use gimp_zoom_preview_get_drawable_id() instead.
+ *
+ * Since: 2.4
+ **/
+GimpDrawable *
+gimp_zoom_preview_get_drawable (GimpZoomPreview *preview)
+{
+ g_return_val_if_fail (GIMP_IS_ZOOM_PREVIEW (preview), NULL);
+
+ return GIMP_ZOOM_PREVIEW_GET_PRIVATE (preview)->drawable;
+}
+
+/**
+ * gimp_zoom_preview_get_model:
+ * @preview: a #GimpZoomPreview widget
+ *
+ * Returns the #GimpZoomModel the preview is using.
+ *
+ * Return Value: a pointer to the #GimpZoomModel owned by the @preview
+ *
+ * Since: 2.4
+ **/
+GimpZoomModel *
+gimp_zoom_preview_get_model (GimpZoomPreview *preview)
+{
+ g_return_val_if_fail (GIMP_IS_ZOOM_PREVIEW (preview), NULL);
+
+ return GIMP_ZOOM_PREVIEW_GET_PRIVATE (preview)->model;
+}
+
+/**
+ * gimp_zoom_preview_get_factor:
+ * @preview: a #GimpZoomPreview widget
+ *
+ * Returns the zoom factor the preview is currently using.
+ *
+ * Return Value: the current zoom factor
+ *
+ * Since: 2.4
+ **/
+gdouble
+gimp_zoom_preview_get_factor (GimpZoomPreview *preview)
+{
+ GimpZoomPreviewPrivate *priv;
+
+ g_return_val_if_fail (GIMP_IS_ZOOM_PREVIEW (preview), 1.0);
+
+ priv = GIMP_ZOOM_PREVIEW_GET_PRIVATE (preview);
+
+ return priv->model ? gimp_zoom_model_get_factor (priv->model) : 1.0;
+}
+
+/**
+ * gimp_zoom_preview_get_source:
+ * @preview: a #GimpZoomPreview widget
+ * @width: a pointer to an int where the current width of the zoom widget
+ * will be put.
+ * @height: a pointer to an int where the current width of the zoom widget
+ * will be put.
+ * @bpp: return location for the number of bytes per pixel
+ *
+ * Returns the scaled image data of the part of the drawable the
+ * #GimpZoomPreview is currently showing, as a newly allocated array of guchar.
+ * This function also allow to get the current width, height and bpp of the
+ * #GimpZoomPreview.
+ *
+ * Return Value: newly allocated data that should be released using g_free()
+ * when it is not any longer needed
+ *
+ * Since: 2.4
+ */
+guchar *
+gimp_zoom_preview_get_source (GimpZoomPreview *preview,
+ gint *width,
+ gint *height,
+ gint *bpp)
+{
+ gint32 drawable_ID;
+
+ g_return_val_if_fail (GIMP_IS_ZOOM_PREVIEW (preview), NULL);
+ g_return_val_if_fail (width != NULL && height != NULL && bpp != NULL, NULL);
+
+ drawable_ID = gimp_zoom_preview_get_drawable_id (preview);
+
+ if (drawable_ID > 0)
+ {
+ GimpPreview *gimp_preview = GIMP_PREVIEW (preview);
+ gint src_x;
+ gint src_y;
+ gint src_width;
+ gint src_height;
+
+ *width = gimp_preview->width;
+ *height = gimp_preview->height;
+
+ gimp_zoom_preview_get_source_area (gimp_preview,
+ &src_x, &src_y,
+ &src_width, &src_height);
+
+ return gimp_drawable_get_sub_thumbnail_data (drawable_ID,
+ src_x, src_y,
+ src_width, src_height,
+ width, height, bpp);
+ }
+ else
+ {
+ *width = 0;
+ *height = 0;
+ *bpp = 0;
+
+ return NULL;
+ }
+}
diff --git a/libgimp/gimpzoompreview.h b/libgimp/gimpzoompreview.h
new file mode 100644
index 0000000..5fdf228
--- /dev/null
+++ b/libgimp/gimpzoompreview.h
@@ -0,0 +1,95 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpzoompreview.h
+ * Copyright (C) 2005 David Odin <dindinx@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_UI_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimpui.h> can be included directly."
+#endif
+
+#ifndef __GIMP_ZOOM_PREVIEW_H__
+#define __GIMP_ZOOM_PREVIEW_H__
+
+G_BEGIN_DECLS
+
+
+/* For information look into the C source or the html documentation */
+
+
+#define GIMP_TYPE_ZOOM_PREVIEW (gimp_zoom_preview_get_type ())
+#define GIMP_ZOOM_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_ZOOM_PREVIEW, GimpZoomPreview))
+#define GIMP_ZOOM_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_ZOOM_PREVIEW, GimpZoomPreviewClass))
+#define GIMP_IS_ZOOM_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_ZOOM_PREVIEW))
+#define GIMP_IS_ZOOM_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_ZOOM_PREVIEW))
+#define GIMP_ZOOM_PREVIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_ZOOM_PREVIEW, GimpZoomPreviewClass))
+
+
+typedef struct _GimpZoomPreviewPrivate GimpZoomPreviewPrivate;
+typedef struct _GimpZoomPreviewClass GimpZoomPreviewClass;
+
+struct _GimpZoomPreview
+{
+ GimpScrolledPreview parent_instance;
+
+ /*< private >*/
+ GimpZoomPreviewPrivate *priv;
+};
+
+struct _GimpZoomPreviewClass
+{
+ GimpScrolledPreviewClass parent_class;
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_zoom_preview_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_zoom_preview_new_from_drawable_id
+ (gint32 drawable_ID);
+GtkWidget * gimp_zoom_preview_new_with_model_from_drawable_id
+ (gint32 drawable_ID,
+ GimpZoomModel *model);
+
+guchar * gimp_zoom_preview_get_source (GimpZoomPreview *preview,
+ gint *width,
+ gint *height,
+ gint *bpp);
+
+gint32 gimp_zoom_preview_get_drawable_id(GimpZoomPreview *preview);
+GimpZoomModel * gimp_zoom_preview_get_model (GimpZoomPreview *preview);
+gdouble gimp_zoom_preview_get_factor (GimpZoomPreview *preview);
+
+GIMP_DEPRECATED_FOR(gimp_zoom_preview_new_from_drawable_id)
+GtkWidget * gimp_zoom_preview_new (GimpDrawable *drawable);
+GIMP_DEPRECATED_FOR(gimp_zoom_preview_new_with_model_from_drawable_id)
+GtkWidget * gimp_zoom_preview_new_with_model (GimpDrawable *drawable,
+ GimpZoomModel *model);
+
+GIMP_DEPRECATED_FOR(gimp_zoom_preview_get_drawable_id)
+GimpDrawable * gimp_zoom_preview_get_drawable (GimpZoomPreview *preview);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_ZOOM_PREVIEW_H__ */
diff --git a/libgimp/libgimp-intl.h b/libgimp/libgimp-intl.h
new file mode 100644
index 0000000..2ae01d9
--- /dev/null
+++ b/libgimp/libgimp-intl.h
@@ -0,0 +1,45 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * libgimp-intl.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LIBGIMP_INTL_H__
+#define __LIBGIMP_INTL_H__
+
+#ifndef GETTEXT_PACKAGE
+#error "config.h must be included prior to libgimp-intl.h"
+#endif
+
+#include <libintl.h>
+
+
+#define _(String) dgettext (GETTEXT_PACKAGE "-libgimp", String)
+#define Q_(String) g_dpgettext (GETTEXT_PACKAGE "-libgimp", String, 0)
+#define C_(Context,String) g_dpgettext (GETTEXT_PACKAGE "-libgimp", Context "\004" String, strlen (Context) + 1)
+
+#undef gettext
+#define gettext(String) dgettext (GETTEXT_PACKAGE "-libgimp", String)
+
+#undef ngettext
+#define ngettext(String1, String2, number) dngettext (GETTEXT_PACKAGE "-libgimp", String1, String2, number)
+
+#define N_(String) (String)
+#define NC_(Context,String) (String)
+
+
+#endif /* __LIBGIMP_INTL_H__ */
diff --git a/libgimp/stdplugins-intl.h b/libgimp/stdplugins-intl.h
new file mode 100644
index 0000000..6aad80e
--- /dev/null
+++ b/libgimp/stdplugins-intl.h
@@ -0,0 +1,42 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * stdplugins-intl.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __STDPLUGINS_INTL_H__
+#define __STDPLUGINS_INTL_H__
+
+#ifndef GETTEXT_PACKAGE
+#error "config.h must be included prior to stdplugins-intl.h"
+#endif
+
+#include <glib/gi18n.h>
+
+#ifndef HAVE_BIND_TEXTDOMAIN_CODESET
+# define bind_textdomain_codeset(Domain, Codeset) (Domain)
+#endif
+
+#define INIT_I18N() G_STMT_START{ \
+ bindtextdomain (GETTEXT_PACKAGE"-std-plug-ins", \
+ gimp_locale_directory ()); \
+ bind_textdomain_codeset (GETTEXT_PACKAGE"-std-plug-ins", "UTF-8"); \
+ textdomain (GETTEXT_PACKAGE"-std-plug-ins"); \
+}G_STMT_END
+
+
+#endif /* __STDPLUGINS_INTL_H__ */
diff --git a/libgimpbase/Makefile.am b/libgimpbase/Makefile.am
new file mode 100644
index 0000000..4214939
--- /dev/null
+++ b/libgimpbase/Makefile.am
@@ -0,0 +1,264 @@
+## Process this file with automake to produce Makefile.in
+
+if PLATFORM_WIN32
+no_undefined = -no-undefined
+endif
+
+if PLATFORM_OSX
+xobjective_c = "-xobjective-c"
+xobjective_cxx = "-xobjective-c++"
+xnone = "-xnone"
+framework_cocoa = -framework Cocoa
+endif
+
+if OS_WIN32
+ole32_lib = -lole32
+gimpbase_def = gimpbase.def
+libgimpbase_export_symbols = -export-symbols $(srcdir)/gimpbase.def
+
+install-libtool-import-lib:
+ $(INSTALL) .libs/libgimpbase-$(GIMP_API_VERSION).dll.a $(DESTDIR)$(libdir)
+ $(INSTALL) $(srcdir)/gimpbase.def $(DESTDIR)$(libdir)
+
+uninstall-libtool-import-lib:
+ -rm $(DESTDIR)$(libdir)/libgimpbase-$(GIMP_API_VERSION).dll.a
+ -rm $(DESTDIR)$(libdir)/gimpbase.def
+else
+libm = -lm
+install-libtool-import-lib:
+uninstall-libtool-import-lib:
+endif
+
+if MS_LIB_AVAILABLE
+noinst_DATA = gimpbase-$(GIMP_API_VERSION).lib
+
+install-ms-lib:
+ $(INSTALL) gimpbase-$(GIMP_API_VERSION).lib $(DESTDIR)$(libdir)
+
+uninstall-ms-lib:
+ -rm $(DESTDIR)$(libdir)/gimpbase-$(GIMP_API_VERSION).lib
+
+gimpbase-@GIMP_API_VERSION@.lib: gimpbase.def
+ lib -name:libgimpbase-$(GIMP_API_VERSION)-@LT_CURRENT_MINUS_AGE@.dll -def:gimpbase.def -out:$@
+
+else
+install-ms-lib:
+uninstall-ms-lib:
+endif
+
+libgimpbaseincludedir = $(includedir)/gimp-$(GIMP_API_VERSION)/libgimpbase
+
+AM_CPPFLAGS = \
+ -DPREFIX=\""$(prefix)"\" \
+ -DGIMPDIR=\""$(gimpdir)"\" \
+ -DGIMPDATADIR=\""$(gimpdatadir)"\" \
+ -DLOCALEDIR=\""$(gimplocaledir)"\" \
+ -DPLUGINDIR=\""$(gimpplugindir)"\" \
+ -DGIMPSYSCONFDIR=\""$(gimpsysconfdir)"\" \
+ -DGIMP_PACKAGE=\""@PACKAGE@"\" \
+ -DGIMP_DATA_VERSION=\"$(GIMP_DATA_VERSION)\" \
+ -DGIMP_USER_VERSION=\"$(GIMP_USER_VERSION)\" \
+ -DGIMP_SYSCONF_VERSION=\"$(GIMP_SYSCONF_VERSION)\" \
+ -DGIMP_PLUGIN_VERSION=\"$(GIMP_PLUGIN_VERSION)\" \
+ -DG_LOG_DOMAIN=\"LibGimpBase\" \
+ -DGIMP_BASE_COMPILATION \
+ -I$(top_srcdir) \
+ $(GIO_CFLAGS) \
+ $(GEXIV2_CFLAGS) \
+ -I$(includedir)
+
+AM_CFLAGS = \
+ $(xobjective_c)
+
+AM_CXXFLAGS = \
+ $(xobjective_cxx)
+
+AM_CCASFLAGS = \
+ -I$(top_builddir) \
+ -I$(top_srcdir)
+
+AM_LDFLAGS = \
+ $(xnone)
+
+EXTRA_DIST = \
+ gimpbase.def
+
+lib_LTLIBRARIES = libgimpbase-@GIMP_API_VERSION@.la
+
+# help `make' along by giving another name for the file, which it knows
+# how to build
+../libgimpbase/gimpversion.h: gimpversion.h
+ @:
+
+
+libgimpbase_sources = \
+ gimpbase.h \
+ gimpbaseenums.h \
+ gimpcompatenums.h \
+ gimpbasetypes.h \
+ gimpbasetypes.c \
+ gimplimits.h \
+ gimpparam.h \
+ gimpversion.h \
+ \
+ gimpbase-private.c \
+ gimpbase-private.h \
+ gimpchecks.c \
+ gimpchecks.h \
+ gimpcpuaccel.c \
+ gimpcpuaccel.h \
+ gimpdatafiles.c \
+ gimpdatafiles.h \
+ gimpenv.c \
+ gimpenv.h \
+ gimpmemsize.c \
+ gimpmemsize.h \
+ gimpmetadata.c \
+ gimpmetadata.h \
+ gimpparasite.c \
+ gimpparasite.h \
+ gimpparasiteio.c \
+ gimpparasiteio.h \
+ gimpprotocol.c \
+ gimpprotocol.h \
+ gimprectangle.c \
+ gimprectangle.h \
+ gimpreloc.c \
+ gimpreloc.h \
+ gimpsignal.c \
+ gimpsignal.h \
+ gimpunit.c \
+ gimpunit.h \
+ gimputils.c \
+ gimputils.h \
+ gimpvaluearray.c \
+ gimpvaluearray.h \
+ gimpwin32-io.h \
+ gimpwire.c \
+ gimpwire.h
+
+libgimpbase_built_sources = \
+ gimpbaseenums.c \
+ gimpcompatenums.c
+
+libgimpbase_@GIMP_API_VERSION@_la_SOURCES = \
+ $(libgimpbase_sources) \
+ $(libgimpbase_built_sources)
+
+
+libgimpbaseinclude_HEADERS = \
+ gimpbase.h \
+ gimpbaseenums.h \
+ gimpbasetypes.h \
+ gimpcpuaccel.h \
+ gimplimits.h \
+ gimpparam.h \
+ gimpversion.h \
+ \
+ gimpchecks.h \
+ gimpdatafiles.h \
+ gimpenv.h \
+ gimpmemsize.h \
+ gimpmetadata.h \
+ gimpparasite.h \
+ gimpparasiteio.h \
+ gimprectangle.h \
+ gimpsignal.h \
+ gimpunit.h \
+ gimputils.h \
+ gimpvaluearray.h
+
+libgimpbase_@GIMP_API_VERSION@_la_LDFLAGS = \
+ -version-info $(LT_VERSION_INFO) \
+ $(no_undefined) \
+ $(libgimpbase_export_symbols) \
+ $(framework_cocoa) \
+ $(xnone)
+
+EXTRA_libgimpbase_@GIMP_API_VERSION@_la_DEPENDENCIES = $(gimpbase_def)
+
+libgimpbase_@GIMP_API_VERSION@_la_LIBADD = \
+ $(GIO_LIBS) \
+ $(GEXIV2_LIBS) \
+ $(libm) \
+ $(ole32_lib)
+
+install-data-local: install-ms-lib install-libtool-import-lib
+
+uninstall-local: uninstall-ms-lib uninstall-libtool-import-lib
+
+#
+# test programs, not to be built by default and never installed
+#
+
+TESTS = test-cpu-accel
+
+test_cpu_accel_SOURCES = test-cpu-accel.c
+
+test_cpu_accel_DEPENDENCIES = \
+ $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+
+test_cpu_accel_LDADD = \
+ $(GLIB_LIBS) \
+ $(test_cpu_accel_DEPENDENCIES)
+
+
+EXTRA_PROGRAMS = test-cpu-accel
+
+
+#
+# rules to generate built sources
+#
+
+gen_sources = xgen-bec xgen-cec
+CLEANFILES = $(EXTRA_PROGRAMS) $(gen_sources)
+
+xgen-bec: $(srcdir)/gimpbaseenums.h $(GIMP_MKENUMS) Makefile.am
+ $(AM_V_GEN) $(GIMP_MKENUMS) \
+ --fhead "#include \"config.h\"\n#include <glib-object.h>\n#undef GIMP_DISABLE_DEPRECATED\n#include \"gimpbasetypes.h\"\n#include \"libgimp/libgimp-intl.h\"" \
+ --fprod "\n/* enumerations from \"@basename@\" */" \
+ --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \
+ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \
+ --vtail " { 0, NULL, NULL }\n };\n" \
+ --dhead " static const Gimp@Type@Desc descs[] =\n {" \
+ --dprod " { @VALUENAME@, @valuedesc@, @valuehelp@ },@if ('@valueabbrev@' ne 'NULL')@\n /* Translators: this is an abbreviated version of @valueudesc@.\n Keep it short. */\n { @VALUENAME@, @valueabbrev@, NULL },@endif@" \
+ --dtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n {\n type = g_@type@_register_static (\"@EnumName@\", values);\n gimp_type_set_translation_domain (type, GETTEXT_PACKAGE \"-libgimp\");\n gimp_type_set_translation_context (type, \"@enumnick@\");\n gimp_@type@_set_value_descriptions (type, descs);\n }\n\n return type;\n}\n" \
+ $< > $@
+
+# copy the generated enum file back to the source directory only if it's
+# changed; otherwise, only update its timestamp, so that the recipe isn't
+# executed again on the next build, however, allow this to (harmlessly) fail,
+# to support building from a read-only source tree.
+$(srcdir)/gimpbaseenums.c: xgen-bec
+ $(AM_V_GEN) if ! cmp -s $< $@; then \
+ cp $< $@; \
+ else \
+ touch $@ 2> /dev/null \
+ || true; \
+ fi
+
+xgen-cec: $(srcdir)/gimpcompatenums.h $(GIMP_MKENUMS) Makefile.am
+ $(AM_V_GEN) $(GIMP_MKENUMS) \
+ --fhead "#include \"config.h\"\n#include <glib-object.h>\n#include \"gimpbasetypes.h\"\n#include \"gimpcompatenums.h\"\n#include \"libgimp/libgimp-intl.h\"" \
+ --fprod "\n/* enumerations from \"@basename@\" */" \
+ --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \
+ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \
+ --vtail " { 0, NULL, NULL }\n };\n" \
+ --dhead " static const Gimp@Type@Desc descs[] =\n {" \
+ --dprod " { @VALUENAME@, @valuedesc@, @valuehelp@ },@if ('@valueabbrev@' ne 'NULL')@\n /* Translators: this is an abbreviated version of @valueudesc@.\n Keep it short. */\n { @VALUENAME@, @valueabbrev@, NULL },@endif@" \
+ --dtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n {\n type = g_@type@_register_static (\"@EnumName@\", values);\n gimp_type_set_translation_domain (type, GETTEXT_PACKAGE \"-libgimp\");\n gimp_type_set_translation_context (type, \"@enumnick@\");\n gimp_@type@_set_value_descriptions (type, descs);\n }\n\n return type;\n}\n" \
+ $< > $@
+
+# copy the generated enum file back to the source directory only if it's
+# changed; otherwise, only update its timestamp, so that the recipe isn't
+# executed again on the next build, however, allow this to (harmlessly) fail,
+# to support building from a read-only source tree.
+$(srcdir)/gimpcompatenums.c: xgen-cec
+ $(AM_V_GEN) if ! cmp -s $< $@; then \
+ cp $< $@; \
+ else \
+ touch $@ 2> /dev/null \
+ || true; \
+ fi
+
+DISTCLEANFILES = gimpversion.h
diff --git a/libgimpbase/Makefile.in b/libgimpbase/Makefile.in
new file mode 100644
index 0000000..419ea65
--- /dev/null
+++ b/libgimpbase/Makefile.in
@@ -0,0 +1,1660 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+TESTS = test-cpu-accel$(EXEEXT)
+EXTRA_PROGRAMS = test-cpu-accel$(EXEEXT)
+subdir = libgimpbase
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(libgimpbaseinclude_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(libdir)" \
+ "$(DESTDIR)$(libgimpbaseincludedir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libgimpbase_@GIMP_API_VERSION@_la_DEPENDENCIES = \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am__objects_1 = gimpbasetypes.lo gimpbase-private.lo gimpchecks.lo \
+ gimpcpuaccel.lo gimpdatafiles.lo gimpenv.lo gimpmemsize.lo \
+ gimpmetadata.lo gimpparasite.lo gimpparasiteio.lo \
+ gimpprotocol.lo gimprectangle.lo gimpreloc.lo gimpsignal.lo \
+ gimpunit.lo gimputils.lo gimpvaluearray.lo gimpwire.lo
+am__objects_2 = gimpbaseenums.lo gimpcompatenums.lo
+am_libgimpbase_@GIMP_API_VERSION@_la_OBJECTS = $(am__objects_1) \
+ $(am__objects_2)
+libgimpbase_@GIMP_API_VERSION@_la_OBJECTS = \
+ $(am_libgimpbase_@GIMP_API_VERSION@_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libgimpbase_@GIMP_API_VERSION@_la_LINK = $(LIBTOOL) $(AM_V_lt) \
+ --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libgimpbase_@GIMP_API_VERSION@_la_LDFLAGS) $(LDFLAGS) -o $@
+am_test_cpu_accel_OBJECTS = test-cpu-accel.$(OBJEXT)
+test_cpu_accel_OBJECTS = $(am_test_cpu_accel_OBJECTS)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/gimpbase-private.Plo \
+ ./$(DEPDIR)/gimpbaseenums.Plo ./$(DEPDIR)/gimpbasetypes.Plo \
+ ./$(DEPDIR)/gimpchecks.Plo ./$(DEPDIR)/gimpcompatenums.Plo \
+ ./$(DEPDIR)/gimpcpuaccel.Plo ./$(DEPDIR)/gimpdatafiles.Plo \
+ ./$(DEPDIR)/gimpenv.Plo ./$(DEPDIR)/gimpmemsize.Plo \
+ ./$(DEPDIR)/gimpmetadata.Plo ./$(DEPDIR)/gimpparasite.Plo \
+ ./$(DEPDIR)/gimpparasiteio.Plo ./$(DEPDIR)/gimpprotocol.Plo \
+ ./$(DEPDIR)/gimprectangle.Plo ./$(DEPDIR)/gimpreloc.Plo \
+ ./$(DEPDIR)/gimpsignal.Plo ./$(DEPDIR)/gimpunit.Plo \
+ ./$(DEPDIR)/gimputils.Plo ./$(DEPDIR)/gimpvaluearray.Plo \
+ ./$(DEPDIR)/gimpwire.Plo ./$(DEPDIR)/test-cpu-accel.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libgimpbase_@GIMP_API_VERSION@_la_SOURCES) \
+ $(test_cpu_accel_SOURCES)
+DIST_SOURCES = $(libgimpbase_@GIMP_API_VERSION@_la_SOURCES) \
+ $(test_cpu_accel_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+DATA = $(noinst_DATA)
+HEADERS = $(libgimpbaseinclude_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__tty_colors_dummy = \
+ mgn= red= grn= lgn= blu= brg= std=; \
+ am__color_tests=no
+am__tty_colors = { \
+ $(am__tty_colors_dummy); \
+ if test "X$(AM_COLOR_TESTS)" = Xno; then \
+ am__color_tests=no; \
+ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+ am__color_tests=yes; \
+ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+ am__color_tests=yes; \
+ fi; \
+ if test $$am__color_tests = yes; then \
+ red=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ fi; \
+}
+am__recheck_rx = ^[ ]*:recheck:[ ]*
+am__global_test_result_rx = ^[ ]*:global-test-result:[ ]*
+am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+ recheck = 1; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ { \
+ if ((getline line2 < ($$0 ".log")) < 0) \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+ { \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+ { \
+ break; \
+ } \
+ }; \
+ if (recheck) \
+ print $$0; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+ print "fatal: making $@: " msg | "cat >&2"; \
+ exit 1; \
+} \
+function rst_section(header) \
+{ \
+ print header; \
+ len = length(header); \
+ for (i = 1; i <= len; i = i + 1) \
+ printf "="; \
+ printf "\n\n"; \
+} \
+{ \
+ copy_in_global_log = 1; \
+ global_test_result = "RUN"; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".trs"); \
+ if (line ~ /$(am__global_test_result_rx)/) \
+ { \
+ sub("$(am__global_test_result_rx)", "", line); \
+ sub("[ ]*$$", "", line); \
+ global_test_result = line; \
+ } \
+ else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+ copy_in_global_log = 0; \
+ }; \
+ if (copy_in_global_log) \
+ { \
+ rst_section(global_test_result ": " $$0); \
+ while ((rc = (getline line < ($$0 ".log"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".log"); \
+ print line; \
+ }; \
+ printf "\n"; \
+ }; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+ --color-tests "$$am__color_tests" \
+ --enable-hard-errors "$$am__enable_hard_errors" \
+ --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test. Creates the
+# directory for the log if needed. Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log. Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT. Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup); \
+$(am__vpath_adj_setup) $(am__vpath_adj) \
+$(am__tty_colors); \
+srcdir=$(srcdir); export srcdir; \
+case "$@" in \
+ */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \
+ *) am__odir=.;; \
+esac; \
+test "x$$am__odir" = x"." || test -d "$$am__odir" \
+ || $(MKDIR_P) "$$am__odir" || exit $$?; \
+if test -f "./$$f"; then dir=./; \
+elif test -f "$$f"; then dir=; \
+else dir="$(srcdir)/"; fi; \
+tst=$$dir$$f; log='$@'; \
+if test -n '$(DISABLE_HARD_ERRORS)'; then \
+ am__enable_hard_errors=no; \
+else \
+ am__enable_hard_errors=yes; \
+fi; \
+case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \
+ am__expect_failure=yes;; \
+ *) \
+ am__expect_failure=no;; \
+esac; \
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed). The result is saved in the shell variable
+# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+ bases='$(TEST_LOGS)'; \
+ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+ bases=`echo $$bases`
+AM_TESTSUITE_SUMMARY_HEADER = ' for $(PACKAGE_STRING)'
+RECHECK_LOGS = $(TEST_LOGS)
+AM_RECURSIVE_TARGETS = check recheck
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+ case '$@' in \
+ */*) \
+ case '$*' in \
+ */*) b='$*';; \
+ *) b=`echo '$@' | sed 's/\.log$$//'`; \
+ esac;; \
+ *) \
+ b='$*';; \
+ esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+ $(TEST_LOG_FLAGS)
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp \
+ $(top_srcdir)/test-driver
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+@PLATFORM_WIN32_TRUE@no_undefined = -no-undefined
+@PLATFORM_OSX_TRUE@xobjective_c = "-xobjective-c"
+@PLATFORM_OSX_TRUE@xobjective_cxx = "-xobjective-c++"
+@PLATFORM_OSX_TRUE@xnone = "-xnone"
+@PLATFORM_OSX_TRUE@framework_cocoa = -framework Cocoa
+@OS_WIN32_TRUE@ole32_lib = -lole32
+@OS_WIN32_TRUE@gimpbase_def = gimpbase.def
+@OS_WIN32_TRUE@libgimpbase_export_symbols = -export-symbols $(srcdir)/gimpbase.def
+@OS_WIN32_FALSE@libm = -lm
+@MS_LIB_AVAILABLE_TRUE@noinst_DATA = gimpbase-$(GIMP_API_VERSION).lib
+libgimpbaseincludedir = $(includedir)/gimp-$(GIMP_API_VERSION)/libgimpbase
+AM_CPPFLAGS = \
+ -DPREFIX=\""$(prefix)"\" \
+ -DGIMPDIR=\""$(gimpdir)"\" \
+ -DGIMPDATADIR=\""$(gimpdatadir)"\" \
+ -DLOCALEDIR=\""$(gimplocaledir)"\" \
+ -DPLUGINDIR=\""$(gimpplugindir)"\" \
+ -DGIMPSYSCONFDIR=\""$(gimpsysconfdir)"\" \
+ -DGIMP_PACKAGE=\""@PACKAGE@"\" \
+ -DGIMP_DATA_VERSION=\"$(GIMP_DATA_VERSION)\" \
+ -DGIMP_USER_VERSION=\"$(GIMP_USER_VERSION)\" \
+ -DGIMP_SYSCONF_VERSION=\"$(GIMP_SYSCONF_VERSION)\" \
+ -DGIMP_PLUGIN_VERSION=\"$(GIMP_PLUGIN_VERSION)\" \
+ -DG_LOG_DOMAIN=\"LibGimpBase\" \
+ -DGIMP_BASE_COMPILATION \
+ -I$(top_srcdir) \
+ $(GIO_CFLAGS) \
+ $(GEXIV2_CFLAGS) \
+ -I$(includedir)
+
+AM_CFLAGS = \
+ $(xobjective_c)
+
+AM_CXXFLAGS = \
+ $(xobjective_cxx)
+
+AM_CCASFLAGS = \
+ -I$(top_builddir) \
+ -I$(top_srcdir)
+
+AM_LDFLAGS = \
+ $(xnone)
+
+EXTRA_DIST = \
+ gimpbase.def
+
+lib_LTLIBRARIES = libgimpbase-@GIMP_API_VERSION@.la
+libgimpbase_sources = \
+ gimpbase.h \
+ gimpbaseenums.h \
+ gimpcompatenums.h \
+ gimpbasetypes.h \
+ gimpbasetypes.c \
+ gimplimits.h \
+ gimpparam.h \
+ gimpversion.h \
+ \
+ gimpbase-private.c \
+ gimpbase-private.h \
+ gimpchecks.c \
+ gimpchecks.h \
+ gimpcpuaccel.c \
+ gimpcpuaccel.h \
+ gimpdatafiles.c \
+ gimpdatafiles.h \
+ gimpenv.c \
+ gimpenv.h \
+ gimpmemsize.c \
+ gimpmemsize.h \
+ gimpmetadata.c \
+ gimpmetadata.h \
+ gimpparasite.c \
+ gimpparasite.h \
+ gimpparasiteio.c \
+ gimpparasiteio.h \
+ gimpprotocol.c \
+ gimpprotocol.h \
+ gimprectangle.c \
+ gimprectangle.h \
+ gimpreloc.c \
+ gimpreloc.h \
+ gimpsignal.c \
+ gimpsignal.h \
+ gimpunit.c \
+ gimpunit.h \
+ gimputils.c \
+ gimputils.h \
+ gimpvaluearray.c \
+ gimpvaluearray.h \
+ gimpwin32-io.h \
+ gimpwire.c \
+ gimpwire.h
+
+libgimpbase_built_sources = \
+ gimpbaseenums.c \
+ gimpcompatenums.c
+
+libgimpbase_@GIMP_API_VERSION@_la_SOURCES = \
+ $(libgimpbase_sources) \
+ $(libgimpbase_built_sources)
+
+libgimpbaseinclude_HEADERS = \
+ gimpbase.h \
+ gimpbaseenums.h \
+ gimpbasetypes.h \
+ gimpcpuaccel.h \
+ gimplimits.h \
+ gimpparam.h \
+ gimpversion.h \
+ \
+ gimpchecks.h \
+ gimpdatafiles.h \
+ gimpenv.h \
+ gimpmemsize.h \
+ gimpmetadata.h \
+ gimpparasite.h \
+ gimpparasiteio.h \
+ gimprectangle.h \
+ gimpsignal.h \
+ gimpunit.h \
+ gimputils.h \
+ gimpvaluearray.h
+
+libgimpbase_@GIMP_API_VERSION@_la_LDFLAGS = \
+ -version-info $(LT_VERSION_INFO) \
+ $(no_undefined) \
+ $(libgimpbase_export_symbols) \
+ $(framework_cocoa) \
+ $(xnone)
+
+EXTRA_libgimpbase_@GIMP_API_VERSION@_la_DEPENDENCIES = $(gimpbase_def)
+libgimpbase_@GIMP_API_VERSION@_la_LIBADD = \
+ $(GIO_LIBS) \
+ $(GEXIV2_LIBS) \
+ $(libm) \
+ $(ole32_lib)
+
+test_cpu_accel_SOURCES = test-cpu-accel.c
+test_cpu_accel_DEPENDENCIES = \
+ $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+
+test_cpu_accel_LDADD = \
+ $(GLIB_LIBS) \
+ $(test_cpu_accel_DEPENDENCIES)
+
+
+#
+# rules to generate built sources
+#
+gen_sources = xgen-bec xgen-cec
+CLEANFILES = $(EXTRA_PROGRAMS) $(gen_sources)
+DISTCLEANFILES = gimpversion.h
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu libgimpbase/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu libgimpbase/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libgimpbase-@GIMP_API_VERSION@.la: $(libgimpbase_@GIMP_API_VERSION@_la_OBJECTS) $(libgimpbase_@GIMP_API_VERSION@_la_DEPENDENCIES) $(EXTRA_libgimpbase_@GIMP_API_VERSION@_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libgimpbase_@GIMP_API_VERSION@_la_LINK) -rpath $(libdir) $(libgimpbase_@GIMP_API_VERSION@_la_OBJECTS) $(libgimpbase_@GIMP_API_VERSION@_la_LIBADD) $(LIBS)
+
+test-cpu-accel$(EXEEXT): $(test_cpu_accel_OBJECTS) $(test_cpu_accel_DEPENDENCIES) $(EXTRA_test_cpu_accel_DEPENDENCIES)
+ @rm -f test-cpu-accel$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_cpu_accel_OBJECTS) $(test_cpu_accel_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbase-private.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbaseenums.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbasetypes.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpchecks.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcompatenums.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcpuaccel.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdatafiles.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpenv.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpmemsize.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpmetadata.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpparasite.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpparasiteio.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpprotocol.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimprectangle.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpreloc.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpsignal.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpunit.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimputils.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpvaluearray.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpwire.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-cpu-accel.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-libgimpbaseincludeHEADERS: $(libgimpbaseinclude_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(libgimpbaseinclude_HEADERS)'; test -n "$(libgimpbaseincludedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libgimpbaseincludedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libgimpbaseincludedir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(libgimpbaseincludedir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(libgimpbaseincludedir)" || exit $$?; \
+ done
+
+uninstall-libgimpbaseincludeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libgimpbaseinclude_HEADERS)'; test -n "$(libgimpbaseincludedir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(libgimpbaseincludedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+ rm -f $< $@
+ $(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+ @:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+ @$(am__set_TESTS_bases); \
+ am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+ redo_bases=`for i in $$bases; do \
+ am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+ done`; \
+ if test -n "$$redo_bases"; then \
+ redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+ redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+ if $(am__make_dryrun); then :; else \
+ rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
+ fi; \
+ fi; \
+ if test -n "$$am__remaking_logs"; then \
+ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+ "recursion detected" >&2; \
+ elif test -n "$$redo_logs"; then \
+ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+ fi; \
+ if $(am__make_dryrun); then :; else \
+ st=0; \
+ errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+ for i in $$redo_bases; do \
+ test -f $$i.trs && test -r $$i.trs \
+ || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+ test -f $$i.log && test -r $$i.log \
+ || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+ done; \
+ test $$st -eq 0 || exit 1; \
+ fi
+ @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+ ws='[ ]'; \
+ results=`for b in $$bases; do echo $$b.trs; done`; \
+ test -n "$$results" || results=/dev/null; \
+ all=` grep "^$$ws*:test-result:" $$results | wc -l`; \
+ pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \
+ fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \
+ skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \
+ xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+ xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+ error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+ if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+ success=true; \
+ else \
+ success=false; \
+ fi; \
+ br='==================='; br=$$br$$br$$br$$br; \
+ result_count () \
+ { \
+ if test x"$$1" = x"--maybe-color"; then \
+ maybe_colorize=yes; \
+ elif test x"$$1" = x"--no-color"; then \
+ maybe_colorize=no; \
+ else \
+ echo "$@: invalid 'result_count' usage" >&2; exit 4; \
+ fi; \
+ shift; \
+ desc=$$1 count=$$2; \
+ if test $$maybe_colorize = yes && test $$count -gt 0; then \
+ color_start=$$3 color_end=$$std; \
+ else \
+ color_start= color_end=; \
+ fi; \
+ echo "$${color_start}# $$desc $$count$${color_end}"; \
+ }; \
+ create_testsuite_report () \
+ { \
+ result_count $$1 "TOTAL:" $$all "$$brg"; \
+ result_count $$1 "PASS: " $$pass "$$grn"; \
+ result_count $$1 "SKIP: " $$skip "$$blu"; \
+ result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+ result_count $$1 "FAIL: " $$fail "$$red"; \
+ result_count $$1 "XPASS:" $$xpass "$$red"; \
+ result_count $$1 "ERROR:" $$error "$$mgn"; \
+ }; \
+ { \
+ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \
+ $(am__rst_title); \
+ create_testsuite_report --no-color; \
+ echo; \
+ echo ".. contents:: :depth: 2"; \
+ echo; \
+ for b in $$bases; do echo $$b; done \
+ | $(am__create_global_log); \
+ } >$(TEST_SUITE_LOG).tmp || exit 1; \
+ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \
+ if $$success; then \
+ col="$$grn"; \
+ else \
+ col="$$red"; \
+ test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \
+ fi; \
+ echo "$${col}$$br$${std}"; \
+ echo "$${col}Testsuite summary"$(AM_TESTSUITE_SUMMARY_HEADER)"$${std}"; \
+ echo "$${col}$$br$${std}"; \
+ create_testsuite_report --maybe-color; \
+ echo "$$col$$br$$std"; \
+ if $$success; then :; else \
+ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \
+ if test -n "$(PACKAGE_BUGREPORT)"; then \
+ echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \
+ fi; \
+ echo "$$col$$br$$std"; \
+ fi; \
+ $$success || exit 1
+
+check-TESTS:
+ @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list
+ @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+ log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+ exit $$?;
+recheck: all
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ bases=`for i in $$bases; do echo $$i; done \
+ | $(am__list_recheck_tests)` || exit 1; \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ log_list=`echo $$log_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+ am__force_recheck=am--force-recheck \
+ TEST_LOGS="$$log_list"; \
+ exit $$?
+test-cpu-accel.log: test-cpu-accel$(EXEEXT)
+ @p='test-cpu-accel$(EXEEXT)'; \
+ b='test-cpu-accel'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+ @p='$<'; \
+ $(am__set_b); \
+ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@ @p='$<'; \
+@am__EXEEXT_TRUE@ $(am__set_b); \
+@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT)
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(DATA) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libgimpbaseincludedir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+ -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+ -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+ -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+ -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/gimpbase-private.Plo
+ -rm -f ./$(DEPDIR)/gimpbaseenums.Plo
+ -rm -f ./$(DEPDIR)/gimpbasetypes.Plo
+ -rm -f ./$(DEPDIR)/gimpchecks.Plo
+ -rm -f ./$(DEPDIR)/gimpcompatenums.Plo
+ -rm -f ./$(DEPDIR)/gimpcpuaccel.Plo
+ -rm -f ./$(DEPDIR)/gimpdatafiles.Plo
+ -rm -f ./$(DEPDIR)/gimpenv.Plo
+ -rm -f ./$(DEPDIR)/gimpmemsize.Plo
+ -rm -f ./$(DEPDIR)/gimpmetadata.Plo
+ -rm -f ./$(DEPDIR)/gimpparasite.Plo
+ -rm -f ./$(DEPDIR)/gimpparasiteio.Plo
+ -rm -f ./$(DEPDIR)/gimpprotocol.Plo
+ -rm -f ./$(DEPDIR)/gimprectangle.Plo
+ -rm -f ./$(DEPDIR)/gimpreloc.Plo
+ -rm -f ./$(DEPDIR)/gimpsignal.Plo
+ -rm -f ./$(DEPDIR)/gimpunit.Plo
+ -rm -f ./$(DEPDIR)/gimputils.Plo
+ -rm -f ./$(DEPDIR)/gimpvaluearray.Plo
+ -rm -f ./$(DEPDIR)/gimpwire.Plo
+ -rm -f ./$(DEPDIR)/test-cpu-accel.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-data-local install-libgimpbaseincludeHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/gimpbase-private.Plo
+ -rm -f ./$(DEPDIR)/gimpbaseenums.Plo
+ -rm -f ./$(DEPDIR)/gimpbasetypes.Plo
+ -rm -f ./$(DEPDIR)/gimpchecks.Plo
+ -rm -f ./$(DEPDIR)/gimpcompatenums.Plo
+ -rm -f ./$(DEPDIR)/gimpcpuaccel.Plo
+ -rm -f ./$(DEPDIR)/gimpdatafiles.Plo
+ -rm -f ./$(DEPDIR)/gimpenv.Plo
+ -rm -f ./$(DEPDIR)/gimpmemsize.Plo
+ -rm -f ./$(DEPDIR)/gimpmetadata.Plo
+ -rm -f ./$(DEPDIR)/gimpparasite.Plo
+ -rm -f ./$(DEPDIR)/gimpparasiteio.Plo
+ -rm -f ./$(DEPDIR)/gimpprotocol.Plo
+ -rm -f ./$(DEPDIR)/gimprectangle.Plo
+ -rm -f ./$(DEPDIR)/gimpreloc.Plo
+ -rm -f ./$(DEPDIR)/gimpsignal.Plo
+ -rm -f ./$(DEPDIR)/gimpunit.Plo
+ -rm -f ./$(DEPDIR)/gimputils.Plo
+ -rm -f ./$(DEPDIR)/gimpvaluearray.Plo
+ -rm -f ./$(DEPDIR)/gimpwire.Plo
+ -rm -f ./$(DEPDIR)/test-cpu-accel.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libLTLIBRARIES \
+ uninstall-libgimpbaseincludeHEADERS uninstall-local
+
+.MAKE: check-am install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-TESTS \
+ check-am clean clean-generic clean-libLTLIBRARIES \
+ clean-libtool cscopelist-am ctags ctags-am distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am \
+ install-data-local install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-libLTLIBRARIES \
+ install-libgimpbaseincludeHEADERS install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ recheck tags tags-am uninstall uninstall-am \
+ uninstall-libLTLIBRARIES uninstall-libgimpbaseincludeHEADERS \
+ uninstall-local
+
+.PRECIOUS: Makefile
+
+
+@OS_WIN32_TRUE@install-libtool-import-lib:
+@OS_WIN32_TRUE@ $(INSTALL) .libs/libgimpbase-$(GIMP_API_VERSION).dll.a $(DESTDIR)$(libdir)
+@OS_WIN32_TRUE@ $(INSTALL) $(srcdir)/gimpbase.def $(DESTDIR)$(libdir)
+
+@OS_WIN32_TRUE@uninstall-libtool-import-lib:
+@OS_WIN32_TRUE@ -rm $(DESTDIR)$(libdir)/libgimpbase-$(GIMP_API_VERSION).dll.a
+@OS_WIN32_TRUE@ -rm $(DESTDIR)$(libdir)/gimpbase.def
+@OS_WIN32_FALSE@install-libtool-import-lib:
+@OS_WIN32_FALSE@uninstall-libtool-import-lib:
+
+@MS_LIB_AVAILABLE_TRUE@install-ms-lib:
+@MS_LIB_AVAILABLE_TRUE@ $(INSTALL) gimpbase-$(GIMP_API_VERSION).lib $(DESTDIR)$(libdir)
+
+@MS_LIB_AVAILABLE_TRUE@uninstall-ms-lib:
+@MS_LIB_AVAILABLE_TRUE@ -rm $(DESTDIR)$(libdir)/gimpbase-$(GIMP_API_VERSION).lib
+
+@MS_LIB_AVAILABLE_TRUE@gimpbase-@GIMP_API_VERSION@.lib: gimpbase.def
+@MS_LIB_AVAILABLE_TRUE@ lib -name:libgimpbase-$(GIMP_API_VERSION)-@LT_CURRENT_MINUS_AGE@.dll -def:gimpbase.def -out:$@
+
+@MS_LIB_AVAILABLE_FALSE@install-ms-lib:
+@MS_LIB_AVAILABLE_FALSE@uninstall-ms-lib:
+
+# help `make' along by giving another name for the file, which it knows
+# how to build
+../libgimpbase/gimpversion.h: gimpversion.h
+ @:
+
+install-data-local: install-ms-lib install-libtool-import-lib
+
+uninstall-local: uninstall-ms-lib uninstall-libtool-import-lib
+
+xgen-bec: $(srcdir)/gimpbaseenums.h $(GIMP_MKENUMS) Makefile.am
+ $(AM_V_GEN) $(GIMP_MKENUMS) \
+ --fhead "#include \"config.h\"\n#include <glib-object.h>\n#undef GIMP_DISABLE_DEPRECATED\n#include \"gimpbasetypes.h\"\n#include \"libgimp/libgimp-intl.h\"" \
+ --fprod "\n/* enumerations from \"@basename@\" */" \
+ --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \
+ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \
+ --vtail " { 0, NULL, NULL }\n };\n" \
+ --dhead " static const Gimp@Type@Desc descs[] =\n {" \
+ --dprod " { @VALUENAME@, @valuedesc@, @valuehelp@ },@if ('@valueabbrev@' ne 'NULL')@\n /* Translators: this is an abbreviated version of @valueudesc@.\n Keep it short. */\n { @VALUENAME@, @valueabbrev@, NULL },@endif@" \
+ --dtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n {\n type = g_@type@_register_static (\"@EnumName@\", values);\n gimp_type_set_translation_domain (type, GETTEXT_PACKAGE \"-libgimp\");\n gimp_type_set_translation_context (type, \"@enumnick@\");\n gimp_@type@_set_value_descriptions (type, descs);\n }\n\n return type;\n}\n" \
+ $< > $@
+
+# copy the generated enum file back to the source directory only if it's
+# changed; otherwise, only update its timestamp, so that the recipe isn't
+# executed again on the next build, however, allow this to (harmlessly) fail,
+# to support building from a read-only source tree.
+$(srcdir)/gimpbaseenums.c: xgen-bec
+ $(AM_V_GEN) if ! cmp -s $< $@; then \
+ cp $< $@; \
+ else \
+ touch $@ 2> /dev/null \
+ || true; \
+ fi
+
+xgen-cec: $(srcdir)/gimpcompatenums.h $(GIMP_MKENUMS) Makefile.am
+ $(AM_V_GEN) $(GIMP_MKENUMS) \
+ --fhead "#include \"config.h\"\n#include <glib-object.h>\n#include \"gimpbasetypes.h\"\n#include \"gimpcompatenums.h\"\n#include \"libgimp/libgimp-intl.h\"" \
+ --fprod "\n/* enumerations from \"@basename@\" */" \
+ --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \
+ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \
+ --vtail " { 0, NULL, NULL }\n };\n" \
+ --dhead " static const Gimp@Type@Desc descs[] =\n {" \
+ --dprod " { @VALUENAME@, @valuedesc@, @valuehelp@ },@if ('@valueabbrev@' ne 'NULL')@\n /* Translators: this is an abbreviated version of @valueudesc@.\n Keep it short. */\n { @VALUENAME@, @valueabbrev@, NULL },@endif@" \
+ --dtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n {\n type = g_@type@_register_static (\"@EnumName@\", values);\n gimp_type_set_translation_domain (type, GETTEXT_PACKAGE \"-libgimp\");\n gimp_type_set_translation_context (type, \"@enumnick@\");\n gimp_@type@_set_value_descriptions (type, descs);\n }\n\n return type;\n}\n" \
+ $< > $@
+
+# copy the generated enum file back to the source directory only if it's
+# changed; otherwise, only update its timestamp, so that the recipe isn't
+# executed again on the next build, however, allow this to (harmlessly) fail,
+# to support building from a read-only source tree.
+$(srcdir)/gimpcompatenums.c: xgen-cec
+ $(AM_V_GEN) if ! cmp -s $< $@; then \
+ cp $< $@; \
+ else \
+ touch $@ 2> /dev/null \
+ || true; \
+ fi
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libgimpbase/gimpbase-private.c b/libgimpbase/gimpbase-private.c
new file mode 100644
index 0000000..7703fce
--- /dev/null
+++ b/libgimpbase/gimpbase-private.c
@@ -0,0 +1,93 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpbase-private.c
+ * Copyright (C) 2003 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include "gimpbasetypes.h"
+
+#include "gimpbase-private.h"
+#include "gimpcompatenums.h"
+
+
+GimpUnitVtable _gimp_unit_vtable = { NULL, };
+
+
+void
+gimp_base_init (GimpUnitVtable *vtable)
+{
+ static gboolean gimp_base_initialized = FALSE;
+
+ g_return_if_fail (vtable != NULL);
+
+ if (gimp_base_initialized)
+ g_error ("gimp_base_init() must only be called once!");
+
+ _gimp_unit_vtable = *vtable;
+
+ gimp_base_compat_enums_init ();
+
+ gimp_base_initialized = TRUE;
+}
+
+void
+gimp_base_compat_enums_init (void)
+{
+ static gboolean gimp_base_compat_initialized = FALSE;
+ GQuark quark;
+
+ if (gimp_base_compat_initialized)
+ return;
+
+ quark = g_quark_from_static_string ("gimp-compat-enum");
+
+ g_type_set_qdata (GIMP_TYPE_ADD_MASK_TYPE, quark,
+ (gpointer) GIMP_TYPE_ADD_MASK_TYPE_COMPAT);
+ g_type_set_qdata (GIMP_TYPE_BLEND_MODE, quark,
+ (gpointer) GIMP_TYPE_BLEND_MODE_COMPAT);
+ g_type_set_qdata (GIMP_TYPE_BUCKET_FILL_MODE, quark,
+ (gpointer) GIMP_TYPE_BUCKET_FILL_MODE_COMPAT);
+ g_type_set_qdata (GIMP_TYPE_CHANNEL_TYPE, quark,
+ (gpointer) GIMP_TYPE_CHANNEL_TYPE_COMPAT);
+ g_type_set_qdata (GIMP_TYPE_CLONE_TYPE, quark,
+ (gpointer) GIMP_TYPE_CLONE_TYPE_COMPAT);
+ g_type_set_qdata (GIMP_TYPE_CONVERT_PALETTE_TYPE, quark,
+ (gpointer) GIMP_TYPE_CONVERT_PALETTE_TYPE_COMPAT);
+ g_type_set_qdata (GIMP_TYPE_CONVOLVE_TYPE, quark,
+ (gpointer) GIMP_TYPE_CONVOLVE_TYPE_COMPAT);
+ g_type_set_qdata (GIMP_TYPE_DESATURATE_MODE, quark,
+ (gpointer) GIMP_TYPE_DESATURATE_MODE_COMPAT);
+ g_type_set_qdata (GIMP_TYPE_DODGE_BURN_TYPE, quark,
+ (gpointer) GIMP_TYPE_DODGE_BURN_TYPE_COMPAT);
+ g_type_set_qdata (GIMP_TYPE_FILL_TYPE, quark,
+ (gpointer) GIMP_TYPE_FILL_TYPE_COMPAT);
+ g_type_set_qdata (GIMP_TYPE_HUE_RANGE, quark,
+ (gpointer) GIMP_TYPE_HUE_RANGE_COMPAT);
+ g_type_set_qdata (GIMP_TYPE_ICON_TYPE, quark,
+ (gpointer) GIMP_TYPE_ICON_TYPE_COMPAT);
+ g_type_set_qdata (GIMP_TYPE_INTERPOLATION_TYPE, quark,
+ (gpointer) GIMP_TYPE_INTERPOLATION_TYPE_COMPAT);
+ g_type_set_qdata (GIMP_TYPE_TRANSFER_MODE, quark,
+ (gpointer) GIMP_TYPE_TRANSFER_MODE_COMPAT);
+
+ gimp_base_compat_initialized = TRUE;
+}
diff --git a/libgimpbase/gimpbase-private.h b/libgimpbase/gimpbase-private.h
new file mode 100644
index 0000000..a36b4a7
--- /dev/null
+++ b/libgimpbase/gimpbase-private.h
@@ -0,0 +1,69 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpbase-private.h
+ * Copyright (C) 2003 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_BASE_PRIVATE_H__
+#define __GIMP_BASE_PRIVATE_H__
+
+
+typedef struct _GimpUnitVtable GimpUnitVtable;
+
+struct _GimpUnitVtable
+{
+ gint (* unit_get_number_of_units) (void);
+ gint (* unit_get_number_of_built_in_units) (void);
+
+ GimpUnit (* unit_new) (gchar *identifier,
+ gdouble factor,
+ gint digits,
+ gchar *symbol,
+ gchar *abbreviation,
+ gchar *singular,
+ gchar *plural);
+ gboolean (* unit_get_deletion_flag) (GimpUnit unit);
+ void (* unit_set_deletion_flag) (GimpUnit unit,
+ gboolean deletion_flag);
+
+ gdouble (* unit_get_factor) (GimpUnit unit);
+ gint (* unit_get_digits) (GimpUnit unit);
+ const gchar * (* unit_get_identifier) (GimpUnit unit);
+ const gchar * (* unit_get_symbol) (GimpUnit unit);
+ const gchar * (* unit_get_abbreviation) (GimpUnit unit);
+ const gchar * (* unit_get_singular) (GimpUnit unit);
+ const gchar * (* unit_get_plural) (GimpUnit unit);
+
+ void (* _reserved_1) (void);
+ void (* _reserved_2) (void);
+ void (* _reserved_3) (void);
+ void (* _reserved_4) (void);
+};
+
+
+extern GimpUnitVtable _gimp_unit_vtable;
+
+
+G_BEGIN_DECLS
+
+void gimp_base_init (GimpUnitVtable *vtable);
+void gimp_base_compat_enums_init (void);
+
+G_END_DECLS
+
+#endif /* __GIMP_BASE_PRIVATE_H__ */
diff --git a/libgimpbase/gimpbase.def b/libgimpbase/gimpbase.def
new file mode 100644
index 0000000..2efc1de
--- /dev/null
+++ b/libgimpbase/gimpbase.def
@@ -0,0 +1,244 @@
+EXPORTS
+ gimp_add_mask_type_compat_get_type
+ gimp_add_mask_type_get_type
+ gimp_any_to_utf8
+ gimp_base_compat_enums_init
+ gimp_base_init
+ gimp_blend_mode_compat_get_type
+ gimp_blend_mode_get_type
+ gimp_brush_generated_shape_get_type
+ gimp_bucket_fill_mode_compat_get_type
+ gimp_bucket_fill_mode_get_type
+ gimp_cache_directory
+ gimp_canonicalize_identifier
+ gimp_cap_style_get_type
+ gimp_channel_ops_get_type
+ gimp_channel_type_compat_get_type
+ gimp_channel_type_get_type
+ gimp_check_size_get_type
+ gimp_check_type_get_type
+ gimp_checks_get_shades
+ gimp_clone_type_compat_get_type
+ gimp_clone_type_get_type
+ gimp_color_tag_get_type
+ gimp_component_type_get_type
+ gimp_convert_dither_type_compat_get_type
+ gimp_convert_palette_type_compat_get_type
+ gimp_convert_palette_type_get_type
+ gimp_convolve_type_compat_get_type
+ gimp_convolve_type_get_type
+ gimp_cpu_accel_get_support
+ gimp_cpu_accel_set_use
+ gimp_data_directory
+ gimp_data_directory_file
+ gimp_datafiles_check_extension
+ gimp_datafiles_read_directories
+ gimp_desaturate_mode_compat_get_type
+ gimp_desaturate_mode_get_type
+ gimp_directory
+ gimp_directory_file
+ gimp_dodge_burn_type_compat_get_type
+ gimp_dodge_burn_type_get_type
+ gimp_enum_get_desc
+ gimp_enum_get_value
+ gimp_enum_get_value_descriptions
+ gimp_enum_set_value_descriptions
+ gimp_enum_value_get_abbrev
+ gimp_enum_value_get_desc
+ gimp_enum_value_get_help
+ gimp_env_init
+ gimp_escape_uline
+ gimp_file_get_utf8_name
+ gimp_file_has_extension
+ gimp_file_show_in_file_manager
+ gimp_filename_to_utf8
+ gimp_fill_type_compat_get_type
+ gimp_fill_type_get_type
+ gimp_flags_get_first_desc
+ gimp_flags_get_first_value
+ gimp_flags_get_value_descriptions
+ gimp_flags_set_value_descriptions
+ gimp_flags_value_get_abbrev
+ gimp_flags_value_get_desc
+ gimp_flags_value_get_help
+ gimp_foreground_extract_mode_get_type
+ gimp_gradient_blend_color_space_get_type
+ gimp_gradient_segment_color_get_type
+ gimp_gradient_segment_type_get_type
+ gimp_gradient_type_get_type
+ gimp_grid_style_get_type
+ gimp_gtkrc
+ gimp_hue_range_compat_get_type
+ gimp_hue_range_get_type
+ gimp_icon_type_compat_get_type
+ gimp_icon_type_get_type
+ gimp_image_base_type_get_type
+ gimp_image_type_get_type
+ gimp_ink_blob_type_get_type
+ gimp_installation_directory
+ gimp_installation_directory_file
+ gimp_interpolation_type_compat_get_type
+ gimp_interpolation_type_get_type
+ gimp_join_style_get_type
+ gimp_layer_mode_effects_get_type
+ gimp_locale_directory
+ gimp_locale_directory_file
+ gimp_major_version
+ gimp_mask_apply_mode_get_type
+ gimp_memsize_deserialize
+ gimp_memsize_get_type
+ gimp_memsize_serialize
+ gimp_memsize_to_string
+ gimp_merge_type_get_type
+ gimp_message_handler_type_get_type
+ gimp_metadata_add_xmp_history
+ gimp_metadata_deserialize
+ gimp_metadata_duplicate
+ gimp_metadata_get_colorspace
+ gimp_metadata_get_guid
+ gimp_metadata_get_resolution
+ gimp_metadata_get_type
+ gimp_metadata_is_tag_supported
+ gimp_metadata_load_from_file
+ gimp_metadata_new
+ gimp_metadata_save_to_file
+ gimp_metadata_serialize
+ gimp_metadata_set_bits_per_sample
+ gimp_metadata_set_colorspace
+ gimp_metadata_set_from_exif
+ gimp_metadata_set_from_iptc
+ gimp_metadata_set_from_xmp
+ gimp_metadata_set_pixel_size
+ gimp_metadata_set_resolution
+ gimp_micro_version
+ gimp_minor_version
+ gimp_offset_type_get_type
+ gimp_orientation_type_get_type
+ gimp_paint_application_mode_get_type
+ gimp_param_memsize_get_type
+ gimp_param_parasite_get_type
+ gimp_param_spec_memsize
+ gimp_param_spec_parasite
+ gimp_param_spec_unit
+ gimp_param_spec_value_array
+ gimp_param_unit_get_type
+ gimp_param_value_array_get_type
+ gimp_parasite_compare
+ gimp_parasite_copy
+ gimp_parasite_data
+ gimp_parasite_data_size
+ gimp_parasite_flags
+ gimp_parasite_free
+ gimp_parasite_get_type
+ gimp_parasite_has_flag
+ gimp_parasite_is_persistent
+ gimp_parasite_is_type
+ gimp_parasite_is_undoable
+ gimp_parasite_name
+ gimp_parasite_new
+ gimp_path_free
+ gimp_path_get_user_writable_dir
+ gimp_path_parse
+ gimp_path_to_str
+ gimp_pdb_arg_type_get_type
+ gimp_pdb_error_handler_get_type
+ gimp_pdb_proc_type_get_type
+ gimp_pdb_status_type_get_type
+ gimp_personal_rc_file
+ gimp_pixels_to_units
+ gimp_pixpipe_params_build
+ gimp_pixpipe_params_free
+ gimp_pixpipe_params_init
+ gimp_pixpipe_params_parse
+ gimp_plug_in_directory
+ gimp_plug_in_directory_file
+ gimp_precision_get_type
+ gimp_progress_command_get_type
+ gimp_rectangle_intersect
+ gimp_rectangle_union
+ gimp_repeat_mode_get_type
+ gimp_rotation_type_get_type
+ gimp_run_mode_get_type
+ gimp_select_criterion_get_type
+ gimp_signal_private
+ gimp_size_type_get_type
+ gimp_stack_trace_available
+ gimp_stack_trace_mode_get_type
+ gimp_stack_trace_print
+ gimp_stack_trace_query
+ gimp_strip_uline
+ gimp_stroke_method_get_type
+ gimp_sysconf_directory
+ gimp_sysconf_directory_file
+ gimp_temp_directory
+ gimp_text_direction_get_type
+ gimp_text_hint_style_get_type
+ gimp_text_justification_get_type
+ gimp_transfer_mode_compat_get_type
+ gimp_transfer_mode_get_type
+ gimp_transform_direction_get_type
+ gimp_transform_resize_get_type
+ gimp_type_get_translation_context
+ gimp_type_get_translation_domain
+ gimp_type_set_translation_context
+ gimp_type_set_translation_domain
+ gimp_unit_format_string
+ gimp_unit_get_abbreviation
+ gimp_unit_get_deletion_flag
+ gimp_unit_get_digits
+ gimp_unit_get_factor
+ gimp_unit_get_identifier
+ gimp_unit_get_number_of_built_in_units
+ gimp_unit_get_number_of_units
+ gimp_unit_get_plural
+ gimp_unit_get_scaled_digits
+ gimp_unit_get_singular
+ gimp_unit_get_symbol
+ gimp_unit_get_type
+ gimp_unit_is_metric
+ gimp_unit_new
+ gimp_unit_set_deletion_flag
+ gimp_units_to_pixels
+ gimp_units_to_points
+ gimp_user_directory
+ gimp_user_directory_get_type
+ gimp_utf8_strtrim
+ gimp_value_array_append
+ gimp_value_array_get_type
+ gimp_value_array_index
+ gimp_value_array_insert
+ gimp_value_array_length
+ gimp_value_array_new
+ gimp_value_array_prepend
+ gimp_value_array_ref
+ gimp_value_array_remove
+ gimp_value_array_truncate
+ gimp_value_array_unref
+ gimp_vectors_stroke_type_get_type
+ gimp_wire_clear_error
+ gimp_wire_destroy
+ gimp_wire_error
+ gimp_wire_flush
+ gimp_wire_read
+ gimp_wire_read_msg
+ gimp_wire_register
+ gimp_wire_set_flusher
+ gimp_wire_set_reader
+ gimp_wire_set_writer
+ gimp_wire_write
+ gimp_wire_write_msg
+ gp_config_write
+ gp_extension_ack_write
+ gp_has_init_write
+ gp_init
+ gp_params_destroy
+ gp_proc_install_write
+ gp_proc_return_write
+ gp_proc_run_write
+ gp_proc_uninstall_write
+ gp_quit_write
+ gp_temp_proc_return_write
+ gp_temp_proc_run_write
+ gp_tile_ack_write
+ gp_tile_data_write
+ gp_tile_req_write
diff --git a/libgimpbase/gimpbase.h b/libgimpbase/gimpbase.h
new file mode 100644
index 0000000..2cc1c4f
--- /dev/null
+++ b/libgimpbase/gimpbase.h
@@ -0,0 +1,46 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_BASE_H__
+#define __GIMP_BASE_H__
+
+#define __GIMP_BASE_H_INSIDE__
+
+#include <libgimpbase/gimpbasetypes.h>
+
+#include <libgimpbase/gimpchecks.h>
+#include <libgimpbase/gimpcpuaccel.h>
+#include <libgimpbase/gimpdatafiles.h>
+#include <libgimpbase/gimpenv.h>
+#include <libgimpbase/gimplimits.h>
+#include <libgimpbase/gimpmemsize.h>
+#include <libgimpbase/gimpmetadata.h>
+#include <libgimpbase/gimpparasite.h>
+#include <libgimpbase/gimprectangle.h>
+#include <libgimpbase/gimpunit.h>
+#include <libgimpbase/gimputils.h>
+#include <libgimpbase/gimpversion.h>
+#include <libgimpbase/gimpvaluearray.h>
+
+#ifndef G_OS_WIN32
+#include <libgimpbase/gimpsignal.h>
+#endif
+
+#undef __GIMP_BASE_H_INSIDE__
+
+#endif /* __GIMP_BASE_H__ */
diff --git a/libgimpbase/gimpbaseenums.c b/libgimpbase/gimpbaseenums.c
new file mode 100644
index 0000000..186e48e
--- /dev/null
+++ b/libgimpbase/gimpbaseenums.c
@@ -0,0 +1,2045 @@
+
+/* Generated data (by gimp-mkenums) */
+
+#include "config.h"
+#include <glib-object.h>
+#undef GIMP_DISABLE_DEPRECATED
+#include "gimpbasetypes.h"
+#include "libgimp/libgimp-intl.h"
+
+/* enumerations from "gimpbaseenums.h" */
+GType
+gimp_add_mask_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_ADD_MASK_WHITE, "GIMP_ADD_MASK_WHITE", "white" },
+ { GIMP_ADD_MASK_BLACK, "GIMP_ADD_MASK_BLACK", "black" },
+ { GIMP_ADD_MASK_ALPHA, "GIMP_ADD_MASK_ALPHA", "alpha" },
+ { GIMP_ADD_MASK_ALPHA_TRANSFER, "GIMP_ADD_MASK_ALPHA_TRANSFER", "alpha-transfer" },
+ { GIMP_ADD_MASK_SELECTION, "GIMP_ADD_MASK_SELECTION", "selection" },
+ { GIMP_ADD_MASK_COPY, "GIMP_ADD_MASK_COPY", "copy" },
+ { GIMP_ADD_MASK_CHANNEL, "GIMP_ADD_MASK_CHANNEL", "channel" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_ADD_MASK_WHITE, NC_("add-mask-type", "_White (full opacity)"), NULL },
+ { GIMP_ADD_MASK_BLACK, NC_("add-mask-type", "_Black (full transparency)"), NULL },
+ { GIMP_ADD_MASK_ALPHA, NC_("add-mask-type", "Layer's _alpha channel"), NULL },
+ { GIMP_ADD_MASK_ALPHA_TRANSFER, NC_("add-mask-type", "_Transfer layer's alpha channel"), NULL },
+ { GIMP_ADD_MASK_SELECTION, NC_("add-mask-type", "_Selection"), NULL },
+ { GIMP_ADD_MASK_COPY, NC_("add-mask-type", "_Grayscale copy of layer"), NULL },
+ { GIMP_ADD_MASK_CHANNEL, NC_("add-mask-type", "C_hannel"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpAddMaskType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "add-mask-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_blend_mode_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_BLEND_FG_BG_RGB, "GIMP_BLEND_FG_BG_RGB", "fg-bg-rgb" },
+ { GIMP_BLEND_FG_BG_HSV, "GIMP_BLEND_FG_BG_HSV", "fg-bg-hsv" },
+ { GIMP_BLEND_FG_TRANSPARENT, "GIMP_BLEND_FG_TRANSPARENT", "fg-transparent" },
+ { GIMP_BLEND_CUSTOM, "GIMP_BLEND_CUSTOM", "custom" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_BLEND_FG_BG_RGB, NC_("blend-mode", "FG to BG (RGB)"), NULL },
+ { GIMP_BLEND_FG_BG_HSV, NC_("blend-mode", "FG to BG (HSV)"), NULL },
+ { GIMP_BLEND_FG_TRANSPARENT, NC_("blend-mode", "FG to transparent"), NULL },
+ { GIMP_BLEND_CUSTOM, NC_("blend-mode", "Custom gradient"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpBlendMode", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "blend-mode");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_brush_generated_shape_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_BRUSH_GENERATED_CIRCLE, "GIMP_BRUSH_GENERATED_CIRCLE", "circle" },
+ { GIMP_BRUSH_GENERATED_SQUARE, "GIMP_BRUSH_GENERATED_SQUARE", "square" },
+ { GIMP_BRUSH_GENERATED_DIAMOND, "GIMP_BRUSH_GENERATED_DIAMOND", "diamond" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_BRUSH_GENERATED_CIRCLE, NC_("brush-generated-shape", "Circle"), NULL },
+ { GIMP_BRUSH_GENERATED_SQUARE, NC_("brush-generated-shape", "Square"), NULL },
+ { GIMP_BRUSH_GENERATED_DIAMOND, NC_("brush-generated-shape", "Diamond"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpBrushGeneratedShape", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "brush-generated-shape");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_bucket_fill_mode_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_BUCKET_FILL_FG, "GIMP_BUCKET_FILL_FG", "fg" },
+ { GIMP_BUCKET_FILL_BG, "GIMP_BUCKET_FILL_BG", "bg" },
+ { GIMP_BUCKET_FILL_PATTERN, "GIMP_BUCKET_FILL_PATTERN", "pattern" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_BUCKET_FILL_FG, NC_("bucket-fill-mode", "FG color fill"), NULL },
+ { GIMP_BUCKET_FILL_BG, NC_("bucket-fill-mode", "BG color fill"), NULL },
+ { GIMP_BUCKET_FILL_PATTERN, NC_("bucket-fill-mode", "Pattern fill"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpBucketFillMode", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "bucket-fill-mode");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_cap_style_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_CAP_BUTT, "GIMP_CAP_BUTT", "butt" },
+ { GIMP_CAP_ROUND, "GIMP_CAP_ROUND", "round" },
+ { GIMP_CAP_SQUARE, "GIMP_CAP_SQUARE", "square" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_CAP_BUTT, NC_("cap-style", "Butt"), NULL },
+ { GIMP_CAP_ROUND, NC_("cap-style", "Round"), NULL },
+ { GIMP_CAP_SQUARE, NC_("cap-style", "Square"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpCapStyle", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "cap-style");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_channel_ops_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_CHANNEL_OP_ADD, "GIMP_CHANNEL_OP_ADD", "add" },
+ { GIMP_CHANNEL_OP_SUBTRACT, "GIMP_CHANNEL_OP_SUBTRACT", "subtract" },
+ { GIMP_CHANNEL_OP_REPLACE, "GIMP_CHANNEL_OP_REPLACE", "replace" },
+ { GIMP_CHANNEL_OP_INTERSECT, "GIMP_CHANNEL_OP_INTERSECT", "intersect" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_CHANNEL_OP_ADD, NC_("channel-ops", "Add to the current selection"), NULL },
+ { GIMP_CHANNEL_OP_SUBTRACT, NC_("channel-ops", "Subtract from the current selection"), NULL },
+ { GIMP_CHANNEL_OP_REPLACE, NC_("channel-ops", "Replace the current selection"), NULL },
+ { GIMP_CHANNEL_OP_INTERSECT, NC_("channel-ops", "Intersect with the current selection"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpChannelOps", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "channel-ops");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_channel_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_CHANNEL_RED, "GIMP_CHANNEL_RED", "red" },
+ { GIMP_CHANNEL_GREEN, "GIMP_CHANNEL_GREEN", "green" },
+ { GIMP_CHANNEL_BLUE, "GIMP_CHANNEL_BLUE", "blue" },
+ { GIMP_CHANNEL_GRAY, "GIMP_CHANNEL_GRAY", "gray" },
+ { GIMP_CHANNEL_INDEXED, "GIMP_CHANNEL_INDEXED", "indexed" },
+ { GIMP_CHANNEL_ALPHA, "GIMP_CHANNEL_ALPHA", "alpha" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_CHANNEL_RED, NC_("channel-type", "Red"), NULL },
+ { GIMP_CHANNEL_GREEN, NC_("channel-type", "Green"), NULL },
+ { GIMP_CHANNEL_BLUE, NC_("channel-type", "Blue"), NULL },
+ { GIMP_CHANNEL_GRAY, NC_("channel-type", "Gray"), NULL },
+ { GIMP_CHANNEL_INDEXED, NC_("channel-type", "Indexed"), NULL },
+ { GIMP_CHANNEL_ALPHA, NC_("channel-type", "Alpha"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpChannelType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "channel-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_check_size_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_CHECK_SIZE_SMALL_CHECKS, "GIMP_CHECK_SIZE_SMALL_CHECKS", "small-checks" },
+ { GIMP_CHECK_SIZE_MEDIUM_CHECKS, "GIMP_CHECK_SIZE_MEDIUM_CHECKS", "medium-checks" },
+ { GIMP_CHECK_SIZE_LARGE_CHECKS, "GIMP_CHECK_SIZE_LARGE_CHECKS", "large-checks" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_CHECK_SIZE_SMALL_CHECKS, NC_("check-size", "Small"), NULL },
+ { GIMP_CHECK_SIZE_MEDIUM_CHECKS, NC_("check-size", "Medium"), NULL },
+ { GIMP_CHECK_SIZE_LARGE_CHECKS, NC_("check-size", "Large"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpCheckSize", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "check-size");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_check_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_CHECK_TYPE_LIGHT_CHECKS, "GIMP_CHECK_TYPE_LIGHT_CHECKS", "light-checks" },
+ { GIMP_CHECK_TYPE_GRAY_CHECKS, "GIMP_CHECK_TYPE_GRAY_CHECKS", "gray-checks" },
+ { GIMP_CHECK_TYPE_DARK_CHECKS, "GIMP_CHECK_TYPE_DARK_CHECKS", "dark-checks" },
+ { GIMP_CHECK_TYPE_WHITE_ONLY, "GIMP_CHECK_TYPE_WHITE_ONLY", "white-only" },
+ { GIMP_CHECK_TYPE_GRAY_ONLY, "GIMP_CHECK_TYPE_GRAY_ONLY", "gray-only" },
+ { GIMP_CHECK_TYPE_BLACK_ONLY, "GIMP_CHECK_TYPE_BLACK_ONLY", "black-only" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_CHECK_TYPE_LIGHT_CHECKS, NC_("check-type", "Light checks"), NULL },
+ { GIMP_CHECK_TYPE_GRAY_CHECKS, NC_("check-type", "Mid-tone checks"), NULL },
+ { GIMP_CHECK_TYPE_DARK_CHECKS, NC_("check-type", "Dark checks"), NULL },
+ { GIMP_CHECK_TYPE_WHITE_ONLY, NC_("check-type", "White only"), NULL },
+ { GIMP_CHECK_TYPE_GRAY_ONLY, NC_("check-type", "Gray only"), NULL },
+ { GIMP_CHECK_TYPE_BLACK_ONLY, NC_("check-type", "Black only"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpCheckType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "check-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_clone_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_CLONE_IMAGE, "GIMP_CLONE_IMAGE", "image" },
+ { GIMP_CLONE_PATTERN, "GIMP_CLONE_PATTERN", "pattern" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_CLONE_IMAGE, NC_("clone-type", "Image"), NULL },
+ { GIMP_CLONE_PATTERN, NC_("clone-type", "Pattern"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpCloneType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "clone-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_color_tag_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_COLOR_TAG_NONE, "GIMP_COLOR_TAG_NONE", "none" },
+ { GIMP_COLOR_TAG_BLUE, "GIMP_COLOR_TAG_BLUE", "blue" },
+ { GIMP_COLOR_TAG_GREEN, "GIMP_COLOR_TAG_GREEN", "green" },
+ { GIMP_COLOR_TAG_YELLOW, "GIMP_COLOR_TAG_YELLOW", "yellow" },
+ { GIMP_COLOR_TAG_ORANGE, "GIMP_COLOR_TAG_ORANGE", "orange" },
+ { GIMP_COLOR_TAG_BROWN, "GIMP_COLOR_TAG_BROWN", "brown" },
+ { GIMP_COLOR_TAG_RED, "GIMP_COLOR_TAG_RED", "red" },
+ { GIMP_COLOR_TAG_VIOLET, "GIMP_COLOR_TAG_VIOLET", "violet" },
+ { GIMP_COLOR_TAG_GRAY, "GIMP_COLOR_TAG_GRAY", "gray" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_COLOR_TAG_NONE, NC_("color-tag", "None"), NULL },
+ { GIMP_COLOR_TAG_BLUE, NC_("color-tag", "Blue"), NULL },
+ { GIMP_COLOR_TAG_GREEN, NC_("color-tag", "Green"), NULL },
+ { GIMP_COLOR_TAG_YELLOW, NC_("color-tag", "Yellow"), NULL },
+ { GIMP_COLOR_TAG_ORANGE, NC_("color-tag", "Orange"), NULL },
+ { GIMP_COLOR_TAG_BROWN, NC_("color-tag", "Brown"), NULL },
+ { GIMP_COLOR_TAG_RED, NC_("color-tag", "Red"), NULL },
+ { GIMP_COLOR_TAG_VIOLET, NC_("color-tag", "Violet"), NULL },
+ { GIMP_COLOR_TAG_GRAY, NC_("color-tag", "Gray"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpColorTag", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "color-tag");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_component_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_COMPONENT_TYPE_U8, "GIMP_COMPONENT_TYPE_U8", "u8" },
+ { GIMP_COMPONENT_TYPE_U16, "GIMP_COMPONENT_TYPE_U16", "u16" },
+ { GIMP_COMPONENT_TYPE_U32, "GIMP_COMPONENT_TYPE_U32", "u32" },
+ { GIMP_COMPONENT_TYPE_HALF, "GIMP_COMPONENT_TYPE_HALF", "half" },
+ { GIMP_COMPONENT_TYPE_FLOAT, "GIMP_COMPONENT_TYPE_FLOAT", "float" },
+ { GIMP_COMPONENT_TYPE_DOUBLE, "GIMP_COMPONENT_TYPE_DOUBLE", "double" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_COMPONENT_TYPE_U8, NC_("component-type", "8-bit integer"), NULL },
+ { GIMP_COMPONENT_TYPE_U16, NC_("component-type", "16-bit integer"), NULL },
+ { GIMP_COMPONENT_TYPE_U32, NC_("component-type", "32-bit integer"), NULL },
+ { GIMP_COMPONENT_TYPE_HALF, NC_("component-type", "16-bit floating point"), NULL },
+ { GIMP_COMPONENT_TYPE_FLOAT, NC_("component-type", "32-bit floating point"), NULL },
+ { GIMP_COMPONENT_TYPE_DOUBLE, NC_("component-type", "64-bit floating point"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpComponentType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "component-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_convert_palette_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_CONVERT_PALETTE_GENERATE, "GIMP_CONVERT_PALETTE_GENERATE", "generate" },
+ { GIMP_CONVERT_PALETTE_WEB, "GIMP_CONVERT_PALETTE_WEB", "web" },
+ { GIMP_CONVERT_PALETTE_MONO, "GIMP_CONVERT_PALETTE_MONO", "mono" },
+ { GIMP_CONVERT_PALETTE_CUSTOM, "GIMP_CONVERT_PALETTE_CUSTOM", "custom" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_CONVERT_PALETTE_GENERATE, NC_("convert-palette-type", "Generate optimum palette"), NULL },
+ { GIMP_CONVERT_PALETTE_WEB, NC_("convert-palette-type", "Use web-optimized palette"), NULL },
+ { GIMP_CONVERT_PALETTE_MONO, NC_("convert-palette-type", "Use black and white (1-bit) palette"), NULL },
+ { GIMP_CONVERT_PALETTE_CUSTOM, NC_("convert-palette-type", "Use custom palette"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpConvertPaletteType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "convert-palette-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_convolve_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_CONVOLVE_BLUR, "GIMP_CONVOLVE_BLUR", "blur" },
+ { GIMP_CONVOLVE_SHARPEN, "GIMP_CONVOLVE_SHARPEN", "sharpen" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_CONVOLVE_BLUR, NC_("convolve-type", "Blur"), NULL },
+ { GIMP_CONVOLVE_SHARPEN, NC_("convolve-type", "Sharpen"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpConvolveType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "convolve-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_desaturate_mode_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_DESATURATE_LIGHTNESS, "GIMP_DESATURATE_LIGHTNESS", "lightness" },
+ { GIMP_DESATURATE_LUMA, "GIMP_DESATURATE_LUMA", "luma" },
+ { GIMP_DESATURATE_AVERAGE, "GIMP_DESATURATE_AVERAGE", "average" },
+ { GIMP_DESATURATE_LUMINANCE, "GIMP_DESATURATE_LUMINANCE", "luminance" },
+ { GIMP_DESATURATE_VALUE, "GIMP_DESATURATE_VALUE", "value" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_DESATURATE_LIGHTNESS, NC_("desaturate-mode", "Lightness (HSL)"), NULL },
+ { GIMP_DESATURATE_LUMA, NC_("desaturate-mode", "Luma"), NULL },
+ { GIMP_DESATURATE_AVERAGE, NC_("desaturate-mode", "Average (HSI Intensity)"), NULL },
+ { GIMP_DESATURATE_LUMINANCE, NC_("desaturate-mode", "Luminance"), NULL },
+ { GIMP_DESATURATE_VALUE, NC_("desaturate-mode", "Value (HSV)"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpDesaturateMode", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "desaturate-mode");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_dodge_burn_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_DODGE_BURN_TYPE_DODGE, "GIMP_DODGE_BURN_TYPE_DODGE", "dodge" },
+ { GIMP_DODGE_BURN_TYPE_BURN, "GIMP_DODGE_BURN_TYPE_BURN", "burn" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_DODGE_BURN_TYPE_DODGE, NC_("dodge-burn-type", "Dodge"), NULL },
+ { GIMP_DODGE_BURN_TYPE_BURN, NC_("dodge-burn-type", "Burn"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpDodgeBurnType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "dodge-burn-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_fill_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_FILL_FOREGROUND, "GIMP_FILL_FOREGROUND", "foreground" },
+ { GIMP_FILL_BACKGROUND, "GIMP_FILL_BACKGROUND", "background" },
+ { GIMP_FILL_WHITE, "GIMP_FILL_WHITE", "white" },
+ { GIMP_FILL_TRANSPARENT, "GIMP_FILL_TRANSPARENT", "transparent" },
+ { GIMP_FILL_PATTERN, "GIMP_FILL_PATTERN", "pattern" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_FILL_FOREGROUND, NC_("fill-type", "Foreground color"), NULL },
+ { GIMP_FILL_BACKGROUND, NC_("fill-type", "Background color"), NULL },
+ { GIMP_FILL_WHITE, NC_("fill-type", "White"), NULL },
+ { GIMP_FILL_TRANSPARENT, NC_("fill-type", "Transparency"), NULL },
+ { GIMP_FILL_PATTERN, NC_("fill-type", "Pattern"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpFillType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "fill-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_foreground_extract_mode_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_FOREGROUND_EXTRACT_SIOX, "GIMP_FOREGROUND_EXTRACT_SIOX", "siox" },
+ { GIMP_FOREGROUND_EXTRACT_MATTING, "GIMP_FOREGROUND_EXTRACT_MATTING", "matting" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_FOREGROUND_EXTRACT_SIOX, "GIMP_FOREGROUND_EXTRACT_SIOX", NULL },
+ { GIMP_FOREGROUND_EXTRACT_MATTING, "GIMP_FOREGROUND_EXTRACT_MATTING", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpForegroundExtractMode", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "foreground-extract-mode");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_gradient_blend_color_space_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_GRADIENT_BLEND_RGB_PERCEPTUAL, "GIMP_GRADIENT_BLEND_RGB_PERCEPTUAL", "rgb-perceptual" },
+ { GIMP_GRADIENT_BLEND_RGB_LINEAR, "GIMP_GRADIENT_BLEND_RGB_LINEAR", "rgb-linear" },
+ { GIMP_GRADIENT_BLEND_CIE_LAB, "GIMP_GRADIENT_BLEND_CIE_LAB", "cie-lab" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_GRADIENT_BLEND_RGB_PERCEPTUAL, NC_("gradient-blend-color-space", "Perceptual RGB"), NULL },
+ { GIMP_GRADIENT_BLEND_RGB_LINEAR, NC_("gradient-blend-color-space", "Linear RGB"), NULL },
+ { GIMP_GRADIENT_BLEND_CIE_LAB, NC_("gradient-blend-color-space", "CIE Lab"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpGradientBlendColorSpace", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "gradient-blend-color-space");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_gradient_segment_color_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_GRADIENT_SEGMENT_RGB, "GIMP_GRADIENT_SEGMENT_RGB", "rgb" },
+ { GIMP_GRADIENT_SEGMENT_HSV_CCW, "GIMP_GRADIENT_SEGMENT_HSV_CCW", "hsv-ccw" },
+ { GIMP_GRADIENT_SEGMENT_HSV_CW, "GIMP_GRADIENT_SEGMENT_HSV_CW", "hsv-cw" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_GRADIENT_SEGMENT_RGB, NC_("gradient-segment-color", "RGB"), NULL },
+ { GIMP_GRADIENT_SEGMENT_HSV_CCW, NC_("gradient-segment-color", "HSV (counter-clockwise hue)"), NULL },
+ /* Translators: this is an abbreviated version of "HSV (counter-clockwise hue)".
+ Keep it short. */
+ { GIMP_GRADIENT_SEGMENT_HSV_CCW, NC_("gradient-segment-color", "HSV (ccw)"), NULL },
+ { GIMP_GRADIENT_SEGMENT_HSV_CW, NC_("gradient-segment-color", "HSV (clockwise hue)"), NULL },
+ /* Translators: this is an abbreviated version of "HSV (clockwise hue)".
+ Keep it short. */
+ { GIMP_GRADIENT_SEGMENT_HSV_CW, NC_("gradient-segment-color", "HSV (cw)"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpGradientSegmentColor", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "gradient-segment-color");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_gradient_segment_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_GRADIENT_SEGMENT_LINEAR, "GIMP_GRADIENT_SEGMENT_LINEAR", "linear" },
+ { GIMP_GRADIENT_SEGMENT_CURVED, "GIMP_GRADIENT_SEGMENT_CURVED", "curved" },
+ { GIMP_GRADIENT_SEGMENT_SINE, "GIMP_GRADIENT_SEGMENT_SINE", "sine" },
+ { GIMP_GRADIENT_SEGMENT_SPHERE_INCREASING, "GIMP_GRADIENT_SEGMENT_SPHERE_INCREASING", "sphere-increasing" },
+ { GIMP_GRADIENT_SEGMENT_SPHERE_DECREASING, "GIMP_GRADIENT_SEGMENT_SPHERE_DECREASING", "sphere-decreasing" },
+ { GIMP_GRADIENT_SEGMENT_STEP, "GIMP_GRADIENT_SEGMENT_STEP", "step" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_GRADIENT_SEGMENT_LINEAR, NC_("gradient-segment-type", "Linear"), NULL },
+ { GIMP_GRADIENT_SEGMENT_CURVED, NC_("gradient-segment-type", "Curved"), NULL },
+ { GIMP_GRADIENT_SEGMENT_SINE, NC_("gradient-segment-type", "Sinusoidal"), NULL },
+ { GIMP_GRADIENT_SEGMENT_SPHERE_INCREASING, NC_("gradient-segment-type", "Spherical (increasing)"), NULL },
+ /* Translators: this is an abbreviated version of "Spherical (increasing)".
+ Keep it short. */
+ { GIMP_GRADIENT_SEGMENT_SPHERE_INCREASING, NC_("gradient-segment-type", "Spherical (inc)"), NULL },
+ { GIMP_GRADIENT_SEGMENT_SPHERE_DECREASING, NC_("gradient-segment-type", "Spherical (decreasing)"), NULL },
+ /* Translators: this is an abbreviated version of "Spherical (decreasing)".
+ Keep it short. */
+ { GIMP_GRADIENT_SEGMENT_SPHERE_DECREASING, NC_("gradient-segment-type", "Spherical (dec)"), NULL },
+ { GIMP_GRADIENT_SEGMENT_STEP, NC_("gradient-segment-type", "Step"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpGradientSegmentType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "gradient-segment-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_gradient_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_GRADIENT_LINEAR, "GIMP_GRADIENT_LINEAR", "linear" },
+ { GIMP_GRADIENT_BILINEAR, "GIMP_GRADIENT_BILINEAR", "bilinear" },
+ { GIMP_GRADIENT_RADIAL, "GIMP_GRADIENT_RADIAL", "radial" },
+ { GIMP_GRADIENT_SQUARE, "GIMP_GRADIENT_SQUARE", "square" },
+ { GIMP_GRADIENT_CONICAL_SYMMETRIC, "GIMP_GRADIENT_CONICAL_SYMMETRIC", "conical-symmetric" },
+ { GIMP_GRADIENT_CONICAL_ASYMMETRIC, "GIMP_GRADIENT_CONICAL_ASYMMETRIC", "conical-asymmetric" },
+ { GIMP_GRADIENT_SHAPEBURST_ANGULAR, "GIMP_GRADIENT_SHAPEBURST_ANGULAR", "shapeburst-angular" },
+ { GIMP_GRADIENT_SHAPEBURST_SPHERICAL, "GIMP_GRADIENT_SHAPEBURST_SPHERICAL", "shapeburst-spherical" },
+ { GIMP_GRADIENT_SHAPEBURST_DIMPLED, "GIMP_GRADIENT_SHAPEBURST_DIMPLED", "shapeburst-dimpled" },
+ { GIMP_GRADIENT_SPIRAL_CLOCKWISE, "GIMP_GRADIENT_SPIRAL_CLOCKWISE", "spiral-clockwise" },
+ { GIMP_GRADIENT_SPIRAL_ANTICLOCKWISE, "GIMP_GRADIENT_SPIRAL_ANTICLOCKWISE", "spiral-anticlockwise" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_GRADIENT_LINEAR, NC_("gradient-type", "Linear"), NULL },
+ { GIMP_GRADIENT_BILINEAR, NC_("gradient-type", "Bi-linear"), NULL },
+ { GIMP_GRADIENT_RADIAL, NC_("gradient-type", "Radial"), NULL },
+ { GIMP_GRADIENT_SQUARE, NC_("gradient-type", "Square"), NULL },
+ { GIMP_GRADIENT_CONICAL_SYMMETRIC, NC_("gradient-type", "Conical (symmetric)"), NULL },
+ /* Translators: this is an abbreviated version of "Conical (symmetric)".
+ Keep it short. */
+ { GIMP_GRADIENT_CONICAL_SYMMETRIC, NC_("gradient-type", "Conical (sym)"), NULL },
+ { GIMP_GRADIENT_CONICAL_ASYMMETRIC, NC_("gradient-type", "Conical (asymmetric)"), NULL },
+ /* Translators: this is an abbreviated version of "Conical (asymmetric)".
+ Keep it short. */
+ { GIMP_GRADIENT_CONICAL_ASYMMETRIC, NC_("gradient-type", "Conical (asym)"), NULL },
+ { GIMP_GRADIENT_SHAPEBURST_ANGULAR, NC_("gradient-type", "Shaped (angular)"), NULL },
+ { GIMP_GRADIENT_SHAPEBURST_SPHERICAL, NC_("gradient-type", "Shaped (spherical)"), NULL },
+ { GIMP_GRADIENT_SHAPEBURST_DIMPLED, NC_("gradient-type", "Shaped (dimpled)"), NULL },
+ { GIMP_GRADIENT_SPIRAL_CLOCKWISE, NC_("gradient-type", "Spiral (clockwise)"), NULL },
+ /* Translators: this is an abbreviated version of "Spiral (clockwise)".
+ Keep it short. */
+ { GIMP_GRADIENT_SPIRAL_CLOCKWISE, NC_("gradient-type", "Spiral (cw)"), NULL },
+ { GIMP_GRADIENT_SPIRAL_ANTICLOCKWISE, NC_("gradient-type", "Spiral (counter-clockwise)"), NULL },
+ /* Translators: this is an abbreviated version of "Spiral (counter-clockwise)".
+ Keep it short. */
+ { GIMP_GRADIENT_SPIRAL_ANTICLOCKWISE, NC_("gradient-type", "Spiral (ccw)"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpGradientType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "gradient-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_grid_style_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_GRID_DOTS, "GIMP_GRID_DOTS", "dots" },
+ { GIMP_GRID_INTERSECTIONS, "GIMP_GRID_INTERSECTIONS", "intersections" },
+ { GIMP_GRID_ON_OFF_DASH, "GIMP_GRID_ON_OFF_DASH", "on-off-dash" },
+ { GIMP_GRID_DOUBLE_DASH, "GIMP_GRID_DOUBLE_DASH", "double-dash" },
+ { GIMP_GRID_SOLID, "GIMP_GRID_SOLID", "solid" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_GRID_DOTS, NC_("grid-style", "Intersections (dots)"), NULL },
+ { GIMP_GRID_INTERSECTIONS, NC_("grid-style", "Intersections (crosshairs)"), NULL },
+ { GIMP_GRID_ON_OFF_DASH, NC_("grid-style", "Dashed"), NULL },
+ { GIMP_GRID_DOUBLE_DASH, NC_("grid-style", "Double dashed"), NULL },
+ { GIMP_GRID_SOLID, NC_("grid-style", "Solid"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpGridStyle", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "grid-style");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_hue_range_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_HUE_RANGE_ALL, "GIMP_HUE_RANGE_ALL", "all" },
+ { GIMP_HUE_RANGE_RED, "GIMP_HUE_RANGE_RED", "red" },
+ { GIMP_HUE_RANGE_YELLOW, "GIMP_HUE_RANGE_YELLOW", "yellow" },
+ { GIMP_HUE_RANGE_GREEN, "GIMP_HUE_RANGE_GREEN", "green" },
+ { GIMP_HUE_RANGE_CYAN, "GIMP_HUE_RANGE_CYAN", "cyan" },
+ { GIMP_HUE_RANGE_BLUE, "GIMP_HUE_RANGE_BLUE", "blue" },
+ { GIMP_HUE_RANGE_MAGENTA, "GIMP_HUE_RANGE_MAGENTA", "magenta" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_HUE_RANGE_ALL, "GIMP_HUE_RANGE_ALL", NULL },
+ { GIMP_HUE_RANGE_RED, "GIMP_HUE_RANGE_RED", NULL },
+ { GIMP_HUE_RANGE_YELLOW, "GIMP_HUE_RANGE_YELLOW", NULL },
+ { GIMP_HUE_RANGE_GREEN, "GIMP_HUE_RANGE_GREEN", NULL },
+ { GIMP_HUE_RANGE_CYAN, "GIMP_HUE_RANGE_CYAN", NULL },
+ { GIMP_HUE_RANGE_BLUE, "GIMP_HUE_RANGE_BLUE", NULL },
+ { GIMP_HUE_RANGE_MAGENTA, "GIMP_HUE_RANGE_MAGENTA", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpHueRange", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "hue-range");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_icon_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_ICON_TYPE_ICON_NAME, "GIMP_ICON_TYPE_ICON_NAME", "icon-name" },
+ { GIMP_ICON_TYPE_INLINE_PIXBUF, "GIMP_ICON_TYPE_INLINE_PIXBUF", "inline-pixbuf" },
+ { GIMP_ICON_TYPE_IMAGE_FILE, "GIMP_ICON_TYPE_IMAGE_FILE", "image-file" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_ICON_TYPE_ICON_NAME, NC_("icon-type", "Icon name"), NULL },
+ { GIMP_ICON_TYPE_INLINE_PIXBUF, NC_("icon-type", "Inline pixbuf"), NULL },
+ { GIMP_ICON_TYPE_IMAGE_FILE, NC_("icon-type", "Image file"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpIconType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "icon-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_image_base_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_RGB, "GIMP_RGB", "rgb" },
+ { GIMP_GRAY, "GIMP_GRAY", "gray" },
+ { GIMP_INDEXED, "GIMP_INDEXED", "indexed" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_RGB, NC_("image-base-type", "RGB color"), NULL },
+ { GIMP_GRAY, NC_("image-base-type", "Grayscale"), NULL },
+ { GIMP_INDEXED, NC_("image-base-type", "Indexed color"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpImageBaseType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "image-base-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_image_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_RGB_IMAGE, "GIMP_RGB_IMAGE", "rgb-image" },
+ { GIMP_RGBA_IMAGE, "GIMP_RGBA_IMAGE", "rgba-image" },
+ { GIMP_GRAY_IMAGE, "GIMP_GRAY_IMAGE", "gray-image" },
+ { GIMP_GRAYA_IMAGE, "GIMP_GRAYA_IMAGE", "graya-image" },
+ { GIMP_INDEXED_IMAGE, "GIMP_INDEXED_IMAGE", "indexed-image" },
+ { GIMP_INDEXEDA_IMAGE, "GIMP_INDEXEDA_IMAGE", "indexeda-image" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_RGB_IMAGE, NC_("image-type", "RGB"), NULL },
+ { GIMP_RGBA_IMAGE, NC_("image-type", "RGB-alpha"), NULL },
+ { GIMP_GRAY_IMAGE, NC_("image-type", "Grayscale"), NULL },
+ { GIMP_GRAYA_IMAGE, NC_("image-type", "Grayscale-alpha"), NULL },
+ { GIMP_INDEXED_IMAGE, NC_("image-type", "Indexed"), NULL },
+ { GIMP_INDEXEDA_IMAGE, NC_("image-type", "Indexed-alpha"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpImageType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "image-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_ink_blob_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_INK_BLOB_TYPE_CIRCLE, "GIMP_INK_BLOB_TYPE_CIRCLE", "circle" },
+ { GIMP_INK_BLOB_TYPE_SQUARE, "GIMP_INK_BLOB_TYPE_SQUARE", "square" },
+ { GIMP_INK_BLOB_TYPE_DIAMOND, "GIMP_INK_BLOB_TYPE_DIAMOND", "diamond" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_INK_BLOB_TYPE_CIRCLE, NC_("ink-blob-type", "Circle"), NULL },
+ { GIMP_INK_BLOB_TYPE_SQUARE, NC_("ink-blob-type", "Square"), NULL },
+ { GIMP_INK_BLOB_TYPE_DIAMOND, NC_("ink-blob-type", "Diamond"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpInkBlobType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "ink-blob-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_interpolation_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_INTERPOLATION_NONE, "GIMP_INTERPOLATION_NONE", "none" },
+ { GIMP_INTERPOLATION_LINEAR, "GIMP_INTERPOLATION_LINEAR", "linear" },
+ { GIMP_INTERPOLATION_CUBIC, "GIMP_INTERPOLATION_CUBIC", "cubic" },
+ { GIMP_INTERPOLATION_NOHALO, "GIMP_INTERPOLATION_NOHALO", "nohalo" },
+ { GIMP_INTERPOLATION_LOHALO, "GIMP_INTERPOLATION_LOHALO", "lohalo" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_INTERPOLATION_NONE, NC_("interpolation-type", "None"), NULL },
+ { GIMP_INTERPOLATION_LINEAR, NC_("interpolation-type", "Linear"), NULL },
+ { GIMP_INTERPOLATION_CUBIC, NC_("interpolation-type", "Cubic"), NULL },
+ { GIMP_INTERPOLATION_NOHALO, NC_("interpolation-type", "NoHalo"), NULL },
+ { GIMP_INTERPOLATION_LOHALO, NC_("interpolation-type", "LoHalo"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpInterpolationType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "interpolation-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_join_style_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_JOIN_MITER, "GIMP_JOIN_MITER", "miter" },
+ { GIMP_JOIN_ROUND, "GIMP_JOIN_ROUND", "round" },
+ { GIMP_JOIN_BEVEL, "GIMP_JOIN_BEVEL", "bevel" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_JOIN_MITER, NC_("join-style", "Miter"), NULL },
+ { GIMP_JOIN_ROUND, NC_("join-style", "Round"), NULL },
+ { GIMP_JOIN_BEVEL, NC_("join-style", "Bevel"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpJoinStyle", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "join-style");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_mask_apply_mode_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_MASK_APPLY, "GIMP_MASK_APPLY", "apply" },
+ { GIMP_MASK_DISCARD, "GIMP_MASK_DISCARD", "discard" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_MASK_APPLY, "GIMP_MASK_APPLY", NULL },
+ { GIMP_MASK_DISCARD, "GIMP_MASK_DISCARD", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpMaskApplyMode", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "mask-apply-mode");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_merge_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_EXPAND_AS_NECESSARY, "GIMP_EXPAND_AS_NECESSARY", "expand-as-necessary" },
+ { GIMP_CLIP_TO_IMAGE, "GIMP_CLIP_TO_IMAGE", "clip-to-image" },
+ { GIMP_CLIP_TO_BOTTOM_LAYER, "GIMP_CLIP_TO_BOTTOM_LAYER", "clip-to-bottom-layer" },
+ { GIMP_FLATTEN_IMAGE, "GIMP_FLATTEN_IMAGE", "flatten-image" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_EXPAND_AS_NECESSARY, NC_("merge-type", "Expanded as necessary"), NULL },
+ { GIMP_CLIP_TO_IMAGE, NC_("merge-type", "Clipped to image"), NULL },
+ { GIMP_CLIP_TO_BOTTOM_LAYER, NC_("merge-type", "Clipped to bottom layer"), NULL },
+ { GIMP_FLATTEN_IMAGE, NC_("merge-type", "Flatten"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpMergeType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "merge-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_message_handler_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_MESSAGE_BOX, "GIMP_MESSAGE_BOX", "message-box" },
+ { GIMP_CONSOLE, "GIMP_CONSOLE", "console" },
+ { GIMP_ERROR_CONSOLE, "GIMP_ERROR_CONSOLE", "error-console" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_MESSAGE_BOX, "GIMP_MESSAGE_BOX", NULL },
+ { GIMP_CONSOLE, "GIMP_CONSOLE", NULL },
+ { GIMP_ERROR_CONSOLE, "GIMP_ERROR_CONSOLE", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpMessageHandlerType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "message-handler-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_offset_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_OFFSET_BACKGROUND, "GIMP_OFFSET_BACKGROUND", "background" },
+ { GIMP_OFFSET_TRANSPARENT, "GIMP_OFFSET_TRANSPARENT", "transparent" },
+ { GIMP_OFFSET_WRAP_AROUND, "GIMP_OFFSET_WRAP_AROUND", "wrap-around" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_OFFSET_BACKGROUND, "GIMP_OFFSET_BACKGROUND", NULL },
+ { GIMP_OFFSET_TRANSPARENT, "GIMP_OFFSET_TRANSPARENT", NULL },
+ { GIMP_OFFSET_WRAP_AROUND, "GIMP_OFFSET_WRAP_AROUND", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpOffsetType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "offset-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_orientation_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_ORIENTATION_HORIZONTAL, "GIMP_ORIENTATION_HORIZONTAL", "horizontal" },
+ { GIMP_ORIENTATION_VERTICAL, "GIMP_ORIENTATION_VERTICAL", "vertical" },
+ { GIMP_ORIENTATION_UNKNOWN, "GIMP_ORIENTATION_UNKNOWN", "unknown" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_ORIENTATION_HORIZONTAL, NC_("orientation-type", "Horizontal"), NULL },
+ { GIMP_ORIENTATION_VERTICAL, NC_("orientation-type", "Vertical"), NULL },
+ { GIMP_ORIENTATION_UNKNOWN, NC_("orientation-type", "Unknown"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpOrientationType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "orientation-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_paint_application_mode_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_PAINT_CONSTANT, "GIMP_PAINT_CONSTANT", "constant" },
+ { GIMP_PAINT_INCREMENTAL, "GIMP_PAINT_INCREMENTAL", "incremental" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_PAINT_CONSTANT, NC_("paint-application-mode", "Constant"), NULL },
+ { GIMP_PAINT_INCREMENTAL, NC_("paint-application-mode", "Incremental"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpPaintApplicationMode", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "paint-application-mode");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_pdb_arg_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_PDB_INT32, "GIMP_PDB_INT32", "int32" },
+ { GIMP_PDB_INT16, "GIMP_PDB_INT16", "int16" },
+ { GIMP_PDB_INT8, "GIMP_PDB_INT8", "int8" },
+ { GIMP_PDB_FLOAT, "GIMP_PDB_FLOAT", "float" },
+ { GIMP_PDB_STRING, "GIMP_PDB_STRING", "string" },
+ { GIMP_PDB_INT32ARRAY, "GIMP_PDB_INT32ARRAY", "int32array" },
+ { GIMP_PDB_INT16ARRAY, "GIMP_PDB_INT16ARRAY", "int16array" },
+ { GIMP_PDB_INT8ARRAY, "GIMP_PDB_INT8ARRAY", "int8array" },
+ { GIMP_PDB_FLOATARRAY, "GIMP_PDB_FLOATARRAY", "floatarray" },
+ { GIMP_PDB_STRINGARRAY, "GIMP_PDB_STRINGARRAY", "stringarray" },
+ { GIMP_PDB_COLOR, "GIMP_PDB_COLOR", "color" },
+ { GIMP_PDB_ITEM, "GIMP_PDB_ITEM", "item" },
+ { GIMP_PDB_DISPLAY, "GIMP_PDB_DISPLAY", "display" },
+ { GIMP_PDB_IMAGE, "GIMP_PDB_IMAGE", "image" },
+ { GIMP_PDB_LAYER, "GIMP_PDB_LAYER", "layer" },
+ { GIMP_PDB_CHANNEL, "GIMP_PDB_CHANNEL", "channel" },
+ { GIMP_PDB_DRAWABLE, "GIMP_PDB_DRAWABLE", "drawable" },
+ { GIMP_PDB_SELECTION, "GIMP_PDB_SELECTION", "selection" },
+ { GIMP_PDB_COLORARRAY, "GIMP_PDB_COLORARRAY", "colorarray" },
+ { GIMP_PDB_VECTORS, "GIMP_PDB_VECTORS", "vectors" },
+ { GIMP_PDB_PARASITE, "GIMP_PDB_PARASITE", "parasite" },
+ { GIMP_PDB_STATUS, "GIMP_PDB_STATUS", "status" },
+ { GIMP_PDB_END, "GIMP_PDB_END", "end" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_PDB_INT32, "GIMP_PDB_INT32", NULL },
+ { GIMP_PDB_INT16, "GIMP_PDB_INT16", NULL },
+ { GIMP_PDB_INT8, "GIMP_PDB_INT8", NULL },
+ { GIMP_PDB_FLOAT, "GIMP_PDB_FLOAT", NULL },
+ { GIMP_PDB_STRING, "GIMP_PDB_STRING", NULL },
+ { GIMP_PDB_INT32ARRAY, "GIMP_PDB_INT32ARRAY", NULL },
+ { GIMP_PDB_INT16ARRAY, "GIMP_PDB_INT16ARRAY", NULL },
+ { GIMP_PDB_INT8ARRAY, "GIMP_PDB_INT8ARRAY", NULL },
+ { GIMP_PDB_FLOATARRAY, "GIMP_PDB_FLOATARRAY", NULL },
+ { GIMP_PDB_STRINGARRAY, "GIMP_PDB_STRINGARRAY", NULL },
+ { GIMP_PDB_COLOR, "GIMP_PDB_COLOR", NULL },
+ { GIMP_PDB_ITEM, "GIMP_PDB_ITEM", NULL },
+ { GIMP_PDB_DISPLAY, "GIMP_PDB_DISPLAY", NULL },
+ { GIMP_PDB_IMAGE, "GIMP_PDB_IMAGE", NULL },
+ { GIMP_PDB_LAYER, "GIMP_PDB_LAYER", NULL },
+ { GIMP_PDB_CHANNEL, "GIMP_PDB_CHANNEL", NULL },
+ { GIMP_PDB_DRAWABLE, "GIMP_PDB_DRAWABLE", NULL },
+ { GIMP_PDB_SELECTION, "GIMP_PDB_SELECTION", NULL },
+ { GIMP_PDB_COLORARRAY, "GIMP_PDB_COLORARRAY", NULL },
+ { GIMP_PDB_VECTORS, "GIMP_PDB_VECTORS", NULL },
+ { GIMP_PDB_PARASITE, "GIMP_PDB_PARASITE", NULL },
+ { GIMP_PDB_STATUS, "GIMP_PDB_STATUS", NULL },
+ { GIMP_PDB_END, "GIMP_PDB_END", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpPDBArgType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "pdb-arg-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_pdb_error_handler_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_PDB_ERROR_HANDLER_INTERNAL, "GIMP_PDB_ERROR_HANDLER_INTERNAL", "internal" },
+ { GIMP_PDB_ERROR_HANDLER_PLUGIN, "GIMP_PDB_ERROR_HANDLER_PLUGIN", "plugin" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_PDB_ERROR_HANDLER_INTERNAL, "GIMP_PDB_ERROR_HANDLER_INTERNAL", NULL },
+ { GIMP_PDB_ERROR_HANDLER_PLUGIN, "GIMP_PDB_ERROR_HANDLER_PLUGIN", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpPDBErrorHandler", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "pdb-error-handler");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_pdb_proc_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_INTERNAL, "GIMP_INTERNAL", "internal" },
+ { GIMP_PLUGIN, "GIMP_PLUGIN", "plugin" },
+ { GIMP_EXTENSION, "GIMP_EXTENSION", "extension" },
+ { GIMP_TEMPORARY, "GIMP_TEMPORARY", "temporary" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_INTERNAL, NC_("pdb-proc-type", "Internal GIMP procedure"), NULL },
+ { GIMP_PLUGIN, NC_("pdb-proc-type", "GIMP Plug-In"), NULL },
+ { GIMP_EXTENSION, NC_("pdb-proc-type", "GIMP Extension"), NULL },
+ { GIMP_TEMPORARY, NC_("pdb-proc-type", "Temporary Procedure"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpPDBProcType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "pdb-proc-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_pdb_status_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_PDB_EXECUTION_ERROR, "GIMP_PDB_EXECUTION_ERROR", "execution-error" },
+ { GIMP_PDB_CALLING_ERROR, "GIMP_PDB_CALLING_ERROR", "calling-error" },
+ { GIMP_PDB_PASS_THROUGH, "GIMP_PDB_PASS_THROUGH", "pass-through" },
+ { GIMP_PDB_SUCCESS, "GIMP_PDB_SUCCESS", "success" },
+ { GIMP_PDB_CANCEL, "GIMP_PDB_CANCEL", "cancel" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_PDB_EXECUTION_ERROR, "GIMP_PDB_EXECUTION_ERROR", NULL },
+ { GIMP_PDB_CALLING_ERROR, "GIMP_PDB_CALLING_ERROR", NULL },
+ { GIMP_PDB_PASS_THROUGH, "GIMP_PDB_PASS_THROUGH", NULL },
+ { GIMP_PDB_SUCCESS, "GIMP_PDB_SUCCESS", NULL },
+ { GIMP_PDB_CANCEL, "GIMP_PDB_CANCEL", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpPDBStatusType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "pdb-status-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_precision_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_PRECISION_U8_LINEAR, "GIMP_PRECISION_U8_LINEAR", "u8-linear" },
+ { GIMP_PRECISION_U8_GAMMA, "GIMP_PRECISION_U8_GAMMA", "u8-gamma" },
+ { GIMP_PRECISION_U16_LINEAR, "GIMP_PRECISION_U16_LINEAR", "u16-linear" },
+ { GIMP_PRECISION_U16_GAMMA, "GIMP_PRECISION_U16_GAMMA", "u16-gamma" },
+ { GIMP_PRECISION_U32_LINEAR, "GIMP_PRECISION_U32_LINEAR", "u32-linear" },
+ { GIMP_PRECISION_U32_GAMMA, "GIMP_PRECISION_U32_GAMMA", "u32-gamma" },
+ { GIMP_PRECISION_HALF_LINEAR, "GIMP_PRECISION_HALF_LINEAR", "half-linear" },
+ { GIMP_PRECISION_HALF_GAMMA, "GIMP_PRECISION_HALF_GAMMA", "half-gamma" },
+ { GIMP_PRECISION_FLOAT_LINEAR, "GIMP_PRECISION_FLOAT_LINEAR", "float-linear" },
+ { GIMP_PRECISION_FLOAT_GAMMA, "GIMP_PRECISION_FLOAT_GAMMA", "float-gamma" },
+ { GIMP_PRECISION_DOUBLE_LINEAR, "GIMP_PRECISION_DOUBLE_LINEAR", "double-linear" },
+ { GIMP_PRECISION_DOUBLE_GAMMA, "GIMP_PRECISION_DOUBLE_GAMMA", "double-gamma" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_PRECISION_U8_LINEAR, NC_("precision", "8-bit linear integer"), NULL },
+ { GIMP_PRECISION_U8_GAMMA, NC_("precision", "8-bit gamma integer"), NULL },
+ { GIMP_PRECISION_U16_LINEAR, NC_("precision", "16-bit linear integer"), NULL },
+ { GIMP_PRECISION_U16_GAMMA, NC_("precision", "16-bit gamma integer"), NULL },
+ { GIMP_PRECISION_U32_LINEAR, NC_("precision", "32-bit linear integer"), NULL },
+ { GIMP_PRECISION_U32_GAMMA, NC_("precision", "32-bit gamma integer"), NULL },
+ { GIMP_PRECISION_HALF_LINEAR, NC_("precision", "16-bit linear floating point"), NULL },
+ { GIMP_PRECISION_HALF_GAMMA, NC_("precision", "16-bit gamma floating point"), NULL },
+ { GIMP_PRECISION_FLOAT_LINEAR, NC_("precision", "32-bit linear floating point"), NULL },
+ { GIMP_PRECISION_FLOAT_GAMMA, NC_("precision", "32-bit gamma floating point"), NULL },
+ { GIMP_PRECISION_DOUBLE_LINEAR, NC_("precision", "64-bit linear floating point"), NULL },
+ { GIMP_PRECISION_DOUBLE_GAMMA, NC_("precision", "64-bit gamma floating point"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpPrecision", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "precision");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_progress_command_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_PROGRESS_COMMAND_START, "GIMP_PROGRESS_COMMAND_START", "start" },
+ { GIMP_PROGRESS_COMMAND_END, "GIMP_PROGRESS_COMMAND_END", "end" },
+ { GIMP_PROGRESS_COMMAND_SET_TEXT, "GIMP_PROGRESS_COMMAND_SET_TEXT", "set-text" },
+ { GIMP_PROGRESS_COMMAND_SET_VALUE, "GIMP_PROGRESS_COMMAND_SET_VALUE", "set-value" },
+ { GIMP_PROGRESS_COMMAND_PULSE, "GIMP_PROGRESS_COMMAND_PULSE", "pulse" },
+ { GIMP_PROGRESS_COMMAND_GET_WINDOW, "GIMP_PROGRESS_COMMAND_GET_WINDOW", "get-window" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_PROGRESS_COMMAND_START, "GIMP_PROGRESS_COMMAND_START", NULL },
+ { GIMP_PROGRESS_COMMAND_END, "GIMP_PROGRESS_COMMAND_END", NULL },
+ { GIMP_PROGRESS_COMMAND_SET_TEXT, "GIMP_PROGRESS_COMMAND_SET_TEXT", NULL },
+ { GIMP_PROGRESS_COMMAND_SET_VALUE, "GIMP_PROGRESS_COMMAND_SET_VALUE", NULL },
+ { GIMP_PROGRESS_COMMAND_PULSE, "GIMP_PROGRESS_COMMAND_PULSE", NULL },
+ { GIMP_PROGRESS_COMMAND_GET_WINDOW, "GIMP_PROGRESS_COMMAND_GET_WINDOW", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpProgressCommand", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "progress-command");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_repeat_mode_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_REPEAT_NONE, "GIMP_REPEAT_NONE", "none" },
+ { GIMP_REPEAT_SAWTOOTH, "GIMP_REPEAT_SAWTOOTH", "sawtooth" },
+ { GIMP_REPEAT_TRIANGULAR, "GIMP_REPEAT_TRIANGULAR", "triangular" },
+ { GIMP_REPEAT_TRUNCATE, "GIMP_REPEAT_TRUNCATE", "truncate" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_REPEAT_NONE, NC_("repeat-mode", "None (extend)"), NULL },
+ { GIMP_REPEAT_SAWTOOTH, NC_("repeat-mode", "Sawtooth wave"), NULL },
+ { GIMP_REPEAT_TRIANGULAR, NC_("repeat-mode", "Triangular wave"), NULL },
+ { GIMP_REPEAT_TRUNCATE, NC_("repeat-mode", "Truncate"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpRepeatMode", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "repeat-mode");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_rotation_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_ROTATE_90, "GIMP_ROTATE_90", "90" },
+ { GIMP_ROTATE_180, "GIMP_ROTATE_180", "180" },
+ { GIMP_ROTATE_270, "GIMP_ROTATE_270", "270" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_ROTATE_90, "GIMP_ROTATE_90", NULL },
+ { GIMP_ROTATE_180, "GIMP_ROTATE_180", NULL },
+ { GIMP_ROTATE_270, "GIMP_ROTATE_270", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpRotationType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "rotation-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_run_mode_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_RUN_INTERACTIVE, "GIMP_RUN_INTERACTIVE", "interactive" },
+ { GIMP_RUN_NONINTERACTIVE, "GIMP_RUN_NONINTERACTIVE", "noninteractive" },
+ { GIMP_RUN_WITH_LAST_VALS, "GIMP_RUN_WITH_LAST_VALS", "with-last-vals" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_RUN_INTERACTIVE, NC_("run-mode", "Run interactively"), NULL },
+ { GIMP_RUN_NONINTERACTIVE, NC_("run-mode", "Run non-interactively"), NULL },
+ { GIMP_RUN_WITH_LAST_VALS, NC_("run-mode", "Run with last used values"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpRunMode", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "run-mode");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_select_criterion_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_SELECT_CRITERION_COMPOSITE, "GIMP_SELECT_CRITERION_COMPOSITE", "composite" },
+ { GIMP_SELECT_CRITERION_R, "GIMP_SELECT_CRITERION_R", "r" },
+ { GIMP_SELECT_CRITERION_G, "GIMP_SELECT_CRITERION_G", "g" },
+ { GIMP_SELECT_CRITERION_B, "GIMP_SELECT_CRITERION_B", "b" },
+ { GIMP_SELECT_CRITERION_H, "GIMP_SELECT_CRITERION_H", "h" },
+ { GIMP_SELECT_CRITERION_S, "GIMP_SELECT_CRITERION_S", "s" },
+ { GIMP_SELECT_CRITERION_V, "GIMP_SELECT_CRITERION_V", "v" },
+ { GIMP_SELECT_CRITERION_A, "GIMP_SELECT_CRITERION_A", "a" },
+ { GIMP_SELECT_CRITERION_LCH_L, "GIMP_SELECT_CRITERION_LCH_L", "lch-l" },
+ { GIMP_SELECT_CRITERION_LCH_C, "GIMP_SELECT_CRITERION_LCH_C", "lch-c" },
+ { GIMP_SELECT_CRITERION_LCH_H, "GIMP_SELECT_CRITERION_LCH_H", "lch-h" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_SELECT_CRITERION_COMPOSITE, NC_("select-criterion", "Composite"), NULL },
+ { GIMP_SELECT_CRITERION_R, NC_("select-criterion", "Red"), NULL },
+ { GIMP_SELECT_CRITERION_G, NC_("select-criterion", "Green"), NULL },
+ { GIMP_SELECT_CRITERION_B, NC_("select-criterion", "Blue"), NULL },
+ { GIMP_SELECT_CRITERION_H, NC_("select-criterion", "HSV Hue"), NULL },
+ { GIMP_SELECT_CRITERION_S, NC_("select-criterion", "HSV Saturation"), NULL },
+ { GIMP_SELECT_CRITERION_V, NC_("select-criterion", "HSV Value"), NULL },
+ { GIMP_SELECT_CRITERION_A, NC_("select-criterion", "Alpha"), NULL },
+ { GIMP_SELECT_CRITERION_LCH_L, NC_("select-criterion", "LCh Lightness"), NULL },
+ { GIMP_SELECT_CRITERION_LCH_C, NC_("select-criterion", "LCh Chroma"), NULL },
+ { GIMP_SELECT_CRITERION_LCH_H, NC_("select-criterion", "LCh Hue"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpSelectCriterion", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "select-criterion");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_size_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_PIXELS, "GIMP_PIXELS", "pixels" },
+ { GIMP_POINTS, "GIMP_POINTS", "points" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_PIXELS, NC_("size-type", "Pixels"), NULL },
+ { GIMP_POINTS, NC_("size-type", "Points"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpSizeType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "size-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_stack_trace_mode_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_STACK_TRACE_NEVER, "GIMP_STACK_TRACE_NEVER", "never" },
+ { GIMP_STACK_TRACE_QUERY, "GIMP_STACK_TRACE_QUERY", "query" },
+ { GIMP_STACK_TRACE_ALWAYS, "GIMP_STACK_TRACE_ALWAYS", "always" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_STACK_TRACE_NEVER, "GIMP_STACK_TRACE_NEVER", NULL },
+ { GIMP_STACK_TRACE_QUERY, "GIMP_STACK_TRACE_QUERY", NULL },
+ { GIMP_STACK_TRACE_ALWAYS, "GIMP_STACK_TRACE_ALWAYS", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpStackTraceMode", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "stack-trace-mode");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_stroke_method_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_STROKE_LINE, "GIMP_STROKE_LINE", "line" },
+ { GIMP_STROKE_PAINT_METHOD, "GIMP_STROKE_PAINT_METHOD", "paint-method" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_STROKE_LINE, NC_("stroke-method", "Stroke line"), NULL },
+ { GIMP_STROKE_PAINT_METHOD, NC_("stroke-method", "Stroke with a paint tool"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpStrokeMethod", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "stroke-method");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_text_direction_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_TEXT_DIRECTION_LTR, "GIMP_TEXT_DIRECTION_LTR", "ltr" },
+ { GIMP_TEXT_DIRECTION_RTL, "GIMP_TEXT_DIRECTION_RTL", "rtl" },
+ { GIMP_TEXT_DIRECTION_TTB_RTL, "GIMP_TEXT_DIRECTION_TTB_RTL", "ttb-rtl" },
+ { GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT, "GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT", "ttb-rtl-upright" },
+ { GIMP_TEXT_DIRECTION_TTB_LTR, "GIMP_TEXT_DIRECTION_TTB_LTR", "ttb-ltr" },
+ { GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT, "GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT", "ttb-ltr-upright" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_TEXT_DIRECTION_LTR, NC_("text-direction", "From left to right"), NULL },
+ { GIMP_TEXT_DIRECTION_RTL, NC_("text-direction", "From right to left"), NULL },
+ { GIMP_TEXT_DIRECTION_TTB_RTL, NC_("text-direction", "Vertical, right to left (mixed orientation)"), NULL },
+ { GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT, NC_("text-direction", "Vertical, right to left (upright orientation)"), NULL },
+ { GIMP_TEXT_DIRECTION_TTB_LTR, NC_("text-direction", "Vertical, left to right (mixed orientation)"), NULL },
+ { GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT, NC_("text-direction", "Vertical, left to right (upright orientation)"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpTextDirection", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "text-direction");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_text_hint_style_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_TEXT_HINT_STYLE_NONE, "GIMP_TEXT_HINT_STYLE_NONE", "none" },
+ { GIMP_TEXT_HINT_STYLE_SLIGHT, "GIMP_TEXT_HINT_STYLE_SLIGHT", "slight" },
+ { GIMP_TEXT_HINT_STYLE_MEDIUM, "GIMP_TEXT_HINT_STYLE_MEDIUM", "medium" },
+ { GIMP_TEXT_HINT_STYLE_FULL, "GIMP_TEXT_HINT_STYLE_FULL", "full" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_TEXT_HINT_STYLE_NONE, NC_("text-hint-style", "None"), NULL },
+ { GIMP_TEXT_HINT_STYLE_SLIGHT, NC_("text-hint-style", "Slight"), NULL },
+ { GIMP_TEXT_HINT_STYLE_MEDIUM, NC_("text-hint-style", "Medium"), NULL },
+ { GIMP_TEXT_HINT_STYLE_FULL, NC_("text-hint-style", "Full"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpTextHintStyle", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "text-hint-style");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_text_justification_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_TEXT_JUSTIFY_LEFT, "GIMP_TEXT_JUSTIFY_LEFT", "left" },
+ { GIMP_TEXT_JUSTIFY_RIGHT, "GIMP_TEXT_JUSTIFY_RIGHT", "right" },
+ { GIMP_TEXT_JUSTIFY_CENTER, "GIMP_TEXT_JUSTIFY_CENTER", "center" },
+ { GIMP_TEXT_JUSTIFY_FILL, "GIMP_TEXT_JUSTIFY_FILL", "fill" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_TEXT_JUSTIFY_LEFT, NC_("text-justification", "Left justified"), NULL },
+ { GIMP_TEXT_JUSTIFY_RIGHT, NC_("text-justification", "Right justified"), NULL },
+ { GIMP_TEXT_JUSTIFY_CENTER, NC_("text-justification", "Centered"), NULL },
+ { GIMP_TEXT_JUSTIFY_FILL, NC_("text-justification", "Filled"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpTextJustification", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "text-justification");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_transfer_mode_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_TRANSFER_SHADOWS, "GIMP_TRANSFER_SHADOWS", "shadows" },
+ { GIMP_TRANSFER_MIDTONES, "GIMP_TRANSFER_MIDTONES", "midtones" },
+ { GIMP_TRANSFER_HIGHLIGHTS, "GIMP_TRANSFER_HIGHLIGHTS", "highlights" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_TRANSFER_SHADOWS, NC_("transfer-mode", "Shadows"), NULL },
+ { GIMP_TRANSFER_MIDTONES, NC_("transfer-mode", "Midtones"), NULL },
+ { GIMP_TRANSFER_HIGHLIGHTS, NC_("transfer-mode", "Highlights"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpTransferMode", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "transfer-mode");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_transform_direction_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_TRANSFORM_FORWARD, "GIMP_TRANSFORM_FORWARD", "forward" },
+ { GIMP_TRANSFORM_BACKWARD, "GIMP_TRANSFORM_BACKWARD", "backward" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_TRANSFORM_FORWARD, NC_("transform-direction", "Normal (Forward)"), NULL },
+ { GIMP_TRANSFORM_BACKWARD, NC_("transform-direction", "Corrective (Backward)"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpTransformDirection", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "transform-direction");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_transform_resize_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_TRANSFORM_RESIZE_ADJUST, "GIMP_TRANSFORM_RESIZE_ADJUST", "adjust" },
+ { GIMP_TRANSFORM_RESIZE_CLIP, "GIMP_TRANSFORM_RESIZE_CLIP", "clip" },
+ { GIMP_TRANSFORM_RESIZE_CROP, "GIMP_TRANSFORM_RESIZE_CROP", "crop" },
+ { GIMP_TRANSFORM_RESIZE_CROP_WITH_ASPECT, "GIMP_TRANSFORM_RESIZE_CROP_WITH_ASPECT", "crop-with-aspect" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_TRANSFORM_RESIZE_ADJUST, NC_("transform-resize", "Adjust"), NULL },
+ { GIMP_TRANSFORM_RESIZE_CLIP, NC_("transform-resize", "Clip"), NULL },
+ { GIMP_TRANSFORM_RESIZE_CROP, NC_("transform-resize", "Crop to result"), NULL },
+ { GIMP_TRANSFORM_RESIZE_CROP_WITH_ASPECT, NC_("transform-resize", "Crop with aspect"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpTransformResize", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "transform-resize");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_user_directory_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_USER_DIRECTORY_DESKTOP, "GIMP_USER_DIRECTORY_DESKTOP", "desktop" },
+ { GIMP_USER_DIRECTORY_DOCUMENTS, "GIMP_USER_DIRECTORY_DOCUMENTS", "documents" },
+ { GIMP_USER_DIRECTORY_DOWNLOAD, "GIMP_USER_DIRECTORY_DOWNLOAD", "download" },
+ { GIMP_USER_DIRECTORY_MUSIC, "GIMP_USER_DIRECTORY_MUSIC", "music" },
+ { GIMP_USER_DIRECTORY_PICTURES, "GIMP_USER_DIRECTORY_PICTURES", "pictures" },
+ { GIMP_USER_DIRECTORY_PUBLIC_SHARE, "GIMP_USER_DIRECTORY_PUBLIC_SHARE", "public-share" },
+ { GIMP_USER_DIRECTORY_TEMPLATES, "GIMP_USER_DIRECTORY_TEMPLATES", "templates" },
+ { GIMP_USER_DIRECTORY_VIDEOS, "GIMP_USER_DIRECTORY_VIDEOS", "videos" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_USER_DIRECTORY_DESKTOP, "GIMP_USER_DIRECTORY_DESKTOP", NULL },
+ { GIMP_USER_DIRECTORY_DOCUMENTS, "GIMP_USER_DIRECTORY_DOCUMENTS", NULL },
+ { GIMP_USER_DIRECTORY_DOWNLOAD, "GIMP_USER_DIRECTORY_DOWNLOAD", NULL },
+ { GIMP_USER_DIRECTORY_MUSIC, "GIMP_USER_DIRECTORY_MUSIC", NULL },
+ { GIMP_USER_DIRECTORY_PICTURES, "GIMP_USER_DIRECTORY_PICTURES", NULL },
+ { GIMP_USER_DIRECTORY_PUBLIC_SHARE, "GIMP_USER_DIRECTORY_PUBLIC_SHARE", NULL },
+ { GIMP_USER_DIRECTORY_TEMPLATES, "GIMP_USER_DIRECTORY_TEMPLATES", NULL },
+ { GIMP_USER_DIRECTORY_VIDEOS, "GIMP_USER_DIRECTORY_VIDEOS", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpUserDirectory", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "user-directory");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_vectors_stroke_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_VECTORS_STROKE_TYPE_BEZIER, "GIMP_VECTORS_STROKE_TYPE_BEZIER", "bezier" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_VECTORS_STROKE_TYPE_BEZIER, "GIMP_VECTORS_STROKE_TYPE_BEZIER", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpVectorsStrokeType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "vectors-stroke-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+
+/* Generated data ends here */
+
diff --git a/libgimpbase/gimpbaseenums.h b/libgimpbase/gimpbaseenums.h
new file mode 100644
index 0000000..b3c6fb7
--- /dev/null
+++ b/libgimpbase/gimpbaseenums.h
@@ -0,0 +1,1558 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_BASE_ENUMS_H__
+#define __GIMP_BASE_ENUMS_H__
+
+
+/**
+ * SECTION: gimpbaseenums
+ * @title: gimpbaseenums
+ * @short_description: Basic GIMP enumeration data types.
+ *
+ * Basic GIMP enumeration data types.
+ **/
+
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+/**
+ * GimpAddMaskType:
+ * @GIMP_ADD_MASK_WHITE: White (full opacity)
+ * @GIMP_ADD_MASK_BLACK: Black (full transparency)
+ * @GIMP_ADD_MASK_ALPHA: Layer's alpha channel
+ * @GIMP_ADD_MASK_ALPHA_TRANSFER: Transfer layer's alpha channel
+ * @GIMP_ADD_MASK_SELECTION: Selection
+ * @GIMP_ADD_MASK_COPY: Grayscale copy of layer
+ * @GIMP_ADD_MASK_CHANNEL: Channel
+ * @GIMP_ADD_WHITE_MASK: Deprecated alias for @GIMP_ADD_MASK_WHITE
+ * @GIMP_ADD_BLACK_MASK: Deprecated alias for @GIMP_ADD_MASK_BLACK
+ * @GIMP_ADD_ALPHA_MASK: Deprecated alias for @GIMP_ADD_MASK_ALPHA
+ * @GIMP_ADD_ALPHA_TRANSFER_MASK: Deprecated alias for
+ * @GIMP_ADD_MASK_ALPHA_TRANSFER
+ * @GIMP_ADD_SELECTION_MASK: Deprecated alias for @GIMP_ADD_MASK_SELECTION
+ * @GIMP_ADD_COPY_MASK: Deprecated alias for @GIMP_ADD_MASK_COPY
+ * @GIMP_ADD_CHANNEL_MASK: Deprecated aliaa for @GIMP_ADD_MASK_CHANNEL
+ *
+ * Modes of initialising a layer mask.
+ **/
+#define GIMP_TYPE_ADD_MASK_TYPE (gimp_add_mask_type_get_type ())
+
+GType gimp_add_mask_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_ADD_MASK_WHITE, /*< desc="_White (full opacity)" >*/
+ GIMP_ADD_MASK_BLACK, /*< desc="_Black (full transparency)" >*/
+ GIMP_ADD_MASK_ALPHA, /*< desc="Layer's _alpha channel" >*/
+ GIMP_ADD_MASK_ALPHA_TRANSFER, /*< desc="_Transfer layer's alpha channel" >*/
+ GIMP_ADD_MASK_SELECTION, /*< desc="_Selection" >*/
+ GIMP_ADD_MASK_COPY, /*< desc="_Grayscale copy of layer" >*/
+ GIMP_ADD_MASK_CHANNEL, /*< desc="C_hannel" >*/
+
+#ifndef GIMP_DISABLE_DEPRECATED
+ GIMP_ADD_WHITE_MASK = GIMP_ADD_MASK_WHITE, /*< skip, pdb-skip >*/
+ GIMP_ADD_BLACK_MASK = GIMP_ADD_MASK_BLACK, /*< skip, pdb-skip >*/
+ GIMP_ADD_ALPHA_MASK = GIMP_ADD_MASK_ALPHA, /*< skip, pdb-skip >*/
+ GIMP_ADD_ALPHA_TRANSFER_MASK = GIMP_ADD_MASK_ALPHA_TRANSFER, /*< skip, pdb-skip >*/
+ GIMP_ADD_SELECTION_MASK = GIMP_ADD_MASK_SELECTION, /*< skip, pdb-skip >*/
+ GIMP_ADD_COPY_MASK = GIMP_ADD_MASK_COPY, /*< skip, pdb-skip >*/
+ GIMP_ADD_CHANNEL_MASK = GIMP_ADD_MASK_CHANNEL /*< skip, pdb-skip >*/
+#endif /* GIMP_DISABLE_DEPRECATED */
+} GimpAddMaskType;
+
+
+/**
+ * GimpBlendMode:
+ * @GIMP_BLEND_FG_BG_RGB: FG to BG (RGB)
+ * @GIMP_BLEND_FG_BG_HSV: FG to BG (HSV)
+ * @GIMP_BLEND_FG_TRANSPARENT: FG to transparent
+ * @GIMP_BLEND_CUSTOM: Custom gradient
+ * @GIMP_FG_BG_RGB_MODE: Deprecated alias for @GIMP_BLEND_FG_BG_RGB
+ * @GIMP_FG_BG_HSV_MODE: Deprecated alias for @GIMP_BLEND_FG_BG_HSV
+ * @GIMP_FG_TRANSPARENT_MODE: Deprecated alias for @GIMP_BLEND_FG_TRANSPARENT
+ * @GIMP_CUSTOM_MODE: Deprecated alias for @GIMP_BLEND_CUSTOM
+ *
+ * Types of gradients.
+ **/
+#define GIMP_TYPE_BLEND_MODE (gimp_blend_mode_get_type ())
+
+GType gimp_blend_mode_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_BLEND_FG_BG_RGB, /*< desc="FG to BG (RGB)" >*/
+ GIMP_BLEND_FG_BG_HSV, /*< desc="FG to BG (HSV)" >*/
+ GIMP_BLEND_FG_TRANSPARENT, /*< desc="FG to transparent" >*/
+ GIMP_BLEND_CUSTOM, /*< desc="Custom gradient" >*/
+
+#ifndef GIMP_DISABLE_DEPRECATED
+ GIMP_FG_BG_RGB_MODE = GIMP_BLEND_FG_BG_RGB, /*< skip, pdb-skip >*/
+ GIMP_FG_BG_HSV_MODE = GIMP_BLEND_FG_BG_HSV, /*< skip, pdb-skip >*/
+ GIMP_FG_TRANSPARENT_MODE = GIMP_BLEND_FG_TRANSPARENT, /*< skip, pdb-skip >*/
+ GIMP_CUSTOM_MODE = GIMP_BLEND_CUSTOM /*< skip, pdb-skip >*/
+#endif /* GIMP_DISABLE_DEPRECATED */
+} GimpBlendMode;
+
+
+/**
+ * GimpBrushGeneratedShape:
+ * @GIMP_BRUSH_GENERATED_CIRCLE: Circle
+ * @GIMP_BRUSH_GENERATED_SQUARE: Square
+ * @GIMP_BRUSH_GENERATED_DIAMOND: Diamond
+ *
+ * Shapes of generated brushes.
+ **/
+#define GIMP_TYPE_BRUSH_GENERATED_SHAPE (gimp_brush_generated_shape_get_type ())
+
+GType gimp_brush_generated_shape_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_BRUSH_GENERATED_CIRCLE, /*< desc="Circle" >*/
+ GIMP_BRUSH_GENERATED_SQUARE, /*< desc="Square" >*/
+ GIMP_BRUSH_GENERATED_DIAMOND /*< desc="Diamond" >*/
+} GimpBrushGeneratedShape;
+
+
+/**
+ * GimpBucketFillMode:
+ * @GIMP_BUCKET_FILL_FG: FG color fill
+ * @GIMP_BUCKET_FILL_BG: BG color fill
+ * @GIMP_BUCKET_FILL_PATTERN: Pattern fill
+ * @GIMP_FG_BUCKET_FILL: Deprecated alias for @GIMP_BUCKET_FILL_FG
+ * @GIMP_BG_BUCKET_FILL: Deprecated alias for @GIMP_BUCKET_FILL_BG
+ * @GIMP_PATTERN_BUCKET_FILL: Deprecated alias for @GIMP_BUCKET_FILL_PATTERN
+ *
+ * Bucket fill modes.
+ */
+#define GIMP_TYPE_BUCKET_FILL_MODE (gimp_bucket_fill_mode_get_type ())
+
+GType gimp_bucket_fill_mode_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_BUCKET_FILL_FG, /*< desc="FG color fill" >*/
+ GIMP_BUCKET_FILL_BG, /*< desc="BG color fill" >*/
+ GIMP_BUCKET_FILL_PATTERN, /*< desc="Pattern fill" >*/
+
+#ifndef GIMP_DISABLE_DEPRECATED
+ GIMP_FG_BUCKET_FILL = GIMP_BUCKET_FILL_FG, /*< skip, pdb-skip >*/
+ GIMP_BG_BUCKET_FILL = GIMP_BUCKET_FILL_BG, /*< skip, pdb-skip >*/
+ GIMP_PATTERN_BUCKET_FILL = GIMP_BUCKET_FILL_PATTERN /*< skip, pdb-skip >*/
+#endif /* GIMP_DISABLE_DEPRECATED */
+} GimpBucketFillMode;
+
+
+/**
+ * GimpCapStyle:
+ * @GIMP_CAP_BUTT: Butt
+ * @GIMP_CAP_ROUND: Round
+ * @GIMP_CAP_SQUARE: Square
+ *
+ * Style of line endings.
+ **/
+#define GIMP_TYPE_CAP_STYLE (gimp_cap_style_get_type ())
+
+GType gimp_cap_style_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_CAP_BUTT, /*< desc="Butt" >*/
+ GIMP_CAP_ROUND, /*< desc="Round" >*/
+ GIMP_CAP_SQUARE /*< desc="Square" >*/
+} GimpCapStyle;
+
+
+/**
+ * GimpChannelOps:
+ * @GIMP_CHANNEL_OP_ADD: Add to the current selection
+ * @GIMP_CHANNEL_OP_SUBTRACT: Subtract from the current selection
+ * @GIMP_CHANNEL_OP_REPLACE: Replace the current selection
+ * @GIMP_CHANNEL_OP_INTERSECT: Intersect with the current selection
+ *
+ * Operations to combine channels and selections.
+ **/
+#define GIMP_TYPE_CHANNEL_OPS (gimp_channel_ops_get_type ())
+
+GType gimp_channel_ops_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_CHANNEL_OP_ADD, /*< desc="Add to the current selection" >*/
+ GIMP_CHANNEL_OP_SUBTRACT, /*< desc="Subtract from the current selection" >*/
+ GIMP_CHANNEL_OP_REPLACE, /*< desc="Replace the current selection" >*/
+ GIMP_CHANNEL_OP_INTERSECT /*< desc="Intersect with the current selection" >*/
+} GimpChannelOps;
+
+
+/**
+ * GimpChannelType:
+ * @GIMP_CHANNEL_RED: Red
+ * @GIMP_CHANNEL_GREEN: Green
+ * @GIMP_CHANNEL_BLUE: Blue
+ * @GIMP_CHANNEL_GRAY: Gray
+ * @GIMP_CHANNEL_INDEXED: Indexed
+ * @GIMP_CHANNEL_ALPHA: Alpha
+ * @GIMP_RED_CHANNEL: Deprecated alias for @GIMP_CHANNEL_RED
+ * @GIMP_GREEN_CHANNEL: Deprecated alias for @GIMP_CHANNEL_GREEN
+ * @GIMP_BLUE_CHANNEL: Deprecated alias for @GIMP_CHANNEL_BLUE
+ * @GIMP_GRAY_CHANNEL: Deprecated alias for @GIMP_CHANNEL_GRAY
+ * @GIMP_INDEXED_CHANNEL: Deprecated alias for @GIMP_CHANNEL_INDEXED
+ * @GIMP_ALPHA_CHANNEL: Deprecated alias for @GIMP_CHANNEL_ALPHA
+ *
+ * Channels (as in color components).
+ **/
+#define GIMP_TYPE_CHANNEL_TYPE (gimp_channel_type_get_type ())
+
+GType gimp_channel_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_CHANNEL_RED, /*< desc="Red" >*/
+ GIMP_CHANNEL_GREEN, /*< desc="Green" >*/
+ GIMP_CHANNEL_BLUE, /*< desc="Blue" >*/
+ GIMP_CHANNEL_GRAY, /*< desc="Gray" >*/
+ GIMP_CHANNEL_INDEXED, /*< desc="Indexed" >*/
+ GIMP_CHANNEL_ALPHA, /*< desc="Alpha" >*/
+
+#ifndef GIMP_DISABLE_DEPRECATED
+ GIMP_RED_CHANNEL = GIMP_CHANNEL_RED, /*< skip, pdb-skip >*/
+ GIMP_GREEN_CHANNEL = GIMP_CHANNEL_GREEN, /*< skip, pdb-skip >*/
+ GIMP_BLUE_CHANNEL = GIMP_CHANNEL_BLUE, /*< skip, pdb-skip >*/
+ GIMP_GRAY_CHANNEL = GIMP_CHANNEL_GRAY, /*< skip, pdb-skip >*/
+ GIMP_INDEXED_CHANNEL = GIMP_CHANNEL_INDEXED, /*< skip, pdb-skip >*/
+ GIMP_ALPHA_CHANNEL = GIMP_CHANNEL_ALPHA /*< skip, pdb-skip >*/
+#endif /* GIMP_DISABLE_DEPRECATED */
+} GimpChannelType;
+
+
+/**
+ * GimpCheckSize:
+ * @GIMP_CHECK_SIZE_SMALL_CHECKS: Small
+ * @GIMP_CHECK_SIZE_MEDIUM_CHECKS: Medium
+ * @GIMP_CHECK_SIZE_LARGE_CHECKS: Large
+ *
+ * Size of the checkerboard indicating transparency.
+ **/
+#define GIMP_TYPE_CHECK_SIZE (gimp_check_size_get_type ())
+
+GType gimp_check_size_get_type (void) G_GNUC_CONST;
+
+typedef enum /*< pdb-skip >*/
+{
+ GIMP_CHECK_SIZE_SMALL_CHECKS = 0, /*< desc="Small" >*/
+ GIMP_CHECK_SIZE_MEDIUM_CHECKS = 1, /*< desc="Medium" >*/
+ GIMP_CHECK_SIZE_LARGE_CHECKS = 2 /*< desc="Large" >*/
+} GimpCheckSize;
+
+
+/**
+ * GimpCheckType:
+ * @GIMP_CHECK_TYPE_LIGHT_CHECKS: Light checks
+ * @GIMP_CHECK_TYPE_GRAY_CHECKS: Mid-tone checks
+ * @GIMP_CHECK_TYPE_DARK_CHECKS: Dark checks
+ * @GIMP_CHECK_TYPE_WHITE_ONLY: White only
+ * @GIMP_CHECK_TYPE_GRAY_ONLY: Gray only
+ * @GIMP_CHECK_TYPE_BLACK_ONLY: Black only
+ *
+ * Color/Brightness of the checkerboard indicating transparency.
+ **/
+#define GIMP_TYPE_CHECK_TYPE (gimp_check_type_get_type ())
+
+GType gimp_check_type_get_type (void) G_GNUC_CONST;
+
+typedef enum /*< pdb-skip >*/
+{
+ GIMP_CHECK_TYPE_LIGHT_CHECKS = 0, /*< desc="Light checks" >*/
+ GIMP_CHECK_TYPE_GRAY_CHECKS = 1, /*< desc="Mid-tone checks" >*/
+ GIMP_CHECK_TYPE_DARK_CHECKS = 2, /*< desc="Dark checks" >*/
+ GIMP_CHECK_TYPE_WHITE_ONLY = 3, /*< desc="White only" >*/
+ GIMP_CHECK_TYPE_GRAY_ONLY = 4, /*< desc="Gray only" >*/
+ GIMP_CHECK_TYPE_BLACK_ONLY = 5 /*< desc="Black only" >*/
+} GimpCheckType;
+
+
+/**
+ * GimpCloneType:
+ * @GIMP_CLONE_IMAGE: Clone from an image/drawable source
+ * @GIMP_CLONE_PATTERN: Clone from a pattern source
+ * @GIMP_IMAGE_CLONE: Deprecated alias for @GIMP_CLONE_IMAGE
+ * @GIMP_PATTERN_CLONE: Deprecated alias for @GIMP_CLONE_PATTERN
+ *
+ * Clone sources.
+ **/
+#define GIMP_TYPE_CLONE_TYPE (gimp_clone_type_get_type ())
+
+GType gimp_clone_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_CLONE_IMAGE, /*< desc="Image" >*/
+ GIMP_CLONE_PATTERN, /*< desc="Pattern" >*/
+
+#ifndef GIMP_DISABLE_DEPRECATED
+ GIMP_IMAGE_CLONE = GIMP_CLONE_IMAGE, /*< skip, pdb-skip >*/
+ GIMP_PATTERN_CLONE = GIMP_CLONE_PATTERN /*< skip, pdb-skip >*/
+#endif /* GIMP_DISABLE_DEPRECATED */
+} GimpCloneType;
+
+
+/**
+ * GimpColorTag:
+ * @GIMP_COLOR_TAG_NONE: None
+ * @GIMP_COLOR_TAG_BLUE: Blue
+ * @GIMP_COLOR_TAG_GREEN: Green
+ * @GIMP_COLOR_TAG_YELLOW: Yellow
+ * @GIMP_COLOR_TAG_ORANGE: Orange
+ * @GIMP_COLOR_TAG_BROWN: Brown
+ * @GIMP_COLOR_TAG_RED: Red
+ * @GIMP_COLOR_TAG_VIOLET: Violet
+ * @GIMP_COLOR_TAG_GRAY: Gray
+ *
+ * Possible tag colors.
+ *
+ * Since: 2.10
+ **/
+#define GIMP_TYPE_COLOR_TAG (gimp_color_tag_get_type ())
+
+GType gimp_color_tag_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_COLOR_TAG_NONE, /*< desc="None" >*/
+ GIMP_COLOR_TAG_BLUE, /*< desc="Blue" >*/
+ GIMP_COLOR_TAG_GREEN, /*< desc="Green" >*/
+ GIMP_COLOR_TAG_YELLOW, /*< desc="Yellow" >*/
+ GIMP_COLOR_TAG_ORANGE, /*< desc="Orange" >*/
+ GIMP_COLOR_TAG_BROWN, /*< desc="Brown" >*/
+ GIMP_COLOR_TAG_RED, /*< desc="Red" >*/
+ GIMP_COLOR_TAG_VIOLET, /*< desc="Violet" >*/
+ GIMP_COLOR_TAG_GRAY /*< desc="Gray" >*/
+} GimpColorTag;
+
+
+/**
+ * GimpComponentType:
+ * @GIMP_COMPONENT_TYPE_U8: 8-bit integer
+ * @GIMP_COMPONENT_TYPE_U16: 16-bit integer
+ * @GIMP_COMPONENT_TYPE_U32: 32-bit integer
+ * @GIMP_COMPONENT_TYPE_HALF: 16-bit floating point
+ * @GIMP_COMPONENT_TYPE_FLOAT: 32-bit floating point
+ * @GIMP_COMPONENT_TYPE_DOUBLE: 64-bit floating point
+ *
+ * Encoding types of image components.
+ *
+ * Since: 2.10
+ **/
+#define GIMP_TYPE_COMPONENT_TYPE (gimp_component_type_get_type ())
+
+GType gimp_component_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_COMPONENT_TYPE_U8 = 100, /*< desc="8-bit integer" >*/
+ GIMP_COMPONENT_TYPE_U16 = 200, /*< desc="16-bit integer" >*/
+ GIMP_COMPONENT_TYPE_U32 = 300, /*< desc="32-bit integer" >*/
+ GIMP_COMPONENT_TYPE_HALF = 500, /*< desc="16-bit floating point" >*/
+ GIMP_COMPONENT_TYPE_FLOAT = 600, /*< desc="32-bit floating point" >*/
+ GIMP_COMPONENT_TYPE_DOUBLE = 700 /*< desc="64-bit floating point" >*/
+} GimpComponentType;
+
+
+/**
+ * GimpConvertPaletteType:
+ * @GIMP_CONVERT_PALETTE_GENERATE: Generate optimum palette
+ * @GIMP_CONVERT_PALETTE_REUSE: Don't use this one
+ * @GIMP_CONVERT_PALETTE_WEB: Use web-optimized palette
+ * @GIMP_CONVERT_PALETTE_MONO: Use black and white (1-bit) palette
+ * @GIMP_CONVERT_PALETTE_CUSTOM: Use custom palette
+ * @GIMP_MAKE_PALETTE: Deprecated alias for
+ * @GIMP_CONVERT_PALETTE_GENERATE
+ * @GIMP_REUSE_PALETTE: Deprecated alias for
+ * @GIMP_CONVERT_PALETTE_REUSE
+ * @GIMP_WEB_PALETTE: Deprecated alias for
+ * @GIMP_CONVERT_PALETTE_WEB
+ * @GIMP_MONO_PALETTE: Deprecated alias for
+ @GIMP_CONVERT_PALETTE_MONO
+ * @GIMP_CUSTOM_PALETTE: Deprecated alias for
+ * @GIMP_CONVERT_PALETTE_CUSTOM
+ *
+ * Types of palettes for indexed conversion.
+ **/
+#define GIMP_TYPE_CONVERT_PALETTE_TYPE (gimp_convert_palette_type_get_type ())
+
+GType gimp_convert_palette_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_CONVERT_PALETTE_GENERATE, /*< desc="Generate optimum palette" >*/
+ GIMP_CONVERT_PALETTE_REUSE, /*< skip >*/
+ GIMP_CONVERT_PALETTE_WEB, /*< desc="Use web-optimized palette" >*/
+ GIMP_CONVERT_PALETTE_MONO, /*< desc="Use black and white (1-bit) palette" >*/
+ GIMP_CONVERT_PALETTE_CUSTOM, /*< desc="Use custom palette" >*/
+
+#ifndef GIMP_DISABLE_DEPRECATED
+ GIMP_MAKE_PALETTE = GIMP_CONVERT_PALETTE_GENERATE, /*< skip, pdb-skip >*/
+ GIMP_REUSE_PALETTE = GIMP_CONVERT_PALETTE_REUSE, /*< skip, pdb-skip >*/
+ GIMP_WEB_PALETTE = GIMP_CONVERT_PALETTE_WEB, /*< skip, pdb-skip >*/
+ GIMP_MONO_PALETTE = GIMP_CONVERT_PALETTE_MONO, /*< skip, pdb-skip >*/
+ GIMP_CUSTOM_PALETTE = GIMP_CONVERT_PALETTE_CUSTOM /*< skip, pdb-skip >*/
+#endif /* GIMP_DISABLE_DEPRECATED */
+} GimpConvertPaletteType;
+
+
+/**
+ * GimpConvolveType:
+ * @GIMP_CONVOLVE_BLUR: Blur
+ * @GIMP_CONVOLVE_SHARPEN: Sharpen
+ * @GIMP_BLUR_CONVOLVE: Deprecated alias for @GIMP_CONVOLVE_BLUR
+ * @GIMP_SHARPEN_CONVOLVE: Deprecated alias for @GIMP_CONVOLVE_SHARPEN
+ *
+ * Types of convolutions.
+ **/
+#define GIMP_TYPE_CONVOLVE_TYPE (gimp_convolve_type_get_type ())
+
+GType gimp_convolve_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_CONVOLVE_BLUR, /*< desc="Blur" >*/
+ GIMP_CONVOLVE_SHARPEN, /*< desc="Sharpen" >*/
+
+#ifndef GIMP_DISABLE_DEPRECATED
+ GIMP_BLUR_CONVOLVE = GIMP_CONVOLVE_BLUR, /*< skip, pdb-skip >*/
+ GIMP_SHARPEN_CONVOLVE = GIMP_CONVOLVE_SHARPEN /*< skip, pdb-skip >*/
+#endif /* GIMP_DISABLE_DEPRECATED */
+} GimpConvolveType;
+
+
+/**
+ * GimpDesaturateMode:
+ * @GIMP_DESATURATE_LIGHTNESS: Lightness (HSL)
+ * @GIMP_DESATURATE_LUMA: Luma
+ * @GIMP_DESATURATE_AVERAGE: Average (HSI Intensity)
+ * @GIMP_DESATURATE_LUMINANCE: Luminance
+ * @GIMP_DESATURATE_VALUE: Value (HSV)
+ * @GIMP_DESATURATE_LUMINOSITY: Deprecated alias for @GIMP_DESATURATE_LUMA
+ *
+ * Grayscale conversion methods.
+ **/
+#define GIMP_TYPE_DESATURATE_MODE (gimp_desaturate_mode_get_type ())
+
+GType gimp_desaturate_mode_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_DESATURATE_LIGHTNESS, /*< desc="Lightness (HSL)" >*/
+ GIMP_DESATURATE_LUMA, /*< desc="Luma" >*/
+ GIMP_DESATURATE_AVERAGE, /*< desc="Average (HSI Intensity)" >*/
+ GIMP_DESATURATE_LUMINANCE, /*< desc="Luminance" >*/
+ GIMP_DESATURATE_VALUE, /*< desc="Value (HSV)" >*/
+
+#ifndef GIMP_DISABLE_DEPRECATED
+ GIMP_DESATURATE_LUMINOSITY = GIMP_DESATURATE_LUMA /*< skip, pdb-skip >*/
+#endif /* GIMP_DISABLE_DEPRECATED */
+} GimpDesaturateMode;
+
+
+/**
+ * GimpDodgeBurnType:
+ * @GIMP_DODGE_BURN_TYPE_DODGE: Dodge
+ * @GIMP_DODGE_BURN_TYPE_BURN: Burn
+ * @GIMP_DODGE: Deprecated alias for @GIMP_DODGE_BURN_TYPE_DODGE
+ * @GIMP_BURN: Deprecated alias for @GIMP_DODGE_BURN_TYPE_BURN
+ *
+ * Methods for the dodge/burn operation.
+ **/
+#define GIMP_TYPE_DODGE_BURN_TYPE (gimp_dodge_burn_type_get_type ())
+
+GType gimp_dodge_burn_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_DODGE_BURN_TYPE_DODGE, /*< desc="Dodge" >*/
+ GIMP_DODGE_BURN_TYPE_BURN, /*< desc="Burn" >*/
+
+#ifndef GIMP_DISABLE_DEPRECATED
+ GIMP_DODGE = GIMP_DODGE_BURN_TYPE_DODGE, /*< skip, pdb-skip >*/
+ GIMP_BURN = GIMP_DODGE_BURN_TYPE_BURN /*< skip, pdb-skip >*/
+#endif /* GIMP_DISABLE_DEPRECATED */
+} GimpDodgeBurnType;
+
+
+/**
+ * GimpFillType:
+ * @GIMP_FILL_FOREGROUND: Foreground color
+ * @GIMP_FILL_BACKGROUND: Background color
+ * @GIMP_FILL_WHITE: White
+ * @GIMP_FILL_TRANSPARENT: Transparency
+ * @GIMP_FILL_PATTERN: Pattern
+ * @GIMP_FOREGROUND_FILL: Deprecated alias for @GIMP_FILL_FOREGROUND
+ * @GIMP_BACKGROUND_FILL: Deprecated alias for @GIMP_FILL_BACKGROUND
+ * @GIMP_WHITE_FILL: Deprecated alias for @GIMP_FILL_WHITE
+ * @GIMP_TRANSPARENT_FILL: Deprecated alias for @GIMP_FILL_TRANSPARENT
+ * @GIMP_PATTERN_FILL: Deprecated alias for @GIMP_FILL_PATTERN
+ *
+ * Types of filling.
+ **/
+#define GIMP_TYPE_FILL_TYPE (gimp_fill_type_get_type ())
+
+GType gimp_fill_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_FILL_FOREGROUND, /*< desc="Foreground color" >*/
+ GIMP_FILL_BACKGROUND, /*< desc="Background color" >*/
+ GIMP_FILL_WHITE, /*< desc="White" >*/
+ GIMP_FILL_TRANSPARENT, /*< desc="Transparency" >*/
+ GIMP_FILL_PATTERN, /*< desc="Pattern" >*/
+
+#ifndef GIMP_DISABLE_DEPRECATED
+ GIMP_FOREGROUND_FILL = GIMP_FILL_FOREGROUND, /*< skip, pdb-skip >*/
+ GIMP_BACKGROUND_FILL = GIMP_FILL_BACKGROUND, /*< skip, pdb-skip >*/
+ GIMP_WHITE_FILL = GIMP_FILL_WHITE, /*< skip, pdb-skip >*/
+ GIMP_TRANSPARENT_FILL = GIMP_FILL_TRANSPARENT, /*< skip, pdb-skip >*/
+ GIMP_PATTERN_FILL = GIMP_FILL_PATTERN /*< skip, pdb-skip >*/
+#endif /* GIMP_DISABLE_DEPRECATED */
+} GimpFillType;
+
+
+/**
+ * GimpForegroundExtractMode:
+ * @GIMP_FOREGROUND_EXTRACT_SIOX: Siox
+ * @GIMP_FOREGROUND_EXTRACT_MATTING: Matting (Since 2.10)
+ *
+ * Foreground extraxt engines.
+ **/
+#define GIMP_TYPE_FOREGROUND_EXTRACT_MODE (gimp_foreground_extract_mode_get_type ())
+
+GType gimp_foreground_extract_mode_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_FOREGROUND_EXTRACT_SIOX,
+ GIMP_FOREGROUND_EXTRACT_MATTING
+} GimpForegroundExtractMode;
+
+
+/**
+ * GimpGradientBlendColorSpace:
+ * @GIMP_GRADIENT_BLEND_RGB_PERCEPTUAL: Perceptual RGB
+ * @GIMP_GRADIENT_BLEND_RGB_LINEAR: Linear RGB
+ * @GIMP_GRADIENT_BLEND_CIE_LAB: CIE Lab
+ *
+ * Color space for blending gradients.
+ *
+ * Since: 2.10
+ */
+#define GIMP_TYPE_GRADIENT_BLEND_COLOR_SPACE (gimp_gradient_blend_color_space_get_type ())
+
+GType gimp_gradient_blend_color_space_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_GRADIENT_BLEND_RGB_PERCEPTUAL, /*< desc="Perceptual RGB", nick=rgb-perceptual >*/
+ GIMP_GRADIENT_BLEND_RGB_LINEAR, /*< desc="Linear RGB", nick=rgb-linear >*/
+ GIMP_GRADIENT_BLEND_CIE_LAB /*< desc="CIE Lab", nick=cie-lab >*/
+} GimpGradientBlendColorSpace;
+
+
+/**
+ * GimpGradientSegmentColor:
+ * @GIMP_GRADIENT_SEGMENT_RGB: RGB
+ * @GIMP_GRADIENT_SEGMENT_HSV_CCW: HSV (counter-clockwise hue)
+ * @GIMP_GRADIENT_SEGMENT_HSV_CW: HSV (clockwise hue)
+ *
+ * Coloring types for gradient segments.
+ **/
+#define GIMP_TYPE_GRADIENT_SEGMENT_COLOR (gimp_gradient_segment_color_get_type ())
+
+GType gimp_gradient_segment_color_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_GRADIENT_SEGMENT_RGB, /*< desc="RGB" >*/
+ GIMP_GRADIENT_SEGMENT_HSV_CCW, /*< desc="HSV (counter-clockwise hue)", abbrev="HSV (ccw)" >*/
+ GIMP_GRADIENT_SEGMENT_HSV_CW /*< desc="HSV (clockwise hue)", abbrev="HSV (cw)" >*/
+} GimpGradientSegmentColor;
+
+
+/**
+ * GimpGradientSegmentType:
+ * @GIMP_GRADIENT_SEGMENT_LINEAR: Linear
+ * @GIMP_GRADIENT_SEGMENT_CURVED: Curved
+ * @GIMP_GRADIENT_SEGMENT_SINE: Sinusoidal
+ * @GIMP_GRADIENT_SEGMENT_SPHERE_INCREASING: Spherical (increasing)
+ * @GIMP_GRADIENT_SEGMENT_SPHERE_DECREASING: Spherical (decreasing)
+ * @GIMP_GRADIENT_SEGMENT_STEP: Step
+ *
+ * Transition functions for gradient segments.
+ **/
+#define GIMP_TYPE_GRADIENT_SEGMENT_TYPE (gimp_gradient_segment_type_get_type ())
+
+GType gimp_gradient_segment_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_GRADIENT_SEGMENT_LINEAR, /*< desc="Linear" >*/
+ GIMP_GRADIENT_SEGMENT_CURVED, /*< desc="Curved" >*/
+ GIMP_GRADIENT_SEGMENT_SINE, /*< desc="Sinusoidal" >*/
+ GIMP_GRADIENT_SEGMENT_SPHERE_INCREASING, /*< desc="Spherical (increasing)", abbrev="Spherical (inc)" >*/
+ GIMP_GRADIENT_SEGMENT_SPHERE_DECREASING, /*< desc="Spherical (decreasing)", abbrev="Spherical (dec)" >*/
+ GIMP_GRADIENT_SEGMENT_STEP /*< desc="Step" >*/
+} GimpGradientSegmentType;
+
+
+/**
+ * GimpGradientType:
+ * @GIMP_GRADIENT_LINEAR: Linear
+ * @GIMP_GRADIENT_BILINEAR: Bi-linear
+ * @GIMP_GRADIENT_RADIAL: Radial
+ * @GIMP_GRADIENT_SQUARE: Square
+ * @GIMP_GRADIENT_CONICAL_SYMMETRIC: Conical (symmetric)
+ * @GIMP_GRADIENT_CONICAL_ASYMMETRIC: Conical (asymmetric)
+ * @GIMP_GRADIENT_SHAPEBURST_ANGULAR: Shaped (angular)
+ * @GIMP_GRADIENT_SHAPEBURST_SPHERICAL: Shaped (spherical)
+ * @GIMP_GRADIENT_SHAPEBURST_DIMPLED: Shaped (dimpled)
+ * @GIMP_GRADIENT_SPIRAL_CLOCKWISE: Spiral (clockwise)
+ * @GIMP_GRADIENT_SPIRAL_ANTICLOCKWISE: Spiral (counter-clockwise)
+ *
+ * Gradient shapes.
+ **/
+#define GIMP_TYPE_GRADIENT_TYPE (gimp_gradient_type_get_type ())
+
+GType gimp_gradient_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_GRADIENT_LINEAR, /*< desc="Linear" >*/
+ GIMP_GRADIENT_BILINEAR, /*< desc="Bi-linear" >*/
+ GIMP_GRADIENT_RADIAL, /*< desc="Radial" >*/
+ GIMP_GRADIENT_SQUARE, /*< desc="Square" >*/
+ GIMP_GRADIENT_CONICAL_SYMMETRIC, /*< desc="Conical (symmetric)", abbrev="Conical (sym)" >*/
+ GIMP_GRADIENT_CONICAL_ASYMMETRIC, /*< desc="Conical (asymmetric)", abbrev="Conical (asym)" >*/
+ GIMP_GRADIENT_SHAPEBURST_ANGULAR, /*< desc="Shaped (angular)" >*/
+ GIMP_GRADIENT_SHAPEBURST_SPHERICAL, /*< desc="Shaped (spherical)" >*/
+ GIMP_GRADIENT_SHAPEBURST_DIMPLED, /*< desc="Shaped (dimpled)" >*/
+ GIMP_GRADIENT_SPIRAL_CLOCKWISE, /*< desc="Spiral (clockwise)", abbrev="Spiral (cw)" >*/
+ GIMP_GRADIENT_SPIRAL_ANTICLOCKWISE /*< desc="Spiral (counter-clockwise)", abbrev="Spiral (ccw)" >*/
+} GimpGradientType;
+
+
+/**
+ * GimpGridStyle:
+ * @GIMP_GRID_DOTS: Intersections (dots)
+ * @GIMP_GRID_INTERSECTIONS: Intersections (crosshairs)
+ * @GIMP_GRID_ON_OFF_DASH: Dashed
+ * @GIMP_GRID_DOUBLE_DASH: Double dashed
+ * @GIMP_GRID_SOLID: Solid
+ *
+ * Rendering types for the display grid.
+ **/
+#define GIMP_TYPE_GRID_STYLE (gimp_grid_style_get_type ())
+
+GType gimp_grid_style_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_GRID_DOTS, /*< desc="Intersections (dots)" >*/
+ GIMP_GRID_INTERSECTIONS, /*< desc="Intersections (crosshairs)" >*/
+ GIMP_GRID_ON_OFF_DASH, /*< desc="Dashed" >*/
+ GIMP_GRID_DOUBLE_DASH, /*< desc="Double dashed" >*/
+ GIMP_GRID_SOLID /*< desc="Solid" >*/
+} GimpGridStyle;
+
+
+/**
+ * GimpHueRange:
+ * @GIMP_HUE_RANGE_ALL: All hues
+ * @GIMP_HUE_RANGE_RED: Red hues
+ * @GIMP_HUE_RANGE_YELLOW: Yellow hues
+ * @GIMP_HUE_RANGE_GREEN: Green hues
+ * @GIMP_HUE_RANGE_CYAN: Cyan hues
+ * @GIMP_HUE_RANGE_BLUE: Blue hues
+ * @GIMP_HUE_RANGE_MAGENTA: Magenta hues
+ * @GIMP_ALL_HUES: Deprecated alias for @GIMP_HUE_RANGE_ALL
+ * @GIMP_RED_HUES: Deprecated alias for @GIMP_HUE_RANGE_RED
+ * @GIMP_YELLOW_HUES: Deprecated alias for @GIMP_HUE_RANGE_YELLOW
+ * @GIMP_GREEN_HUES: Deprecated alias for @GIMP_HUE_RANGE_GREEN
+ * @GIMP_CYAN_HUES: Deprecated alias for @GIMP_HUE_RANGE_CYAN
+ * @GIMP_BLUE_HUES: Deprecated alias for @GIMP_HUE_RANGE_BLUE
+ * @GIMP_MAGENTA_HUES: Deprecated alias for @GIMP_HUE_RANGE_MAGENTA
+ *
+ * Hue ranges.
+ **/
+#define GIMP_TYPE_HUE_RANGE (gimp_hue_range_get_type ())
+
+GType gimp_hue_range_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_HUE_RANGE_ALL,
+ GIMP_HUE_RANGE_RED,
+ GIMP_HUE_RANGE_YELLOW,
+ GIMP_HUE_RANGE_GREEN,
+ GIMP_HUE_RANGE_CYAN,
+ GIMP_HUE_RANGE_BLUE,
+ GIMP_HUE_RANGE_MAGENTA,
+
+#ifndef GIMP_DISABLE_DEPRECATED
+ GIMP_ALL_HUES = GIMP_HUE_RANGE_ALL, /*< skip, pdb-skip >*/
+ GIMP_RED_HUES = GIMP_HUE_RANGE_RED, /*< skip, pdb-skip >*/
+ GIMP_YELLOW_HUES = GIMP_HUE_RANGE_YELLOW, /*< skip, pdb-skip >*/
+ GIMP_GREEN_HUES = GIMP_HUE_RANGE_GREEN, /*< skip, pdb-skip >*/
+ GIMP_CYAN_HUES = GIMP_HUE_RANGE_CYAN, /*< skip, pdb-skip >*/
+ GIMP_BLUE_HUES = GIMP_HUE_RANGE_BLUE, /*< skip, pdb-skip >*/
+ GIMP_MAGENTA_HUES = GIMP_HUE_RANGE_MAGENTA /*< skip, pdb-skip >*/
+#endif /* GIMP_DISABLE_DEPRECATED */
+} GimpHueRange;
+
+
+/**
+ * GimpIconType:
+ * @GIMP_ICON_TYPE_ICON_NAME: Icon name
+ * @GIMP_ICON_TYPE_INLINE_PIXBUF: Inline pixbuf
+ * @GIMP_ICON_TYPE_IMAGE_FILE: Image file
+ * @GIMP_ICON_TYPE_STOCK_ID: Deprecated alias for
+ * @GIMP_ICON_TYPE_ICON_NAME, old stock IDs
+ * are interpreted as icon names
+ *
+ * Icon types for plug-ins to register.
+ **/
+#define GIMP_TYPE_ICON_TYPE (gimp_icon_type_get_type ())
+
+GType gimp_icon_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_ICON_TYPE_ICON_NAME, /*< desc="Icon name" >*/
+ GIMP_ICON_TYPE_INLINE_PIXBUF, /*< desc="Inline pixbuf" >*/
+ GIMP_ICON_TYPE_IMAGE_FILE, /*< desc="Image file" >*/
+
+#ifndef GIMP_DISABLE_DEPRECATED
+ GIMP_ICON_TYPE_STOCK_ID = GIMP_ICON_TYPE_ICON_NAME /*< skip, pdb-skip >*/
+#endif /* GIMP_DISABLE_DEPRECATED */
+} GimpIconType;
+
+
+/**
+ * GimpImageBaseType:
+ * @GIMP_RGB: RGB color
+ * @GIMP_GRAY: Grayscale
+ * @GIMP_INDEXED: Indexed color
+ *
+ * Image color models.
+ **/
+#define GIMP_TYPE_IMAGE_BASE_TYPE (gimp_image_base_type_get_type ())
+
+GType gimp_image_base_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_RGB, /*< desc="RGB color" >*/
+ GIMP_GRAY, /*< desc="Grayscale" >*/
+ GIMP_INDEXED /*< desc="Indexed color" >*/
+} GimpImageBaseType;
+
+
+/**
+ * GimpImageType:
+ * @GIMP_RGB_IMAGE: RGB
+ * @GIMP_RGBA_IMAGE: RGB-alpha
+ * @GIMP_GRAY_IMAGE: Grayscale
+ * @GIMP_GRAYA_IMAGE: Grayscale-alpha
+ * @GIMP_INDEXED_IMAGE: Indexed
+ * @GIMP_INDEXEDA_IMAGE: Indexed-alpha
+ *
+ * Possible drawable types.
+ **/
+#define GIMP_TYPE_IMAGE_TYPE (gimp_image_type_get_type ())
+
+GType gimp_image_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_RGB_IMAGE, /*< desc="RGB" >*/
+ GIMP_RGBA_IMAGE, /*< desc="RGB-alpha" >*/
+ GIMP_GRAY_IMAGE, /*< desc="Grayscale" >*/
+ GIMP_GRAYA_IMAGE, /*< desc="Grayscale-alpha" >*/
+ GIMP_INDEXED_IMAGE, /*< desc="Indexed" >*/
+ GIMP_INDEXEDA_IMAGE /*< desc="Indexed-alpha" >*/
+} GimpImageType;
+
+
+/**
+ * GimpInkBlobType:
+ * @GIMP_INK_BLOB_TYPE_CIRCLE: Circle
+ * @GIMP_INK_BLOB_TYPE_SQUARE: Square
+ * @GIMP_INK_BLOB_TYPE_DIAMOND: Diamond
+ *
+ * Ink tool tips.
+ **/
+#define GIMP_TYPE_INK_BLOB_TYPE (gimp_ink_blob_type_get_type ())
+
+GType gimp_ink_blob_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_INK_BLOB_TYPE_CIRCLE, /*< desc="Circle" >*/
+ GIMP_INK_BLOB_TYPE_SQUARE, /*< desc="Square" >*/
+ GIMP_INK_BLOB_TYPE_DIAMOND /*< desc="Diamond" >*/
+} GimpInkBlobType;
+
+
+/**
+ * GimpInterpolationType:
+ * @GIMP_INTERPOLATION_NONE: None
+ * @GIMP_INTERPOLATION_LINEAR: Linear
+ * @GIMP_INTERPOLATION_CUBIC: Cubic
+ * @GIMP_INTERPOLATION_NOHALO: NoHalo
+ * @GIMP_INTERPOLATION_LOHALO: LoHalo
+ * @GIMP_INTERPOLATION_LANCZOS: Deprecated alias for @GIMP_INTERPOLATION_NOHALO
+ *
+ * Interpolation types.
+ **/
+#define GIMP_TYPE_INTERPOLATION_TYPE (gimp_interpolation_type_get_type ())
+
+GType gimp_interpolation_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_INTERPOLATION_NONE, /*< desc="None" >*/
+ GIMP_INTERPOLATION_LINEAR, /*< desc="Linear" >*/
+ GIMP_INTERPOLATION_CUBIC, /*< desc="Cubic" >*/
+ GIMP_INTERPOLATION_NOHALO, /*< desc="NoHalo" >*/
+ GIMP_INTERPOLATION_LOHALO, /*< desc="LoHalo" >*/
+
+#ifndef GIMP_DISABLE_DEPRECATED
+ GIMP_INTERPOLATION_LANCZOS = GIMP_INTERPOLATION_NOHALO /*< skip, pdb-skip >*/
+#endif /* GIMP_DISABLE_DEPRECATED */
+} GimpInterpolationType;
+
+
+/**
+ * GimpJoinStyle:
+ * @GIMP_JOIN_MITER: Miter
+ * @GIMP_JOIN_ROUND: Round
+ * @GIMP_JOIN_BEVEL: Bevel
+ *
+ * Line join styles.
+ **/
+#define GIMP_TYPE_JOIN_STYLE (gimp_join_style_get_type ())
+
+GType gimp_join_style_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_JOIN_MITER, /*< desc="Miter" >*/
+ GIMP_JOIN_ROUND, /*< desc="Round" >*/
+ GIMP_JOIN_BEVEL /*< desc="Bevel" >*/
+} GimpJoinStyle;
+
+
+/**
+ * GimpMaskApplyMode:
+ * @GIMP_MASK_APPLY: Apply the mask
+ * @GIMP_MASK_DISCARD: Discard the mask
+ *
+ * Layer mask apply modes.
+ **/
+#define GIMP_TYPE_MASK_APPLY_MODE (gimp_mask_apply_mode_get_type ())
+
+GType gimp_mask_apply_mode_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_MASK_APPLY,
+ GIMP_MASK_DISCARD
+} GimpMaskApplyMode;
+
+
+/**
+ * GimpMergeType:
+ * @GIMP_EXPAND_AS_NECESSARY: Expanded as necessary
+ * @GIMP_CLIP_TO_IMAGE: Clipped to image
+ * @GIMP_CLIP_TO_BOTTOM_LAYER: Clipped to bottom layer
+ * @GIMP_FLATTEN_IMAGE: Flatten
+ *
+ * Types of merging layers.
+ **/
+#define GIMP_TYPE_MERGE_TYPE (gimp_merge_type_get_type ())
+
+GType gimp_merge_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_EXPAND_AS_NECESSARY, /*< desc="Expanded as necessary" >*/
+ GIMP_CLIP_TO_IMAGE, /*< desc="Clipped to image" >*/
+ GIMP_CLIP_TO_BOTTOM_LAYER, /*< desc="Clipped to bottom layer" >*/
+ GIMP_FLATTEN_IMAGE /*< desc="Flatten" >*/
+} GimpMergeType;
+
+
+/**
+ * GimpMessageHandlerType:
+ * @GIMP_MESSAGE_BOX: A popup dialog
+ * @GIMP_CONSOLE: The terminal
+ * @GIMP_ERROR_CONSOLE: The error console dockable
+ *
+ * How to present messages.
+ **/
+#define GIMP_TYPE_MESSAGE_HANDLER_TYPE (gimp_message_handler_type_get_type ())
+
+GType gimp_message_handler_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_MESSAGE_BOX,
+ GIMP_CONSOLE,
+ GIMP_ERROR_CONSOLE
+} GimpMessageHandlerType;
+
+
+/**
+ * GimpOffsetType:
+ * @GIMP_OFFSET_BACKGROUND: Background
+ * @GIMP_OFFSET_TRANSPARENT: Transparent
+ * @GIMP_OFFSET_WRAP_AROUND: Wrap image around
+ *
+ * Background fill types for the offset operation.
+ **/
+#define GIMP_TYPE_OFFSET_TYPE (gimp_offset_type_get_type ())
+
+GType gimp_offset_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_OFFSET_BACKGROUND,
+ GIMP_OFFSET_TRANSPARENT,
+ GIMP_OFFSET_WRAP_AROUND
+} GimpOffsetType;
+
+
+/**
+ * GimpOrientationType:
+ * @GIMP_ORIENTATION_HORIZONTAL: Horizontal
+ * @GIMP_ORIENTATION_VERTICAL: Vertical
+ * @GIMP_ORIENTATION_UNKNOWN: Unknown
+ *
+ * Orientations for various purposes.
+ **/
+#define GIMP_TYPE_ORIENTATION_TYPE (gimp_orientation_type_get_type ())
+
+GType gimp_orientation_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_ORIENTATION_HORIZONTAL, /*< desc="Horizontal" >*/
+ GIMP_ORIENTATION_VERTICAL, /*< desc="Vertical" >*/
+ GIMP_ORIENTATION_UNKNOWN /*< desc="Unknown" >*/
+} GimpOrientationType;
+
+
+/**
+ * GimpPaintApplicationMode:
+ * @GIMP_PAINT_CONSTANT: Constant
+ * @GIMP_PAINT_INCREMENTAL: Incremental
+ *
+ * Paint application modes.
+ **/
+#define GIMP_TYPE_PAINT_APPLICATION_MODE (gimp_paint_application_mode_get_type ())
+
+GType gimp_paint_application_mode_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_PAINT_CONSTANT, /*< desc="Constant" >*/
+ GIMP_PAINT_INCREMENTAL /*< desc="Incremental" >*/
+} GimpPaintApplicationMode;
+
+
+/**
+ * GimpPDBArgType:
+ * @GIMP_PDB_INT32: 32-bit integer
+ * @GIMP_PDB_INT16: 16-bit integer
+ * @GIMP_PDB_INT8: 8-bit integer
+ * @GIMP_PDB_FLOAT: Float
+ * @GIMP_PDB_STRING: String
+ * @GIMP_PDB_INT32ARRAY: Array of INT32
+ * @GIMP_PDB_INT16ARRAY: Array of INT16
+ * @GIMP_PDB_INT8ARRAY: Array of INT8
+ * @GIMP_PDB_FLOATARRAY: Array of floats
+ * @GIMP_PDB_STRINGARRAY: Array of strings
+ * @GIMP_PDB_COLOR: Color
+ * @GIMP_PDB_ITEM: Item ID
+ * @GIMP_PDB_DISPLAY: Display ID
+ * @GIMP_PDB_IMAGE: Image ID
+ * @GIMP_PDB_LAYER: Layer ID
+ * @GIMP_PDB_CHANNEL: Channel ID
+ * @GIMP_PDB_DRAWABLE: Drawable ID
+ * @GIMP_PDB_SELECTION: Selection ID
+ * @GIMP_PDB_COLORARRAY: Array of colors
+ * @GIMP_PDB_VECTORS: Vectors (psath) ID
+ * @GIMP_PDB_PARASITE: Parasite
+ * @GIMP_PDB_STATUS: Procedure return status
+ * @GIMP_PDB_END: Marker for last enum value
+ * @GIMP_PDB_PATH: Deprecated alias for @GIMP_PDB_VECTORS
+ * @GIMP_PDB_BOUNDARY: Deprecated alias for @GIMP_PDB_COLORARRAY
+ * @GIMP_PDB_REGION: Deprecated alias for @GIMP_PDB_ITEM
+ *
+ * Parameter types of the PDB.
+ **/
+#define GIMP_TYPE_PDB_ARG_TYPE (gimp_pdb_arg_type_get_type ())
+
+GType gimp_pdb_arg_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_PDB_INT32,
+ GIMP_PDB_INT16,
+ GIMP_PDB_INT8,
+ GIMP_PDB_FLOAT,
+ GIMP_PDB_STRING,
+ GIMP_PDB_INT32ARRAY,
+ GIMP_PDB_INT16ARRAY,
+ GIMP_PDB_INT8ARRAY,
+ GIMP_PDB_FLOATARRAY,
+ GIMP_PDB_STRINGARRAY,
+ GIMP_PDB_COLOR,
+ GIMP_PDB_ITEM,
+ GIMP_PDB_DISPLAY,
+ GIMP_PDB_IMAGE,
+ GIMP_PDB_LAYER,
+ GIMP_PDB_CHANNEL,
+ GIMP_PDB_DRAWABLE,
+ GIMP_PDB_SELECTION,
+ GIMP_PDB_COLORARRAY,
+ GIMP_PDB_VECTORS,
+ GIMP_PDB_PARASITE,
+ GIMP_PDB_STATUS,
+ GIMP_PDB_END,
+
+#ifndef GIMP_DISABLE_DEPRECATED
+ GIMP_PDB_PATH = GIMP_PDB_VECTORS, /*< skip >*/
+ GIMP_PDB_BOUNDARY = GIMP_PDB_COLORARRAY, /*< skip >*/
+ GIMP_PDB_REGION = GIMP_PDB_ITEM /*< skip >*/
+#endif /* GIMP_DISABLE_DEPRECATED */
+} GimpPDBArgType;
+
+
+/**
+ * GimpPDBErrorHandler:
+ * @GIMP_PDB_ERROR_HANDLER_INTERNAL: Internal
+ * @GIMP_PDB_ERROR_HANDLER_PLUGIN: Plug-In
+ *
+ * PDB error handlers.
+ **/
+#define GIMP_TYPE_PDB_ERROR_HANDLER (gimp_pdb_error_handler_get_type ())
+
+GType gimp_pdb_error_handler_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_PDB_ERROR_HANDLER_INTERNAL,
+ GIMP_PDB_ERROR_HANDLER_PLUGIN
+} GimpPDBErrorHandler;
+
+
+/**
+ * GimpPDBProcType:
+ * @GIMP_INTERNAL: Internal GIMP procedure
+ * @GIMP_PLUGIN: GIMP Plug-In
+ * @GIMP_EXTENSION: GIMP Extension
+ * @GIMP_TEMPORARY: Temporary Procedure
+ *
+ * Types of PDB procedures.
+ **/
+#define GIMP_TYPE_PDB_PROC_TYPE (gimp_pdb_proc_type_get_type ())
+
+GType gimp_pdb_proc_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_INTERNAL, /*< desc="Internal GIMP procedure" >*/
+ GIMP_PLUGIN, /*< desc="GIMP Plug-In" >*/
+ GIMP_EXTENSION, /*< desc="GIMP Extension" >*/
+ GIMP_TEMPORARY /*< desc="Temporary Procedure" >*/
+} GimpPDBProcType;
+
+
+/**
+ * GimpPDBStatusType:
+ * @GIMP_PDB_EXECUTION_ERROR: Execution error
+ * @GIMP_PDB_CALLING_ERROR: Calling error
+ * @GIMP_PDB_PASS_THROUGH: Pass through
+ * @GIMP_PDB_SUCCESS: Success
+ * @GIMP_PDB_CANCEL: User cancel
+ *
+ * Return status of PDB calls.
+ **/
+#define GIMP_TYPE_PDB_STATUS_TYPE (gimp_pdb_status_type_get_type ())
+
+GType gimp_pdb_status_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_PDB_EXECUTION_ERROR,
+ GIMP_PDB_CALLING_ERROR,
+ GIMP_PDB_PASS_THROUGH,
+ GIMP_PDB_SUCCESS,
+ GIMP_PDB_CANCEL
+} GimpPDBStatusType;
+
+
+/**
+ * GimpPrecision:
+ * @GIMP_PRECISION_U8_LINEAR: 8-bit linear integer
+ * @GIMP_PRECISION_U8_GAMMA: 8-bit gamma integer
+ * @GIMP_PRECISION_U16_LINEAR: 16-bit linear integer
+ * @GIMP_PRECISION_U16_GAMMA: 16-bit gamma integer
+ * @GIMP_PRECISION_U32_LINEAR: 32-bit linear integer
+ * @GIMP_PRECISION_U32_GAMMA: 32-bit gamma integer
+ * @GIMP_PRECISION_HALF_LINEAR: 16-bit linear floating point
+ * @GIMP_PRECISION_HALF_GAMMA: 16-bit gamma floating point
+ * @GIMP_PRECISION_FLOAT_LINEAR: 32-bit linear floating point
+ * @GIMP_PRECISION_FLOAT_GAMMA: 32-bit gamma floating point
+ * @GIMP_PRECISION_DOUBLE_LINEAR: 64-bit linear floating point
+ * @GIMP_PRECISION_DOUBLE_GAMMA: 64-bit gamma floating point
+ *
+ * Precisions for pixel encoding.
+ *
+ * Since: 2.10
+ **/
+#define GIMP_TYPE_PRECISION (gimp_precision_get_type ())
+
+GType gimp_precision_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_PRECISION_U8_LINEAR = 100, /*< desc="8-bit linear integer" >*/
+ GIMP_PRECISION_U8_GAMMA = 150, /*< desc="8-bit gamma integer" >*/
+ GIMP_PRECISION_U16_LINEAR = 200, /*< desc="16-bit linear integer" >*/
+ GIMP_PRECISION_U16_GAMMA = 250, /*< desc="16-bit gamma integer" >*/
+ GIMP_PRECISION_U32_LINEAR = 300, /*< desc="32-bit linear integer" >*/
+ GIMP_PRECISION_U32_GAMMA = 350, /*< desc="32-bit gamma integer" >*/
+ GIMP_PRECISION_HALF_LINEAR = 500, /*< desc="16-bit linear floating point" >*/
+ GIMP_PRECISION_HALF_GAMMA = 550, /*< desc="16-bit gamma floating point" >*/
+ GIMP_PRECISION_FLOAT_LINEAR = 600, /*< desc="32-bit linear floating point" >*/
+ GIMP_PRECISION_FLOAT_GAMMA = 650, /*< desc="32-bit gamma floating point" >*/
+ GIMP_PRECISION_DOUBLE_LINEAR = 700, /*< desc="64-bit linear floating point" >*/
+ GIMP_PRECISION_DOUBLE_GAMMA = 750 /*< desc="64-bit gamma floating point" >*/
+} GimpPrecision;
+
+
+/**
+ * GimpProgressCommand:
+ * @GIMP_PROGRESS_COMMAND_START: Start a progress
+ * @GIMP_PROGRESS_COMMAND_END: End the progress
+ * @GIMP_PROGRESS_COMMAND_SET_TEXT: Set the text
+ * @GIMP_PROGRESS_COMMAND_SET_VALUE: Set the percentage
+ * @GIMP_PROGRESS_COMMAND_PULSE: Pulse the progress
+ * @GIMP_PROGRESS_COMMAND_GET_WINDOW: Get the window where the progress is shown
+ *
+ * Commands for the progress API.
+ **/
+#define GIMP_TYPE_PROGRESS_COMMAND (gimp_progress_command_get_type ())
+
+GType gimp_progress_command_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_PROGRESS_COMMAND_START,
+ GIMP_PROGRESS_COMMAND_END,
+ GIMP_PROGRESS_COMMAND_SET_TEXT,
+ GIMP_PROGRESS_COMMAND_SET_VALUE,
+ GIMP_PROGRESS_COMMAND_PULSE,
+ GIMP_PROGRESS_COMMAND_GET_WINDOW
+} GimpProgressCommand;
+
+
+/**
+ * GimpRepeatMode:
+ * @GIMP_REPEAT_NONE: None (extend)
+ * @GIMP_REPEAT_SAWTOOTH: Sawtooth wave
+ * @GIMP_REPEAT_TRIANGULAR: Triangular wave
+ * @GIMP_REPEAT_TRUNCATE: Truncate
+ *
+ * Repeat modes for example for gradients.
+ **/
+#define GIMP_TYPE_REPEAT_MODE (gimp_repeat_mode_get_type ())
+
+GType gimp_repeat_mode_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_REPEAT_NONE, /*< desc="None (extend)" >*/
+ GIMP_REPEAT_SAWTOOTH, /*< desc="Sawtooth wave" >*/
+ GIMP_REPEAT_TRIANGULAR, /*< desc="Triangular wave" >*/
+ GIMP_REPEAT_TRUNCATE /*< desc="Truncate" >*/
+} GimpRepeatMode;
+
+
+/**
+ * GimpRotationType:
+ * @GIMP_ROTATE_90: 90 degrees
+ * @GIMP_ROTATE_180: 180 degrees
+ * @GIMP_ROTATE_270: 270 degrees
+ *
+ * Types of simple rotations.
+ **/
+#define GIMP_TYPE_ROTATION_TYPE (gimp_rotation_type_get_type ())
+
+GType gimp_rotation_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_ROTATE_90,
+ GIMP_ROTATE_180,
+ GIMP_ROTATE_270
+} GimpRotationType;
+
+
+/**
+ * GimpRunMode:
+ * @GIMP_RUN_INTERACTIVE: Run interactively
+ * @GIMP_RUN_NONINTERACTIVE: Run non-interactively
+ * @GIMP_RUN_WITH_LAST_VALS: Run with last used values
+ *
+ * Run modes for plug-ins.
+ **/
+#define GIMP_TYPE_RUN_MODE (gimp_run_mode_get_type ())
+
+GType gimp_run_mode_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_RUN_INTERACTIVE, /*< desc="Run interactively" >*/
+ GIMP_RUN_NONINTERACTIVE, /*< desc="Run non-interactively" >*/
+ GIMP_RUN_WITH_LAST_VALS /*< desc="Run with last used values" >*/
+} GimpRunMode;
+
+
+/**
+ * GimpSelectCriterion:
+ * @GIMP_SELECT_CRITERION_COMPOSITE: Composite
+ * @GIMP_SELECT_CRITERION_R: Red
+ * @GIMP_SELECT_CRITERION_G: Green
+ * @GIMP_SELECT_CRITERION_B: Blue
+ * @GIMP_SELECT_CRITERION_H: HSV Hue
+ * @GIMP_SELECT_CRITERION_S: HSV Saturation
+ * @GIMP_SELECT_CRITERION_V: HSV Value
+ * @GIMP_SELECT_CRITERION_A: Alpha
+ * @GIMP_SELECT_CRITERION_LCH_L: LCh Lightness
+ * @GIMP_SELECT_CRITERION_LCH_C: LCh Chroma
+ * @GIMP_SELECT_CRITERION_LCH_H: LCh Hue
+ *
+ * Criterions for color similarity.
+ **/
+#define GIMP_TYPE_SELECT_CRITERION (gimp_select_criterion_get_type ())
+
+GType gimp_select_criterion_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_SELECT_CRITERION_COMPOSITE, /*< desc="Composite" >*/
+ GIMP_SELECT_CRITERION_R, /*< desc="Red" >*/
+ GIMP_SELECT_CRITERION_G, /*< desc="Green" >*/
+ GIMP_SELECT_CRITERION_B, /*< desc="Blue" >*/
+ GIMP_SELECT_CRITERION_H, /*< desc="HSV Hue" >*/
+ GIMP_SELECT_CRITERION_S, /*< desc="HSV Saturation" >*/
+ GIMP_SELECT_CRITERION_V, /*< desc="HSV Value" >*/
+ GIMP_SELECT_CRITERION_A, /*< desc="Alpha" >*/
+ GIMP_SELECT_CRITERION_LCH_L, /*< desc="LCh Lightness" >*/
+ GIMP_SELECT_CRITERION_LCH_C, /*< desc="LCh Chroma" >*/
+ GIMP_SELECT_CRITERION_LCH_H, /*< desc="LCh Hue" >*/
+} GimpSelectCriterion;
+
+
+/**
+ * GimpSizeType:
+ * @GIMP_PIXELS: Pixels
+ * @GIMP_POINTS: Points
+ *
+ * Size types for the old-style text API.
+ **/
+#define GIMP_TYPE_SIZE_TYPE (gimp_size_type_get_type ())
+
+GType gimp_size_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_PIXELS, /*< desc="Pixels" >*/
+ GIMP_POINTS /*< desc="Points" >*/
+} GimpSizeType;
+
+
+/**
+ * GimpStackTraceMode:
+ * @GIMP_STACK_TRACE_NEVER: Never
+ * @GIMP_STACK_TRACE_QUERY: Ask each time
+ * @GIMP_STACK_TRACE_ALWAYS: Always
+ *
+ * When to generate stack traces in case of an error.
+ **/
+#define GIMP_TYPE_STACK_TRACE_MODE (gimp_stack_trace_mode_get_type ())
+
+GType gimp_stack_trace_mode_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_STACK_TRACE_NEVER,
+ GIMP_STACK_TRACE_QUERY,
+ GIMP_STACK_TRACE_ALWAYS
+} GimpStackTraceMode;
+
+
+/**
+ * GimpStrokeMethod:
+ * @GIMP_STROKE_LINE: Stroke line
+ * @GIMP_STROKE_PAINT_METHOD: Stroke with a paint tool
+ *
+ * Methods of stroking selections and paths.
+ **/
+#define GIMP_TYPE_STROKE_METHOD (gimp_stroke_method_get_type ())
+
+GType gimp_stroke_method_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_STROKE_LINE, /*< desc="Stroke line" >*/
+ GIMP_STROKE_PAINT_METHOD /*< desc="Stroke with a paint tool" >*/
+} GimpStrokeMethod;
+
+
+/**
+ * GimpTextDirection:
+ * @GIMP_TEXT_DIRECTION_LTR: From left to right
+ * @GIMP_TEXT_DIRECTION_RTL: From right to left
+ * @GIMP_TEXT_DIRECTION_TTB_RTL: Characters are from top to bottom, Lines are from right to left
+ * @GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT: Upright characters are from top to bottom, Lines are from right to left
+ * @GIMP_TEXT_DIRECTION_TTB_LTR: Characters are from top to bottom, Lines are from left to right
+ * @GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT: Upright characters are from top to bottom, Lines are from left to right
+ *
+ * Text directions.
+ **/
+#define GIMP_TYPE_TEXT_DIRECTION (gimp_text_direction_get_type ())
+
+GType gimp_text_direction_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_TEXT_DIRECTION_LTR, /*< desc="From left to right" >*/
+ GIMP_TEXT_DIRECTION_RTL, /*< desc="From right to left" >*/
+ GIMP_TEXT_DIRECTION_TTB_RTL, /*< desc="Vertical, right to left (mixed orientation)" >*/
+ GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT, /*< desc="Vertical, right to left (upright orientation)" >*/
+ GIMP_TEXT_DIRECTION_TTB_LTR, /*< desc="Vertical, left to right (mixed orientation)" >*/
+ GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT /*< desc="Vertical, left to right (upright orientation)" >*/
+} GimpTextDirection;
+
+
+/**
+ * GimpTextHintStyle:
+ * @GIMP_TEXT_HINT_STYLE_NONE: None
+ * @GIMP_TEXT_HINT_STYLE_SLIGHT: Slight
+ * @GIMP_TEXT_HINT_STYLE_MEDIUM: Medium
+ * @GIMP_TEXT_HINT_STYLE_FULL: Full
+ *
+ * Text hint strengths.
+ **/
+#define GIMP_TYPE_TEXT_HINT_STYLE (gimp_text_hint_style_get_type ())
+
+GType gimp_text_hint_style_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_TEXT_HINT_STYLE_NONE, /*< desc="None" >*/
+ GIMP_TEXT_HINT_STYLE_SLIGHT, /*< desc="Slight" >*/
+ GIMP_TEXT_HINT_STYLE_MEDIUM, /*< desc="Medium" >*/
+ GIMP_TEXT_HINT_STYLE_FULL /*< desc="Full" >*/
+} GimpTextHintStyle;
+
+
+/**
+ * GimpTextJustification:
+ * @GIMP_TEXT_JUSTIFY_LEFT: Left justified
+ * @GIMP_TEXT_JUSTIFY_RIGHT: Right justified
+ * @GIMP_TEXT_JUSTIFY_CENTER: Centered
+ * @GIMP_TEXT_JUSTIFY_FILL: Filled
+ *
+ * Text justifications.
+ **/
+#define GIMP_TYPE_TEXT_JUSTIFICATION (gimp_text_justification_get_type ())
+
+GType gimp_text_justification_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_TEXT_JUSTIFY_LEFT, /*< desc="Left justified" >*/
+ GIMP_TEXT_JUSTIFY_RIGHT, /*< desc="Right justified" >*/
+ GIMP_TEXT_JUSTIFY_CENTER, /*< desc="Centered" >*/
+ GIMP_TEXT_JUSTIFY_FILL /*< desc="Filled" >*/
+} GimpTextJustification;
+
+
+/**
+ * GimpTransferMode:
+ * @GIMP_TRANSFER_SHADOWS: Shadows
+ * @GIMP_TRANSFER_MIDTONES: Midtones
+ * @GIMP_TRANSFER_HIGHLIGHTS: Highlights
+ * @GIMP_SHADOWS: Deprecated alias for @GIMP_TRANSFER_SHADOWS
+ * @GIMP_MIDTONES: Deprecated alias for @GIMP_TRANSFER_MIDTONES
+ * @GIMP_HIGHLIGHTS: Deprecated alias for @GIMP_TRANSFER_HIGHLIGHTS
+ *
+ * For choosing which brightness ranges to transform.
+ **/
+#define GIMP_TYPE_TRANSFER_MODE (gimp_transfer_mode_get_type ())
+
+GType gimp_transfer_mode_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_TRANSFER_SHADOWS, /*< desc="Shadows" >*/
+ GIMP_TRANSFER_MIDTONES, /*< desc="Midtones" >*/
+ GIMP_TRANSFER_HIGHLIGHTS, /*< desc="Highlights" >*/
+
+#ifndef GIMP_DISABLE_DEPRECATED
+ GIMP_SHADOWS = GIMP_TRANSFER_SHADOWS, /*< skip, pdb-skip >*/
+ GIMP_MIDTONES = GIMP_TRANSFER_MIDTONES, /*< skip, pdb-skip >*/
+ GIMP_HIGHLIGHTS = GIMP_TRANSFER_HIGHLIGHTS /*< skip, pdb-skip >*/
+#endif /* GIMP_DISABLE_DEPRECATED */
+} GimpTransferMode;
+
+
+/**
+ * GimpTransformDirection:
+ * @GIMP_TRANSFORM_FORWARD: Normal (Forward)
+ * @GIMP_TRANSFORM_BACKWARD: Corrective (Backward)
+ *
+ * Transform directions.
+ **/
+#define GIMP_TYPE_TRANSFORM_DIRECTION (gimp_transform_direction_get_type ())
+
+GType gimp_transform_direction_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_TRANSFORM_FORWARD, /*< desc="Normal (Forward)" >*/
+ GIMP_TRANSFORM_BACKWARD /*< desc="Corrective (Backward)" >*/
+} GimpTransformDirection;
+
+
+/**
+ * GimpTransformResize:
+ * @GIMP_TRANSFORM_RESIZE_ADJUST: Adjust
+ * @GIMP_TRANSFORM_RESIZE_CLIP: Clip
+ * @GIMP_TRANSFORM_RESIZE_CROP: Crop to result
+ * @GIMP_TRANSFORM_RESIZE_CROP_WITH_ASPECT: Crop with aspect
+ *
+ * Ways of clipping the result when transforming drawables.
+ **/
+#define GIMP_TYPE_TRANSFORM_RESIZE (gimp_transform_resize_get_type ())
+
+GType gimp_transform_resize_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_TRANSFORM_RESIZE_ADJUST, /*< desc="Adjust" >*/
+ GIMP_TRANSFORM_RESIZE_CLIP, /*< desc="Clip" >*/
+ GIMP_TRANSFORM_RESIZE_CROP, /*< desc="Crop to result" >*/
+ GIMP_TRANSFORM_RESIZE_CROP_WITH_ASPECT /*< desc="Crop with aspect" >*/
+} GimpTransformResize;
+
+
+/**
+ * GimpUnit:
+ * @GIMP_UNIT_PIXEL: Pixels
+ * @GIMP_UNIT_INCH: Inches
+ * @GIMP_UNIT_MM: Millimeters
+ * @GIMP_UNIT_POINT: Points
+ * @GIMP_UNIT_PICA: Picas
+ * @GIMP_UNIT_END: Marker for end-of-builtin-units
+ * @GIMP_UNIT_PERCENT: Pseudo-unit percent
+ *
+ * Units used for dimensions in images.
+ **/
+typedef enum /*< skip >*/
+{
+ GIMP_UNIT_PIXEL = 0,
+
+ GIMP_UNIT_INCH = 1,
+ GIMP_UNIT_MM = 2,
+ GIMP_UNIT_POINT = 3,
+ GIMP_UNIT_PICA = 4,
+
+ GIMP_UNIT_END = 5,
+
+ GIMP_UNIT_PERCENT = 65536 /*< pdb-skip >*/
+} GimpUnit;
+
+
+#ifndef GIMP_DISABLE_DEPRECATED
+/**
+ * GimpUserDirectory:
+ * @GIMP_USER_DIRECTORY_DESKTOP: Deprecated
+ * @GIMP_USER_DIRECTORY_DOCUMENTS: Deprecated
+ * @GIMP_USER_DIRECTORY_DOWNLOAD: Deprecated
+ * @GIMP_USER_DIRECTORY_MUSIC: Deprecated
+ * @GIMP_USER_DIRECTORY_PICTURES: Deprecated
+ * @GIMP_USER_DIRECTORY_PUBLIC_SHARE: Deprecated
+ * @GIMP_USER_DIRECTORY_TEMPLATES: Deprecated
+ * @GIMP_USER_DIRECTORY_VIDEOS: Deprecated
+ *
+ * Deprecated enum, don't use.
+ **/
+#define GIMP_TYPE_USER_DIRECTORY (gimp_user_directory_get_type ())
+
+GType gimp_user_directory_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_USER_DIRECTORY_DESKTOP,
+ GIMP_USER_DIRECTORY_DOCUMENTS,
+ GIMP_USER_DIRECTORY_DOWNLOAD,
+ GIMP_USER_DIRECTORY_MUSIC,
+ GIMP_USER_DIRECTORY_PICTURES,
+ GIMP_USER_DIRECTORY_PUBLIC_SHARE,
+ GIMP_USER_DIRECTORY_TEMPLATES,
+ GIMP_USER_DIRECTORY_VIDEOS
+} GimpUserDirectory;
+#endif /* !GIMP_DISABLE_DEPRECATED */
+
+
+/**
+ * GimpVectorsStrokeType:
+ * @GIMP_VECTORS_STROKE_TYPE_BEZIER: A bezier stroke
+ *
+ * Possible type of strokes in vectors objects.
+ **/
+#define GIMP_TYPE_VECTORS_STROKE_TYPE (gimp_vectors_stroke_type_get_type ())
+
+GType gimp_vectors_stroke_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_VECTORS_STROKE_TYPE_BEZIER
+} GimpVectorsStrokeType;
+
+G_END_DECLS
+
+#endif /* __GIMP_BASE_ENUMS_H__ */
diff --git a/libgimpbase/gimpbasetypes.c b/libgimpbase/gimpbasetypes.c
new file mode 100644
index 0000000..fe22464
--- /dev/null
+++ b/libgimpbase/gimpbasetypes.c
@@ -0,0 +1,244 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimpbasetypes.c
+ * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+
+#include "gimpbasetypes.h"
+
+
+/**
+ * SECTION: gimpbasetypes
+ * @title: gimpbasetypes
+ * @short_description: Translation between gettext translation domain
+ * identifier and GType.
+ *
+ * Translation between gettext translation domain identifier and
+ * GType.
+ **/
+
+
+static GQuark gimp_translation_domain_quark (void) G_GNUC_CONST;
+static GQuark gimp_translation_context_quark (void) G_GNUC_CONST;
+static GQuark gimp_value_descriptions_quark (void) G_GNUC_CONST;
+
+
+/**
+ * gimp_type_set_translation_domain:
+ * @type: a #GType
+ * @domain: a constant string that identifies a translation domain or %NULL
+ *
+ * This function attaches a constant string as a gettext translation
+ * domain identifier to a #GType. The only purpose of this function is
+ * to use it when registering a #G_TYPE_ENUM with translatable value
+ * names.
+ *
+ * Since: 2.2
+ **/
+void
+gimp_type_set_translation_domain (GType type,
+ const gchar *domain)
+{
+ g_type_set_qdata (type,
+ gimp_translation_domain_quark (), (gpointer) domain);
+}
+
+/**
+ * gimp_type_get_translation_domain:
+ * @type: a #GType
+ *
+ * Retrieves the gettext translation domain identifier that has been
+ * previously set using gimp_type_set_translation_domain(). You should
+ * not need to use this function directly, use gimp_enum_get_value()
+ * or gimp_enum_value_get_desc() instead.
+ *
+ * Return value: the translation domain associated with @type
+ * or %NULL if no domain was set
+ *
+ * Since: 2.2
+ **/
+const gchar *
+gimp_type_get_translation_domain (GType type)
+{
+ return (const gchar *) g_type_get_qdata (type,
+ gimp_translation_domain_quark ());
+}
+
+/**
+ * gimp_type_set_translation_context:
+ * @type: a #GType
+ * @context: a constant string that identifies a translation context or %NULL
+ *
+ * This function attaches a constant string as a translation context
+ * to a #GType. The only purpose of this function is to use it when
+ * registering a #G_TYPE_ENUM with translatable value names.
+ *
+ * Since: 2.8
+ **/
+void
+gimp_type_set_translation_context (GType type,
+ const gchar *context)
+{
+ g_type_set_qdata (type,
+ gimp_translation_context_quark (), (gpointer) context);
+}
+
+/**
+ * gimp_type_get_translation_context:
+ * @type: a #GType
+ *
+ * Retrieves the translation context that has been previously set
+ * using gimp_type_set_translation_context(). You should not need to
+ * use this function directly, use gimp_enum_get_value() or
+ * gimp_enum_value_get_desc() instead.
+ *
+ * Return value: the translation context associated with @type
+ * or %NULL if no context was set
+ *
+ * Since: 2.8
+ **/
+const gchar *
+gimp_type_get_translation_context (GType type)
+{
+ return (const gchar *) g_type_get_qdata (type,
+ gimp_translation_context_quark ());
+}
+
+/**
+ * gimp_enum_set_value_descriptions:
+ * @enum_type: a #GType
+ * @descriptions: a %NULL terminated constant static array of #GimpEnumDesc
+ *
+ * Sets the array of human readable and translatable descriptions
+ * and help texts for enum values.
+ *
+ * Since: 2.2
+ **/
+void
+gimp_enum_set_value_descriptions (GType enum_type,
+ const GimpEnumDesc *descriptions)
+{
+ g_return_if_fail (g_type_is_a (enum_type, G_TYPE_ENUM));
+ g_return_if_fail (descriptions != NULL);
+
+ g_type_set_qdata (enum_type,
+ gimp_value_descriptions_quark (),
+ (gpointer) descriptions);
+}
+
+/**
+ * gimp_enum_get_value_descriptions:
+ * @enum_type: a #GType
+ *
+ * Retreives the array of human readable and translatable descriptions
+ * and help texts for enum values.
+ *
+ * Returns: a %NULL terminated constant array of #GimpEnumDesc
+ *
+ * Since: 2.2
+ **/
+const GimpEnumDesc *
+gimp_enum_get_value_descriptions (GType enum_type)
+{
+ g_return_val_if_fail (g_type_is_a (enum_type, G_TYPE_ENUM), NULL);
+
+ return (const GimpEnumDesc *)
+ g_type_get_qdata (enum_type, gimp_value_descriptions_quark ());
+}
+
+/**
+ * gimp_flags_set_value_descriptions:
+ * @flags_type: a #GType
+ * @descriptions: a %NULL terminated constant static array of #GimpFlagsDesc
+ *
+ * Sets the array of human readable and translatable descriptions
+ * and help texts for flags values.
+ *
+ * Since: 2.2
+ **/
+void
+gimp_flags_set_value_descriptions (GType flags_type,
+ const GimpFlagsDesc *descriptions)
+{
+ g_return_if_fail (g_type_is_a (flags_type, G_TYPE_FLAGS));
+ g_return_if_fail (descriptions != NULL);
+
+ g_type_set_qdata (flags_type,
+ gimp_value_descriptions_quark (),
+ (gpointer) descriptions);
+}
+
+/**
+ * gimp_flags_get_value_descriptions:
+ * @flags_type: a #GType
+ *
+ * Retreives the array of human readable and translatable descriptions
+ * and help texts for flags values.
+ *
+ * Returns: a %NULL terminated constant array of #GimpFlagsDesc
+ *
+ * Since: 2.2
+ **/
+const GimpFlagsDesc *
+gimp_flags_get_value_descriptions (GType flags_type)
+{
+ g_return_val_if_fail (g_type_is_a (flags_type, G_TYPE_FLAGS), NULL);
+
+ return (const GimpFlagsDesc *)
+ g_type_get_qdata (flags_type, gimp_value_descriptions_quark ());
+}
+
+
+/* private functions */
+
+static GQuark
+gimp_translation_domain_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (! quark)
+ quark = g_quark_from_static_string ("gimp-translation-domain-quark");
+
+ return quark;
+}
+
+static GQuark
+gimp_translation_context_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (! quark)
+ quark = g_quark_from_static_string ("gimp-translation-context-quark");
+
+ return quark;
+}
+
+static GQuark
+gimp_value_descriptions_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (! quark)
+ quark = g_quark_from_static_string ("gimp-value-descriptions-quark");
+
+ return quark;
+}
diff --git a/libgimpbase/gimpbasetypes.h b/libgimpbase/gimpbasetypes.h
new file mode 100644
index 0000000..9c3680d
--- /dev/null
+++ b/libgimpbase/gimpbasetypes.h
@@ -0,0 +1,113 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_BASE_TYPES_H__
+#define __GIMP_BASE_TYPES_H__
+
+
+#include <libgimpcolor/gimpcolortypes.h>
+#include <libgimpmath/gimpmathtypes.h>
+
+#include <libgimpbase/gimpbaseenums.h>
+#include <libgimpbase/gimpparam.h>
+
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+/* XXX FIXME move these to a separate file */
+
+#ifdef GIMP_DISABLE_DEPRECATION_WARNINGS
+#define GIMP_DEPRECATED
+#define GIMP_DEPRECATED_FOR(f)
+#define GIMP_UNAVAILABLE(maj,min)
+#else
+#define GIMP_DEPRECATED G_DEPRECATED
+#define GIMP_DEPRECATED_FOR(f) G_DEPRECATED_FOR(f)
+#define GIMP_UNAVAILABLE(maj,min) G_UNAVAILABLE(maj,min)
+#endif
+
+
+typedef struct _GimpParasite GimpParasite;
+typedef struct _GimpDatafileData GimpDatafileData;
+typedef struct _GimpEnumDesc GimpEnumDesc;
+typedef struct _GimpFlagsDesc GimpFlagsDesc;
+typedef struct _GimpValueArray GimpValueArray;
+
+
+typedef void (* GimpDatafileLoaderFunc) (const GimpDatafileData *file_data,
+ gpointer user_data);
+
+typedef struct _GimpMetadata GimpMetadata;
+
+
+/**
+ * GimpEnumDesc:
+ * @value: An enum value.
+ * @value_desc: The value's description.
+ * @value_help: The value's help text.
+ *
+ * This structure is used to register translatable descriptions and
+ * help texts for enum values. See gimp_enum_set_value_descriptions().
+ **/
+struct _GimpEnumDesc
+{
+ gint value;
+ const gchar *value_desc;
+ const gchar *value_help;
+};
+
+/**
+ * GimpFlagsDesc:
+ * @value: A flag value.
+ * @value_desc: The value's description.
+ * @value_help: The value's help text.
+ *
+ * This structure is used to register translatable descriptions and
+ * help texts for flag values. See gimp_flags_set_value_descriptions().
+ **/
+struct _GimpFlagsDesc
+{
+ guint value;
+ const gchar *value_desc;
+ const gchar *value_help;
+};
+
+
+void gimp_type_set_translation_domain (GType type,
+ const gchar *domain);
+const gchar * gimp_type_get_translation_domain (GType type);
+
+void gimp_type_set_translation_context (GType type,
+ const gchar *context);
+const gchar * gimp_type_get_translation_context (GType type);
+
+void gimp_enum_set_value_descriptions (GType enum_type,
+ const GimpEnumDesc *descriptions);
+const GimpEnumDesc * gimp_enum_get_value_descriptions (GType enum_type);
+
+void gimp_flags_set_value_descriptions (GType flags_type,
+ const GimpFlagsDesc *descriptions);
+const GimpFlagsDesc * gimp_flags_get_value_descriptions (GType flags_type);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_BASE_TYPES_H__ */
diff --git a/libgimpbase/gimpchecks.c b/libgimpbase/gimpchecks.c
new file mode 100644
index 0000000..589bf6f
--- /dev/null
+++ b/libgimpbase/gimpchecks.c
@@ -0,0 +1,73 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimpchecks.c
+ * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+
+#include "gimpbasetypes.h"
+
+#include "gimpchecks.h"
+
+
+/**
+ * SECTION: gimpchecks
+ * @title: gimpchecks
+ * @short_description: Constants and functions related to rendering
+ * checkerboards.
+ *
+ * Constants and functions related to rendering checkerboards.
+ **/
+
+
+/**
+ * gimp_checks_get_shades:
+ * @type: the checkerboard type
+ * @light: return location for the light shade
+ * @dark: return location for the dark shade
+ *
+ * Retrieves the actual shades of gray to use when drawing a
+ * checkerboard for a certain #GimpCheckType.
+ *
+ * Since: 2.2
+ **/
+void
+gimp_checks_get_shades (GimpCheckType type,
+ guchar *light,
+ guchar *dark)
+{
+ const guchar shades[6][2] =
+ {
+ { 204, 255 }, /* LIGHT_CHECKS */
+ { 102, 153 }, /* GRAY_CHECKS */
+ { 0, 51 }, /* DARK_CHECKS */
+ { 255, 255 }, /* WHITE_ONLY */
+ { 127, 127 }, /* GRAY_ONLY */
+ { 0, 0 } /* BLACK_ONLY */
+ };
+
+ type = MIN (type, 5);
+
+ if (light)
+ *light = shades[type][1];
+ if (dark)
+ *dark = shades[type][0];
+}
diff --git a/libgimpbase/gimpchecks.h b/libgimpbase/gimpchecks.h
new file mode 100644
index 0000000..c41a4d6
--- /dev/null
+++ b/libgimpbase/gimpchecks.h
@@ -0,0 +1,68 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_BASE_H_INSIDE__) && !defined (GIMP_BASE_COMPILATION)
+#error "Only <libgimpbase/gimpbase.h> can be included directly."
+#endif
+
+#ifndef __GIMP_CHECKS_H__
+#define __GIMP_CHECKS_H__
+
+G_BEGIN_DECLS
+
+
+/**
+ * GIMP_CHECK_SIZE:
+ *
+ * The default checkerboard size in pixels. This is configurable in
+ * the core but GIMP plug-ins can't access the user preference and
+ * should use this constant instead.
+ **/
+#define GIMP_CHECK_SIZE 8
+
+/**
+ * GIMP_CHECK_SIZE_SM:
+ *
+ * The default small checkerboard size in pixels.
+ **/
+#define GIMP_CHECK_SIZE_SM 4
+
+
+/**
+ * GIMP_CHECK_DARK:
+ *
+ * The dark gray value for the default checkerboard pattern.
+ **/
+#define GIMP_CHECK_DARK 0.4
+
+/**
+ * GIMP_CHECK_LIGHT:
+ *
+ * The dark light value for the default checkerboard pattern.
+ **/
+#define GIMP_CHECK_LIGHT 0.6
+
+
+void gimp_checks_get_shades (GimpCheckType type,
+ guchar *light,
+ guchar *dark);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_CHECKS_H__ */
diff --git a/libgimpbase/gimpcompatenums.c b/libgimpbase/gimpcompatenums.c
new file mode 100644
index 0000000..82fdafe
--- /dev/null
+++ b/libgimpbase/gimpcompatenums.c
@@ -0,0 +1,581 @@
+
+/* Generated data (by gimp-mkenums) */
+
+#include "config.h"
+#include <glib-object.h>
+#include "gimpbasetypes.h"
+#include "gimpcompatenums.h"
+#include "libgimp/libgimp-intl.h"
+
+/* enumerations from "gimpcompatenums.h" */
+GType
+gimp_add_mask_type_compat_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_ADD_WHITE_MASK, "GIMP_ADD_WHITE_MASK", "white-mask" },
+ { GIMP_ADD_BLACK_MASK, "GIMP_ADD_BLACK_MASK", "black-mask" },
+ { GIMP_ADD_ALPHA_MASK, "GIMP_ADD_ALPHA_MASK", "alpha-mask" },
+ { GIMP_ADD_ALPHA_TRANSFER_MASK, "GIMP_ADD_ALPHA_TRANSFER_MASK", "alpha-transfer-mask" },
+ { GIMP_ADD_SELECTION_MASK, "GIMP_ADD_SELECTION_MASK", "selection-mask" },
+ { GIMP_ADD_COPY_MASK, "GIMP_ADD_COPY_MASK", "copy-mask" },
+ { GIMP_ADD_CHANNEL_MASK, "GIMP_ADD_CHANNEL_MASK", "channel-mask" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_ADD_WHITE_MASK, "GIMP_ADD_WHITE_MASK", NULL },
+ { GIMP_ADD_BLACK_MASK, "GIMP_ADD_BLACK_MASK", NULL },
+ { GIMP_ADD_ALPHA_MASK, "GIMP_ADD_ALPHA_MASK", NULL },
+ { GIMP_ADD_ALPHA_TRANSFER_MASK, "GIMP_ADD_ALPHA_TRANSFER_MASK", NULL },
+ { GIMP_ADD_SELECTION_MASK, "GIMP_ADD_SELECTION_MASK", NULL },
+ { GIMP_ADD_COPY_MASK, "GIMP_ADD_COPY_MASK", NULL },
+ { GIMP_ADD_CHANNEL_MASK, "GIMP_ADD_CHANNEL_MASK", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpAddMaskTypeCompat", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "add-mask-type-compat");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_blend_mode_compat_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_FG_BG_RGB_MODE, "GIMP_FG_BG_RGB_MODE", "fg-bg-rgb-mode" },
+ { GIMP_FG_BG_HSV_MODE, "GIMP_FG_BG_HSV_MODE", "fg-bg-hsv-mode" },
+ { GIMP_FG_TRANSPARENT_MODE, "GIMP_FG_TRANSPARENT_MODE", "fg-transparent-mode" },
+ { GIMP_CUSTOM_MODE, "GIMP_CUSTOM_MODE", "custom-mode" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_FG_BG_RGB_MODE, "GIMP_FG_BG_RGB_MODE", NULL },
+ { GIMP_FG_BG_HSV_MODE, "GIMP_FG_BG_HSV_MODE", NULL },
+ { GIMP_FG_TRANSPARENT_MODE, "GIMP_FG_TRANSPARENT_MODE", NULL },
+ { GIMP_CUSTOM_MODE, "GIMP_CUSTOM_MODE", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpBlendModeCompat", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "blend-mode-compat");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_bucket_fill_mode_compat_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_FG_BUCKET_FILL, "GIMP_FG_BUCKET_FILL", "fg-bucket-fill" },
+ { GIMP_BG_BUCKET_FILL, "GIMP_BG_BUCKET_FILL", "bg-bucket-fill" },
+ { GIMP_PATTERN_BUCKET_FILL, "GIMP_PATTERN_BUCKET_FILL", "pattern-bucket-fill" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_FG_BUCKET_FILL, "GIMP_FG_BUCKET_FILL", NULL },
+ { GIMP_BG_BUCKET_FILL, "GIMP_BG_BUCKET_FILL", NULL },
+ { GIMP_PATTERN_BUCKET_FILL, "GIMP_PATTERN_BUCKET_FILL", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpBucketFillModeCompat", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "bucket-fill-mode-compat");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_channel_type_compat_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_RED_CHANNEL, "GIMP_RED_CHANNEL", "red-channel" },
+ { GIMP_GREEN_CHANNEL, "GIMP_GREEN_CHANNEL", "green-channel" },
+ { GIMP_BLUE_CHANNEL, "GIMP_BLUE_CHANNEL", "blue-channel" },
+ { GIMP_GRAY_CHANNEL, "GIMP_GRAY_CHANNEL", "gray-channel" },
+ { GIMP_INDEXED_CHANNEL, "GIMP_INDEXED_CHANNEL", "indexed-channel" },
+ { GIMP_ALPHA_CHANNEL, "GIMP_ALPHA_CHANNEL", "alpha-channel" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_RED_CHANNEL, "GIMP_RED_CHANNEL", NULL },
+ { GIMP_GREEN_CHANNEL, "GIMP_GREEN_CHANNEL", NULL },
+ { GIMP_BLUE_CHANNEL, "GIMP_BLUE_CHANNEL", NULL },
+ { GIMP_GRAY_CHANNEL, "GIMP_GRAY_CHANNEL", NULL },
+ { GIMP_INDEXED_CHANNEL, "GIMP_INDEXED_CHANNEL", NULL },
+ { GIMP_ALPHA_CHANNEL, "GIMP_ALPHA_CHANNEL", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpChannelTypeCompat", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "channel-type-compat");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_clone_type_compat_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_IMAGE_CLONE, "GIMP_IMAGE_CLONE", "image-clone" },
+ { GIMP_PATTERN_CLONE, "GIMP_PATTERN_CLONE", "pattern-clone" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_IMAGE_CLONE, "GIMP_IMAGE_CLONE", NULL },
+ { GIMP_PATTERN_CLONE, "GIMP_PATTERN_CLONE", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpCloneTypeCompat", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "clone-type-compat");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_convert_dither_type_compat_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_NO_DITHER, "GIMP_NO_DITHER", "no-dither" },
+ { GIMP_FS_DITHER, "GIMP_FS_DITHER", "fs-dither" },
+ { GIMP_FSLOWBLEED_DITHER, "GIMP_FSLOWBLEED_DITHER", "fslowbleed-dither" },
+ { GIMP_FIXED_DITHER, "GIMP_FIXED_DITHER", "fixed-dither" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_NO_DITHER, "GIMP_NO_DITHER", NULL },
+ { GIMP_FS_DITHER, "GIMP_FS_DITHER", NULL },
+ { GIMP_FSLOWBLEED_DITHER, "GIMP_FSLOWBLEED_DITHER", NULL },
+ { GIMP_FIXED_DITHER, "GIMP_FIXED_DITHER", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpConvertDitherTypeCompat", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "convert-dither-type-compat");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_convert_palette_type_compat_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_MAKE_PALETTE, "GIMP_MAKE_PALETTE", "make-palette" },
+ { GIMP_REUSE_PALETTE, "GIMP_REUSE_PALETTE", "reuse-palette" },
+ { GIMP_WEB_PALETTE, "GIMP_WEB_PALETTE", "web-palette" },
+ { GIMP_MONO_PALETTE, "GIMP_MONO_PALETTE", "mono-palette" },
+ { GIMP_CUSTOM_PALETTE, "GIMP_CUSTOM_PALETTE", "custom-palette" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_MAKE_PALETTE, "GIMP_MAKE_PALETTE", NULL },
+ { GIMP_REUSE_PALETTE, "GIMP_REUSE_PALETTE", NULL },
+ { GIMP_WEB_PALETTE, "GIMP_WEB_PALETTE", NULL },
+ { GIMP_MONO_PALETTE, "GIMP_MONO_PALETTE", NULL },
+ { GIMP_CUSTOM_PALETTE, "GIMP_CUSTOM_PALETTE", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpConvertPaletteTypeCompat", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "convert-palette-type-compat");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_convolve_type_compat_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_BLUR_CONVOLVE, "GIMP_BLUR_CONVOLVE", "blur-convolve" },
+ { GIMP_SHARPEN_CONVOLVE, "GIMP_SHARPEN_CONVOLVE", "sharpen-convolve" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_BLUR_CONVOLVE, "GIMP_BLUR_CONVOLVE", NULL },
+ { GIMP_SHARPEN_CONVOLVE, "GIMP_SHARPEN_CONVOLVE", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpConvolveTypeCompat", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "convolve-type-compat");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_desaturate_mode_compat_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_DESATURATE_LUMINOSITY, "GIMP_DESATURATE_LUMINOSITY", "luminosity" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_DESATURATE_LUMINOSITY, "GIMP_DESATURATE_LUMINOSITY", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpDesaturateModeCompat", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "desaturate-mode-compat");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_dodge_burn_type_compat_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_DODGE, "GIMP_DODGE", "dodge" },
+ { GIMP_BURN, "GIMP_BURN", "burn" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_DODGE, "GIMP_DODGE", NULL },
+ { GIMP_BURN, "GIMP_BURN", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpDodgeBurnTypeCompat", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "dodge-burn-type-compat");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_fill_type_compat_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_FOREGROUND_FILL, "GIMP_FOREGROUND_FILL", "foreground-fill" },
+ { GIMP_BACKGROUND_FILL, "GIMP_BACKGROUND_FILL", "background-fill" },
+ { GIMP_WHITE_FILL, "GIMP_WHITE_FILL", "white-fill" },
+ { GIMP_TRANSPARENT_FILL, "GIMP_TRANSPARENT_FILL", "transparent-fill" },
+ { GIMP_PATTERN_FILL, "GIMP_PATTERN_FILL", "pattern-fill" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_FOREGROUND_FILL, "GIMP_FOREGROUND_FILL", NULL },
+ { GIMP_BACKGROUND_FILL, "GIMP_BACKGROUND_FILL", NULL },
+ { GIMP_WHITE_FILL, "GIMP_WHITE_FILL", NULL },
+ { GIMP_TRANSPARENT_FILL, "GIMP_TRANSPARENT_FILL", NULL },
+ { GIMP_PATTERN_FILL, "GIMP_PATTERN_FILL", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpFillTypeCompat", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "fill-type-compat");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_hue_range_compat_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_ALL_HUES, "GIMP_ALL_HUES", "all-hues" },
+ { GIMP_RED_HUES, "GIMP_RED_HUES", "red-hues" },
+ { GIMP_YELLOW_HUES, "GIMP_YELLOW_HUES", "yellow-hues" },
+ { GIMP_GREEN_HUES, "GIMP_GREEN_HUES", "green-hues" },
+ { GIMP_CYAN_HUES, "GIMP_CYAN_HUES", "cyan-hues" },
+ { GIMP_BLUE_HUES, "GIMP_BLUE_HUES", "blue-hues" },
+ { GIMP_MAGENTA_HUES, "GIMP_MAGENTA_HUES", "magenta-hues" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_ALL_HUES, "GIMP_ALL_HUES", NULL },
+ { GIMP_RED_HUES, "GIMP_RED_HUES", NULL },
+ { GIMP_YELLOW_HUES, "GIMP_YELLOW_HUES", NULL },
+ { GIMP_GREEN_HUES, "GIMP_GREEN_HUES", NULL },
+ { GIMP_CYAN_HUES, "GIMP_CYAN_HUES", NULL },
+ { GIMP_BLUE_HUES, "GIMP_BLUE_HUES", NULL },
+ { GIMP_MAGENTA_HUES, "GIMP_MAGENTA_HUES", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpHueRangeCompat", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "hue-range-compat");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_icon_type_compat_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_ICON_TYPE_STOCK_ID, "GIMP_ICON_TYPE_STOCK_ID", "id" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_ICON_TYPE_STOCK_ID, "GIMP_ICON_TYPE_STOCK_ID", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpIconTypeCompat", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "icon-type-compat");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_interpolation_type_compat_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_INTERPOLATION_LANCZOS, "GIMP_INTERPOLATION_LANCZOS", "lanczos" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_INTERPOLATION_LANCZOS, "GIMP_INTERPOLATION_LANCZOS", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpInterpolationTypeCompat", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "interpolation-type-compat");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_layer_mode_effects_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_NORMAL_MODE, "GIMP_NORMAL_MODE", "normal-mode" },
+ { GIMP_DISSOLVE_MODE, "GIMP_DISSOLVE_MODE", "dissolve-mode" },
+ { GIMP_BEHIND_MODE, "GIMP_BEHIND_MODE", "behind-mode" },
+ { GIMP_MULTIPLY_MODE, "GIMP_MULTIPLY_MODE", "multiply-mode" },
+ { GIMP_SCREEN_MODE, "GIMP_SCREEN_MODE", "screen-mode" },
+ { GIMP_OVERLAY_MODE, "GIMP_OVERLAY_MODE", "overlay-mode" },
+ { GIMP_DIFFERENCE_MODE, "GIMP_DIFFERENCE_MODE", "difference-mode" },
+ { GIMP_ADDITION_MODE, "GIMP_ADDITION_MODE", "addition-mode" },
+ { GIMP_SUBTRACT_MODE, "GIMP_SUBTRACT_MODE", "subtract-mode" },
+ { GIMP_DARKEN_ONLY_MODE, "GIMP_DARKEN_ONLY_MODE", "darken-only-mode" },
+ { GIMP_LIGHTEN_ONLY_MODE, "GIMP_LIGHTEN_ONLY_MODE", "lighten-only-mode" },
+ { GIMP_HUE_MODE, "GIMP_HUE_MODE", "hue-mode" },
+ { GIMP_SATURATION_MODE, "GIMP_SATURATION_MODE", "saturation-mode" },
+ { GIMP_COLOR_MODE, "GIMP_COLOR_MODE", "color-mode" },
+ { GIMP_VALUE_MODE, "GIMP_VALUE_MODE", "value-mode" },
+ { GIMP_DIVIDE_MODE, "GIMP_DIVIDE_MODE", "divide-mode" },
+ { GIMP_DODGE_MODE, "GIMP_DODGE_MODE", "dodge-mode" },
+ { GIMP_BURN_MODE, "GIMP_BURN_MODE", "burn-mode" },
+ { GIMP_HARDLIGHT_MODE, "GIMP_HARDLIGHT_MODE", "hardlight-mode" },
+ { GIMP_SOFTLIGHT_MODE, "GIMP_SOFTLIGHT_MODE", "softlight-mode" },
+ { GIMP_GRAIN_EXTRACT_MODE, "GIMP_GRAIN_EXTRACT_MODE", "grain-extract-mode" },
+ { GIMP_GRAIN_MERGE_MODE, "GIMP_GRAIN_MERGE_MODE", "grain-merge-mode" },
+ { GIMP_COLOR_ERASE_MODE, "GIMP_COLOR_ERASE_MODE", "color-erase-mode" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_NORMAL_MODE, "GIMP_NORMAL_MODE", NULL },
+ { GIMP_DISSOLVE_MODE, "GIMP_DISSOLVE_MODE", NULL },
+ { GIMP_BEHIND_MODE, "GIMP_BEHIND_MODE", NULL },
+ { GIMP_MULTIPLY_MODE, "GIMP_MULTIPLY_MODE", NULL },
+ { GIMP_SCREEN_MODE, "GIMP_SCREEN_MODE", NULL },
+ { GIMP_OVERLAY_MODE, "GIMP_OVERLAY_MODE", NULL },
+ { GIMP_DIFFERENCE_MODE, "GIMP_DIFFERENCE_MODE", NULL },
+ { GIMP_ADDITION_MODE, "GIMP_ADDITION_MODE", NULL },
+ { GIMP_SUBTRACT_MODE, "GIMP_SUBTRACT_MODE", NULL },
+ { GIMP_DARKEN_ONLY_MODE, "GIMP_DARKEN_ONLY_MODE", NULL },
+ { GIMP_LIGHTEN_ONLY_MODE, "GIMP_LIGHTEN_ONLY_MODE", NULL },
+ { GIMP_HUE_MODE, "GIMP_HUE_MODE", NULL },
+ { GIMP_SATURATION_MODE, "GIMP_SATURATION_MODE", NULL },
+ { GIMP_COLOR_MODE, "GIMP_COLOR_MODE", NULL },
+ { GIMP_VALUE_MODE, "GIMP_VALUE_MODE", NULL },
+ { GIMP_DIVIDE_MODE, "GIMP_DIVIDE_MODE", NULL },
+ { GIMP_DODGE_MODE, "GIMP_DODGE_MODE", NULL },
+ { GIMP_BURN_MODE, "GIMP_BURN_MODE", NULL },
+ { GIMP_HARDLIGHT_MODE, "GIMP_HARDLIGHT_MODE", NULL },
+ { GIMP_SOFTLIGHT_MODE, "GIMP_SOFTLIGHT_MODE", NULL },
+ { GIMP_GRAIN_EXTRACT_MODE, "GIMP_GRAIN_EXTRACT_MODE", NULL },
+ { GIMP_GRAIN_MERGE_MODE, "GIMP_GRAIN_MERGE_MODE", NULL },
+ { GIMP_COLOR_ERASE_MODE, "GIMP_COLOR_ERASE_MODE", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpLayerModeEffects", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "layer-mode-effects");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_transfer_mode_compat_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_SHADOWS, "GIMP_SHADOWS", "shadows" },
+ { GIMP_MIDTONES, "GIMP_MIDTONES", "midtones" },
+ { GIMP_HIGHLIGHTS, "GIMP_HIGHLIGHTS", "highlights" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_SHADOWS, "GIMP_SHADOWS", NULL },
+ { GIMP_MIDTONES, "GIMP_MIDTONES", NULL },
+ { GIMP_HIGHLIGHTS, "GIMP_HIGHLIGHTS", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpTransferModeCompat", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "transfer-mode-compat");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+
+/* Generated data ends here */
+
diff --git a/libgimpbase/gimpcompatenums.h b/libgimpbase/gimpcompatenums.h
new file mode 100644
index 0000000..173dee9
--- /dev/null
+++ b/libgimpbase/gimpcompatenums.h
@@ -0,0 +1,252 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_COMPAT_ENUMS_H__
+#define __GIMP_COMPAT_ENUMS_H__
+
+
+G_BEGIN_DECLS
+
+/* These enums exist only for compatibility, their nicks are needed
+ * for config file parsing; they are registered in gimp_base_init().
+ */
+
+
+#define GIMP_TYPE_ADD_MASK_TYPE_COMPAT (gimp_add_mask_type_compat_get_type ())
+
+GType gimp_add_mask_type_compat_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_ADD_WHITE_MASK = GIMP_ADD_MASK_WHITE,
+ GIMP_ADD_BLACK_MASK = GIMP_ADD_MASK_BLACK,
+ GIMP_ADD_ALPHA_MASK = GIMP_ADD_MASK_ALPHA,
+ GIMP_ADD_ALPHA_TRANSFER_MASK = GIMP_ADD_MASK_ALPHA_TRANSFER,
+ GIMP_ADD_SELECTION_MASK = GIMP_ADD_MASK_SELECTION,
+ GIMP_ADD_COPY_MASK = GIMP_ADD_MASK_COPY,
+ GIMP_ADD_CHANNEL_MASK = GIMP_ADD_MASK_CHANNEL
+} GimpAddMaskTypeCompat;
+
+
+#define GIMP_TYPE_BLEND_MODE_COMPAT (gimp_blend_mode_compat_get_type ())
+
+GType gimp_blend_mode_compat_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_FG_BG_RGB_MODE = GIMP_BLEND_FG_BG_RGB,
+ GIMP_FG_BG_HSV_MODE = GIMP_BLEND_FG_BG_HSV,
+ GIMP_FG_TRANSPARENT_MODE = GIMP_BLEND_FG_TRANSPARENT,
+ GIMP_CUSTOM_MODE = GIMP_BLEND_CUSTOM
+} GimpBlendModeCompat;
+
+
+#define GIMP_TYPE_BUCKET_FILL_MODE_COMPAT (gimp_bucket_fill_mode_compat_get_type ())
+
+GType gimp_bucket_fill_mode_compat_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_FG_BUCKET_FILL = GIMP_BUCKET_FILL_FG,
+ GIMP_BG_BUCKET_FILL = GIMP_BUCKET_FILL_BG,
+ GIMP_PATTERN_BUCKET_FILL = GIMP_BUCKET_FILL_PATTERN
+} GimpBucketFillModeCompat;
+
+
+#define GIMP_TYPE_CHANNEL_TYPE_COMPAT (gimp_channel_type_compat_get_type ())
+
+GType gimp_channel_type_compat_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_RED_CHANNEL = GIMP_CHANNEL_RED,
+ GIMP_GREEN_CHANNEL = GIMP_CHANNEL_GREEN,
+ GIMP_BLUE_CHANNEL = GIMP_CHANNEL_BLUE,
+ GIMP_GRAY_CHANNEL = GIMP_CHANNEL_GRAY,
+ GIMP_INDEXED_CHANNEL = GIMP_CHANNEL_INDEXED,
+ GIMP_ALPHA_CHANNEL = GIMP_CHANNEL_ALPHA
+} GimpChannelTypeCompat;
+
+
+#define GIMP_TYPE_CLONE_TYPE_COMPAT (gimp_clone_type_compat_get_type ())
+
+GType gimp_clone_type_compat_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_IMAGE_CLONE = GIMP_CLONE_IMAGE,
+ GIMP_PATTERN_CLONE = GIMP_CLONE_PATTERN
+} GimpCloneTypeCompat;
+
+
+#define GIMP_TYPE_CONVERT_DITHER_TYPE_COMPAT (gimp_convert_dither_type_compat_get_type ())
+
+GType gimp_convert_dither_type_compat_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_NO_DITHER,
+ GIMP_FS_DITHER,
+ GIMP_FSLOWBLEED_DITHER,
+ GIMP_FIXED_DITHER
+} GimpConvertDitherTypeCompat;
+
+
+#define GIMP_TYPE_CONVERT_PALETTE_TYPE_COMPAT (gimp_convert_palette_type_compat_get_type ())
+
+GType gimp_convert_palette_type_compat_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_MAKE_PALETTE,
+ GIMP_REUSE_PALETTE,
+ GIMP_WEB_PALETTE,
+ GIMP_MONO_PALETTE,
+ GIMP_CUSTOM_PALETTE
+} GimpConvertPaletteTypeCompat;
+
+
+#define GIMP_TYPE_CONVOLVE_TYPE_COMPAT (gimp_convolve_type_compat_get_type ())
+
+GType gimp_convolve_type_compat_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_BLUR_CONVOLVE = GIMP_CONVOLVE_BLUR,
+ GIMP_SHARPEN_CONVOLVE = GIMP_CONVOLVE_SHARPEN
+} GimpConvolveTypeCompat;
+
+
+#define GIMP_TYPE_DESATURATE_MODE_COMPAT (gimp_desaturate_mode_compat_get_type ())
+
+GType gimp_desaturate_mode_compat_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_DESATURATE_LUMINOSITY = GIMP_DESATURATE_LUMA
+} GimpDesaturateModeCompat;
+
+
+#define GIMP_TYPE_DODGE_BURN_TYPE_COMPAT (gimp_dodge_burn_type_compat_get_type ())
+
+GType gimp_dodge_burn_type_compat_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_DODGE = GIMP_DODGE_BURN_TYPE_DODGE,
+ GIMP_BURN = GIMP_DODGE_BURN_TYPE_BURN
+} GimpDodgeBurnTypeCompat;
+
+
+#define GIMP_TYPE_FILL_TYPE_COMPAT (gimp_fill_type_compat_get_type ())
+
+GType gimp_fill_type_compat_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_FOREGROUND_FILL = GIMP_FILL_FOREGROUND,
+ GIMP_BACKGROUND_FILL = GIMP_FILL_BACKGROUND,
+ GIMP_WHITE_FILL = GIMP_FILL_WHITE,
+ GIMP_TRANSPARENT_FILL = GIMP_FILL_TRANSPARENT,
+ GIMP_PATTERN_FILL = GIMP_FILL_PATTERN
+} GimpFillTypeCompat;
+
+
+#define GIMP_TYPE_HUE_RANGE_COMPAT (gimp_hue_range_compat_get_type ())
+
+GType gimp_hue_range_compat_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_ALL_HUES = GIMP_HUE_RANGE_ALL,
+ GIMP_RED_HUES = GIMP_HUE_RANGE_RED,
+ GIMP_YELLOW_HUES = GIMP_HUE_RANGE_YELLOW,
+ GIMP_GREEN_HUES = GIMP_HUE_RANGE_GREEN,
+ GIMP_CYAN_HUES = GIMP_HUE_RANGE_CYAN,
+ GIMP_BLUE_HUES = GIMP_HUE_RANGE_BLUE,
+ GIMP_MAGENTA_HUES = GIMP_HUE_RANGE_MAGENTA
+} GimpHueRangeCompat;
+
+
+#define GIMP_TYPE_ICON_TYPE_COMPAT (gimp_icon_type_compat_get_type ())
+
+GType gimp_icon_type_compat_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_ICON_TYPE_STOCK_ID = GIMP_ICON_TYPE_ICON_NAME
+} GimpIconTypeCompat;
+
+
+#define GIMP_TYPE_INTERPOLATION_TYPE_COMPAT (gimp_interpolation_type_compat_get_type ())
+
+GType gimp_interpolation_type_compat_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_INTERPOLATION_LANCZOS = GIMP_INTERPOLATION_NOHALO
+} GimpInterpolationTypeCompat;
+
+
+#define GIMP_TYPE_LAYER_MODE_EFFECTS (gimp_layer_mode_effects_get_type ())
+
+GType gimp_layer_mode_effects_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_NORMAL_MODE,
+ GIMP_DISSOLVE_MODE,
+ GIMP_BEHIND_MODE,
+ GIMP_MULTIPLY_MODE,
+ GIMP_SCREEN_MODE,
+ GIMP_OVERLAY_MODE,
+ GIMP_DIFFERENCE_MODE,
+ GIMP_ADDITION_MODE,
+ GIMP_SUBTRACT_MODE,
+ GIMP_DARKEN_ONLY_MODE,
+ GIMP_LIGHTEN_ONLY_MODE,
+ GIMP_HUE_MODE,
+ GIMP_SATURATION_MODE,
+ GIMP_COLOR_MODE,
+ GIMP_VALUE_MODE,
+ GIMP_DIVIDE_MODE,
+ GIMP_DODGE_MODE,
+ GIMP_BURN_MODE,
+ GIMP_HARDLIGHT_MODE,
+ GIMP_SOFTLIGHT_MODE,
+ GIMP_GRAIN_EXTRACT_MODE,
+ GIMP_GRAIN_MERGE_MODE,
+ GIMP_COLOR_ERASE_MODE
+} GimpLayerModeEffects;
+
+
+#define GIMP_TYPE_TRANSFER_MODE_COMPAT (gimp_transfer_mode_compat_get_type ())
+
+GType gimp_transfer_mode_compat_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_SHADOWS = GIMP_TRANSFER_SHADOWS,
+ GIMP_MIDTONES = GIMP_TRANSFER_MIDTONES,
+ GIMP_HIGHLIGHTS = GIMP_TRANSFER_HIGHLIGHTS
+} GimpTransferModeCompat;
+
+
+G_END_DECLS
+
+#endif /* __GIMP_COMPAT_ENUMS_H__ */
diff --git a/libgimpbase/gimpcpuaccel.c b/libgimpbase/gimpcpuaccel.c
new file mode 100644
index 0000000..86a2d49
--- /dev/null
+++ b/libgimpbase/gimpcpuaccel.c
@@ -0,0 +1,531 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * x86 bits Copyright (C) Manish Singh <yosh@gimp.org>
+ */
+
+/*
+ * PPC CPU acceleration detection was taken from DirectFB but seems to be
+ * originating from mpeg2dec with the following copyright:
+ *
+ * Copyright (C) 1999-2001 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <signal.h>
+#include <setjmp.h>
+
+#include <glib.h>
+
+#include "gimpcpuaccel.h"
+
+
+/**
+ * SECTION: gimpcpuaccel
+ * @title: gimpcpuaccel
+ * @short_description: Functions to query and configure CPU acceleration.
+ *
+ * Functions to query and configure CPU acceleration.
+ **/
+
+
+static GimpCpuAccelFlags cpu_accel (void) G_GNUC_CONST;
+
+
+static gboolean use_cpu_accel = TRUE;
+
+
+/**
+ * gimp_cpu_accel_get_support:
+ *
+ * Query for CPU acceleration support.
+ *
+ * Return value: #GimpCpuAccelFlags as supported by the CPU.
+ *
+ * Since: 2.4
+ */
+GimpCpuAccelFlags
+gimp_cpu_accel_get_support (void)
+{
+ return use_cpu_accel ? cpu_accel () : GIMP_CPU_ACCEL_NONE;
+}
+
+/**
+ * gimp_cpu_accel_set_use:
+ * @use: whether to use CPU acceleration features or not
+ *
+ * This function is for internal use only.
+ *
+ * Since: 2.4
+ */
+void
+gimp_cpu_accel_set_use (gboolean use)
+{
+ use_cpu_accel = use ? TRUE : FALSE;
+}
+
+
+#if defined(ARCH_X86) && defined(USE_MMX) && defined(__GNUC__)
+
+#define HAVE_ACCEL 1
+
+
+typedef enum
+{
+ ARCH_X86_VENDOR_NONE,
+ ARCH_X86_VENDOR_INTEL,
+ ARCH_X86_VENDOR_AMD,
+ ARCH_X86_VENDOR_CENTAUR,
+ ARCH_X86_VENDOR_CYRIX,
+ ARCH_X86_VENDOR_NSC,
+ ARCH_X86_VENDOR_TRANSMETA,
+ ARCH_X86_VENDOR_NEXGEN,
+ ARCH_X86_VENDOR_RISE,
+ ARCH_X86_VENDOR_UMC,
+ ARCH_X86_VENDOR_SIS,
+ ARCH_X86_VENDOR_HYGON,
+ ARCH_X86_VENDOR_UNKNOWN = 0xff
+} X86Vendor;
+
+enum
+{
+ ARCH_X86_INTEL_FEATURE_MMX = 1 << 23,
+ ARCH_X86_INTEL_FEATURE_XMM = 1 << 25,
+ ARCH_X86_INTEL_FEATURE_XMM2 = 1 << 26,
+
+ ARCH_X86_AMD_FEATURE_MMXEXT = 1 << 22,
+ ARCH_X86_AMD_FEATURE_3DNOW = 1 << 31,
+
+ ARCH_X86_CENTAUR_FEATURE_MMX = 1 << 23,
+ ARCH_X86_CENTAUR_FEATURE_MMXEXT = 1 << 24,
+ ARCH_X86_CENTAUR_FEATURE_3DNOW = 1 << 31,
+
+ ARCH_X86_CYRIX_FEATURE_MMX = 1 << 23,
+ ARCH_X86_CYRIX_FEATURE_MMXEXT = 1 << 24
+};
+
+enum
+{
+ ARCH_X86_INTEL_FEATURE_PNI = 1 << 0,
+ ARCH_X86_INTEL_FEATURE_SSSE3 = 1 << 9,
+ ARCH_X86_INTEL_FEATURE_SSE4_1 = 1 << 19,
+ ARCH_X86_INTEL_FEATURE_SSE4_2 = 1 << 20,
+ ARCH_X86_INTEL_FEATURE_AVX = 1 << 28
+};
+
+#if !defined(ARCH_X86_64) && (defined(PIC) || defined(__PIC__))
+#define cpuid(op,eax,ebx,ecx,edx) \
+ __asm__ ("movl %%ebx, %%esi\n\t" \
+ "cpuid\n\t" \
+ "xchgl %%ebx,%%esi" \
+ : "=a" (eax), \
+ "=S" (ebx), \
+ "=c" (ecx), \
+ "=d" (edx) \
+ : "0" (op))
+#else
+#define cpuid(op,eax,ebx,ecx,edx) \
+ __asm__ ("cpuid" \
+ : "=a" (eax), \
+ "=b" (ebx), \
+ "=c" (ecx), \
+ "=d" (edx) \
+ : "0" (op))
+#endif
+
+
+static X86Vendor
+arch_get_vendor (void)
+{
+ guint32 eax, ebx, ecx, edx;
+ union{
+ gchar idaschar[16];
+ int idasint[4];
+ }id;
+
+#ifndef ARCH_X86_64
+ /* Only need to check this on ia32 */
+ __asm__ ("pushfl\n\t"
+ "pushfl\n\t"
+ "popl %0\n\t"
+ "movl %0,%1\n\t"
+ "xorl $0x200000,%0\n\t"
+ "pushl %0\n\t"
+ "popfl\n\t"
+ "pushfl\n\t"
+ "popl %0\n\t"
+ "popfl"
+ : "=a" (eax),
+ "=c" (ecx)
+ :
+ : "cc");
+
+ if (eax == ecx)
+ return ARCH_X86_VENDOR_NONE;
+#endif
+
+ cpuid (0, eax, ebx, ecx, edx);
+
+ if (eax == 0)
+ return ARCH_X86_VENDOR_NONE;
+
+ id.idasint[0] = ebx;
+ id.idasint[1] = edx;
+ id.idasint[2] = ecx;
+
+ id.idaschar[12] = '\0';
+
+#ifdef ARCH_X86_64
+ if (strcmp (id.idaschar, "AuthenticAMD") == 0)
+ return ARCH_X86_VENDOR_AMD;
+ else if (strcmp (id.idaschar, "HygonGenuine") == 0)
+ return ARCH_X86_VENDOR_HYGON;
+ else if (strcmp (id.idaschar, "GenuineIntel") == 0)
+ return ARCH_X86_VENDOR_INTEL;
+#else
+ if (strcmp (id.idaschar, "GenuineIntel") == 0)
+ return ARCH_X86_VENDOR_INTEL;
+ else if (strcmp (id.idaschar, "AuthenticAMD") == 0)
+ return ARCH_X86_VENDOR_AMD;
+ else if (strcmp (id.idaschar, "HygonGenuine") == 0)
+ return ARCH_X86_VENDOR_HYGON;
+ else if (strcmp (id.idaschar, "CentaurHauls") == 0)
+ return ARCH_X86_VENDOR_CENTAUR;
+ else if (strcmp (id.idaschar, "CyrixInstead") == 0)
+ return ARCH_X86_VENDOR_CYRIX;
+ else if (strcmp (id.idaschar, "Geode by NSC") == 0)
+ return ARCH_X86_VENDOR_NSC;
+ else if (strcmp (id.idaschar, "GenuineTMx86") == 0 ||
+ strcmp (id.idaschar, "TransmetaCPU") == 0)
+ return ARCH_X86_VENDOR_TRANSMETA;
+ else if (strcmp (id.idaschar, "NexGenDriven") == 0)
+ return ARCH_X86_VENDOR_NEXGEN;
+ else if (strcmp (id.idaschar, "RiseRiseRise") == 0)
+ return ARCH_X86_VENDOR_RISE;
+ else if (strcmp (id.idaschar, "UMC UMC UMC ") == 0)
+ return ARCH_X86_VENDOR_UMC;
+ else if (strcmp (id.idaschar, "SiS SiS SiS ") == 0)
+ return ARCH_X86_VENDOR_SIS;
+#endif
+
+ return ARCH_X86_VENDOR_UNKNOWN;
+}
+
+static guint32
+arch_accel_intel (void)
+{
+ guint32 caps = 0;
+
+#ifdef USE_MMX
+ {
+ guint32 eax, ebx, ecx, edx;
+
+ cpuid (1, eax, ebx, ecx, edx);
+
+ if ((edx & ARCH_X86_INTEL_FEATURE_MMX) == 0)
+ return 0;
+
+ caps = GIMP_CPU_ACCEL_X86_MMX;
+
+#ifdef USE_SSE
+ if (edx & ARCH_X86_INTEL_FEATURE_XMM)
+ caps |= GIMP_CPU_ACCEL_X86_SSE | GIMP_CPU_ACCEL_X86_MMXEXT;
+
+ if (edx & ARCH_X86_INTEL_FEATURE_XMM2)
+ caps |= GIMP_CPU_ACCEL_X86_SSE2;
+
+ if (ecx & ARCH_X86_INTEL_FEATURE_PNI)
+ caps |= GIMP_CPU_ACCEL_X86_SSE3;
+
+ if (ecx & ARCH_X86_INTEL_FEATURE_SSSE3)
+ caps |= GIMP_CPU_ACCEL_X86_SSSE3;
+
+ if (ecx & ARCH_X86_INTEL_FEATURE_SSE4_1)
+ caps |= GIMP_CPU_ACCEL_X86_SSE4_1;
+
+ if (ecx & ARCH_X86_INTEL_FEATURE_SSE4_2)
+ caps |= GIMP_CPU_ACCEL_X86_SSE4_2;
+
+ if (ecx & ARCH_X86_INTEL_FEATURE_AVX)
+ caps |= GIMP_CPU_ACCEL_X86_AVX;
+#endif /* USE_SSE */
+ }
+#endif /* USE_MMX */
+
+ return caps;
+}
+
+static guint32
+arch_accel_amd (void)
+{
+ guint32 caps;
+
+ caps = arch_accel_intel ();
+
+#ifdef USE_MMX
+ {
+ guint32 eax, ebx, ecx, edx;
+
+ cpuid (0x80000000, eax, ebx, ecx, edx);
+
+ if (eax < 0x80000001)
+ return caps;
+
+#ifdef USE_SSE
+ cpuid (0x80000001, eax, ebx, ecx, edx);
+
+ if (edx & ARCH_X86_AMD_FEATURE_3DNOW)
+ caps |= GIMP_CPU_ACCEL_X86_3DNOW;
+
+ if (edx & ARCH_X86_AMD_FEATURE_MMXEXT)
+ caps |= GIMP_CPU_ACCEL_X86_MMXEXT;
+#endif /* USE_SSE */
+ }
+#endif /* USE_MMX */
+
+ return caps;
+}
+
+static guint32
+arch_accel_centaur (void)
+{
+ guint32 caps;
+
+ caps = arch_accel_intel ();
+
+#ifdef USE_MMX
+ {
+ guint32 eax, ebx, ecx, edx;
+
+ cpuid (0x80000000, eax, ebx, ecx, edx);
+
+ if (eax < 0x80000001)
+ return caps;
+
+ cpuid (0x80000001, eax, ebx, ecx, edx);
+
+ if (edx & ARCH_X86_CENTAUR_FEATURE_MMX)
+ caps |= GIMP_CPU_ACCEL_X86_MMX;
+
+#ifdef USE_SSE
+ if (edx & ARCH_X86_CENTAUR_FEATURE_3DNOW)
+ caps |= GIMP_CPU_ACCEL_X86_3DNOW;
+
+ if (edx & ARCH_X86_CENTAUR_FEATURE_MMXEXT)
+ caps |= GIMP_CPU_ACCEL_X86_MMXEXT;
+#endif /* USE_SSE */
+ }
+#endif /* USE_MMX */
+
+ return caps;
+}
+
+static guint32
+arch_accel_cyrix (void)
+{
+ guint32 caps;
+
+ caps = arch_accel_intel ();
+
+#ifdef USE_MMX
+ {
+ guint32 eax, ebx, ecx, edx;
+
+ cpuid (0, eax, ebx, ecx, edx);
+
+ if (eax != 2)
+ return caps;
+
+ cpuid (0x80000001, eax, ebx, ecx, edx);
+
+ if (edx & ARCH_X86_CYRIX_FEATURE_MMX)
+ caps |= GIMP_CPU_ACCEL_X86_MMX;
+
+#ifdef USE_SSE
+ if (edx & ARCH_X86_CYRIX_FEATURE_MMXEXT)
+ caps |= GIMP_CPU_ACCEL_X86_MMXEXT;
+#endif /* USE_SSE */
+ }
+#endif /* USE_MMX */
+
+ return caps;
+}
+
+#ifdef USE_SSE
+static jmp_buf sigill_return;
+
+static void
+sigill_handler (gint n)
+{
+ longjmp (sigill_return, 1);
+}
+
+static gboolean
+arch_accel_sse_os_support (void)
+{
+ if (setjmp (sigill_return))
+ {
+ return FALSE;
+ }
+ else
+ {
+ signal (SIGILL, sigill_handler);
+ __asm__ __volatile__ ("xorps %xmm0, %xmm0");
+ signal (SIGILL, SIG_DFL);
+ }
+
+ return TRUE;
+}
+#endif /* USE_SSE */
+
+static guint32
+arch_accel (void)
+{
+ guint32 caps;
+ X86Vendor vendor;
+
+ vendor = arch_get_vendor ();
+
+ switch (vendor)
+ {
+ case ARCH_X86_VENDOR_NONE:
+ caps = 0;
+ break;
+
+ case ARCH_X86_VENDOR_AMD:
+ case ARCH_X86_VENDOR_HYGON:
+ caps = arch_accel_amd ();
+ break;
+
+ case ARCH_X86_VENDOR_CENTAUR:
+ caps = arch_accel_centaur ();
+ break;
+
+ case ARCH_X86_VENDOR_CYRIX:
+ case ARCH_X86_VENDOR_NSC:
+ caps = arch_accel_cyrix ();
+ break;
+
+ /* check for what Intel speced, even if UNKNOWN */
+ default:
+ caps = arch_accel_intel ();
+ break;
+ }
+
+#ifdef USE_SSE
+ if ((caps & GIMP_CPU_ACCEL_X86_SSE) && !arch_accel_sse_os_support ())
+ caps &= ~(GIMP_CPU_ACCEL_X86_SSE | GIMP_CPU_ACCEL_X86_SSE2);
+#endif
+
+ return caps;
+}
+
+#endif /* ARCH_X86 && USE_MMX && __GNUC__ */
+
+
+#if defined(ARCH_PPC) && defined (USE_ALTIVEC)
+
+#if defined(HAVE_ALTIVEC_SYSCTL)
+
+#include <sys/sysctl.h>
+
+#define HAVE_ACCEL 1
+
+static guint32
+arch_accel (void)
+{
+ gint sels[2] = { CTL_HW, HW_VECTORUNIT };
+ gboolean has_vu = FALSE;
+ gsize length = sizeof(has_vu);
+ gint err;
+
+ err = sysctl (sels, 2, &has_vu, &length, NULL, 0);
+
+ if (err == 0 && has_vu)
+ return GIMP_CPU_ACCEL_PPC_ALTIVEC;
+
+ return 0;
+}
+
+#elif defined(__GNUC__)
+
+#define HAVE_ACCEL 1
+
+static sigjmp_buf jmpbuf;
+static volatile sig_atomic_t canjump = 0;
+
+static void
+sigill_handler (gint sig)
+{
+ if (!canjump)
+ {
+ signal (sig, SIG_DFL);
+ raise (sig);
+ }
+
+ canjump = 0;
+ siglongjmp (jmpbuf, 1);
+}
+
+static guint32
+arch_accel (void)
+{
+ signal (SIGILL, sigill_handler);
+
+ if (sigsetjmp (jmpbuf, 1))
+ {
+ signal (SIGILL, SIG_DFL);
+ return 0;
+ }
+
+ canjump = 1;
+
+ asm volatile ("mtspr 256, %0\n\t"
+ "vand %%v0, %%v0, %%v0"
+ :
+ : "r" (-1));
+
+ signal (SIGILL, SIG_DFL);
+
+ return GIMP_CPU_ACCEL_PPC_ALTIVEC;
+}
+#endif /* __GNUC__ */
+
+#endif /* ARCH_PPC && USE_ALTIVEC */
+
+
+static GimpCpuAccelFlags
+cpu_accel (void)
+{
+#ifdef HAVE_ACCEL
+ static guint32 accel = ~0U;
+
+ if (accel != ~0U)
+ return accel;
+
+ accel = arch_accel ();
+
+ return (GimpCpuAccelFlags) accel;
+
+#else /* !HAVE_ACCEL */
+ return GIMP_CPU_ACCEL_NONE;
+#endif
+}
diff --git a/libgimpbase/gimpcpuaccel.h b/libgimpbase/gimpcpuaccel.h
new file mode 100644
index 0000000..03c0d4b
--- /dev/null
+++ b/libgimpbase/gimpcpuaccel.h
@@ -0,0 +1,76 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_BASE_H_INSIDE__) && !defined (GIMP_BASE_COMPILATION)
+#error "Only <libgimpbase/gimpbase.h> can be included directly."
+#endif
+
+#ifndef __GIMP_CPU_ACCEL_H__
+#define __GIMP_CPU_ACCEL_H__
+
+G_BEGIN_DECLS
+
+
+/**
+ * GimpCpuAccelFlags:
+ * @GIMP_CPU_ACCEL_NONE: None
+ * @GIMP_CPU_ACCEL_X86_MMX: MMX
+ * @GIMP_CPU_ACCEL_X86_3DNOW: 3dNow
+ * @GIMP_CPU_ACCEL_X86_MMXEXT: MMXEXT
+ * @GIMP_CPU_ACCEL_X86_SSE: SSE
+ * @GIMP_CPU_ACCEL_X86_SSE2: SSE2
+ * @GIMP_CPU_ACCEL_X86_SSE3: SSE3
+ * @GIMP_CPU_ACCEL_X86_SSSE3: SSSE3
+ * @GIMP_CPU_ACCEL_X86_SSE4_1: SSE4_1
+ * @GIMP_CPU_ACCEL_X86_SSE4_2: SSE4_2
+ * @GIMP_CPU_ACCEL_X86_AVX: AVX
+ * @GIMP_CPU_ACCEL_PPC_ALTIVEC: Altivec
+ *
+ * Types of detectable CPU accelerations
+ **/
+typedef enum
+{
+ GIMP_CPU_ACCEL_NONE = 0x0,
+
+ /* x86 accelerations */
+ GIMP_CPU_ACCEL_X86_MMX = 0x80000000,
+ GIMP_CPU_ACCEL_X86_3DNOW = 0x40000000,
+ GIMP_CPU_ACCEL_X86_MMXEXT = 0x20000000,
+ GIMP_CPU_ACCEL_X86_SSE = 0x10000000,
+ GIMP_CPU_ACCEL_X86_SSE2 = 0x08000000,
+ GIMP_CPU_ACCEL_X86_SSE3 = 0x02000000,
+ GIMP_CPU_ACCEL_X86_SSSE3 = 0x01000000,
+ GIMP_CPU_ACCEL_X86_SSE4_1 = 0x00800000,
+ GIMP_CPU_ACCEL_X86_SSE4_2 = 0x00400000,
+ GIMP_CPU_ACCEL_X86_AVX = 0x00200000,
+
+ /* powerpc accelerations */
+ GIMP_CPU_ACCEL_PPC_ALTIVEC = 0x04000000
+} GimpCpuAccelFlags;
+
+
+GimpCpuAccelFlags gimp_cpu_accel_get_support (void);
+
+
+/* for internal use only */
+void gimp_cpu_accel_set_use (gboolean use);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_CPU_ACCEL_H__ */
diff --git a/libgimpbase/gimpdatafiles.c b/libgimpbase/gimpdatafiles.c
new file mode 100644
index 0000000..535f275
--- /dev/null
+++ b/libgimpbase/gimpdatafiles.c
@@ -0,0 +1,225 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * Datafiles module copyight (C) 1996 Federico Mena Quintero
+ * federico@nuclecu.unam.mx
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gio/gio.h>
+
+#include "gimpbasetypes.h"
+
+#include "gimpdatafiles.h"
+#include "gimpenv.h"
+
+
+/**
+ * SECTION: gimpdatafiles
+ * @title: gimpdatafiles
+ * @short_description: Functions to handle GIMP data files.
+ *
+ * Functions to handle GIMP data files.
+ **/
+
+
+static inline gboolean is_script (const gchar *filename);
+
+
+/* public functions */
+
+gboolean
+gimp_datafiles_check_extension (const gchar *filename,
+ const gchar *extension)
+{
+ gint name_len;
+ gint ext_len;
+
+ g_return_val_if_fail (filename != NULL, FALSE);
+ g_return_val_if_fail (extension != NULL, FALSE);
+
+ name_len = strlen (filename);
+ ext_len = strlen (extension);
+
+ if (! (name_len && ext_len && (name_len > ext_len)))
+ return FALSE;
+
+ return (g_ascii_strcasecmp (&filename[name_len - ext_len], extension) == 0);
+}
+
+void
+gimp_datafiles_read_directories (const gchar *path_str,
+ GFileTest flags,
+ GimpDatafileLoaderFunc loader_func,
+ gpointer user_data)
+{
+ gchar *local_path;
+ GList *path;
+ GList *list;
+
+ g_return_if_fail (path_str != NULL);
+ g_return_if_fail (loader_func != NULL);
+
+ local_path = g_strdup (path_str);
+
+ path = gimp_path_parse (local_path, 256, TRUE, NULL);
+
+ for (list = path; list; list = g_list_next (list))
+ {
+ const gchar *dirname = list->data;
+ GDir *dir;
+
+ dir = g_dir_open (dirname, 0, NULL);
+
+ if (dir)
+ {
+ const gchar *dir_ent;
+
+ while ((dir_ent = g_dir_read_name (dir)))
+ {
+ gchar *filename;
+ GFile *file;
+ GFileInfo *info;
+
+ filename = g_build_filename (dirname, dir_ent, NULL);
+ file = g_file_new_for_path (filename);
+
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN ","
+ G_FILE_ATTRIBUTE_STANDARD_TYPE ","
+ G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE ","
+ "time::*",
+ G_FILE_QUERY_INFO_NONE,
+ NULL, NULL);
+
+ if (info)
+ {
+ GimpDatafileData file_data;
+ GFileType file_type;
+
+ file_data.filename = filename;
+ file_data.dirname = dirname;
+ file_data.basename = dir_ent;
+
+ file_data.atime =
+ g_file_info_get_attribute_uint64 (info,
+ G_FILE_ATTRIBUTE_TIME_ACCESS);
+
+ file_data.mtime =
+ g_file_info_get_attribute_uint64 (info,
+ G_FILE_ATTRIBUTE_TIME_MODIFIED);
+
+ file_data.ctime =
+ g_file_info_get_attribute_uint64 (info,
+ G_FILE_ATTRIBUTE_TIME_CREATED);
+
+ file_type = g_file_info_get_file_type (info);
+
+ if (g_file_info_get_is_hidden (info))
+ {
+ /* do nothing */
+ }
+ else if (flags & G_FILE_TEST_EXISTS)
+ {
+ (* loader_func) (&file_data, user_data);
+ }
+ else if ((flags & G_FILE_TEST_IS_REGULAR) &&
+ (file_type == G_FILE_TYPE_REGULAR))
+ {
+ (* loader_func) (&file_data, user_data);
+ }
+ else if ((flags & G_FILE_TEST_IS_DIR) &&
+ (file_type == G_FILE_TYPE_DIRECTORY))
+ {
+ (* loader_func) (&file_data, user_data);
+ }
+ else if ((flags & G_FILE_TEST_IS_SYMLINK) &&
+ (file_type == G_FILE_TYPE_SYMBOLIC_LINK))
+ {
+ (* loader_func) (&file_data, user_data);
+ }
+ else if ((flags & G_FILE_TEST_IS_EXECUTABLE) &&
+ (g_file_info_get_attribute_boolean (info,
+ G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE) ||
+ ((file_type == G_FILE_TYPE_REGULAR) &&
+ is_script (filename))))
+ {
+ (* loader_func) (&file_data, user_data);
+ }
+
+ g_object_unref (info);
+ }
+
+ g_object_unref (file);
+ g_free (filename);
+ }
+
+ g_dir_close (dir);
+ }
+ }
+
+ gimp_path_free (path);
+ g_free (local_path);
+}
+
+
+/* private functions */
+
+static inline gboolean
+is_script (const gchar *filename)
+{
+#ifdef G_OS_WIN32
+ /* On Windows there is no concept like the Unix executable flag.
+ * There is a weak emulation provided by the MS C Runtime using file
+ * extensions (com, exe, cmd, bat). This needs to be extended to treat
+ * scripts (Python, Perl, ...) as executables, too. We use the PATHEXT
+ * variable, which is also used by cmd.exe.
+ */
+ static gchar **exts = NULL;
+
+ const gchar *ext = strrchr (filename, '.');
+ gchar *pathext;
+ gint i;
+
+ if (exts == NULL)
+ {
+ pathext = (gchar *) g_getenv ("PATHEXT");
+ if (pathext != NULL)
+ {
+ exts = g_strsplit (pathext, G_SEARCHPATH_SEPARATOR_S, 100);
+ }
+ else
+ {
+ exts = g_new (gchar *, 1);
+ exts[0] = NULL;
+ }
+ }
+
+ i = 0;
+ while (exts[i] != NULL)
+ {
+ if (g_ascii_strcasecmp (ext, exts[i]) == 0)
+ return TRUE;
+ i++;
+ }
+#endif /* G_OS_WIN32 */
+
+ return FALSE;
+}
diff --git a/libgimpbase/gimpdatafiles.h b/libgimpbase/gimpdatafiles.h
new file mode 100644
index 0000000..c75edab
--- /dev/null
+++ b/libgimpbase/gimpdatafiles.h
@@ -0,0 +1,72 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * Datafiles module copyight (C) 1996 Federico Mena Quintero
+ * federico@nuclecu.unam.mx
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_BASE_H_INSIDE__) && !defined (GIMP_BASE_COMPILATION)
+#error "Only <libgimpbase/gimpbase.h> can be included directly."
+#endif
+
+#ifndef __GIMP_DATAFILES_H__
+#define __GIMP_DATAFILES_H__
+
+#include <time.h>
+
+G_BEGIN_DECLS
+
+
+/**
+ * GimpDatafileData:
+ * @filename: the data file's full path.
+ * @dirname: the folder the data file is in.
+ * @basename: the data file's basename.
+ * @atime: the last time the file was accessed for reading.
+ * @mtime: the last time the file was modified.
+ * @ctime: the time the file was created.
+ *
+ * This structure is passed to the #GimpDatafileLoaderFunc given to
+ * gimp_datafiles_read_directories() for each file encountered in the
+ * data path.
+ **/
+struct _GimpDatafileData
+{
+ const gchar *filename;
+ const gchar *dirname;
+ const gchar *basename;
+
+ time_t atime;
+ time_t mtime;
+ time_t ctime;
+};
+
+
+GIMP_DEPRECATED
+gboolean gimp_datafiles_check_extension (const gchar *filename,
+ const gchar *extension);
+
+GIMP_DEPRECATED_FOR(GFileEnumerator)
+void gimp_datafiles_read_directories (const gchar *path_str,
+ GFileTest flags,
+ GimpDatafileLoaderFunc loader_func,
+ gpointer user_data);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_DATAFILES_H__ */
diff --git a/libgimpbase/gimpenv.c b/libgimpbase/gimpenv.c
new file mode 100644
index 0000000..10696a5
--- /dev/null
+++ b/libgimpbase/gimpenv.c
@@ -0,0 +1,1277 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpenv.c
+ * Copyright (C) 1999 Tor Lillqvist <tml@iki.fi>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef PLATFORM_OSX
+#include <AppKit/AppKit.h>
+#endif
+
+#include <gio/gio.h>
+#include <glib/gstdio.h>
+
+#undef GIMP_DISABLE_DEPRECATED
+#include "gimpbasetypes.h"
+
+#define __GIMP_ENV_C__
+#include "gimpenv.h"
+#include "gimpversion.h"
+#include "gimpreloc.h"
+
+#ifdef G_OS_WIN32
+#define STRICT
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <io.h>
+#ifndef S_IWUSR
+# define S_IWUSR _S_IWRITE
+#endif
+#ifndef S_IWGRP
+#define S_IWGRP (_S_IWRITE>>3)
+#define S_IWOTH (_S_IWRITE>>6)
+#endif
+#ifndef S_ISDIR
+# define __S_ISTYPE(mode, mask) (((mode) & _S_IFMT) == (mask))
+# define S_ISDIR(mode) __S_ISTYPE((mode), _S_IFDIR)
+#endif
+#define uid_t gint
+#define gid_t gint
+#define geteuid() 0
+#define getegid() 0
+
+#include <shlobj.h>
+
+/* Constant available since Shell32.dll 4.72 */
+#ifndef CSIDL_APPDATA
+#define CSIDL_APPDATA 0x001a
+#endif
+
+#endif
+
+
+/**
+ * SECTION: gimpenv
+ * @title: gimpenv
+ * @short_description: Functions to access the GIMP environment.
+ *
+ * A set of functions to find the locations of GIMP's data directories
+ * and configuration files.
+ **/
+
+
+static gchar * gimp_env_get_dir (const gchar *gimp_env_name,
+ const gchar *compile_time_dir,
+ const gchar *relative_subdir);
+#ifdef G_OS_WIN32
+static gchar * get_special_folder (gint csidl);
+#endif
+
+
+const guint gimp_major_version = GIMP_MAJOR_VERSION;
+const guint gimp_minor_version = GIMP_MINOR_VERSION;
+const guint gimp_micro_version = GIMP_MICRO_VERSION;
+
+
+/**
+ * gimp_env_init:
+ * @plug_in: must be %TRUE if this function is called from a plug-in
+ *
+ * You don't need to care about this function. It is being called for
+ * you automatically (by means of the MAIN() macro that every plug-in
+ * runs). Calling it again will cause a fatal error.
+ *
+ * Since: 2.4
+ */
+void
+gimp_env_init (gboolean plug_in)
+{
+ static gboolean gimp_env_initialized = FALSE;
+ const gchar *data_home = g_get_user_data_dir ();
+
+ if (gimp_env_initialized)
+ g_error ("gimp_env_init() must only be called once!");
+
+ gimp_env_initialized = TRUE;
+
+#ifndef G_OS_WIN32
+ if (plug_in)
+ {
+ _gimp_reloc_init_lib (NULL);
+ }
+ else if (_gimp_reloc_init (NULL))
+ {
+ /* Set $LD_LIBRARY_PATH to ensure that plugins can be loaded. */
+
+ const gchar *ldpath = g_getenv ("LD_LIBRARY_PATH");
+ gchar *libdir = g_build_filename (gimp_installation_directory (),
+ "lib",
+ NULL);
+
+ if (ldpath && *ldpath)
+ {
+ gchar *tmp = g_strconcat (libdir, ":", ldpath, NULL);
+
+ g_setenv ("LD_LIBRARY_PATH", tmp, TRUE);
+
+ g_free (tmp);
+ }
+ else
+ {
+ g_setenv ("LD_LIBRARY_PATH", libdir, TRUE);
+ }
+
+ g_free (libdir);
+ }
+#endif
+
+ /* The user data directory (XDG_DATA_HOME on Unix) is used to store
+ * various data, like crash logs (win32) or recently used file history
+ * (by GTK+). Yet it may be absent, in particular on non-Linux
+ * platforms. Make sure it exists.
+ */
+ if (! g_file_test (data_home, G_FILE_TEST_IS_DIR))
+ {
+ if (g_mkdir_with_parents (data_home, S_IRUSR | S_IWUSR | S_IXUSR) != 0)
+ {
+ g_warning ("Failed to create the data directory '%s': %s",
+ data_home, g_strerror (errno));
+ }
+ }
+}
+
+/**
+ * gimp_directory:
+ *
+ * Returns the user-specific GIMP settings directory. If the
+ * environment variable GIMP2_DIRECTORY exists, it is used. If it is
+ * an absolute path, it is used as is. If it is a relative path, it
+ * is taken to be a subdirectory of the home directory. If it is a
+ * relative path, and no home directory can be determined, it is taken
+ * to be a subdirectory of gimp_data_directory().
+ *
+ * The usual case is that no GIMP2_DIRECTORY environment variable
+ * exists, and then we use the GIMPDIR subdirectory of the local
+ * configuration directory:
+ *
+ * - UNIX: $XDG_CONFIG_HOME (defaults to $HOME/.config/)
+ *
+ * - Windows: CSIDL_APPDATA
+ *
+ * - OSX (UNIX exception): the Application Support Directory.
+ *
+ * If neither the configuration nor home directory exist,
+ * g_get_user_config_dir() will return {tmp}/{user_name}/.config/ where
+ * the temporary directory {tmp} and the {user_name} are determined
+ * according to platform rules.
+ *
+ * In any case, we always return some non-empty string, whether it
+ * corresponds to an existing directory or not.
+ *
+ * In config files such as gimprc, the string ${gimp_dir} expands to
+ * this directory.
+ *
+ * The returned string is owned by GIMP and must not be modified or
+ * freed. The returned string is in the encoding used for filenames by
+ * GLib, which isn't necessarily UTF-8 (on Windows it is always
+ * UTF-8.)
+ *
+ * Returns: The user-specific GIMP settings directory.
+ **/
+const gchar *
+gimp_directory (void)
+{
+ static gchar *gimp_dir = NULL;
+ static gchar *last_env_gimp_dir = NULL;
+
+ const gchar *env_gimp_dir;
+
+ env_gimp_dir = g_getenv ("GIMP2_DIRECTORY");
+
+ if (gimp_dir)
+ {
+ gboolean gimp2_directory_changed = FALSE;
+
+ /* We have constructed the gimp_dir already. We can return
+ * gimp_dir unless some parameter gimp_dir depends on has
+ * changed. For now we just check for changes to GIMP2_DIRECTORY
+ */
+ gimp2_directory_changed =
+ (env_gimp_dir == NULL &&
+ last_env_gimp_dir != NULL) ||
+ (env_gimp_dir != NULL &&
+ last_env_gimp_dir == NULL) ||
+ (env_gimp_dir != NULL &&
+ last_env_gimp_dir != NULL &&
+ strcmp (env_gimp_dir, last_env_gimp_dir) != 0);
+
+ if (! gimp2_directory_changed)
+ {
+ return gimp_dir;
+ }
+ else
+ {
+ /* Free the old gimp_dir and go on to update it */
+ g_free (gimp_dir);
+ gimp_dir = NULL;
+ }
+ }
+
+ /* Remember the GIMP2_DIRECTORY to next invocation so we can check
+ * if it changes
+ */
+ g_free (last_env_gimp_dir);
+ last_env_gimp_dir = g_strdup (env_gimp_dir);
+
+ if (env_gimp_dir)
+ {
+ if (g_path_is_absolute (env_gimp_dir))
+ {
+ gimp_dir = g_strdup (env_gimp_dir);
+ }
+ else
+ {
+ const gchar *home_dir = g_get_home_dir ();
+
+ if (home_dir)
+ gimp_dir = g_build_filename (home_dir, env_gimp_dir, NULL);
+ else
+ gimp_dir = g_build_filename (gimp_data_directory (), env_gimp_dir, NULL);
+ }
+ }
+ else if (g_path_is_absolute (GIMPDIR))
+ {
+ gimp_dir = g_strdup (GIMPDIR);
+ }
+ else
+ {
+#ifdef PLATFORM_OSX
+
+ NSAutoreleasePool *pool;
+ NSArray *path;
+ NSString *library_dir;
+
+ pool = [[NSAutoreleasePool alloc] init];
+
+ path = NSSearchPathForDirectoriesInDomains (NSApplicationSupportDirectory,
+ NSUserDomainMask, YES);
+ library_dir = [path objectAtIndex:0];
+
+ gimp_dir = g_build_filename ([library_dir UTF8String],
+ GIMPDIR, GIMP_USER_VERSION, NULL);
+
+ [pool drain];
+
+#elif defined G_OS_WIN32
+
+ gchar *conf_dir = get_special_folder (CSIDL_APPDATA);
+
+ gimp_dir = g_build_filename (conf_dir,
+ GIMPDIR, GIMP_USER_VERSION, NULL);
+ g_free(conf_dir);
+
+#else /* UNIX */
+
+ /* g_get_user_config_dir () always returns a path as a non-null
+ * and non-empty string
+ */
+ gimp_dir = g_build_filename (g_get_user_config_dir (),
+ GIMPDIR, GIMP_USER_VERSION, NULL);
+
+#endif /* PLATFORM_OSX */
+ }
+
+ return gimp_dir;
+}
+
+#ifdef G_OS_WIN32
+
+/* Taken from glib 2.35 code. */
+static gchar *
+get_special_folder (int csidl)
+{
+ wchar_t path[MAX_PATH+1];
+ HRESULT hr;
+ LPITEMIDLIST pidl = NULL;
+ BOOL b;
+ gchar *retval = NULL;
+
+ hr = SHGetSpecialFolderLocation (NULL, csidl, &pidl);
+ if (hr == S_OK)
+ {
+ b = SHGetPathFromIDListW (pidl, path);
+ if (b)
+ retval = g_utf16_to_utf8 (path, -1, NULL, NULL, NULL);
+ CoTaskMemFree (pidl);
+ }
+
+ return retval;
+}
+
+static HMODULE libgimpbase_dll = NULL;
+
+/* Minimal DllMain that just stores the handle to this DLL */
+
+BOOL WINAPI /* Avoid silly "no previous prototype" gcc warning */
+DllMain (HINSTANCE hinstDLL,
+ DWORD fdwReason,
+ LPVOID lpvReserved);
+
+BOOL WINAPI
+DllMain (HINSTANCE hinstDLL,
+ DWORD fdwReason,
+ LPVOID lpvReserved)
+{
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ libgimpbase_dll = hinstDLL;
+ break;
+ }
+
+ return TRUE;
+}
+
+#endif
+
+/**
+ * gimp_installation_directory:
+ *
+ * Returns the top installation directory of GIMP. On Unix the
+ * compile-time defined installation prefix is used. On Windows, the
+ * installation directory as deduced from the executable's full
+ * filename is used. On OSX we ask [NSBundle mainBundle] for the
+ * resource path to check if GIMP is part of a relocatable bundle.
+ *
+ * In config files such as gimprc, the string ${gimp_installation_dir}
+ * expands to this directory.
+ *
+ * The returned string is owned by GIMP and must not be modified or
+ * freed. The returned string is in the encoding used for filenames by
+ * GLib, which isn't necessarily UTF-8. (On Windows it always is
+ * UTF-8.)
+ *
+ * Since: 2.8
+ *
+ * Returns: The toplevel installation directory of GIMP.
+ **/
+const gchar *
+gimp_installation_directory (void)
+{
+ static gchar *toplevel = NULL;
+
+ if (toplevel)
+ return toplevel;
+
+#ifdef G_OS_WIN32
+
+ toplevel = g_win32_get_package_installation_directory_of_module (libgimpbase_dll);
+ if (! toplevel)
+ g_error ("g_win32_get_package_installation_directory_of_module() failed");
+
+#elif PLATFORM_OSX
+
+ {
+ NSAutoreleasePool *pool;
+ NSString *resource_path;
+ gchar *basename;
+ gchar *basepath;
+ gchar *dirname;
+
+ pool = [[NSAutoreleasePool alloc] init];
+
+ resource_path = [[NSBundle mainBundle] resourcePath];
+
+ basename = g_path_get_basename ([resource_path UTF8String]);
+ basepath = g_path_get_dirname ([resource_path UTF8String]);
+ dirname = g_path_get_basename (basepath);
+
+ if (! strcmp (basename, ".libs"))
+ {
+ /* we are running from the source dir, do normal unix things */
+
+ toplevel = _gimp_reloc_find_prefix (PREFIX);
+ }
+ else if (! strcmp (basename, "bin"))
+ {
+ /* we are running the main app, but not from a bundle, the resource
+ * path is the directory which contains the executable
+ */
+
+ toplevel = g_strdup (basepath);
+ }
+ else if (! strcmp (basename, "plug-ins"))
+ {
+ /* same for plug-ins, go three levels up from prefix/lib/gimp/x.y */
+
+ gchar *tmp = g_path_get_dirname (basepath);
+ gchar *tmp2 = g_path_get_dirname (tmp);
+
+ toplevel = g_path_get_dirname (tmp2);
+
+ g_free (tmp);
+ g_free (tmp2);
+ }
+ else if (! strcmp (dirname, "plug-ins"))
+ {
+ /* same for plug-ins in subdirectory, go three levels up from prefix/lib/gimp/x.y */
+
+ gchar *tmp = g_path_get_dirname (basepath);
+ gchar *tmp2 = g_path_get_dirname (tmp);
+ gchar *tmp3 = g_path_get_dirname (tmp2);
+
+ toplevel = g_path_get_dirname (tmp3);
+
+ g_free (tmp);
+ g_free (tmp2);
+ g_free (tmp3);
+ }
+ else
+ {
+ /* if none of the above match, we assume that we are really in a bundle */
+
+ toplevel = g_strdup ([resource_path UTF8String]);
+ }
+
+ g_free (basename);
+ g_free (basepath);
+ g_free (dirname);
+
+ [pool drain];
+ }
+
+#else
+
+ toplevel = _gimp_reloc_find_prefix (PREFIX);
+
+#endif
+
+ return toplevel;
+}
+
+/**
+ * gimp_data_directory:
+ *
+ * Returns the default top directory for GIMP data. If the environment
+ * variable GIMP2_DATADIR exists, that is used. It should be an
+ * absolute pathname. Otherwise, on Unix the compile-time defined
+ * directory is used. On Windows, the installation directory as
+ * deduced from the executable's full filename is used.
+ *
+ * Note that the actual directories used for GIMP data files can be
+ * overridden by the user in the preferences dialog.
+ *
+ * In config files such as gimprc, the string ${gimp_data_dir} expands
+ * to this directory.
+ *
+ * The returned string is owned by GIMP and must not be modified or
+ * freed. The returned string is in the encoding used for filenames by
+ * GLib, which isn't necessarily UTF-8. (On Windows it always is
+ * UTF-8.)
+ *
+ * Returns: The top directory for GIMP data.
+ **/
+const gchar *
+gimp_data_directory (void)
+{
+ static gchar *gimp_data_dir = NULL;
+
+ if (! gimp_data_dir)
+ {
+ gchar *tmp = g_build_filename ("share",
+ GIMP_PACKAGE,
+ GIMP_DATA_VERSION,
+ NULL);
+
+ gimp_data_dir = gimp_env_get_dir ("GIMP2_DATADIR", GIMPDATADIR, tmp);
+ g_free (tmp);
+ }
+
+ return gimp_data_dir;
+}
+
+/**
+ * gimp_locale_directory:
+ *
+ * Returns the top directory for GIMP locale files. If the environment
+ * variable GIMP2_LOCALEDIR exists, that is used. It should be an
+ * absolute pathname. Otherwise, on Unix the compile-time defined
+ * directory is used. On Windows, the installation directory as deduced
+ * from the executable's full filename is used.
+ *
+ * The returned string is owned by GIMP and must not be modified or
+ * freed. The returned string is in the encoding used for filenames by
+ * the C library, which isn't necessarily UTF-8. (On Windows, unlike
+ * the other similar functions here, the return value from this
+ * function is in the system codepage, never in UTF-8. It can thus be
+ * passed directly to the bindtextdomain() function from libintl which
+ * does not handle UTF-8.)
+ *
+ * Returns: The top directory for GIMP locale files.
+ */
+const gchar *
+gimp_locale_directory (void)
+{
+ static gchar *gimp_locale_dir = NULL;
+
+ if (! gimp_locale_dir)
+ {
+ gchar *tmp = g_build_filename ("share",
+ "locale",
+ NULL);
+
+ gimp_locale_dir = gimp_env_get_dir ("GIMP2_LOCALEDIR", LOCALEDIR, tmp);
+ g_free (tmp);
+
+#ifdef G_OS_WIN32
+ /* FIXME: g_win32_locale_filename_from_utf8() can actually return
+ * NULL (we had actual cases of this). Not sure exactly what
+ * gimp_locale_directory() should do when this happens. Anyway
+ * that's really broken, and something should be done some day
+ * about this!
+ */
+ tmp = g_win32_locale_filename_from_utf8 (gimp_locale_dir);
+ g_free (gimp_locale_dir);
+ gimp_locale_dir = tmp;
+#endif
+ }
+
+ return gimp_locale_dir;
+}
+
+/**
+ * gimp_sysconf_directory:
+ *
+ * Returns the top directory for GIMP config files. If the environment
+ * variable GIMP2_SYSCONFDIR exists, that is used. It should be an
+ * absolute pathname. Otherwise, on Unix the compile-time defined
+ * directory is used. On Windows, the installation directory as deduced
+ * from the executable's full filename is used.
+ *
+ * In config files such as gimprc, the string ${gimp_sysconf_dir}
+ * expands to this directory.
+ *
+ * The returned string is owned by GIMP and must not be modified or
+ * freed. The returned string is in the encoding used for filenames by
+ * GLib, which isn't necessarily UTF-8. (On Windows it always is
+ * UTF-8.).
+ *
+ * Returns: The top directory for GIMP config files.
+ **/
+const gchar *
+gimp_sysconf_directory (void)
+{
+ static gchar *gimp_sysconf_dir = NULL;
+
+ if (! gimp_sysconf_dir)
+ {
+ gchar *tmp = g_build_filename ("etc",
+ GIMP_PACKAGE,
+ GIMP_SYSCONF_VERSION,
+ NULL);
+
+ gimp_sysconf_dir = gimp_env_get_dir ("GIMP2_SYSCONFDIR", GIMPSYSCONFDIR, tmp);
+ g_free (tmp);
+ }
+
+ return gimp_sysconf_dir;
+}
+
+/**
+ * gimp_plug_in_directory:
+ *
+ * Returns the default top directory for GIMP plug-ins and modules. If
+ * the environment variable GIMP2_PLUGINDIR exists, that is used. It
+ * should be an absolute pathname. Otherwise, on Unix the compile-time
+ * defined directory is used. On Windows, the installation directory
+ * as deduced from the executable's full filename is used.
+ *
+ * Note that the actual directories used for GIMP plug-ins and modules
+ * can be overridden by the user in the preferences dialog.
+ *
+ * In config files such as gimprc, the string ${gimp_plug_in_dir}
+ * expands to this directory.
+ *
+ * The returned string is owned by GIMP and must not be modified or
+ * freed. The returned string is in the encoding used for filenames by
+ * GLib, which isn't necessarily UTF-8. (On Windows it always is
+ * UTF-8.)
+ *
+ * Returns: The top directory for GIMP plug_ins and modules.
+ **/
+const gchar *
+gimp_plug_in_directory (void)
+{
+ static gchar *gimp_plug_in_dir = NULL;
+
+ if (! gimp_plug_in_dir)
+ {
+ gchar *tmp = g_build_filename ("lib",
+ GIMP_PACKAGE,
+ GIMP_PLUGIN_VERSION,
+ NULL);
+
+ gimp_plug_in_dir = gimp_env_get_dir ("GIMP2_PLUGINDIR", PLUGINDIR, tmp);
+ g_free (tmp);
+ }
+
+ return gimp_plug_in_dir;
+}
+
+/**
+ * gimp_cache_directory:
+ *
+ * Returns the default top directory for GIMP cached files. If the
+ * environment variable GIMP2_CACHEDIR exists, that is used. It
+ * should be an absolute pathname. Otherwise, a subdirectory of the
+ * directory returned by g_get_user_cache_dir() is used.
+ *
+ * Note that the actual directories used for GIMP caches files can
+ * be overridden by the user in the preferences dialog.
+ *
+ * In config files such as gimprc, the string ${gimp_cache_dir}
+ * expands to this directory.
+ *
+ * The returned string is owned by GIMP and must not be modified or
+ * freed. The returned string is in the encoding used for filenames by
+ * GLib, which isn't necessarily UTF-8. (On Windows it always is
+ * UTF-8.).
+ *
+ * Since: 2.10.10
+ *
+ * Returns: The default top directory for GIMP cached files.
+ **/
+const gchar *
+gimp_cache_directory (void)
+{
+ static gchar *gimp_cache_dir = NULL;
+
+ if (! gimp_cache_dir)
+ {
+ gchar *tmp = g_build_filename (g_get_user_cache_dir (),
+ GIMP_PACKAGE,
+ GIMP_USER_VERSION,
+ NULL);
+
+ gimp_cache_dir = gimp_env_get_dir ("GIMP2_CACHEDIR", NULL, tmp);
+ g_free (tmp);
+ }
+
+ return gimp_cache_dir;
+}
+
+/**
+ * gimp_temp_directory:
+ *
+ * Returns the default top directory for GIMP temporary files. If the
+ * environment variable GIMP2_TEMPDIR exists, that is used. It
+ * should be an absolute pathname. Otherwise, a subdirectory of the
+ * directory returned by g_get_tmp_dir() is used.
+ *
+ * In config files such as gimprc, the string ${gimp_temp_dir} expands
+ * to this directory.
+ *
+ * Note that the actual directories used for GIMP temporary files can
+ * be overridden by the user in the preferences dialog.
+ *
+ * The returned string is owned by GIMP and must not be modified or
+ * freed. The returned string is in the encoding used for filenames by
+ * GLib, which isn't necessarily UTF-8. (On Windows it always is
+ * UTF-8.).
+ *
+ * Since: 2.10.10
+ *
+ * Returns: The default top directory for GIMP temporary files.
+ **/
+const gchar *
+gimp_temp_directory (void)
+{
+ static gchar *gimp_temp_dir = NULL;
+
+ if (! gimp_temp_dir)
+ {
+ gchar *tmp = g_build_filename (g_get_tmp_dir (),
+ GIMP_PACKAGE,
+ GIMP_USER_VERSION,
+ NULL);
+
+ gimp_temp_dir = gimp_env_get_dir ("GIMP2_TEMPDIR", NULL, tmp);
+ g_free (tmp);
+ }
+
+ return gimp_temp_dir;
+}
+
+static GFile *
+gimp_child_file (const gchar *parent,
+ const gchar *element,
+ va_list args)
+{
+ GFile *file = g_file_new_for_path (parent);
+
+ while (element)
+ {
+ GFile *child = g_file_get_child (file, element);
+
+ g_object_unref (file);
+ file = child;
+
+ element = va_arg (args, const gchar *);
+ }
+
+ return file;
+}
+
+/**
+ * gimp_directory_file:
+ * @first_element: the first element of a path to a file in the
+ * user's GIMP directory, or %NULL.
+ * @...: a %NULL terminated list of the remaining elements of the path
+ * to the file.
+ *
+ * Returns a #GFile in the user's GIMP directory, or the data
+ * directory itself if @first_element is %NULL.
+ *
+ * See also: gimp_directory().
+ *
+ * Since: 2.10
+ *
+ * Returns: a new @GFile for the path, Free with g_object_unref().
+ **/
+GFile *
+gimp_directory_file (const gchar *first_element,
+ ...)
+{
+ GFile *file;
+ va_list args;
+
+ va_start (args, first_element);
+ file = gimp_child_file (gimp_directory (), first_element, args);
+ va_end (args);
+
+ return file;
+}
+
+/**
+ * gimp_installation_directory_file:
+ * @first_element: the first element of a path to a file in the
+ * top installation directory, or %NULL.
+ * @...: a %NULL terminated list of the remaining elements of the path
+ * to the file.
+ *
+ * Returns a #GFile in the installation directory, or the installation
+ * directory itself if @first_element is %NULL.
+ *
+ * See also: gimp_installation_directory().
+ *
+ * Since: 2.10.10
+ *
+ * Returns: a new @GFile for the path, Free with g_object_unref().
+ **/
+GFile *
+gimp_installation_directory_file (const gchar *first_element,
+ ...)
+{
+ GFile *file;
+ va_list args;
+
+ va_start (args, first_element);
+ file = gimp_child_file (gimp_installation_directory (), first_element, args);
+ va_end (args);
+
+ return file;
+}
+
+/**
+ * gimp_data_directory_file:
+ * @first_element: the first element of a path to a file in the
+ * data directory, or %NULL.
+ * @...: a %NULL terminated list of the remaining elements of the path
+ * to the file.
+ *
+ * Returns a #GFile in the data directory, or the data directory
+ * itself if @first_element is %NULL.
+ *
+ * See also: gimp_data_directory().
+ *
+ * Since: 2.10
+ *
+ * Returns: a new @GFile for the path, Free with g_object_unref().
+ **/
+GFile *
+gimp_data_directory_file (const gchar *first_element,
+ ...)
+{
+ GFile *file;
+ va_list args;
+
+ va_start (args, first_element);
+ file = gimp_child_file (gimp_data_directory (), first_element, args);
+ va_end (args);
+
+ return file;
+}
+
+/**
+ * gimp_locale_directory_file:
+ * @first_element: the first element of a path to a file in the
+ * locale directory, or %NULL.
+ * @...: a %NULL terminated list of the remaining elements of the path
+ * to the file.
+ *
+ * Returns a #GFile in the locale directory, or the locale directory
+ * itself if @first_element is %NULL.
+ *
+ * See also: gimp_locale_directory().
+ *
+ * Since: 2.10
+ *
+ * Returns: a new @GFile for the path, Free with g_object_unref().
+ **/
+GFile *
+gimp_locale_directory_file (const gchar *first_element,
+ ...)
+{
+ GFile *file;
+ va_list args;
+
+ va_start (args, first_element);
+ file = gimp_child_file (gimp_locale_directory (), first_element, args);
+ va_end (args);
+
+ return file;
+}
+
+/**
+ * gimp_sysconf_directory_file:
+ * @first_element: the first element of a path to a file in the
+ * sysconf directory, or %NULL.
+ * @...: a %NULL terminated list of the remaining elements of the path
+ * to the file.
+ *
+ * Returns a #GFile in the sysconf directory, or the sysconf directory
+ * itself if @first_element is %NULL.
+ *
+ * See also: gimp_sysconf_directory().
+ *
+ * Since: 2.10
+ *
+ * Returns: a new @GFile for the path, Free with g_object_unref().
+ **/
+GFile *
+gimp_sysconf_directory_file (const gchar *first_element,
+ ...)
+{
+ GFile *file;
+ va_list args;
+
+ va_start (args, first_element);
+ file = gimp_child_file (gimp_sysconf_directory (), first_element, args);
+ va_end (args);
+
+ return file;
+}
+
+/**
+ * gimp_plug_in_directory_file:
+ * @first_element: the first element of a path to a file in the
+ * plug-in directory, or %NULL.
+ * @...: a %NULL terminated list of the remaining elements of the path
+ * to the file.
+ *
+ * Returns a #GFile in the plug-in directory, or the plug-in directory
+ * itself if @first_element is %NULL.
+ *
+ * See also: gimp_plug_in_directory().
+ *
+ * Since: 2.10
+ *
+ * Returns: a new @GFile for the path, Free with g_object_unref().
+ **/
+GFile *
+gimp_plug_in_directory_file (const gchar *first_element,
+ ...)
+{
+ GFile *file;
+ va_list args;
+
+ va_start (args, first_element);
+ file = gimp_child_file (gimp_plug_in_directory (), first_element, args);
+ va_end (args);
+
+ return file;
+}
+
+/**
+ * gimp_user_directory:
+ * @type: the type of user directory to retrieve
+ *
+ * This procedure is deprecated! Use g_get_user_special_dir() instead.
+ *
+ * Returns: The path to the specified user directory, or %NULL if the
+ * logical ID was not found.
+ *
+ * Since: 2.4
+ **/
+const gchar *
+gimp_user_directory (GimpUserDirectory type)
+{
+ return g_get_user_special_dir ((GUserDirectory) type);
+}
+
+/**
+ * gimp_personal_rc_file:
+ * @basename: The basename of a rc_file.
+ *
+ * Returns the name of a file in the user-specific GIMP settings directory.
+ *
+ * The returned string is newly allocated and should be freed with
+ * g_free() after use. The returned string is in the encoding used for
+ * filenames by GLib, which isn't necessarily UTF-8. (On Windows it
+ * always is UTF-8.)
+ *
+ * Returns: The name of a file in the user-specific GIMP settings directory.
+ **/
+gchar *
+gimp_personal_rc_file (const gchar *basename)
+{
+ return g_build_filename (gimp_directory (), basename, NULL);
+}
+
+/**
+ * gimp_gtkrc:
+ *
+ * Returns the name of GIMP's application-specific gtkrc file.
+ *
+ * The returned string is owned by GIMP and must not be modified or
+ * freed. The returned string is in the encoding used for filenames by
+ * GLib, which isn't necessarily UTF-8. (On Windows it always is
+ * UTF-8.)
+ *
+ * Returns: The name of GIMP's application-specific gtkrc file.
+ **/
+const gchar *
+gimp_gtkrc (void)
+{
+ static gchar *gimp_gtkrc_filename = NULL;
+
+ if (! gimp_gtkrc_filename)
+ gimp_gtkrc_filename = g_build_filename (gimp_data_directory (),
+ "themes", "System", "gtkrc",
+ NULL);
+
+ return gimp_gtkrc_filename;
+}
+
+/**
+ * gimp_path_runtime_fix:
+ * @path: A pointer to a string (allocated with g_malloc) that is
+ * (or could be) a pathname.
+ *
+ * On Windows, this function checks if the string pointed to by @path
+ * starts with the compile-time prefix, and in that case, replaces the
+ * prefix with the run-time one. @path should be a pointer to a
+ * dynamically allocated (with g_malloc, g_strconcat, etc) string. If
+ * the replacement takes place, the original string is deallocated,
+ * and *@path is replaced with a pointer to a new string with the
+ * run-time prefix spliced in.
+ *
+ * On Linux, it does the same thing, but only if BinReloc support is enabled.
+ * On other Unices, it does nothing because those platforms don't have a
+ * way to find out where our binary is.
+ */
+static void
+gimp_path_runtime_fix (gchar **path)
+{
+#if defined (G_OS_WIN32) && defined (PREFIX)
+ gchar *p;
+
+ /* Yes, I do mean forward slashes below */
+ if (strncmp (*path, PREFIX "/", strlen (PREFIX "/")) == 0)
+ {
+ /* This is a compile-time entry. Replace the path with the
+ * real one on this machine.
+ */
+ p = *path;
+ *path = g_strconcat (gimp_installation_directory (),
+ "\\",
+ *path + strlen (PREFIX "/"),
+ NULL);
+ g_free (p);
+ }
+ /* Replace forward slashes with backslashes, just for
+ * completeness */
+ p = *path;
+ while ((p = strchr (p, '/')) != NULL)
+ {
+ *p = '\\';
+ p++;
+ }
+#elif defined (G_OS_WIN32)
+ /* without defineing PREFIX do something useful too */
+ gchar *p = *path;
+ if (!g_path_is_absolute (p))
+ {
+ *path = g_build_filename (gimp_installation_directory (), *path, NULL);
+ g_free (p);
+ }
+#else
+ gchar *p;
+
+ if (strncmp (*path, PREFIX G_DIR_SEPARATOR_S,
+ strlen (PREFIX G_DIR_SEPARATOR_S)) == 0)
+ {
+ /* This is a compile-time entry. Replace the path with the
+ * real one on this machine.
+ */
+ p = *path;
+ *path = g_build_filename (gimp_installation_directory (),
+ *path + strlen (PREFIX G_DIR_SEPARATOR_S),
+ NULL);
+ g_free (p);
+ }
+#endif
+}
+
+/**
+ * gimp_path_parse:
+ * @path: A list of directories separated by #G_SEARCHPATH_SEPARATOR.
+ * @max_paths: The maximum number of directories to return.
+ * @check: %TRUE if you want the directories to be checked.
+ * @check_failed: Returns a #GList of path elements for which the
+ * check failed.
+ *
+ * Returns: A #GList of all directories in @path.
+ **/
+GList *
+gimp_path_parse (const gchar *path,
+ gint max_paths,
+ gboolean check,
+ GList **check_failed)
+{
+ gchar **patharray;
+ GList *list = NULL;
+ GList *fail_list = NULL;
+ gint i;
+ gboolean exists = TRUE;
+
+ if (!path || !*path || max_paths < 1 || max_paths > 256)
+ return NULL;
+
+ patharray = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, max_paths);
+
+ for (i = 0; i < max_paths; i++)
+ {
+ GString *dir;
+
+ if (! patharray[i])
+ break;
+
+#ifndef G_OS_WIN32
+ if (*patharray[i] == '~')
+ {
+ dir = g_string_new (g_get_home_dir ());
+ g_string_append (dir, patharray[i] + 1);
+ }
+ else
+#endif
+ {
+ gimp_path_runtime_fix (&patharray[i]);
+ dir = g_string_new (patharray[i]);
+ }
+
+ if (check)
+ exists = g_file_test (dir->str, G_FILE_TEST_IS_DIR);
+
+ if (exists)
+ {
+ GList *dup;
+
+ /* check for duplicate entries, see bug #784502 */
+ for (dup = list; dup; dup = g_list_next (dup))
+ {
+ if (! strcmp (dir->str, dup->data))
+ break;
+ }
+
+ /* only add to the list if it's not a duplicate */
+ if (! dup)
+ list = g_list_prepend (list, g_strdup (dir->str));
+ }
+ else if (check_failed)
+ {
+ fail_list = g_list_prepend (fail_list, g_strdup (dir->str));
+ }
+
+ g_string_free (dir, TRUE);
+ }
+
+ g_strfreev (patharray);
+
+ list = g_list_reverse (list);
+
+ if (check && check_failed)
+ {
+ fail_list = g_list_reverse (fail_list);
+ *check_failed = fail_list;
+ }
+
+ return list;
+}
+
+/**
+ * gimp_path_to_str:
+ * @path: A list of directories as returned by gimp_path_parse().
+ *
+ * Returns: A searchpath string separated by #G_SEARCHPATH_SEPARATOR.
+ **/
+gchar *
+gimp_path_to_str (GList *path)
+{
+ GString *str = NULL;
+ GList *list;
+ gchar *retval = NULL;
+
+ for (list = path; list; list = g_list_next (list))
+ {
+ gchar *dir = list->data;
+
+ if (str)
+ {
+ g_string_append_c (str, G_SEARCHPATH_SEPARATOR);
+ g_string_append (str, dir);
+ }
+ else
+ {
+ str = g_string_new (dir);
+ }
+ }
+
+ if (str)
+ retval = g_string_free (str, FALSE);
+
+ return retval;
+}
+
+/**
+ * gimp_path_free:
+ * @path: A list of directories as returned by gimp_path_parse().
+ *
+ * This function frees the memory allocated for the list and the strings
+ * it contains.
+ **/
+void
+gimp_path_free (GList *path)
+{
+ g_list_free_full (path, (GDestroyNotify) g_free);
+}
+
+/**
+ * gimp_path_get_user_writable_dir:
+ * @path: A list of directories as returned by gimp_path_parse().
+ *
+ * Note that you have to g_free() the returned string.
+ *
+ * Returns: The first directory in @path where the user has write permission.
+ **/
+gchar *
+gimp_path_get_user_writable_dir (GList *path)
+{
+ GList *list;
+ uid_t euid;
+ gid_t egid;
+ GStatBuf filestat;
+ gint err;
+
+ g_return_val_if_fail (path != NULL, NULL);
+
+ euid = geteuid ();
+ egid = getegid ();
+
+ for (list = path; list; list = g_list_next (list))
+ {
+ gchar *dir = list->data;
+
+ /* check if directory exists */
+ err = g_stat (dir, &filestat);
+
+ /* this is tricky:
+ * if a file is e.g. owned by the current user but not user-writable,
+ * the user has no permission to write to the file regardless
+ * of his group's or other's write permissions
+ */
+ if (!err && S_ISDIR (filestat.st_mode) &&
+
+ ((filestat.st_mode & S_IWUSR) ||
+
+ ((filestat.st_mode & S_IWGRP) &&
+ (euid != filestat.st_uid)) ||
+
+ ((filestat.st_mode & S_IWOTH) &&
+ (euid != filestat.st_uid) &&
+ (egid != filestat.st_gid))))
+ {
+ return g_strdup (dir);
+ }
+ }
+
+ return NULL;
+}
+
+static gchar *
+gimp_env_get_dir (const gchar *gimp_env_name,
+ const gchar *compile_time_dir,
+ const gchar *relative_subdir)
+{
+ const gchar *env = g_getenv (gimp_env_name);
+
+ if (env)
+ {
+ if (! g_path_is_absolute (env))
+ g_error ("%s environment variable should be an absolute path.",
+ gimp_env_name);
+
+ return g_strdup (env);
+ }
+ else if (compile_time_dir)
+ {
+ gchar *retval = g_strdup (compile_time_dir);
+
+ gimp_path_runtime_fix (&retval);
+
+ return retval;
+ }
+ else if (! g_path_is_absolute (relative_subdir))
+ {
+ return g_build_filename (gimp_installation_directory (),
+ relative_subdir,
+ NULL);
+ }
+
+ return g_strdup (relative_subdir);
+}
diff --git a/libgimpbase/gimpenv.h b/libgimpbase/gimpenv.h
new file mode 100644
index 0000000..04a83af
--- /dev/null
+++ b/libgimpbase/gimpenv.h
@@ -0,0 +1,96 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpenv.h
+ * Copyright (C) 1999 Tor Lillqvist <tml@iki.fi>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_BASE_H_INSIDE__) && !defined (GIMP_BASE_COMPILATION)
+#error "Only <libgimpbase/gimpbase.h> can be included directly."
+#endif
+
+#ifndef __GIMP_ENV_H__
+#define __GIMP_ENV_H__
+
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+#ifdef G_OS_WIN32
+# ifdef __GIMP_ENV_C__
+# define GIMPVAR extern __declspec(dllexport)
+# else /* !__GIMP_ENV_C__ */
+# define GIMPVAR extern __declspec(dllimport)
+# endif /* !__GIMP_ENV_C__ */
+#else /* !G_OS_WIN32 */
+# define GIMPVAR extern
+#endif
+
+GIMPVAR const guint gimp_major_version;
+GIMPVAR const guint gimp_minor_version;
+GIMPVAR const guint gimp_micro_version;
+
+
+const gchar * gimp_directory (void) G_GNUC_CONST;
+const gchar * gimp_installation_directory (void) G_GNUC_CONST;
+const gchar * gimp_data_directory (void) G_GNUC_CONST;
+const gchar * gimp_locale_directory (void) G_GNUC_CONST;
+const gchar * gimp_sysconf_directory (void) G_GNUC_CONST;
+const gchar * gimp_plug_in_directory (void) G_GNUC_CONST;
+const gchar * gimp_cache_directory (void) G_GNUC_CONST;
+const gchar * gimp_temp_directory (void) G_GNUC_CONST;
+
+GFile * gimp_directory_file (const gchar *first_element,
+ ...) G_GNUC_MALLOC;
+GFile * gimp_installation_directory_file (const gchar *first_element,
+ ...) G_GNUC_MALLOC;
+GFile * gimp_data_directory_file (const gchar *first_element,
+ ...) G_GNUC_MALLOC;
+GFile * gimp_locale_directory_file (const gchar *first_element,
+ ...) G_GNUC_MALLOC;
+GFile * gimp_sysconf_directory_file (const gchar *first_element,
+ ...) G_GNUC_MALLOC;
+GFile * gimp_plug_in_directory_file (const gchar *first_element,
+ ...) G_GNUC_MALLOC;
+
+#ifndef GIMP_DISABLE_DEPRECATED
+GIMP_DEPRECATED_FOR(g_get_user_special_dir)
+const gchar * gimp_user_directory (GimpUserDirectory type) G_GNUC_CONST;
+#endif /* !GIMP_DISABLE_DEPRECATED */
+
+const gchar * gimp_gtkrc (void) G_GNUC_CONST;
+gchar * gimp_personal_rc_file (const gchar *basename) G_GNUC_MALLOC;
+
+GList * gimp_path_parse (const gchar *path,
+ gint max_paths,
+ gboolean check,
+ GList **check_failed);
+gchar * gimp_path_to_str (GList *path) G_GNUC_MALLOC;
+void gimp_path_free (GList *path);
+
+gchar * gimp_path_get_user_writable_dir (GList *path) G_GNUC_MALLOC;
+
+
+/* should be considered private, don't use! */
+void gimp_env_init (gboolean plug_in);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_ENV_H__ */
diff --git a/libgimpbase/gimplimits.h b/libgimpbase/gimplimits.h
new file mode 100644
index 0000000..75a08b9
--- /dev/null
+++ b/libgimpbase/gimplimits.h
@@ -0,0 +1,97 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1999 Peter Mattis and Spencer Kimball
+ *
+ * gimplimits.h
+ * Copyright (C) 1999 Michael Natterer <mitschel@cs.tu-berlin.de>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_BASE_H_INSIDE__) && !defined (GIMP_BASE_COMPILATION)
+#error "Only <libgimpbase/gimpbase.h> can be included directly."
+#endif
+
+#ifndef __GIMP_LIMITS_H__
+#define __GIMP_LIMITS_H__
+
+G_BEGIN_DECLS
+
+
+/**
+ * SECTION: gimplimits
+ * @title: gimplimits
+ * @short_description: Boundaries of some GIMP data types and some
+ * global constants.
+ *
+ * Boundaries of some GIMP data types and some global constants.
+ **/
+
+
+/**
+ * GIMP_MIN_IMAGE_SIZE:
+ *
+ * The minimum width and height of a GIMP image in pixels.
+ **/
+#define GIMP_MIN_IMAGE_SIZE 1
+
+/**
+ * GIMP_MAX_IMAGE_SIZE:
+ *
+ * The maximum width and height of a GIMP image in pixels. This is a
+ * somewhat arbitrary value that can be used when an upper value for
+ * pixel sizes is needed; for example to give a spin button an upper
+ * limit.
+ **/
+#define GIMP_MAX_IMAGE_SIZE 524288 /* 2^19 */
+
+
+/**
+ * GIMP_MIN_RESOLUTION:
+ *
+ * The minimum resolution of a GIMP image in pixels per inch. This is
+ * a somewhat arbitrary value that can be used when a lower value for a
+ * resolution is needed. GIMP will not accept resolutions smaller than
+ * this value.
+ **/
+#define GIMP_MIN_RESOLUTION 5e-3 /* shouldn't display as 0.000 */
+
+/**
+ * GIMP_MAX_RESOLUTION:
+ *
+ * The maximum resolution of a GIMP image in pixels per inch. This is
+ * a somewhat arbitrary value that can be used to when an upper value
+ * for a resolution is needed. GIMP will not accept resolutions larger
+ * than this value.
+ **/
+#define GIMP_MAX_RESOLUTION 1048576.0
+
+
+/**
+ * GIMP_MAX_MEMSIZE:
+ *
+ * A large but arbitrary value that can be used when an upper limit
+ * for a memory size (in bytes) is needed. It is smaller than
+ * %G_MAXDOUBLE since the #GimpMemsizeEntry doesn't handle larger
+ * values.
+ **/
+#define GIMP_MAX_MEMSIZE ((guint64) 1 << 42) /* 4 terabyte;
+ * needs a 64bit variable
+ * and must be < G_MAXDOUBLE
+ */
+
+
+G_END_DECLS
+
+#endif /* __GIMP_LIMITS_H__ */
diff --git a/libgimpbase/gimpmemsize.c b/libgimpbase/gimpmemsize.c
new file mode 100644
index 0000000..4be14b6
--- /dev/null
+++ b/libgimpbase/gimpmemsize.c
@@ -0,0 +1,288 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+
+#include <glib-object.h>
+
+#include "gimpbasetypes.h"
+
+#include "gimpmemsize.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpmemsize
+ * @title: gimpmemsize
+ * @short_description: Functions to (de)serialize a given memory size.
+ *
+ * Functions to (de)serialize a given memory size.
+ **/
+
+
+static void memsize_to_string (const GValue *src_value,
+ GValue *dest_value);
+static void string_to_memsize (const GValue *src_value,
+ GValue *dest_value);
+
+
+GType
+gimp_memsize_get_type (void)
+{
+ static GType memsize_type = 0;
+
+ if (! memsize_type)
+ {
+ const GTypeInfo type_info = { 0, };
+
+ memsize_type = g_type_register_static (G_TYPE_UINT64, "GimpMemsize",
+ &type_info, 0);
+
+ g_value_register_transform_func (memsize_type, G_TYPE_STRING,
+ memsize_to_string);
+ g_value_register_transform_func (G_TYPE_STRING, memsize_type,
+ string_to_memsize);
+ }
+
+ return memsize_type;
+}
+
+/**
+ * gimp_memsize_serialize:
+ * @memsize: memory size in bytes
+ *
+ * Creates a string representation of a given memory size. This string
+ * can be parsed by gimp_memsize_deserialize() and can thus be used in
+ * config files. It should not be displayed to the user. If you need a
+ * nice human-readable string please use g_format_size().
+ *
+ * Return value: A newly allocated string representation of @memsize.
+ *
+ * Since: 2.2
+ **/
+gchar *
+gimp_memsize_serialize (guint64 memsize)
+{
+ if (memsize > (1 << 30) && memsize % (1 << 30) == 0)
+ return g_strdup_printf ("%" G_GUINT64_FORMAT "G", memsize >> 30);
+ else if (memsize > (1 << 20) && memsize % (1 << 20) == 0)
+ return g_strdup_printf ("%" G_GUINT64_FORMAT "M", memsize >> 20);
+ else if (memsize > (1 << 10) && memsize % (1 << 10) == 0)
+ return g_strdup_printf ("%" G_GUINT64_FORMAT "k", memsize >> 10);
+ else
+ return g_strdup_printf ("%" G_GUINT64_FORMAT, memsize);
+}
+
+/**
+ * gimp_memsize_deserialize:
+ * @string: a string as returned by gimp_memsize_serialize()
+ * @memsize: return location for memory size in bytes
+ *
+ * Parses a string representation of a memory size as returned by
+ * gimp_memsize_serialize().
+ *
+ * Return value: %TRUE if the @string was successfully parsed and
+ * @memsize has been set, %FALSE otherwise.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_memsize_deserialize (const gchar *string,
+ guint64 *memsize)
+{
+ gchar *end;
+ guint64 size;
+
+ g_return_val_if_fail (string != NULL, FALSE);
+ g_return_val_if_fail (memsize != NULL, FALSE);
+
+ size = g_ascii_strtoull (string, &end, 0);
+
+ if (size == G_MAXUINT64 && errno == ERANGE)
+ return FALSE;
+
+ if (end && *end)
+ {
+ guint shift;
+
+ switch (g_ascii_tolower (*end))
+ {
+ case 'b':
+ shift = 0;
+ break;
+ case 'k':
+ shift = 10;
+ break;
+ case 'm':
+ shift = 20;
+ break;
+ case 'g':
+ shift = 30;
+ break;
+ default:
+ return FALSE;
+ }
+
+ /* protect against overflow */
+ if (shift)
+ {
+ guint64 limit = G_MAXUINT64 >> shift;
+
+ if (size != (size & limit))
+ return FALSE;
+
+ size <<= shift;
+ }
+ }
+
+ *memsize = size;
+
+ return TRUE;
+}
+
+
+/**
+ * gimp_memsize_to_string:
+ * @memsize: A memory size in bytes.
+ *
+ * This function is deprecated! Use g_format_size() instead.
+ *
+ * Return value: A newly allocated human-readable, translated string.
+ **/
+gchar *
+gimp_memsize_to_string (guint64 memsize)
+{
+ return g_format_size (memsize);
+}
+
+
+static void
+memsize_to_string (const GValue *src_value,
+ GValue *dest_value)
+{
+ g_value_take_string (dest_value,
+ gimp_memsize_serialize (g_value_get_uint64 (src_value)));
+}
+
+static void
+string_to_memsize (const GValue *src_value,
+ GValue *dest_value)
+{
+ const gchar *str;
+ guint64 memsize;
+
+ str = g_value_get_string (src_value);
+
+ if (str && gimp_memsize_deserialize (str, &memsize))
+ {
+ g_value_set_uint64 (dest_value, memsize);
+ }
+ else
+ {
+ g_warning ("Can't convert string to GimpMemsize.");
+ }
+}
+
+
+/*
+ * GIMP_TYPE_PARAM_MEMSIZE
+ */
+
+static void gimp_param_memsize_class_init (GParamSpecClass *class);
+
+/**
+ * gimp_param_memsize_get_type:
+ *
+ * Reveals the object type
+ *
+ * Returns: the #GType for a memsize object
+ *
+ * Since: 2.4
+ **/
+GType
+gimp_param_memsize_get_type (void)
+{
+ static GType spec_type = 0;
+
+ if (! spec_type)
+ {
+ const GTypeInfo type_info =
+ {
+ sizeof (GParamSpecClass),
+ NULL, NULL,
+ (GClassInitFunc) gimp_param_memsize_class_init,
+ NULL, NULL,
+ sizeof (GParamSpecUInt64),
+ 0, NULL, NULL
+ };
+
+ spec_type = g_type_register_static (G_TYPE_PARAM_UINT64,
+ "GimpParamMemsize",
+ &type_info, 0);
+ }
+
+ return spec_type;
+}
+
+static void
+gimp_param_memsize_class_init (GParamSpecClass *class)
+{
+ class->value_type = GIMP_TYPE_MEMSIZE;
+}
+
+/**
+ * gimp_param_spec_memsize:
+ * @name: Canonical name of the param
+ * @nick: Nickname of the param
+ * @blurb: Brief description of param.
+ * @minimum: Smallest allowed value of the parameter.
+ * @maximum: Largest allowed value of the parameter.
+ * @default_value: Value to use if none is assigned.
+ * @flags: a combination of #GParamFlags
+ *
+ * Creates a param spec to hold a memory size value.
+ * See g_param_spec_internal() for more information.
+ *
+ * Returns: a newly allocated #GParamSpec instance
+ *
+ * Since: 2.4
+ **/
+GParamSpec *
+gimp_param_spec_memsize (const gchar *name,
+ const gchar *nick,
+ const gchar *blurb,
+ guint64 minimum,
+ guint64 maximum,
+ guint64 default_value,
+ GParamFlags flags)
+{
+ GParamSpecUInt64 *pspec;
+
+ pspec = g_param_spec_internal (GIMP_TYPE_PARAM_MEMSIZE,
+ name, nick, blurb, flags);
+
+ pspec->minimum = minimum;
+ pspec->maximum = maximum;
+ pspec->default_value = default_value;
+
+ return G_PARAM_SPEC (pspec);
+}
+
diff --git a/libgimpbase/gimpmemsize.h b/libgimpbase/gimpmemsize.h
new file mode 100644
index 0000000..f9f1201
--- /dev/null
+++ b/libgimpbase/gimpmemsize.h
@@ -0,0 +1,68 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_BASE_H_INSIDE__) && !defined (GIMP_BASE_COMPILATION)
+#error "Only <libgimpbase/gimpbase.h> can be included directly."
+#endif
+
+#ifndef __GIMP_MEMSIZE_H__
+#define __GIMP_MEMSIZE_H__
+
+G_BEGIN_DECLS
+
+
+/**
+ * GIMP_TYPE_MEMSIZE:
+ *
+ * #GIMP_TYPE_MEMSIZE is a #GType derived from #G_TYPE_UINT64.
+ **/
+
+#define GIMP_TYPE_MEMSIZE (gimp_memsize_get_type ())
+#define GIMP_VALUE_HOLDS_MEMSIZE(value) (G_TYPE_CHECK_VALUE_TYPE ((value), GIMP_TYPE_MEMSIZE))
+
+GType gimp_memsize_get_type (void) G_GNUC_CONST;
+
+gchar * gimp_memsize_serialize (guint64 memsize) G_GNUC_MALLOC;
+gboolean gimp_memsize_deserialize (const gchar *string,
+ guint64 *memsize);
+
+GIMP_DEPRECATED_FOR(g_format_size)
+gchar * gimp_memsize_to_string (guint64 memsize) G_GNUC_MALLOC;
+
+
+/*
+ * GIMP_TYPE_PARAM_MEMSIZE
+ */
+
+#define GIMP_TYPE_PARAM_MEMSIZE (gimp_param_memsize_get_type ())
+#define GIMP_IS_PARAM_SPEC_MEMSIZE(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_MEMSIZE))
+
+GType gimp_param_memsize_get_type (void) G_GNUC_CONST;
+
+GParamSpec * gimp_param_spec_memsize (const gchar *name,
+ const gchar *nick,
+ const gchar *blurb,
+ guint64 minimum,
+ guint64 maximum,
+ guint64 default_value,
+ GParamFlags flags);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_MEMSIZE_H__ */
diff --git a/libgimpbase/gimpmetadata.c b/libgimpbase/gimpmetadata.c
new file mode 100644
index 0000000..8bf3ed8
--- /dev/null
+++ b/libgimpbase/gimpmetadata.c
@@ -0,0 +1,1796 @@
+/* LIBGIMPBASE - The GIMP Basic Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpmetadata.c
+ * Copyright (C) 2013 Hartmut Kuhse <hartmutkuhse@src.gnome.org>
+ * Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <gio/gio.h>
+#include <gexiv2/gexiv2.h>
+
+#include "libgimpmath/gimpmath.h"
+
+#include "gimpbasetypes.h"
+
+#include "gimplimits.h"
+#include "gimpmetadata.h"
+#include "gimpunit.h"
+
+#include "libgimp/libgimp-intl.h"
+
+typedef struct _GimpMetadataClass GimpMetadataClass;
+typedef struct _GimpMetadataPrivate GimpMetadataPrivate;
+
+struct _GimpMetadata
+{
+ GExiv2Metadata parent_instance;
+};
+
+struct _GimpMetadataPrivate
+{
+ /* dummy entry to avoid a critical warning due to size 0 */
+ gpointer _gimp_reserved1;
+};
+
+struct _GimpMetadataClass
+{
+ GExiv2MetadataClass parent_class;
+
+ /* Padding for future expansion */
+ void (*_gimp_reserved1) (void);
+ void (*_gimp_reserved2) (void);
+ void (*_gimp_reserved3) (void);
+ void (*_gimp_reserved4) (void);
+ void (*_gimp_reserved5) (void);
+ void (*_gimp_reserved6) (void);
+ void (*_gimp_reserved7) (void);
+ void (*_gimp_reserved8) (void);
+};
+
+/**
+ * SECTION: gimpmetadata
+ * @title: GimpMetadata
+ * @short_description: Basic functions for handling #GimpMetadata objects.
+ * @see_also: gimp_image_metadata_load_prepare(),
+ * gimp_image_metadata_load_finish(),
+ * gimp_image_metadata_save_prepare(),
+ * gimp_image_metadata_save_finish().
+ *
+ * Basic functions for handling #GimpMetadata objects.
+ **/
+
+
+#define GIMP_METADATA_ERROR gimp_metadata_error_quark ()
+
+static GQuark gimp_metadata_error_quark (void);
+static void gimp_metadata_copy_tag (GExiv2Metadata *src,
+ GExiv2Metadata *dest,
+ const gchar *tag);
+static void gimp_metadata_copy_tags (GExiv2Metadata *src,
+ GExiv2Metadata *dest,
+ const gchar **tags);
+static void gimp_metadata_add (GimpMetadata *src,
+ GimpMetadata *dest);
+
+
+static const gchar *tiff_tags[] =
+{
+ "Xmp.tiff",
+ "Exif.Image.ImageWidth",
+ "Exif.Image.ImageLength",
+ "Exif.Image.BitsPerSample",
+ "Exif.Image.Compression",
+ "Exif.Image.PhotometricInterpretation",
+ "Exif.Image.FillOrder",
+ "Exif.Image.SamplesPerPixel",
+ "Exif.Image.StripOffsets",
+ "Exif.Image.RowsPerStrip",
+ "Exif.Image.StripByteCounts",
+ "Exif.Image.PlanarConfiguration"
+};
+
+static const gchar *jpeg_tags[] =
+{
+ "Exif.Image.JPEGProc",
+ "Exif.Image.JPEGInterchangeFormat",
+ "Exif.Image.JPEGInterchangeFormatLength",
+ "Exif.Image.JPEGRestartInterval",
+ "Exif.Image.JPEGLosslessPredictors",
+ "Exif.Image.JPEGPointTransforms",
+ "Exif.Image.JPEGQTables",
+ "Exif.Image.JPEGDCTables",
+ "Exif.Image.JPEGACTables"
+};
+
+static const gchar *unsupported_tags[] =
+{
+ "Exif.Image.SubIFDs",
+ "Exif.Image.ClipPath",
+ "Exif.Image.XClipPathUnits",
+ "Exif.Image.YClipPathUnits",
+ "Exif.Image.XPTitle",
+ "Exif.Image.XPComment",
+ "Exif.Image.XPAuthor",
+ "Exif.Image.XPKeywords",
+ "Exif.Image.XPSubject",
+ "Exif.Image.DNGVersion",
+ "Exif.Image.DNGBackwardVersion",
+ "Exif.Iop",
+ /* FIXME Even though adding the tags below fixes the issue it's not very flexible.
+ It might be better in the long run if there was a way for a user to configure which
+ tags to block or a way for us to detect problems with tags before writing them. */
+ /* Issues #1367, #2253. Offending tag is PreviewOffset but the other Preview tags
+ (PreviewResolution, PreviewLength, PreviewImageBorders) also make no sense because
+ we are not including a Pentax specific preview image. */
+ "Exif.Pentax.Preview",
+ "Exif.PentaxDng.Preview",
+ /* Never save the complete brand specific MakerNote data. We load and
+ * should only save the specific brand tags inside the MakerNote.
+ * Sometimes the MakerNote is invalid or exiv2 doesn't know how to parse
+ * it. In that case we still get the (invalid) MakerNote, but not the
+ * individual tags or just a subset of them.
+ * If there are recognized brand specific tags, exiv2 will create the
+ * required MakerNote itself (which in can still be invalid but that's an
+ * exiv2 issue not ours). */
+ "Exif.Photo.MakerNote",
+ "Exif.MakerNote.ByteOrder",
+ "Exif.MakerNote.Offset",
+};
+
+static const guint8 minimal_exif[] =
+{
+ 0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01,
+ 0x01, 0x01, 0x00, 0x5a, 0x00, 0x5a, 0x00, 0x00, 0xff, 0xe1
+};
+
+static const guint8 wilber_jpg[] =
+{
+ 0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01,
+ 0x01, 0x01, 0x00, 0x5a, 0x00, 0x5a, 0x00, 0x00, 0xff, 0xdb, 0x00, 0x43,
+ 0x00, 0x50, 0x37, 0x3c, 0x46, 0x3c, 0x32, 0x50, 0x46, 0x41, 0x46, 0x5a,
+ 0x55, 0x50, 0x5f, 0x78, 0xc8, 0x82, 0x78, 0x6e, 0x6e, 0x78, 0xf5, 0xaf,
+ 0xb9, 0x91, 0xc8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdb, 0x00, 0x43, 0x01, 0x55, 0x5a,
+ 0x5a, 0x78, 0x69, 0x78, 0xeb, 0x82, 0x82, 0xeb, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xc0, 0x00, 0x11, 0x08, 0x00, 0x10, 0x00, 0x10, 0x03,
+ 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x00,
+ 0x16, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x02, 0xff, 0xc4, 0x00,
+ 0x1e, 0x10, 0x00, 0x01, 0x05, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x03, 0x11, 0x31,
+ 0x04, 0x12, 0x51, 0x61, 0x71, 0xff, 0xc4, 0x00, 0x14, 0x01, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xff, 0xc4, 0x00, 0x14, 0x11, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11,
+ 0x00, 0x3f, 0x00, 0x18, 0xa0, 0x0e, 0x6d, 0xbc, 0xf5, 0xca, 0xf7, 0x78,
+ 0xb6, 0xfe, 0x3b, 0x23, 0xb2, 0x1d, 0x64, 0x68, 0xf0, 0x8a, 0x39, 0x4b,
+ 0x74, 0x9c, 0xa5, 0x5f, 0x35, 0x8a, 0xb2, 0x7e, 0xa0, 0xff, 0xd9, 0x00
+};
+
+static const guint wilber_jpg_len = G_N_ELEMENTS (wilber_jpg);
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpMetadata, gimp_metadata, GEXIV2_TYPE_METADATA)
+
+
+static void
+gimp_metadata_class_init (GimpMetadataClass *klass)
+{
+ if (! gexiv2_metadata_register_xmp_namespace ("http://ns.adobe.com/DICOM/",
+ "DICOM"))
+ {
+ g_printerr ("Failed to register XMP namespace 'DICOM'\n");
+ }
+
+ if (! gexiv2_metadata_register_xmp_namespace ("http://darktable.sf.net/",
+ "darktable"))
+ {
+ g_printerr ("Failed to register XMP namespace 'darktable'\n");
+ }
+
+ /* Usage example Xmp.GIMP.tagname */
+ if (! gexiv2_metadata_register_xmp_namespace ("http://www.gimp.org/xmp/",
+ "GIMP"))
+ {
+ g_printerr ("Failed to register XMP namespace 'GIMP'\n");
+ }
+}
+
+static void
+gimp_metadata_init (GimpMetadata *metadata)
+{
+}
+
+/**
+ * gimp_metadata_get_guid:
+ *
+ * Generate Version 4 UUID/GUID.
+ *
+ * Return value: The new GUID/UUID string.
+ *
+ * Since: 2.10
+ */
+gchar *
+gimp_metadata_get_guid (void)
+{
+ GRand *rand;
+ gint bake;
+ gchar *GUID;
+ const gchar *szHex = "0123456789abcdef-";
+
+ rand = g_rand_new ();
+
+#define DALLOC 36
+
+ GUID = g_malloc0 (DALLOC + 1);
+
+ for (bake = 0; bake < DALLOC; bake++)
+ {
+ gint r = g_rand_int (rand) % 16;
+ gchar c = ' ';
+
+ switch (bake)
+ {
+ default:
+ c = szHex [r];
+ break;
+
+ case 19 :
+ c = szHex [(r & 0x03) | 0x08];
+ break;
+
+ case 8:
+ case 13:
+ case 18:
+ case 23:
+ c = '-';
+ break;
+
+ case 14:
+ c = '4';
+ break;
+ }
+
+ GUID[bake] = (bake < DALLOC) ? c : 0x00;
+ }
+
+ g_rand_free (rand);
+
+ return GUID;
+}
+
+/**
+ * gimp_metadata_add_history:
+ *
+ * Add XMP mm History data to file metadata.
+ *
+ * Since: 2.10
+ */
+void
+gimp_metadata_add_xmp_history (GimpMetadata *metadata,
+ gchar *state_status)
+{
+ time_t now;
+ struct tm *now_tm;
+ gchar *tmp;
+ char timestr[256];
+ char tzstr[7];
+ gchar iid_data[256];
+ gchar strdata[1024];
+ gchar tagstr[1024];
+ gchar *uuid;
+ gchar *did;
+ gchar *odid;
+ gint id_count;
+ gint found;
+ gint lastfound;
+ gint count;
+ int ii;
+
+ static const gchar *tags[] =
+ {
+ "Xmp.xmpMM.InstanceID",
+ "Xmp.xmpMM.DocumentID",
+ "Xmp.xmpMM.OriginalDocumentID",
+ "Xmp.xmpMM.History"
+ };
+
+ static const gchar *history_tags[] =
+ {
+ "/stEvt:action",
+ "/stEvt:instanceID",
+ "/stEvt:when",
+ "/stEvt:softwareAgent",
+ "/stEvt:changed"
+ };
+
+ g_return_if_fail (GIMP_IS_METADATA (metadata));
+
+ /* Update new Instance ID */
+ uuid = gimp_metadata_get_guid ();
+
+ strcpy (iid_data, "xmp.iid:");
+ strcat (iid_data, uuid);
+
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
+ tags[0], iid_data);
+ g_free (uuid);
+
+ /* Update new Document ID if none found */
+ did = gexiv2_metadata_get_tag_interpreted_string (GEXIV2_METADATA (metadata),
+ tags[1]);
+ if (! did || ! strlen (did))
+ {
+ gchar did_data[256];
+
+ uuid = gimp_metadata_get_guid ();
+
+ strcpy (did_data, "gimp:docid:gimp:");
+ strcat (did_data, uuid);
+
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
+ tags[1], did_data);
+ g_free (uuid);
+ }
+
+ /* Update new Original Document ID if none found */
+ odid = gexiv2_metadata_get_tag_interpreted_string (GEXIV2_METADATA (metadata),
+ tags[2]);
+ if (! odid || ! strlen (odid))
+ {
+ gchar did_data[256];
+ gchar *uuid = gimp_metadata_get_guid ();
+
+ strcpy (did_data, "xmp.did:");
+ strcat (did_data, uuid);
+
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
+ tags[2], did_data);
+ g_free (uuid);
+ }
+
+ /* Handle Xmp.xmpMM.History */
+
+ gexiv2_metadata_set_xmp_tag_struct (GEXIV2_METADATA (metadata),
+ tags[3],
+ GEXIV2_STRUCTURE_XA_SEQ);
+
+ /* Find current number of entries for Xmp.xmpMM.History */
+ found = 0;
+ for (count = 1; count < 65536; count++)
+ {
+ lastfound = 0;
+ for (ii = 0; ii < 5; ii++)
+ {
+ g_snprintf (tagstr, sizeof (tagstr), "%s[%d]%s",
+ tags[3], count, history_tags[ii]);
+
+ if (gexiv2_metadata_has_tag (GEXIV2_METADATA (metadata),
+ tagstr))
+ {
+ lastfound = 1;
+ }
+ }
+
+ if (lastfound == 0)
+ break;
+
+ found++;
+ }
+
+ id_count = found + 1;
+
+ memset (tagstr, 0, sizeof (tagstr));
+ memset (strdata, 0, sizeof (strdata));
+
+ g_snprintf (tagstr, sizeof (tagstr), "%s[%d]%s",
+ tags[3], id_count, history_tags[0]);
+
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
+ tagstr, "saved");
+
+ memset (tagstr, 0, sizeof (tagstr));
+ memset (strdata, 0, sizeof (strdata));
+
+ uuid = gimp_metadata_get_guid ();
+
+ g_snprintf (tagstr, sizeof (tagstr), "%s[%d]%s",
+ tags[3], id_count, history_tags[1]);
+ g_snprintf (strdata, sizeof (strdata), "xmp.iid:%s",
+ uuid);
+
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
+ tagstr, strdata);
+ g_free(uuid);
+
+ memset (tagstr, 0, sizeof (tagstr));
+ memset (strdata, 0, sizeof (strdata));
+
+ g_snprintf (tagstr, sizeof (tagstr), "%s[%d]%s",
+ tags[3], id_count, history_tags[2]);
+
+ /* get local time */
+ time (&now);
+ now_tm = localtime (&now);
+
+ /* get timezone and fix format */
+ strftime (tzstr, 7, "%z", now_tm);
+ tzstr[6] = '\0';
+ tzstr[5] = tzstr[4];
+ tzstr[4] = tzstr[3];
+ tzstr[3] = ':';
+
+ /* get current time and timezone string */
+ strftime (timestr, 256, "%Y-%m-%dT%H:%M:%S", now_tm);
+ tmp = g_strdup_printf ("%s%s", timestr, tzstr);
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
+ tagstr, tmp);
+ g_free (tmp);
+
+ memset (tagstr, 0, sizeof (tagstr));
+ memset (strdata, 0, sizeof (strdata));
+
+ g_snprintf (tagstr, sizeof (tagstr), "%s[%d]%s",
+ tags[3], id_count, history_tags[3]);
+
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
+ tagstr,
+ "Gimp 2.10 "
+#if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
+ "(Windows)");
+#elif defined(__linux__)
+ "(Linux)");
+#elif defined(__APPLE__) && defined(__MACH__)
+ "(Mac OS)");
+#elif defined(unix) || defined(__unix__) || defined(__unix)
+ "(Unix)");
+#else
+ "(Unknown)");
+#endif
+
+ memset (tagstr, 0, sizeof (tagstr));
+ memset (strdata, 0, sizeof (strdata));
+
+ g_snprintf (tagstr, sizeof (tagstr), "%s[%d]%s",
+ tags[3], id_count, history_tags[4]);
+
+ strcpy (strdata, "/");
+ strcat (strdata, state_status);
+
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
+ tagstr, strdata);
+}
+
+/**
+ * gimp_metadata_new:
+ *
+ * Creates a new #GimpMetadata instance.
+ *
+ * Return value: The new #GimpMetadata.
+ *
+ * Since: 2.10
+ */
+GimpMetadata *
+gimp_metadata_new (void)
+{
+ GimpMetadata *metadata = NULL;
+
+ if (gexiv2_initialize ())
+ {
+ metadata = g_object_new (GIMP_TYPE_METADATA, NULL);
+
+ if (! gexiv2_metadata_open_buf (GEXIV2_METADATA (metadata),
+ wilber_jpg, wilber_jpg_len,
+ NULL))
+ {
+ g_object_unref (metadata);
+
+ return NULL;
+ }
+ }
+
+ return metadata;
+}
+
+/**
+ * gimp_metadata_duplicate:
+ * @metadata: The object to duplicate, or %NULL.
+ *
+ * Duplicates a #GimpMetadata instance.
+ *
+ * Return value: The new #GimpMetadata, or %NULL if @metadata is %NULL.
+ *
+ * Since: 2.10
+ */
+GimpMetadata *
+gimp_metadata_duplicate (GimpMetadata *metadata)
+{
+ GimpMetadata *new_metadata = NULL;
+
+ g_return_val_if_fail (metadata == NULL || GIMP_IS_METADATA (metadata), NULL);
+
+ if (metadata)
+ {
+ gchar *xml;
+
+ xml = gimp_metadata_serialize (metadata);
+ new_metadata = gimp_metadata_deserialize (xml);
+ g_free (xml);
+ }
+
+ return new_metadata;
+}
+
+typedef struct
+{
+ gchar name[1024];
+ gboolean base64;
+ gboolean excessive_message_shown;
+ GimpMetadata *metadata;
+} GimpMetadataParseData;
+
+static const gchar*
+gimp_metadata_attribute_name_to_value (const gchar **attribute_names,
+ const gchar **attribute_values,
+ const gchar *name)
+{
+ while (*attribute_names)
+ {
+ if (! strcmp (*attribute_names, name))
+ {
+ return *attribute_values;
+ }
+
+ attribute_names++;
+ attribute_values++;
+ }
+
+ return NULL;
+}
+
+static void
+gimp_metadata_deserialize_start_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ GimpMetadataParseData *parse_data = user_data;
+
+ if (! strcmp (element_name, "tag"))
+ {
+ const gchar *name;
+ const gchar *encoding;
+
+ name = gimp_metadata_attribute_name_to_value (attribute_names,
+ attribute_values,
+ "name");
+ encoding = gimp_metadata_attribute_name_to_value (attribute_names,
+ attribute_values,
+ "encoding");
+
+ if (! name)
+ {
+ g_set_error (error, GIMP_METADATA_ERROR, 1001,
+ "Element 'tag' does not contain required attribute 'name'.");
+ return;
+ }
+
+ strncpy (parse_data->name, name, sizeof (parse_data->name));
+ parse_data->name[sizeof (parse_data->name) - 1] = 0;
+
+ parse_data->base64 = (encoding && ! strcmp (encoding, "base64"));
+ }
+}
+
+static void
+gimp_metadata_deserialize_end_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+}
+
+static void
+gimp_metadata_deserialize_text (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
+{
+ GimpMetadataParseData *parse_data = user_data;
+ const gchar *current_element;
+
+ current_element = g_markup_parse_context_get_element (context);
+
+ if (! g_strcmp0 (current_element, "tag"))
+ {
+ gchar *value = g_strndup (text, text_len);
+
+ if (parse_data->base64)
+ {
+ guchar *decoded;
+ gsize len;
+
+ decoded = g_base64_decode (value, &len);
+
+ if (decoded[len - 1] == '\0')
+ {
+ g_free (value);
+ value = (gchar *) decoded;
+ }
+ else
+ {
+ g_clear_pointer (&value, g_free);
+ g_clear_pointer (&decoded, g_free);
+ }
+ }
+
+ if (value)
+ {
+ GExiv2Metadata *g2_metadata = GEXIV2_METADATA (parse_data->metadata);
+ gchar **values;
+
+ values = gexiv2_metadata_get_tag_multiple (g2_metadata,
+ parse_data->name);
+
+ if (values)
+ {
+ guint length = g_strv_length (values);
+
+ if (length > 1000 &&
+ ! g_strcmp0 (parse_data->name, "Xmp.photoshop.DocumentAncestors"))
+ {
+ /* Issue #8025, see also #7464 Some XCF images can have huge
+ * amounts of this tag, apparently due to a bug in PhotoShop.
+ * This makes deserializing it in the way we currently do
+ * too slow. Until we can change this let's ignore everything
+ * but the first 1000 values when serializing. */
+
+ if (! parse_data->excessive_message_shown)
+ {
+ g_message ("Excessive number of Xmp.photoshop.DocumentAncestors tags found. "
+ "Only keeping the first 1000 values.");
+ parse_data->excessive_message_shown = TRUE;
+ }
+ }
+ else
+ {
+ values = g_renew (gchar *, values, length + 2);
+ values[length] = value;
+ values[length + 1] = NULL;
+
+ gexiv2_metadata_set_tag_multiple (g2_metadata,
+ parse_data->name,
+ (const gchar **) values);
+ }
+ g_strfreev (values);
+ }
+ else
+ {
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g2_metadata),
+ parse_data->name,
+ value);
+ g_free (value);
+ }
+ }
+ }
+}
+
+static void
+gimp_metadata_deserialize_error (GMarkupParseContext *context,
+ GError *error,
+ gpointer user_data)
+{
+ g_printerr ("Metadata parse error: %s\n", error->message);
+}
+
+/**
+ * gimp_metadata_deserialize:
+ * @metadata_xml: A string of serialized metadata XML.
+ *
+ * Deserializes a string of XML that has been created by
+ * gimp_metadata_serialize().
+ *
+ * Return value: The new #GimpMetadata.
+ *
+ * Since: 2.10
+ */
+GimpMetadata *
+gimp_metadata_deserialize (const gchar *metadata_xml)
+{
+ GimpMetadata *metadata;
+ GMarkupParser markup_parser;
+ GimpMetadataParseData parse_data;
+ GMarkupParseContext *context;
+
+ g_return_val_if_fail (metadata_xml != NULL, NULL);
+
+ metadata = gimp_metadata_new ();
+
+ parse_data.metadata = metadata;
+ parse_data.excessive_message_shown = FALSE;
+
+ markup_parser.start_element = gimp_metadata_deserialize_start_element;
+ markup_parser.end_element = gimp_metadata_deserialize_end_element;
+ markup_parser.text = gimp_metadata_deserialize_text;
+ markup_parser.passthrough = NULL;
+ markup_parser.error = gimp_metadata_deserialize_error;
+
+ context = g_markup_parse_context_new (&markup_parser, 0, &parse_data, NULL);
+
+ g_markup_parse_context_parse (context,
+ metadata_xml, strlen (metadata_xml),
+ NULL);
+
+ g_markup_parse_context_unref (context);
+
+ return metadata;
+}
+
+static gchar *
+gimp_metadata_escape (const gchar *name,
+ const gchar *value,
+ gboolean *base64)
+{
+ if (! g_utf8_validate (value, -1, NULL))
+ {
+ gchar *encoded;
+
+ encoded = g_base64_encode ((const guchar *) value, strlen (value) + 1);
+
+ g_printerr ("Invalid UTF-8 in metadata value %s, encoding as base64: %s\n",
+ name, encoded);
+
+ *base64 = TRUE;
+
+ return encoded;
+ }
+
+ *base64 = FALSE;
+
+ return g_markup_escape_text (value, -1);
+}
+
+static void
+gimp_metadata_append_tag (GString *string,
+ const gchar *name,
+ gchar *value,
+ gboolean base64)
+{
+ if (value)
+ {
+ if (base64)
+ {
+ g_string_append_printf (string, " <tag name=\"%s\" encoding=\"base64\">%s</tag>\n",
+ name, value);
+ }
+ else
+ {
+ g_string_append_printf (string, " <tag name=\"%s\">%s</tag>\n",
+ name, value);
+ }
+
+ g_free (value);
+ }
+}
+
+/**
+ * gimp_metadata_serialize:
+ * @metadata: A #GimpMetadata instance.
+ *
+ * Serializes @metadata into an XML string that can later be deserialized
+ * using gimp_metadata_deserialize().
+ *
+ * Return value: The serialized XML string.
+ *
+ * Since: 2.10
+ */
+gchar *
+gimp_metadata_serialize (GimpMetadata *metadata)
+{
+ GString *string;
+ gchar **exif_data = NULL;
+ gchar **iptc_data = NULL;
+ gchar **xmp_data = NULL;
+ gchar *value;
+ gchar *escaped;
+ gboolean base64;
+ gint i;
+
+ g_return_val_if_fail (GIMP_IS_METADATA (metadata), NULL);
+
+ string = g_string_new (NULL);
+
+ g_string_append (string, "<?xml version='1.0' encoding='UTF-8'?>\n");
+ g_string_append (string, "<metadata>\n");
+
+ exif_data = gexiv2_metadata_get_exif_tags (GEXIV2_METADATA (metadata));
+
+ if (exif_data)
+ {
+ for (i = 0; exif_data[i] != NULL; i++)
+ {
+ value = gexiv2_metadata_get_tag_string (GEXIV2_METADATA (metadata),
+ exif_data[i]);
+ escaped = gimp_metadata_escape (exif_data[i], value, &base64);
+ g_free (value);
+
+ gimp_metadata_append_tag (string, exif_data[i], escaped, base64);
+ }
+
+ g_strfreev (exif_data);
+ }
+
+ xmp_data = gexiv2_metadata_get_xmp_tags (GEXIV2_METADATA (metadata));
+
+ if (xmp_data)
+ {
+ for (i = 0; xmp_data[i] != NULL; i++)
+ {
+ /* XmpText is always a single value, but structures like
+ * XmpBag and XmpSeq can have multiple values that need to be
+ * treated separately or else saving will do things wrong. */
+ if (! g_strcmp0 (gexiv2_metadata_get_tag_type (xmp_data[i]), "XmpText"))
+ {
+ value = gexiv2_metadata_get_tag_string (GEXIV2_METADATA (metadata),
+ xmp_data[i]);
+ escaped = gimp_metadata_escape (xmp_data[i], value, &base64);
+ g_free (value);
+
+ gimp_metadata_append_tag (string, xmp_data[i], escaped, base64);
+ }
+ else
+ {
+ gchar **values;
+
+ values = gexiv2_metadata_get_tag_multiple (GEXIV2_METADATA (metadata),
+ xmp_data[i]);
+
+ if (values)
+ {
+ gint vi;
+ gint cnt = 0;
+
+ if (! g_strcmp0 (xmp_data[i], "Xmp.photoshop.DocumentAncestors"))
+ {
+ /* Issue #7464 Some images can have huge amounts of this
+ * tag (more than 100000 in certain cases), apparently
+ * due to a bug in PhotoShop. This makes deserializing it
+ * in the way we currently do too slow. Until we can
+ * change this let's remove everything but the first 1000
+ * values when serializing. */
+ cnt = g_strv_length (values);
+
+ if (cnt > 1000)
+ {
+ g_message ("Excessive number of Xmp.photoshop.DocumentAncestors tags found: %d. "
+ "Only keeping the first 1000 values.", cnt);
+ }
+ }
+
+ for (vi = 0; values[vi] != NULL && (cnt <= 1000 || vi < 1000); vi++)
+ {
+ escaped = gimp_metadata_escape (xmp_data[i], values[vi], &base64);
+ gimp_metadata_append_tag (string, xmp_data[i], escaped, base64);
+ }
+
+ g_strfreev (values);
+ }
+ }
+ }
+ g_strfreev (xmp_data);
+ }
+
+ iptc_data = gexiv2_metadata_get_iptc_tags (GEXIV2_METADATA (metadata));
+
+ if (iptc_data)
+ {
+ gchar **iptc_tags = iptc_data;
+ gchar *last_tag = NULL;
+
+ while (*iptc_tags)
+ {
+ gchar **values;
+
+ if (last_tag && ! strcmp (*iptc_tags, last_tag))
+ {
+ iptc_tags++;
+ continue;
+ }
+ last_tag = *iptc_tags;
+
+ values = gexiv2_metadata_get_tag_multiple (GEXIV2_METADATA (metadata),
+ *iptc_tags);
+
+ if (values)
+ {
+ for (i = 0; values[i] != NULL; i++)
+ {
+ escaped = gimp_metadata_escape (*iptc_tags, values[i], &base64);
+ gimp_metadata_append_tag (string, *iptc_tags, escaped, base64);
+ }
+
+ g_strfreev (values);
+ }
+
+ iptc_tags++;
+ }
+
+ g_strfreev (iptc_data);
+ }
+
+ g_string_append (string, "</metadata>\n");
+
+ return g_string_free (string, FALSE);
+}
+
+/**
+ * gimp_metadata_load_from_file:
+ * @file: The #GFile to load the metadata from
+ * @error: Return location for error message
+ *
+ * Loads #GimpMetadata from @file.
+ *
+ * Return value: The loaded #GimpMetadata.
+ *
+ * Since: 2.10
+ */
+GimpMetadata *
+gimp_metadata_load_from_file (GFile *file,
+ GError **error)
+{
+ GimpMetadata *meta = NULL;
+ gchar *path;
+ gchar *filename;
+
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ path = g_file_get_path (file);
+
+ if (! path)
+ {
+ g_set_error (error, GIMP_METADATA_ERROR, 0,
+ _("Can load metadata only from local files"));
+ return NULL;
+ }
+
+ filename = g_strdup (path);
+
+ g_free (path);
+
+ if (gexiv2_initialize ())
+ {
+ meta = g_object_new (GIMP_TYPE_METADATA, NULL);
+
+ if (! gexiv2_metadata_open_path (GEXIV2_METADATA (meta), filename, error))
+ {
+ g_object_unref (meta);
+ g_free (filename);
+
+ return NULL;
+ }
+ }
+
+ g_free (filename);
+
+ return meta;
+}
+
+/**
+ * gimp_metadata_save_to_file:
+ * @metadata: A #GimpMetadata instance.
+ * @file: The file to save the metadata to
+ * @error: Return location for error message
+ *
+ * Saves @metadata to @file.
+ *
+ * Return value: %TRUE on success, %FALSE otherwise.
+ *
+ * Since: 2.10
+ */
+gboolean
+gimp_metadata_save_to_file (GimpMetadata *metadata,
+ GFile *file,
+ GError **error)
+{
+ gchar *path;
+ gchar *filename;
+ gboolean success;
+
+ g_return_val_if_fail (GIMP_IS_METADATA (metadata), FALSE);
+ g_return_val_if_fail (G_IS_FILE (file), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ path = g_file_get_path (file);
+
+ if (! path)
+ {
+ g_set_error (error, GIMP_METADATA_ERROR, 0,
+ _("Can save metadata only to local files"));
+ return FALSE;
+ }
+
+ filename = g_strdup (path);
+
+ g_free (path);
+
+ success = gexiv2_metadata_save_file (GEXIV2_METADATA (metadata),
+ filename, error);
+
+ g_free (filename);
+
+ return success;
+}
+
+/**
+ * gimp_metadata_set_from_exif:
+ * @metadata: A #GimpMetadata instance.
+ * @exif_data: The blob of Exif data to set
+ * @exif_data_length: Length of @exif_data, in bytes
+ * @error: Return location for error message
+ *
+ * Sets the tags from a piece of Exif data on @metadata.
+ *
+ * Return value: %TRUE on success, %FALSE otherwise.
+ *
+ * Since: 2.10
+ */
+gboolean
+gimp_metadata_set_from_exif (GimpMetadata *metadata,
+ const guchar *exif_data,
+ gint exif_data_length,
+ GError **error)
+{
+
+ GByteArray *exif_bytes;
+ GimpMetadata *exif_metadata;
+ guint8 data_size[2] = { 0, };
+ const guint8 eoi[2] = { 0xff, 0xd9 };
+
+ g_return_val_if_fail (GIMP_IS_METADATA (metadata), FALSE);
+ g_return_val_if_fail (exif_data != NULL || exif_data_length == 0, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ if (exif_data_length < 0 || exif_data_length + 2 >= 65536)
+ {
+ g_set_error (error, GIMP_METADATA_ERROR, 0,
+ _("Invalid Exif data size."));
+ return FALSE;
+ }
+
+ data_size[0] = ((exif_data_length + 2) & 0xFF00) >> 8;
+ data_size[1] = ((exif_data_length + 2) & 0x00FF);
+
+ exif_bytes = g_byte_array_new ();
+ exif_bytes = g_byte_array_append (exif_bytes,
+ minimal_exif, G_N_ELEMENTS (minimal_exif));
+ exif_bytes = g_byte_array_append (exif_bytes,
+ data_size, 2);
+ exif_bytes = g_byte_array_append (exif_bytes,
+ (guint8 *) exif_data, exif_data_length);
+ exif_bytes = g_byte_array_append (exif_bytes, eoi, 2);
+
+ exif_metadata = gimp_metadata_new ();
+
+ if (! gexiv2_metadata_open_buf (GEXIV2_METADATA (exif_metadata),
+ exif_bytes->data, exif_bytes->len, error))
+ {
+ g_object_unref (exif_metadata);
+ g_byte_array_free (exif_bytes, TRUE);
+ return FALSE;
+ }
+
+ if (! gexiv2_metadata_has_exif (GEXIV2_METADATA (exif_metadata)))
+ {
+ g_set_error (error, GIMP_METADATA_ERROR, 0,
+ _("Parsing Exif data failed."));
+ g_object_unref (exif_metadata);
+ g_byte_array_free (exif_bytes, TRUE);
+ return FALSE;
+ }
+
+ gimp_metadata_add (exif_metadata, metadata);
+ g_object_unref (exif_metadata);
+ g_byte_array_free (exif_bytes, TRUE);
+
+ return TRUE;
+}
+
+/**
+ * gimp_metadata_set_from_iptc:
+ * @metadata: A #GimpMetadata instance.
+ * @iptc_data: The blob of Ipc data to set
+ * @iptc_data_length:Length of @iptc_data, in bytes
+ * @error: Return location for error message
+ *
+ * Sets the tags from a piece of IPTC data on @metadata.
+ *
+ * Return value: %TRUE on success, %FALSE otherwise.
+ *
+ * Since: 2.10
+ */
+gboolean
+gimp_metadata_set_from_iptc (GimpMetadata *metadata,
+ const guchar *iptc_data,
+ gint iptc_data_length,
+ GError **error)
+{
+ GimpMetadata *iptc_metadata;
+
+ g_return_val_if_fail (GIMP_IS_METADATA (metadata), FALSE);
+ g_return_val_if_fail (iptc_data != NULL || iptc_data_length == 0, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ iptc_metadata = gimp_metadata_new ();
+
+ if (! gexiv2_metadata_open_buf (GEXIV2_METADATA (iptc_metadata),
+ iptc_data, iptc_data_length, error))
+ {
+ g_object_unref (iptc_metadata);
+ return FALSE;
+ }
+
+ if (! gexiv2_metadata_has_iptc (GEXIV2_METADATA (iptc_metadata)))
+ {
+ g_set_error (error, GIMP_METADATA_ERROR, 0,
+ _("Parsing IPTC data failed."));
+ g_object_unref (iptc_metadata);
+ return FALSE;
+ }
+
+ gimp_metadata_add (iptc_metadata, metadata);
+ g_object_unref (iptc_metadata);
+
+ return TRUE;
+}
+
+/**
+ * gimp_metadata_set_from_xmp:
+ * @metadata: A #GimpMetadata instance.
+ * @xmp_data: The blob of Exif data to set
+ * @xmp_data_length: Length of @exif_data, in bytes
+ * @error: Return location for error message
+ *
+ * Sets the tags from a piece of XMP data on @metadata.
+ *
+ * Return value: %TRUE on success, %FALSE otherwise.
+ *
+ * Since: 2.10
+ */
+gboolean
+gimp_metadata_set_from_xmp (GimpMetadata *metadata,
+ const guchar *xmp_data,
+ gint xmp_data_length,
+ GError **error)
+{
+ GimpMetadata *xmp_metadata;
+
+ g_return_val_if_fail (GIMP_IS_METADATA (metadata), FALSE);
+ g_return_val_if_fail (xmp_data != NULL || xmp_data_length == 0, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ xmp_metadata = gimp_metadata_new ();
+
+ if (! gexiv2_metadata_open_buf (GEXIV2_METADATA (xmp_metadata),
+ xmp_data, xmp_data_length, error))
+ {
+ g_object_unref (xmp_metadata);
+ return FALSE;
+ }
+
+ if (! gexiv2_metadata_has_xmp (GEXIV2_METADATA (xmp_metadata)))
+ {
+ g_set_error (error, GIMP_METADATA_ERROR, 0,
+ _("Parsing XMP data failed."));
+ g_object_unref (xmp_metadata);
+ return FALSE;
+ }
+
+ gimp_metadata_add (xmp_metadata, metadata);
+ g_object_unref (xmp_metadata);
+
+ return TRUE;
+}
+
+/**
+ * gimp_metadata_set_pixel_size:
+ * @metadata: A #GimpMetadata instance.
+ * @width: Width in pixels
+ * @height: Height in pixels
+ *
+ * Sets Exif.Image.ImageWidth and Exif.Image.ImageLength on @metadata.
+ *
+ * Since: 2.10
+ */
+void
+gimp_metadata_set_pixel_size (GimpMetadata *metadata,
+ gint width,
+ gint height)
+{
+ gchar buffer[32];
+
+ g_return_if_fail (GIMP_IS_METADATA (metadata));
+
+ g_snprintf (buffer, sizeof (buffer), "%d", width);
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
+ "Exif.Image.ImageWidth", buffer);
+
+ g_snprintf (buffer, sizeof (buffer), "%d", height);
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
+ "Exif.Image.ImageLength", buffer);
+}
+
+/**
+ * gimp_metadata_set_bits_per_sample:
+ * @metadata: A #GimpMetadata instance.
+ * @bits_per_sample: Bits per pixel, per component
+ *
+ * Sets Exif.Image.BitsPerSample on @metadata.
+ *
+ * Since: 2.10
+ */
+void
+gimp_metadata_set_bits_per_sample (GimpMetadata *metadata,
+ gint bits_per_sample)
+{
+ gchar buffer[32];
+
+ g_return_if_fail (GIMP_IS_METADATA (metadata));
+
+ g_snprintf (buffer, sizeof (buffer), "%d %d %d",
+ bits_per_sample, bits_per_sample, bits_per_sample);
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
+ "Exif.Image.BitsPerSample", buffer);
+}
+
+/**
+ * gimp_metadata_get_resolution:
+ * @metadata: A #GimpMetadata instance.
+ * @xres: Return location for the X Resolution, in ppi
+ * @yres: Return location for the Y Resolution, in ppi
+ * @unit: Return location for the unit unit
+ *
+ * Returns values based on Exif.Image.XResolution,
+ * Exif.Image.YResolution and Exif.Image.ResolutionUnit of @metadata.
+ *
+ * Return value: %TRUE on success, %FALSE otherwise.
+ *
+ * Since: 2.10
+ */
+gboolean
+gimp_metadata_get_resolution (GimpMetadata *metadata,
+ gdouble *xres,
+ gdouble *yres,
+ GimpUnit *unit)
+{
+ gint xnom, xdenom;
+ gint ynom, ydenom;
+
+ g_return_val_if_fail (GIMP_IS_METADATA (metadata), FALSE);
+
+ if (gexiv2_metadata_get_exif_tag_rational (GEXIV2_METADATA (metadata),
+ "Exif.Image.XResolution",
+ &xnom, &xdenom) &&
+ gexiv2_metadata_get_exif_tag_rational (GEXIV2_METADATA (metadata),
+ "Exif.Image.YResolution",
+ &ynom, &ydenom))
+ {
+ gchar *un;
+ gint exif_unit = 2;
+
+ un = gexiv2_metadata_get_tag_string (GEXIV2_METADATA (metadata),
+ "Exif.Image.ResolutionUnit");
+ if (un)
+ {
+ exif_unit = atoi (un);
+ g_free (un);
+ }
+
+ if (xnom != 0 && xdenom != 0 &&
+ ynom != 0 && ydenom != 0)
+ {
+ gdouble xresolution = (gdouble) xnom / (gdouble) xdenom;
+ gdouble yresolution = (gdouble) ynom / (gdouble) ydenom;
+
+ if (exif_unit == 3)
+ {
+ xresolution *= 2.54;
+ yresolution *= 2.54;
+ }
+
+ if (xresolution >= GIMP_MIN_RESOLUTION &&
+ xresolution <= GIMP_MAX_RESOLUTION &&
+ yresolution >= GIMP_MIN_RESOLUTION &&
+ yresolution <= GIMP_MAX_RESOLUTION)
+ {
+ if (xres)
+ *xres = xresolution;
+
+ if (yres)
+ *yres = yresolution;
+
+ if (unit)
+ {
+ if (exif_unit == 3)
+ *unit = GIMP_UNIT_MM;
+ else
+ *unit = GIMP_UNIT_INCH;
+ }
+
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * gimp_metadata_set_resolution:
+ * @metadata: A #GimpMetadata instance.
+ * @xres: The image's X Resolution, in ppi
+ * @yres: The image's Y Resolution, in ppi
+ * @unit: The image's unit
+ *
+ * Sets Exif.Image.XResolution, Exif.Image.YResolution and
+ * Exif.Image.ResolutionUnit of @metadata.
+ *
+ * Since: 2.10
+ */
+void
+gimp_metadata_set_resolution (GimpMetadata *metadata,
+ gdouble xres,
+ gdouble yres,
+ GimpUnit unit)
+{
+ gchar buffer[32];
+ gint exif_unit;
+ gint factor;
+
+ g_return_if_fail (GIMP_IS_METADATA (metadata));
+
+ if (gimp_unit_is_metric (unit))
+ {
+ xres /= 2.54;
+ yres /= 2.54;
+
+ exif_unit = 3;
+ }
+ else
+ {
+ exif_unit = 2;
+ }
+
+ for (factor = 1; factor <= 100 /* arbitrary */; factor++)
+ {
+ if (fabs (xres * factor - ROUND (xres * factor)) < 0.01 &&
+ fabs (yres * factor - ROUND (yres * factor)) < 0.01)
+ break;
+ }
+
+ gexiv2_metadata_set_exif_tag_rational (GEXIV2_METADATA (metadata),
+ "Exif.Image.XResolution",
+ ROUND (xres * factor), factor);
+
+ gexiv2_metadata_set_exif_tag_rational (GEXIV2_METADATA (metadata),
+ "Exif.Image.YResolution",
+ ROUND (yres * factor), factor);
+
+ g_snprintf (buffer, sizeof (buffer), "%d", exif_unit);
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
+ "Exif.Image.ResolutionUnit", buffer);
+}
+
+/**
+ * gimp_metadata_get_colorspace:
+ * @metadata: A #GimpMetadata instance.
+ *
+ * Returns values based on Exif.Photo.ColorSpace, Xmp.exif.ColorSpace,
+ * Exif.Iop.InteroperabilityIndex, Exif.Nikon3.ColorSpace,
+ * Exif.Canon.ColorSpace of @metadata.
+ *
+ * Return value: The colorspace specified by above tags.
+ *
+ * Since: 2.10
+ */
+GimpMetadataColorspace
+gimp_metadata_get_colorspace (GimpMetadata *metadata)
+{
+ glong exif_cs = -1;
+
+ g_return_val_if_fail (GIMP_IS_METADATA (metadata),
+ GIMP_METADATA_COLORSPACE_UNSPECIFIED);
+
+ /* the logic here was mostly taken from darktable and libkexiv2 */
+
+ if (gexiv2_metadata_has_tag (GEXIV2_METADATA (metadata),
+ "Exif.Photo.ColorSpace"))
+ {
+ exif_cs = gexiv2_metadata_get_tag_long (GEXIV2_METADATA (metadata),
+ "Exif.Photo.ColorSpace");
+ }
+ else if (gexiv2_metadata_has_tag (GEXIV2_METADATA (metadata),
+ "Xmp.exif.ColorSpace"))
+ {
+ exif_cs = gexiv2_metadata_get_tag_long (GEXIV2_METADATA (metadata),
+ "Xmp.exif.ColorSpace");
+ }
+
+ if (exif_cs == 0x01)
+ {
+ return GIMP_METADATA_COLORSPACE_SRGB;
+ }
+ else if (exif_cs == 0x02)
+ {
+ return GIMP_METADATA_COLORSPACE_ADOBERGB;
+ }
+ else
+ {
+ if (exif_cs == 0xffff)
+ {
+ gchar *iop_index;
+
+ iop_index = gexiv2_metadata_get_tag_string (GEXIV2_METADATA (metadata),
+ "Exif.Iop.InteroperabilityIndex");
+
+ if (! g_strcmp0 (iop_index, "R03"))
+ {
+ g_free (iop_index);
+
+ return GIMP_METADATA_COLORSPACE_ADOBERGB;
+ }
+ else if (! g_strcmp0 (iop_index, "R98"))
+ {
+ g_free (iop_index);
+
+ return GIMP_METADATA_COLORSPACE_SRGB;
+ }
+
+ g_free (iop_index);
+ }
+
+ if (gexiv2_metadata_has_tag (GEXIV2_METADATA (metadata),
+ "Exif.Nikon3.ColorSpace"))
+ {
+ glong nikon_cs;
+
+ nikon_cs = gexiv2_metadata_get_tag_long (GEXIV2_METADATA (metadata),
+ "Exif.Nikon3.ColorSpace");
+
+ if (nikon_cs == 0x01)
+ {
+ return GIMP_METADATA_COLORSPACE_SRGB;
+ }
+ else if (nikon_cs == 0x02)
+ {
+ return GIMP_METADATA_COLORSPACE_ADOBERGB;
+ }
+ }
+
+ if (gexiv2_metadata_has_tag (GEXIV2_METADATA (metadata),
+ "Exif.Canon.ColorSpace"))
+ {
+ glong canon_cs;
+
+ canon_cs = gexiv2_metadata_get_tag_long (GEXIV2_METADATA (metadata),
+ "Exif.Canon.ColorSpace");
+
+ if (canon_cs == 0x01)
+ {
+ return GIMP_METADATA_COLORSPACE_SRGB;
+ }
+ else if (canon_cs == 0x02)
+ {
+ return GIMP_METADATA_COLORSPACE_ADOBERGB;
+ }
+ }
+
+ if (exif_cs == 0xffff)
+ return GIMP_METADATA_COLORSPACE_UNCALIBRATED;
+ }
+
+ return GIMP_METADATA_COLORSPACE_UNSPECIFIED;
+}
+
+/**
+ * gimp_metadata_set_colorspace:
+ * @metadata: A #GimpMetadata instance.
+ * @colorspace: The color space.
+ *
+ * Sets Exif.Photo.ColorSpace, Xmp.exif.ColorSpace,
+ * Exif.Iop.InteroperabilityIndex, Exif.Nikon3.ColorSpace,
+ * Exif.Canon.ColorSpace of @metadata.
+ *
+ * Since: 2.10
+ */
+void
+gimp_metadata_set_colorspace (GimpMetadata *metadata,
+ GimpMetadataColorspace colorspace)
+{
+ GExiv2Metadata *g2metadata = GEXIV2_METADATA (metadata);
+
+ switch (colorspace)
+ {
+ case GIMP_METADATA_COLORSPACE_UNSPECIFIED:
+ gexiv2_metadata_clear_tag (g2metadata, "Exif.Photo.ColorSpace");
+ gexiv2_metadata_clear_tag (g2metadata, "Xmp.exif.ColorSpace");
+ gexiv2_metadata_clear_tag (g2metadata, "Exif.Iop.InteroperabilityIndex");
+ gexiv2_metadata_clear_tag (g2metadata, "Exif.Nikon3.ColorSpace");
+ gexiv2_metadata_clear_tag (g2metadata, "Exif.Canon.ColorSpace");
+ break;
+
+ case GIMP_METADATA_COLORSPACE_UNCALIBRATED:
+ gexiv2_metadata_set_tag_long (g2metadata, "Exif.Photo.ColorSpace", 0xffff);
+ if (gexiv2_metadata_has_tag (g2metadata, "Xmp.exif.ColorSpace"))
+ gexiv2_metadata_set_tag_long (g2metadata, "Xmp.exif.ColorSpace", 0xffff);
+ gexiv2_metadata_clear_tag (g2metadata, "Exif.Iop.InteroperabilityIndex");
+ gexiv2_metadata_clear_tag (g2metadata, "Exif.Nikon3.ColorSpace");
+ gexiv2_metadata_clear_tag (g2metadata, "Exif.Canon.ColorSpace");
+ break;
+
+ case GIMP_METADATA_COLORSPACE_SRGB:
+ gexiv2_metadata_set_tag_long (g2metadata, "Exif.Photo.ColorSpace", 0x01);
+
+ if (gexiv2_metadata_has_tag (g2metadata, "Xmp.exif.ColorSpace"))
+ gexiv2_metadata_set_tag_long (g2metadata, "Xmp.exif.ColorSpace", 0x01);
+
+ if (gexiv2_metadata_has_tag (g2metadata, "Exif.Iop.InteroperabilityIndex"))
+ gexiv2_metadata_set_tag_string (g2metadata,
+ "Exif.Iop.InteroperabilityIndex", "R98");
+
+ if (gexiv2_metadata_has_tag (g2metadata, "Exif.Nikon3.ColorSpace"))
+ gexiv2_metadata_set_tag_long (g2metadata, "Exif.Nikon3.ColorSpace", 0x01);
+
+ if (gexiv2_metadata_has_tag (g2metadata, "Exif.Canon.ColorSpace"))
+ gexiv2_metadata_set_tag_long (g2metadata, "Exif.Canon.ColorSpace", 0x01);
+ break;
+
+ case GIMP_METADATA_COLORSPACE_ADOBERGB:
+ gexiv2_metadata_set_tag_long (g2metadata, "Exif.Photo.ColorSpace", 0x02);
+
+ if (gexiv2_metadata_has_tag (g2metadata, "Xmp.exif.ColorSpace"))
+ gexiv2_metadata_set_tag_long (g2metadata, "Xmp.exif.ColorSpace", 0x02);
+
+ if (gexiv2_metadata_has_tag (g2metadata, "Exif.Iop.InteroperabilityIndex"))
+ gexiv2_metadata_set_tag_string (g2metadata,
+ "Exif.Iop.InteroperabilityIndex", "R03");
+
+ if (gexiv2_metadata_has_tag (g2metadata, "Exif.Nikon3.ColorSpace"))
+ gexiv2_metadata_set_tag_long (g2metadata, "Exif.Nikon3.ColorSpace", 0x02);
+
+ if (gexiv2_metadata_has_tag (g2metadata, "Exif.Canon.ColorSpace"))
+ gexiv2_metadata_set_tag_long (g2metadata, "Exif.Canon.ColorSpace", 0x02);
+ break;
+ }
+}
+
+/**
+ * gimp_metadata_is_tag_supported:
+ * @tag: A metadata tag name
+ * @mime_type: A mime type
+ *
+ * Returns whether @tag is supported in a file of type @mime_type.
+ *
+ * Return value: %TRUE if the @tag supported with @mime_type, %FALSE otherwise.
+ *
+ * Since: 2.10
+ */
+gboolean
+gimp_metadata_is_tag_supported (const gchar *tag,
+ const gchar *mime_type)
+{
+ gint j;
+
+ g_return_val_if_fail (tag != NULL, FALSE);
+ g_return_val_if_fail (mime_type != NULL, FALSE);
+
+ for (j = 0; j < G_N_ELEMENTS (unsupported_tags); j++)
+ {
+ if (g_str_has_prefix (tag, unsupported_tags[j]))
+ {
+ return FALSE;
+ }
+ }
+
+ if (! strcmp (mime_type, "image/jpeg"))
+ {
+ for (j = 0; j < G_N_ELEMENTS (tiff_tags); j++)
+ {
+ if (g_str_has_prefix (tag, tiff_tags[j]))
+ {
+ return FALSE;
+ }
+ }
+ }
+ else if (! strcmp (mime_type, "image/tiff"))
+ {
+ for (j = 0; j < G_N_ELEMENTS (jpeg_tags); j++)
+ {
+ if (g_str_has_prefix (tag, jpeg_tags[j]))
+ {
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+
+/* private functions */
+
+static GQuark
+gimp_metadata_error_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (G_UNLIKELY (quark == 0))
+ quark = g_quark_from_static_string ("gimp-metadata-error-quark");
+
+ return quark;
+}
+
+static void
+gimp_metadata_copy_tag (GExiv2Metadata *src,
+ GExiv2Metadata *dest,
+ const gchar *tag)
+{
+#if GEXIV2_CHECK_VERSION(0, 12, 2)
+ gchar **values;
+ GError *error = NULL;
+
+ values = gexiv2_metadata_try_get_tag_multiple (src, tag, &error);
+
+ if (error)
+ {
+ g_printerr ("%s: %s\n", G_STRFUNC, error->message);
+ g_clear_error (&error);
+ g_strfreev (values);
+ }
+ else if (values)
+ {
+ gexiv2_metadata_try_set_tag_multiple (dest, tag, (const gchar **) values, &error);
+ if (error)
+ {
+ g_warning ("%s: failed to set multiple metadata '%s': %s\n",
+ G_STRFUNC, tag, error->message);
+ g_clear_error (&error);
+ }
+
+ g_strfreev (values);
+ }
+ else
+ {
+ gchar *value = gexiv2_metadata_try_get_tag_string (src, tag, &error);
+
+ if (value)
+ {
+ gexiv2_metadata_try_set_tag_string (dest, tag, value, &error);
+ if (error)
+ {
+ g_warning ("%s: failed to set metadata '%s': %s\n",
+ G_STRFUNC, tag, error->message);
+ g_clear_error (&error);
+ }
+ g_free (value);
+ }
+ else if (error)
+ {
+ g_warning ("%s: failed to get metadata '%s': %s\n",
+ G_STRFUNC, tag, error->message);
+ g_clear_error (&error);
+ }
+ }
+#else
+ gchar **values = gexiv2_metadata_get_tag_multiple (src, tag);
+
+ if (values)
+ {
+ gexiv2_metadata_set_tag_multiple (dest, tag, (const gchar **) values);
+ g_strfreev (values);
+ }
+ else
+ {
+ gchar *value = gexiv2_metadata_get_tag_string (src, tag);
+
+ if (value)
+ {
+ gexiv2_metadata_set_tag_string (dest, tag, value);
+ g_free (value);
+ }
+ }
+#endif
+}
+
+static void
+gimp_metadata_copy_tags (GExiv2Metadata *src,
+ GExiv2Metadata *dest,
+ const gchar **tags)
+{
+ gint i;
+
+ for (i = 0; tags[i] != NULL; i++)
+ {
+ /* don't copy the same tag multiple times */
+ if (i > 0 && ! strcmp (tags[i], tags[i - 1]))
+ continue;
+
+ gimp_metadata_copy_tag (src, dest, tags[i]);
+ }
+ }
+
+static void
+gimp_metadata_add (GimpMetadata *src,
+ GimpMetadata *dest)
+{
+ GExiv2Metadata *g2src = GEXIV2_METADATA (src);
+ GExiv2Metadata *g2dest = GEXIV2_METADATA (dest);
+
+ if (gexiv2_metadata_get_supports_exif (g2src) &&
+ gexiv2_metadata_get_supports_exif (g2dest))
+ {
+ gchar **exif_tags = gexiv2_metadata_get_exif_tags (g2src);
+
+ if (exif_tags)
+ {
+ gimp_metadata_copy_tags (g2src, g2dest,
+ (const gchar **) exif_tags);
+ g_strfreev (exif_tags);
+ }
+ }
+
+ if (gexiv2_metadata_get_supports_xmp (g2src) &&
+ gexiv2_metadata_get_supports_xmp (g2dest))
+ {
+ gchar **xmp_tags = gexiv2_metadata_get_xmp_tags (g2src);
+
+ if (xmp_tags)
+ {
+ gimp_metadata_copy_tags (g2src, g2dest,
+ (const gchar **) xmp_tags);
+ g_strfreev (xmp_tags);
+ }
+ }
+
+ if (gexiv2_metadata_get_supports_iptc (g2src) &&
+ gexiv2_metadata_get_supports_iptc (g2dest))
+ {
+ gchar **iptc_tags = gexiv2_metadata_get_iptc_tags (g2src);
+
+ if (iptc_tags)
+ {
+ gimp_metadata_copy_tags (g2src, g2dest,
+ (const gchar **) iptc_tags);
+ g_strfreev (iptc_tags);
+ }
+ }
+}
diff --git a/libgimpbase/gimpmetadata.h b/libgimpbase/gimpmetadata.h
new file mode 100644
index 0000000..30a6f39
--- /dev/null
+++ b/libgimpbase/gimpmetadata.h
@@ -0,0 +1,154 @@
+/* LIBGIMPBASE - The GIMP Basic Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpmetadata.h
+ * Copyright (C) 2013 Hartmut Kuhse <hartmutkuhse@src.gnome.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_METADATA_H__
+#define __GIMP_METADATA_H__
+
+G_BEGIN_DECLS
+
+#define GIMP_TYPE_METADATA (gimp_metadata_get_type ())
+#define GIMP_METADATA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_METADATA, GimpMetadata))
+#define GIMP_METADATA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_METADATA, GimpMetadataClass))
+#define GIMP_IS_METADATA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_METADATA))
+#define GIMP_IS_METADATA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_METADATA))
+#define GIMP_METADATA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_METADATA, GimpMetadataClass))
+
+
+/**
+ * GimpMetadataLoadFlags:
+ * @GIMP_METADATA_LOAD_COMMENT: Load the comment
+ * @GIMP_METADATA_LOAD_RESOLUTION: Load the resolution
+ * @GIMP_METADATA_LOAD_ORIENTATION: Load the orientation (rotation)
+ * @GIMP_METADATA_LOAD_COLORSPACE: Load the colorspace
+ * @GIMP_METADATA_LOAD_ALL: Load all of the above
+ *
+ * What metadata to load when importing images.
+ **/
+typedef enum
+{
+ GIMP_METADATA_LOAD_COMMENT = 1 << 0,
+ GIMP_METADATA_LOAD_RESOLUTION = 1 << 1,
+ GIMP_METADATA_LOAD_ORIENTATION = 1 << 2,
+ GIMP_METADATA_LOAD_COLORSPACE = 1 << 3,
+
+ GIMP_METADATA_LOAD_ALL = 0xffffffff
+} GimpMetadataLoadFlags;
+
+
+/**
+ * GimpMetadataSaveFlags:
+ * @GIMP_METADATA_SAVE_EXIF: Save EXIF
+ * @GIMP_METADATA_SAVE_XMP: Save XMP
+ * @GIMP_METADATA_SAVE_IPTC: Save IPTC
+ * @GIMP_METADATA_SAVE_THUMBNAIL: Save a thumbnail of the image
+ * @GIMP_METADATA_SAVE_COLOR_PROFILE: Save the image's color profile
+ * Since: 2.10.10
+ * @GIMP_METADATA_SAVE_ALL: Save all of the above
+ *
+ * What kinds of metadata to save when exporting images.
+ **/
+typedef enum
+{
+ GIMP_METADATA_SAVE_EXIF = 1 << 0,
+ GIMP_METADATA_SAVE_XMP = 1 << 1,
+ GIMP_METADATA_SAVE_IPTC = 1 << 2,
+ GIMP_METADATA_SAVE_THUMBNAIL = 1 << 3,
+ GIMP_METADATA_SAVE_COLOR_PROFILE = 1 << 4,
+
+ GIMP_METADATA_SAVE_ALL = 0xffffffff
+} GimpMetadataSaveFlags;
+
+
+/**
+ * GimpMetadataColorspace:
+ * @GIMP_METADATA_COLORSPACE_UNSPECIFIED: Unspecified
+ * @GIMP_METADATA_COLORSPACE_UNCALIBRATED: Uncalibrated
+ * @GIMP_METADATA_COLORSPACE_SRGB: sRGB
+ * @GIMP_METADATA_COLORSPACE_ADOBERGB: Adobe RGB
+ *
+ * Well-defined colorspace information available from metadata
+ **/
+typedef enum
+{
+ GIMP_METADATA_COLORSPACE_UNSPECIFIED,
+ GIMP_METADATA_COLORSPACE_UNCALIBRATED,
+ GIMP_METADATA_COLORSPACE_SRGB,
+ GIMP_METADATA_COLORSPACE_ADOBERGB
+} GimpMetadataColorspace;
+
+
+GType gimp_metadata_get_type (void) G_GNUC_CONST;
+
+GimpMetadata * gimp_metadata_new (void);
+GimpMetadata * gimp_metadata_duplicate (GimpMetadata *metadata);
+
+GimpMetadata * gimp_metadata_deserialize (const gchar *metadata_xml);
+gchar * gimp_metadata_serialize (GimpMetadata *metadata);
+gchar * gimp_metadata_get_guid (void);
+
+void gimp_metadata_add_xmp_history (GimpMetadata *metadata,
+ gchar *state_status);
+
+GimpMetadata * gimp_metadata_load_from_file (GFile *file,
+ GError **error);
+gboolean gimp_metadata_save_to_file (GimpMetadata *metadata,
+ GFile *file,
+ GError **error);
+
+gboolean gimp_metadata_set_from_exif (GimpMetadata *metadata,
+ const guchar *exif_data,
+ gint exif_data_length,
+ GError **error);
+gboolean gimp_metadata_set_from_iptc (GimpMetadata *metadata,
+ const guchar *iptc_data,
+ gint iptc_data_length,
+ GError **error);
+gboolean gimp_metadata_set_from_xmp (GimpMetadata *metadata,
+ const guchar *xmp_data,
+ gint xmp_data_length,
+ GError **error);
+
+void gimp_metadata_set_pixel_size (GimpMetadata *metadata,
+ gint width,
+ gint height);
+void gimp_metadata_set_bits_per_sample (GimpMetadata *metadata,
+ gint bits_per_sample);
+
+gboolean gimp_metadata_get_resolution (GimpMetadata *metadata,
+ gdouble *xres,
+ gdouble *yres,
+ GimpUnit *unit);
+void gimp_metadata_set_resolution (GimpMetadata *metadata,
+ gdouble xres,
+ gdouble yres,
+ GimpUnit unit);
+
+GimpMetadataColorspace
+ gimp_metadata_get_colorspace (GimpMetadata *metadata);
+void gimp_metadata_set_colorspace (GimpMetadata *metadata,
+ GimpMetadataColorspace colorspace);
+
+gboolean gimp_metadata_is_tag_supported (const gchar *tag,
+ const gchar *mime_type);
+
+G_END_DECLS
+
+#endif /* __GIMP_METADATA_H__ */
diff --git a/libgimpbase/gimpparam.h b/libgimpbase/gimpparam.h
new file mode 100644
index 0000000..6b6427c
--- /dev/null
+++ b/libgimpbase/gimpparam.h
@@ -0,0 +1,66 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_PARAM_H__
+#define __GIMP_PARAM_H__
+
+
+/**
+ * SECTION: gimpparam
+ * @title: gimpparam
+ * @short_description: Definitions of useful #GParamFlags.
+ *
+ * Definitions of useful #GParamFlags.
+ **/
+
+
+/**
+ * GIMP_PARAM_STATIC_STRINGS:
+ *
+ * Since: 2.4
+ **/
+#define GIMP_PARAM_STATIC_STRINGS (G_PARAM_STATIC_NAME | \
+ G_PARAM_STATIC_NICK | \
+ G_PARAM_STATIC_BLURB)
+
+/**
+ * GIMP_PARAM_READABLE:
+ *
+ * Since: 2.4
+ **/
+#define GIMP_PARAM_READABLE (G_PARAM_READABLE | \
+ GIMP_PARAM_STATIC_STRINGS)
+
+/**
+ * GIMP_PARAM_WRITABLE:
+ *
+ * Since: 2.4
+ **/
+#define GIMP_PARAM_WRITABLE (G_PARAM_WRITABLE | \
+ GIMP_PARAM_STATIC_STRINGS)
+
+/**
+ * GIMP_PARAM_READWRITE:
+ *
+ * Since: 2.4
+ **/
+#define GIMP_PARAM_READWRITE (G_PARAM_READWRITE | \
+ GIMP_PARAM_STATIC_STRINGS)
+
+
+#endif /* __GIMP_PARAM_H__ */
diff --git a/libgimpbase/gimpparasite.c b/libgimpbase/gimpparasite.c
new file mode 100644
index 0000000..ac66491
--- /dev/null
+++ b/libgimpbase/gimpparasite.c
@@ -0,0 +1,352 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpparasite.c
+ * Copyright (C) 1998 Jay Cox <jaycox@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+
+#include <glib-object.h>
+
+#ifdef G_OS_WIN32
+#include <process.h> /* For _getpid() */
+#endif
+
+#include "gimpbasetypes.h"
+
+#include "gimpparasite.h"
+
+
+/**
+ * SECTION: gimpparasite
+ * @title: GimpParasite
+ * @short_description: Arbitrary pieces of data which can be attached
+ * to various GIMP objects.
+ * @see_also: gimp_image_parasite_attach(),
+ * gimp_drawable_parasite_attach(), gimp_parasite_attach()
+ * and their related functions.
+ *
+ * Arbitrary pieces of data which can be attached to various GIMP objects.
+ **/
+
+
+/*
+ * GIMP_TYPE_PARASITE
+ */
+
+GType
+gimp_parasite_get_type (void)
+{
+ static GType type = 0;
+
+ if (! type)
+ type = g_boxed_type_register_static ("GimpParasite",
+ (GBoxedCopyFunc) gimp_parasite_copy,
+ (GBoxedFreeFunc) gimp_parasite_free);
+
+ return type;
+}
+
+
+/*
+ * GIMP_TYPE_PARAM_PARASITE
+ */
+
+#define GIMP_PARAM_SPEC_PARASITE(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_PARASITE, GimpParamSpecParasite))
+
+typedef struct _GimpParamSpecParasite GimpParamSpecParasite;
+
+struct _GimpParamSpecParasite
+{
+ GParamSpecBoxed parent_instance;
+};
+
+static void gimp_param_parasite_class_init (GParamSpecClass *class);
+static void gimp_param_parasite_init (GParamSpec *pspec);
+static gboolean gimp_param_parasite_validate (GParamSpec *pspec,
+ GValue *value);
+static gint gimp_param_parasite_values_cmp (GParamSpec *pspec,
+ const GValue *value1,
+ const GValue *value2);
+
+GType
+gimp_param_parasite_get_type (void)
+{
+ static GType type = 0;
+
+ if (! type)
+ {
+ const GTypeInfo type_info =
+ {
+ sizeof (GParamSpecClass),
+ NULL, NULL,
+ (GClassInitFunc) gimp_param_parasite_class_init,
+ NULL, NULL,
+ sizeof (GimpParamSpecParasite),
+ 0,
+ (GInstanceInitFunc) gimp_param_parasite_init
+ };
+
+ type = g_type_register_static (G_TYPE_PARAM_BOXED,
+ "GimpParamParasite",
+ &type_info, 0);
+ }
+
+ return type;
+}
+
+static void
+gimp_param_parasite_class_init (GParamSpecClass *class)
+{
+ class->value_type = GIMP_TYPE_PARASITE;
+ class->value_validate = gimp_param_parasite_validate;
+ class->values_cmp = gimp_param_parasite_values_cmp;
+}
+
+static void
+gimp_param_parasite_init (GParamSpec *pspec)
+{
+}
+
+static gboolean
+gimp_param_parasite_validate (GParamSpec *pspec,
+ GValue *value)
+{
+ GimpParasite *parasite = value->data[0].v_pointer;
+
+ if (! parasite)
+ {
+ return TRUE;
+ }
+ else if (parasite->name == NULL ||
+ *parasite->name == '\0' ||
+ ! g_utf8_validate (parasite->name, -1, NULL) ||
+ (parasite->size == 0 && parasite->data != NULL) ||
+ (parasite->size > 0 && parasite->data == NULL))
+ {
+ g_value_set_boxed (value, NULL);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gint
+gimp_param_parasite_values_cmp (GParamSpec *pspec,
+ const GValue *value1,
+ const GValue *value2)
+{
+ GimpParasite *parasite1 = value1->data[0].v_pointer;
+ GimpParasite *parasite2 = value2->data[0].v_pointer;
+
+ /* try to return at least *something*, it's useless anyway... */
+
+ if (! parasite1)
+ return parasite2 != NULL ? -1 : 0;
+ else if (! parasite2)
+ return parasite1 != NULL;
+ else
+ return gimp_parasite_compare (parasite1, parasite2);
+}
+
+GParamSpec *
+gimp_param_spec_parasite (const gchar *name,
+ const gchar *nick,
+ const gchar *blurb,
+ GParamFlags flags)
+{
+ GimpParamSpecParasite *parasite_spec;
+
+ parasite_spec = g_param_spec_internal (GIMP_TYPE_PARAM_PARASITE,
+ name, nick, blurb, flags);
+
+ return G_PARAM_SPEC (parasite_spec);
+}
+
+
+#ifdef DEBUG
+static void
+gimp_parasite_print (GimpParasite *parasite)
+{
+ if (parasite == NULL)
+ {
+ g_print ("pid %d: attempt to print a null parasite\n", getpid ());
+ return;
+ }
+
+ g_print ("pid %d: parasite: %p\n", getpid (), parasite);
+
+ if (parasite->name)
+ g_print ("\tname: %s\n", parasite->name);
+ else
+ g_print ("\tname: NULL\n");
+
+ g_print ("\tflags: %d\n", parasite->flags);
+ g_print ("\tsize: %d\n", parasite->size);
+ if (parasite->size > 0)
+ g_print ("\tdata: %p\n", parasite->data);
+}
+#endif
+
+GimpParasite *
+gimp_parasite_new (const gchar *name,
+ guint32 flags,
+ guint32 size,
+ gconstpointer data)
+{
+ GimpParasite *parasite;
+
+ if (! (name && *name))
+ return NULL;
+
+ parasite = g_slice_new (GimpParasite);
+ parasite->name = g_strdup (name);
+ parasite->flags = (flags & 0xFF);
+ parasite->size = size;
+
+ if (size)
+ parasite->data = g_memdup (data, size);
+ else
+ parasite->data = NULL;
+
+ return parasite;
+}
+
+void
+gimp_parasite_free (GimpParasite *parasite)
+{
+ if (parasite == NULL)
+ return;
+
+ if (parasite->name)
+ g_free (parasite->name);
+
+ if (parasite->data)
+ g_free (parasite->data);
+
+ g_slice_free (GimpParasite, parasite);
+}
+
+gboolean
+gimp_parasite_is_type (const GimpParasite *parasite,
+ const gchar *name)
+{
+ if (!parasite || !parasite->name)
+ return FALSE;
+
+ return (strcmp (parasite->name, name) == 0);
+}
+
+GimpParasite *
+gimp_parasite_copy (const GimpParasite *parasite)
+{
+ if (parasite == NULL)
+ return NULL;
+
+ return gimp_parasite_new (parasite->name, parasite->flags,
+ parasite->size, parasite->data);
+}
+
+gboolean
+gimp_parasite_compare (const GimpParasite *a,
+ const GimpParasite *b)
+{
+ if (a && b &&
+ a->name && b->name &&
+ strcmp (a->name, b->name) == 0 &&
+ a->flags == b->flags &&
+ a->size == b->size)
+ {
+ if (a->data == NULL && b->data == NULL)
+ return TRUE;
+ else if (a->data && b->data && memcmp (a->data, b->data, a->size) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gulong
+gimp_parasite_flags (const GimpParasite *parasite)
+{
+ if (parasite == NULL)
+ return 0;
+
+ return parasite->flags;
+}
+
+gboolean
+gimp_parasite_is_persistent (const GimpParasite *parasite)
+{
+ if (parasite == NULL)
+ return FALSE;
+
+ return (parasite->flags & GIMP_PARASITE_PERSISTENT);
+}
+
+gboolean
+gimp_parasite_is_undoable (const GimpParasite *parasite)
+{
+ if (parasite == NULL)
+ return FALSE;
+
+ return (parasite->flags & GIMP_PARASITE_UNDOABLE);
+}
+
+gboolean
+gimp_parasite_has_flag (const GimpParasite *parasite,
+ gulong flag)
+{
+ if (parasite == NULL)
+ return FALSE;
+
+ return (parasite->flags & flag);
+}
+
+const gchar *
+gimp_parasite_name (const GimpParasite *parasite)
+{
+ if (parasite)
+ return parasite->name;
+
+ return NULL;
+}
+
+gconstpointer
+gimp_parasite_data (const GimpParasite *parasite)
+{
+ if (parasite)
+ return parasite->data;
+
+ return NULL;
+}
+
+glong
+gimp_parasite_data_size (const GimpParasite *parasite)
+{
+ if (parasite)
+ return parasite->size;
+
+ return 0;
+}
diff --git a/libgimpbase/gimpparasite.h b/libgimpbase/gimpparasite.h
new file mode 100644
index 0000000..3c17bf7
--- /dev/null
+++ b/libgimpbase/gimpparasite.h
@@ -0,0 +1,113 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpparasite.h
+ * Copyright (C) 1998 Jay Cox <jaycox@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_BASE_H_INSIDE__) && !defined (GIMP_BASE_COMPILATION)
+#error "Only <libgimpbase/gimpbase.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PARASITE_H__
+#define __GIMP_PARASITE_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+/*
+ * GIMP_TYPE_PARASITE
+ */
+
+#define GIMP_TYPE_PARASITE (gimp_parasite_get_type ())
+#define GIMP_VALUE_HOLDS_PARASITE(value) (G_TYPE_CHECK_VALUE_TYPE ((value), GIMP_TYPE_PARASITE))
+
+GType gimp_parasite_get_type (void) G_GNUC_CONST;
+
+
+/*
+ * GIMP_TYPE_PARAM_PARASITE
+ */
+
+#define GIMP_TYPE_PARAM_PARASITE (gimp_param_parasite_get_type ())
+#define GIMP_IS_PARAM_SPEC_PARASITE(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_PARASITE))
+
+GType gimp_param_parasite_get_type (void) G_GNUC_CONST;
+
+GParamSpec * gimp_param_spec_parasite (const gchar *name,
+ const gchar *nick,
+ const gchar *blurb,
+ GParamFlags flags);
+
+
+#define GIMP_PARASITE_PERSISTENT 1
+#define GIMP_PARASITE_UNDOABLE 2
+
+#define GIMP_PARASITE_ATTACH_PARENT (0x80 << 8)
+#define GIMP_PARASITE_PARENT_PERSISTENT (GIMP_PARASITE_PERSISTENT << 8)
+#define GIMP_PARASITE_PARENT_UNDOABLE (GIMP_PARASITE_UNDOABLE << 8)
+
+#define GIMP_PARASITE_ATTACH_GRANDPARENT (0x80 << 16)
+#define GIMP_PARASITE_GRANDPARENT_PERSISTENT (GIMP_PARASITE_PERSISTENT << 16)
+#define GIMP_PARASITE_GRANDPARENT_UNDOABLE (GIMP_PARASITE_UNDOABLE << 16)
+
+
+/**
+ * GimpParasite:
+ * @name: the parasite name, USE A UNIQUE PREFIX
+ * @flags: the parasite flags, like save in XCF etc.
+ * @size: the parasite size in bytes
+ * @data: the parasite data, the owner os the parasite is responsible
+ * for tracking byte order and internal structure
+ **/
+struct _GimpParasite
+{
+ gchar *name;
+ guint32 flags;
+ guint32 size;
+ gpointer data;
+};
+
+
+GimpParasite * gimp_parasite_new (const gchar *name,
+ guint32 flags,
+ guint32 size,
+ gconstpointer data);
+void gimp_parasite_free (GimpParasite *parasite);
+
+GimpParasite * gimp_parasite_copy (const GimpParasite *parasite);
+
+gboolean gimp_parasite_compare (const GimpParasite *a,
+ const GimpParasite *b);
+
+gboolean gimp_parasite_is_type (const GimpParasite *parasite,
+ const gchar *name);
+gboolean gimp_parasite_is_persistent (const GimpParasite *parasite);
+gboolean gimp_parasite_is_undoable (const GimpParasite *parasite);
+gboolean gimp_parasite_has_flag (const GimpParasite *parasite,
+ gulong flag);
+gulong gimp_parasite_flags (const GimpParasite *parasite);
+const gchar * gimp_parasite_name (const GimpParasite *parasite);
+gconstpointer gimp_parasite_data (const GimpParasite *parasite);
+glong gimp_parasite_data_size (const GimpParasite *parasite);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PARASITE_H__ */
diff --git a/libgimpbase/gimpparasiteio.c b/libgimpbase/gimpparasiteio.c
new file mode 100644
index 0000000..727048a
--- /dev/null
+++ b/libgimpbase/gimpparasiteio.c
@@ -0,0 +1,204 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpparasiteio.c
+ * Copyright (C) 1999 Tor Lillqvist <tml@iki.fi>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Functions for building and parsing string representations of
+ * various standard parasite types.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include "gimpparasiteio.h"
+
+
+/**
+ * SECTION: gimpparasiteio
+ * @title: gimpparasiteio
+ * @short_description: Utility functions to (de)serialize certain C
+ * structures to/from #GimpParasite's.
+ * @see_also: #GimpParasite
+ *
+ * Utility functions to (de)serialize certain C structures to/from*
+ * #GimpParasite's.
+ **/
+
+
+void
+gimp_pixpipe_params_init (GimpPixPipeParams *params)
+{
+ gint i;
+
+ g_return_if_fail (params != NULL);
+
+ params->step = 100;
+ params->ncells = 1;
+ params->cellwidth = 1;
+ params->cellheight = 1;
+ params->dim = 1;
+ params->cols = 1;
+ params->rows = 1;
+ params->placement = g_strdup ("constant");
+
+ for (i = 0; i < GIMP_PIXPIPE_MAXDIM; i++)
+ params->selection[i] = g_strdup ("random");
+
+ params->rank[0] = 1;
+ for (i = 1; i < GIMP_PIXPIPE_MAXDIM; i++)
+ params->rank[i] = 0;
+}
+
+void
+gimp_pixpipe_params_parse (const gchar *string,
+ GimpPixPipeParams *params)
+{
+ gchar *copy;
+ gchar *p, *q, *r;
+ gint i;
+
+ g_return_if_fail (string != NULL);
+ g_return_if_fail (params != NULL);
+
+ copy = g_strdup (string);
+
+ q = copy;
+ while ((p = strtok (q, " \r\n")) != NULL)
+ {
+ q = NULL;
+ r = strchr (p, ':');
+ if (r)
+ *r = 0;
+
+ if (strcmp (p, "ncells") == 0)
+ {
+ if (r)
+ params->ncells = atoi (r + 1);
+ }
+ else if (strcmp (p, "step") == 0)
+ {
+ if (r)
+ params->step = atoi (r + 1);
+ }
+ else if (strcmp (p, "dim") == 0)
+ {
+ if (r)
+ {
+ params->dim = atoi (r + 1);
+ params->dim = CLAMP (params->dim, 1, GIMP_PIXPIPE_MAXDIM);
+ }
+ }
+ else if (strcmp (p, "cols") == 0)
+ {
+ if (r)
+ params->cols = atoi (r + 1);
+ }
+ else if (strcmp (p, "rows") == 0)
+ {
+ if (r)
+ params->rows = atoi (r + 1);
+ }
+ else if (strcmp (p, "cellwidth") == 0)
+ {
+ if (r)
+ params->cellwidth = atoi (r + 1);
+ }
+ else if (strcmp (p, "cellheight") == 0)
+ {
+ if (r)
+ params->cellheight = atoi (r + 1);
+ }
+ else if (strcmp (p, "placement") == 0)
+ {
+ if (r)
+ {
+ g_free (params->placement);
+ params->placement = g_strdup (r + 1);
+ }
+ }
+ else if (strncmp (p, "rank", strlen ("rank")) == 0 && r)
+ {
+ if (r)
+ {
+ i = atoi (p + strlen ("rank"));
+ if (i >= 0 && i < params->dim)
+ params->rank[i] = atoi (r + 1);
+ }
+ }
+ else if (strncmp (p, "sel", strlen ("sel")) == 0 && r)
+ {
+ if (r)
+ {
+ i = atoi (p + strlen ("sel"));
+ if (i >= 0 && i < params->dim)
+ {
+ g_free (params->selection[i]);
+ params->selection[i] = g_strdup (r + 1);
+ }
+ }
+ }
+ if (r)
+ *r = ':';
+ }
+
+ g_free (copy);
+}
+
+gchar *
+gimp_pixpipe_params_build (GimpPixPipeParams *params)
+{
+ GString *str;
+ gint i;
+
+ g_return_val_if_fail (params != NULL, NULL);
+
+ str = g_string_new (NULL);
+
+ g_string_printf (str, "ncells:%d cellwidth:%d cellheight:%d "
+ "step:%d dim:%d cols:%d rows:%d placement:%s",
+ params->ncells, params->cellwidth, params->cellheight,
+ params->step, params->dim,
+ params->cols, params->rows,
+ params->placement);
+
+ for (i = 0; i < params->dim; i++)
+ {
+ g_string_append_printf (str, " rank%d:%d", i, params->rank[i]);
+ g_string_append_printf (str, " sel%d:%s", i, params->selection[i]);
+ }
+
+ return g_string_free (str, FALSE);
+}
+
+void
+gimp_pixpipe_params_free (GimpPixPipeParams *params)
+{
+ gint i;
+
+ g_free (params->placement);
+
+ for (i = 0; i < GIMP_PIXPIPE_MAXDIM; i++)
+ g_free (params->selection[i]);
+}
diff --git a/libgimpbase/gimpparasiteio.h b/libgimpbase/gimpparasiteio.h
new file mode 100644
index 0000000..9024b48
--- /dev/null
+++ b/libgimpbase/gimpparasiteio.h
@@ -0,0 +1,92 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpparasiteio.h
+ * Copyright (C) 1999 Tor Lillqvist <tml@iki.fi>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_PARASITE_IO_H__
+#define __GIMP_PARASITE_IO_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+/* Data structures for various standard parasites used by plug-ins and
+ * the GIMP core, and functions to build and parse their string
+ * representations.
+ */
+
+/*
+ * Pixmap brush pipes.
+ */
+
+#define GIMP_PIXPIPE_MAXDIM 4
+
+typedef struct _GimpPixPipeParams GimpPixPipeParams;
+
+/**
+ * GimpPixPipeParams:
+ * @step: Step
+ * @ncells: Number of cells
+ * @dim: Dimension
+ * @cols: Columns
+ * @rows: Rows
+ * @cellwidth: Cell width
+ * @cellheight: Cell height
+ * @placement: Placement
+ * @free_placement_string: Unused, ignore
+ * @rank: Rank
+ * @selection: Selection
+ * @free_selection_string: Unused, ignore
+ *
+ * PLease somebody help documenting this.
+ **/
+struct _GimpPixPipeParams
+{
+ gint step;
+ gint ncells;
+ gint dim;
+ gint cols;
+ gint rows;
+ gint cellwidth;
+ gint cellheight;
+ gchar *placement;
+ gboolean free_placement_string;
+ gint rank[GIMP_PIXPIPE_MAXDIM];
+ gchar *selection[GIMP_PIXPIPE_MAXDIM];
+ /* this flag is now useless. All selection strings are allocated. */
+ gboolean free_selection_string;
+};
+
+/* Initialize with dummy values */
+void gimp_pixpipe_params_init (GimpPixPipeParams *params);
+
+/* Parse a string into a GimpPixPipeParams */
+void gimp_pixpipe_params_parse (const gchar *parameters,
+ GimpPixPipeParams *params);
+
+/* Build a string representation of GimpPixPipeParams */
+gchar * gimp_pixpipe_params_build (GimpPixPipeParams *params) G_GNUC_MALLOC;
+
+/* Free the internal values. It does not free the struct itself. */
+void gimp_pixpipe_params_free (GimpPixPipeParams *params);
+
+G_END_DECLS
+
+#endif /* __GIMP_PARASITE_IO_H__ */
diff --git a/libgimpbase/gimpprotocol.c b/libgimpbase/gimpprotocol.c
new file mode 100644
index 0000000..ded796b
--- /dev/null
+++ b/libgimpbase/gimpprotocol.c
@@ -0,0 +1,1934 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+
+#include "gimpbasetypes.h"
+
+#include "gimpparasite.h"
+#include "gimpprotocol.h"
+#include "gimpwire.h"
+
+
+static void _gp_quit_read (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+static void _gp_quit_write (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+static void _gp_quit_destroy (GimpWireMessage *msg);
+
+static void _gp_config_read (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+static void _gp_config_write (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+static void _gp_config_destroy (GimpWireMessage *msg);
+
+static void _gp_tile_req_read (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+static void _gp_tile_req_write (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+static void _gp_tile_req_destroy (GimpWireMessage *msg);
+
+static void _gp_tile_ack_read (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+static void _gp_tile_ack_write (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+static void _gp_tile_ack_destroy (GimpWireMessage *msg);
+
+static void _gp_tile_data_read (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+static void _gp_tile_data_write (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+static void _gp_tile_data_destroy (GimpWireMessage *msg);
+
+static void _gp_proc_run_read (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+static void _gp_proc_run_write (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+static void _gp_proc_run_destroy (GimpWireMessage *msg);
+
+static void _gp_proc_return_read (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+static void _gp_proc_return_write (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+static void _gp_proc_return_destroy (GimpWireMessage *msg);
+
+static void _gp_temp_proc_run_read (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+static void _gp_temp_proc_run_write (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+static void _gp_temp_proc_run_destroy (GimpWireMessage *msg);
+
+static void _gp_temp_proc_return_read (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+static void _gp_temp_proc_return_write (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+static void _gp_temp_proc_return_destroy (GimpWireMessage *msg);
+
+static void _gp_proc_install_read (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+static void _gp_proc_install_write (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+static void _gp_proc_install_destroy (GimpWireMessage *msg);
+
+static void _gp_proc_uninstall_read (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+static void _gp_proc_uninstall_write (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+static void _gp_proc_uninstall_destroy (GimpWireMessage *msg);
+
+static void _gp_extension_ack_read (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+static void _gp_extension_ack_write (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+static void _gp_extension_ack_destroy (GimpWireMessage *msg);
+
+static void _gp_params_read (GIOChannel *channel,
+ GPParam **params,
+ guint *nparams,
+ gpointer user_data);
+static void _gp_params_write (GIOChannel *channel,
+ GPParam *params,
+ gint nparams,
+ gpointer user_data);
+
+static void _gp_has_init_read (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+static void _gp_has_init_write (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+static void _gp_has_init_destroy (GimpWireMessage *msg);
+
+
+
+void
+gp_init (void)
+{
+ gimp_wire_register (GP_QUIT,
+ _gp_quit_read,
+ _gp_quit_write,
+ _gp_quit_destroy);
+ gimp_wire_register (GP_CONFIG,
+ _gp_config_read,
+ _gp_config_write,
+ _gp_config_destroy);
+ gimp_wire_register (GP_TILE_REQ,
+ _gp_tile_req_read,
+ _gp_tile_req_write,
+ _gp_tile_req_destroy);
+ gimp_wire_register (GP_TILE_ACK,
+ _gp_tile_ack_read,
+ _gp_tile_ack_write,
+ _gp_tile_ack_destroy);
+ gimp_wire_register (GP_TILE_DATA,
+ _gp_tile_data_read,
+ _gp_tile_data_write,
+ _gp_tile_data_destroy);
+ gimp_wire_register (GP_PROC_RUN,
+ _gp_proc_run_read,
+ _gp_proc_run_write,
+ _gp_proc_run_destroy);
+ gimp_wire_register (GP_PROC_RETURN,
+ _gp_proc_return_read,
+ _gp_proc_return_write,
+ _gp_proc_return_destroy);
+ gimp_wire_register (GP_TEMP_PROC_RUN,
+ _gp_temp_proc_run_read,
+ _gp_temp_proc_run_write,
+ _gp_temp_proc_run_destroy);
+ gimp_wire_register (GP_TEMP_PROC_RETURN,
+ _gp_temp_proc_return_read,
+ _gp_temp_proc_return_write,
+ _gp_temp_proc_return_destroy);
+ gimp_wire_register (GP_PROC_INSTALL,
+ _gp_proc_install_read,
+ _gp_proc_install_write,
+ _gp_proc_install_destroy);
+ gimp_wire_register (GP_PROC_UNINSTALL,
+ _gp_proc_uninstall_read,
+ _gp_proc_uninstall_write,
+ _gp_proc_uninstall_destroy);
+ gimp_wire_register (GP_EXTENSION_ACK,
+ _gp_extension_ack_read,
+ _gp_extension_ack_write,
+ _gp_extension_ack_destroy);
+ gimp_wire_register (GP_HAS_INIT,
+ _gp_has_init_read,
+ _gp_has_init_write,
+ _gp_has_init_destroy);
+}
+
+gboolean
+gp_quit_write (GIOChannel *channel,
+ gpointer user_data)
+{
+ GimpWireMessage msg;
+
+ msg.type = GP_QUIT;
+ msg.data = NULL;
+
+ if (! gimp_wire_write_msg (channel, &msg, user_data))
+ return FALSE;
+
+ if (! gimp_wire_flush (channel, user_data))
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean
+gp_config_write (GIOChannel *channel,
+ GPConfig *config,
+ gpointer user_data)
+{
+ GimpWireMessage msg;
+
+ msg.type = GP_CONFIG;
+ msg.data = config;
+
+ if (! gimp_wire_write_msg (channel, &msg, user_data))
+ return FALSE;
+
+ if (! gimp_wire_flush (channel, user_data))
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean
+gp_tile_req_write (GIOChannel *channel,
+ GPTileReq *tile_req,
+ gpointer user_data)
+{
+ GimpWireMessage msg;
+
+ msg.type = GP_TILE_REQ;
+ msg.data = tile_req;
+
+ if (! gimp_wire_write_msg (channel, &msg, user_data))
+ return FALSE;
+
+ if (! gimp_wire_flush (channel, user_data))
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean
+gp_tile_ack_write (GIOChannel *channel,
+ gpointer user_data)
+{
+ GimpWireMessage msg;
+
+ msg.type = GP_TILE_ACK;
+ msg.data = NULL;
+
+ if (! gimp_wire_write_msg (channel, &msg, user_data))
+ return FALSE;
+
+ if (! gimp_wire_flush (channel, user_data))
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean
+gp_tile_data_write (GIOChannel *channel,
+ GPTileData *tile_data,
+ gpointer user_data)
+{
+ GimpWireMessage msg;
+
+ msg.type = GP_TILE_DATA;
+ msg.data = tile_data;
+
+ if (! gimp_wire_write_msg (channel, &msg, user_data))
+ return FALSE;
+
+ if (! gimp_wire_flush (channel, user_data))
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean
+gp_proc_run_write (GIOChannel *channel,
+ GPProcRun *proc_run,
+ gpointer user_data)
+{
+ GimpWireMessage msg;
+
+ msg.type = GP_PROC_RUN;
+ msg.data = proc_run;
+
+ if (! gimp_wire_write_msg (channel, &msg, user_data))
+ return FALSE;
+
+ if (! gimp_wire_flush (channel, user_data))
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean
+gp_proc_return_write (GIOChannel *channel,
+ GPProcReturn *proc_return,
+ gpointer user_data)
+{
+ GimpWireMessage msg;
+
+ msg.type = GP_PROC_RETURN;
+ msg.data = proc_return;
+
+ if (! gimp_wire_write_msg (channel, &msg, user_data))
+ return FALSE;
+
+ if (! gimp_wire_flush (channel, user_data))
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean
+gp_temp_proc_run_write (GIOChannel *channel,
+ GPProcRun *proc_run,
+ gpointer user_data)
+{
+ GimpWireMessage msg;
+
+ msg.type = GP_TEMP_PROC_RUN;
+ msg.data = proc_run;
+
+ if (! gimp_wire_write_msg (channel, &msg, user_data))
+ return FALSE;
+
+ if (! gimp_wire_flush (channel, user_data))
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean
+gp_temp_proc_return_write (GIOChannel *channel,
+ GPProcReturn *proc_return,
+ gpointer user_data)
+{
+ GimpWireMessage msg;
+
+ msg.type = GP_TEMP_PROC_RETURN;
+ msg.data = proc_return;
+
+ if (! gimp_wire_write_msg (channel, &msg, user_data))
+ return FALSE;
+
+ if (! gimp_wire_flush (channel, user_data))
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean
+gp_proc_install_write (GIOChannel *channel,
+ GPProcInstall *proc_install,
+ gpointer user_data)
+{
+ GimpWireMessage msg;
+
+ msg.type = GP_PROC_INSTALL;
+ msg.data = proc_install;
+
+ if (! gimp_wire_write_msg (channel, &msg, user_data))
+ return FALSE;
+
+ if (! gimp_wire_flush (channel, user_data))
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean
+gp_proc_uninstall_write (GIOChannel *channel,
+ GPProcUninstall *proc_uninstall,
+ gpointer user_data)
+{
+ GimpWireMessage msg;
+
+ msg.type = GP_PROC_UNINSTALL;
+ msg.data = proc_uninstall;
+
+ if (! gimp_wire_write_msg (channel, &msg, user_data))
+ return FALSE;
+
+ if (! gimp_wire_flush (channel, user_data))
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean
+gp_extension_ack_write (GIOChannel *channel,
+ gpointer user_data)
+{
+ GimpWireMessage msg;
+
+ msg.type = GP_EXTENSION_ACK;
+ msg.data = NULL;
+
+ if (! gimp_wire_write_msg (channel, &msg, user_data))
+ return FALSE;
+
+ if (! gimp_wire_flush (channel, user_data))
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean
+gp_has_init_write (GIOChannel *channel,
+ gpointer user_data)
+{
+ GimpWireMessage msg;
+
+ msg.type = GP_HAS_INIT;
+ msg.data = NULL;
+
+ if (! gimp_wire_write_msg (channel, &msg, user_data))
+ return FALSE;
+
+ if (! gimp_wire_flush (channel, user_data))
+ return FALSE;
+
+ return TRUE;
+}
+
+/* quit */
+
+static void
+_gp_quit_read (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data)
+{
+}
+
+static void
+_gp_quit_write (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data)
+{
+}
+
+static void
+_gp_quit_destroy (GimpWireMessage *msg)
+{
+}
+
+/* config */
+
+static void
+_gp_config_read (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data)
+{
+ GPConfig *config = g_slice_new0 (GPConfig);
+
+ if (! _gimp_wire_read_int32 (channel,
+ &config->version, 1, user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_int32 (channel,
+ &config->tile_width, 1, user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_int32 (channel,
+ &config->tile_height, 1, user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_int32 (channel,
+ (guint32 *) &config->shm_ID, 1, user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_int8 (channel,
+ (guint8 *) &config->check_size, 1, user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_int8 (channel,
+ (guint8 *) &config->check_type, 1, user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_int8 (channel,
+ (guint8 *) &config->show_help_button, 1,
+ user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_int8 (channel,
+ (guint8 *) &config->use_cpu_accel, 1,
+ user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_int8 (channel,
+ (guint8 *) &config->use_opencl, 1,
+ user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_int8 (channel,
+ (guint8 *) &config->export_exif, 1,
+ user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_int8 (channel,
+ (guint8 *) &config->export_xmp, 1,
+ user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_int8 (channel,
+ (guint8 *) &config->export_iptc, 1,
+ user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_int8 (channel,
+ (guint8 *) &config->export_profile, 1,
+ user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_int8 (channel,
+ (guint8 *) &config->show_tooltips, 1, user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_int32 (channel,
+ (guint32 *) &config->min_colors, 1, user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_int32 (channel,
+ (guint32 *) &config->gdisp_ID, 1, user_data))
+ goto cleanup;
+
+ if (! _gimp_wire_read_string (channel,
+ &config->app_name, 1, user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_string (channel,
+ &config->wm_class, 1, user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_string (channel,
+ &config->display_name, 1, user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_int32 (channel,
+ (guint32 *) &config->monitor_number, 1,
+ user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_int32 (channel,
+ &config->timestamp, 1, user_data))
+ goto cleanup;
+
+ if (config->version < 0x0017)
+ goto end;
+
+ if (! _gimp_wire_read_string (channel,
+ &config->icon_theme_dir, 1, user_data))
+ goto cleanup;
+
+ if (config->version < 0x0019)
+ goto end;
+
+ if (! _gimp_wire_read_int64 (channel,
+ &config->tile_cache_size, 1, user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_string (channel,
+ &config->swap_path, 1, user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_int32 (channel,
+ (guint32 *) &config->num_processors, 1,
+ user_data))
+ goto cleanup;
+
+ if (config->version < 0x001A)
+ goto end;
+
+ if (! _gimp_wire_read_string (channel,
+ &config->swap_compression, 1, user_data))
+ goto cleanup;
+
+ end:
+ msg->data = config;
+ return;
+
+ cleanup:
+ g_free (config->app_name);
+ g_free (config->wm_class);
+ g_free (config->display_name);
+ g_free (config->icon_theme_dir);
+ g_free (config->swap_path);
+ g_free (config->swap_compression);
+ g_slice_free (GPConfig, config);
+}
+
+static void
+_gp_config_write (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data)
+{
+ GPConfig *config = msg->data;
+
+ if (! _gimp_wire_write_int32 (channel,
+ &config->version, 1, user_data))
+ return;
+ if (! _gimp_wire_write_int32 (channel,
+ &config->tile_width, 1, user_data))
+ return;
+ if (! _gimp_wire_write_int32 (channel,
+ &config->tile_height, 1, user_data))
+ return;
+ if (! _gimp_wire_write_int32 (channel,
+ (const guint32 *) &config->shm_ID, 1,
+ user_data))
+ return;
+ if (! _gimp_wire_write_int8 (channel,
+ (const guint8 *) &config->check_size, 1,
+ user_data))
+ return;
+ if (! _gimp_wire_write_int8 (channel,
+ (const guint8 *) &config->check_type, 1,
+ user_data))
+ return;
+ if (! _gimp_wire_write_int8 (channel,
+ (const guint8 *) &config->show_help_button, 1,
+ user_data))
+ return;
+ if (! _gimp_wire_write_int8 (channel,
+ (const guint8 *) &config->use_cpu_accel, 1,
+ user_data))
+ return;
+ if (! _gimp_wire_write_int8 (channel,
+ (const guint8 *) &config->use_opencl, 1,
+ user_data))
+ return;
+ if (! _gimp_wire_write_int8 (channel,
+ (const guint8 *) &config->export_exif, 1,
+ user_data))
+ return;
+ if (! _gimp_wire_write_int8 (channel,
+ (const guint8 *) &config->export_xmp, 1,
+ user_data))
+ return;
+ if (! _gimp_wire_write_int8 (channel,
+ (const guint8 *) &config->export_iptc, 1,
+ user_data))
+ return;
+ if (! _gimp_wire_write_int8 (channel,
+ (const guint8 *) &config->export_profile, 1,
+ user_data))
+ return;
+ if (! _gimp_wire_write_int8 (channel,
+ (const guint8 *) &config->show_tooltips, 1,
+ user_data))
+ return;
+ if (! _gimp_wire_write_int32 (channel,
+ (const guint32 *) &config->min_colors, 1,
+ user_data))
+ return;
+ if (! _gimp_wire_write_int32 (channel,
+ (const guint32 *) &config->gdisp_ID, 1,
+ user_data))
+ return;
+ if (! _gimp_wire_write_string (channel,
+ &config->app_name, 1, user_data))
+ return;
+ if (! _gimp_wire_write_string (channel,
+ &config->wm_class, 1, user_data))
+ return;
+ if (! _gimp_wire_write_string (channel,
+ &config->display_name, 1, user_data))
+ return;
+ if (! _gimp_wire_write_int32 (channel,
+ (const guint32 *) &config->monitor_number, 1,
+ user_data))
+ return;
+ if (! _gimp_wire_write_int32 (channel,
+ (const guint32 *) &config->timestamp, 1,
+ user_data))
+ return;
+
+ if (config->version < 0x0017)
+ return;
+
+ if (! _gimp_wire_write_string (channel,
+ &config->icon_theme_dir, 1, user_data))
+ return;
+
+ if (config->version < 0x0019)
+ return;
+
+ if (! _gimp_wire_write_int64 (channel,
+ &config->tile_cache_size, 1, user_data))
+ return;
+ if (! _gimp_wire_write_string (channel,
+ &config->swap_path, 1, user_data))
+ return;
+ if (! _gimp_wire_write_int32 (channel,
+ (const guint32 *) &config->num_processors, 1,
+ user_data))
+ return;
+
+ if (config->version < 0x001A)
+ return;
+
+ if (! _gimp_wire_write_string (channel,
+ &config->swap_compression, 1, user_data))
+ return;
+}
+
+static void
+_gp_config_destroy (GimpWireMessage *msg)
+{
+ GPConfig *config = msg->data;
+
+ if (config)
+ {
+ g_free (config->app_name);
+ g_free (config->wm_class);
+ g_free (config->display_name);
+ g_free (config->icon_theme_dir);
+ g_free (config->swap_path);
+ g_free (config->swap_compression);
+ g_slice_free (GPConfig, config);
+ }
+}
+
+/* tile_req */
+
+static void
+_gp_tile_req_read (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data)
+{
+ GPTileReq *tile_req = g_slice_new0 (GPTileReq);
+
+ if (! _gimp_wire_read_int32 (channel,
+ (guint32 *) &tile_req->drawable_ID, 1,
+ user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_int32 (channel,
+ &tile_req->tile_num, 1, user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_int32 (channel,
+ &tile_req->shadow, 1, user_data))
+ goto cleanup;
+
+ msg->data = tile_req;
+ return;
+
+ cleanup:
+ g_slice_free (GPTileReq, tile_req);
+ msg->data = NULL;
+}
+
+static void
+_gp_tile_req_write (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data)
+{
+ GPTileReq *tile_req = msg->data;
+
+ if (! _gimp_wire_write_int32 (channel,
+ (const guint32 *) &tile_req->drawable_ID, 1,
+ user_data))
+ return;
+ if (! _gimp_wire_write_int32 (channel,
+ &tile_req->tile_num, 1, user_data))
+ return;
+ if (! _gimp_wire_write_int32 (channel,
+ &tile_req->shadow, 1, user_data))
+ return;
+}
+
+static void
+_gp_tile_req_destroy (GimpWireMessage *msg)
+{
+ GPTileReq *tile_req = msg->data;
+
+ if (tile_req)
+ g_slice_free (GPTileReq, msg->data);
+}
+
+/* tile_ack */
+
+static void
+_gp_tile_ack_read (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data)
+{
+}
+
+static void
+_gp_tile_ack_write (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data)
+{
+}
+
+static void
+_gp_tile_ack_destroy (GimpWireMessage *msg)
+{
+}
+
+/* tile_data */
+
+static void
+_gp_tile_data_read (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data)
+{
+ GPTileData *tile_data = g_slice_new0 (GPTileData);
+
+ if (! _gimp_wire_read_int32 (channel,
+ (guint32 *) &tile_data->drawable_ID, 1,
+ user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_int32 (channel,
+ &tile_data->tile_num, 1, user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_int32 (channel,
+ &tile_data->shadow, 1, user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_int32 (channel,
+ &tile_data->bpp, 1, user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_int32 (channel,
+ &tile_data->width, 1, user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_int32 (channel,
+ &tile_data->height, 1, user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_int32 (channel,
+ &tile_data->use_shm, 1, user_data))
+ goto cleanup;
+
+ if (!tile_data->use_shm)
+ {
+ guint length = tile_data->width * tile_data->height * tile_data->bpp;
+
+ tile_data->data = g_new (guchar, length);
+
+ if (! _gimp_wire_read_int8 (channel,
+ (guint8 *) tile_data->data, length,
+ user_data))
+ goto cleanup;
+ }
+
+ msg->data = tile_data;
+ return;
+
+ cleanup:
+ g_free (tile_data->data);
+ g_slice_free (GPTileData, tile_data);
+ msg->data = NULL;
+}
+
+static void
+_gp_tile_data_write (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data)
+{
+ GPTileData *tile_data = msg->data;
+
+ if (! _gimp_wire_write_int32 (channel,
+ (const guint32 *) &tile_data->drawable_ID, 1,
+ user_data))
+ return;
+ if (! _gimp_wire_write_int32 (channel,
+ &tile_data->tile_num, 1, user_data))
+ return;
+ if (! _gimp_wire_write_int32 (channel,
+ &tile_data->shadow, 1, user_data))
+ return;
+ if (! _gimp_wire_write_int32 (channel,
+ &tile_data->bpp, 1, user_data))
+ return;
+ if (! _gimp_wire_write_int32 (channel,
+ &tile_data->width, 1, user_data))
+ return;
+ if (! _gimp_wire_write_int32 (channel,
+ &tile_data->height, 1, user_data))
+ return;
+ if (! _gimp_wire_write_int32 (channel,
+ &tile_data->use_shm, 1, user_data))
+ return;
+
+ if (!tile_data->use_shm)
+ {
+ guint length = tile_data->width * tile_data->height * tile_data->bpp;
+
+ if (! _gimp_wire_write_int8 (channel,
+ (const guint8 *) tile_data->data, length,
+ user_data))
+ return;
+ }
+}
+
+static void
+_gp_tile_data_destroy (GimpWireMessage *msg)
+{
+ GPTileData *tile_data = msg->data;
+
+ if (tile_data)
+ {
+ if (tile_data->data)
+ {
+ g_free (tile_data->data);
+ tile_data->data = NULL;
+ }
+
+ g_slice_free (GPTileData, tile_data);
+ }
+}
+
+/* proc_run */
+
+static void
+_gp_proc_run_read (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data)
+{
+ GPProcRun *proc_run = g_slice_new0 (GPProcRun);
+
+ if (! _gimp_wire_read_string (channel, &proc_run->name, 1, user_data))
+ goto cleanup;
+
+ _gp_params_read (channel,
+ &proc_run->params, (guint *) &proc_run->nparams,
+ user_data);
+
+ msg->data = proc_run;
+ return;
+
+ cleanup:
+ g_slice_free (GPProcRun, proc_run);
+ msg->data = NULL;
+}
+
+static void
+_gp_proc_run_write (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data)
+{
+ GPProcRun *proc_run = msg->data;
+
+ if (! _gimp_wire_write_string (channel, &proc_run->name, 1, user_data))
+ return;
+
+ _gp_params_write (channel, proc_run->params, proc_run->nparams, user_data);
+}
+
+static void
+_gp_proc_run_destroy (GimpWireMessage *msg)
+{
+ GPProcRun *proc_run = msg->data;
+
+ if (proc_run)
+ {
+ gp_params_destroy (proc_run->params, proc_run->nparams);
+
+ g_free (proc_run->name);
+ g_slice_free (GPProcRun, proc_run);
+ }
+}
+
+/* proc_return */
+
+static void
+_gp_proc_return_read (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data)
+{
+ GPProcReturn *proc_return = g_slice_new0 (GPProcReturn);
+
+ if (! _gimp_wire_read_string (channel, &proc_return->name, 1, user_data))
+ goto cleanup;
+
+ _gp_params_read (channel,
+ &proc_return->params, (guint *) &proc_return->nparams,
+ user_data);
+
+ msg->data = proc_return;
+ return;
+
+ cleanup:
+ g_slice_free (GPProcReturn, proc_return);
+ msg->data = NULL;
+}
+
+static void
+_gp_proc_return_write (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data)
+{
+ GPProcReturn *proc_return = msg->data;
+
+ if (! _gimp_wire_write_string (channel, &proc_return->name, 1, user_data))
+ return;
+
+ _gp_params_write (channel,
+ proc_return->params, proc_return->nparams, user_data);
+}
+
+static void
+_gp_proc_return_destroy (GimpWireMessage *msg)
+{
+ GPProcReturn *proc_return = msg->data;
+
+ if (proc_return)
+ {
+ gp_params_destroy (proc_return->params, proc_return->nparams);
+
+ g_free (proc_return->name);
+ g_slice_free (GPProcReturn, proc_return);
+ }
+}
+
+/* temp_proc_run */
+
+static void
+_gp_temp_proc_run_read (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data)
+{
+ _gp_proc_run_read (channel, msg, user_data);
+}
+
+static void
+_gp_temp_proc_run_write (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data)
+{
+ _gp_proc_run_write (channel, msg, user_data);
+}
+
+static void
+_gp_temp_proc_run_destroy (GimpWireMessage *msg)
+{
+ _gp_proc_run_destroy (msg);
+}
+
+/* temp_proc_return */
+
+static void
+_gp_temp_proc_return_read (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data)
+{
+ _gp_proc_return_read (channel, msg, user_data);
+}
+
+static void
+_gp_temp_proc_return_write (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data)
+{
+ _gp_proc_return_write (channel, msg, user_data);
+}
+
+static void
+_gp_temp_proc_return_destroy (GimpWireMessage *msg)
+{
+ _gp_proc_return_destroy (msg);
+}
+
+/* proc_install */
+
+static void
+_gp_proc_install_read (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data)
+{
+ GPProcInstall *proc_install = g_slice_new0 (GPProcInstall);
+ gint i;
+
+ if (! _gimp_wire_read_string (channel,
+ &proc_install->name, 1, user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_string (channel,
+ &proc_install->blurb, 1, user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_string (channel,
+ &proc_install->help, 1, user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_string (channel,
+ &proc_install->author, 1, user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_string (channel,
+ &proc_install->copyright, 1, user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_string (channel,
+ &proc_install->date, 1, user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_string (channel,
+ &proc_install->menu_path, 1, user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_string (channel,
+ &proc_install->image_types, 1, user_data))
+ goto cleanup;
+
+ if (! _gimp_wire_read_int32 (channel,
+ &proc_install->type, 1, user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_int32 (channel,
+ &proc_install->nparams, 1, user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_int32 (channel,
+ &proc_install->nreturn_vals, 1, user_data))
+ goto cleanup;
+
+ proc_install->params = g_new0 (GPParamDef, proc_install->nparams);
+
+ for (i = 0; i < proc_install->nparams; i++)
+ {
+ if (! _gimp_wire_read_int32 (channel,
+ (guint32 *) &proc_install->params[i].type, 1,
+ user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_string (channel,
+ &proc_install->params[i].name, 1,
+ user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_string (channel,
+ &proc_install->params[i].description, 1,
+ user_data))
+ goto cleanup;
+ }
+
+ proc_install->return_vals = g_new0 (GPParamDef, proc_install->nreturn_vals);
+
+ for (i = 0; i < proc_install->nreturn_vals; i++)
+ {
+ if (! _gimp_wire_read_int32 (channel,
+ (guint32 *) &proc_install->return_vals[i].type, 1,
+ user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_string (channel,
+ &proc_install->return_vals[i].name, 1,
+ user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_string (channel,
+ &proc_install->return_vals[i].description, 1,
+ user_data))
+ goto cleanup;
+ }
+
+ msg->data = proc_install;
+ return;
+
+ cleanup:
+ g_free (proc_install->name);
+ g_free (proc_install->blurb);
+ g_free (proc_install->help);
+ g_free (proc_install->author);
+ g_free (proc_install->copyright);
+ g_free (proc_install->date);
+ g_free (proc_install->menu_path);
+ g_free (proc_install->image_types);
+
+ if (proc_install->params)
+ {
+ for (i = 0; i < proc_install->nparams; i++)
+ {
+ if (!proc_install->params[i].name)
+ break;
+
+ g_free (proc_install->params[i].name);
+ g_free (proc_install->params[i].description);
+ }
+
+ g_free (proc_install->params);
+ }
+
+ if (proc_install->return_vals)
+ {
+ for (i = 0; i < proc_install->nreturn_vals; i++)
+ {
+ if (!proc_install->return_vals[i].name)
+ break;
+
+ g_free (proc_install->return_vals[i].name);
+ g_free (proc_install->return_vals[i].description);
+ }
+
+ g_free (proc_install->return_vals);
+ }
+
+ g_slice_free (GPProcInstall, proc_install);
+ msg->data = NULL;
+}
+
+static void
+_gp_proc_install_write (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data)
+{
+ GPProcInstall *proc_install = msg->data;
+ gint i;
+
+ if (! _gimp_wire_write_string (channel,
+ &proc_install->name, 1, user_data))
+ return;
+ if (! _gimp_wire_write_string (channel,
+ &proc_install->blurb, 1, user_data))
+ return;
+ if (! _gimp_wire_write_string (channel,
+ &proc_install->help, 1, user_data))
+ return;
+ if (! _gimp_wire_write_string (channel,
+ &proc_install->author, 1, user_data))
+ return;
+ if (! _gimp_wire_write_string (channel,
+ &proc_install->copyright, 1, user_data))
+ return;
+ if (! _gimp_wire_write_string (channel,
+ &proc_install->date, 1, user_data))
+ return;
+ if (! _gimp_wire_write_string (channel,
+ &proc_install->menu_path, 1, user_data))
+ return;
+ if (! _gimp_wire_write_string (channel,
+ &proc_install->image_types, 1, user_data))
+ return;
+
+ if (! _gimp_wire_write_int32 (channel,
+ &proc_install->type, 1, user_data))
+ return;
+ if (! _gimp_wire_write_int32 (channel,
+ &proc_install->nparams, 1, user_data))
+ return;
+ if (! _gimp_wire_write_int32 (channel,
+ &proc_install->nreturn_vals, 1, user_data))
+ return;
+
+ for (i = 0; i < proc_install->nparams; i++)
+ {
+ if (! _gimp_wire_write_int32 (channel,
+ (guint32 *) &proc_install->params[i].type, 1,
+ user_data))
+ return;
+ if (! _gimp_wire_write_string (channel,
+ &proc_install->params[i].name, 1,
+ user_data))
+ return;
+ if (! _gimp_wire_write_string (channel,
+ &proc_install->params[i].description, 1,
+ user_data))
+ return;
+ }
+
+ for (i = 0; i < proc_install->nreturn_vals; i++)
+ {
+ if (! _gimp_wire_write_int32 (channel,
+ (guint32 *) &proc_install->return_vals[i].type, 1,
+ user_data))
+ return;
+ if (! _gimp_wire_write_string (channel,
+ &proc_install->return_vals[i].name, 1,
+ user_data))
+ return;
+ if (! _gimp_wire_write_string (channel,
+ &proc_install->return_vals[i].description, 1,
+ user_data))
+ return;
+ }
+}
+
+static void
+_gp_proc_install_destroy (GimpWireMessage *msg)
+{
+ GPProcInstall *proc_install = msg->data;
+
+ if (proc_install)
+ {
+ gint i;
+
+ g_free (proc_install->name);
+ g_free (proc_install->blurb);
+ g_free (proc_install->help);
+ g_free (proc_install->author);
+ g_free (proc_install->copyright);
+ g_free (proc_install->date);
+ g_free (proc_install->menu_path);
+ g_free (proc_install->image_types);
+
+ for (i = 0; i < proc_install->nparams; i++)
+ {
+ g_free (proc_install->params[i].name);
+ g_free (proc_install->params[i].description);
+ }
+
+ for (i = 0; i < proc_install->nreturn_vals; i++)
+ {
+ g_free (proc_install->return_vals[i].name);
+ g_free (proc_install->return_vals[i].description);
+ }
+
+ g_free (proc_install->params);
+ g_free (proc_install->return_vals);
+ g_slice_free (GPProcInstall, proc_install);
+ }
+}
+
+/* proc_uninstall */
+
+static void
+_gp_proc_uninstall_read (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data)
+{
+ GPProcUninstall *proc_uninstall = g_slice_new0 (GPProcUninstall);
+
+ if (! _gimp_wire_read_string (channel, &proc_uninstall->name, 1, user_data))
+ goto cleanup;
+
+ msg->data = proc_uninstall;
+ return;
+
+ cleanup:
+ g_slice_free (GPProcUninstall, proc_uninstall);
+}
+
+static void
+_gp_proc_uninstall_write (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data)
+{
+ GPProcUninstall *proc_uninstall = msg->data;
+
+ if (! _gimp_wire_write_string (channel, &proc_uninstall->name, 1, user_data))
+ return;
+}
+
+static void
+_gp_proc_uninstall_destroy (GimpWireMessage *msg)
+{
+ GPProcUninstall *proc_uninstall = msg->data;
+
+ if (proc_uninstall)
+ {
+ g_free (proc_uninstall->name);
+ g_slice_free (GPProcUninstall, proc_uninstall);
+ }
+}
+
+/* extension_ack */
+
+static void
+_gp_extension_ack_read (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data)
+{
+}
+
+static void
+_gp_extension_ack_write (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data)
+{
+}
+
+static void
+_gp_extension_ack_destroy (GimpWireMessage *msg)
+{
+}
+
+/* params */
+
+static void
+_gp_params_read (GIOChannel *channel,
+ GPParam **params,
+ guint *nparams,
+ gpointer user_data)
+{
+ gint i, j;
+
+ if (! _gimp_wire_read_int32 (channel, (guint32 *) nparams, 1, user_data))
+ return;
+
+ if (*nparams == 0)
+ {
+ *params = NULL;
+ return;
+ }
+
+ *params = g_new0 (GPParam, *nparams);
+
+ for (i = 0; i < *nparams; i++)
+ {
+ if (! _gimp_wire_read_int32 (channel,
+ (guint32 *) &(*params)[i].type, 1,
+ user_data))
+ goto cleanup;
+
+ switch ((*params)[i].type)
+ {
+ case GIMP_PDB_INT32:
+ if (! _gimp_wire_read_int32 (channel,
+ (guint32 *) &(*params)[i].data.d_int32, 1,
+ user_data))
+ goto cleanup;
+ break;
+
+ case GIMP_PDB_INT16:
+ if (! _gimp_wire_read_int16 (channel,
+ (guint16 *) &(*params)[i].data.d_int16, 1,
+ user_data))
+ goto cleanup;
+ break;
+
+ case GIMP_PDB_INT8:
+ if (! _gimp_wire_read_int8 (channel,
+ &(*params)[i].data.d_int8, 1,
+ user_data))
+ goto cleanup;
+ break;
+
+ case GIMP_PDB_FLOAT:
+ if (! _gimp_wire_read_double (channel,
+ &(*params)[i].data.d_float, 1,
+ user_data))
+ goto cleanup;
+ break;
+
+ case GIMP_PDB_STRING:
+ if (! _gimp_wire_read_string (channel,
+ &(*params)[i].data.d_string, 1,
+ user_data))
+ goto cleanup;
+ break;
+
+ case GIMP_PDB_INT32ARRAY:
+ (*params)[i-1].data.d_int32 = MAX (0, (*params)[i-1].data.d_int32);
+ (*params)[i].data.d_int32array = g_new (gint32,
+ (*params)[i-1].data.d_int32);
+
+ if (! _gimp_wire_read_int32 (channel,
+ (guint32 *) (*params)[i].data.d_int32array,
+ (*params)[i-1].data.d_int32,
+ user_data))
+ {
+ g_free ((*params)[i].data.d_int32array);
+ goto cleanup;
+ }
+ break;
+
+ case GIMP_PDB_INT16ARRAY:
+ (*params)[i-1].data.d_int32 = MAX (0, (*params)[i-1].data.d_int32);
+ (*params)[i].data.d_int16array = g_new (gint16,
+ (*params)[i-1].data.d_int32);
+ if (! _gimp_wire_read_int16 (channel,
+ (guint16 *) (*params)[i].data.d_int16array,
+ (*params)[i-1].data.d_int32,
+ user_data))
+ {
+ g_free ((*params)[i].data.d_int16array);
+ goto cleanup;
+ }
+ break;
+
+ case GIMP_PDB_INT8ARRAY:
+ (*params)[i-1].data.d_int32 = MAX (0, (*params)[i-1].data.d_int32);
+ (*params)[i].data.d_int8array = g_new (guint8,
+ (*params)[i-1].data.d_int32);
+ if (! _gimp_wire_read_int8 (channel,
+ (*params)[i].data.d_int8array,
+ (*params)[i-1].data.d_int32,
+ user_data))
+ {
+ g_free ((*params)[i].data.d_int8array);
+ goto cleanup;
+ }
+ break;
+
+ case GIMP_PDB_FLOATARRAY:
+ (*params)[i-1].data.d_int32 = MAX (0, (*params)[i-1].data.d_int32);
+ (*params)[i].data.d_floatarray = g_new (gdouble,
+ (*params)[i-1].data.d_int32);
+ if (! _gimp_wire_read_double (channel,
+ (*params)[i].data.d_floatarray,
+ (*params)[i-1].data.d_int32,
+ user_data))
+ {
+ g_free ((*params)[i].data.d_floatarray);
+ goto cleanup;
+ }
+ break;
+
+ case GIMP_PDB_STRINGARRAY:
+ (*params)[i-1].data.d_int32 = MAX (0, (*params)[i-1].data.d_int32);
+ (*params)[i].data.d_stringarray = g_new0 (gchar *,
+ (*params)[i-1].data.d_int32);
+ if (! _gimp_wire_read_string (channel,
+ (*params)[i].data.d_stringarray,
+ (*params)[i-1].data.d_int32,
+ user_data))
+ {
+ for (j = 0; j < (*params)[i-1].data.d_int32; j++)
+ g_free (((*params)[i].data.d_stringarray)[j]);
+ g_free ((*params)[i].data.d_stringarray);
+ goto cleanup;
+ }
+ break;
+
+ case GIMP_PDB_COLOR:
+ if (! _gimp_wire_read_color (channel,
+ &(*params)[i].data.d_color, 1,
+ user_data))
+ goto cleanup;
+ break;
+
+ case GIMP_PDB_ITEM:
+ if (! _gimp_wire_read_int32 (channel,
+ (guint32 *) &(*params)[i].data.d_item, 1,
+ user_data))
+ goto cleanup;
+ break;
+
+ case GIMP_PDB_DISPLAY:
+ if (! _gimp_wire_read_int32 (channel,
+ (guint32 *) &(*params)[i].data.d_display, 1,
+ user_data))
+ goto cleanup;
+ break;
+
+ case GIMP_PDB_IMAGE:
+ if (! _gimp_wire_read_int32 (channel,
+ (guint32 *) &(*params)[i].data.d_image, 1,
+ user_data))
+ goto cleanup;
+ break;
+
+ case GIMP_PDB_LAYER:
+ if (! _gimp_wire_read_int32 (channel,
+ (guint32 *) &(*params)[i].data.d_layer, 1,
+ user_data))
+ goto cleanup;
+ break;
+
+ case GIMP_PDB_CHANNEL:
+ if (! _gimp_wire_read_int32 (channel,
+ (guint32 *) &(*params)[i].data.d_channel, 1,
+ user_data))
+ goto cleanup;
+ break;
+
+ case GIMP_PDB_DRAWABLE:
+ if (! _gimp_wire_read_int32 (channel,
+ (guint32 *) &(*params)[i].data.d_drawable, 1,
+ user_data))
+ goto cleanup;
+ break;
+
+ case GIMP_PDB_SELECTION:
+ if (! _gimp_wire_read_int32 (channel,
+ (guint32 *) &(*params)[i].data.d_selection, 1,
+ user_data))
+ goto cleanup;
+ break;
+
+ case GIMP_PDB_COLORARRAY:
+ (*params)[i].data.d_colorarray = g_new (GimpRGB,
+ (*params)[i-1].data.d_int32);
+ if (! _gimp_wire_read_color (channel,
+ (*params)[i].data.d_colorarray,
+ (*params)[i-1].data.d_int32,
+ user_data))
+ {
+ g_free ((*params)[i].data.d_colorarray);
+ goto cleanup;
+ }
+ break;
+
+ case GIMP_PDB_VECTORS:
+ if (! _gimp_wire_read_int32 (channel,
+ (guint32 *) &(*params)[i].data.d_vectors, 1,
+ user_data))
+ goto cleanup;
+ break;
+
+ case GIMP_PDB_PARASITE:
+ if (! _gimp_wire_read_string (channel,
+ &(*params)[i].data.d_parasite.name, 1,
+ user_data))
+ goto cleanup;
+ if ((*params)[i].data.d_parasite.name == NULL)
+ {
+ /* we have a null parasite */
+ (*params)[i].data.d_parasite.data = NULL;
+ break;
+ }
+ if (! _gimp_wire_read_int32 (channel,
+ &((*params)[i].data.d_parasite.flags), 1,
+ user_data))
+ goto cleanup;
+ if (! _gimp_wire_read_int32 (channel,
+ &((*params)[i].data.d_parasite.size), 1,
+ user_data))
+ goto cleanup;
+ if ((*params)[i].data.d_parasite.size > 0)
+ {
+ (*params)[i].data.d_parasite.data =
+ g_malloc ((*params)[i].data.d_parasite.size);
+ if (! _gimp_wire_read_int8 (channel,
+ (*params)[i].data.d_parasite.data,
+ (*params)[i].data.d_parasite.size,
+ user_data))
+ {
+ g_free ((*params)[i].data.d_parasite.data);
+ goto cleanup;
+ }
+ }
+ else
+ (*params)[i].data.d_parasite.data = NULL;
+ break;
+
+ case GIMP_PDB_STATUS:
+ if (! _gimp_wire_read_int32 (channel,
+ (guint32 *) &(*params)[i].data.d_status, 1,
+ user_data))
+ goto cleanup;
+ break;
+
+ case GIMP_PDB_END:
+ break;
+ }
+ }
+
+ return;
+
+ cleanup:
+ *nparams = 0;
+ g_free (*params);
+ *params = NULL;
+}
+
+static void
+_gp_params_write (GIOChannel *channel,
+ GPParam *params,
+ gint nparams,
+ gpointer user_data)
+{
+ gint i;
+
+ if (! _gimp_wire_write_int32 (channel,
+ (const guint32 *) &nparams, 1, user_data))
+ return;
+
+ for (i = 0; i < nparams; i++)
+ {
+ if (! _gimp_wire_write_int32 (channel,
+ (const guint32 *) &params[i].type, 1,
+ user_data))
+ return;
+
+ switch (params[i].type)
+ {
+ case GIMP_PDB_INT32:
+ if (! _gimp_wire_write_int32 (channel,
+ (const guint32 *) &params[i].data.d_int32, 1,
+ user_data))
+ return;
+ break;
+
+ case GIMP_PDB_INT16:
+ if (! _gimp_wire_write_int16 (channel,
+ (const guint16 *) &params[i].data.d_int16, 1,
+ user_data))
+ return;
+ break;
+
+ case GIMP_PDB_INT8:
+ if (! _gimp_wire_write_int8 (channel,
+ (const guint8 *) &params[i].data.d_int8, 1,
+ user_data))
+ return;
+ break;
+
+ case GIMP_PDB_FLOAT:
+ if (! _gimp_wire_write_double (channel,
+ &params[i].data.d_float, 1,
+ user_data))
+ return;
+ break;
+
+ case GIMP_PDB_STRING:
+ if (! _gimp_wire_write_string (channel,
+ &params[i].data.d_string, 1,
+ user_data))
+ return;
+ break;
+
+ case GIMP_PDB_INT32ARRAY:
+ if (! _gimp_wire_write_int32 (channel,
+ (const guint32 *) params[i].data.d_int32array,
+ params[i-1].data.d_int32,
+ user_data))
+ return;
+ break;
+
+ case GIMP_PDB_INT16ARRAY:
+ if (! _gimp_wire_write_int16 (channel,
+ (const guint16 *) params[i].data.d_int16array,
+ params[i-1].data.d_int32,
+ user_data))
+ return;
+ break;
+
+ case GIMP_PDB_INT8ARRAY:
+ if (! _gimp_wire_write_int8 (channel,
+ (const guint8 *) params[i].data.d_int8array,
+ params[i-1].data.d_int32,
+ user_data))
+ return;
+ break;
+
+ case GIMP_PDB_FLOATARRAY:
+ if (! _gimp_wire_write_double (channel,
+ params[i].data.d_floatarray,
+ params[i-1].data.d_int32,
+ user_data))
+ return;
+ break;
+
+ case GIMP_PDB_STRINGARRAY:
+ if (! _gimp_wire_write_string (channel,
+ params[i].data.d_stringarray,
+ params[i-1].data.d_int32,
+ user_data))
+ return;
+ break;
+
+ case GIMP_PDB_COLOR:
+ if (! _gimp_wire_write_color (channel,
+ &params[i].data.d_color, 1, user_data))
+ return;
+ break;
+
+ case GIMP_PDB_ITEM:
+ if (! _gimp_wire_write_int32 (channel,
+ (const guint32 *) &params[i].data.d_item, 1,
+ user_data))
+ return;
+ break;
+
+ case GIMP_PDB_DISPLAY:
+ if (! _gimp_wire_write_int32 (channel,
+ (const guint32 *) &params[i].data.d_display, 1,
+ user_data))
+ return;
+ break;
+
+ case GIMP_PDB_IMAGE:
+ if (! _gimp_wire_write_int32 (channel,
+ (const guint32 *) &params[i].data.d_image, 1,
+ user_data))
+ return;
+ break;
+
+ case GIMP_PDB_LAYER:
+ if (! _gimp_wire_write_int32 (channel,
+ (const guint32 *) &params[i].data.d_layer, 1,
+ user_data))
+ return;
+ break;
+
+ case GIMP_PDB_CHANNEL:
+ if (! _gimp_wire_write_int32 (channel,
+ (const guint32 *) &params[i].data.d_channel, 1,
+ user_data))
+ return;
+ break;
+
+ case GIMP_PDB_DRAWABLE:
+ if (! _gimp_wire_write_int32 (channel,
+ (const guint32 *) &params[i].data.d_drawable, 1,
+ user_data))
+ return;
+ break;
+
+ case GIMP_PDB_SELECTION:
+ if (! _gimp_wire_write_int32 (channel,
+ (const guint32 *) &params[i].data.d_selection, 1,
+ user_data))
+ return;
+ break;
+
+ case GIMP_PDB_COLORARRAY:
+ if (! _gimp_wire_write_color (channel,
+ params[i].data.d_colorarray,
+ params[i-1].data.d_int32,
+ user_data))
+ return;
+ break;
+
+ case GIMP_PDB_VECTORS:
+ if (! _gimp_wire_write_int32 (channel,
+ (const guint32 *) &params[i].data.d_vectors, 1,
+ user_data))
+ return;
+ break;
+
+ case GIMP_PDB_PARASITE:
+ {
+ GimpParasite *p = &params[i].data.d_parasite;
+
+ if (p->name == NULL)
+ {
+ /* write a null string to signal a null parasite */
+ _gimp_wire_write_string (channel, &p->name, 1, user_data);
+ break;
+ }
+
+ if (! _gimp_wire_write_string (channel, &p->name, 1, user_data))
+ return;
+ if (! _gimp_wire_write_int32 (channel, &p->flags, 1, user_data))
+ return;
+ if (! _gimp_wire_write_int32 (channel, &p->size, 1, user_data))
+ return;
+ if (p->size > 0)
+ {
+ if (! _gimp_wire_write_int8 (channel,
+ p->data, p->size, user_data))
+ return;
+ }
+ }
+ break;
+
+ case GIMP_PDB_STATUS:
+ if (! _gimp_wire_write_int32 (channel,
+ (const guint32 *) &params[i].data.d_status, 1,
+ user_data))
+ return;
+ break;
+
+ case GIMP_PDB_END:
+ break;
+ }
+ }
+}
+
+void
+gp_params_destroy (GPParam *params,
+ gint nparams)
+{
+ gint i;
+
+ for (i = 0; i < nparams; i++)
+ {
+ switch (params[i].type)
+ {
+ case GIMP_PDB_INT32:
+ case GIMP_PDB_INT16:
+ case GIMP_PDB_INT8:
+ case GIMP_PDB_FLOAT:
+ case GIMP_PDB_COLOR:
+ case GIMP_PDB_ITEM:
+ case GIMP_PDB_DISPLAY:
+ case GIMP_PDB_IMAGE:
+ case GIMP_PDB_LAYER:
+ case GIMP_PDB_CHANNEL:
+ case GIMP_PDB_DRAWABLE:
+ case GIMP_PDB_SELECTION:
+ case GIMP_PDB_VECTORS:
+ case GIMP_PDB_STATUS:
+ break;
+
+ case GIMP_PDB_STRING:
+ g_free (params[i].data.d_string);
+ break;
+
+ case GIMP_PDB_INT32ARRAY:
+ g_free (params[i].data.d_int32array);
+ break;
+
+ case GIMP_PDB_INT16ARRAY:
+ g_free (params[i].data.d_int16array);
+ break;
+
+ case GIMP_PDB_INT8ARRAY:
+ g_free (params[i].data.d_int8array);
+ break;
+
+ case GIMP_PDB_FLOATARRAY:
+ g_free (params[i].data.d_floatarray);
+ break;
+
+ case GIMP_PDB_STRINGARRAY:
+ if ((i > 0) && (params[i-1].type == GIMP_PDB_INT32))
+ {
+ gint count = params[i-1].data.d_int32;
+ gint j;
+
+ for (j = 0; j < count; j++)
+ g_free (params[i].data.d_stringarray[j]);
+
+ g_free (params[i].data.d_stringarray);
+ }
+ break;
+
+ case GIMP_PDB_COLORARRAY:
+ g_free (params[i].data.d_colorarray);
+ break;
+
+ case GIMP_PDB_PARASITE:
+ if (params[i].data.d_parasite.name)
+ g_free (params[i].data.d_parasite.name);
+ if (params[i].data.d_parasite.data)
+ g_free (params[i].data.d_parasite.data);
+ break;
+
+ case GIMP_PDB_END:
+ break;
+ }
+ }
+
+ g_free (params);
+}
+
+/* has_init */
+
+static void
+_gp_has_init_read (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data)
+{
+}
+
+static void
+_gp_has_init_write (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data)
+{
+}
+
+static void
+_gp_has_init_destroy (GimpWireMessage *msg)
+{
+}
diff --git a/libgimpbase/gimpprotocol.h b/libgimpbase/gimpprotocol.h
new file mode 100644
index 0000000..991abed
--- /dev/null
+++ b/libgimpbase/gimpprotocol.h
@@ -0,0 +1,245 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_PROTOCOL_H__
+#define __GIMP_PROTOCOL_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+/* Increment every time the protocol changes
+ */
+#define GIMP_PROTOCOL_VERSION 0x001A
+
+
+enum
+{
+ GP_QUIT,
+ GP_CONFIG,
+ GP_TILE_REQ,
+ GP_TILE_ACK,
+ GP_TILE_DATA,
+ GP_PROC_RUN,
+ GP_PROC_RETURN,
+ GP_TEMP_PROC_RUN,
+ GP_TEMP_PROC_RETURN,
+ GP_PROC_INSTALL,
+ GP_PROC_UNINSTALL,
+ GP_EXTENSION_ACK,
+ GP_HAS_INIT
+};
+
+
+typedef struct _GPConfig GPConfig;
+typedef struct _GPTileReq GPTileReq;
+typedef struct _GPTileAck GPTileAck;
+typedef struct _GPTileData GPTileData;
+typedef struct _GPParam GPParam;
+typedef struct _GPParamDef GPParamDef;
+typedef struct _GPProcRun GPProcRun;
+typedef struct _GPProcReturn GPProcReturn;
+typedef struct _GPProcInstall GPProcInstall;
+typedef struct _GPProcUninstall GPProcUninstall;
+
+
+struct _GPConfig
+{
+ guint32 version;
+ guint32 tile_width;
+ guint32 tile_height;
+ gint32 shm_ID;
+ gint8 check_size;
+ gint8 check_type;
+ gint8 show_help_button;
+ gint8 use_cpu_accel;
+ gint8 use_opencl;
+ gint8 export_exif;
+ gint8 export_xmp;
+ gint8 export_iptc;
+ gint8 export_profile;
+ gint8 show_tooltips;
+ gint32 min_colors;
+ gint32 gdisp_ID;
+ gchar *app_name;
+ gchar *wm_class;
+ gchar *display_name;
+ gint32 monitor_number;
+ guint32 timestamp;
+
+ /* since protocol version 0x0017: */
+ gchar *icon_theme_dir;
+
+ /* since protocol version 0x0019: */
+ guint64 tile_cache_size;
+ gchar *swap_path;
+ gint32 num_processors;
+
+ /* since protocol version 0x001a: */
+ gchar *swap_compression;
+};
+
+struct _GPTileReq
+{
+ gint32 drawable_ID;
+ guint32 tile_num;
+ guint32 shadow;
+};
+
+struct _GPTileData
+{
+ gint32 drawable_ID;
+ guint32 tile_num;
+ guint32 shadow;
+ guint32 bpp;
+ guint32 width;
+ guint32 height;
+ guint32 use_shm;
+ guchar *data;
+};
+
+struct _GPParam
+{
+ guint32 type;
+
+ union
+ {
+ gint32 d_int32;
+ gint16 d_int16;
+ guint8 d_int8;
+ gdouble d_float;
+ gchar *d_string;
+ gint32 *d_int32array;
+ gint16 *d_int16array;
+ guint8 *d_int8array;
+ gdouble *d_floatarray;
+ gchar **d_stringarray;
+ GimpRGB *d_colorarray;
+ GimpRGB d_color;
+ struct
+ {
+ gint32 x;
+ gint32 y;
+ gint32 width;
+ gint32 height;
+ } d_region; /* deprecated */
+ gint32 d_display;
+ gint32 d_image;
+ gint32 d_item;
+ gint32 d_layer;
+ gint32 d_channel;
+ gint32 d_drawable;
+ gint32 d_selection;
+ gint32 d_boundary;
+ gint32 d_path; /* deprecated */
+ gint32 d_vectors;
+ gint32 d_status;
+ GimpParasite d_parasite;
+ } data;
+};
+
+struct _GPParamDef
+{
+ guint32 type;
+ gchar *name;
+ gchar *description;
+};
+
+struct _GPProcRun
+{
+ gchar *name;
+ guint32 nparams;
+ GPParam *params;
+};
+
+struct _GPProcReturn
+{
+ gchar *name;
+ guint32 nparams;
+ GPParam *params;
+};
+
+struct _GPProcInstall
+{
+ gchar *name;
+ gchar *blurb;
+ gchar *help;
+ gchar *author;
+ gchar *copyright;
+ gchar *date;
+ gchar *menu_path;
+ gchar *image_types;
+ guint32 type;
+ guint32 nparams;
+ guint32 nreturn_vals;
+ GPParamDef *params;
+ GPParamDef *return_vals;
+};
+
+struct _GPProcUninstall
+{
+ gchar *name;
+};
+
+
+void gp_init (void);
+
+gboolean gp_quit_write (GIOChannel *channel,
+ gpointer user_data);
+gboolean gp_config_write (GIOChannel *channel,
+ GPConfig *config,
+ gpointer user_data);
+gboolean gp_tile_req_write (GIOChannel *channel,
+ GPTileReq *tile_req,
+ gpointer user_data);
+gboolean gp_tile_ack_write (GIOChannel *channel,
+ gpointer user_data);
+gboolean gp_tile_data_write (GIOChannel *channel,
+ GPTileData *tile_data,
+ gpointer user_data);
+gboolean gp_proc_run_write (GIOChannel *channel,
+ GPProcRun *proc_run,
+ gpointer user_data);
+gboolean gp_proc_return_write (GIOChannel *channel,
+ GPProcReturn *proc_return,
+ gpointer user_data);
+gboolean gp_temp_proc_run_write (GIOChannel *channel,
+ GPProcRun *proc_run,
+ gpointer user_data);
+gboolean gp_temp_proc_return_write (GIOChannel *channel,
+ GPProcReturn *proc_return,
+ gpointer user_data);
+gboolean gp_proc_install_write (GIOChannel *channel,
+ GPProcInstall *proc_install,
+ gpointer user_data);
+gboolean gp_proc_uninstall_write (GIOChannel *channel,
+ GPProcUninstall *proc_uninstall,
+ gpointer user_data);
+gboolean gp_extension_ack_write (GIOChannel *channel,
+ gpointer user_data);
+gboolean gp_has_init_write (GIOChannel *channel,
+ gpointer user_data);
+
+void gp_params_destroy (GPParam *params,
+ gint nparams);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PROTOCOL_H__ */
diff --git a/libgimpbase/gimprectangle.c b/libgimpbase/gimprectangle.c
new file mode 100644
index 0000000..678ff82
--- /dev/null
+++ b/libgimpbase/gimprectangle.c
@@ -0,0 +1,133 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimprectangle.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+
+#include "gimprectangle.h"
+
+
+/**
+ * SECTION: gimprectangle
+ * @title: gimprectangle
+ * @short_description: Utility functions dealing with rectangle extents.
+ *
+ * Utility functions dealing with rectangle extents.
+ **/
+
+
+/**
+ * gimp_rectangle_intersect:
+ * @x1: origin of first rectangle
+ * @y1: origin of first rectangle
+ * @width1: width of first rectangle
+ * @height1: height of first rectangle
+ * @x2: origin of second rectangle
+ * @y2: origin of second rectangle
+ * @width2: width of second rectangle
+ * @height2: height of second rectangle
+ * @dest_x: return location for origin of intersection (may be %NULL)
+ * @dest_y: return location for origin of intersection (may be %NULL)
+ * @dest_width: return location for width of intersection (may be %NULL)
+ * @dest_height: return location for height of intersection (may be %NULL)
+ *
+ * Calculates the intersection of two rectangles.
+ *
+ * Return value: %TRUE if the intersection is non-empty, %FALSE otherwise
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_rectangle_intersect (gint x1,
+ gint y1,
+ gint width1,
+ gint height1,
+ gint x2,
+ gint y2,
+ gint width2,
+ gint height2,
+ gint *dest_x,
+ gint *dest_y,
+ gint *dest_width,
+ gint *dest_height)
+{
+ gint d_x, d_y;
+ gint d_w, d_h;
+
+ d_x = MAX (x1, x2);
+ d_y = MAX (y1, y2);
+ d_w = MIN (x1 + width1, x2 + width2) - d_x;
+ d_h = MIN (y1 + height1, y2 + height2) - d_y;
+
+ if (dest_x) *dest_x = d_x;
+ if (dest_y) *dest_y = d_y;
+ if (dest_width) *dest_width = d_w;
+ if (dest_height) *dest_height = d_h;
+
+ return (d_w > 0 && d_h > 0);
+}
+
+/**
+ * gimp_rectangle_union:
+ * @x1: origin of first rectangle
+ * @y1: origin of first rectangle
+ * @width1: width of first rectangle
+ * @height1: height of first rectangle
+ * @x2: origin of second rectangle
+ * @y2: origin of second rectangle
+ * @width2: width of second rectangle
+ * @height2: height of second rectangle
+ * @dest_x: return location for origin of union (may be %NULL)
+ * @dest_y: return location for origin of union (may be %NULL)
+ * @dest_width: return location for width of union (may be %NULL)
+ * @dest_height: return location for height of union (may be %NULL)
+ *
+ * Calculates the union of two rectangles.
+ *
+ * Since: 2.8
+ **/
+void
+gimp_rectangle_union (gint x1,
+ gint y1,
+ gint width1,
+ gint height1,
+ gint x2,
+ gint y2,
+ gint width2,
+ gint height2,
+ gint *dest_x,
+ gint *dest_y,
+ gint *dest_width,
+ gint *dest_height)
+{
+ gint d_x, d_y;
+ gint d_w, d_h;
+
+ d_x = MIN (x1, x2);
+ d_y = MIN (y1, y2);
+ d_w = MAX (x1 + width1, x2 + width2) - d_x;
+ d_h = MAX (y1 + height1, y2 + height2) - d_y;
+
+ if (dest_x) *dest_x = d_x;
+ if (dest_y) *dest_y = d_y;
+ if (dest_width) *dest_width = d_w;
+ if (dest_height) *dest_height = d_h;
+}
diff --git a/libgimpbase/gimprectangle.h b/libgimpbase/gimprectangle.h
new file mode 100644
index 0000000..8ace674
--- /dev/null
+++ b/libgimpbase/gimprectangle.h
@@ -0,0 +1,60 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_BASE_H_INSIDE__) && !defined (GIMP_BASE_COMPILATION)
+#error "Only <libgimpbase/gimpbase.h> can be included directly."
+#endif
+
+#ifndef __GIMP_RECTANGLE_H__
+#define __GIMP_RECTANGLE_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_rectangle_intersect (gint x1,
+ gint y1,
+ gint width1,
+ gint height1,
+ gint x2,
+ gint y2,
+ gint width2,
+ gint height2,
+ gint *dest_x,
+ gint *dest_y,
+ gint *dest_width,
+ gint *dest_height);
+
+void gimp_rectangle_union (gint x1,
+ gint y1,
+ gint width1,
+ gint height1,
+ gint x2,
+ gint y2,
+ gint width2,
+ gint height2,
+ gint *dest_x,
+ gint *dest_y,
+ gint *dest_width,
+ gint *dest_height);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_RECTANGLE_H__ */
diff --git a/libgimpbase/gimpreloc.c b/libgimpbase/gimpreloc.c
new file mode 100644
index 0000000..b9fa4df
--- /dev/null
+++ b/libgimpbase/gimpreloc.c
@@ -0,0 +1,435 @@
+/*
+ * BinReloc - a library for creating relocatable executables
+ * Written by: Hongli Lai <h.lai@chello.nl>
+ * http://autopackage.org/
+ *
+ * This source code is public domain. You can relicense this code
+ * under whatever license you want.
+ *
+ * See http://autopackage.org/docs/binreloc/ for
+ * more information and how to use this.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+
+#if defined(ENABLE_RELOCATABLE_RESOURCES) && ! defined(G_OS_WIN32)
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#endif /* ENABLE_RELOCATABLE_RESOURCES && ! G_OS_WIN32 */
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "gimpreloc.h"
+
+
+/*
+ * Find the canonical filename of the executable. Returns the filename
+ * (which must be freed) or NULL on error. If the parameter 'error' is
+ * not NULL, the error code will be stored there, if an error occurred.
+ */
+static char *
+_br_find_exe (GimpBinrelocInitError *error)
+{
+#if ! defined(ENABLE_RELOCATABLE_RESOURCES) || defined(G_OS_WIN32)
+ if (error)
+ *error = GIMP_RELOC_INIT_ERROR_DISABLED;
+ return NULL;
+#else
+ char *path, *path2, *line, *result;
+ size_t buf_size;
+ ssize_t size;
+ struct stat stat_buf;
+ FILE *f;
+
+ /* Read from /proc/self/exe (symlink) */
+ if (sizeof (path) > SSIZE_MAX)
+ buf_size = SSIZE_MAX - 1;
+ else
+ buf_size = PATH_MAX - 1;
+ path = g_try_new (char, buf_size);
+ if (path == NULL)
+ {
+ /* Cannot allocate memory. */
+ if (error)
+ *error = GIMP_RELOC_INIT_ERROR_NOMEM;
+ return NULL;
+ }
+ path2 = g_try_new (char, buf_size);
+ if (path2 == NULL)
+ {
+ /* Cannot allocate memory. */
+ if (error)
+ *error = GIMP_RELOC_INIT_ERROR_NOMEM;
+ g_free (path);
+ return NULL;
+ }
+
+ strncpy (path2, "/proc/self/exe", buf_size - 1);
+
+ while (1)
+ {
+ int i;
+
+ size = readlink (path2, path, buf_size - 1);
+ if (size == -1)
+ {
+ /* Error. */
+ g_free (path2);
+ break;
+ }
+
+ /* readlink() success. */
+ path[size] = '\0';
+
+ /* Check whether the symlink's target is also a symlink.
+ * We want to get the final target. */
+ i = stat (path, &stat_buf);
+ if (i == -1)
+ {
+ /* Error. */
+ g_free (path2);
+ break;
+ }
+
+ /* stat() success. */
+ if (!S_ISLNK (stat_buf.st_mode))
+ {
+ /* path is not a symlink. Done. */
+ g_free (path2);
+ return path;
+ }
+
+ /* path is a symlink. Continue loop and resolve this. */
+ strncpy (path, path2, buf_size - 1);
+ }
+
+
+ /* readlink() or stat() failed; this can happen when the program is
+ * running in Valgrind 2.2. Read from /proc/self/maps as fallback. */
+
+ buf_size = PATH_MAX + 128;
+ line = (char *) g_try_realloc (path, buf_size);
+ if (line == NULL)
+ {
+ /* Cannot allocate memory. */
+ g_free (path);
+ if (error)
+ *error = GIMP_RELOC_INIT_ERROR_NOMEM;
+ return NULL;
+ }
+
+ f = g_fopen ("/proc/self/maps", "r");
+ if (f == NULL)
+ {
+ g_free (line);
+ if (error)
+ *error = GIMP_RELOC_INIT_ERROR_OPEN_MAPS;
+ return NULL;
+ }
+
+ /* The first entry should be the executable name. */
+ result = fgets (line, (int) buf_size, f);
+ if (result == NULL)
+ {
+ fclose (f);
+ g_free (line);
+ if (error)
+ *error = GIMP_RELOC_INIT_ERROR_READ_MAPS;
+ return NULL;
+ }
+
+ /* Get rid of newline character. */
+ buf_size = strlen (line);
+ if (buf_size == 0)
+ {
+ /* Huh? An empty string? */
+ fclose (f);
+ g_free (line);
+ if (error)
+ *error = GIMP_RELOC_INIT_ERROR_INVALID_MAPS;
+ return NULL;
+ }
+ if (line[buf_size - 1] == 10)
+ line[buf_size - 1] = 0;
+
+ /* Extract the filename; it is always an absolute path. */
+ path = strchr (line, '/');
+
+ /* Sanity check. */
+ if (strstr (line, " r-xp ") == NULL || path == NULL)
+ {
+ fclose (f);
+ g_free (line);
+ if (error)
+ *error = GIMP_RELOC_INIT_ERROR_INVALID_MAPS;
+ return NULL;
+ }
+
+ path = g_strdup (path);
+ g_free (line);
+ fclose (f);
+ return path;
+#endif /* ! ENABLE_RELOCATABLE_RESOURCES || G_OS_WIN32 */
+}
+
+
+/*
+ * Find the canonical filename of the executable which owns symbol.
+ * Returns a filename which must be freed, or NULL on error.
+ */
+static char *
+_br_find_exe_for_symbol (const void *symbol, GimpBinrelocInitError *error)
+{
+#if ! defined(ENABLE_RELOCATABLE_RESOURCES) || defined(G_OS_WIN32)
+ if (error)
+ *error = GIMP_RELOC_INIT_ERROR_DISABLED;
+ return (char *) NULL;
+#else
+#define SIZE PATH_MAX + 100
+ FILE *f;
+ size_t address_string_len;
+ char *address_string, line[SIZE], *found;
+
+ if (symbol == NULL)
+ return (char *) NULL;
+
+ f = g_fopen ("/proc/self/maps", "r");
+ if (f == NULL)
+ return (char *) NULL;
+
+ address_string_len = 4;
+ address_string = g_try_new (char, address_string_len);
+ found = (char *) NULL;
+
+ while (!feof (f))
+ {
+ char *start_addr, *end_addr, *end_addr_end, *file;
+ void *start_addr_p, *end_addr_p;
+ size_t len;
+
+ if (fgets (line, SIZE, f) == NULL)
+ break;
+
+ /* Sanity check. */
+ if (strstr (line, " r-xp ") == NULL || strchr (line, '/') == NULL)
+ continue;
+
+ /* Parse line. */
+ start_addr = line;
+ end_addr = strchr (line, '-');
+ file = strchr (line, '/');
+
+ /* More sanity check. */
+ if (!(file > end_addr && end_addr != NULL && end_addr[0] == '-'))
+ continue;
+
+ end_addr[0] = '\0';
+ end_addr++;
+ end_addr_end = strchr (end_addr, ' ');
+ if (end_addr_end == NULL)
+ continue;
+
+ end_addr_end[0] = '\0';
+ len = strlen (file);
+ if (len == 0)
+ continue;
+ if (file[len - 1] == '\n')
+ file[len - 1] = '\0';
+
+ /* Get rid of "(deleted)" from the filename. */
+ len = strlen (file);
+ if (len > 10 && strcmp (file + len - 10, " (deleted)") == 0)
+ file[len - 10] = '\0';
+
+ /* I don't know whether this can happen but better safe than sorry. */
+ len = strlen (start_addr);
+ if (len != strlen (end_addr))
+ continue;
+
+
+ /* Transform the addresses into a string in the form of 0xdeadbeef,
+ * then transform that into a pointer. */
+ if (address_string_len < len + 3)
+ {
+ address_string_len = len + 3;
+ address_string = (char *) g_try_realloc (address_string, address_string_len);
+ }
+
+ memcpy (address_string, "0x", 2);
+ memcpy (address_string + 2, start_addr, len);
+ address_string[2 + len] = '\0';
+ sscanf (address_string, "%p", &start_addr_p);
+
+ memcpy (address_string, "0x", 2);
+ memcpy (address_string + 2, end_addr, len);
+ address_string[2 + len] = '\0';
+ sscanf (address_string, "%p", &end_addr_p);
+
+
+ if (symbol >= start_addr_p && symbol < end_addr_p)
+ {
+ found = file;
+ break;
+ }
+ }
+
+ g_free (address_string);
+ fclose (f);
+
+ if (found == NULL)
+ return (char *) NULL;
+ else
+ return g_strdup (found);
+#endif /* ! ENABLE_RELOCATABLE_RESOURCES || G_OS_WIN32 */
+}
+
+
+static gchar *exe = NULL;
+
+static void set_gerror (GError **error, GimpBinrelocInitError errcode);
+
+
+/* Initialize the BinReloc library (for applications).
+ *
+ * This function must be called before using any other BinReloc functions.
+ * It attempts to locate the application's canonical filename.
+ *
+ * @note If you want to use BinReloc for a library, then you should call
+ * _gimp_reloc_init_lib() instead.
+ * @note Initialization failure is not fatal. BinReloc functions will just
+ * fallback to the supplied default path.
+ *
+ * @param error If BinReloc failed to initialize, then the error report will
+ * be stored in this variable. Set to NULL if you don't want an
+ * error report. See the #GimpBinrelocInitError for a list of error
+ * codes.
+ *
+ * @returns TRUE on success, FALSE if BinReloc failed to initialize.
+ */
+gboolean
+_gimp_reloc_init (GError **error)
+{
+ GimpBinrelocInitError errcode;
+
+ /* Shut up compiler warning about uninitialized variable. */
+ errcode = GIMP_RELOC_INIT_ERROR_NOMEM;
+
+ /* Locate the application's filename. */
+ exe = _br_find_exe (&errcode);
+ if (exe != NULL)
+ /* Success! */
+ return TRUE;
+ else
+ {
+ /* Failed :-( */
+ set_gerror (error, errcode);
+ return FALSE;
+ }
+}
+
+
+/* Initialize the BinReloc library (for libraries).
+ *
+ * This function must be called before using any other BinReloc functions.
+ * It attempts to locate the calling library's canonical filename.
+ *
+ * @note The BinReloc source code MUST be included in your library, or this
+ * function won't work correctly.
+ * @note Initialization failure is not fatal. BinReloc functions will just
+ * fallback to the supplied default path.
+ *
+ * @returns TRUE on success, FALSE if a filename cannot be found.
+ */
+gboolean
+_gimp_reloc_init_lib (GError **error)
+{
+ GimpBinrelocInitError errcode;
+
+ /* Shut up compiler warning about uninitialized variable. */
+ errcode = GIMP_RELOC_INIT_ERROR_NOMEM;
+
+ exe = _br_find_exe_for_symbol ((const void *) "", &errcode);
+ if (exe != NULL)
+ /* Success! */
+ return TRUE;
+ else
+ {
+ /* Failed :-( */
+ set_gerror (error, errcode);
+ return exe != NULL;
+ }
+}
+
+static void
+set_gerror (GError **error, GimpBinrelocInitError errcode)
+{
+ const gchar *error_message;
+
+ if (error == NULL)
+ return;
+
+ switch (errcode)
+ {
+ case GIMP_RELOC_INIT_ERROR_NOMEM:
+ error_message = "Cannot allocate memory.";
+ break;
+ case GIMP_RELOC_INIT_ERROR_OPEN_MAPS:
+ error_message = "Unable to open /proc/self/maps for reading.";
+ break;
+ case GIMP_RELOC_INIT_ERROR_READ_MAPS:
+ error_message = "Unable to read from /proc/self/maps.";
+ break;
+ case GIMP_RELOC_INIT_ERROR_INVALID_MAPS:
+ error_message = "The file format of /proc/self/maps is invalid.";
+ break;
+ case GIMP_RELOC_INIT_ERROR_DISABLED:
+ error_message = "Binary relocation support is disabled.";
+ break;
+ default:
+ error_message = "Unknown error.";
+ break;
+ };
+ g_set_error (error, g_quark_from_static_string ("GBinReloc"),
+ errcode, "%s", error_message);
+}
+
+
+/* Locate the prefix in which the current application is installed.
+ *
+ * The prefix is generated by the following pseudo-code evaluation:
+ * \code
+ * dirname(dirname(exename))
+ * \endcode
+ *
+ * @param default_prefix A default prefix which will used as fallback.
+ * @return A string containing the prefix, which must be freed when no
+ * longer necessary. If BinReloc is not initialized, or if the
+ * initialization function failed, then a copy of default_prefix
+ * will be returned. If default_prefix is NULL, then NULL will be
+ * returned.
+ */
+gchar *
+_gimp_reloc_find_prefix (const gchar *default_prefix)
+{
+ gchar *dir1, *dir2;
+
+ if (exe == NULL)
+ {
+ /* BinReloc not initialized. */
+ if (default_prefix != NULL)
+ return g_strdup (default_prefix);
+ else
+ return NULL;
+ }
+
+ dir1 = g_path_get_dirname (exe);
+ dir2 = g_path_get_dirname (dir1);
+ g_free (dir1);
+ return dir2;
+}
diff --git a/libgimpbase/gimpreloc.h b/libgimpbase/gimpreloc.h
new file mode 100644
index 0000000..a7af912
--- /dev/null
+++ b/libgimpbase/gimpreloc.h
@@ -0,0 +1,46 @@
+/*
+ * BinReloc - a library for creating relocatable executables
+ * Written by: Hongli Lai <h.lai@chello.nl>
+ * http://autopackage.org/
+ *
+ * This source code is public domain. You can relicense this code
+ * under whatever license you want.
+ *
+ * See http://autopackage.org/docs/binreloc/ for
+ * more information and how to use this.
+ */
+
+#ifndef __GIMP_RELOC_H__
+#define __GIMP_RELOC_H__
+
+G_BEGIN_DECLS
+
+
+/* These error codes can be returned from _gimp_reloc_init() or
+ * _gimp_reloc_init_lib().
+ */
+
+typedef enum
+{
+ /** Cannot allocate memory. */
+ GIMP_RELOC_INIT_ERROR_NOMEM,
+ /** Unable to open /proc/self/maps; see errno for details. */
+ GIMP_RELOC_INIT_ERROR_OPEN_MAPS,
+ /** Unable to read from /proc/self/maps; see errno for details. */
+ GIMP_RELOC_INIT_ERROR_READ_MAPS,
+ /** The file format of /proc/self/maps is invalid; kernel bug? */
+ GIMP_RELOC_INIT_ERROR_INVALID_MAPS,
+ /** BinReloc is disabled (the ENABLE_BINRELOC macro is not defined). */
+ GIMP_RELOC_INIT_ERROR_DISABLED
+} GimpBinrelocInitError;
+
+
+G_GNUC_INTERNAL gboolean _gimp_reloc_init (GError **error);
+G_GNUC_INTERNAL gboolean _gimp_reloc_init_lib (GError **error);
+
+G_GNUC_INTERNAL gchar * _gimp_reloc_find_prefix (const gchar *default_prefix);
+
+
+G_END_DECLS
+
+#endif /* _GIMPRELOC_H_ */
diff --git a/libgimpbase/gimpsignal.c b/libgimpbase/gimpsignal.c
new file mode 100644
index 0000000..a0221f3
--- /dev/null
+++ b/libgimpbase/gimpsignal.c
@@ -0,0 +1,110 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ *
+ * $Revision$
+ */
+
+#include "config.h"
+
+#define _GNU_SOURCE /* for the sigaction stuff */
+
+#include <glib.h>
+
+#include "gimpsignal.h"
+
+
+/**
+ * SECTION: gimpsignal
+ * @title: gimpsignal
+ * @short_description: Portable signal handling.
+ * @see_also: signal(2), signal(5 or 7), sigaction(2).
+ *
+ * Portable signal handling.
+ **/
+
+
+/* Courtesy of Austin Donnelly 06-04-2000 to address bug #2742 */
+
+/**
+ * gimp_signal_private:
+ * @signum: Selects signal to be handled see man 5 signal (or man 7 signal)
+ * @handler: Handler that maps to signum. Invoked by O/S.
+ * Handler gets signal that caused invocation. Corresponds
+ * to the @sa_handler field of the @sigaction struct.
+ * @flags: Preferences. OR'ed SA_&lt;xxx&gt;. See man sigaction. Corresponds
+ * to the @sa_flags field of the @sigaction struct.
+ *
+ * This function furnishes a workalike for signal(2) but
+ * which internally invokes sigaction(2) after certain
+ * sa_flags are set; these primarily to ensure restarting
+ * of interrupted system calls. See sigaction(2) It is a
+ * aid to transition and not new development: that effort
+ * should employ sigaction directly. [gosgood 18.04.2000]
+ *
+ * Cause @handler to be run when @signum is delivered. We
+ * use sigaction(2) rather than signal(2) so that we can control the
+ * signal handler's environment completely via @flags: some signal(2)
+ * implementations differ in their semantics, so we need to nail down
+ * exactly what we want. [austin 06.04.2000]
+ *
+ * Returns: A reference to the signal handling function which was
+ * active before the call to gimp_signal_private().
+ */
+GimpSignalHandlerFunc
+gimp_signal_private (gint signum,
+ GimpSignalHandlerFunc handler,
+ gint flags)
+{
+#ifndef G_OS_WIN32
+ gint ret;
+ struct sigaction sa;
+ struct sigaction osa;
+
+ /* The sa_handler (mandated by POSIX.1) and sa_sigaction (a
+ * common extension) are often implemented by the OS as members
+ * of a union. This means you CAN NOT set both, you set one or
+ * the other. Caveat programmer!
+ */
+
+ /* Passing gimp_signal_private a gimp_sighandler of NULL is not
+ * an error, and generally results in the action for that signal
+ * being set to SIG_DFL (default behavior). Many OSes define
+ * SIG_DFL as (void (*)()0, so setting sa_handler to NULL is
+ * the same thing as passing SIG_DFL to it.
+ */
+ sa.sa_handler = handler;
+
+ /* Mask all signals while handler runs to avoid re-entrancy
+ * problems.
+ */
+ sigfillset (&sa.sa_mask);
+
+ sa.sa_flags = flags;
+
+ ret = sigaction (signum, &sa, &osa);
+
+ if (ret < 0)
+ g_error ("unable to set handler for signal %d\n", signum);
+
+ return (GimpSignalHandlerFunc) osa.sa_handler;
+#else
+ return NULL; /* Or g_error()? Should all calls to
+ * this function really be inside
+ * #ifdef G_OS_UNIX?
+ */
+#endif
+}
diff --git a/libgimpbase/gimpsignal.h b/libgimpbase/gimpsignal.h
new file mode 100644
index 0000000..978a394
--- /dev/null
+++ b/libgimpbase/gimpsignal.h
@@ -0,0 +1,48 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2000 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_SIGNAL_H__
+#define __GIMP_SIGNAL_H__
+
+#include <signal.h>
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+/**
+ * GimpSignalHandlerFunc:
+ * @signum: The number of the signal. Useful if different signals are
+ * handled by a single handler.
+ *
+ * A prototype for a reference to a signal handler functions. Note
+ * that each function which takes or returns a variable of this type
+ * also accepts or may return special values defined by your system's
+ * signal.h header file (like @SIG_DFL or @SIG_IGN).
+ **/
+typedef void (* GimpSignalHandlerFunc) (gint signum);
+
+GimpSignalHandlerFunc gimp_signal_private (gint signum,
+ GimpSignalHandlerFunc handler,
+ gint flags);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_SIGNAL_H__ */
diff --git a/libgimpbase/gimpunit.c b/libgimpbase/gimpunit.c
new file mode 100644
index 0000000..e0aa626
--- /dev/null
+++ b/libgimpbase/gimpunit.c
@@ -0,0 +1,755 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpunit.c
+ * Copyright (C) 2003 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <string.h>
+
+#include <glib-object.h>
+
+#include "gimpbasetypes.h"
+
+#include "gimpbase-private.h"
+#include "gimpunit.h"
+
+
+/**
+ * SECTION: gimpunit
+ * @title: gimpunit
+ * @short_description: Provides a collection of predefined units and
+ * functions for creating user-defined units.
+ * @see_also: #GimpUnitMenu, #GimpSizeEntry.
+ *
+ * Provides a collection of predefined units and functions for
+ * creating user-defined units.
+ **/
+
+
+static void unit_to_string (const GValue *src_value,
+ GValue *dest_value);
+static void string_to_unit (const GValue *src_value,
+ GValue *dest_value);
+
+GType
+gimp_unit_get_type (void)
+{
+ static GType unit_type = 0;
+
+ if (! unit_type)
+ {
+ const GTypeInfo type_info = { 0, };
+
+ unit_type = g_type_register_static (G_TYPE_INT, "GimpUnit",
+ &type_info, 0);
+
+ g_value_register_transform_func (unit_type, G_TYPE_STRING,
+ unit_to_string);
+ g_value_register_transform_func (G_TYPE_STRING, unit_type,
+ string_to_unit);
+ }
+
+ return unit_type;
+}
+
+static void
+unit_to_string (const GValue *src_value,
+ GValue *dest_value)
+{
+ GimpUnit unit = (GimpUnit) g_value_get_int (src_value);
+
+ g_value_set_string (dest_value, gimp_unit_get_identifier (unit));
+}
+
+static void
+string_to_unit (const GValue *src_value,
+ GValue *dest_value)
+{
+ const gchar *str;
+ gint num_units;
+ gint i;
+
+ str = g_value_get_string (src_value);
+
+ if (!str || !*str)
+ goto error;
+
+ num_units = gimp_unit_get_number_of_units ();
+
+ for (i = GIMP_UNIT_PIXEL; i < num_units; i++)
+ if (strcmp (str, gimp_unit_get_identifier (i)) == 0)
+ break;
+
+ if (i == num_units)
+ {
+ if (strcmp (str, gimp_unit_get_identifier (GIMP_UNIT_PERCENT)) == 0)
+ i = GIMP_UNIT_PERCENT;
+ else
+ goto error;
+ }
+
+ g_value_set_int (dest_value, i);
+ return;
+
+ error:
+ g_warning ("Can't convert string '%s' to GimpUnit.", str);
+}
+
+
+/**
+ * gimp_unit_get_number_of_units:
+ *
+ * Returns the number of units which are known to the #GimpUnit system.
+ *
+ * Returns: The number of defined units.
+ **/
+gint
+gimp_unit_get_number_of_units (void)
+{
+ g_return_val_if_fail (_gimp_unit_vtable.unit_get_number_of_units != NULL,
+ GIMP_UNIT_END);
+
+ return _gimp_unit_vtable.unit_get_number_of_units ();
+}
+
+/**
+ * gimp_unit_get_number_of_built_in_units:
+ *
+ * Returns the number of #GimpUnit's which are hardcoded in the unit system
+ * (UNIT_INCH, UNIT_MM, UNIT_POINT, UNIT_PICA and the two "pseudo unit"
+ * UNIT_PIXEL).
+ *
+ * Returns: The number of built-in units.
+ **/
+gint
+gimp_unit_get_number_of_built_in_units (void)
+{
+ g_return_val_if_fail (_gimp_unit_vtable.unit_get_number_of_built_in_units
+ != NULL, GIMP_UNIT_END);
+
+ return _gimp_unit_vtable.unit_get_number_of_built_in_units ();
+}
+
+/**
+ * gimp_unit_new:
+ * @identifier: The unit's identifier string.
+ * @factor: The unit's factor (how many units are in one inch).
+ * @digits: The unit's suggested number of digits (see gimp_unit_get_digits()).
+ * @symbol: The symbol of the unit (e.g. "''" for inch).
+ * @abbreviation: The abbreviation of the unit.
+ * @singular: The singular form of the unit.
+ * @plural: The plural form of the unit.
+ *
+ * Returns the integer ID of the new #GimpUnit.
+ *
+ * Note that a new unit is always created with its deletion flag
+ * set to %TRUE. You will have to set it to %FALSE with
+ * gimp_unit_set_deletion_flag() to make the unit definition persistent.
+ *
+ * Returns: The ID of the new unit.
+ **/
+GimpUnit
+gimp_unit_new (gchar *identifier,
+ gdouble factor,
+ gint digits,
+ gchar *symbol,
+ gchar *abbreviation,
+ gchar *singular,
+ gchar *plural)
+{
+ g_return_val_if_fail (_gimp_unit_vtable.unit_new != NULL, GIMP_UNIT_INCH);
+
+ return _gimp_unit_vtable.unit_new (identifier, factor, digits,
+ symbol, abbreviation, singular, plural);
+}
+
+/**
+ * gimp_unit_get_deletion_flag:
+ * @unit: The unit you want to know the @deletion_flag of.
+ *
+ * Returns: The unit's @deletion_flag.
+ **/
+gboolean
+gimp_unit_get_deletion_flag (GimpUnit unit)
+{
+ g_return_val_if_fail (_gimp_unit_vtable.unit_get_deletion_flag != NULL, FALSE);
+
+ return _gimp_unit_vtable.unit_get_deletion_flag (unit);
+}
+
+/**
+ * gimp_unit_set_deletion_flag:
+ * @unit: The unit you want to set the @deletion_flag for.
+ * @deletion_flag: The new deletion_flag.
+ *
+ * Sets a #GimpUnit's @deletion_flag. If the @deletion_flag of a unit is
+ * %TRUE when GIMP exits, this unit will not be saved in the users's
+ * "unitrc" file.
+ *
+ * Trying to change the @deletion_flag of a built-in unit will be silently
+ * ignored.
+ **/
+void
+gimp_unit_set_deletion_flag (GimpUnit unit,
+ gboolean deletion_flag)
+{
+ g_return_if_fail (_gimp_unit_vtable.unit_set_deletion_flag != NULL);
+
+ _gimp_unit_vtable.unit_set_deletion_flag (unit, deletion_flag);
+}
+
+/**
+ * gimp_unit_get_factor:
+ * @unit: The unit you want to know the factor of.
+ *
+ * A #GimpUnit's @factor is defined to be:
+ *
+ * distance_in_units == (@factor * distance_in_inches)
+ *
+ * Returns 0 for @unit == GIMP_UNIT_PIXEL.
+ *
+ * Returns: The unit's factor.
+ **/
+gdouble
+gimp_unit_get_factor (GimpUnit unit)
+{
+ g_return_val_if_fail (_gimp_unit_vtable.unit_get_factor != NULL, 1.0);
+
+ return _gimp_unit_vtable.unit_get_factor (unit);
+}
+
+/**
+ * gimp_unit_get_digits:
+ * @unit: The unit you want to know the digits.
+ *
+ * Returns the number of digits set for @unit.
+ * Built-in units' accuracy is approximately the same as an inch with
+ * two digits. User-defined units can suggest a different accuracy.
+ *
+ * Note: the value is as-set by defaults or by the user and does not
+ * necessary provide enough precision on high-resolution images.
+ * When the information is needed for a specific image, the use of
+ * gimp_unit_get_scaled_digits() may be more appropriate.
+ *
+ * Returns 0 for @unit == GIMP_UNIT_PIXEL.
+ *
+ * Returns: The suggested number of digits.
+ **/
+gint
+gimp_unit_get_digits (GimpUnit unit)
+{
+ g_return_val_if_fail (_gimp_unit_vtable.unit_get_digits != NULL, 2);
+
+ return _gimp_unit_vtable.unit_get_digits (unit);
+}
+
+/**
+ * gimp_unit_get_scaled_digits:
+ * @unit: The unit you want to know the digits.
+ * @resolution: the resolution in PPI.
+ *
+ * Returns the number of digits a @unit field should provide to get
+ * enough accuracy so that every pixel position shows a different
+ * value from neighboring pixels.
+ *
+ * Note: when needing digit accuracy to display a diagonal distance,
+ * the @resolution may not correspond to the image's horizontal or
+ * vertical resolution, but instead to the result of:
+ * `distance_in_pixel / distance_in_inch`.
+ *
+ * Returns: The suggested number of digits.
+ **/
+gint
+gimp_unit_get_scaled_digits (GimpUnit unit,
+ gdouble resolution)
+{
+ gint digits;
+
+ g_return_val_if_fail (_gimp_unit_vtable.unit_get_digits != NULL, 2);
+
+ digits = ceil (log10 (1.0 /
+ gimp_pixels_to_units (1.0, unit, resolution)));
+
+ return MAX (digits, gimp_unit_get_digits (unit));
+}
+
+/**
+ * gimp_unit_get_identifier:
+ * @unit: The unit you want to know the identifier of.
+ *
+ * This is an untranslated string and must not be changed or freed.
+ *
+ * Returns: The unit's identifier.
+ **/
+const gchar *
+gimp_unit_get_identifier (GimpUnit unit)
+{
+ g_return_val_if_fail (_gimp_unit_vtable.unit_get_identifier != NULL, NULL);
+
+ return _gimp_unit_vtable.unit_get_identifier (unit);
+}
+
+/**
+ * gimp_unit_get_symbol:
+ * @unit: The unit you want to know the symbol of.
+ *
+ * This is e.g. "''" for UNIT_INCH.
+ *
+ * NOTE: This string must not be changed or freed.
+ *
+ * Returns: The unit's symbol.
+ **/
+const gchar *
+gimp_unit_get_symbol (GimpUnit unit)
+{
+ g_return_val_if_fail (_gimp_unit_vtable.unit_get_symbol != NULL, NULL);
+
+ return _gimp_unit_vtable.unit_get_symbol (unit);
+}
+
+/**
+ * gimp_unit_get_abbreviation:
+ * @unit: The unit you want to know the abbreviation of.
+ *
+ * For built-in units, this function returns the translated abbreviation
+ * of the unit.
+ *
+ * NOTE: This string must not be changed or freed.
+ *
+ * Returns: The unit's abbreviation.
+ **/
+const gchar *
+gimp_unit_get_abbreviation (GimpUnit unit)
+{
+ g_return_val_if_fail (_gimp_unit_vtable.unit_get_abbreviation != NULL, NULL);
+
+ return _gimp_unit_vtable.unit_get_abbreviation (unit);
+}
+
+/**
+ * gimp_unit_get_singular:
+ * @unit: The unit you want to know the singular form of.
+ *
+ * For built-in units, this function returns the translated singular form
+ * of the unit's name.
+ *
+ * NOTE: This string must not be changed or freed.
+ *
+ * Returns: The unit's singular form.
+ **/
+const gchar *
+gimp_unit_get_singular (GimpUnit unit)
+{
+ g_return_val_if_fail (_gimp_unit_vtable.unit_get_singular != NULL, NULL);
+
+ return _gimp_unit_vtable.unit_get_singular (unit);
+}
+
+/**
+ * gimp_unit_get_plural:
+ * @unit: The unit you want to know the plural form of.
+ *
+ * For built-in units, this function returns the translated plural form
+ * of the unit's name.
+ *
+ * NOTE: This string must not be changed or freed.
+ *
+ * Returns: The unit's plural form.
+ **/
+const gchar *
+gimp_unit_get_plural (GimpUnit unit)
+{
+ g_return_val_if_fail (_gimp_unit_vtable.unit_get_plural != NULL, NULL);
+
+ return _gimp_unit_vtable.unit_get_plural (unit);
+}
+
+static gint print (gchar *buf,
+ gint len,
+ gint start,
+ const gchar *fmt,
+ ...) G_GNUC_PRINTF (4, 5);
+
+static gint
+print (gchar *buf,
+ gint len,
+ gint start,
+ const gchar *fmt,
+ ...)
+{
+ va_list args;
+ gint printed;
+
+ va_start (args, fmt);
+
+ printed = g_vsnprintf (buf + start, len - start, fmt, args);
+ if (printed < 0)
+ printed = len - start;
+
+ va_end (args);
+
+ return printed;
+}
+
+/**
+ * gimp_unit_format_string:
+ * @format: A printf-like format string which is used to create the unit
+ * string.
+ * @unit: A unit.
+ *
+ * The @format string supports the following percent expansions:
+ *
+ * <informaltable pgwide="1" frame="none" role="enum">
+ * <tgroup cols="2"><colspec colwidth="1*"/><colspec colwidth="8*"/>
+ * <tbody>
+ * <row>
+ * <entry>% f</entry>
+ * <entry>Factor (how many units make up an inch)</entry>
+ * </row>
+ * <row>
+ * <entry>% y</entry>
+ * <entry>Symbol (e.g. "''" for GIMP_UNIT_INCH)</entry>
+ * </row>
+ * <row>
+ * <entry>% a</entry>
+ * <entry>Abbreviation</entry>
+ * </row>
+ * <row>
+ * <entry>% s</entry>
+ * <entry>Singular</entry>
+ * </row>
+ * <row>
+ * <entry>% p</entry>
+ * <entry>Plural</entry>
+ * </row>
+ * <row>
+ * <entry>%%</entry>
+ * <entry>Literal percent</entry>
+ * </row>
+ * </tbody>
+ * </tgroup>
+ * </informaltable>
+ *
+ * Returns: A newly allocated string with above percent expressions
+ * replaced with the resp. strings for @unit.
+ *
+ * Since: 2.8
+ **/
+gchar *
+gimp_unit_format_string (const gchar *format,
+ GimpUnit unit)
+{
+ gchar buffer[1024];
+ gint i = 0;
+
+ g_return_val_if_fail (format != NULL, NULL);
+ g_return_val_if_fail (unit == GIMP_UNIT_PERCENT ||
+ (unit >= GIMP_UNIT_PIXEL &&
+ unit < gimp_unit_get_number_of_units ()), NULL);
+
+ while (i < (sizeof (buffer) - 1) && *format)
+ {
+ switch (*format)
+ {
+ case '%':
+ format++;
+ switch (*format)
+ {
+ case 0:
+ g_warning ("%s: unit-menu-format string ended within %%-sequence",
+ G_STRFUNC);
+ break;
+
+ case '%':
+ buffer[i++] = '%';
+ break;
+
+ case 'f': /* factor (how many units make up an inch) */
+ i += print (buffer, sizeof (buffer), i, "%f",
+ gimp_unit_get_factor (unit));
+ break;
+
+ case 'y': /* symbol ("''" for inch) */
+ i += print (buffer, sizeof (buffer), i, "%s",
+ gimp_unit_get_symbol (unit));
+ break;
+
+ case 'a': /* abbreviation */
+ i += print (buffer, sizeof (buffer), i, "%s",
+ gimp_unit_get_abbreviation (unit));
+ break;
+
+ case 's': /* singular */
+ i += print (buffer, sizeof (buffer), i, "%s",
+ gimp_unit_get_singular (unit));
+ break;
+
+ case 'p': /* plural */
+ i += print (buffer, sizeof (buffer), i, "%s",
+ gimp_unit_get_plural (unit));
+ break;
+
+ default:
+ g_warning ("%s: unit-menu-format contains unknown format "
+ "sequence '%%%c'", G_STRFUNC, *format);
+ break;
+ }
+ break;
+
+ default:
+ buffer[i++] = *format;
+ break;
+ }
+
+ format++;
+ }
+
+ buffer[MIN (i, sizeof (buffer) - 1)] = 0;
+
+ return g_strdup (buffer);
+}
+
+/*
+ * GIMP_TYPE_PARAM_UNIT
+ */
+
+#define GIMP_PARAM_SPEC_UNIT(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_UNIT, GimpParamSpecUnit))
+
+typedef struct _GimpParamSpecUnit GimpParamSpecUnit;
+
+struct _GimpParamSpecUnit
+{
+ GParamSpecInt parent_instance;
+
+ gboolean allow_percent;
+};
+
+static void gimp_param_unit_class_init (GParamSpecClass *class);
+static gboolean gimp_param_unit_value_validate (GParamSpec *pspec,
+ GValue *value);
+
+/**
+ * gimp_param_unit_get_type:
+ *
+ * Reveals the object type
+ *
+ * Returns: the #GType for a unit param object
+ *
+ * Since: 2.4
+ **/
+GType
+gimp_param_unit_get_type (void)
+{
+ static GType spec_type = 0;
+
+ if (! spec_type)
+ {
+ const GTypeInfo type_info =
+ {
+ sizeof (GParamSpecClass),
+ NULL, NULL,
+ (GClassInitFunc) gimp_param_unit_class_init,
+ NULL, NULL,
+ sizeof (GimpParamSpecUnit),
+ 0, NULL, NULL
+ };
+
+ spec_type = g_type_register_static (G_TYPE_PARAM_INT,
+ "GimpParamUnit",
+ &type_info, 0);
+ }
+
+ return spec_type;
+}
+
+static void
+gimp_param_unit_class_init (GParamSpecClass *class)
+{
+ class->value_type = GIMP_TYPE_UNIT;
+ class->value_validate = gimp_param_unit_value_validate;
+}
+
+static gboolean
+gimp_param_unit_value_validate (GParamSpec *pspec,
+ GValue *value)
+{
+ GParamSpecInt *ispec = G_PARAM_SPEC_INT (pspec);
+ GimpParamSpecUnit *uspec = GIMP_PARAM_SPEC_UNIT (pspec);
+ gint oval = value->data[0].v_int;
+
+ if (!(uspec->allow_percent && value->data[0].v_int == GIMP_UNIT_PERCENT))
+ {
+ value->data[0].v_int = CLAMP (value->data[0].v_int,
+ ispec->minimum,
+ gimp_unit_get_number_of_units () - 1);
+ }
+
+ return value->data[0].v_int != oval;
+}
+
+/**
+ * gimp_param_spec_unit:
+ * @name: Canonical name of the param
+ * @nick: Nickname of the param
+ * @blurb: Brief description of param.
+ * @allow_pixels: Whether "pixels" is an allowed unit.
+ * @allow_percent: Whether "percent" is an allowed unit.
+ * @default_value: Unit to use if none is assigned.
+ * @flags: a combination of #GParamFlags
+ *
+ * Creates a param spec to hold a units param.
+ * See g_param_spec_internal() for more information.
+ *
+ * Returns: a newly allocated #GParamSpec instance
+ *
+ * Since: 2.4
+ **/
+GParamSpec *
+gimp_param_spec_unit (const gchar *name,
+ const gchar *nick,
+ const gchar *blurb,
+ gboolean allow_pixels,
+ gboolean allow_percent,
+ GimpUnit default_value,
+ GParamFlags flags)
+{
+ GimpParamSpecUnit *pspec;
+ GParamSpecInt *ispec;
+
+ pspec = g_param_spec_internal (GIMP_TYPE_PARAM_UNIT,
+ name, nick, blurb, flags);
+
+ ispec = G_PARAM_SPEC_INT (pspec);
+
+ ispec->default_value = default_value;
+ ispec->minimum = allow_pixels ? GIMP_UNIT_PIXEL : GIMP_UNIT_INCH;
+ ispec->maximum = GIMP_UNIT_PERCENT - 1;
+
+ pspec->allow_percent = allow_percent;
+
+ return G_PARAM_SPEC (pspec);
+}
+
+/**
+ * gimp_pixels_to_units:
+ * @pixels: value in pixels
+ * @unit: unit to convert to
+ * @resolution: resolution in DPI
+ *
+ * Converts a @value specified in pixels to @unit.
+ *
+ * Returns: @pixels converted to units.
+ *
+ * Since: 2.8
+ **/
+gdouble
+gimp_pixels_to_units (gdouble pixels,
+ GimpUnit unit,
+ gdouble resolution)
+{
+ if (unit == GIMP_UNIT_PIXEL)
+ return pixels;
+
+ return pixels * gimp_unit_get_factor (unit) / resolution;
+}
+
+/**
+ * gimp_units_to_pixels:
+ * @value: value in units
+ * @unit: unit of @value
+ * @resolution: resloution in DPI
+ *
+ * Converts a @value specified in @unit to pixels.
+ *
+ * Returns: @value converted to pixels.
+ *
+ * Since: 2.8
+ **/
+gdouble
+gimp_units_to_pixels (gdouble value,
+ GimpUnit unit,
+ gdouble resolution)
+{
+ if (unit == GIMP_UNIT_PIXEL)
+ return value;
+
+ return value * resolution / gimp_unit_get_factor (unit);
+}
+
+/**
+ * gimp_units_to_points:
+ * @value: value in units
+ * @unit: unit of @value
+ * @resolution: resloution in DPI
+ *
+ * Converts a @value specified in @unit to points.
+ *
+ * Returns: @value converted to points.
+ *
+ * Since: 2.8
+ **/
+gdouble
+gimp_units_to_points (gdouble value,
+ GimpUnit unit,
+ gdouble resolution)
+{
+ if (unit == GIMP_UNIT_POINT)
+ return value;
+
+ if (unit == GIMP_UNIT_PIXEL)
+ return (value * gimp_unit_get_factor (GIMP_UNIT_POINT) / resolution);
+
+ return (value *
+ gimp_unit_get_factor (GIMP_UNIT_POINT) / gimp_unit_get_factor (unit));
+}
+
+/**
+ * gimp_unit_is_metric:
+ * @unit: The unit
+ *
+ * Checks if the given @unit is metric. A simplistic test is used
+ * that looks at the unit's factor and checks if it is 2.54 multiplied
+ * by some common powers of 10. Currently it checks for mm, cm, dm, m.
+ *
+ * See also: gimp_unit_get_factor()
+ *
+ * Returns: %TRUE if the @unit is metric.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_unit_is_metric (GimpUnit unit)
+{
+ gdouble factor;
+
+ if (unit == GIMP_UNIT_MM)
+ return TRUE;
+
+ factor = gimp_unit_get_factor (unit);
+
+ if (factor == 0.0)
+ return FALSE;
+
+ return ((ABS (factor - 0.0254) < 1e-7) || /* m */
+ (ABS (factor - 0.254) < 1e-6) || /* dm */
+ (ABS (factor - 2.54) < 1e-5) || /* cm */
+ (ABS (factor - 25.4) < 1e-4)); /* mm */
+}
diff --git a/libgimpbase/gimpunit.h b/libgimpbase/gimpunit.h
new file mode 100644
index 0000000..c24d94e
--- /dev/null
+++ b/libgimpbase/gimpunit.h
@@ -0,0 +1,110 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpunit.h
+ * Copyright (C) 1999-2003 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_BASE_H_INSIDE__) && !defined (GIMP_BASE_COMPILATION)
+#error "Only <libgimpbase/gimpbase.h> can be included directly."
+#endif
+
+#ifndef __GIMP_UNIT_H__
+#define __GIMP_UNIT_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+/**
+ * GIMP_TYPE_UNIT:
+ *
+ * #GIMP_TYPE_UNIT is a #GType derived from #G_TYPE_INT.
+ **/
+
+#define GIMP_TYPE_UNIT (gimp_unit_get_type ())
+#define GIMP_VALUE_HOLDS_UNIT(value) (G_TYPE_CHECK_VALUE_TYPE ((value), GIMP_TYPE_UNIT))
+
+GType gimp_unit_get_type (void) G_GNUC_CONST;
+
+
+/*
+ * GIMP_TYPE_PARAM_UNIT
+ */
+
+#define GIMP_TYPE_PARAM_UNIT (gimp_param_unit_get_type ())
+#define GIMP_IS_PARAM_SPEC_UNIT(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_UNIT))
+
+GType gimp_param_unit_get_type (void) G_GNUC_CONST;
+
+GParamSpec * gimp_param_spec_unit (const gchar *name,
+ const gchar *nick,
+ const gchar *blurb,
+ gboolean allow_pixels,
+ gboolean allow_percent,
+ GimpUnit default_value,
+ GParamFlags flags);
+
+
+
+gint gimp_unit_get_number_of_units (void);
+gint gimp_unit_get_number_of_built_in_units (void) G_GNUC_CONST;
+
+GimpUnit gimp_unit_new (gchar *identifier,
+ gdouble factor,
+ gint digits,
+ gchar *symbol,
+ gchar *abbreviation,
+ gchar *singular,
+ gchar *plural);
+
+gboolean gimp_unit_get_deletion_flag (GimpUnit unit);
+void gimp_unit_set_deletion_flag (GimpUnit unit,
+ gboolean deletion_flag);
+
+gdouble gimp_unit_get_factor (GimpUnit unit);
+
+gint gimp_unit_get_digits (GimpUnit unit);
+gint gimp_unit_get_scaled_digits (GimpUnit unit,
+ gdouble resolution);
+
+const gchar * gimp_unit_get_identifier (GimpUnit unit);
+
+const gchar * gimp_unit_get_symbol (GimpUnit unit);
+const gchar * gimp_unit_get_abbreviation (GimpUnit unit);
+const gchar * gimp_unit_get_singular (GimpUnit unit);
+const gchar * gimp_unit_get_plural (GimpUnit unit);
+
+gchar * gimp_unit_format_string (const gchar *format,
+ GimpUnit unit);
+
+gdouble gimp_pixels_to_units (gdouble pixels,
+ GimpUnit unit,
+ gdouble resolution);
+gdouble gimp_units_to_pixels (gdouble value,
+ GimpUnit unit,
+ gdouble resolution);
+gdouble gimp_units_to_points (gdouble value,
+ GimpUnit unit,
+ gdouble resolution);
+
+gboolean gimp_unit_is_metric (GimpUnit unit);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_UNIT_H__ */
diff --git a/libgimpbase/gimputils.c b/libgimpbase/gimputils.c
new file mode 100644
index 0000000..2ee1338
--- /dev/null
+++ b/libgimpbase/gimputils.c
@@ -0,0 +1,1603 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimputils.c
+ * Copyright (C) 2003 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#ifdef PLATFORM_OSX
+#include <AppKit/AppKit.h>
+#endif
+
+#ifdef HAVE_EXECINFO_H
+/* Allowing backtrace() API. */
+#include <execinfo.h>
+#endif
+
+#include <gio/gio.h>
+#include <glib/gprintf.h>
+
+#if defined(G_OS_WIN32)
+# include <windows.h>
+# include <shlobj.h>
+
+#else /* G_OS_WIN32 */
+
+/* For waitpid() */
+#include <sys/wait.h>
+#include <unistd.h>
+#include <errno.h>
+
+/* For thread IDs. */
+#include <sys/types.h>
+#include <sys/syscall.h>
+
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#endif
+
+#ifdef HAVE_SYS_THR_H
+#include <sys/thr.h>
+#endif
+
+#endif /* G_OS_WIN32 */
+
+#include "gimpbasetypes.h"
+#include "gimputils.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimputils
+ * @title: gimputils
+ * @short_description: Utilities of general interest
+ *
+ * Utilities of general interest
+ **/
+
+static gboolean gimp_utils_generic_available (const gchar *program,
+ gint major,
+ gint minor);
+static gboolean gimp_utils_gdb_available (gint major,
+ gint minor);
+
+/**
+ * gimp_utf8_strtrim:
+ * @str: an UTF-8 encoded string (or %NULL)
+ * @max_chars: the maximum number of characters before the string get
+ * trimmed
+ *
+ * Creates a (possibly trimmed) copy of @str. The string is cut if it
+ * exceeds @max_chars characters or on the first newline. The fact
+ * that the string was trimmed is indicated by appending an ellipsis.
+ *
+ * Returns: A (possibly trimmed) copy of @str which should be freed
+ * using g_free() when it is not needed any longer.
+ **/
+gchar *
+gimp_utf8_strtrim (const gchar *str,
+ gint max_chars)
+{
+ /* FIXME: should we make this translatable? */
+ const gchar ellipsis[] = "...";
+ const gint e_len = strlen (ellipsis);
+
+ if (str)
+ {
+ const gchar *p;
+ const gchar *newline = NULL;
+ gint chars = 0;
+ gunichar unichar;
+
+ for (p = str; *p; p = g_utf8_next_char (p))
+ {
+ if (++chars > max_chars)
+ break;
+
+ unichar = g_utf8_get_char (p);
+
+ switch (g_unichar_break_type (unichar))
+ {
+ case G_UNICODE_BREAK_MANDATORY:
+ case G_UNICODE_BREAK_LINE_FEED:
+ newline = p;
+ break;
+ default:
+ continue;
+ }
+
+ break;
+ }
+
+ if (*p)
+ {
+ gsize len = p - str;
+ gchar *trimmed = g_new (gchar, len + e_len + 2);
+
+ memcpy (trimmed, str, len);
+ if (newline)
+ trimmed[len++] = ' ';
+
+ g_strlcpy (trimmed + len, ellipsis, e_len + 1);
+
+ return trimmed;
+ }
+
+ return g_strdup (str);
+ }
+
+ return NULL;
+}
+
+/**
+ * gimp_any_to_utf8:
+ * @str: The string to be converted to UTF-8.
+ * @len: The length of the string, or -1 if the string
+ * is nul-terminated.
+ * @warning_format: The message format for the warning message if conversion
+ * to UTF-8 fails. See the <function>printf()</function>
+ * documentation.
+ * @...: The parameters to insert into the format string.
+ *
+ * This function takes any string (UTF-8 or not) and always returns a valid
+ * UTF-8 string.
+ *
+ * If @str is valid UTF-8, a copy of the string is returned.
+ *
+ * If UTF-8 validation fails, g_locale_to_utf8() is tried and if it
+ * succeeds the resulting string is returned.
+ *
+ * Otherwise, the portion of @str that is UTF-8, concatenated
+ * with "(invalid UTF-8 string)" is returned. If not even the start
+ * of @str is valid UTF-8, only "(invalid UTF-8 string)" is returned.
+ *
+ * Return value: The UTF-8 string as described above.
+ **/
+gchar *
+gimp_any_to_utf8 (const gchar *str,
+ gssize len,
+ const gchar *warning_format,
+ ...)
+{
+ const gchar *start_invalid;
+ gchar *utf8;
+
+ g_return_val_if_fail (str != NULL, NULL);
+
+ if (g_utf8_validate (str, len, &start_invalid))
+ {
+ if (len < 0)
+ utf8 = g_strdup (str);
+ else
+ utf8 = g_strndup (str, len);
+ }
+ else
+ {
+ utf8 = g_locale_to_utf8 (str, len, NULL, NULL, NULL);
+ }
+
+ if (! utf8)
+ {
+ if (warning_format)
+ {
+ va_list warning_args;
+
+ va_start (warning_args, warning_format);
+
+ g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE,
+ warning_format, warning_args);
+
+ va_end (warning_args);
+ }
+
+ if (start_invalid > str)
+ {
+ gchar *tmp;
+
+ tmp = g_strndup (str, start_invalid - str);
+ utf8 = g_strconcat (tmp, " ", _("(invalid UTF-8 string)"), NULL);
+ g_free (tmp);
+ }
+ else
+ {
+ utf8 = g_strdup (_("(invalid UTF-8 string)"));
+ }
+ }
+
+ return utf8;
+}
+
+/**
+ * gimp_filename_to_utf8:
+ * @filename: The filename to be converted to UTF-8.
+ *
+ * Convert a filename in the filesystem's encoding to UTF-8
+ * temporarily. The return value is a pointer to a string that is
+ * guaranteed to be valid only during the current iteration of the
+ * main loop or until the next call to gimp_filename_to_utf8().
+ *
+ * The only purpose of this function is to provide an easy way to pass
+ * a filename in the filesystem encoding to a function that expects an
+ * UTF-8 encoded filename.
+ *
+ * Return value: A temporarily valid UTF-8 representation of @filename.
+ * This string must not be changed or freed.
+ **/
+const gchar *
+gimp_filename_to_utf8 (const gchar *filename)
+{
+ /* Simpleminded implementation, but at least allocates just one copy
+ * of each translation. Could check if already UTF-8, and if so
+ * return filename as is. Could perhaps (re)use a suitably large
+ * cyclic buffer, but then would have to verify that all calls
+ * really need the return value just for a "short" time.
+ */
+
+ static GHashTable *ht = NULL;
+ gchar *filename_utf8;
+
+ if (! filename)
+ return NULL;
+
+ if (! ht)
+ ht = g_hash_table_new (g_str_hash, g_str_equal);
+
+ filename_utf8 = g_hash_table_lookup (ht, filename);
+
+ if (! filename_utf8)
+ {
+ filename_utf8 = g_filename_display_name (filename);
+ g_hash_table_insert (ht, g_strdup (filename), filename_utf8);
+ }
+
+ return filename_utf8;
+}
+
+/**
+ * gimp_file_get_utf8_name:
+ * @file: a #GFile
+ *
+ * This function works like gimp_filename_to_utf8() and returns
+ * a UTF-8 encoded string that does not need to be freed.
+ *
+ * It converts a #GFile's path or uri to UTF-8 temporarily. The
+ * return value is a pointer to a string that is guaranteed to be
+ * valid only during the current iteration of the main loop or until
+ * the next call to gimp_file_get_utf8_name().
+ *
+ * The only purpose of this function is to provide an easy way to pass
+ * a #GFile's name to a function that expects an UTF-8 encoded string.
+ *
+ * See g_file_get_parse_name().
+ *
+ * Since: 2.10
+ *
+ * Return value: A temporarily valid UTF-8 representation of @file's name.
+ * This string must not be changed or freed.
+ **/
+const gchar *
+gimp_file_get_utf8_name (GFile *file)
+{
+ gchar *name;
+
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+ name = g_file_get_parse_name (file);
+
+ g_object_set_data_full (G_OBJECT (file), "gimp-parse-name", name,
+ (GDestroyNotify) g_free);
+
+ return name;
+}
+
+/**
+ * gimp_file_has_extension:
+ * @file: a #GFile
+ * @extension: an ASCII extension
+ *
+ * This function checks if @file's URI ends with @extension. It behaves
+ * like g_str_has_suffix() on g_file_get_uri(), except that the string
+ * comparison is done case-insensitively using g_ascii_strcasecmp().
+ *
+ * Since: 2.10
+ *
+ * Return value: %TRUE if @file's URI ends with @extension,
+ * %FALSE otherwise.
+ **/
+gboolean
+gimp_file_has_extension (GFile *file,
+ const gchar *extension)
+{
+ gchar *uri;
+ gint uri_len;
+ gint ext_len;
+ gboolean result = FALSE;
+
+ g_return_val_if_fail (G_IS_FILE (file), FALSE);
+ g_return_val_if_fail (extension != NULL, FALSE);
+
+ uri = g_file_get_uri (file);
+
+ uri_len = strlen (uri);
+ ext_len = strlen (extension);
+
+ if (uri_len && ext_len && (uri_len > ext_len))
+ {
+ if (g_ascii_strcasecmp (uri + uri_len - ext_len, extension) == 0)
+ result = TRUE;
+ }
+
+ g_free (uri);
+
+ return result;
+}
+
+/**
+ * gimp_file_show_in_file_manager:
+ * @file: a #GFile
+ * @error: return location for a #GError
+ *
+ * Shows @file in the system file manager.
+ *
+ * Since: 2.10
+ *
+ * Return value: %TRUE on success, %FALSE otherwise. On %FALSE, @error
+ * is set.
+ **/
+gboolean
+gimp_file_show_in_file_manager (GFile *file,
+ GError **error)
+{
+ g_return_val_if_fail (G_IS_FILE (file), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+#if defined(G_OS_WIN32)
+
+ {
+ gboolean ret;
+ char *filename;
+ int n;
+ LPWSTR w_filename = NULL;
+ ITEMIDLIST *pidl = NULL;
+
+ ret = FALSE;
+
+ /* Calling this function multiple times should do no harm, but it is
+ easier to put this here as it needs linking against ole32. */
+ CoInitialize (NULL);
+
+ filename = g_file_get_path (file);
+ if (!filename)
+ {
+ g_set_error_literal (error, G_FILE_ERROR, 0,
+ _("File path is NULL"));
+ goto out;
+ }
+
+ n = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS,
+ filename, -1, NULL, 0);
+ if (n == 0)
+ {
+ g_set_error_literal (error, G_FILE_ERROR, 0,
+ _("Error converting UTF-8 filename to wide char"));
+ goto out;
+ }
+
+ w_filename = g_malloc_n (n + 1, sizeof (wchar_t));
+ n = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS,
+ filename, -1,
+ w_filename, (n + 1) * sizeof (wchar_t));
+ if (n == 0)
+ {
+ g_set_error_literal (error, G_FILE_ERROR, 0,
+ _("Error converting UTF-8 filename to wide char"));
+ goto out;
+ }
+
+ pidl = ILCreateFromPathW (w_filename);
+ if (!pidl)
+ {
+ g_set_error_literal (error, G_FILE_ERROR, 0,
+ _("ILCreateFromPath() failed"));
+ goto out;
+ }
+
+ SHOpenFolderAndSelectItems (pidl, 0, NULL, 0);
+ ret = TRUE;
+
+ out:
+ if (pidl)
+ ILFree (pidl);
+ g_free (w_filename);
+ g_free (filename);
+
+ return ret;
+ }
+
+#elif defined(PLATFORM_OSX)
+
+ {
+ gchar *uri;
+ NSString *filename;
+ NSURL *url;
+ gboolean retval = TRUE;
+
+ uri = g_file_get_uri (file);
+ filename = [NSString stringWithUTF8String:uri];
+
+ url = [NSURL URLWithString:filename];
+ if (url)
+ {
+ NSArray *url_array = [NSArray arrayWithObject:url];
+
+ [[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:url_array];
+ }
+ else
+ {
+ g_set_error (error, G_FILE_ERROR, 0,
+ _("Cannot convert '%s' into a valid NSURL."), uri);
+ retval = FALSE;
+ }
+
+ g_free (uri);
+
+ return retval;
+ }
+
+#else /* UNIX */
+
+ {
+ GDBusProxy *proxy;
+ GVariant *retval;
+ GVariantBuilder *builder;
+ gchar *uri;
+
+ proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ "org.freedesktop.FileManager1",
+ "/org/freedesktop/FileManager1",
+ "org.freedesktop.FileManager1",
+ NULL, error);
+
+ if (! proxy)
+ {
+ g_prefix_error (error,
+ _("Connecting to org.freedesktop.FileManager1 failed: "));
+ return FALSE;
+ }
+
+ uri = g_file_get_uri (file);
+
+ builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
+ g_variant_builder_add (builder, "s", uri);
+
+ g_free (uri);
+
+ retval = g_dbus_proxy_call_sync (proxy,
+ "ShowItems",
+ g_variant_new ("(ass)",
+ builder,
+ ""),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL, error);
+
+ g_variant_builder_unref (builder);
+ g_object_unref (proxy);
+
+ if (! retval)
+ {
+ g_prefix_error (error, _("Calling ShowItems failed: "));
+ return FALSE;
+ }
+
+ g_variant_unref (retval);
+
+ return TRUE;
+ }
+
+#endif
+}
+
+/**
+ * gimp_strip_uline:
+ * @str: underline infested string (or %NULL)
+ *
+ * This function returns a copy of @str stripped of underline
+ * characters. This comes in handy when needing to strip mnemonics
+ * from menu paths etc.
+ *
+ * In some languages, mnemonics are handled by adding the mnemonic
+ * character in brackets (like "File (_F)"). This function recognizes
+ * this construct and removes the whole bracket construction to get
+ * rid of the mnemonic (see bug 157561).
+ *
+ * Return value: A (possibly stripped) copy of @str which should be
+ * freed using g_free() when it is not needed any longer.
+ **/
+gchar *
+gimp_strip_uline (const gchar *str)
+{
+ gchar *escaped;
+ gchar *p;
+ gboolean past_bracket = FALSE;
+
+ if (! str)
+ return NULL;
+
+ p = escaped = g_strdup (str);
+
+ while (*str)
+ {
+ if (*str == '_')
+ {
+ /* "__" means a literal "_" in the menu path */
+ if (str[1] == '_')
+ {
+ *p++ = *str++;
+ str++;
+ continue;
+ }
+
+ /* find the "(_X)" construct and remove it entirely */
+ if (past_bracket && str[1] && *(g_utf8_next_char (str + 1)) == ')')
+ {
+ str = g_utf8_next_char (str + 1) + 1;
+ p--;
+ }
+ else
+ {
+ str++;
+ }
+ }
+ else
+ {
+ past_bracket = (*str == '(');
+
+ *p++ = *str++;
+ }
+ }
+
+ *p = '\0';
+
+ return escaped;
+}
+
+/**
+ * gimp_escape_uline:
+ * @str: Underline infested string (or %NULL)
+ *
+ * This function returns a copy of @str with all underline converted
+ * to two adjacent underlines. This comes in handy when needing to display
+ * strings with underlines (like filenames) in a place that would convert
+ * them to mnemonics.
+ *
+ * Return value: A (possibly escaped) copy of @str which should be
+ * freed using g_free() when it is not needed any longer.
+ *
+ * Since: 2.2
+ **/
+gchar *
+gimp_escape_uline (const gchar *str)
+{
+ gchar *escaped;
+ gchar *p;
+ gint n_ulines = 0;
+
+ if (! str)
+ return NULL;
+
+ for (p = (gchar *) str; *p; p++)
+ if (*p == '_')
+ n_ulines++;
+
+ p = escaped = g_malloc (strlen (str) + n_ulines + 1);
+
+ while (*str)
+ {
+ if (*str == '_')
+ *p++ = '_';
+
+ *p++ = *str++;
+ }
+
+ *p = '\0';
+
+ return escaped;
+}
+
+/**
+ * gimp_canonicalize_identifier:
+ * @identifier: The identifier string to canonicalize.
+ *
+ * Turns any input string into a canonicalized string.
+ *
+ * Canonical identifiers are e.g. expected by the PDB for procedure
+ * and parameter names. Every character of the input string that is
+ * not either '-', 'a-z', 'A-Z' or '0-9' will be replaced by a '-'.
+ *
+ * Return value: The canonicalized identifier. This is a newly
+ * allocated string that should be freed with g_free()
+ * when no longer needed.
+ *
+ * Since: 2.4
+ **/
+gchar *
+gimp_canonicalize_identifier (const gchar *identifier)
+{
+ gchar *canonicalized = NULL;
+
+ if (identifier)
+ {
+ gchar *p;
+
+ canonicalized = g_strdup (identifier);
+
+ for (p = canonicalized; *p != 0; p++)
+ {
+ gchar c = *p;
+
+ if (c != '-' &&
+ (c < '0' || c > '9') &&
+ (c < 'A' || c > 'Z') &&
+ (c < 'a' || c > 'z'))
+ *p = '-';
+ }
+ }
+
+ return canonicalized;
+}
+
+/**
+ * gimp_enum_get_desc:
+ * @enum_class: a #GEnumClass
+ * @value: a value from @enum_class
+ *
+ * Retrieves #GimpEnumDesc associated with the given value, or %NULL.
+ *
+ * Return value: the value's #GimpEnumDesc.
+ *
+ * Since: 2.2
+ **/
+GimpEnumDesc *
+gimp_enum_get_desc (GEnumClass *enum_class,
+ gint value)
+{
+ const GimpEnumDesc *value_desc;
+
+ g_return_val_if_fail (G_IS_ENUM_CLASS (enum_class), NULL);
+
+ value_desc =
+ gimp_enum_get_value_descriptions (G_TYPE_FROM_CLASS (enum_class));
+
+ if (value_desc)
+ {
+ while (value_desc->value_desc)
+ {
+ if (value_desc->value == value)
+ return (GimpEnumDesc *) value_desc;
+
+ value_desc++;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * gimp_enum_get_value:
+ * @enum_type: the #GType of a registered enum
+ * @value: an integer value
+ * @value_name: return location for the value's name (or %NULL)
+ * @value_nick: return location for the value's nick (or %NULL)
+ * @value_desc: return location for the value's translated description (or %NULL)
+ * @value_help: return location for the value's translated help (or %NULL)
+ *
+ * Checks if @value is valid for the enum registered as @enum_type.
+ * If the value exists in that enum, its name, nick and its translated
+ * description and help are returned (if @value_name, @value_nick,
+ * @value_desc and @value_help are not %NULL).
+ *
+ * Return value: %TRUE if @value is valid for the @enum_type,
+ * %FALSE otherwise
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_enum_get_value (GType enum_type,
+ gint value,
+ const gchar **value_name,
+ const gchar **value_nick,
+ const gchar **value_desc,
+ const gchar **value_help)
+{
+ GEnumClass *enum_class;
+ GEnumValue *enum_value;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), FALSE);
+
+ enum_class = g_type_class_ref (enum_type);
+ enum_value = g_enum_get_value (enum_class, value);
+
+ if (enum_value)
+ {
+ if (value_name)
+ *value_name = enum_value->value_name;
+
+ if (value_nick)
+ *value_nick = enum_value->value_nick;
+
+ if (value_desc || value_help)
+ {
+ GimpEnumDesc *enum_desc;
+
+ enum_desc = gimp_enum_get_desc (enum_class, value);
+
+ if (value_desc)
+ {
+ if (enum_desc && enum_desc->value_desc)
+ {
+ const gchar *context;
+
+ context = gimp_type_get_translation_context (enum_type);
+
+ if (context) /* the new way, using NC_() */
+ *value_desc = g_dpgettext2 (gimp_type_get_translation_domain (enum_type),
+ context,
+ enum_desc->value_desc);
+ else /* for backward compatibility */
+ *value_desc = g_strip_context (enum_desc->value_desc,
+ dgettext (gimp_type_get_translation_domain (enum_type),
+ enum_desc->value_desc));
+ }
+ else
+ {
+ *value_desc = NULL;
+ }
+ }
+
+ if (value_help)
+ {
+ *value_help = ((enum_desc && enum_desc->value_help) ?
+ dgettext (gimp_type_get_translation_domain (enum_type),
+ enum_desc->value_help) :
+ NULL);
+ }
+ }
+
+ success = TRUE;
+ }
+
+ g_type_class_unref (enum_class);
+
+ return success;
+}
+
+/**
+ * gimp_enum_value_get_desc:
+ * @enum_class: a #GEnumClass
+ * @enum_value: a #GEnumValue from @enum_class
+ *
+ * Retrieves the translated description for a given @enum_value.
+ *
+ * Return value: the translated description of the enum value
+ *
+ * Since: 2.2
+ **/
+const gchar *
+gimp_enum_value_get_desc (GEnumClass *enum_class,
+ GEnumValue *enum_value)
+{
+ GType type = G_TYPE_FROM_CLASS (enum_class);
+ GimpEnumDesc *enum_desc;
+
+ enum_desc = gimp_enum_get_desc (enum_class, enum_value->value);
+
+ if (enum_desc && enum_desc->value_desc)
+ {
+ const gchar *context;
+
+ context = gimp_type_get_translation_context (type);
+
+ if (context) /* the new way, using NC_() */
+ return g_dpgettext2 (gimp_type_get_translation_domain (type),
+ context,
+ enum_desc->value_desc);
+ else /* for backward compatibility */
+ return g_strip_context (enum_desc->value_desc,
+ dgettext (gimp_type_get_translation_domain (type),
+ enum_desc->value_desc));
+ }
+
+ return enum_value->value_name;
+}
+
+/**
+ * gimp_enum_value_get_help:
+ * @enum_class: a #GEnumClass
+ * @enum_value: a #GEnumValue from @enum_class
+ *
+ * Retrieves the translated help for a given @enum_value.
+ *
+ * Return value: the translated help of the enum value
+ *
+ * Since: 2.2
+ **/
+const gchar *
+gimp_enum_value_get_help (GEnumClass *enum_class,
+ GEnumValue *enum_value)
+{
+ GType type = G_TYPE_FROM_CLASS (enum_class);
+ GimpEnumDesc *enum_desc;
+
+ enum_desc = gimp_enum_get_desc (enum_class, enum_value->value);
+
+ if (enum_desc && enum_desc->value_help)
+ return dgettext (gimp_type_get_translation_domain (type),
+ enum_desc->value_help);
+
+ return NULL;
+}
+
+/**
+ * gimp_enum_value_get_abbrev:
+ * @enum_class: a #GEnumClass
+ * @enum_value: a #GEnumValue from @enum_class
+ *
+ * Retrieves the translated abbreviation for a given @enum_value.
+ *
+ * Return value: the translated abbreviation of the enum value
+ *
+ * Since: 2.10
+ **/
+const gchar *
+gimp_enum_value_get_abbrev (GEnumClass *enum_class,
+ GEnumValue *enum_value)
+{
+ GType type = G_TYPE_FROM_CLASS (enum_class);
+ GimpEnumDesc *enum_desc;
+
+ enum_desc = gimp_enum_get_desc (enum_class, enum_value->value);
+
+ if (enum_desc &&
+ enum_desc[1].value == enum_desc->value &&
+ enum_desc[1].value_desc)
+ {
+ return g_dpgettext2 (gimp_type_get_translation_domain (type),
+ gimp_type_get_translation_context (type),
+ enum_desc[1].value_desc);
+ }
+
+ return NULL;
+}
+
+/**
+ * gimp_flags_get_first_desc:
+ * @flags_class: a #GFlagsClass
+ * @value: a value from @flags_class
+ *
+ * Retrieves the first #GimpFlagsDesc that matches the given value, or %NULL.
+ *
+ * Return value: the value's #GimpFlagsDesc.
+ *
+ * Since: 2.2
+ **/
+GimpFlagsDesc *
+gimp_flags_get_first_desc (GFlagsClass *flags_class,
+ guint value)
+{
+ const GimpFlagsDesc *value_desc;
+
+ g_return_val_if_fail (G_IS_FLAGS_CLASS (flags_class), NULL);
+
+ value_desc =
+ gimp_flags_get_value_descriptions (G_TYPE_FROM_CLASS (flags_class));
+
+ if (value_desc)
+ {
+ while (value_desc->value_desc)
+ {
+ if ((value_desc->value & value) == value_desc->value)
+ return (GimpFlagsDesc *) value_desc;
+
+ value_desc++;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * gimp_flags_get_first_value:
+ * @flags_type: the #GType of registered flags
+ * @value: an integer value
+ * @value_name: return location for the value's name (or %NULL)
+ * @value_nick: return location for the value's nick (or %NULL)
+ * @value_desc: return location for the value's translated description (or %NULL)
+ * @value_help: return location for the value's translated help (or %NULL)
+ *
+ * Checks if @value is valid for the flags registered as @flags_type.
+ * If the value exists in that flags, its name, nick and its
+ * translated description and help are returned (if @value_name,
+ * @value_nick, @value_desc and @value_help are not %NULL).
+ *
+ * Return value: %TRUE if @value is valid for the @flags_type,
+ * %FALSE otherwise
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_flags_get_first_value (GType flags_type,
+ guint value,
+ const gchar **value_name,
+ const gchar **value_nick,
+ const gchar **value_desc,
+ const gchar **value_help)
+{
+ GFlagsClass *flags_class;
+ GFlagsValue *flags_value;
+
+ g_return_val_if_fail (G_TYPE_IS_FLAGS (flags_type), FALSE);
+
+ flags_class = g_type_class_peek (flags_type);
+ flags_value = g_flags_get_first_value (flags_class, value);
+
+ if (flags_value)
+ {
+ if (value_name)
+ *value_name = flags_value->value_name;
+
+ if (value_nick)
+ *value_nick = flags_value->value_nick;
+
+ if (value_desc || value_help)
+ {
+ GimpFlagsDesc *flags_desc;
+
+ flags_desc = gimp_flags_get_first_desc (flags_class, value);
+
+ if (value_desc)
+ *value_desc = ((flags_desc && flags_desc->value_desc) ?
+ dgettext (gimp_type_get_translation_domain (flags_type),
+ flags_desc->value_desc) :
+ NULL);
+
+ if (value_help)
+ *value_help = ((flags_desc && flags_desc->value_desc) ?
+ dgettext (gimp_type_get_translation_domain (flags_type),
+ flags_desc->value_help) :
+ NULL);
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * gimp_flags_value_get_desc:
+ * @flags_class: a #GFlagsClass
+ * @flags_value: a #GFlagsValue from @flags_class
+ *
+ * Retrieves the translated description for a given @flags_value.
+ *
+ * Return value: the translated description of the flags value
+ *
+ * Since: 2.2
+ **/
+const gchar *
+gimp_flags_value_get_desc (GFlagsClass *flags_class,
+ GFlagsValue *flags_value)
+{
+ GType type = G_TYPE_FROM_CLASS (flags_class);
+ GimpFlagsDesc *flags_desc;
+
+ flags_desc = gimp_flags_get_first_desc (flags_class, flags_value->value);
+
+ if (flags_desc->value_desc)
+ {
+ const gchar *context;
+
+ context = gimp_type_get_translation_context (type);
+
+ if (context) /* the new way, using NC_() */
+ return g_dpgettext2 (gimp_type_get_translation_domain (type),
+ context,
+ flags_desc->value_desc);
+ else /* for backward compatibility */
+ return g_strip_context (flags_desc->value_desc,
+ dgettext (gimp_type_get_translation_domain (type),
+ flags_desc->value_desc));
+ }
+
+ return flags_value->value_name;
+}
+
+/**
+ * gimp_flags_value_get_help:
+ * @flags_class: a #GFlagsClass
+ * @flags_value: a #GFlagsValue from @flags_class
+ *
+ * Retrieves the translated help for a given @flags_value.
+ *
+ * Return value: the translated help of the flags value
+ *
+ * Since: 2.2
+ **/
+const gchar *
+gimp_flags_value_get_help (GFlagsClass *flags_class,
+ GFlagsValue *flags_value)
+{
+ GType type = G_TYPE_FROM_CLASS (flags_class);
+ GimpFlagsDesc *flags_desc;
+
+ flags_desc = gimp_flags_get_first_desc (flags_class, flags_value->value);
+
+ if (flags_desc->value_help)
+ return dgettext (gimp_type_get_translation_domain (type),
+ flags_desc->value_help);
+
+ return NULL;
+}
+
+/**
+ * gimp_flags_value_get_abbrev:
+ * @flags_class: a #GFlagsClass
+ * @flags_value: a #GFlagsValue from @flags_class
+ *
+ * Retrieves the translated abbreviation for a given @flags_value.
+ *
+ * Return value: the translated abbreviation of the flags value
+ *
+ * Since: 2.10
+ **/
+const gchar *
+gimp_flags_value_get_abbrev (GFlagsClass *flags_class,
+ GFlagsValue *flags_value)
+{
+ GType type = G_TYPE_FROM_CLASS (flags_class);
+ GimpFlagsDesc *flags_desc;
+
+ flags_desc = gimp_flags_get_first_desc (flags_class, flags_value->value);
+
+ if (flags_desc &&
+ flags_desc[1].value == flags_desc->value &&
+ flags_desc[1].value_desc)
+ {
+ return g_dpgettext2 (gimp_type_get_translation_domain (type),
+ gimp_type_get_translation_context (type),
+ flags_desc[1].value_desc);
+ }
+
+ return NULL;
+}
+
+/**
+ * gimp_stack_trace_available:
+ * @optimal: whether we get optimal traces.
+ *
+ * Returns #TRUE if we have dependencies to generate backtraces. If
+ * @optimal is #TRUE, the function will return #TRUE only when we
+ * are able to generate optimal traces (i.e. with GDB or LLDB);
+ * otherwise we return #TRUE even if only backtrace() API is available.
+ *
+ * On Win32, we return TRUE if Dr. Mingw is built-in, FALSE otherwise.
+ *
+ * Note: this function is not crash-safe, i.e. you should not try to use
+ * it in a callback when the program is already crashing. In such a
+ * case, call gimp_stack_trace_print() or gimp_stack_trace_query()
+ * directly.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_stack_trace_available (gboolean optimal)
+{
+#ifndef G_OS_WIN32
+ gchar *lld_path = NULL;
+ gboolean has_lldb = FALSE;
+
+ /* Similarly to gdb, we could check for lldb by calling:
+ * gimp_utils_generic_available ("lldb", major, minor).
+ * We don't do so on purpose because on macOS, when lldb is absent, it
+ * triggers a popup asking to install Xcode. So instead, we just
+ * search for the executable in path.
+ * This is the reason why this function is not crash-safe, since
+ * g_find_program_in_path() allocates memory.
+ * See issue #1999.
+ */
+ lld_path = g_find_program_in_path ("lldb");
+ if (lld_path)
+ {
+ has_lldb = TRUE;
+ g_free (lld_path);
+ }
+
+ if (gimp_utils_gdb_available (7, 0) || has_lldb)
+ return TRUE;
+#ifdef HAVE_EXECINFO_H
+ if (! optimal)
+ return TRUE;
+#endif
+#else /* G_OS_WIN32 */
+#ifdef HAVE_EXCHNDL
+ return TRUE;
+#endif
+#endif /* G_OS_WIN32 */
+ return FALSE;
+}
+
+/**
+ * gimp_stack_trace_print:
+ * @prog_name: the program to attach to.
+ * @stream: a #FILE * stream.
+ * @trace: location to store a newly allocated string of the trace.
+ *
+ * Attempts to generate a stack trace at current code position in
+ * @prog_name. @prog_name is mostly a helper and can be set to NULL.
+ * Nevertheless if set, it has to be the current program name (argv[0]).
+ * This function is not meant to generate stack trace for third-party
+ * programs, and will attach the current process id only.
+ * Internally, this function uses `gdb` or `lldb` if they are available,
+ * or the stacktrace() API on platforms where it is available. It always
+ * fails on Win32.
+ *
+ * The stack trace, once generated, will either be printed to @stream or
+ * returned as a newly allocated string in @trace, if not #NULL.
+ *
+ * In some error cases (e.g. segmentation fault), trying to allocate
+ * more memory will trigger more segmentation faults and therefore loop
+ * our error handling (which is just wrong). Therefore printing to a
+ * file description is an implementation without any memory allocation.
+
+ * Return value: #TRUE if a stack trace could be generated, #FALSE
+ * otherwise.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_stack_trace_print (const gchar *prog_name,
+ gpointer stream,
+ gchar **trace)
+{
+ gboolean stack_printed = FALSE;
+
+ /* This works only on UNIX systems. */
+#ifndef G_OS_WIN32
+ GString *gtrace = NULL;
+ gchar gimp_pid[16];
+ gchar buffer[256];
+ ssize_t read_n;
+ int sync_fd[2];
+ int out_fd[2];
+ pid_t fork_pid;
+ pid_t pid = getpid();
+ gint eintr_count = 0;
+#if defined(G_OS_WIN32)
+ DWORD tid = GetCurrentThreadId ();
+#elif defined(PLATFORM_OSX)
+ uint64 tid64;
+ long tid;
+
+ pthread_threadid_np (NULL, &tid64);
+ tid = (long) tid64;
+#elif defined(SYS_gettid)
+ long tid = syscall (SYS_gettid);
+#elif defined(HAVE_THR_SELF)
+ long tid = 0;
+ thr_self (&tid);
+#endif
+
+ g_snprintf (gimp_pid, 16, "%u", (guint) pid);
+
+ if (pipe (sync_fd) == -1)
+ {
+ return FALSE;
+ }
+
+ if (pipe (out_fd) == -1)
+ {
+ close (sync_fd[0]);
+ close (sync_fd[1]);
+
+ return FALSE;
+ }
+
+ fork_pid = fork ();
+ if (fork_pid == 0)
+ {
+ /* Child process. */
+ gchar *args[9] = { "gdb", "-batch",
+ "-ex", "info threads",
+ "-ex", "thread apply all backtrace full",
+ (gchar *) prog_name, NULL, NULL };
+
+ if (prog_name == NULL)
+ args[6] = "-p";
+
+ args[7] = gimp_pid;
+
+ /* Wait until the parent enabled us to ptrace it. */
+ {
+ gchar dummy;
+
+ close (sync_fd[1]);
+ while (read (sync_fd[0], &dummy, 1) < 0 && errno == EINTR);
+ close (sync_fd[0]);
+ }
+
+ /* Redirect the debugger output. */
+ dup2 (out_fd[1], STDOUT_FILENO);
+ close (out_fd[0]);
+ close (out_fd[1]);
+
+ /* Run GDB if version 7.0 or over. Why I do such a check is that
+ * it turns out older versions may not only fail, but also have
+ * very undesirable side effects like terminating the debugged
+ * program, at least on FreeBSD where GDB 6.1 is apparently
+ * installed by default on the stable release at day of writing.
+ * See bug 793514. */
+ if (! gimp_utils_gdb_available (7, 0) ||
+ execvp (args[0], args) == -1)
+ {
+ /* LLDB as alternative if the GDB call failed or if it was in
+ * a too-old version. */
+ gchar *args_lldb[15] = { "lldb", "--attach-pid", NULL, "--batch",
+ "--one-line", "thread list",
+ "--one-line", "thread backtrace all",
+ "--one-line", "bt all",
+ "--one-line-on-crash", "bt",
+ "--one-line-on-crash", "quit", NULL };
+
+ args_lldb[2] = gimp_pid;
+
+ execvp (args_lldb[0], args_lldb);
+ }
+
+ _exit (0);
+ }
+ else if (fork_pid > 0)
+ {
+ /* Main process */
+ int status;
+
+ /* Allow the child to ptrace us, and signal it to start. */
+ close (sync_fd[0]);
+#ifdef PR_SET_PTRACER
+ prctl (PR_SET_PTRACER, fork_pid, 0, 0, 0);
+#endif
+ close (sync_fd[1]);
+
+ /* It is important to close the writing side of the pipe, otherwise
+ * the read() will wait forever without getting the information that
+ * writing is finished.
+ */
+ close (out_fd[1]);
+
+ while ((read_n = read (out_fd[0], buffer, 256)) != 0)
+ {
+ if (read_n < 0)
+ {
+ /* LLDB on macOS seems to trigger a few EINTR error (see
+ * !13), though read() finally ends up working later. So
+ * let's not make this error fatal, and instead try again.
+ * Yet to avoid infinite loop (in case the error really
+ * happens at every call), we abandon after a few
+ * consecutive errors.
+ */
+ if (errno == EINTR && eintr_count <= 5)
+ {
+ eintr_count++;
+ continue;
+ }
+ break;
+ }
+ eintr_count = 0;
+ if (! stack_printed)
+ {
+#if defined(PLATFORM_OSX)
+ if (stream)
+ g_fprintf (stream,
+ "\n# Stack traces obtained from PID %d - Thread 0x%lx #\n\n",
+ pid, tid);
+#elif defined(G_OS_WIN32) || defined(SYS_gettid) || defined(HAVE_THR_SELF)
+ if (stream)
+ g_fprintf (stream,
+ "\n# Stack traces obtained from PID %d - Thread %lu #\n\n",
+ pid, tid);
+#endif
+ if (trace)
+ {
+ gtrace = g_string_new (NULL);
+#if defined(PLATFORM_OSX)
+ g_string_printf (gtrace,
+ "\n# Stack traces obtained from PID %d - Thread 0x%lx #\n\n",
+ pid, tid);
+#elif defined(G_OS_WIN32) || defined(SYS_gettid) || defined(HAVE_THR_SELF)
+ g_string_printf (gtrace,
+ "\n# Stack traces obtained from PID %d - Thread %lu #\n\n",
+ pid, tid);
+#endif
+ }
+ }
+ /* It's hard to know if the debugger was found since it
+ * happened in the child. Let's just assume that any output
+ * means it succeeded.
+ */
+ stack_printed = TRUE;
+
+ buffer[read_n] = '\0';
+ if (stream)
+ g_fprintf (stream, "%s", buffer);
+ if (trace)
+ g_string_append (gtrace, (const gchar *) buffer);
+ }
+ close (out_fd[0]);
+
+#ifdef PR_SET_PTRACER
+ /* Clear ptrace permission set above */
+ prctl (PR_SET_PTRACER, 0, 0, 0, 0);
+#endif
+
+ waitpid (fork_pid, &status, 0);
+ }
+ /* else if (fork_pid == (pid_t) -1)
+ * Fork failed!
+ * Just continue, maybe the backtrace() API will succeed.
+ */
+
+#ifdef HAVE_EXECINFO_H
+ if (! stack_printed)
+ {
+ /* As a last resort, try using the backtrace() Linux API. It is a bit
+ * less fancy than gdb or lldb, which is why it is not given priority.
+ */
+ void *bt_buf[100];
+ int n_symbols;
+
+ n_symbols = backtrace (bt_buf, 100);
+ if (trace && n_symbols)
+ {
+ char **symbols;
+ int i;
+
+ symbols = backtrace_symbols (bt_buf, n_symbols);
+ if (symbols)
+ {
+ for (i = 0; i < n_symbols; i++)
+ {
+ if (stream)
+ g_fprintf (stream, "%s\n", (const gchar *) symbols[i]);
+ if (trace)
+ {
+ if (! gtrace)
+ gtrace = g_string_new (NULL);
+ g_string_append (gtrace,
+ (const gchar *) symbols[i]);
+ g_string_append_c (gtrace, '\n');
+ }
+ }
+ free (symbols);
+ }
+ }
+ else if (n_symbols)
+ {
+ /* This allows to generate traces without memory allocation.
+ * In some cases, this is necessary, especially during
+ * segfault-type crashes.
+ */
+ backtrace_symbols_fd (bt_buf, n_symbols, fileno ((FILE *) stream));
+ }
+ stack_printed = (n_symbols > 0);
+ }
+#endif /* HAVE_EXECINFO_H */
+
+ if (trace)
+ {
+ if (gtrace)
+ *trace = g_string_free (gtrace, FALSE);
+ else
+ *trace = NULL;
+ }
+#endif /* G_OS_WIN32 */
+
+ return stack_printed;
+}
+
+/**
+ * gimp_stack_trace_query:
+ * @prog_name: the program to attach to.
+ *
+ * This is mostly the same as g_on_error_query() except that we use our
+ * own backtrace function, much more complete.
+ * @prog_name must be the current program name (argv[0]).
+ * It does nothing on Win32.
+ *
+ * Since: 2.10
+ **/
+void
+gimp_stack_trace_query (const gchar *prog_name)
+{
+#ifndef G_OS_WIN32
+ gchar buf[16];
+
+ retry:
+
+ g_fprintf (stdout,
+ "%s (pid:%u): %s: ",
+ prog_name,
+ (guint) getpid (),
+ "[E]xit, show [S]tack trace or [P]roceed");
+ fflush (stdout);
+
+ if (isatty(0) && isatty(1))
+ fgets (buf, 8, stdin);
+ else
+ strcpy (buf, "E\n");
+
+ if ((buf[0] == 'E' || buf[0] == 'e')
+ && buf[1] == '\n')
+ _exit (0);
+ else if ((buf[0] == 'P' || buf[0] == 'p')
+ && buf[1] == '\n')
+ return;
+ else if ((buf[0] == 'S' || buf[0] == 's')
+ && buf[1] == '\n')
+ {
+ if (! gimp_stack_trace_print (prog_name, stdout, NULL))
+ g_fprintf (stderr, "%s\n", "Stack trace not available on your system.");
+ goto retry;
+ }
+ else
+ goto retry;
+#endif
+}
+
+
+/* Private functions. */
+
+static gboolean
+gimp_utils_generic_available (const gchar *program,
+ gint major,
+ gint minor)
+{
+#ifndef G_OS_WIN32
+ pid_t pid;
+ int out_fd[2];
+
+ if (pipe (out_fd) == -1)
+ {
+ return FALSE;
+ }
+
+ /* XXX: I don't use g_spawn_sync() or similar glib functions because
+ * to read the contents of the stdout, these functions would allocate
+ * memory dynamically. As we know, when debugging crashes, this is a
+ * definite blocker. So instead I simply use a buffer on the stack
+ * with a lower level fork() call.
+ */
+ pid = fork ();
+ if (pid == 0)
+ {
+ /* Child process. */
+ gchar *args[3] = { (gchar *) program, "--version", NULL };
+
+ /* Redirect the debugger output. */
+ dup2 (out_fd[1], STDOUT_FILENO);
+ close (out_fd[0]);
+ close (out_fd[1]);
+
+ /* Run version check. */
+ execvp (args[0], args);
+ _exit (-1);
+ }
+ else if (pid > 0)
+ {
+ /* Main process */
+ gchar buffer[256];
+ ssize_t read_n;
+ int status;
+ gint installed_major = 0;
+ gint installed_minor = 0;
+ gboolean major_reading = FALSE;
+ gboolean minor_reading = FALSE;
+ gint i;
+ gchar c;
+
+ waitpid (pid, &status, 0);
+
+ if (! WIFEXITED (status) || WEXITSTATUS (status) != 0)
+ return FALSE;
+
+ /* It is important to close the writing side of the pipe, otherwise
+ * the read() will wait forever without getting the information that
+ * writing is finished.
+ */
+ close (out_fd[1]);
+
+ /* I could loop forever until EOL, but I am pretty sure the
+ * version information is stored on the first line and one call to
+ * read() with 256 characters should be more than enough.
+ */
+ read_n = read (out_fd[0], buffer, 256);
+
+ /* This is quite a very stupid parser. I only look for the first
+ * numbers and consider them as version information. This works
+ * fine for both GDB and LLDB as far as I can see for the output
+ * of `${program} --version` but this should obviously not be
+ * considered as a *really* generic version test.
+ */
+ for (i = 0; i < read_n; i++)
+ {
+ c = buffer[i];
+ if (c >= '0' && c <= '9')
+ {
+ if (minor_reading)
+ {
+ installed_minor = 10 * installed_minor + (c - '0');
+ }
+ else
+ {
+ major_reading = TRUE;
+ installed_major = 10 * installed_major + (c - '0');
+ }
+ }
+ else if (c == '.')
+ {
+ if (major_reading)
+ {
+ minor_reading = TRUE;
+ major_reading = FALSE;
+ }
+ else if (minor_reading)
+ {
+ break;
+ }
+ }
+ else if (c == '\n')
+ {
+ /* Version information should be in the first line. */
+ break;
+ }
+ }
+ close (out_fd[0]);
+
+ return (installed_major > 0 &&
+ (installed_major > major ||
+ (installed_major == major && installed_minor >= minor)));
+ }
+#endif
+
+ /* Fork failed, or Win32. */
+ return FALSE;
+}
+
+static gboolean
+gimp_utils_gdb_available (gint major,
+ gint minor)
+{
+ return gimp_utils_generic_available ("gdb", major, minor);
+}
diff --git a/libgimpbase/gimputils.h b/libgimpbase/gimputils.h
new file mode 100644
index 0000000..ca7b730
--- /dev/null
+++ b/libgimpbase/gimputils.h
@@ -0,0 +1,87 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_BASE_H_INSIDE__) && !defined (GIMP_BASE_COMPILATION)
+#error "Only <libgimpbase/gimpbase.h> can be included directly."
+#endif
+
+#ifndef __GIMP_UTILS_H__
+#define __GIMP_UTILS_H__
+
+G_BEGIN_DECLS
+
+
+gchar * gimp_utf8_strtrim (const gchar *str,
+ gint max_chars) G_GNUC_MALLOC;
+gchar * gimp_any_to_utf8 (const gchar *str,
+ gssize len,
+ const gchar *warning_format,
+ ...) G_GNUC_PRINTF (3, 4) G_GNUC_MALLOC;
+const gchar * gimp_filename_to_utf8 (const gchar *filename);
+
+const gchar * gimp_file_get_utf8_name (GFile *file);
+gboolean gimp_file_has_extension (GFile *file,
+ const gchar *extension);
+gboolean gimp_file_show_in_file_manager (GFile *file,
+ GError **error);
+
+gchar * gimp_strip_uline (const gchar *str) G_GNUC_MALLOC;
+gchar * gimp_escape_uline (const gchar *str) G_GNUC_MALLOC;
+
+gchar * gimp_canonicalize_identifier (const gchar *identifier) G_GNUC_MALLOC;
+
+GimpEnumDesc * gimp_enum_get_desc (GEnumClass *enum_class,
+ gint value);
+gboolean gimp_enum_get_value (GType enum_type,
+ gint value,
+ const gchar **value_name,
+ const gchar **value_nick,
+ const gchar **value_desc,
+ const gchar **value_help);
+const gchar * gimp_enum_value_get_desc (GEnumClass *enum_class,
+ GEnumValue *enum_value);
+const gchar * gimp_enum_value_get_help (GEnumClass *enum_class,
+ GEnumValue *enum_value);
+const gchar * gimp_enum_value_get_abbrev (GEnumClass *enum_class,
+ GEnumValue *enum_value);
+
+GimpFlagsDesc * gimp_flags_get_first_desc (GFlagsClass *flags_class,
+ guint value);
+gboolean gimp_flags_get_first_value (GType flags_type,
+ guint value,
+ const gchar **value_name,
+ const gchar **value_nick,
+ const gchar **value_desc,
+ const gchar **value_help);
+const gchar * gimp_flags_value_get_desc (GFlagsClass *flags_class,
+ GFlagsValue *flags_value);
+const gchar * gimp_flags_value_get_help (GFlagsClass *flags_class,
+ GFlagsValue *flags_value);
+const gchar * gimp_flags_value_get_abbrev (GFlagsClass *flags_class,
+ GFlagsValue *flags_value);
+
+gboolean gimp_stack_trace_available (gboolean optimal);
+gboolean gimp_stack_trace_print (const gchar *prog_name,
+ gpointer stream,
+ gchar **trace);
+void gimp_stack_trace_query (const gchar *prog_name);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_UTILS_H__ */
diff --git a/libgimpbase/gimpvaluearray.c b/libgimpbase/gimpvaluearray.c
new file mode 100644
index 0000000..eb23b82
--- /dev/null
+++ b/libgimpbase/gimpvaluearray.c
@@ -0,0 +1,589 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpvaluearray.c ported from GValueArray
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib-object.h>
+
+#include "gimpbasetypes.h"
+
+#include "gimpvaluearray.h"
+
+
+/**
+ * SECTION:gimpvaluearray
+ * @short_description: A container structure to maintain an array of
+ * generic values
+ * @see_also: #GValue, #GParamSpecValueArray, gimp_param_spec_value_array()
+ * @title: GimpValueArray
+ *
+ * The prime purpose of a #GimpValueArray is for it to be used as an
+ * object property that holds an array of values. A #GimpValueArray wraps
+ * an array of #GValue elements in order for it to be used as a boxed
+ * type through %GIMP_TYPE_VALUE_ARRAY.
+ */
+
+
+#define GROUP_N_VALUES (1) /* power of 2 !! */
+
+
+/**
+ * GimpValueArray:
+ *
+ * A #GimpValueArray contains an array of #GValue elements.
+ *
+ * Since: 2.10
+ */
+struct _GimpValueArray
+{
+ gint n_values;
+ GValue *values;
+
+ gint n_prealloced;
+ gint ref_count;
+};
+
+
+G_DEFINE_BOXED_TYPE (GimpValueArray, gimp_value_array,
+ gimp_value_array_ref, gimp_value_array_unref)
+
+
+/**
+ * gimp_value_array_index:
+ * @value_array: #GimpValueArray to get a value from
+ * @index: index of the value of interest
+ *
+ * Return a pointer to the value at @index contained in @value_array.
+ *
+ * Returns: (transfer none): pointer to a value at @index in @value_array
+ *
+ * Since: 2.10
+ */
+GValue *
+gimp_value_array_index (const GimpValueArray *value_array,
+ gint index)
+{
+ g_return_val_if_fail (value_array != NULL, NULL);
+ g_return_val_if_fail (index < value_array->n_values, NULL);
+
+ return value_array->values + index;
+}
+
+static inline void
+value_array_grow (GimpValueArray *value_array,
+ gint n_values,
+ gboolean zero_init)
+{
+ g_return_if_fail ((guint) n_values >= (guint) value_array->n_values);
+
+ value_array->n_values = n_values;
+ if (value_array->n_values > value_array->n_prealloced)
+ {
+ gint i = value_array->n_prealloced;
+
+ value_array->n_prealloced = (value_array->n_values + GROUP_N_VALUES - 1) & ~(GROUP_N_VALUES - 1);
+ value_array->values = g_renew (GValue, value_array->values, value_array->n_prealloced);
+
+ if (!zero_init)
+ i = value_array->n_values;
+
+ memset (value_array->values + i, 0,
+ (value_array->n_prealloced - i) * sizeof (value_array->values[0]));
+ }
+}
+
+static inline void
+value_array_shrink (GimpValueArray *value_array)
+{
+ if (value_array->n_prealloced >= value_array->n_values + GROUP_N_VALUES)
+ {
+ value_array->n_prealloced = (value_array->n_values + GROUP_N_VALUES - 1) & ~(GROUP_N_VALUES - 1);
+ value_array->values = g_renew (GValue, value_array->values, value_array->n_prealloced);
+ }
+}
+
+/**
+ * gimp_value_array_new:
+ * @n_prealloced: number of values to preallocate space for
+ *
+ * Allocate and initialize a new #GimpValueArray, optionally preserve space
+ * for @n_prealloced elements. New arrays always contain 0 elements,
+ * regardless of the value of @n_prealloced.
+ *
+ * Returns: a newly allocated #GimpValueArray with 0 values
+ *
+ * Since: 2.10
+ */
+GimpValueArray *
+gimp_value_array_new (gint n_prealloced)
+{
+ GimpValueArray *value_array = g_slice_new (GimpValueArray);
+
+ value_array->n_values = 0;
+ value_array->n_prealloced = 0;
+ value_array->values = NULL;
+ value_array_grow (value_array, n_prealloced, TRUE);
+ value_array->n_values = 0;
+ value_array->ref_count = 1;
+
+ return value_array;
+}
+
+/**
+ * gimp_value_array_ref:
+ * @value_array: #GimpValueArray to ref
+ *
+ * Adds a reference to a #GimpValueArray.
+ *
+ * Return value: the same @value_array
+ *
+ * Since: 2.10
+ */
+GimpValueArray *
+gimp_value_array_ref (GimpValueArray *value_array)
+{
+ g_return_val_if_fail (value_array != NULL, NULL);
+
+ value_array->ref_count++;
+
+ return value_array;
+}
+
+/**
+ * gimp_value_array_unref:
+ * @value_array: #GimpValueArray to unref
+ *
+ * Unref a #GimpValueArray. If the reference count drops to zero, the
+ * array including its contents are freed.
+ *
+ * Since: 2.10
+ */
+void
+gimp_value_array_unref (GimpValueArray *value_array)
+{
+ g_return_if_fail (value_array != NULL);
+
+ value_array->ref_count--;
+
+ if (value_array->ref_count < 1)
+ {
+ gint i;
+
+ for (i = 0; i < value_array->n_values; i++)
+ {
+ GValue *value = value_array->values + i;
+
+ if (G_VALUE_TYPE (value) != 0) /* we allow unset values in the array */
+ g_value_unset (value);
+ }
+
+ g_free (value_array->values);
+ g_slice_free (GimpValueArray, value_array);
+ }
+}
+
+gint
+gimp_value_array_length (const GimpValueArray *value_array)
+{
+ g_return_val_if_fail (value_array != NULL, 0);
+
+ return value_array->n_values;
+}
+
+/**
+ * gimp_value_array_prepend:
+ * @value_array: #GimpValueArray to add an element to
+ * @value: (allow-none): #GValue to copy into #GimpValueArray, or %NULL
+ *
+ * Insert a copy of @value as first element of @value_array. If @value is
+ * %NULL, an uninitialized value is prepended.
+ *
+ * Returns: (transfer none): the #GimpValueArray passed in as @value_array
+ *
+ * Since: 2.10
+ */
+GimpValueArray *
+gimp_value_array_prepend (GimpValueArray *value_array,
+ const GValue *value)
+{
+ g_return_val_if_fail (value_array != NULL, NULL);
+
+ return gimp_value_array_insert (value_array, 0, value);
+}
+
+/**
+ * gimp_value_array_append:
+ * @value_array: #GimpValueArray to add an element to
+ * @value: (allow-none): #GValue to copy into #GimpValueArray, or %NULL
+ *
+ * Insert a copy of @value as last element of @value_array. If @value is
+ * %NULL, an uninitialized value is appended.
+ *
+ * Returns: (transfer none): the #GimpValueArray passed in as @value_array
+ *
+ * Since: 2.10
+ */
+GimpValueArray *
+gimp_value_array_append (GimpValueArray *value_array,
+ const GValue *value)
+{
+ g_return_val_if_fail (value_array != NULL, NULL);
+
+ return gimp_value_array_insert (value_array, value_array->n_values, value);
+}
+
+/**
+ * gimp_value_array_insert:
+ * @value_array: #GimpValueArray to add an element to
+ * @index: insertion position, must be &lt;= gimp_value_array_length()
+ * @value: (allow-none): #GValue to copy into #GimpValueArray, or %NULL
+ *
+ * Insert a copy of @value at specified position into @value_array. If @value
+ * is %NULL, an uninitialized value is inserted.
+ *
+ * Returns: (transfer none): the #GimpValueArray passed in as @value_array
+ *
+ * Since: 2.10
+ */
+GimpValueArray *
+gimp_value_array_insert (GimpValueArray *value_array,
+ gint index,
+ const GValue *value)
+{
+ gint i;
+
+ g_return_val_if_fail (value_array != NULL, NULL);
+ g_return_val_if_fail (index <= value_array->n_values, value_array);
+
+ i = value_array->n_values;
+ value_array_grow (value_array, value_array->n_values + 1, FALSE);
+
+ if (index + 1 < value_array->n_values)
+ memmove (value_array->values + index + 1, value_array->values + index,
+ (i - index) * sizeof (value_array->values[0]));
+
+ memset (value_array->values + index, 0, sizeof (value_array->values[0]));
+
+ if (value)
+ {
+ g_value_init (value_array->values + index, G_VALUE_TYPE (value));
+ g_value_copy (value, value_array->values + index);
+ }
+
+ return value_array;
+}
+
+/**
+ * gimp_value_array_remove:
+ * @value_array: #GimpValueArray to remove an element from
+ * @index: position of value to remove, which must be less than
+ * gimp_value_array_length()
+ *
+ * Remove the value at position @index from @value_array.
+ *
+ * Returns: (transfer none): the #GimpValueArray passed in as @value_array
+ *
+ * Since: 2.10
+ */
+GimpValueArray *
+gimp_value_array_remove (GimpValueArray *value_array,
+ gint index)
+{
+ g_return_val_if_fail (value_array != NULL, NULL);
+ g_return_val_if_fail (index < value_array->n_values, value_array);
+
+ if (G_VALUE_TYPE (value_array->values + index) != 0)
+ g_value_unset (value_array->values + index);
+
+ value_array->n_values--;
+
+ if (index < value_array->n_values)
+ memmove (value_array->values + index, value_array->values + index + 1,
+ (value_array->n_values - index) * sizeof (value_array->values[0]));
+
+ value_array_shrink (value_array);
+
+ if (value_array->n_prealloced > value_array->n_values)
+ memset (value_array->values + value_array->n_values, 0, sizeof (value_array->values[0]));
+
+ return value_array;
+}
+
+void
+gimp_value_array_truncate (GimpValueArray *value_array,
+ gint n_values)
+{
+ gint i;
+
+ g_return_if_fail (value_array != NULL);
+ g_return_if_fail (n_values > 0 && n_values <= value_array->n_values);
+
+ for (i = value_array->n_values; i > n_values; i--)
+ gimp_value_array_remove (value_array, i - 1);
+}
+
+
+/*
+ * GIMP_TYPE_PARAM_VALUE_ARRAY
+ */
+
+static void gimp_param_value_array_class_init (GParamSpecClass *klass);
+static void gimp_param_value_array_init (GParamSpec *pspec);
+static void gimp_param_value_array_finalize (GParamSpec *pspec);
+static void gimp_param_value_array_set_default (GParamSpec *pspec,
+ GValue *value);
+static gboolean gimp_param_value_array_validate (GParamSpec *pspec,
+ GValue *value);
+static gint gimp_param_value_array_values_cmp (GParamSpec *pspec,
+ const GValue *value1,
+ const GValue *value2);
+
+GType
+gimp_param_value_array_get_type (void)
+{
+ static GType type = 0;
+
+ if (! type)
+ {
+ const GTypeInfo info =
+ {
+ sizeof (GParamSpecClass),
+ NULL, NULL,
+ (GClassInitFunc) gimp_param_value_array_class_init,
+ NULL, NULL,
+ sizeof (GimpParamSpecValueArray),
+ 0,
+ (GInstanceInitFunc) gimp_param_value_array_init
+ };
+
+ type = g_type_register_static (G_TYPE_PARAM_BOXED,
+ "GimpParamValueArray", &info, 0);
+ }
+
+ return type;
+}
+
+
+static void
+gimp_param_value_array_class_init (GParamSpecClass *klass)
+{
+ klass->value_type = GIMP_TYPE_VALUE_ARRAY;
+ klass->finalize = gimp_param_value_array_finalize;
+ klass->value_set_default = gimp_param_value_array_set_default;
+ klass->value_validate = gimp_param_value_array_validate;
+ klass->values_cmp = gimp_param_value_array_values_cmp;
+}
+
+static void
+gimp_param_value_array_init (GParamSpec *pspec)
+{
+ GimpParamSpecValueArray *aspec = GIMP_PARAM_SPEC_VALUE_ARRAY (pspec);
+
+ aspec->element_spec = NULL;
+ aspec->fixed_n_elements = 0; /* disable */
+}
+
+static inline guint
+gimp_value_array_ensure_size (GimpValueArray *value_array,
+ guint fixed_n_elements)
+{
+ guint changed = 0;
+
+ if (fixed_n_elements)
+ {
+ while (gimp_value_array_length (value_array) < fixed_n_elements)
+ {
+ gimp_value_array_append (value_array, NULL);
+ changed++;
+ }
+
+ while (gimp_value_array_length (value_array) > fixed_n_elements)
+ {
+ gimp_value_array_remove (value_array,
+ gimp_value_array_length (value_array) - 1);
+ changed++;
+ }
+ }
+
+ return changed;
+}
+
+static void
+gimp_param_value_array_finalize (GParamSpec *pspec)
+{
+ GimpParamSpecValueArray *aspec = GIMP_PARAM_SPEC_VALUE_ARRAY (pspec);
+ GParamSpecClass *parent_class;
+
+ parent_class = g_type_class_peek (g_type_parent (GIMP_TYPE_PARAM_VALUE_ARRAY));
+
+ g_clear_pointer (&aspec->element_spec, g_param_spec_unref);
+
+ parent_class->finalize (pspec);
+}
+
+static void
+gimp_param_value_array_set_default (GParamSpec *pspec,
+ GValue *value)
+{
+ GimpParamSpecValueArray *aspec = GIMP_PARAM_SPEC_VALUE_ARRAY (pspec);
+
+ if (! value->data[0].v_pointer && aspec->fixed_n_elements)
+ value->data[0].v_pointer = gimp_value_array_new (aspec->fixed_n_elements);
+
+ if (value->data[0].v_pointer)
+ {
+ /* g_value_reset (value); already done */
+ gimp_value_array_ensure_size (value->data[0].v_pointer,
+ aspec->fixed_n_elements);
+ }
+}
+
+static gboolean
+gimp_param_value_array_validate (GParamSpec *pspec,
+ GValue *value)
+{
+ GimpParamSpecValueArray *aspec = GIMP_PARAM_SPEC_VALUE_ARRAY (pspec);
+ GimpValueArray *value_array = value->data[0].v_pointer;
+ guint changed = 0;
+
+ if (! value->data[0].v_pointer && aspec->fixed_n_elements)
+ value->data[0].v_pointer = gimp_value_array_new (aspec->fixed_n_elements);
+
+ if (value->data[0].v_pointer)
+ {
+ /* ensure array size validity */
+ changed += gimp_value_array_ensure_size (value_array,
+ aspec->fixed_n_elements);
+
+ /* ensure array values validity against a present element spec */
+ if (aspec->element_spec)
+ {
+ GParamSpec *element_spec = aspec->element_spec;
+ gint length = gimp_value_array_length (value_array);
+ gint i;
+
+ for (i = 0; i < length; i++)
+ {
+ GValue *element = gimp_value_array_index (value_array, i);
+
+ /* need to fixup value type, or ensure that the array
+ * value is initialized at all
+ */
+ if (! g_value_type_compatible (G_VALUE_TYPE (element),
+ G_PARAM_SPEC_VALUE_TYPE (element_spec)))
+ {
+ if (G_VALUE_TYPE (element) != 0)
+ g_value_unset (element);
+
+ g_value_init (element, G_PARAM_SPEC_VALUE_TYPE (element_spec));
+ g_param_value_set_default (element_spec, element);
+ changed++;
+ }
+
+ /* validate array value against element_spec */
+ changed += g_param_value_validate (element_spec, element);
+ }
+ }
+ }
+
+ return changed;
+}
+
+static gint
+gimp_param_value_array_values_cmp (GParamSpec *pspec,
+ const GValue *value1,
+ const GValue *value2)
+{
+ GimpParamSpecValueArray *aspec = GIMP_PARAM_SPEC_VALUE_ARRAY (pspec);
+ GimpValueArray *value_array1 = value1->data[0].v_pointer;
+ GimpValueArray *value_array2 = value2->data[0].v_pointer;
+ gint length1;
+ gint length2;
+
+ if (!value_array1 || !value_array2)
+ return value_array2 ? -1 : value_array1 != value_array2;
+
+ length1 = gimp_value_array_length (value_array1);
+ length2 = gimp_value_array_length (value_array2);
+
+ if (length1 != length2)
+ {
+ return length1 < length2 ? -1 : 1;
+ }
+ else if (! aspec->element_spec)
+ {
+ /* we need an element specification for comparisons, so there's
+ * not much to compare here, try to at least provide stable
+ * lesser/greater result
+ */
+ return length1 < length2 ? -1 : length1 > length2;
+ }
+ else /* length1 == length2 */
+ {
+ guint i;
+
+ for (i = 0; i < length1; i++)
+ {
+ GValue *element1 = gimp_value_array_index (value_array1, i);
+ GValue *element2 = gimp_value_array_index (value_array2, i);
+ gint cmp;
+
+ /* need corresponding element types, provide stable result
+ * otherwise
+ */
+ if (G_VALUE_TYPE (element1) != G_VALUE_TYPE (element2))
+ return G_VALUE_TYPE (element1) < G_VALUE_TYPE (element2) ? -1 : 1;
+
+ cmp = g_param_values_cmp (aspec->element_spec, element1, element2);
+ if (cmp)
+ return cmp;
+ }
+
+ return 0;
+ }
+}
+
+GParamSpec *
+gimp_param_spec_value_array (const gchar *name,
+ const gchar *nick,
+ const gchar *blurb,
+ GParamSpec *element_spec,
+ GParamFlags flags)
+{
+ GimpParamSpecValueArray *aspec;
+
+ if (element_spec)
+ g_return_val_if_fail (G_IS_PARAM_SPEC (element_spec), NULL);
+
+ aspec = g_param_spec_internal (GIMP_TYPE_PARAM_VALUE_ARRAY,
+ name,
+ nick,
+ blurb,
+ flags);
+ if (element_spec)
+ {
+ aspec->element_spec = g_param_spec_ref (element_spec);
+ g_param_spec_sink (element_spec);
+ }
+
+ return G_PARAM_SPEC (aspec);
+}
diff --git a/libgimpbase/gimpvaluearray.h b/libgimpbase/gimpvaluearray.h
new file mode 100644
index 0000000..d7aefcb
--- /dev/null
+++ b/libgimpbase/gimpvaluearray.h
@@ -0,0 +1,104 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpvaluearray.h ported from GValueArray
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_BASE_H_INSIDE__) && !defined (GIMP_BASE_COMPILATION)
+#error "Only <libgimpbase/gimpbase.h> can be included directly."
+#endif
+
+#ifndef __GIMP_VALUE_ARRAY_H__
+#define __GIMP_VALUE_ARRAY_H__
+
+G_BEGIN_DECLS
+
+/**
+ * GIMP_TYPE_VALUE_ARRAY:
+ *
+ * The type ID of the "GimpValueArray" type which is a boxed type,
+ * used to pass around pointers to GimpValueArrays.
+ *
+ * Since: 2.10
+ */
+#define GIMP_TYPE_VALUE_ARRAY (gimp_value_array_get_type ())
+
+
+GType gimp_value_array_get_type (void) G_GNUC_CONST;
+
+GimpValueArray * gimp_value_array_new (gint n_prealloced);
+
+GimpValueArray * gimp_value_array_ref (GimpValueArray *value_array);
+void gimp_value_array_unref (GimpValueArray *value_array);
+
+gint gimp_value_array_length (const GimpValueArray *value_array);
+
+GValue * gimp_value_array_index (const GimpValueArray *value_array,
+ gint index);
+
+GimpValueArray * gimp_value_array_prepend (GimpValueArray *value_array,
+ const GValue *value);
+GimpValueArray * gimp_value_array_append (GimpValueArray *value_array,
+ const GValue *value);
+GimpValueArray * gimp_value_array_insert (GimpValueArray *value_array,
+ gint index,
+ const GValue *value);
+
+GimpValueArray * gimp_value_array_remove (GimpValueArray *value_array,
+ gint index);
+void gimp_value_array_truncate (GimpValueArray *value_array,
+ gint n_values);
+
+
+/*
+ * GIMP_TYPE_PARAM_VALUE_ARRAY
+ */
+
+#define GIMP_TYPE_PARAM_VALUE_ARRAY (gimp_param_value_array_get_type ())
+#define GIMP_IS_PARAM_SPEC_VALUE_ARRAY(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_VALUE_ARRAY))
+#define GIMP_PARAM_SPEC_VALUE_ARRAY(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_VALUE_ARRAY, GimpParamSpecValueArray))
+
+typedef struct _GimpParamSpecValueArray GimpParamSpecValueArray;
+
+/**
+ * GimpParamSpecValueArray:
+ * @parent_instance: private #GParamSpec portion
+ * @element_spec: the #GParamSpec of the array elements
+ * @fixed_n_elements: default length of the array
+ *
+ * A #GParamSpec derived structure that contains the meta data for
+ * value array properties.
+ **/
+struct _GimpParamSpecValueArray
+{
+ GParamSpec parent_instance;
+ GParamSpec *element_spec;
+ gint fixed_n_elements;
+};
+
+GType gimp_param_value_array_get_type (void) G_GNUC_CONST;
+
+GParamSpec * gimp_param_spec_value_array (const gchar *name,
+ const gchar *nick,
+ const gchar *blurb,
+ GParamSpec *element_spec,
+ GParamFlags flags);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_VALUE_ARRAY_H__ */
diff --git a/libgimpbase/gimpversion.h b/libgimpbase/gimpversion.h
new file mode 100644
index 0000000..5871d3c
--- /dev/null
+++ b/libgimpbase/gimpversion.h
@@ -0,0 +1,70 @@
+/* gimpversion.h
+ *
+ * This is a generated file. Please modify 'configure.ac'
+ */
+
+#if !defined (__GIMP_BASE_H_INSIDE__) && !defined (GIMP_BASE_COMPILATION)
+#error "Only <libgimpbase/gimpbase.h> can be included directly."
+#endif
+
+#ifndef __GIMP_VERSION_H__
+#define __GIMP_VERSION_H__
+
+G_BEGIN_DECLS
+
+
+/**
+ * SECTION: gimpversion
+ * @title: gimpversion
+ * @short_description: Macros and constants useful for determining
+ * GIMP's version number and capabilities.
+ *
+ * Macros and constants useful for determining GIMP's version number and
+ * capabilities.
+ **/
+
+/**
+ * GIMP_MAJOR_VERSION:
+ *
+ * The major GIMP version number.
+ **/
+#define GIMP_MAJOR_VERSION (2)
+
+/**
+ * GIMP_MINOR_VERSION:
+ *
+ * The minor GIMP version number.
+ **/
+#define GIMP_MINOR_VERSION (10)
+
+/**
+ * GIMP_MICRO_VERSION:
+ *
+ * The micro GIMP version number.
+ **/
+#define GIMP_MICRO_VERSION (34)
+
+/**
+ * GIMP_VERSION:
+ *
+ * The GIMP version as a string.
+ **/
+#define GIMP_VERSION "2.10.34"
+
+/**
+ * GIMP_API_VERSION:
+ *
+ * Since: 2.2
+ **/
+#define GIMP_API_VERSION "2.0"
+
+#define GIMP_CHECK_VERSION(major, minor, micro) \
+ (GIMP_MAJOR_VERSION > (major) || \
+ (GIMP_MAJOR_VERSION == (major) && GIMP_MINOR_VERSION > (minor)) || \
+ (GIMP_MAJOR_VERSION == (major) && GIMP_MINOR_VERSION == (minor) && \
+ GIMP_MICRO_VERSION >= (micro)))
+
+
+G_END_DECLS
+
+#endif /* __GIMP_VERSION_H__ */
diff --git a/libgimpbase/gimpwin32-io.h b/libgimpbase/gimpwin32-io.h
new file mode 100644
index 0000000..45a3104
--- /dev/null
+++ b/libgimpbase/gimpwin32-io.h
@@ -0,0 +1,99 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpwin32-io.h
+ * Compatibility defines, you mostly need this as unistd.h replacement
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_WIN32_IO_H__
+#define __GIMP_WIN32_IO_H__
+
+#include <io.h>
+#include <direct.h>
+
+G_BEGIN_DECLS
+
+
+#define mkdir(n,a) _mkdir(n)
+#define chmod(n,f) _chmod(n,f)
+#define access(f,p) _access(f,p)
+
+#ifndef S_ISREG
+#define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
+#endif
+#ifndef S_ISDIR
+#define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
+#endif
+
+#ifndef S_IRUSR
+#define S_IRUSR _S_IREAD
+#endif
+#ifndef S_IWUSR
+#define S_IWUSR _S_IWRITE
+#endif
+#ifndef S_IXUSR
+#define S_IXUSR _S_IEXEC
+#endif
+
+#ifndef S_IRGRP
+#define S_IRGRP _S_IREAD
+#endif
+#ifndef S_IXGRP
+#define S_IXGRP _S_IEXEC
+#endif
+#ifndef S_IROTH
+#define S_IROTH _S_IREAD
+#endif
+#ifndef S_IXOTH
+#define S_IXOTH _S_IEXEC
+#endif
+
+#ifndef _O_BINARY
+#define _O_BINARY 0
+#endif
+#ifndef _O_TEMPORARY
+#define _O_TEMPORARY 0
+#endif
+
+#ifndef F_OK
+#define F_OK 0
+#endif
+#ifndef W_OK
+#define W_OK 2
+#endif
+#ifndef R_OK
+#define R_OK 4
+#endif
+#ifndef X_OK
+#define X_OK 0 /* not really */
+#endif
+
+/*
+2004-09-15 Tor Lillqvist <tml@iki.fi>
+
+ * glib/gwin32.h: Don't define ftruncate as a macro. Was never a
+ good idea, and it clashes with newest mingw headers, which have a
+ ftruncate implementation as an inline function. Thanks to Dominik R.
+ */
+/* needs coorection for msvc though ;( */
+#ifdef _MSC_VER
+#define ftruncate(f,s) g_win32_ftruncate(f,s)
+#endif
+
+G_END_DECLS
+
+#endif /* __GIMP_WIN32_IO_H__ */
diff --git a/libgimpbase/gimpwire.c b/libgimpbase/gimpwire.c
new file mode 100644
index 0000000..9382bee
--- /dev/null
+++ b/libgimpbase/gimpwire.c
@@ -0,0 +1,695 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib-object.h>
+
+#include <libgimpcolor/gimpcolortypes.h>
+
+#include "gimpwire.h"
+
+
+typedef struct _GimpWireHandler GimpWireHandler;
+
+struct _GimpWireHandler
+{
+ guint32 type;
+ GimpWireReadFunc read_func;
+ GimpWireWriteFunc write_func;
+ GimpWireDestroyFunc destroy_func;
+};
+
+
+static GHashTable *wire_ht = NULL;
+static GimpWireIOFunc wire_read_func = NULL;
+static GimpWireIOFunc wire_write_func = NULL;
+static GimpWireFlushFunc wire_flush_func = NULL;
+static gboolean wire_error_val = FALSE;
+
+
+static void gimp_wire_init (void);
+
+
+void
+gimp_wire_register (guint32 type,
+ GimpWireReadFunc read_func,
+ GimpWireWriteFunc write_func,
+ GimpWireDestroyFunc destroy_func)
+{
+ GimpWireHandler *handler;
+
+ if (! wire_ht)
+ gimp_wire_init ();
+
+ handler = g_hash_table_lookup (wire_ht, &type);
+
+ if (! handler)
+ handler = g_slice_new0 (GimpWireHandler);
+
+ handler->type = type;
+ handler->read_func = read_func;
+ handler->write_func = write_func;
+ handler->destroy_func = destroy_func;
+
+ g_hash_table_insert (wire_ht, &handler->type, handler);
+}
+
+void
+gimp_wire_set_reader (GimpWireIOFunc read_func)
+{
+ wire_read_func = read_func;
+}
+
+void
+gimp_wire_set_writer (GimpWireIOFunc write_func)
+{
+ wire_write_func = write_func;
+}
+
+void
+gimp_wire_set_flusher (GimpWireFlushFunc flush_func)
+{
+ wire_flush_func = flush_func;
+}
+
+gboolean
+gimp_wire_read (GIOChannel *channel,
+ guint8 *buf,
+ gsize count,
+ gpointer user_data)
+{
+ if (wire_read_func)
+ {
+ if (!(* wire_read_func) (channel, buf, count, user_data))
+ {
+ /* Gives a confusing error message most of the time, disable:
+ g_warning ("%s: gimp_wire_read: error", g_get_prgname ());
+ */
+ wire_error_val = TRUE;
+ return FALSE;
+ }
+ }
+ else
+ {
+ GIOStatus status;
+ GError *error = NULL;
+ gsize bytes;
+
+ while (count > 0)
+ {
+ do
+ {
+ bytes = 0;
+ status = g_io_channel_read_chars (channel,
+ (gchar *) buf, count,
+ &bytes,
+ &error);
+ }
+ while (G_UNLIKELY (status == G_IO_STATUS_AGAIN));
+
+ if (G_UNLIKELY (status != G_IO_STATUS_NORMAL))
+ {
+ if (error)
+ {
+ g_warning ("%s: gimp_wire_read(): error: %s",
+ g_get_prgname (), error->message);
+ g_error_free (error);
+ }
+ else
+ {
+ g_warning ("%s: gimp_wire_read(): error",
+ g_get_prgname ());
+ }
+
+ wire_error_val = TRUE;
+ return FALSE;
+ }
+
+ if (G_UNLIKELY (bytes == 0))
+ {
+ g_warning ("%s: gimp_wire_read(): unexpected EOF",
+ g_get_prgname ());
+ wire_error_val = TRUE;
+ return FALSE;
+ }
+
+ count -= bytes;
+ buf += bytes;
+ }
+ }
+
+ return TRUE;
+}
+
+gboolean
+gimp_wire_write (GIOChannel *channel,
+ const guint8 *buf,
+ gsize count,
+ gpointer user_data)
+{
+ if (wire_write_func)
+ {
+ if (!(* wire_write_func) (channel, (guint8 *) buf, count, user_data))
+ {
+ g_warning ("%s: gimp_wire_write: error", g_get_prgname ());
+ wire_error_val = TRUE;
+ return FALSE;
+ }
+ }
+ else
+ {
+ GIOStatus status;
+ GError *error = NULL;
+ gsize bytes;
+
+ while (count > 0)
+ {
+ do
+ {
+ bytes = 0;
+ status = g_io_channel_write_chars (channel,
+ (const gchar *) buf, count,
+ &bytes,
+ &error);
+ }
+ while (G_UNLIKELY (status == G_IO_STATUS_AGAIN));
+
+ if (G_UNLIKELY (status != G_IO_STATUS_NORMAL))
+ {
+ if (error)
+ {
+ g_warning ("%s: gimp_wire_write(): error: %s",
+ g_get_prgname (), error->message);
+ g_error_free (error);
+ }
+ else
+ {
+ g_warning ("%s: gimp_wire_write(): error",
+ g_get_prgname ());
+ }
+
+ wire_error_val = TRUE;
+ return FALSE;
+ }
+
+ count -= bytes;
+ buf += bytes;
+ }
+ }
+
+ return TRUE;
+}
+
+gboolean
+gimp_wire_flush (GIOChannel *channel,
+ gpointer user_data)
+{
+ if (wire_flush_func)
+ return (* wire_flush_func) (channel, user_data);
+
+ return FALSE;
+}
+
+gboolean
+gimp_wire_error (void)
+{
+ return wire_error_val;
+}
+
+void
+gimp_wire_clear_error (void)
+{
+ wire_error_val = FALSE;
+}
+
+gboolean
+gimp_wire_read_msg (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data)
+{
+ GimpWireHandler *handler;
+
+ if (G_UNLIKELY (! wire_ht))
+ g_error ("gimp_wire_read_msg: the wire protocol has not been initialized");
+
+ if (wire_error_val)
+ return !wire_error_val;
+
+ if (! _gimp_wire_read_int32 (channel, &msg->type, 1, user_data))
+ return FALSE;
+
+ handler = g_hash_table_lookup (wire_ht, &msg->type);
+
+ if (G_UNLIKELY (! handler))
+ g_error ("gimp_wire_read_msg: could not find handler for message: %d",
+ msg->type);
+
+ (* handler->read_func) (channel, msg, user_data);
+
+ return !wire_error_val;
+}
+
+gboolean
+gimp_wire_write_msg (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data)
+{
+ GimpWireHandler *handler;
+
+ if (G_UNLIKELY (! wire_ht))
+ g_error ("gimp_wire_write_msg: the wire protocol has not been initialized");
+
+ if (wire_error_val)
+ return !wire_error_val;
+
+ handler = g_hash_table_lookup (wire_ht, &msg->type);
+
+ if (G_UNLIKELY (! handler))
+ g_error ("gimp_wire_write_msg: could not find handler for message: %d",
+ msg->type);
+
+ if (! _gimp_wire_write_int32 (channel, &msg->type, 1, user_data))
+ return FALSE;
+
+ (* handler->write_func) (channel, msg, user_data);
+
+ return !wire_error_val;
+}
+
+void
+gimp_wire_destroy (GimpWireMessage *msg)
+{
+ GimpWireHandler *handler;
+
+ if (G_UNLIKELY (! wire_ht))
+ g_error ("gimp_wire_destroy: the wire protocol has not been initialized");
+
+ handler = g_hash_table_lookup (wire_ht, &msg->type);
+
+ if (G_UNLIKELY (! handler))
+ g_error ("gimp_wire_destroy: could not find handler for message: %d\n",
+ msg->type);
+
+ (* handler->destroy_func) (msg);
+}
+
+gboolean
+_gimp_wire_read_int64 (GIOChannel *channel,
+ guint64 *data,
+ gint count,
+ gpointer user_data)
+{
+ g_return_val_if_fail (count >= 0, FALSE);
+
+ if (count > 0)
+ {
+ if (! _gimp_wire_read_int8 (channel,
+ (guint8 *) data, count * 8, user_data))
+ return FALSE;
+
+ while (count--)
+ {
+ *data = GUINT64_FROM_BE (*data);
+ data++;
+ }
+ }
+
+ return TRUE;
+}
+
+gboolean
+_gimp_wire_read_int32 (GIOChannel *channel,
+ guint32 *data,
+ gint count,
+ gpointer user_data)
+{
+ g_return_val_if_fail (count >= 0, FALSE);
+
+ if (count > 0)
+ {
+ if (! _gimp_wire_read_int8 (channel,
+ (guint8 *) data, count * 4, user_data))
+ return FALSE;
+
+ while (count--)
+ {
+ *data = g_ntohl (*data);
+ data++;
+ }
+ }
+
+ return TRUE;
+}
+
+gboolean
+_gimp_wire_read_int16 (GIOChannel *channel,
+ guint16 *data,
+ gint count,
+ gpointer user_data)
+{
+ g_return_val_if_fail (count >= 0, FALSE);
+
+ if (count > 0)
+ {
+ if (! _gimp_wire_read_int8 (channel,
+ (guint8 *) data, count * 2, user_data))
+ return FALSE;
+
+ while (count--)
+ {
+ *data = g_ntohs (*data);
+ data++;
+ }
+ }
+
+ return TRUE;
+}
+
+gboolean
+_gimp_wire_read_int8 (GIOChannel *channel,
+ guint8 *data,
+ gint count,
+ gpointer user_data)
+{
+ g_return_val_if_fail (count >= 0, FALSE);
+
+ return gimp_wire_read (channel, data, count, user_data);
+}
+
+gboolean
+_gimp_wire_read_double (GIOChannel *channel,
+ gdouble *data,
+ gint count,
+ gpointer user_data)
+{
+ gdouble *t;
+ guint8 tmp[8];
+ gint i;
+
+ g_return_val_if_fail (count >= 0, FALSE);
+
+ t = (gdouble *) tmp;
+
+ for (i = 0; i < count; i++)
+ {
+#if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
+ gint j;
+#endif
+
+ if (! _gimp_wire_read_int8 (channel, tmp, 8, user_data))
+ return FALSE;
+
+#if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
+ for (j = 0; j < 4; j++)
+ {
+ guint8 swap;
+
+ swap = tmp[j];
+ tmp[j] = tmp[7 - j];
+ tmp[7 - j] = swap;
+ }
+#endif
+
+ data[i] = *t;
+ }
+
+ return TRUE;
+}
+
+gboolean
+_gimp_wire_read_string (GIOChannel *channel,
+ gchar **data,
+ gint count,
+ gpointer user_data)
+{
+ gint i;
+
+ g_return_val_if_fail (count >= 0, FALSE);
+
+ for (i = 0; i < count; i++)
+ {
+ guint32 tmp;
+
+ if (! _gimp_wire_read_int32 (channel, &tmp, 1, user_data))
+ return FALSE;
+
+ if (tmp > 0)
+ {
+ data[i] = g_try_new (gchar, tmp);
+
+ if (! data[i])
+ {
+ g_printerr ("%s: failed to allocate %u bytes\n", G_STRFUNC, tmp);
+ return FALSE;
+ }
+
+ if (! _gimp_wire_read_int8 (channel,
+ (guint8 *) data[i], tmp, user_data))
+ {
+ g_free (data[i]);
+ return FALSE;
+ }
+
+ /* make sure that the string is NULL-terminated */
+ data[i][tmp - 1] = '\0';
+ }
+ else
+ {
+ data[i] = NULL;
+ }
+ }
+
+ return TRUE;
+}
+
+gboolean
+_gimp_wire_read_color (GIOChannel *channel,
+ GimpRGB *data,
+ gint count,
+ gpointer user_data)
+{
+ g_return_val_if_fail (count >= 0, FALSE);
+
+ return _gimp_wire_read_double (channel,
+ (gdouble *) data, 4 * count, user_data);
+}
+
+gboolean
+_gimp_wire_write_int64 (GIOChannel *channel,
+ const guint64 *data,
+ gint count,
+ gpointer user_data)
+{
+ g_return_val_if_fail (count >= 0, FALSE);
+
+ if (count > 0)
+ {
+ gint i;
+
+ for (i = 0; i < count; i++)
+ {
+ guint64 tmp = GUINT64_TO_BE (data[i]);
+
+ if (! _gimp_wire_write_int8 (channel,
+ (const guint8 *) &tmp, 8, user_data))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+gboolean
+_gimp_wire_write_int32 (GIOChannel *channel,
+ const guint32 *data,
+ gint count,
+ gpointer user_data)
+{
+ g_return_val_if_fail (count >= 0, FALSE);
+
+ if (count > 0)
+ {
+ gint i;
+
+ for (i = 0; i < count; i++)
+ {
+ guint32 tmp = g_htonl (data[i]);
+
+ if (! _gimp_wire_write_int8 (channel,
+ (const guint8 *) &tmp, 4, user_data))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+gboolean
+_gimp_wire_write_int16 (GIOChannel *channel,
+ const guint16 *data,
+ gint count,
+ gpointer user_data)
+{
+ g_return_val_if_fail (count >= 0, FALSE);
+
+ if (count > 0)
+ {
+ gint i;
+
+ for (i = 0; i < count; i++)
+ {
+ guint16 tmp = g_htons (data[i]);
+
+ if (! _gimp_wire_write_int8 (channel,
+ (const guint8 *) &tmp, 2, user_data))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+gboolean
+_gimp_wire_write_int8 (GIOChannel *channel,
+ const guint8 *data,
+ gint count,
+ gpointer user_data)
+{
+ g_return_val_if_fail (count >= 0, FALSE);
+
+ return gimp_wire_write (channel, data, count, user_data);
+}
+
+gboolean
+_gimp_wire_write_double (GIOChannel *channel,
+ const gdouble *data,
+ gint count,
+ gpointer user_data)
+{
+ gdouble *t;
+ guint8 tmp[8];
+ gint i;
+#if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
+ gint j;
+#endif
+
+ g_return_val_if_fail (count >= 0, FALSE);
+
+ t = (gdouble *) tmp;
+
+ for (i = 0; i < count; i++)
+ {
+ *t = data[i];
+
+#if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
+ for (j = 0; j < 4; j++)
+ {
+ guint8 swap;
+
+ swap = tmp[j];
+ tmp[j] = tmp[7 - j];
+ tmp[7 - j] = swap;
+ }
+#endif
+
+ if (! _gimp_wire_write_int8 (channel, tmp, 8, user_data))
+ return FALSE;
+
+#if 0
+ {
+ gint k;
+
+ g_print ("Wire representation of %f:\t", data[i]);
+
+ for (k = 0; k < 8; k++)
+ g_print ("%02x ", tmp[k]);
+
+ g_print ("\n");
+ }
+#endif
+ }
+
+ return TRUE;
+}
+
+gboolean
+_gimp_wire_write_string (GIOChannel *channel,
+ gchar **data,
+ gint count,
+ gpointer user_data)
+{
+ gint i;
+
+ g_return_val_if_fail (count >= 0, FALSE);
+
+ for (i = 0; i < count; i++)
+ {
+ guint32 tmp;
+
+ if (data[i])
+ tmp = strlen (data[i]) + 1;
+ else
+ tmp = 0;
+
+ if (! _gimp_wire_write_int32 (channel, &tmp, 1, user_data))
+ return FALSE;
+
+ if (tmp > 0)
+ if (! _gimp_wire_write_int8 (channel,
+ (const guint8 *) data[i], tmp, user_data))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+_gimp_wire_write_color (GIOChannel *channel,
+ const GimpRGB *data,
+ gint count,
+ gpointer user_data)
+{
+ g_return_val_if_fail (count >= 0, FALSE);
+
+ return _gimp_wire_write_double (channel,
+ (gdouble *) data, 4 * count, user_data);
+}
+
+static guint
+gimp_wire_hash (const guint32 *key)
+{
+ return *key;
+}
+
+static gboolean
+gimp_wire_compare (const guint32 *a,
+ const guint32 *b)
+{
+ return (*a == *b);
+}
+
+static void
+gimp_wire_init (void)
+{
+ if (! wire_ht)
+ wire_ht = g_hash_table_new ((GHashFunc) gimp_wire_hash,
+ (GCompareFunc) gimp_wire_compare);
+}
diff --git a/libgimpbase/gimpwire.h b/libgimpbase/gimpwire.h
new file mode 100644
index 0000000..dde69d9
--- /dev/null
+++ b/libgimpbase/gimpwire.h
@@ -0,0 +1,146 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_WIRE_H__
+#define __GIMP_WIRE_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+typedef struct _GimpWireMessage GimpWireMessage;
+
+typedef void (* GimpWireReadFunc) (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+typedef void (* GimpWireWriteFunc) (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+typedef void (* GimpWireDestroyFunc) (GimpWireMessage *msg);
+typedef gboolean (* GimpWireIOFunc) (GIOChannel *channel,
+ const guint8 *buf,
+ gulong count,
+ gpointer user_data);
+typedef gboolean (* GimpWireFlushFunc) (GIOChannel *channel,
+ gpointer user_data);
+
+
+struct _GimpWireMessage
+{
+ guint32 type;
+ gpointer data;
+};
+
+
+void gimp_wire_register (guint32 type,
+ GimpWireReadFunc read_func,
+ GimpWireWriteFunc write_func,
+ GimpWireDestroyFunc destroy_func);
+
+void gimp_wire_set_reader (GimpWireIOFunc read_func);
+void gimp_wire_set_writer (GimpWireIOFunc write_func);
+void gimp_wire_set_flusher (GimpWireFlushFunc flush_func);
+
+gboolean gimp_wire_read (GIOChannel *channel,
+ guint8 *buf,
+ gsize count,
+ gpointer user_data);
+gboolean gimp_wire_write (GIOChannel *channel,
+ const guint8 *buf,
+ gsize count,
+ gpointer user_data);
+gboolean gimp_wire_flush (GIOChannel *channel,
+ gpointer user_data);
+
+gboolean gimp_wire_error (void);
+void gimp_wire_clear_error (void);
+
+gboolean gimp_wire_read_msg (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+gboolean gimp_wire_write_msg (GIOChannel *channel,
+ GimpWireMessage *msg,
+ gpointer user_data);
+
+void gimp_wire_destroy (GimpWireMessage *msg);
+
+
+/* for internal use in libgimpbase */
+
+G_GNUC_INTERNAL gboolean _gimp_wire_read_int64 (GIOChannel *channel,
+ guint64 *data,
+ gint count,
+ gpointer user_data);
+G_GNUC_INTERNAL gboolean _gimp_wire_read_int32 (GIOChannel *channel,
+ guint32 *data,
+ gint count,
+ gpointer user_data);
+G_GNUC_INTERNAL gboolean _gimp_wire_read_int16 (GIOChannel *channel,
+ guint16 *data,
+ gint count,
+ gpointer user_data);
+G_GNUC_INTERNAL gboolean _gimp_wire_read_int8 (GIOChannel *channel,
+ guint8 *data,
+ gint count,
+ gpointer user_data);
+G_GNUC_INTERNAL gboolean _gimp_wire_read_double (GIOChannel *channel,
+ gdouble *data,
+ gint count,
+ gpointer user_data);
+G_GNUC_INTERNAL gboolean _gimp_wire_read_string (GIOChannel *channel,
+ gchar **data,
+ gint count,
+ gpointer user_data);
+G_GNUC_INTERNAL gboolean _gimp_wire_read_color (GIOChannel *channel,
+ GimpRGB *data,
+ gint count,
+ gpointer user_data);
+G_GNUC_INTERNAL gboolean _gimp_wire_write_int64 (GIOChannel *channel,
+ const guint64 *data,
+ gint count,
+ gpointer user_data);
+G_GNUC_INTERNAL gboolean _gimp_wire_write_int32 (GIOChannel *channel,
+ const guint32 *data,
+ gint count,
+ gpointer user_data);
+G_GNUC_INTERNAL gboolean _gimp_wire_write_int16 (GIOChannel *channel,
+ const guint16 *data,
+ gint count,
+ gpointer user_data);
+G_GNUC_INTERNAL gboolean _gimp_wire_write_int8 (GIOChannel *channel,
+ const guint8 *data,
+ gint count,
+ gpointer user_data);
+G_GNUC_INTERNAL gboolean _gimp_wire_write_double (GIOChannel *channel,
+ const gdouble *data,
+ gint count,
+ gpointer user_data);
+G_GNUC_INTERNAL gboolean _gimp_wire_write_string (GIOChannel *channel,
+ gchar **data,
+ gint count,
+ gpointer user_data);
+G_GNUC_INTERNAL gboolean _gimp_wire_write_color (GIOChannel *channel,
+ const GimpRGB *data,
+ gint count,
+ gpointer user_data);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_WIRE_H__ */
diff --git a/libgimpbase/test-cpu-accel.c b/libgimpbase/test-cpu-accel.c
new file mode 100644
index 0000000..5c0349f
--- /dev/null
+++ b/libgimpbase/test-cpu-accel.c
@@ -0,0 +1,48 @@
+/* A small test program for the CPU detection code */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <glib.h>
+
+#include "gimpcpuaccel.h"
+
+
+static void
+cpu_accel_print_results (void)
+{
+ GimpCpuAccelFlags support;
+
+ g_printerr ("Testing CPU features...\n");
+
+ support = gimp_cpu_accel_get_support ();
+
+#ifdef ARCH_X86
+ g_printerr (" mmx : %s\n",
+ (support & GIMP_CPU_ACCEL_X86_MMX) ? "yes" : "no");
+ g_printerr (" 3dnow : %s\n",
+ (support & GIMP_CPU_ACCEL_X86_3DNOW) ? "yes" : "no");
+ g_printerr (" mmxext : %s\n",
+ (support & GIMP_CPU_ACCEL_X86_MMXEXT) ? "yes" : "no");
+ g_printerr (" sse : %s\n",
+ (support & GIMP_CPU_ACCEL_X86_SSE) ? "yes" : "no");
+ g_printerr (" sse2 : %s\n",
+ (support & GIMP_CPU_ACCEL_X86_SSE2) ? "yes" : "no");
+ g_printerr (" sse3 : %s\n",
+ (support & GIMP_CPU_ACCEL_X86_SSE3) ? "yes" : "no");
+#endif
+#ifdef ARCH_PPC
+ g_printerr (" altivec : %s\n",
+ (support & GIMP_CPU_ACCEL_PPC_ALTIVEC) ? "yes" : "no");
+#endif
+ g_printerr ("\n");
+}
+
+int
+main (void)
+{
+ cpu_accel_print_results ();
+
+ return EXIT_SUCCESS;
+}
diff --git a/libgimpcolor/Makefile.am b/libgimpcolor/Makefile.am
new file mode 100644
index 0000000..767f3b9
--- /dev/null
+++ b/libgimpcolor/Makefile.am
@@ -0,0 +1,148 @@
+## Process this file with automake to produce Makefile.in
+
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+
+if PLATFORM_WIN32
+no_undefined = -no-undefined
+endif
+
+if PLATFORM_WIN32
+else
+libm = -lm
+endif
+
+if OS_WIN32
+gimpcolor_def = gimpcolor.def
+libgimpcolor_export_symbols = -export-symbols $(srcdir)/gimpcolor.def
+
+install-libtool-import-lib:
+ $(INSTALL) .libs/libgimpcolor-$(GIMP_API_VERSION).dll.a $(DESTDIR)$(libdir)
+ $(INSTALL) $(srcdir)/gimpcolor.def $(DESTDIR)$(libdir)
+
+uninstall-libtool-import-lib:
+ -rm $(DESTDIR)$(libdir)/libgimpcolor-$(GIMP_API_VERSION).dll.a
+ -rm $(DESTDIR)$(libdir)/gimpcolor.def
+else
+install-libtool-import-lib:
+uninstall-libtool-import-lib:
+endif
+
+if MS_LIB_AVAILABLE
+noinst_DATA = gimpcolor-$(GIMP_API_VERSION).lib
+
+install-ms-lib:
+ $(INSTALL) gimpcolor-$(GIMP_API_VERSION).lib $(DESTDIR)$(libdir)
+
+uninstall-ms-lib:
+ -rm $(DESTDIR)$(libdir)/gimpcolor-$(GIMP_API_VERSION).lib
+
+gimpcolor-@GIMP_API_VERSION@.lib: gimpcolor.def
+ lib -name:libgimpcolor-$(GIMP_API_VERSION)-@LT_CURRENT_MINUS_AGE@.dll -def:gimpcolor.def -out:$@
+
+else
+install-ms-lib:
+uninstall-ms-lib:
+endif
+
+libgimpcolorincludedir = $(includedir)/gimp-$(GIMP_API_VERSION)/libgimpcolor
+
+AM_CPPFLAGS = \
+ -DG_LOG_DOMAIN=\"LibGimpColor\" \
+ -DGIMP_COLOR_COMPILATION \
+ -I$(top_srcdir) \
+ $(GEGL_CFLAGS) \
+ $(CAIRO_CFLAGS) \
+ $(GDK_PIXBUF_CFLAGS) \
+ $(LCMS_CFLAGS) \
+ -I$(includedir)
+
+EXTRA_DIST = \
+ gimpcolor.def
+
+lib_LTLIBRARIES = libgimpcolor-@GIMP_API_VERSION@.la
+
+libgimpcolor_@GIMP_API_VERSION@_la_SOURCES = \
+ gimpcolor.h \
+ gimpcolortypes.h \
+ gimpadaptivesupersample.c \
+ gimpadaptivesupersample.h \
+ gimpbilinear.c \
+ gimpbilinear.h \
+ gimpcairo.c \
+ gimpcairo.h \
+ gimpcmyk.c \
+ gimpcmyk.h \
+ gimpcolormanaged.c \
+ gimpcolormanaged.h \
+ gimpcolorprofile.c \
+ gimpcolorprofile.h \
+ gimpcolorspace.c \
+ gimpcolorspace.h \
+ gimpcolortransform.c \
+ gimpcolortransform.h \
+ gimphsl.c \
+ gimphsl.h \
+ gimphsv.c \
+ gimphsv.h \
+ gimppixbuf.c \
+ gimppixbuf.h \
+ gimprgb.c \
+ gimprgb.h \
+ gimprgb-parse.c
+
+libgimpcolorinclude_HEADERS = \
+ gimpcolor.h \
+ gimpcolortypes.h \
+ gimpadaptivesupersample.h \
+ gimpbilinear.h \
+ gimpcairo.h \
+ gimpcmyk.h \
+ gimpcolormanaged.h \
+ gimpcolorprofile.h \
+ gimpcolorspace.h \
+ gimpcolortransform.h \
+ gimphsl.h \
+ gimphsv.h \
+ gimppixbuf.h \
+ gimprgb.h
+
+libgimpcolor_@GIMP_API_VERSION@_la_LDFLAGS = \
+ -version-info $(LT_VERSION_INFO) \
+ $(no_undefined) \
+ $(libgimpcolor_export_symbols)
+
+EXTRA_libgimpcolor_@GIMP_API_VERSION@_la_DEPENDENCIES = $(gimpcolor_def)
+
+libgimpcolor_@GIMP_API_VERSION@_la_LIBADD = \
+ $(libgimpbase) \
+ $(GEGL_LIBS) \
+ $(CAIRO_LIBS) \
+ $(GDK_PIXBUF_LIBS) \
+ $(LCMS_LIBS) \
+ $(libm)
+
+
+#
+# test programs, not to be built by default and never installed
+#
+
+TESTS = test-color-parser$(EXEEXT)
+
+EXTRA_PROGRAMS = test-color-parser
+
+test_color_parser_DEPENDENCIES = \
+ $(libgimpbase) \
+ $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+
+test_color_parser_LDADD = \
+ $(CAIRO_LIBS) \
+ $(GLIB_LIBS) \
+ $(test_color_parser_DEPENDENCIES)
+
+
+CLEANFILES = $(EXTRA_PROGRAMS)
+
+
+install-data-local: install-ms-lib install-libtool-import-lib
+
+uninstall-local: uninstall-ms-lib uninstall-libtool-import-lib
diff --git a/libgimpcolor/Makefile.in b/libgimpcolor/Makefile.in
new file mode 100644
index 0000000..b3d7659
--- /dev/null
+++ b/libgimpcolor/Makefile.in
@@ -0,0 +1,1523 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+EXTRA_PROGRAMS = test-color-parser$(EXEEXT)
+subdir = libgimpcolor
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(libgimpcolorinclude_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(libdir)" \
+ "$(DESTDIR)$(libgimpcolorincludedir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libgimpcolor_@GIMP_API_VERSION@_la_DEPENDENCIES = $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+am_libgimpcolor_@GIMP_API_VERSION@_la_OBJECTS = \
+ gimpadaptivesupersample.lo gimpbilinear.lo gimpcairo.lo \
+ gimpcmyk.lo gimpcolormanaged.lo gimpcolorprofile.lo \
+ gimpcolorspace.lo gimpcolortransform.lo gimphsl.lo gimphsv.lo \
+ gimppixbuf.lo gimprgb.lo gimprgb-parse.lo
+libgimpcolor_@GIMP_API_VERSION@_la_OBJECTS = \
+ $(am_libgimpcolor_@GIMP_API_VERSION@_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libgimpcolor_@GIMP_API_VERSION@_la_LINK = $(LIBTOOL) $(AM_V_lt) \
+ --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libgimpcolor_@GIMP_API_VERSION@_la_LDFLAGS) $(LDFLAGS) -o $@
+test_color_parser_SOURCES = test-color-parser.c
+test_color_parser_OBJECTS = test-color-parser.$(OBJEXT)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/gimpadaptivesupersample.Plo \
+ ./$(DEPDIR)/gimpbilinear.Plo ./$(DEPDIR)/gimpcairo.Plo \
+ ./$(DEPDIR)/gimpcmyk.Plo ./$(DEPDIR)/gimpcolormanaged.Plo \
+ ./$(DEPDIR)/gimpcolorprofile.Plo \
+ ./$(DEPDIR)/gimpcolorspace.Plo \
+ ./$(DEPDIR)/gimpcolortransform.Plo ./$(DEPDIR)/gimphsl.Plo \
+ ./$(DEPDIR)/gimphsv.Plo ./$(DEPDIR)/gimppixbuf.Plo \
+ ./$(DEPDIR)/gimprgb-parse.Plo ./$(DEPDIR)/gimprgb.Plo \
+ ./$(DEPDIR)/test-color-parser.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libgimpcolor_@GIMP_API_VERSION@_la_SOURCES) \
+ test-color-parser.c
+DIST_SOURCES = $(libgimpcolor_@GIMP_API_VERSION@_la_SOURCES) \
+ test-color-parser.c
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+DATA = $(noinst_DATA)
+HEADERS = $(libgimpcolorinclude_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__tty_colors_dummy = \
+ mgn= red= grn= lgn= blu= brg= std=; \
+ am__color_tests=no
+am__tty_colors = { \
+ $(am__tty_colors_dummy); \
+ if test "X$(AM_COLOR_TESTS)" = Xno; then \
+ am__color_tests=no; \
+ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+ am__color_tests=yes; \
+ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+ am__color_tests=yes; \
+ fi; \
+ if test $$am__color_tests = yes; then \
+ red=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ fi; \
+}
+am__recheck_rx = ^[ ]*:recheck:[ ]*
+am__global_test_result_rx = ^[ ]*:global-test-result:[ ]*
+am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+ recheck = 1; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ { \
+ if ((getline line2 < ($$0 ".log")) < 0) \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+ { \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+ { \
+ break; \
+ } \
+ }; \
+ if (recheck) \
+ print $$0; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+ print "fatal: making $@: " msg | "cat >&2"; \
+ exit 1; \
+} \
+function rst_section(header) \
+{ \
+ print header; \
+ len = length(header); \
+ for (i = 1; i <= len; i = i + 1) \
+ printf "="; \
+ printf "\n\n"; \
+} \
+{ \
+ copy_in_global_log = 1; \
+ global_test_result = "RUN"; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".trs"); \
+ if (line ~ /$(am__global_test_result_rx)/) \
+ { \
+ sub("$(am__global_test_result_rx)", "", line); \
+ sub("[ ]*$$", "", line); \
+ global_test_result = line; \
+ } \
+ else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+ copy_in_global_log = 0; \
+ }; \
+ if (copy_in_global_log) \
+ { \
+ rst_section(global_test_result ": " $$0); \
+ while ((rc = (getline line < ($$0 ".log"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".log"); \
+ print line; \
+ }; \
+ printf "\n"; \
+ }; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+ --color-tests "$$am__color_tests" \
+ --enable-hard-errors "$$am__enable_hard_errors" \
+ --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test. Creates the
+# directory for the log if needed. Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log. Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT. Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup); \
+$(am__vpath_adj_setup) $(am__vpath_adj) \
+$(am__tty_colors); \
+srcdir=$(srcdir); export srcdir; \
+case "$@" in \
+ */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \
+ *) am__odir=.;; \
+esac; \
+test "x$$am__odir" = x"." || test -d "$$am__odir" \
+ || $(MKDIR_P) "$$am__odir" || exit $$?; \
+if test -f "./$$f"; then dir=./; \
+elif test -f "$$f"; then dir=; \
+else dir="$(srcdir)/"; fi; \
+tst=$$dir$$f; log='$@'; \
+if test -n '$(DISABLE_HARD_ERRORS)'; then \
+ am__enable_hard_errors=no; \
+else \
+ am__enable_hard_errors=yes; \
+fi; \
+case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \
+ am__expect_failure=yes;; \
+ *) \
+ am__expect_failure=no;; \
+esac; \
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed). The result is saved in the shell variable
+# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+ bases='$(TEST_LOGS)'; \
+ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+ bases=`echo $$bases`
+AM_TESTSUITE_SUMMARY_HEADER = ' for $(PACKAGE_STRING)'
+RECHECK_LOGS = $(TEST_LOGS)
+AM_RECURSIVE_TARGETS = check recheck
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+ case '$@' in \
+ */*) \
+ case '$*' in \
+ */*) b='$*';; \
+ *) b=`echo '$@' | sed 's/\.log$$//'`; \
+ esac;; \
+ *) \
+ b='$*';; \
+ esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+ $(TEST_LOG_FLAGS)
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp \
+ $(top_srcdir)/test-driver
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+@PLATFORM_WIN32_TRUE@no_undefined = -no-undefined
+@PLATFORM_WIN32_FALSE@libm = -lm
+@OS_WIN32_TRUE@gimpcolor_def = gimpcolor.def
+@OS_WIN32_TRUE@libgimpcolor_export_symbols = -export-symbols $(srcdir)/gimpcolor.def
+@MS_LIB_AVAILABLE_TRUE@noinst_DATA = gimpcolor-$(GIMP_API_VERSION).lib
+libgimpcolorincludedir = $(includedir)/gimp-$(GIMP_API_VERSION)/libgimpcolor
+AM_CPPFLAGS = \
+ -DG_LOG_DOMAIN=\"LibGimpColor\" \
+ -DGIMP_COLOR_COMPILATION \
+ -I$(top_srcdir) \
+ $(GEGL_CFLAGS) \
+ $(CAIRO_CFLAGS) \
+ $(GDK_PIXBUF_CFLAGS) \
+ $(LCMS_CFLAGS) \
+ -I$(includedir)
+
+EXTRA_DIST = \
+ gimpcolor.def
+
+lib_LTLIBRARIES = libgimpcolor-@GIMP_API_VERSION@.la
+libgimpcolor_@GIMP_API_VERSION@_la_SOURCES = \
+ gimpcolor.h \
+ gimpcolortypes.h \
+ gimpadaptivesupersample.c \
+ gimpadaptivesupersample.h \
+ gimpbilinear.c \
+ gimpbilinear.h \
+ gimpcairo.c \
+ gimpcairo.h \
+ gimpcmyk.c \
+ gimpcmyk.h \
+ gimpcolormanaged.c \
+ gimpcolormanaged.h \
+ gimpcolorprofile.c \
+ gimpcolorprofile.h \
+ gimpcolorspace.c \
+ gimpcolorspace.h \
+ gimpcolortransform.c \
+ gimpcolortransform.h \
+ gimphsl.c \
+ gimphsl.h \
+ gimphsv.c \
+ gimphsv.h \
+ gimppixbuf.c \
+ gimppixbuf.h \
+ gimprgb.c \
+ gimprgb.h \
+ gimprgb-parse.c
+
+libgimpcolorinclude_HEADERS = \
+ gimpcolor.h \
+ gimpcolortypes.h \
+ gimpadaptivesupersample.h \
+ gimpbilinear.h \
+ gimpcairo.h \
+ gimpcmyk.h \
+ gimpcolormanaged.h \
+ gimpcolorprofile.h \
+ gimpcolorspace.h \
+ gimpcolortransform.h \
+ gimphsl.h \
+ gimphsv.h \
+ gimppixbuf.h \
+ gimprgb.h
+
+libgimpcolor_@GIMP_API_VERSION@_la_LDFLAGS = \
+ -version-info $(LT_VERSION_INFO) \
+ $(no_undefined) \
+ $(libgimpcolor_export_symbols)
+
+EXTRA_libgimpcolor_@GIMP_API_VERSION@_la_DEPENDENCIES = $(gimpcolor_def)
+libgimpcolor_@GIMP_API_VERSION@_la_LIBADD = \
+ $(libgimpbase) \
+ $(GEGL_LIBS) \
+ $(CAIRO_LIBS) \
+ $(GDK_PIXBUF_LIBS) \
+ $(LCMS_LIBS) \
+ $(libm)
+
+
+#
+# test programs, not to be built by default and never installed
+#
+TESTS = test-color-parser$(EXEEXT)
+test_color_parser_DEPENDENCIES = \
+ $(libgimpbase) \
+ $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+
+test_color_parser_LDADD = \
+ $(CAIRO_LIBS) \
+ $(GLIB_LIBS) \
+ $(test_color_parser_DEPENDENCIES)
+
+CLEANFILES = $(EXTRA_PROGRAMS)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu libgimpcolor/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu libgimpcolor/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libgimpcolor-@GIMP_API_VERSION@.la: $(libgimpcolor_@GIMP_API_VERSION@_la_OBJECTS) $(libgimpcolor_@GIMP_API_VERSION@_la_DEPENDENCIES) $(EXTRA_libgimpcolor_@GIMP_API_VERSION@_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libgimpcolor_@GIMP_API_VERSION@_la_LINK) -rpath $(libdir) $(libgimpcolor_@GIMP_API_VERSION@_la_OBJECTS) $(libgimpcolor_@GIMP_API_VERSION@_la_LIBADD) $(LIBS)
+
+test-color-parser$(EXEEXT): $(test_color_parser_OBJECTS) $(test_color_parser_DEPENDENCIES) $(EXTRA_test_color_parser_DEPENDENCIES)
+ @rm -f test-color-parser$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_color_parser_OBJECTS) $(test_color_parser_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpadaptivesupersample.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbilinear.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcairo.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcmyk.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcolormanaged.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcolorprofile.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcolorspace.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcolortransform.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimphsl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimphsv.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppixbuf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimprgb-parse.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimprgb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-color-parser.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-libgimpcolorincludeHEADERS: $(libgimpcolorinclude_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(libgimpcolorinclude_HEADERS)'; test -n "$(libgimpcolorincludedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libgimpcolorincludedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libgimpcolorincludedir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(libgimpcolorincludedir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(libgimpcolorincludedir)" || exit $$?; \
+ done
+
+uninstall-libgimpcolorincludeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libgimpcolorinclude_HEADERS)'; test -n "$(libgimpcolorincludedir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(libgimpcolorincludedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+ rm -f $< $@
+ $(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+ @:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+ @$(am__set_TESTS_bases); \
+ am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+ redo_bases=`for i in $$bases; do \
+ am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+ done`; \
+ if test -n "$$redo_bases"; then \
+ redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+ redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+ if $(am__make_dryrun); then :; else \
+ rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
+ fi; \
+ fi; \
+ if test -n "$$am__remaking_logs"; then \
+ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+ "recursion detected" >&2; \
+ elif test -n "$$redo_logs"; then \
+ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+ fi; \
+ if $(am__make_dryrun); then :; else \
+ st=0; \
+ errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+ for i in $$redo_bases; do \
+ test -f $$i.trs && test -r $$i.trs \
+ || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+ test -f $$i.log && test -r $$i.log \
+ || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+ done; \
+ test $$st -eq 0 || exit 1; \
+ fi
+ @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+ ws='[ ]'; \
+ results=`for b in $$bases; do echo $$b.trs; done`; \
+ test -n "$$results" || results=/dev/null; \
+ all=` grep "^$$ws*:test-result:" $$results | wc -l`; \
+ pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \
+ fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \
+ skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \
+ xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+ xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+ error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+ if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+ success=true; \
+ else \
+ success=false; \
+ fi; \
+ br='==================='; br=$$br$$br$$br$$br; \
+ result_count () \
+ { \
+ if test x"$$1" = x"--maybe-color"; then \
+ maybe_colorize=yes; \
+ elif test x"$$1" = x"--no-color"; then \
+ maybe_colorize=no; \
+ else \
+ echo "$@: invalid 'result_count' usage" >&2; exit 4; \
+ fi; \
+ shift; \
+ desc=$$1 count=$$2; \
+ if test $$maybe_colorize = yes && test $$count -gt 0; then \
+ color_start=$$3 color_end=$$std; \
+ else \
+ color_start= color_end=; \
+ fi; \
+ echo "$${color_start}# $$desc $$count$${color_end}"; \
+ }; \
+ create_testsuite_report () \
+ { \
+ result_count $$1 "TOTAL:" $$all "$$brg"; \
+ result_count $$1 "PASS: " $$pass "$$grn"; \
+ result_count $$1 "SKIP: " $$skip "$$blu"; \
+ result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+ result_count $$1 "FAIL: " $$fail "$$red"; \
+ result_count $$1 "XPASS:" $$xpass "$$red"; \
+ result_count $$1 "ERROR:" $$error "$$mgn"; \
+ }; \
+ { \
+ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \
+ $(am__rst_title); \
+ create_testsuite_report --no-color; \
+ echo; \
+ echo ".. contents:: :depth: 2"; \
+ echo; \
+ for b in $$bases; do echo $$b; done \
+ | $(am__create_global_log); \
+ } >$(TEST_SUITE_LOG).tmp || exit 1; \
+ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \
+ if $$success; then \
+ col="$$grn"; \
+ else \
+ col="$$red"; \
+ test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \
+ fi; \
+ echo "$${col}$$br$${std}"; \
+ echo "$${col}Testsuite summary"$(AM_TESTSUITE_SUMMARY_HEADER)"$${std}"; \
+ echo "$${col}$$br$${std}"; \
+ create_testsuite_report --maybe-color; \
+ echo "$$col$$br$$std"; \
+ if $$success; then :; else \
+ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \
+ if test -n "$(PACKAGE_BUGREPORT)"; then \
+ echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \
+ fi; \
+ echo "$$col$$br$$std"; \
+ fi; \
+ $$success || exit 1
+
+check-TESTS:
+ @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list
+ @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+ log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+ exit $$?;
+recheck: all
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ bases=`for i in $$bases; do echo $$i; done \
+ | $(am__list_recheck_tests)` || exit 1; \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ log_list=`echo $$log_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+ am__force_recheck=am--force-recheck \
+ TEST_LOGS="$$log_list"; \
+ exit $$?
+test-color-parser.log: test-color-parser$(EXEEXT)
+ @p='test-color-parser$(EXEEXT)'; \
+ b='test-color-parser'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+ @p='$<'; \
+ $(am__set_b); \
+ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@ @p='$<'; \
+@am__EXEEXT_TRUE@ $(am__set_b); \
+@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT)
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(DATA) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libgimpcolorincludedir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+ -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+ -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+ -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/gimpadaptivesupersample.Plo
+ -rm -f ./$(DEPDIR)/gimpbilinear.Plo
+ -rm -f ./$(DEPDIR)/gimpcairo.Plo
+ -rm -f ./$(DEPDIR)/gimpcmyk.Plo
+ -rm -f ./$(DEPDIR)/gimpcolormanaged.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorprofile.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorspace.Plo
+ -rm -f ./$(DEPDIR)/gimpcolortransform.Plo
+ -rm -f ./$(DEPDIR)/gimphsl.Plo
+ -rm -f ./$(DEPDIR)/gimphsv.Plo
+ -rm -f ./$(DEPDIR)/gimppixbuf.Plo
+ -rm -f ./$(DEPDIR)/gimprgb-parse.Plo
+ -rm -f ./$(DEPDIR)/gimprgb.Plo
+ -rm -f ./$(DEPDIR)/test-color-parser.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-data-local install-libgimpcolorincludeHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/gimpadaptivesupersample.Plo
+ -rm -f ./$(DEPDIR)/gimpbilinear.Plo
+ -rm -f ./$(DEPDIR)/gimpcairo.Plo
+ -rm -f ./$(DEPDIR)/gimpcmyk.Plo
+ -rm -f ./$(DEPDIR)/gimpcolormanaged.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorprofile.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorspace.Plo
+ -rm -f ./$(DEPDIR)/gimpcolortransform.Plo
+ -rm -f ./$(DEPDIR)/gimphsl.Plo
+ -rm -f ./$(DEPDIR)/gimphsv.Plo
+ -rm -f ./$(DEPDIR)/gimppixbuf.Plo
+ -rm -f ./$(DEPDIR)/gimprgb-parse.Plo
+ -rm -f ./$(DEPDIR)/gimprgb.Plo
+ -rm -f ./$(DEPDIR)/test-color-parser.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libLTLIBRARIES \
+ uninstall-libgimpcolorincludeHEADERS uninstall-local
+
+.MAKE: check-am install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-TESTS \
+ check-am clean clean-generic clean-libLTLIBRARIES \
+ clean-libtool cscopelist-am ctags ctags-am distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am \
+ install-data-local install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-libLTLIBRARIES \
+ install-libgimpcolorincludeHEADERS install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ recheck tags tags-am uninstall uninstall-am \
+ uninstall-libLTLIBRARIES uninstall-libgimpcolorincludeHEADERS \
+ uninstall-local
+
+.PRECIOUS: Makefile
+
+
+@OS_WIN32_TRUE@install-libtool-import-lib:
+@OS_WIN32_TRUE@ $(INSTALL) .libs/libgimpcolor-$(GIMP_API_VERSION).dll.a $(DESTDIR)$(libdir)
+@OS_WIN32_TRUE@ $(INSTALL) $(srcdir)/gimpcolor.def $(DESTDIR)$(libdir)
+
+@OS_WIN32_TRUE@uninstall-libtool-import-lib:
+@OS_WIN32_TRUE@ -rm $(DESTDIR)$(libdir)/libgimpcolor-$(GIMP_API_VERSION).dll.a
+@OS_WIN32_TRUE@ -rm $(DESTDIR)$(libdir)/gimpcolor.def
+@OS_WIN32_FALSE@install-libtool-import-lib:
+@OS_WIN32_FALSE@uninstall-libtool-import-lib:
+
+@MS_LIB_AVAILABLE_TRUE@install-ms-lib:
+@MS_LIB_AVAILABLE_TRUE@ $(INSTALL) gimpcolor-$(GIMP_API_VERSION).lib $(DESTDIR)$(libdir)
+
+@MS_LIB_AVAILABLE_TRUE@uninstall-ms-lib:
+@MS_LIB_AVAILABLE_TRUE@ -rm $(DESTDIR)$(libdir)/gimpcolor-$(GIMP_API_VERSION).lib
+
+@MS_LIB_AVAILABLE_TRUE@gimpcolor-@GIMP_API_VERSION@.lib: gimpcolor.def
+@MS_LIB_AVAILABLE_TRUE@ lib -name:libgimpcolor-$(GIMP_API_VERSION)-@LT_CURRENT_MINUS_AGE@.dll -def:gimpcolor.def -out:$@
+
+@MS_LIB_AVAILABLE_FALSE@install-ms-lib:
+@MS_LIB_AVAILABLE_FALSE@uninstall-ms-lib:
+
+install-data-local: install-ms-lib install-libtool-import-lib
+
+uninstall-local: uninstall-ms-lib uninstall-libtool-import-lib
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libgimpcolor/gimpadaptivesupersample.c b/libgimpcolor/gimpadaptivesupersample.c
new file mode 100644
index 0000000..3b9bedc
--- /dev/null
+++ b/libgimpcolor/gimpadaptivesupersample.c
@@ -0,0 +1,394 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <glib-object.h>
+
+#include "libgimpmath/gimpmath.h"
+
+#include "gimpcolortypes.h"
+
+#include "gimpadaptivesupersample.h"
+#include "gimprgb.h"
+
+
+/**
+ * SECTION: gimpadaptivesupersample
+ * @title: GimpAdaptiveSupersample
+ * @short_description: Functions to perform adaptive supersampling on
+ * an area.
+ *
+ * Functions to perform adaptive supersampling on an area.
+ **/
+
+
+/*********************************************************************/
+/* Sumpersampling code (Quartic) */
+/* This code is *largely* based on the sources for POV-Ray 3.0. I am */
+/* grateful to the POV-Team for such a great program and for making */
+/* their sources available. All comments / bug reports / */
+/* etc. regarding this code should be addressed to me, not to the */
+/* POV-Ray team. Any bugs are my responsibility, not theirs. */
+/*********************************************************************/
+
+
+typedef struct _GimpSampleType GimpSampleType;
+
+struct _GimpSampleType
+{
+ guchar ready;
+ GimpRGB color;
+};
+
+
+static gulong
+gimp_render_sub_pixel (gint max_depth,
+ gint depth,
+ GimpSampleType **block,
+ gint x,
+ gint y,
+ gint x1,
+ gint y1,
+ gint x3,
+ gint y3,
+ gdouble threshold,
+ gint sub_pixel_size,
+ GimpRGB *color,
+ GimpRenderFunc render_func,
+ gpointer render_data)
+{
+ gint x2, y2; /* Coords of center sample */
+ gdouble dx1, dy1; /* Delta to upper left sample */
+ gdouble dx3, dy3; /* Delta to lower right sample */
+ GimpRGB c[4]; /* Sample colors */
+ gulong num_samples = 0;
+ gint cnt;
+
+ g_return_val_if_fail (render_func != NULL, 0);
+
+ /* Get offsets for corners */
+
+ dx1 = (gdouble) (x1 - sub_pixel_size / 2) / sub_pixel_size;
+ dx3 = (gdouble) (x3 - sub_pixel_size / 2) / sub_pixel_size;
+
+ dy1 = (gdouble) (y1 - sub_pixel_size / 2) / sub_pixel_size;
+ dy3 = (gdouble) (y3 - sub_pixel_size / 2) / sub_pixel_size;
+
+ /* Render upper left sample */
+
+ if (! block[y1][x1].ready)
+ {
+ num_samples++;
+
+ render_func (x + dx1, y + dy1, &c[0], render_data);
+
+ block[y1][x1].ready = TRUE;
+ block[y1][x1].color = c[0];
+ }
+ else
+ {
+ c[0] = block[y1][x1].color;
+ }
+
+ /* Render upper right sample */
+
+ if (! block[y1][x3].ready)
+ {
+ num_samples++;
+
+ render_func (x + dx3, y + dy1, &c[1], render_data);
+
+ block[y1][x3].ready = TRUE;
+ block[y1][x3].color = c[1];
+ }
+ else
+ {
+ c[1] = block[y1][x3].color;
+ }
+
+ /* Render lower left sample */
+
+ if (! block[y3][x1].ready)
+ {
+ num_samples++;
+
+ render_func (x + dx1, y + dy3, &c[2], render_data);
+
+ block[y3][x1].ready = TRUE;
+ block[y3][x1].color = c[2];
+ }
+ else
+ {
+ c[2] = block[y3][x1].color;
+ }
+
+ /* Render lower right sample */
+
+ if (! block[y3][x3].ready)
+ {
+ num_samples++;
+
+ render_func (x + dx3, y + dy3, &c[3], render_data);
+
+ block[y3][x3].ready = TRUE;
+ block[y3][x3].color = c[3];
+ }
+ else
+ {
+ c[3] = block[y3][x3].color;
+ }
+
+ /* Check for supersampling */
+
+ if (depth <= max_depth)
+ {
+ /* Check whether we have to supersample */
+
+ if ((gimp_rgba_distance (&c[0], &c[1]) >= threshold) ||
+ (gimp_rgba_distance (&c[0], &c[2]) >= threshold) ||
+ (gimp_rgba_distance (&c[0], &c[3]) >= threshold) ||
+ (gimp_rgba_distance (&c[1], &c[2]) >= threshold) ||
+ (gimp_rgba_distance (&c[1], &c[3]) >= threshold) ||
+ (gimp_rgba_distance (&c[2], &c[3]) >= threshold))
+ {
+ /* Calc coordinates of center subsample */
+
+ x2 = (x1 + x3) / 2;
+ y2 = (y1 + y3) / 2;
+
+ /* Render sub-blocks */
+
+ num_samples += gimp_render_sub_pixel (max_depth, depth + 1, block,
+ x, y, x1, y1, x2, y2,
+ threshold, sub_pixel_size,
+ &c[0],
+ render_func, render_data);
+
+ num_samples += gimp_render_sub_pixel (max_depth, depth + 1, block,
+ x, y, x2, y1, x3, y2,
+ threshold, sub_pixel_size,
+ &c[1],
+ render_func, render_data);
+
+ num_samples += gimp_render_sub_pixel (max_depth, depth + 1, block,
+ x, y, x1, y2, x2, y3,
+ threshold, sub_pixel_size,
+ &c[2],
+ render_func, render_data);
+
+ num_samples += gimp_render_sub_pixel (max_depth, depth + 1, block,
+ x, y, x2, y2, x3, y3,
+ threshold, sub_pixel_size,
+ &c[3],
+ render_func, render_data);
+ }
+ }
+
+ if (c[0].a == 0.0 || c[1].a == 0.0 || c[2].a == 0.0 || c[3].a == 0.0)
+ {
+ GimpRGB tmpcol;
+ gdouble weight;
+
+ gimp_rgb_set (&tmpcol, 0.0, 0.0, 0.0);
+
+ weight = 2.0;
+
+ for (cnt = 0; cnt < 4; cnt++)
+ {
+ if (c[cnt].a != 0.0)
+ {
+ tmpcol.r += c[cnt].r;
+ tmpcol.g += c[cnt].g;
+ tmpcol.b += c[cnt].b;
+
+ weight /= 2.0;
+ }
+ }
+
+ color->r = weight * tmpcol.r;
+ color->g = weight * tmpcol.g;
+ color->b = weight * tmpcol.b;
+ }
+ else
+ {
+ color->r = 0.25 * (c[0].r + c[1].r + c[2].r + c[3].r);
+ color->g = 0.25 * (c[0].g + c[1].g + c[2].g + c[3].g);
+ color->b = 0.25 * (c[0].b + c[1].b + c[2].b + c[3].b);
+ }
+
+ color->a = 0.25 * (c[0].a + c[1].a + c[2].a + c[3].a);
+
+ return num_samples;
+}
+
+gulong
+gimp_adaptive_supersample_area (gint x1,
+ gint y1,
+ gint x2,
+ gint y2,
+ gint max_depth,
+ gdouble threshold,
+ GimpRenderFunc render_func,
+ gpointer render_data,
+ GimpPutPixelFunc put_pixel_func,
+ gpointer put_pixel_data,
+ GimpProgressFunc progress_func,
+ gpointer progress_data)
+{
+ gint x, y, width; /* Counters, width of region */
+ gint xt, xtt, yt; /* Temporary counters */
+ gint sub_pixel_size; /* Number of samples per pixel (1D) */
+ GimpRGB color; /* Rendered pixel's color */
+ GimpSampleType tmp_sample; /* For swapping samples */
+ GimpSampleType *top_row, *bot_row, *tmp_row; /* Sample rows */
+ GimpSampleType **block; /* Sample block matrix */
+ gulong num_samples;
+
+ g_return_val_if_fail (render_func != NULL, 0);
+ g_return_val_if_fail (put_pixel_func != NULL, 0);
+
+ /* Initialize color */
+
+ gimp_rgba_set (&color, 0.0, 0.0, 0.0, 0.0);
+
+ /* Calculate sub-pixel size */
+
+ sub_pixel_size = 1 << max_depth;
+
+ /* Create row arrays */
+
+ width = x2 - x1 + 1;
+
+ top_row = gegl_scratch_new (GimpSampleType, sub_pixel_size * width + 1);
+ bot_row = gegl_scratch_new (GimpSampleType, sub_pixel_size * width + 1);
+
+ for (x = 0; x < (sub_pixel_size * width + 1); x++)
+ {
+ top_row[x].ready = FALSE;
+
+ gimp_rgba_set (&top_row[x].color, 0.0, 0.0, 0.0, 0.0);
+
+ bot_row[x].ready = FALSE;
+
+ gimp_rgba_set (&bot_row[x].color, 0.0, 0.0, 0.0, 0.0);
+ }
+
+ /* Allocate block matrix */
+
+ block = gegl_scratch_new (GimpSampleType *, sub_pixel_size + 1); /* Rows */
+
+ for (y = 0; y < (sub_pixel_size + 1); y++)
+ {
+ block[y] = gegl_scratch_new (GimpSampleType, sub_pixel_size + 1); /* Columns */
+
+ for (x = 0; x < (sub_pixel_size + 1); x++)
+ {
+ block[y][x].ready = FALSE;
+
+ gimp_rgba_set (&block[y][x].color, 0.0, 0.0, 0.0, 0.0);
+ }
+ }
+
+ /* Render region */
+
+ num_samples = 0;
+
+ for (y = y1; y <= y2; y++)
+ {
+ /* Clear the bottom row */
+
+ for (xt = 0; xt < (sub_pixel_size * width + 1); xt++)
+ bot_row[xt].ready = FALSE;
+
+ /* Clear first column */
+
+ for (yt = 0; yt < (sub_pixel_size + 1); yt++)
+ block[yt][0].ready = FALSE;
+
+ /* Render row */
+
+ for (x = x1; x <= x2; x++)
+ {
+ /* Initialize block by clearing all but first row/column */
+
+ for (yt = 1; yt < (sub_pixel_size + 1); yt++)
+ for (xt = 1; xt < (sub_pixel_size + 1); xt++)
+ block[yt][xt].ready = FALSE;
+
+ /* Copy samples from top row to block */
+
+ for (xtt = 0, xt = (x - x1) * sub_pixel_size;
+ xtt < (sub_pixel_size + 1);
+ xtt++, xt++)
+ block[0][xtt] = top_row[xt];
+
+ /* Render pixel on (x, y) */
+
+ num_samples += gimp_render_sub_pixel (max_depth, 1, block, x, y, 0, 0,
+ sub_pixel_size, sub_pixel_size,
+ threshold, sub_pixel_size,
+ &color,
+ render_func, render_data);
+
+ if (put_pixel_func)
+ (* put_pixel_func) (x, y, &color, put_pixel_data);
+
+ /* Copy block information to rows */
+
+ top_row[(x - x1 + 1) * sub_pixel_size] = block[0][sub_pixel_size];
+
+ for (xtt = 0, xt = (x - x1) * sub_pixel_size;
+ xtt < (sub_pixel_size + 1);
+ xtt++, xt++)
+ bot_row[xt] = block[sub_pixel_size][xtt];
+
+ /* Swap first and last columns */
+
+ for (yt = 0; yt < (sub_pixel_size + 1); yt++)
+ {
+ tmp_sample = block[yt][0];
+ block[yt][0] = block[yt][sub_pixel_size];
+ block[yt][sub_pixel_size] = tmp_sample;
+ }
+ }
+
+ /* Swap rows */
+
+ tmp_row = top_row;
+ top_row = bot_row;
+ bot_row = tmp_row;
+
+ /* Call progress display function (if any) */
+
+ if (progress_func != NULL)
+ (* progress_func) (y1, y2, y, progress_data);
+ }
+
+ /* Free memory */
+
+ for (y = 0; y < (sub_pixel_size + 1); y++)
+ gegl_scratch_free (block[y]);
+
+ gegl_scratch_free (block);
+ gegl_scratch_free (top_row);
+ gegl_scratch_free (bot_row);
+
+ return num_samples;
+}
diff --git a/libgimpcolor/gimpadaptivesupersample.h b/libgimpcolor/gimpadaptivesupersample.h
new file mode 100644
index 0000000..fdee486
--- /dev/null
+++ b/libgimpcolor/gimpadaptivesupersample.h
@@ -0,0 +1,50 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_COLOR_H_INSIDE__) && !defined (GIMP_COLOR_COMPILATION)
+#error "Only <libgimpcolor/gimpcolor.h> can be included directly."
+#endif
+
+#ifndef __GIMP_ADAPTIVE_SUPERSAMPLE_H__
+#define __GIMP_ADAPTIVE_SUPERSAMPLE_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+/* adaptive supersampling function taken from LibGCK */
+
+
+gulong gimp_adaptive_supersample_area (gint x1,
+ gint y1,
+ gint x2,
+ gint y2,
+ gint max_depth,
+ gdouble threshold,
+ GimpRenderFunc render_func,
+ gpointer render_data,
+ GimpPutPixelFunc put_pixel_func,
+ gpointer put_pixel_data,
+ GimpProgressFunc progress_func,
+ gpointer progress_data);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_ADAPTIVE_SUPERSAMPLE_H__ */
diff --git a/libgimpcolor/gimpbilinear.c b/libgimpcolor/gimpbilinear.c
new file mode 100644
index 0000000..1de06b6
--- /dev/null
+++ b/libgimpcolor/gimpbilinear.c
@@ -0,0 +1,313 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+
+#include "libgimpmath/gimpmath.h"
+
+#include "gimpcolortypes.h"
+
+#include "gimpbilinear.h"
+
+
+/**
+ * SECTION: gimpbilinear
+ * @title: GimpBilinear
+ * @short_description: Utility functions for bilinear interpolation.
+ *
+ * Utility functions for bilinear interpolation.
+ **/
+
+
+gdouble
+gimp_bilinear (gdouble x,
+ gdouble y,
+ gdouble *values)
+{
+ gdouble m0, m1;
+
+ g_return_val_if_fail (values != NULL, 0.0);
+
+ x = fmod (x, 1.0);
+ y = fmod (y, 1.0);
+
+ if (x < 0.0)
+ x += 1.0;
+ if (y < 0.0)
+ y += 1.0;
+
+ m0 = (1.0 - x) * values[0] + x * values[1];
+ m1 = (1.0 - x) * values[2] + x * values[3];
+
+ return (1.0 - y) * m0 + y * m1;
+}
+
+guchar
+gimp_bilinear_8 (gdouble x,
+ gdouble y,
+ guchar *values)
+{
+ gdouble m0, m1;
+
+ g_return_val_if_fail (values != NULL, 0);
+
+ x = fmod (x, 1.0);
+ y = fmod (y, 1.0);
+
+ if (x < 0.0)
+ x += 1.0;
+ if (y < 0.0)
+ y += 1.0;
+
+ m0 = (1.0 - x) * values[0] + x * values[1];
+ m1 = (1.0 - x) * values[2] + x * values[3];
+
+ return (guchar) ((1.0 - y) * m0 + y * m1);
+}
+
+guint16
+gimp_bilinear_16 (gdouble x,
+ gdouble y,
+ guint16 *values)
+{
+ gdouble m0, m1;
+
+ g_return_val_if_fail (values != NULL, 0);
+
+ x = fmod (x, 1.0);
+ y = fmod (y, 1.0);
+
+ if (x < 0.0)
+ x += 1.0;
+ if (y < 0.0)
+ y += 1.0;
+
+ m0 = (1.0 - x) * values[0] + x * values[1];
+ m1 = (1.0 - x) * values[2] + x * values[3];
+
+ return (guint16) ((1.0 - y) * m0 + y * m1);
+}
+
+guint32
+gimp_bilinear_32 (gdouble x,
+ gdouble y,
+ guint32 *values)
+{
+ gdouble m0, m1;
+
+ g_return_val_if_fail (values != NULL, 0);
+
+ x = fmod (x, 1.0);
+ y = fmod (y, 1.0);
+
+ if (x < 0.0)
+ x += 1.0;
+ if (y < 0.0)
+ y += 1.0;
+
+ m0 = (1.0 - x) * values[0] + x * values[1];
+ m1 = (1.0 - x) * values[2] + x * values[3];
+
+ return (guint32) ((1.0 - y) * m0 + y * m1);
+}
+
+GimpRGB
+gimp_bilinear_rgb (gdouble x,
+ gdouble y,
+ GimpRGB *values)
+{
+ gdouble m0, m1;
+ gdouble ix, iy;
+ GimpRGB v = { 0, };
+
+ g_return_val_if_fail (values != NULL, v);
+
+ x = fmod(x, 1.0);
+ y = fmod(y, 1.0);
+
+ if (x < 0)
+ x += 1.0;
+ if (y < 0)
+ y += 1.0;
+
+ ix = 1.0 - x;
+ iy = 1.0 - y;
+
+ /* Red */
+
+ m0 = ix * values[0].r + x * values[1].r;
+ m1 = ix * values[2].r + x * values[3].r;
+
+ v.r = iy * m0 + y * m1;
+
+ /* Green */
+
+ m0 = ix * values[0].g + x * values[1].g;
+ m1 = ix * values[2].g + x * values[3].g;
+
+ v.g = iy * m0 + y * m1;
+
+ /* Blue */
+
+ m0 = ix * values[0].b + x * values[1].b;
+ m1 = ix * values[2].b + x * values[3].b;
+
+ v.b = iy * m0 + y * m1;
+
+ return v;
+}
+
+GimpRGB
+gimp_bilinear_rgba (gdouble x,
+ gdouble y,
+ GimpRGB *values)
+{
+ gdouble m0, m1;
+ gdouble ix, iy;
+ gdouble a0, a1, a2, a3, alpha;
+ GimpRGB v = { 0, };
+
+ g_return_val_if_fail (values != NULL, v);
+
+ x = fmod (x, 1.0);
+ y = fmod (y, 1.0);
+
+ if (x < 0)
+ x += 1.0;
+ if (y < 0)
+ y += 1.0;
+
+ ix = 1.0 - x;
+ iy = 1.0 - y;
+
+ a0 = values[0].a;
+ a1 = values[1].a;
+ a2 = values[2].a;
+ a3 = values[3].a;
+
+ /* Alpha */
+
+ m0 = ix * a0 + x * a1;
+ m1 = ix * a2 + x * a3;
+
+ alpha = v.a = iy * m0 + y * m1;
+
+ if (alpha > 0)
+ {
+ /* Red */
+
+ m0 = ix * a0 * values[0].r + x * a1 * values[1].r;
+ m1 = ix * a2 * values[2].r + x * a3 * values[3].r;
+
+ v.r = (iy * m0 + y * m1)/alpha;
+
+ /* Green */
+
+ m0 = ix * a0 * values[0].g + x * a1 * values[1].g;
+ m1 = ix * a2 * values[2].g + x * a3 * values[3].g;
+
+ v.g = (iy * m0 + y * m1)/alpha;
+
+ /* Blue */
+
+ m0 = ix * a0 * values[0].b + x * a1 * values[1].b;
+ m1 = ix * a2 * values[2].b + x * a3 * values[3].b;
+
+ v.b = (iy * m0 + y * m1)/alpha;
+ }
+
+ return v;
+}
+
+/**
+ * gimp_bilinear_pixels_8:
+ * @dest: Pixel, where interpolation result is to be stored.
+ * @x: x-coordinate (0.0 to 1.0).
+ * @y: y-coordinate (0.0 to 1.0).
+ * @bpp: Bytes per pixel. @dest and each @values item is an array of
+ * @bpp bytes.
+ * @has_alpha: %TRUE if the last channel is an alpha channel.
+ * @values: Array of four pointers to pixels.
+ *
+ * Computes bilinear interpolation of four pixels.
+ *
+ * When @has_alpha is %FALSE, it's identical to gimp_bilinear_8() on
+ * each channel separately. When @has_alpha is %TRUE, it handles
+ * alpha channel correctly.
+ *
+ * The pixels in @values correspond to corner x, y coordinates in the
+ * following order: [0,0], [1,0], [0,1], [1,1].
+ **/
+void
+gimp_bilinear_pixels_8 (guchar *dest,
+ gdouble x,
+ gdouble y,
+ guint bpp,
+ gboolean has_alpha,
+ guchar **values)
+{
+ guint i;
+
+ g_return_if_fail (dest != NULL);
+ g_return_if_fail (values != NULL);
+
+ x = fmod (x, 1.0);
+ y = fmod (y, 1.0);
+
+ if (x < 0.0)
+ x += 1.0;
+ if (y < 0.0)
+ y += 1.0;
+
+ if (has_alpha)
+ {
+ guint ai = bpp - 1;
+ gdouble alpha0 = values[0][ai];
+ gdouble alpha1 = values[1][ai];
+ gdouble alpha2 = values[2][ai];
+ gdouble alpha3 = values[3][ai];
+ gdouble alpha = ((1.0 - y) * ((1.0 - x) * alpha0 + x * alpha1)
+ + y * ((1.0 - x) * alpha2 + x * alpha3));
+
+ dest[ai] = (guchar) alpha;
+ if (dest[ai])
+ {
+ for (i = 0; i < ai; i++)
+ {
+ gdouble m0 = ((1.0 - x) * values[0][i] * alpha0
+ + x * values[1][i] * alpha1);
+ gdouble m1 = ((1.0 - x) * values[2][i] * alpha2
+ + x * values[3][i] * alpha3);
+
+ dest[i] = (guchar) (((1.0 - y) * m0 + y * m1) / alpha);
+ }
+ }
+ }
+ else
+ {
+ for (i = 0; i < bpp; i++)
+ {
+ gdouble m0 = (1.0 - x) * values[0][i] + x * values[1][i];
+ gdouble m1 = (1.0 - x) * values[2][i] + x * values[3][i];
+
+ dest[i] = (guchar) ((1.0 - y) * m0 + y * m1);
+ }
+ }
+}
diff --git a/libgimpcolor/gimpbilinear.h b/libgimpcolor/gimpbilinear.h
new file mode 100644
index 0000000..d56007c
--- /dev/null
+++ b/libgimpcolor/gimpbilinear.h
@@ -0,0 +1,63 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_COLOR_H_INSIDE__) && !defined (GIMP_COLOR_COMPILATION)
+#error "Only <libgimpcolor/gimpcolor.h> can be included directly."
+#endif
+
+#ifndef __GIMP_BILINEAR_H__
+#define __GIMP_BILINEAR_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+/* bilinear interpolation functions taken from LibGCK */
+
+
+gdouble gimp_bilinear (gdouble x,
+ gdouble y,
+ gdouble *values);
+guchar gimp_bilinear_8 (gdouble x,
+ gdouble y,
+ guchar *values);
+guint16 gimp_bilinear_16 (gdouble x,
+ gdouble y,
+ guint16 *values);
+guint32 gimp_bilinear_32 (gdouble x,
+ gdouble y,
+ guint32 *values);
+GimpRGB gimp_bilinear_rgb (gdouble x,
+ gdouble y,
+ GimpRGB *values);
+GimpRGB gimp_bilinear_rgba (gdouble x,
+ gdouble y,
+ GimpRGB *values);
+
+GIMP_DEPRECATED
+void gimp_bilinear_pixels_8 (guchar *dest,
+ gdouble x,
+ gdouble y,
+ guint bpp,
+ gboolean has_alpha,
+ guchar **values);
+
+G_END_DECLS
+
+#endif /* __GIMP_BILINEAR_H__ */
diff --git a/libgimpcolor/gimpcairo.c b/libgimpcolor/gimpcairo.c
new file mode 100644
index 0000000..e1e57d9
--- /dev/null
+++ b/libgimpcolor/gimpcairo.c
@@ -0,0 +1,210 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcairo.c
+ * Copyright (C) 2007 Sven Neumann <sven@gimp.org>
+ * 2010-2012 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <cairo.h>
+#include <gio/gio.h>
+#include <gegl.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "gimpcolortypes.h"
+
+#include "gimpcairo.h"
+
+
+/**
+ * SECTION: gimpcairo
+ * @title: GimpCairo
+ * @short_description: Color utility functions for cairo
+ *
+ * Utility functions that make cairo easier to use with GIMP color
+ * data types.
+ **/
+
+
+/**
+ * gimp_cairo_set_source_rgb:
+ * @cr: Cairo context
+ * @color: GimpRGB color
+ *
+ * Sets the source pattern within @cr to the solid opaque color
+ * described by @color.
+ *
+ * This function calls cairo_set_source_rgb() for you.
+ *
+ * Since: 2.6
+ **/
+void
+gimp_cairo_set_source_rgb (cairo_t *cr,
+ const GimpRGB *color)
+{
+ cairo_set_source_rgb (cr, color->r, color->g, color->b);
+}
+
+/**
+ * gimp_cairo_set_source_rgba:
+ * @cr: Cairo context
+ * @color: GimpRGB color
+ *
+ * Sets the source pattern within @cr to the solid translucent color
+ * described by @color.
+ *
+ * This function calls cairo_set_source_rgba() for you.
+ *
+ * Since: 2.6
+ **/
+void
+gimp_cairo_set_source_rgba (cairo_t *cr,
+ const GimpRGB *color)
+{
+ cairo_set_source_rgba (cr, color->r, color->g, color->b, color->a);
+}
+
+/**
+ * gimp_cairo_checkerboard_create:
+ * @cr: Cairo context
+ * @size: check size
+ * @light: light check color or %NULL to use the default light gray
+ * @dark: dark check color or %NULL to use the default dark gray
+ *
+ * Create a repeating checkerboard pattern.
+ *
+ * Return value: a new Cairo pattern that can be used as a source on @cr.
+ *
+ * Since: 2.6
+ **/
+cairo_pattern_t *
+gimp_cairo_checkerboard_create (cairo_t *cr,
+ gint size,
+ const GimpRGB *light,
+ const GimpRGB *dark)
+{
+ cairo_t *context;
+ cairo_surface_t *surface;
+ cairo_pattern_t *pattern;
+
+ g_return_val_if_fail (cr != NULL, NULL);
+ g_return_val_if_fail (size > 0, NULL);
+
+ surface = cairo_surface_create_similar (cairo_get_target (cr),
+ CAIRO_CONTENT_COLOR,
+ 2 * size, 2 * size);
+ context = cairo_create (surface);
+
+ if (light)
+ gimp_cairo_set_source_rgb (context, light);
+ else
+ cairo_set_source_rgb (context,
+ GIMP_CHECK_LIGHT, GIMP_CHECK_LIGHT, GIMP_CHECK_LIGHT);
+
+ cairo_rectangle (context, 0, 0, size, size);
+ cairo_rectangle (context, size, size, size, size);
+ cairo_fill (context);
+
+ if (dark)
+ gimp_cairo_set_source_rgb (context, dark);
+ else
+ cairo_set_source_rgb (context,
+ GIMP_CHECK_DARK, GIMP_CHECK_DARK, GIMP_CHECK_DARK);
+
+ cairo_rectangle (context, 0, size, size, size);
+ cairo_rectangle (context, size, 0, size, size);
+ cairo_fill (context);
+
+ cairo_destroy (context);
+
+ pattern = cairo_pattern_create_for_surface (surface);
+ cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
+
+ cairo_surface_destroy (surface);
+
+ return pattern;
+}
+
+/**
+ * gimp_cairo_surface_get_format:
+ * @surface: a Cairo surface
+ *
+ * This function returns a #Babl format that corresponds to @surface's
+ * pixel format.
+ *
+ * Return value: the #Babl format of @surface.
+ *
+ * Since: 2.10
+ **/
+const Babl *
+gimp_cairo_surface_get_format (cairo_surface_t *surface)
+{
+ g_return_val_if_fail (surface != NULL, NULL);
+ g_return_val_if_fail (cairo_surface_get_type (surface) ==
+ CAIRO_SURFACE_TYPE_IMAGE, NULL);
+
+ switch (cairo_image_surface_get_format (surface))
+ {
+ case CAIRO_FORMAT_RGB24: return babl_format ("cairo-RGB24");
+ case CAIRO_FORMAT_ARGB32: return babl_format ("cairo-ARGB32");
+ case CAIRO_FORMAT_A8: return babl_format ("cairo-A8");
+
+ default:
+ break;
+ }
+
+ g_return_val_if_reached (NULL);
+}
+
+/**
+ * gimp_cairo_surface_create_buffer:
+ * @surface: a Cairo surface
+ *
+ * This function returns a #GeglBuffer which wraps @surface's pixels.
+ * It must only be called on image surfaces, calling it on other surface
+ * types is an error.
+ *
+ * Return value: a #GeglBuffer
+ *
+ * Since: 2.10
+ **/
+GeglBuffer *
+gimp_cairo_surface_create_buffer (cairo_surface_t *surface)
+{
+ const Babl *format;
+ gint width;
+ gint height;
+
+ g_return_val_if_fail (surface != NULL, NULL);
+ g_return_val_if_fail (cairo_surface_get_type (surface) ==
+ CAIRO_SURFACE_TYPE_IMAGE, NULL);
+
+ format = gimp_cairo_surface_get_format (surface);
+ width = cairo_image_surface_get_width (surface);
+ height = cairo_image_surface_get_height (surface);
+
+ return
+ gegl_buffer_linear_new_from_data (cairo_image_surface_get_data (surface),
+ format,
+ GEGL_RECTANGLE (0, 0, width, height),
+ cairo_image_surface_get_stride (surface),
+ (GDestroyNotify) cairo_surface_destroy,
+ cairo_surface_reference (surface));
+}
diff --git a/libgimpcolor/gimpcairo.h b/libgimpcolor/gimpcairo.h
new file mode 100644
index 0000000..d96e963
--- /dev/null
+++ b/libgimpcolor/gimpcairo.h
@@ -0,0 +1,159 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcairo.h
+ * Copyright (C) 2007 Sven Neumann <sven@gimp.org>
+ * 2010-2012 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_CAIRO_H__
+#define __GIMP_CAIRO_H__
+
+
+void gimp_cairo_set_source_rgb (cairo_t *cr,
+ const GimpRGB *color);
+void gimp_cairo_set_source_rgba (cairo_t *cr,
+ const GimpRGB *color);
+
+cairo_pattern_t * gimp_cairo_checkerboard_create (cairo_t *cr,
+ gint size,
+ const GimpRGB *light,
+ const GimpRGB *dark);
+
+const Babl * gimp_cairo_surface_get_format (cairo_surface_t *surface);
+GeglBuffer * gimp_cairo_surface_create_buffer (cairo_surface_t *surface);
+
+
+/* some useful macros for writing directly to a Cairo surface */
+
+/**
+ * GIMP_CAIRO_RGB24_SET_PIXEL:
+ * @d: pointer to the destination buffer
+ * @r: red component
+ * @g: green component
+ * @b: blue component
+ *
+ * Sets a single pixel in an Cairo image surface in %CAIRO_FORMAT_RGB24.
+ *
+ * Since: 2.6
+ **/
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+#define GIMP_CAIRO_RGB24_SET_PIXEL(d, r, g, b) \
+ G_STMT_START { d[0] = (b); d[1] = (g); d[2] = (r); } G_STMT_END
+#else
+#define GIMP_CAIRO_RGB24_SET_PIXEL(d, r, g, b) \
+ G_STMT_START { d[1] = (r); d[2] = (g); d[3] = (b); } G_STMT_END
+#endif
+
+
+/**
+ * GIMP_CAIRO_RGB24_GET_PIXEL:
+ * @s: pointer to the source buffer
+ * @r: red component
+ * @g: green component
+ * @b: blue component
+ *
+ * Gets a single pixel from a Cairo image surface in %CAIRO_FORMAT_RGB24.
+ *
+ * Since: 2.8
+ **/
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+#define GIMP_CAIRO_RGB24_GET_PIXEL(s, r, g, b) \
+ G_STMT_START { (b) = s[0]; (g) = s[1]; (r) = s[2]; } G_STMT_END
+#else
+#define GIMP_CAIRO_RGB24_GET_PIXEL(s, r, g, b) \
+ G_STMT_START { (r) = s[1]; (g) = s[2]; (b) = s[3]; } G_STMT_END
+#endif
+
+
+/**
+ * GIMP_CAIRO_ARGB32_SET_PIXEL:
+ * @d: pointer to the destination buffer
+ * @r: red component, not pre-multiplied
+ * @g: green component, not pre-multiplied
+ * @b: blue component, not pre-multiplied
+ * @a: alpha component
+ *
+ * Sets a single pixel in an Cairo image surface in %CAIRO_FORMAT_ARGB32.
+ *
+ * Since: 2.6
+ **/
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+#define GIMP_CAIRO_ARGB32_SET_PIXEL(d, r, g, b, a) \
+ G_STMT_START { \
+ const guint tr = (a) * (r) + 0x80; \
+ const guint tg = (a) * (g) + 0x80; \
+ const guint tb = (a) * (b) + 0x80; \
+ (d)[0] = (((tb) >> 8) + (tb)) >> 8; \
+ (d)[1] = (((tg) >> 8) + (tg)) >> 8; \
+ (d)[2] = (((tr) >> 8) + (tr)) >> 8; \
+ (d)[3] = (a); \
+ } G_STMT_END
+#else
+#define GIMP_CAIRO_ARGB32_SET_PIXEL(d, r, g, b, a) \
+ G_STMT_START { \
+ const guint tr = (a) * (r) + 0x80; \
+ const guint tg = (a) * (g) + 0x80; \
+ const guint tb = (a) * (b) + 0x80; \
+ (d)[0] = (a); \
+ (d)[1] = (((tr) >> 8) + (tr)) >> 8; \
+ (d)[2] = (((tg) >> 8) + (tg)) >> 8; \
+ (d)[3] = (((tb) >> 8) + (tb)) >> 8; \
+ } G_STMT_END
+#endif
+
+
+/**
+ * GIMP_CAIRO_ARGB32_GET_PIXEL:
+ * @s: pointer to the source buffer
+ * @r: red component, not pre-multiplied
+ * @g: green component, not pre-multiplied
+ * @b: blue component, not pre-multiplied
+ * @a: alpha component
+ *
+ * Gets a single pixel from a Cairo image surface in %CAIRO_FORMAT_ARGB32.
+ *
+ * Since: 2.8
+ **/
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+#define GIMP_CAIRO_ARGB32_GET_PIXEL(s, r, g, b, a) \
+ G_STMT_START { \
+ const guint tb = (s)[0]; \
+ const guint tg = (s)[1]; \
+ const guint tr = (s)[2]; \
+ const guint ta = (s)[3]; \
+ (r) = (tr << 8) / (ta + 1); \
+ (g) = (tg << 8) / (ta + 1); \
+ (b) = (tb << 8) / (ta + 1); \
+ (a) = ta; \
+ } G_STMT_END
+#else
+#define GIMP_CAIRO_ARGB32_GET_PIXEL(s, r, g, b, a) \
+ G_STMT_START { \
+ const guint ta = (s)[0]; \
+ const guint tr = (s)[1]; \
+ const guint tg = (s)[2]; \
+ const guint tb = (s)[3]; \
+ (r) = (tr << 8) / (ta + 1); \
+ (g) = (tg << 8) / (ta + 1); \
+ (b) = (tb << 8) / (ta + 1); \
+ (a) = ta; \
+ } G_STMT_END
+#endif
+
+
+#endif /* __GIMP_CAIRO_H__ */
diff --git a/libgimpcolor/gimpcmyk.c b/libgimpcolor/gimpcmyk.c
new file mode 100644
index 0000000..28b08eb
--- /dev/null
+++ b/libgimpcolor/gimpcmyk.c
@@ -0,0 +1,233 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+
+#include "libgimpmath/gimpmath.h"
+
+#include "gimpcolortypes.h"
+
+#include "gimpcmyk.h"
+
+
+/**
+ * SECTION: gimpcmyk
+ * @title: GimpCMYK
+ * @short_description: Definitions and Functions relating to CMYK colors.
+ *
+ * Definitions and Functions relating to CMYK colors.
+ **/
+
+
+/*
+ * GIMP_TYPE_CMYK
+ */
+
+static GimpCMYK * gimp_cmyk_copy (const GimpCMYK *cmyk);
+
+
+GType
+gimp_cmyk_get_type (void)
+{
+ static GType cmyk_type = 0;
+
+ if (!cmyk_type)
+ cmyk_type = g_boxed_type_register_static ("GimpCMYK",
+ (GBoxedCopyFunc) gimp_cmyk_copy,
+ (GBoxedFreeFunc) g_free);
+
+ return cmyk_type;
+}
+
+static GimpCMYK *
+gimp_cmyk_copy (const GimpCMYK *cmyk)
+{
+ return g_memdup (cmyk, sizeof (GimpCMYK));
+}
+
+
+/* CMYK functions */
+
+/**
+ * gimp_cmyk_set:
+ * @cmyk: A #GimpCMYK structure which will hold the specified CMYK value.
+ * @cyan: The Cyan channel of the CMYK value
+ * @magenta: The Magenta channel
+ * @yellow: The Yellow channel
+ * @black: The blacK channel
+ *
+ * Very basic initialiser for the internal #GimpCMYK structure. Channel
+ * values are doubles in the range 0 to 1.
+ **/
+void
+gimp_cmyk_set (GimpCMYK *cmyk,
+ gdouble cyan,
+ gdouble magenta,
+ gdouble yellow,
+ gdouble black)
+{
+ g_return_if_fail (cmyk != NULL);
+
+ cmyk->c = cyan;
+ cmyk->m = magenta;
+ cmyk->y = yellow;
+ cmyk->k = black;
+}
+
+/**
+ * gimp_cmyk_set_uchar:
+ * @cmyk: A #GimpCMYK structure which will hold the specified CMYK value.
+ * @cyan: The Cyan channel of the CMYK value
+ * @magenta: The Magenta channel
+ * @yellow: The Yellow channel
+ * @black: The blacK channel
+ *
+ * The same as gimp_cmyk_set(), except that channel values are
+ * unsigned chars in the range 0 to 255.
+ **/
+void
+gimp_cmyk_set_uchar (GimpCMYK *cmyk,
+ guchar cyan,
+ guchar magenta,
+ guchar yellow,
+ guchar black)
+{
+ g_return_if_fail (cmyk != NULL);
+
+ cmyk->c = (gdouble) cyan / 255.0;
+ cmyk->m = (gdouble) magenta / 255.0;
+ cmyk->y = (gdouble) yellow / 255.0;
+ cmyk->k = (gdouble) black / 255.0;
+}
+
+/**
+ * gimp_cmyk_get_uchar:
+ * @cmyk: A #GimpCMYK structure which will hold the specified CMYK value.
+ * @cyan: The Cyan channel of the CMYK value
+ * @magenta: The Magenta channel
+ * @yellow: The Yellow channel
+ * @black: The blacK channel
+ *
+ * Retrieve individual channel values from a #GimpCMYK structure. Channel
+ * values are pointers to unsigned chars in the range 0 to 255.
+ **/
+void
+gimp_cmyk_get_uchar (const GimpCMYK *cmyk,
+ guchar *cyan,
+ guchar *magenta,
+ guchar *yellow,
+ guchar *black)
+{
+ g_return_if_fail (cmyk != NULL);
+
+ if (cyan) *cyan = ROUND (CLAMP (cmyk->c, 0.0, 1.0) * 255.0);
+ if (magenta) *magenta = ROUND (CLAMP (cmyk->m, 0.0, 1.0) * 255.0);
+ if (yellow) *yellow = ROUND (CLAMP (cmyk->y, 0.0, 1.0) * 255.0);
+ if (black) *black = ROUND (CLAMP (cmyk->k, 0.0, 1.0) * 255.0);
+}
+
+
+/* CMYKA functions */
+
+/**
+ * gimp_cmyka_set:
+ * @cmyka: A #GimpCMYK structure which will hold the specified CMYKA value.
+ * @cyan: The Cyan channel of the CMYK value
+ * @magenta: The Magenta channel
+ * @yellow: The Yellow channel
+ * @black: The blacK channel
+ * @alpha: The Alpha channel
+ *
+ * Initialiser for the internal #GimpCMYK structure. Channel values are
+ * doubles in the range 0 to 1.
+ **/
+void
+gimp_cmyka_set (GimpCMYK *cmyka,
+ gdouble cyan,
+ gdouble magenta,
+ gdouble yellow,
+ gdouble black,
+ gdouble alpha)
+{
+ g_return_if_fail (cmyka != NULL);
+
+ cmyka->c = cyan;
+ cmyka->m = magenta;
+ cmyka->y = yellow;
+ cmyka->k = black;
+ cmyka->a = alpha;
+}
+
+/**
+ * gimp_cmyka_set_uchar:
+ * @cmyka: A #GimpCMYK structure which will hold the specified CMYKA value.
+ * @cyan: The Cyan channel of the CMYK value
+ * @magenta: The Magenta channel
+ * @yellow: The Yellow channel
+ * @black: The blacK channel
+ * @alpha: The Alpha channel
+ *
+ * The same as gimp_cmyka_set(), except that channel values are
+ * unsigned chars in the range 0 to 255.
+ **/
+void
+gimp_cmyka_set_uchar (GimpCMYK *cmyka,
+ guchar cyan,
+ guchar magenta,
+ guchar yellow,
+ guchar black,
+ guchar alpha)
+{
+ g_return_if_fail (cmyka != NULL);
+
+ cmyka->c = (gdouble) cyan / 255.0;
+ cmyka->m = (gdouble) magenta / 255.0;
+ cmyka->y = (gdouble) yellow / 255.0;
+ cmyka->k = (gdouble) black / 255.0;
+ cmyka->a = (gdouble) alpha / 255.0;
+}
+/**
+ * gimp_cmyka_get_uchar:
+ * @cmyka: A #GimpCMYK structure which will hold the specified CMYKA value.
+ * @cyan: The Cyan channel of the CMYK value
+ * @magenta: The Magenta channel
+ * @yellow: The Yellow channel
+ * @black: The blacK channel
+ * @alpha: The Alpha channel
+ *
+ * Retrieve individual channel values from a #GimpCMYK structure.
+ * Channel values are pointers to unsigned chars in the range 0 to 255.
+ **/
+void
+gimp_cmyka_get_uchar (const GimpCMYK *cmyka,
+ guchar *cyan,
+ guchar *magenta,
+ guchar *yellow,
+ guchar *black,
+ guchar *alpha)
+{
+ g_return_if_fail (cmyka != NULL);
+
+ if (cyan) *cyan = ROUND (CLAMP (cmyka->c, 0.0, 1.0) * 255.0);
+ if (magenta) *magenta = ROUND (CLAMP (cmyka->m, 0.0, 1.0) * 255.0);
+ if (yellow) *yellow = ROUND (CLAMP (cmyka->y, 0.0, 1.0) * 255.0);
+ if (black) *black = ROUND (CLAMP (cmyka->k, 0.0, 1.0) * 255.0);
+ if (alpha) *alpha = ROUND (CLAMP (cmyka->a, 0.0, 1.0) * 255.0);
+}
diff --git a/libgimpcolor/gimpcmyk.h b/libgimpcolor/gimpcmyk.h
new file mode 100644
index 0000000..cafb252
--- /dev/null
+++ b/libgimpcolor/gimpcmyk.h
@@ -0,0 +1,78 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_COLOR_H_INSIDE__) && !defined (GIMP_COLOR_COMPILATION)
+#error "Only <libgimpcolor/gimpcolor.h> can be included directly."
+#endif
+
+#ifndef __GIMP_CMYK_H__
+#define __GIMP_CMYK_H__
+
+G_BEGIN_DECLS
+
+
+/* For information look into the C source or the html documentation */
+
+
+/*
+ * GIMP_TYPE_CMYK
+ */
+
+#define GIMP_TYPE_CMYK (gimp_cmyk_get_type ())
+
+GType gimp_cmyk_get_type (void) G_GNUC_CONST;
+
+void gimp_cmyk_set (GimpCMYK *cmyk,
+ gdouble cyan,
+ gdouble magenta,
+ gdouble yellow,
+ gdouble black);
+void gimp_cmyk_set_uchar (GimpCMYK *cmyk,
+ guchar cyan,
+ guchar magenta,
+ guchar yellow,
+ guchar black);
+void gimp_cmyk_get_uchar (const GimpCMYK *cmyk,
+ guchar *cyan,
+ guchar *magenta,
+ guchar *yellow,
+ guchar *black);
+
+void gimp_cmyka_set (GimpCMYK *cmyka,
+ gdouble cyan,
+ gdouble magenta,
+ gdouble yellow,
+ gdouble black,
+ gdouble alpha);
+void gimp_cmyka_set_uchar (GimpCMYK *cmyka,
+ guchar cyan,
+ guchar magenta,
+ guchar yellow,
+ guchar black,
+ guchar alpha);
+void gimp_cmyka_get_uchar (const GimpCMYK *cmyka,
+ guchar *cyan,
+ guchar *magenta,
+ guchar *yellow,
+ guchar *black,
+ guchar *alpha);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_CMYK_H__ */
diff --git a/libgimpcolor/gimpcolor.def b/libgimpcolor/gimpcolor.def
new file mode 100644
index 0000000..3feaf98
--- /dev/null
+++ b/libgimpcolor/gimpcolor.def
@@ -0,0 +1,128 @@
+EXPORTS
+ gimp_adaptive_supersample_area
+ gimp_bilinear
+ gimp_bilinear_16
+ gimp_bilinear_32
+ gimp_bilinear_8
+ gimp_bilinear_pixels_8
+ gimp_bilinear_rgb
+ gimp_bilinear_rgba
+ gimp_cairo_checkerboard_create
+ gimp_cairo_set_source_rgb
+ gimp_cairo_set_source_rgba
+ gimp_cairo_surface_create_buffer
+ gimp_cairo_surface_get_format
+ gimp_cmyk_get_type
+ gimp_cmyk_get_uchar
+ gimp_cmyk_set
+ gimp_cmyk_set_uchar
+ gimp_cmyk_to_rgb
+ gimp_cmyk_to_rgb_int
+ gimp_cmyka_get_uchar
+ gimp_cmyka_set
+ gimp_cmyka_set_uchar
+ gimp_color_managed_get_color_profile
+ gimp_color_managed_get_icc_profile
+ gimp_color_managed_get_type
+ gimp_color_managed_interface_get_type
+ gimp_color_managed_profile_changed
+ gimp_color_profile_get_copyright
+ gimp_color_profile_get_description
+ gimp_color_profile_get_format
+ gimp_color_profile_get_icc_profile
+ gimp_color_profile_get_label
+ gimp_color_profile_get_lcms_format
+ gimp_color_profile_get_lcms_profile
+ gimp_color_profile_get_manufacturer
+ gimp_color_profile_get_model
+ gimp_color_profile_get_space
+ gimp_color_profile_get_summary
+ gimp_color_profile_get_type
+ gimp_color_profile_is_cmyk
+ gimp_color_profile_is_equal
+ gimp_color_profile_is_gray
+ gimp_color_profile_is_linear
+ gimp_color_profile_is_rgb
+ gimp_color_profile_new_d50_gray_lab_trc
+ gimp_color_profile_new_d65_gray_linear
+ gimp_color_profile_new_d65_gray_srgb_trc
+ gimp_color_profile_new_from_file
+ gimp_color_profile_new_from_icc_profile
+ gimp_color_profile_new_from_lcms_profile
+ gimp_color_profile_new_linear_from_color_profile
+ gimp_color_profile_new_rgb_adobe
+ gimp_color_profile_new_rgb_srgb
+ gimp_color_profile_new_rgb_srgb_linear
+ gimp_color_profile_new_srgb_trc_from_color_profile
+ gimp_color_profile_save_to_file
+ gimp_color_transform_can_gegl_copy
+ gimp_color_transform_get_type
+ gimp_color_transform_new
+ gimp_color_transform_new_proofing
+ gimp_color_transform_process_buffer
+ gimp_color_transform_process_pixels
+ gimp_hsl_get_type
+ gimp_hsl_set
+ gimp_hsl_set_alpha
+ gimp_hsl_to_rgb
+ gimp_hsl_to_rgb_int
+ gimp_hsv_clamp
+ gimp_hsv_get_type
+ gimp_hsv_set
+ gimp_hsv_to_rgb
+ gimp_hsv_to_rgb4
+ gimp_hsv_to_rgb_int
+ gimp_hsva_set
+ gimp_hwb_to_rgb
+ gimp_param_rgb_get_type
+ gimp_param_spec_rgb
+ gimp_param_spec_rgb_get_default
+ gimp_param_spec_rgb_has_alpha
+ gimp_pixbuf_create_buffer
+ gimp_pixbuf_get_format
+ gimp_pixbuf_get_icc_profile
+ gimp_rgb_add
+ gimp_rgb_clamp
+ gimp_rgb_composite
+ gimp_rgb_distance
+ gimp_rgb_gamma
+ gimp_rgb_get_pixel
+ gimp_rgb_get_type
+ gimp_rgb_get_uchar
+ gimp_rgb_intensity
+ gimp_rgb_intensity_uchar
+ gimp_rgb_list_names
+ gimp_rgb_luminance
+ gimp_rgb_luminance_uchar
+ gimp_rgb_max
+ gimp_rgb_min
+ gimp_rgb_multiply
+ gimp_rgb_parse_css
+ gimp_rgb_parse_hex
+ gimp_rgb_parse_name
+ gimp_rgb_set
+ gimp_rgb_set_alpha
+ gimp_rgb_set_pixel
+ gimp_rgb_set_uchar
+ gimp_rgb_subtract
+ gimp_rgb_to_cmyk
+ gimp_rgb_to_cmyk_int
+ gimp_rgb_to_hsl
+ gimp_rgb_to_hsl_int
+ gimp_rgb_to_hsv
+ gimp_rgb_to_hsv4
+ gimp_rgb_to_hsv_int
+ gimp_rgb_to_hwb
+ gimp_rgb_to_l_int
+ gimp_rgba_add
+ gimp_rgba_distance
+ gimp_rgba_get_pixel
+ gimp_rgba_get_uchar
+ gimp_rgba_multiply
+ gimp_rgba_parse_css
+ gimp_rgba_set
+ gimp_rgba_set_pixel
+ gimp_rgba_set_uchar
+ gimp_rgba_subtract
+ gimp_value_get_rgb
+ gimp_value_set_rgb
diff --git a/libgimpcolor/gimpcolor.h b/libgimpcolor/gimpcolor.h
new file mode 100644
index 0000000..7e960a1
--- /dev/null
+++ b/libgimpcolor/gimpcolor.h
@@ -0,0 +1,41 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_COLOR_H__
+#define __GIMP_COLOR_H__
+
+#define __GIMP_COLOR_H_INSIDE__
+
+#include <libgimpcolor/gimpcolortypes.h>
+
+#include <libgimpcolor/gimpadaptivesupersample.h>
+#include <libgimpcolor/gimpbilinear.h>
+#include <libgimpcolor/gimpcairo.h>
+#include <libgimpcolor/gimpcolormanaged.h>
+#include <libgimpcolor/gimpcolorprofile.h>
+#include <libgimpcolor/gimpcolorspace.h>
+#include <libgimpcolor/gimpcolortransform.h>
+#include <libgimpcolor/gimpcmyk.h>
+#include <libgimpcolor/gimphsl.h>
+#include <libgimpcolor/gimphsv.h>
+#include <libgimpcolor/gimppixbuf.h>
+#include <libgimpcolor/gimprgb.h>
+
+#undef __GIMP_COLOR_H_INSIDE__
+
+#endif /* __GIMP_COLOR_H__ */
diff --git a/libgimpcolor/gimpcolormanaged.c b/libgimpcolor/gimpcolormanaged.c
new file mode 100644
index 0000000..60c6eb1
--- /dev/null
+++ b/libgimpcolor/gimpcolormanaged.c
@@ -0,0 +1,151 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * GimpColorManaged interface
+ * Copyright (C) 2007 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+#include <gegl.h>
+
+#include "gimpcolortypes.h"
+
+#include "gimpcolormanaged.h"
+#include "gimpcolorprofile.h"
+
+
+/**
+ * SECTION: gimpcolormanaged
+ * @title: GimpColorManaged
+ * @short_description: An interface dealing with color profiles.
+ *
+ * An interface dealing with color profiles.
+ **/
+
+
+enum
+{
+ PROFILE_CHANGED,
+ LAST_SIGNAL
+};
+
+
+G_DEFINE_INTERFACE (GimpColorManaged, gimp_color_managed, G_TYPE_OBJECT)
+
+
+static guint gimp_color_managed_signals[LAST_SIGNAL] = { 0 };
+
+
+/* private functions */
+
+
+GType
+gimp_color_managed_interface_get_type (void)
+{
+ return gimp_color_managed_get_type ();
+}
+
+static void
+gimp_color_managed_default_init (GimpColorManagedInterface *iface)
+{
+ gimp_color_managed_signals[PROFILE_CHANGED] =
+ g_signal_new ("profile-changed",
+ G_TYPE_FROM_INTERFACE (iface),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpColorManagedInterface,
+ profile_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+
+/* public functions */
+
+
+/**
+ * gimp_color_managed_get_icc_profile:
+ * @managed: an object the implements the #GimpColorManaged interface
+ * @len: return location for the number of bytes in the profile data
+ *
+ * Return value: A pointer to a blob of data that represents an ICC
+ * color profile.
+ *
+ * Since: 2.4
+ **/
+const guint8 *
+gimp_color_managed_get_icc_profile (GimpColorManaged *managed,
+ gsize *len)
+{
+ GimpColorManagedInterface *iface;
+
+ g_return_val_if_fail (GIMP_IS_COLOR_MANAGED (managed), NULL);
+ g_return_val_if_fail (len != NULL, NULL);
+
+ *len = 0;
+
+ iface = GIMP_COLOR_MANAGED_GET_INTERFACE (managed);
+
+ if (iface->get_icc_profile)
+ return iface->get_icc_profile (managed, len);
+
+ return NULL;
+}
+
+/**
+ * gimp_color_managed_get_color_profile:
+ * @managed: an object the implements the #GimpColorManaged interface
+ *
+ * This function always returns a #GimpColorProfile and falls back to
+ * gimp_color_profile_new_rgb_srgb() if the method is not implemented.
+ *
+ * Return value: The @managed's #GimpColorProfile.
+ *
+ * Since: 2.10
+ **/
+GimpColorProfile *
+gimp_color_managed_get_color_profile (GimpColorManaged *managed)
+{
+ GimpColorManagedInterface *iface;
+
+ g_return_val_if_fail (GIMP_IS_COLOR_MANAGED (managed), NULL);
+
+ iface = GIMP_COLOR_MANAGED_GET_INTERFACE (managed);
+
+ if (iface->get_color_profile)
+ return iface->get_color_profile (managed);
+
+ return NULL;
+}
+
+/**
+ * gimp_color_managed_profile_changed:
+ * @managed: an object the implements the #GimpColorManaged interface
+ *
+ * Emits the "profile-changed" signal.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_color_managed_profile_changed (GimpColorManaged *managed)
+{
+ g_return_if_fail (GIMP_IS_COLOR_MANAGED (managed));
+
+ g_signal_emit (managed, gimp_color_managed_signals[PROFILE_CHANGED], 0);
+}
diff --git a/libgimpcolor/gimpcolormanaged.h b/libgimpcolor/gimpcolormanaged.h
new file mode 100644
index 0000000..d24e0c8
--- /dev/null
+++ b/libgimpcolor/gimpcolormanaged.h
@@ -0,0 +1,82 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * GimpColorManaged interface
+ * Copyright (C) 2007 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_COLOR_H_INSIDE__) && !defined (GIMP_COLOR_COMPILATION)
+#error "Only <libgimpcolor/gimpcolor.h> can be included directly."
+#endif
+
+#ifndef __GIMP_COLOR_MANAGED_H__
+#define __GIMP_COLOR_MANAGED_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+#define GIMP_TYPE_COLOR_MANAGED (gimp_color_managed_get_type ())
+#define GIMP_IS_COLOR_MANAGED(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_COLOR_MANAGED))
+#define GIMP_COLOR_MANAGED(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_COLOR_MANAGED, GimpColorManaged))
+#define GIMP_COLOR_MANAGED_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GIMP_TYPE_COLOR_MANAGED, GimpColorManagedInterface))
+
+
+typedef struct _GimpColorManagedInterface GimpColorManagedInterface;
+
+/**
+ * GimpColorManagedInterface:
+ * @base_iface: The parent interface
+ * @get_icc_profile: Returns the ICC profile of the pixels managed by
+ * the object
+ * @profile_changed: This signal is emitted when the object's color profile
+ * has changed
+ * @get_color_profile: Returns the #GimpColorProfile of the pixels managed
+ * by the object
+ **/
+struct _GimpColorManagedInterface
+{
+ GTypeInterface base_iface;
+
+ /* virtual functions */
+ const guint8 * (* get_icc_profile) (GimpColorManaged *managed,
+ gsize *len);
+
+ /* signals */
+ void (* profile_changed) (GimpColorManaged *managed);
+
+ /* virtual functions */
+ GimpColorProfile * (* get_color_profile) (GimpColorManaged *managed);
+};
+
+
+GType gimp_color_managed_get_type (void) G_GNUC_CONST;
+
+GIMP_DEPRECATED_FOR (gimp_color_managed_get_type)
+GType gimp_color_managed_interface_get_type (void) G_GNUC_CONST;
+
+const guint8 * gimp_color_managed_get_icc_profile (GimpColorManaged *managed,
+ gsize *len);
+GimpColorProfile * gimp_color_managed_get_color_profile (GimpColorManaged *managed);
+
+void gimp_color_managed_profile_changed (GimpColorManaged *managed);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_COLOR_MANAGED_IFACE_H__ */
diff --git a/libgimpcolor/gimpcolorprofile.c b/libgimpcolor/gimpcolorprofile.c
new file mode 100644
index 0000000..69236b4
--- /dev/null
+++ b/libgimpcolor/gimpcolorprofile.c
@@ -0,0 +1,1789 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimpcolorprofile.c
+ * Copyright (C) 2014 Michael Natterer <mitch@gimp.org>
+ * Elle Stone <ellestone@ninedegreesbelow.com>
+ * Øyvind Kolås <pippin@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <lcms2.h>
+
+#include <gio/gio.h>
+#include <gegl.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "gimpcolortypes.h"
+
+#include "gimpcolorprofile.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+#ifndef TYPE_RGBA_DBL
+#define TYPE_RGBA_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(0))
+#endif
+
+#ifndef TYPE_GRAYA_HALF_FLT
+#define TYPE_GRAYA_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2))
+#endif
+
+#ifndef TYPE_GRAYA_FLT
+#define TYPE_GRAYA_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(4))
+#endif
+
+#ifndef TYPE_GRAYA_DBL
+#define TYPE_GRAYA_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(0))
+#endif
+
+#ifndef TYPE_CMYKA_DBL
+#define TYPE_CMYKA_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_CMYK)|EXTRA_SH(1)|CHANNELS_SH(4)|BYTES_SH(0))
+#endif
+
+#ifndef TYPE_CMYKA_HALF_FLT
+#define TYPE_CMYKA_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_CMYK)|EXTRA_SH(1)|CHANNELS_SH(4)|BYTES_SH(2))
+#endif
+
+#ifndef TYPE_CMYKA_FLT
+#define TYPE_CMYKA_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_CMYK)|EXTRA_SH(1)|CHANNELS_SH(4)|BYTES_SH(4))
+#endif
+
+#ifndef TYPE_CMYKA_16
+#define TYPE_CMYKA_16 (COLORSPACE_SH(PT_CMYK)|EXTRA_SH(1)|CHANNELS_SH(4)|BYTES_SH(2))
+#endif
+
+
+/**
+ * SECTION: gimpcolorprofile
+ * @title: GimpColorProfile
+ * @short_description: Definitions and Functions relating to LCMS.
+ *
+ * Definitions and Functions relating to LCMS.
+ **/
+
+/**
+ * GimpColorProfile:
+ *
+ * Simply a typedef to #gpointer, but actually is a cmsHPROFILE. It's
+ * used in public GIMP APIs in order to avoid having to include LCMS
+ * headers.
+ **/
+
+
+struct _GimpColorProfilePrivate
+{
+ cmsHPROFILE lcms_profile;
+ guint8 *data;
+ gsize length;
+
+ gchar *description;
+ gchar *manufacturer;
+ gchar *model;
+ gchar *copyright;
+ gchar *label;
+ gchar *summary;
+};
+
+
+static void gimp_color_profile_finalize (GObject *object);
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpColorProfile, gimp_color_profile, G_TYPE_OBJECT)
+
+#define parent_class gimp_color_profile_parent_class
+
+
+#define GIMP_COLOR_PROFILE_ERROR gimp_color_profile_error_quark ()
+
+static GQuark
+gimp_color_profile_error_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (G_UNLIKELY (quark == 0))
+ quark = g_quark_from_static_string ("gimp-color-profile-error-quark");
+
+ return quark;
+}
+
+static void
+gimp_color_profile_class_init (GimpColorProfileClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gimp_color_profile_finalize;
+}
+
+static void
+gimp_color_profile_init (GimpColorProfile *profile)
+{
+ profile->priv = gimp_color_profile_get_instance_private (profile);
+}
+
+static void
+gimp_color_profile_finalize (GObject *object)
+{
+ GimpColorProfile *profile = GIMP_COLOR_PROFILE (object);
+
+ g_clear_pointer (&profile->priv->lcms_profile, cmsCloseProfile);
+
+ g_clear_pointer (&profile->priv->data, g_free);
+ profile->priv->length = 0;
+
+ g_clear_pointer (&profile->priv->description, g_free);
+ g_clear_pointer (&profile->priv->manufacturer, g_free);
+ g_clear_pointer (&profile->priv->model, g_free);
+ g_clear_pointer (&profile->priv->copyright, g_free);
+ g_clear_pointer (&profile->priv->label, g_free);
+ g_clear_pointer (&profile->priv->summary, g_free);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+/**
+ * gimp_color_profile_new_from_file:
+ * @file: a #GFile
+ * @error: return location for #GError
+ *
+ * This function opens an ICC color profile from @file.
+ *
+ * Return value: the #GimpColorProfile, or %NULL. On error, %NULL is
+ * returned and @error is set.
+ *
+ * Since: 2.10
+ **/
+GimpColorProfile *
+gimp_color_profile_new_from_file (GFile *file,
+ GError **error)
+{
+ GimpColorProfile *profile = NULL;
+ cmsHPROFILE lcms_profile = NULL;
+ guint8 *data = NULL;
+ gsize length = 0;
+ gchar *path;
+
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ path = g_file_get_path (file);
+
+ if (path)
+ {
+ GMappedFile *mapped;
+
+ mapped = g_mapped_file_new (path, FALSE, error);
+ g_free (path);
+
+ if (! mapped)
+ return NULL;
+
+ length = g_mapped_file_get_length (mapped);
+ data = g_memdup (g_mapped_file_get_contents (mapped), length);
+
+ lcms_profile = cmsOpenProfileFromMem (data, length);
+
+ g_mapped_file_unref (mapped);
+ }
+ else
+ {
+ GFileInfo *info;
+
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_SIZE,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, error);
+ if (info)
+ {
+ GInputStream *input;
+
+ length = g_file_info_get_size (info);
+ data = g_malloc (length);
+
+ g_object_unref (info);
+
+ input = G_INPUT_STREAM (g_file_read (file, NULL, error));
+
+ if (input)
+ {
+ gsize bytes_read;
+
+ if (g_input_stream_read_all (input, data, length,
+ &bytes_read, NULL, error) &&
+ bytes_read == length)
+ {
+ lcms_profile = cmsOpenProfileFromMem (data, length);
+ }
+
+ g_object_unref (input);
+ }
+ }
+ }
+
+ if (lcms_profile)
+ {
+ profile = g_object_new (GIMP_TYPE_COLOR_PROFILE, NULL);
+
+ profile->priv->lcms_profile = lcms_profile;
+ profile->priv->data = data;
+ profile->priv->length = length;
+ }
+ else
+ {
+ if (data)
+ g_free (data);
+
+ if (error && *error == NULL)
+ {
+ g_set_error (error, GIMP_COLOR_PROFILE_ERROR, 0,
+ _("'%s' does not appear to be an ICC color profile"),
+ gimp_file_get_utf8_name (file));
+ }
+ }
+
+ return profile;
+}
+
+/**
+ * gimp_color_profile_new_from_icc_profile:
+ * @data: pointer to memory containing an ICC profile
+ * @length: length of the profile in memory, in bytes
+ * @error: return location for #GError
+ *
+ * This function opens an ICC color profile from memory. On error,
+ * %NULL is returned and @error is set.
+ *
+ * Return value: the #GimpColorProfile, or %NULL.
+ *
+ * Since: 2.10
+ **/
+GimpColorProfile *
+gimp_color_profile_new_from_icc_profile (const guint8 *data,
+ gsize length,
+ GError **error)
+{
+ cmsHPROFILE lcms_profile = 0;
+ GimpColorProfile *profile = NULL;
+
+ g_return_val_if_fail (data != NULL || length == 0, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ if (length > 0)
+ lcms_profile = cmsOpenProfileFromMem (data, length);
+
+ if (lcms_profile)
+ {
+ profile = g_object_new (GIMP_TYPE_COLOR_PROFILE, NULL);
+
+ profile->priv->lcms_profile = lcms_profile;
+ profile->priv->data = g_memdup (data, length);
+ profile->priv->length = length;
+ }
+ else
+ {
+ g_set_error_literal (error, GIMP_COLOR_PROFILE_ERROR, 0,
+ _("Data does not appear to be an ICC color profile"));
+ }
+
+ return profile;
+}
+
+/**
+ * gimp_color_profile_new_from_lcms_profile:
+ * @lcms_profile: an LCMS cmsHPROFILE pointer
+ * @error: return location for #GError
+ *
+ * This function creates a GimpColorProfile from a cmsHPROFILE. On
+ * error, %NULL is returned and @error is set. The passed
+ * @lcms_profile pointer is not retained by the created
+ * #GimpColorProfile.
+ *
+ * Return value: the #GimpColorProfile, or %NULL.
+ *
+ * Since: 2.10
+ **/
+GimpColorProfile *
+gimp_color_profile_new_from_lcms_profile (gpointer lcms_profile,
+ GError **error)
+{
+ cmsUInt32Number size;
+
+ g_return_val_if_fail (lcms_profile != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ if (cmsSaveProfileToMem (lcms_profile, NULL, &size))
+ {
+ guint8 *data = g_malloc (size);
+
+ if (cmsSaveProfileToMem (lcms_profile, data, &size))
+ {
+ gsize length = size;
+
+ lcms_profile = cmsOpenProfileFromMem (data, length);
+
+ if (lcms_profile)
+ {
+ GimpColorProfile *profile;
+
+ profile = g_object_new (GIMP_TYPE_COLOR_PROFILE, NULL);
+
+ profile->priv->lcms_profile = lcms_profile;
+ profile->priv->data = data;
+ profile->priv->length = length;
+
+ return profile;
+ }
+ }
+
+ g_free (data);
+ }
+
+ g_set_error_literal (error, GIMP_COLOR_PROFILE_ERROR, 0,
+ _("Could not save color profile to memory"));
+
+ return NULL;
+}
+
+/**
+ * gimp_color_profile_save_to_file:
+ * @profile: a #GimpColorProfile
+ * @file: a #GFile
+ * @error: return location for #GError
+ *
+ * This function saves @profile to @file as ICC profile.
+ *
+ * Return value: %TRUE on success, %FALSE if an error occurred.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_color_profile_save_to_file (GimpColorProfile *profile,
+ GFile *file,
+ GError **error)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), FALSE);
+ g_return_val_if_fail (G_IS_FILE (file), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ return g_file_replace_contents (file,
+ (const gchar *) profile->priv->data,
+ profile->priv->length,
+ NULL, FALSE,
+ G_FILE_CREATE_NONE,
+ NULL,
+ NULL,
+ error);
+}
+
+/**
+ * gimp_color_profile_get_icc_profile:
+ * @profile: a #GimpColorProfile
+ * @length: return location for the number of bytes
+ *
+ * This function returns @profile as ICC profile data. The returned
+ * memory belongs to @profile and must not be modified or freed.
+ *
+ * Return value: a pointer to the IIC profile data.
+ *
+ * Since: 2.10
+ **/
+const guint8 *
+gimp_color_profile_get_icc_profile (GimpColorProfile *profile,
+ gsize *length)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), NULL);
+ g_return_val_if_fail (length != NULL, NULL);
+
+ *length = profile->priv->length;
+
+ return profile->priv->data;
+}
+
+/**
+ * gimp_color_profile_get_lcms_profile:
+ * @profile: a #GimpColorProfile
+ *
+ * This function returns @profile's cmsHPROFILE. The returned
+ * value belongs to @profile and must not be modified or freed.
+ *
+ * Return value: a pointer to the cmsHPROFILE.
+ *
+ * Since: 2.10
+ **/
+gpointer
+gimp_color_profile_get_lcms_profile (GimpColorProfile *profile)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), NULL);
+
+ return profile->priv->lcms_profile;
+}
+
+static gchar *
+gimp_color_profile_get_info (GimpColorProfile *profile,
+ cmsInfoType info)
+{
+ cmsUInt32Number size;
+ gchar *text = NULL;
+
+ size = cmsGetProfileInfoASCII (profile->priv->lcms_profile, info,
+ "en", "US", NULL, 0);
+ if (size > 0)
+ {
+ gchar *data = g_new (gchar, size + 1);
+
+ size = cmsGetProfileInfoASCII (profile->priv->lcms_profile, info,
+ "en", "US", data, size);
+ if (size > 0)
+ text = gimp_any_to_utf8 (data, -1, NULL);
+
+ g_free (data);
+ }
+
+ return text;
+}
+
+/**
+ * gimp_color_profile_get_description:
+ * @profile: a #GimpColorProfile
+ *
+ * Return value: a string containing @profile's description. The
+ * returned value belongs to @profile and must not be
+ * modified or freed.
+ *
+ * Since: 2.10
+ **/
+const gchar *
+gimp_color_profile_get_description (GimpColorProfile *profile)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), NULL);
+
+ if (! profile->priv->description)
+ profile->priv->description =
+ gimp_color_profile_get_info (profile, cmsInfoDescription);
+
+ return profile->priv->description;
+}
+
+/**
+ * gimp_color_profile_get_manufacturer:
+ * @profile: a #GimpColorProfile
+ *
+ * Return value: a string containing @profile's manufacturer. The
+ * returned value belongs to @profile and must not be
+ * modified or freed.
+ *
+ * Since: 2.10
+ **/
+const gchar *
+gimp_color_profile_get_manufacturer (GimpColorProfile *profile)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), NULL);
+
+ if (! profile->priv->manufacturer)
+ profile->priv->manufacturer =
+ gimp_color_profile_get_info (profile, cmsInfoManufacturer);
+
+ return profile->priv->manufacturer;
+}
+
+/**
+ * gimp_color_profile_get_model:
+ * @profile: a #GimpColorProfile
+ *
+ * Return value: a string containing @profile's model. The returned
+ * value belongs to @profile and must not be modified or
+ * freed.
+ *
+ * Since: 2.10
+ **/
+const gchar *
+gimp_color_profile_get_model (GimpColorProfile *profile)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), NULL);
+
+ if (! profile->priv->model)
+ profile->priv->model =
+ gimp_color_profile_get_info (profile, cmsInfoModel);
+
+ return profile->priv->model;
+}
+
+/**
+ * gimp_color_profile_get_copyright:
+ * @profile: a #GimpColorProfile
+ *
+ * Return value: a string containing @profile's copyright. The
+ * returned value belongs to @profile and must not be
+ * modified or freed.
+ *
+ * Since: 2.10
+ **/
+const gchar *
+gimp_color_profile_get_copyright (GimpColorProfile *profile)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), NULL);
+
+ if (! profile->priv->copyright)
+ profile->priv->copyright =
+ gimp_color_profile_get_info (profile, cmsInfoCopyright);
+
+ return profile->priv->copyright;
+}
+
+/**
+ * gimp_color_profile_get_label:
+ * @profile: a #GimpColorProfile
+ *
+ * This function returns a string containing @profile's "title", a
+ * string that can be used to label the profile in a user interface.
+ *
+ * Unlike gimp_color_profile_get_description(), this function always
+ * returns a string (as a fallback, it returns "(unnamed profile)").
+ *
+ * Return value: the @profile's label. The returned value belongs to
+ * @profile and must not be modified or freed.
+ *
+ * Since: 2.10
+ **/
+const gchar *
+gimp_color_profile_get_label (GimpColorProfile *profile)
+{
+
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), NULL);
+
+ if (! profile->priv->label)
+ {
+ const gchar *label = gimp_color_profile_get_description (profile);
+
+ if (! label || ! strlen (label))
+ label = _("(unnamed profile)");
+
+ profile->priv->label = g_strdup (label);
+ }
+
+ return profile->priv->label;
+}
+
+/**
+ * gimp_color_profile_get_summary:
+ * @profile: a #GimpColorProfile
+ *
+ * This function return a string containing a multi-line summary of
+ * @profile's description, model, manufacturer and copyright, to be
+ * used as detailed information about the profile in a user
+ * interface.
+ *
+ * Return value: the @profile's summary. The returned value belongs to
+ * @profile and must not be modified or freed.
+ *
+ * Since: 2.10
+ **/
+const gchar *
+gimp_color_profile_get_summary (GimpColorProfile *profile)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), NULL);
+
+ if (! profile->priv->summary)
+ {
+ GString *string = g_string_new (NULL);
+ const gchar *text;
+
+ text = gimp_color_profile_get_description (profile);
+ if (text)
+ g_string_append (string, text);
+
+ text = gimp_color_profile_get_model (profile);
+ if (text)
+ {
+ if (string->len > 0)
+ g_string_append (string, "\n");
+
+ g_string_append_printf (string, _("Model: %s"), text);
+ }
+
+ text = gimp_color_profile_get_manufacturer (profile);
+ if (text)
+ {
+ if (string->len > 0)
+ g_string_append (string, "\n");
+
+ g_string_append_printf (string, _("Manufacturer: %s"), text);
+ }
+
+ text = gimp_color_profile_get_copyright (profile);
+ if (text)
+ {
+ if (string->len > 0)
+ g_string_append (string, "\n");
+
+ g_string_append_printf (string, _("Copyright: %s"), text);
+ }
+
+ profile->priv->summary = g_string_free (string, FALSE);
+ }
+
+ return profile->priv->summary;
+}
+
+/**
+ * gimp_color_profile_is_equal:
+ * @profile1: a #GimpColorProfile
+ * @profile2: a #GimpColorProfile
+ *
+ * Compares two profiles.
+ *
+ * Return value: %TRUE if the profiles are equal, %FALSE otherwise.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_color_profile_is_equal (GimpColorProfile *profile1,
+ GimpColorProfile *profile2)
+{
+ const gsize header_len = sizeof (cmsICCHeader);
+
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile1), FALSE);
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile2), FALSE);
+
+ return profile1 == profile2 ||
+ (profile1->priv->length == profile2->priv->length &&
+ memcmp (profile1->priv->data + header_len,
+ profile2->priv->data + header_len,
+ profile1->priv->length - header_len) == 0);
+}
+
+/**
+ * gimp_color_profile_is_rgb:
+ * @profile: a #GimpColorProfile
+ *
+ * Return value: %TRUE if the profile's color space is RGB, %FALSE
+ * otherwise.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_color_profile_is_rgb (GimpColorProfile *profile)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), FALSE);
+
+ return (cmsGetColorSpace (profile->priv->lcms_profile) == cmsSigRgbData);
+}
+
+/**
+ * gimp_color_profile_is_gray:
+ * @profile: a #GimpColorProfile
+ *
+ * Return value: %TRUE if the profile's color space is grayscale, %FALSE
+ * otherwise.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_color_profile_is_gray (GimpColorProfile *profile)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), FALSE);
+
+ return (cmsGetColorSpace (profile->priv->lcms_profile) == cmsSigGrayData);
+}
+
+/**
+ * gimp_color_profile_is_cmyk:
+ * @profile: a #GimpColorProfile
+ *
+ * Return value: %TRUE if the profile's color space is CMYK, %FALSE
+ * otherwise.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_color_profile_is_cmyk (GimpColorProfile *profile)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), FALSE);
+
+ return (cmsGetColorSpace (profile->priv->lcms_profile) == cmsSigCmykData);
+}
+
+
+/**
+ * gimp_color_profile_is_linear:
+ * @profile: a #GimpColorProfile
+ *
+ * This function determines is the ICC profile represented by a GimpColorProfile
+ * is a linear RGB profile or not, some profiles that are LUTs though linear
+ * will also return FALSE;
+ *
+ * Return value: %TRUE if the profile is a matrix shaping profile with linear
+ * TRCs, %FALSE otherwise.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_color_profile_is_linear (GimpColorProfile *profile)
+{
+ cmsHPROFILE prof;
+ cmsToneCurve *curve;
+
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), FALSE);
+
+ prof = profile->priv->lcms_profile;
+
+ if (! cmsIsMatrixShaper (prof))
+ return FALSE;
+
+ if (cmsIsCLUT (prof, INTENT_PERCEPTUAL, LCMS_USED_AS_INPUT))
+ return FALSE;
+
+ if (cmsIsCLUT (prof, INTENT_PERCEPTUAL, LCMS_USED_AS_OUTPUT))
+ return FALSE;
+
+ if (gimp_color_profile_is_rgb (profile))
+ {
+ curve = cmsReadTag(prof, cmsSigRedTRCTag);
+ if (curve == NULL || ! cmsIsToneCurveLinear (curve))
+ return FALSE;
+
+ curve = cmsReadTag (prof, cmsSigGreenTRCTag);
+ if (curve == NULL || ! cmsIsToneCurveLinear (curve))
+ return FALSE;
+
+ curve = cmsReadTag (prof, cmsSigBlueTRCTag);
+ if (curve == NULL || ! cmsIsToneCurveLinear (curve))
+ return FALSE;
+ }
+ else if (gimp_color_profile_is_gray (profile))
+ {
+ curve = cmsReadTag(prof, cmsSigGrayTRCTag);
+ if (curve == NULL || ! cmsIsToneCurveLinear (curve))
+ return FALSE;
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+gimp_color_profile_set_tag (cmsHPROFILE profile,
+ cmsTagSignature sig,
+ const gchar *tag)
+{
+ cmsMLU *mlu;
+
+ mlu = cmsMLUalloc (NULL, 1);
+ cmsMLUsetASCII (mlu, "en", "US", tag);
+ cmsWriteTag (profile, sig, mlu);
+ cmsMLUfree (mlu);
+}
+
+static gboolean
+gimp_color_profile_get_rgb_matrix_colorants (GimpColorProfile *profile,
+ GimpMatrix3 *matrix)
+{
+ cmsHPROFILE lcms_profile;
+ cmsCIEXYZ *red;
+ cmsCIEXYZ *green;
+ cmsCIEXYZ *blue;
+
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), FALSE);
+
+ lcms_profile = profile->priv->lcms_profile;
+
+ red = cmsReadTag (lcms_profile, cmsSigRedColorantTag);
+ green = cmsReadTag (lcms_profile, cmsSigGreenColorantTag);
+ blue = cmsReadTag (lcms_profile, cmsSigBlueColorantTag);
+
+ if (red && green && blue)
+ {
+ if (matrix)
+ {
+ matrix->coeff[0][0] = red->X;
+ matrix->coeff[0][1] = red->Y;
+ matrix->coeff[0][2] = red->Z;
+
+ matrix->coeff[1][0] = green->X;
+ matrix->coeff[1][1] = green->Y;
+ matrix->coeff[1][2] = green->Z;
+
+ matrix->coeff[2][0] = blue->X;
+ matrix->coeff[2][1] = blue->Y;
+ matrix->coeff[2][2] = blue->Z;
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+gimp_color_profile_make_tag (cmsHPROFILE profile,
+ cmsTagSignature sig,
+ const gchar *gimp_tag,
+ const gchar *gimp_prefix,
+ const gchar *gimp_prefix_alt,
+ const gchar *original_tag)
+{
+ if (! original_tag || ! strlen (original_tag) ||
+ ! strcmp (original_tag, gimp_tag))
+ {
+ /* if there is no original tag (or it is the same as the new
+ * tag), just use the new tag
+ */
+
+ gimp_color_profile_set_tag (profile, sig, gimp_tag);
+ }
+ else
+ {
+ /* otherwise prefix the existing tag with a gimp prefix
+ * indicating that the profile has been generated
+ */
+
+ if (g_str_has_prefix (original_tag, gimp_prefix))
+ {
+ /* don't add multiple GIMP prefixes */
+ gimp_color_profile_set_tag (profile, sig, original_tag);
+ }
+ else if (gimp_prefix_alt &&
+ g_str_has_prefix (original_tag, gimp_prefix_alt))
+ {
+ /* replace GIMP prefix_alt by prefix */
+ gchar *new_tag = g_strconcat (gimp_prefix,
+ original_tag + strlen (gimp_prefix_alt),
+ NULL);
+
+ gimp_color_profile_set_tag (profile, sig, new_tag);
+ g_free (new_tag);
+ }
+ else
+ {
+ gchar *new_tag = g_strconcat (gimp_prefix,
+ original_tag,
+ NULL);
+
+ gimp_color_profile_set_tag (profile, sig, new_tag);
+ g_free (new_tag);
+ }
+ }
+}
+
+static GimpColorProfile *
+gimp_color_profile_new_from_color_profile (GimpColorProfile *profile,
+ gboolean linear)
+{
+ GimpColorProfile *new_profile;
+ cmsHPROFILE target_profile;
+ GimpMatrix3 matrix = { { { 0, } } };
+ cmsCIEXYZ *whitepoint;
+ cmsToneCurve *curve;
+
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), NULL);
+
+ if (gimp_color_profile_is_rgb (profile))
+ {
+ if (! gimp_color_profile_get_rgb_matrix_colorants (profile, &matrix))
+ return NULL;
+ }
+ else if (! gimp_color_profile_is_gray (profile))
+ {
+ return NULL;
+ }
+
+ whitepoint = cmsReadTag (profile->priv->lcms_profile,
+ cmsSigMediaWhitePointTag);
+
+ target_profile = cmsCreateProfilePlaceholder (0);
+
+ cmsSetProfileVersion (target_profile, 4.3);
+ cmsSetDeviceClass (target_profile, cmsSigDisplayClass);
+ cmsSetPCS (target_profile, cmsSigXYZData);
+
+ cmsWriteTag (target_profile, cmsSigMediaWhitePointTag, whitepoint);
+
+ if (linear)
+ {
+ /* linear light */
+ curve = cmsBuildGamma (NULL, 1.00);
+
+ gimp_color_profile_make_tag (target_profile, cmsSigProfileDescriptionTag,
+ "linear TRC from unnamed profile",
+ "linear TRC from ",
+ "sRGB TRC from ",
+ gimp_color_profile_get_description (profile));
+ }
+ else
+ {
+ cmsFloat64Number srgb_parameters[5] =
+ { 2.4, 1.0 / 1.055, 0.055 / 1.055, 1.0 / 12.92, 0.04045 };
+
+ /* sRGB curve */
+ curve = cmsBuildParametricToneCurve (NULL, 4, srgb_parameters);
+
+ gimp_color_profile_make_tag (target_profile, cmsSigProfileDescriptionTag,
+ "sRGB TRC from unnamed profile",
+ "sRGB TRC from ",
+ "linear TRC from ",
+ gimp_color_profile_get_description (profile));
+ }
+
+ if (gimp_color_profile_is_rgb (profile))
+ {
+ cmsCIEXYZ red;
+ cmsCIEXYZ green;
+ cmsCIEXYZ blue;
+
+ cmsSetColorSpace (target_profile, cmsSigRgbData);
+
+ red.X = matrix.coeff[0][0];
+ red.Y = matrix.coeff[0][1];
+ red.Z = matrix.coeff[0][2];
+
+ green.X = matrix.coeff[1][0];
+ green.Y = matrix.coeff[1][1];
+ green.Z = matrix.coeff[1][2];
+
+ blue.X = matrix.coeff[2][0];
+ blue.Y = matrix.coeff[2][1];
+ blue.Z = matrix.coeff[2][2];
+
+ cmsWriteTag (target_profile, cmsSigRedColorantTag, &red);
+ cmsWriteTag (target_profile, cmsSigGreenColorantTag, &green);
+ cmsWriteTag (target_profile, cmsSigBlueColorantTag, &blue);
+
+ cmsWriteTag (target_profile, cmsSigRedTRCTag, curve);
+ cmsWriteTag (target_profile, cmsSigGreenTRCTag, curve);
+ cmsWriteTag (target_profile, cmsSigBlueTRCTag, curve);
+ }
+ else
+ {
+ cmsSetColorSpace (target_profile, cmsSigGrayData);
+
+ cmsWriteTag (target_profile, cmsSigGrayTRCTag, curve);
+ }
+
+ cmsFreeToneCurve (curve);
+
+ gimp_color_profile_make_tag (target_profile, cmsSigDeviceMfgDescTag,
+ "GIMP",
+ "GIMP from ", NULL,
+ gimp_color_profile_get_manufacturer (profile));
+ gimp_color_profile_make_tag (target_profile, cmsSigDeviceModelDescTag,
+ "Generated by GIMP",
+ "GIMP from ", NULL,
+ gimp_color_profile_get_model (profile));
+ gimp_color_profile_make_tag (target_profile, cmsSigCopyrightTag,
+ "Public Domain",
+ "GIMP from ", NULL,
+ gimp_color_profile_get_copyright (profile));
+
+ new_profile = gimp_color_profile_new_from_lcms_profile (target_profile, NULL);
+
+ cmsCloseProfile (target_profile);
+
+ return new_profile;
+}
+
+/**
+ * gimp_color_profile_new_srgb_trc_from_color_profile:
+ * @profile: a #GimpColorProfile
+ *
+ * This function creates a new RGB #GimpColorProfile with a sRGB gamma
+ * TRC and @profile's RGB chromacities and whitepoint.
+ *
+ * Return value: the new #GimpColorProfile, or %NULL if @profile is not
+ * an RGB profile or not matrix-based.
+ *
+ * Since: 2.10
+ **/
+GimpColorProfile *
+gimp_color_profile_new_srgb_trc_from_color_profile (GimpColorProfile *profile)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), NULL);
+
+ return gimp_color_profile_new_from_color_profile (profile, FALSE);
+}
+
+/**
+ * gimp_color_profile_new_linear_from_color_profile:
+ * @profile: a #GimpColorProfile
+ *
+ * This function creates a new RGB #GimpColorProfile with a linear TRC
+ * and @profile's RGB chromacities and whitepoint.
+ *
+ * Return value: the new #GimpColorProfile, or %NULL if @profile is not
+ * an RGB profile or not matrix-based.
+ *
+ * Since: 2.10
+ **/
+GimpColorProfile *
+gimp_color_profile_new_linear_from_color_profile (GimpColorProfile *profile)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), NULL);
+
+ return gimp_color_profile_new_from_color_profile (profile, TRUE);
+}
+
+static cmsHPROFILE *
+gimp_color_profile_new_rgb_srgb_internal (void)
+{
+ cmsHPROFILE profile;
+
+ /* white point is D65 from the sRGB specs */
+ cmsCIExyY whitepoint = { 0.3127, 0.3290, 1.0 };
+
+ /* primaries are ITU‐R BT.709‐5 (xYY), which are also the primaries
+ * from the sRGB specs, modified to properly account for hexadecimal
+ * quantization during the profile making process.
+ */
+ cmsCIExyYTRIPLE primaries =
+ {
+ /* R { 0.6400, 0.3300, 1.0 }, */
+ /* G { 0.3000, 0.6000, 1.0 }, */
+ /* B { 0.1500, 0.0600, 1.0 } */
+ /* R */ { 0.639998686, 0.330010138, 1.0 },
+ /* G */ { 0.300003784, 0.600003357, 1.0 },
+ /* B */ { 0.150002046, 0.059997204, 1.0 }
+ };
+
+ cmsFloat64Number srgb_parameters[5] =
+ { 2.4, 1.0 / 1.055, 0.055 / 1.055, 1.0 / 12.92, 0.04045 };
+
+ cmsToneCurve *curve[3];
+
+ /* sRGB curve */
+ curve[0] = curve[1] = curve[2] = cmsBuildParametricToneCurve (NULL, 4,
+ srgb_parameters);
+
+ profile = cmsCreateRGBProfile (&whitepoint, &primaries, curve);
+
+ cmsFreeToneCurve (curve[0]);
+
+ gimp_color_profile_set_tag (profile, cmsSigProfileDescriptionTag,
+ "GIMP built-in sRGB");
+ gimp_color_profile_set_tag (profile, cmsSigDeviceMfgDescTag,
+ "GIMP");
+ gimp_color_profile_set_tag (profile, cmsSigDeviceModelDescTag,
+ "sRGB");
+ gimp_color_profile_set_tag (profile, cmsSigCopyrightTag,
+ "Public Domain");
+
+ /* The following line produces a V2 profile with a point curve TRC.
+ * Profiles with point curve TRCs can't be used in LCMS2 unbounded
+ * mode ICC profile conversions. A V2 profile might be appropriate
+ * for embedding in sRGB images saved to disk, if the image is to be
+ * opened by an image editing application that doesn't understand V4
+ * profiles.
+ *
+ * cmsSetProfileVersion (srgb_profile, 2.1);
+ */
+
+ return profile;
+}
+
+/**
+ * gimp_color_profile_new_rgb_srgb:
+ *
+ * This function is a replacement for cmsCreate_sRGBProfile() and
+ * returns an sRGB profile that is functionally the same as the
+ * ArgyllCMS sRGB.icm profile. "Functionally the same" means it has
+ * the same red, green, and blue colorants and the V4 "chad"
+ * equivalent of the ArgyllCMS V2 white point. The profile TRC is also
+ * functionally equivalent to the ArgyllCMS sRGB.icm TRC and is the
+ * same as the LCMS sRGB built-in profile TRC.
+ *
+ * The actual primaries in the sRGB specification are
+ * red xy: {0.6400, 0.3300, 1.0}
+ * green xy: {0.3000, 0.6000, 1.0}
+ * blue xy: {0.1500, 0.0600, 1.0}
+ *
+ * The sRGB primaries given below are "pre-quantized" to compensate
+ * for hexadecimal quantization during the profile-making process.
+ * Unless the profile-making code compensates for this quantization,
+ * the resulting profile's red, green, and blue colorants will deviate
+ * slightly from the correct XYZ values.
+ *
+ * LCMS2 doesn't compensate for hexadecimal quantization. The
+ * "pre-quantized" primaries below were back-calculated from the
+ * ArgyllCMS sRGB.icm profile. The resulting sRGB profile's colorants
+ * exactly matches the ArgyllCMS sRGB.icm profile colorants.
+ *
+ * Return value: the sRGB #GimpColorProfile.
+ *
+ * Since: 2.10
+ **/
+GimpColorProfile *
+gimp_color_profile_new_rgb_srgb (void)
+{
+ static GimpColorProfile *profile = NULL;
+
+ const guint8 *data;
+ gsize length;
+
+ if (G_UNLIKELY (profile == NULL))
+ {
+ cmsHPROFILE lcms_profile = gimp_color_profile_new_rgb_srgb_internal ();
+
+ profile = gimp_color_profile_new_from_lcms_profile (lcms_profile, NULL);
+
+ cmsCloseProfile (lcms_profile);
+ }
+
+ data = gimp_color_profile_get_icc_profile (profile, &length);
+
+ return gimp_color_profile_new_from_icc_profile (data, length, NULL);
+}
+
+static cmsHPROFILE
+gimp_color_profile_new_rgb_srgb_linear_internal (void)
+{
+ cmsHPROFILE profile;
+
+ /* white point is D65 from the sRGB specs */
+ cmsCIExyY whitepoint = { 0.3127, 0.3290, 1.0 };
+
+ /* primaries are ITU‐R BT.709‐5 (xYY), which are also the primaries
+ * from the sRGB specs, modified to properly account for hexadecimal
+ * quantization during the profile making process.
+ */
+ cmsCIExyYTRIPLE primaries =
+ {
+ /* R { 0.6400, 0.3300, 1.0 }, */
+ /* G { 0.3000, 0.6000, 1.0 }, */
+ /* B { 0.1500, 0.0600, 1.0 } */
+ /* R */ { 0.639998686, 0.330010138, 1.0 },
+ /* G */ { 0.300003784, 0.600003357, 1.0 },
+ /* B */ { 0.150002046, 0.059997204, 1.0 }
+ };
+
+ cmsToneCurve *curve[3];
+
+ /* linear light */
+ curve[0] = curve[1] = curve[2] = cmsBuildGamma (NULL, 1.0);
+
+ profile = cmsCreateRGBProfile (&whitepoint, &primaries, curve);
+
+ cmsFreeToneCurve (curve[0]);
+
+ gimp_color_profile_set_tag (profile, cmsSigProfileDescriptionTag,
+ "GIMP built-in Linear sRGB");
+ gimp_color_profile_set_tag (profile, cmsSigDeviceMfgDescTag,
+ "GIMP");
+ gimp_color_profile_set_tag (profile, cmsSigDeviceModelDescTag,
+ "Linear sRGB");
+ gimp_color_profile_set_tag (profile, cmsSigCopyrightTag,
+ "Public Domain");
+
+ return profile;
+}
+
+/**
+ * gimp_color_profile_new_rgb_srgb_linear:
+ *
+ * This function creates a profile for babl_model("RGB"). Please
+ * somebody write something smarter here.
+ *
+ * Return value: the linear RGB #GimpColorProfile.
+ *
+ * Since: 2.10
+ **/
+GimpColorProfile *
+gimp_color_profile_new_rgb_srgb_linear (void)
+{
+ static GimpColorProfile *profile = NULL;
+
+ const guint8 *data;
+ gsize length;
+
+ if (G_UNLIKELY (profile == NULL))
+ {
+ cmsHPROFILE lcms_profile = gimp_color_profile_new_rgb_srgb_linear_internal ();
+
+ profile = gimp_color_profile_new_from_lcms_profile (lcms_profile, NULL);
+
+ cmsCloseProfile (lcms_profile);
+ }
+
+ data = gimp_color_profile_get_icc_profile (profile, &length);
+
+ return gimp_color_profile_new_from_icc_profile (data, length, NULL);
+}
+
+static cmsHPROFILE *
+gimp_color_profile_new_rgb_adobe_internal (void)
+{
+ cmsHPROFILE profile;
+
+ /* white point is D65 from the sRGB specs */
+ cmsCIExyY whitepoint = { 0.3127, 0.3290, 1.0 };
+
+ /* AdobeRGB1998 and sRGB have the same white point.
+ *
+ * The primaries below are technically correct, but because of
+ * hexadecimal rounding these primaries don't make a profile that
+ * matches the original.
+ *
+ * cmsCIExyYTRIPLE primaries = {
+ * { 0.6400, 0.3300, 1.0 },
+ * { 0.2100, 0.7100, 1.0 },
+ * { 0.1500, 0.0600, 1.0 }
+ * };
+ */
+ cmsCIExyYTRIPLE primaries =
+ {
+ { 0.639996511, 0.329996864, 1.0 },
+ { 0.210005295, 0.710004866, 1.0 },
+ { 0.149997606, 0.060003644, 1.0 }
+ };
+
+ cmsToneCurve *curve[3];
+
+ /* gamma 2.2 */
+ curve[0] = curve[1] = curve[2] = cmsBuildGamma (NULL, 2.19921875);
+
+ profile = cmsCreateRGBProfile (&whitepoint, &primaries, curve);
+
+ cmsFreeToneCurve (curve[0]);
+
+ gimp_color_profile_set_tag (profile, cmsSigProfileDescriptionTag,
+ "Compatible with Adobe RGB (1998)");
+ gimp_color_profile_set_tag (profile, cmsSigDeviceMfgDescTag,
+ "GIMP");
+ gimp_color_profile_set_tag (profile, cmsSigDeviceModelDescTag,
+ "Compatible with Adobe RGB (1998)");
+ gimp_color_profile_set_tag (profile, cmsSigCopyrightTag,
+ "Public Domain");
+
+ return profile;
+}
+
+/**
+ * gimp_color_profile_new_rgb_adobe:
+ *
+ * This function creates a profile compatible with AbobeRGB (1998).
+ *
+ * Return value: the AdobeRGB-compatible #GimpColorProfile.
+ *
+ * Since: 2.10
+ **/
+GimpColorProfile *
+gimp_color_profile_new_rgb_adobe (void)
+{
+ static GimpColorProfile *profile = NULL;
+
+ const guint8 *data;
+ gsize length;
+
+ if (G_UNLIKELY (profile == NULL))
+ {
+ cmsHPROFILE lcms_profile = gimp_color_profile_new_rgb_adobe_internal ();
+
+ profile = gimp_color_profile_new_from_lcms_profile (lcms_profile, NULL);
+
+ cmsCloseProfile (lcms_profile);
+ }
+
+ data = gimp_color_profile_get_icc_profile (profile, &length);
+
+ return gimp_color_profile_new_from_icc_profile (data, length, NULL);
+}
+
+static cmsHPROFILE *
+gimp_color_profile_new_d65_gray_srgb_trc_internal (void)
+{
+ cmsHPROFILE profile;
+
+ /* white point is D65 from the sRGB specs */
+ cmsCIExyY whitepoint = { 0.3127, 0.3290, 1.0 };
+
+ cmsFloat64Number srgb_parameters[5] =
+ { 2.4, 1.0 / 1.055, 0.055 / 1.055, 1.0 / 12.92, 0.04045 };
+
+ cmsToneCurve *curve = cmsBuildParametricToneCurve (NULL, 4,
+ srgb_parameters);
+
+ profile = cmsCreateGrayProfile (&whitepoint, curve);
+
+ cmsFreeToneCurve (curve);
+
+ gimp_color_profile_set_tag (profile, cmsSigProfileDescriptionTag,
+ "GIMP built-in D65 Grayscale with sRGB TRC");
+ gimp_color_profile_set_tag (profile, cmsSigDeviceMfgDescTag,
+ "GIMP");
+ gimp_color_profile_set_tag (profile, cmsSigDeviceModelDescTag,
+ "D65 Grayscale with sRGB TRC");
+ gimp_color_profile_set_tag (profile, cmsSigCopyrightTag,
+ "Public Domain");
+
+ return profile;
+}
+
+/**
+ * gimp_color_profile_new_d65_gray_srgb_trc
+ *
+ * This function creates a grayscale #GimpColorProfile with an
+ * sRGB TRC. See gimp_color_profile_new_rgb_srgb().
+ *
+ * Return value: the sRGB-gamma grayscale #GimpColorProfile.
+ *
+ * Since: 2.10
+ **/
+GimpColorProfile *
+gimp_color_profile_new_d65_gray_srgb_trc (void)
+{
+ static GimpColorProfile *profile = NULL;
+
+ const guint8 *data;
+ gsize length;
+
+ if (G_UNLIKELY (profile == NULL))
+ {
+ cmsHPROFILE lcms_profile = gimp_color_profile_new_d65_gray_srgb_trc_internal ();
+
+ profile = gimp_color_profile_new_from_lcms_profile (lcms_profile, NULL);
+
+ cmsCloseProfile (lcms_profile);
+ }
+
+ data = gimp_color_profile_get_icc_profile (profile, &length);
+
+ return gimp_color_profile_new_from_icc_profile (data, length, NULL);
+}
+
+static cmsHPROFILE
+gimp_color_profile_new_d65_gray_linear_internal (void)
+{
+ cmsHPROFILE profile;
+
+ /* white point is D65 from the sRGB specs */
+ cmsCIExyY whitepoint = { 0.3127, 0.3290, 1.0 };
+
+ cmsToneCurve *curve = cmsBuildGamma (NULL, 1.0);
+
+ profile = cmsCreateGrayProfile (&whitepoint, curve);
+
+ cmsFreeToneCurve (curve);
+
+ gimp_color_profile_set_tag (profile, cmsSigProfileDescriptionTag,
+ "GIMP built-in D65 Linear Grayscale");
+ gimp_color_profile_set_tag (profile, cmsSigDeviceMfgDescTag,
+ "GIMP");
+ gimp_color_profile_set_tag (profile, cmsSigDeviceModelDescTag,
+ "D65 Linear Grayscale");
+ gimp_color_profile_set_tag (profile, cmsSigCopyrightTag,
+ "Public Domain");
+
+ return profile;
+}
+
+/**
+ * gimp_color_profile_new_d65_gray_srgb_gray:
+ *
+ * This function creates a profile for babl_model("Y"). Please
+ * somebody write something smarter here.
+ *
+ * Return value: the linear grayscale #GimpColorProfile.
+ *
+ * Since: 2.10
+ **/
+GimpColorProfile *
+gimp_color_profile_new_d65_gray_linear (void)
+{
+ static GimpColorProfile *profile = NULL;
+
+ const guint8 *data;
+ gsize length;
+
+ if (G_UNLIKELY (profile == NULL))
+ {
+ cmsHPROFILE lcms_profile = gimp_color_profile_new_d65_gray_linear_internal ();
+
+ profile = gimp_color_profile_new_from_lcms_profile (lcms_profile, NULL);
+
+ cmsCloseProfile (lcms_profile);
+ }
+
+ data = gimp_color_profile_get_icc_profile (profile, &length);
+
+ return gimp_color_profile_new_from_icc_profile (data, length, NULL);
+}
+
+static cmsHPROFILE *
+gimp_color_profile_new_d50_gray_lab_trc_internal (void)
+{
+ cmsHPROFILE profile;
+
+ /* white point is D50 from the ICC profile illuminant specs */
+ cmsCIExyY whitepoint = {0.345702915, 0.358538597, 1.0};
+
+ cmsFloat64Number lab_parameters[5] =
+ { 3.0, 1.0 / 1.16, 0.16 / 1.16, 2700.0 / 24389.0, 0.08000 };
+
+ cmsToneCurve *curve = cmsBuildParametricToneCurve (NULL, 4,
+ lab_parameters);
+
+ profile = cmsCreateGrayProfile (&whitepoint, curve);
+
+ cmsFreeToneCurve (curve);
+
+ gimp_color_profile_set_tag (profile, cmsSigProfileDescriptionTag,
+ "GIMP built-in D50 Grayscale with LAB L TRC");
+ gimp_color_profile_set_tag (profile, cmsSigDeviceMfgDescTag,
+ "GIMP");
+ gimp_color_profile_set_tag (profile, cmsSigDeviceModelDescTag,
+ "D50 Grayscale with LAB L TRC");
+ gimp_color_profile_set_tag (profile, cmsSigCopyrightTag,
+ "Public Domain");
+
+ return profile;
+}
+
+
+/**
+ * gimp_color_profile_new_d50_gray_lab_trc
+ *
+ * This function creates a grayscale #GimpColorProfile with the
+ * D50 ICC profile illuminant as the profile white point and the
+ * LAB companding curve as the TRC.
+ *
+ * Return value: a gray profile with the D50 ICC profile illuminant
+ * as the profile white point and the LAB companding curve as the TRC.
+ * as the TRC.
+ *
+ * Since: 2.10
+ **/
+GimpColorProfile *
+gimp_color_profile_new_d50_gray_lab_trc (void)
+{
+ static GimpColorProfile *profile = NULL;
+
+ const guint8 *data;
+ gsize length;
+
+ if (G_UNLIKELY (profile == NULL))
+ {
+ cmsHPROFILE lcms_profile = gimp_color_profile_new_d50_gray_lab_trc_internal ();
+
+ profile = gimp_color_profile_new_from_lcms_profile (lcms_profile, NULL);
+
+ cmsCloseProfile (lcms_profile);
+ }
+
+ data = gimp_color_profile_get_icc_profile (profile, &length);
+
+ return gimp_color_profile_new_from_icc_profile (data, length, NULL);
+}
+
+/**
+ * gimp_color_profile_get_space:
+ * @profile: a #GimpColorProfile
+ * @intent: a #GimpColorRenderingIntent
+ * @error: return location for #GError
+ *
+ * This function returns the #Babl space of @profile, for the
+ * specified @intent.
+ *
+ * Return value: the new #Babl space.
+ *
+ * Since: 2.10.6
+ **/
+const Babl *
+gimp_color_profile_get_space (GimpColorProfile *profile,
+ GimpColorRenderingIntent intent,
+ GError **error)
+{
+ const Babl *space;
+ const gchar *babl_error = NULL;
+
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ space = babl_icc_make_space ((const gchar *) profile->priv->data,
+ profile->priv->length,
+ (BablIccIntent) intent,
+ &babl_error);
+
+ if (! space)
+ g_set_error (error, GIMP_COLOR_PROFILE_ERROR, 0,
+ "%s: %s",
+ gimp_color_profile_get_label (profile), babl_error);
+
+ return space;
+}
+
+/**
+ * gimp_color_profile_get_format:
+ * @profile: a #GimpColorProfile
+ * @format: a #Babl format
+ * @intent: a #GimpColorRenderingIntent
+ * @error: return location for #GError
+ *
+ * This function takes a #GimpColorProfile and a #Babl format and
+ * returns a new #Babl format with @profile's RGB primaries and TRC,
+ * and @format's pixel layout.
+ *
+ * Return value: the new #Babl format.
+ *
+ * Since: 2.10
+ **/
+const Babl *
+gimp_color_profile_get_format (GimpColorProfile *profile,
+ const Babl *format,
+ GimpColorRenderingIntent intent,
+ GError **error)
+{
+ const Babl *space;
+
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (profile), NULL);
+ g_return_val_if_fail (format != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ space = gimp_color_profile_get_space (profile, intent, error);
+
+ if (! space)
+ return NULL;
+
+ return babl_format_with_space (babl_get_name (format), space);
+}
+
+/**
+ * gimp_color_profile_get_lcms_format:
+ * @format: a #Babl format
+ * @lcms_format: return location for an lcms format
+ *
+ * This function takes a #Babl format and returns the lcms format to
+ * be used with that @format. It also returns a #Babl format to be
+ * used instead of the passed @format, which usually is the same as
+ * @format, unless lcms doesn't support @format.
+ *
+ * Note that this function currently only supports RGB, RGBA, R'G'B',
+ * R'G'B'A, Y, YA, Y', Y'A and the cairo-RGB24 and cairo-ARGB32 formats.
+ *
+ * Return value: the #Babl format to be used instead of @format, or %NULL
+ * if the passed @format is not supported at all.
+ *
+ * Since: 2.10
+ **/
+const Babl *
+gimp_color_profile_get_lcms_format (const Babl *format,
+ guint32 *lcms_format)
+{
+ const Babl *output_format = NULL;
+ const Babl *type;
+ const Babl *model;
+ gboolean has_alpha;
+ gboolean rgb = FALSE;
+ gboolean gray = FALSE;
+ gboolean cmyk = FALSE;
+ gboolean linear = FALSE;
+
+ g_return_val_if_fail (format != NULL, NULL);
+ g_return_val_if_fail (lcms_format != NULL, NULL);
+
+ has_alpha = babl_format_has_alpha (format);
+ type = babl_format_get_type (format, 0);
+ model = babl_format_get_model (format);
+
+ if (format == babl_format ("cairo-RGB24"))
+ {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ *lcms_format = TYPE_BGRA_8;
+#else
+ *lcms_format = TYPE_ARGB_8;
+#endif
+
+ return format;
+ }
+ else if (format == babl_format ("cairo-ARGB32"))
+ {
+ rgb = TRUE;
+ }
+ else if (model == babl_model ("RGB") ||
+ model == babl_model ("RGBA") ||
+ model == babl_model ("RaGaBaA"))
+ {
+ rgb = TRUE;
+ linear = TRUE;
+ }
+ else if (model == babl_model ("R'G'B'") ||
+ model == babl_model ("R'G'B'A") ||
+ model == babl_model ("R'aG'aB'aA"))
+ {
+ rgb = TRUE;
+ }
+ else if (model == babl_model ("Y") ||
+ model == babl_model ("YA") ||
+ model == babl_model ("YaA"))
+ {
+ gray = TRUE;
+ linear = TRUE;
+ }
+ else if (model == babl_model ("Y'") ||
+ model == babl_model ("Y'A") ||
+ model == babl_model ("Y'aA"))
+ {
+ gray = TRUE;
+ }
+ else if (model == babl_model ("CMYK"))
+#if 0
+ /* FIXME missing from babl */
+ || model == babl_model ("CMYKA"))
+#endif
+ {
+ cmyk = TRUE;
+ }
+ else if (model == babl_model ("CIE Lab") ||
+ model == babl_model ("CIE Lab alpha") ||
+ model == babl_model ("CIE LCH(ab)") ||
+ model == babl_model ("CIE LCH(ab) alpha"))
+ {
+ if (has_alpha)
+ {
+ *lcms_format = TYPE_RGBA_FLT;
+
+ return babl_format ("RGBA float");
+ }
+ else
+ {
+ *lcms_format = TYPE_RGB_FLT;
+
+ return babl_format ("RGB float");
+ }
+ }
+ else if (babl_format_is_palette (format))
+ {
+ if (has_alpha)
+ {
+ *lcms_format = TYPE_RGBA_8;
+
+ return babl_format ("R'G'B'A u8");
+ }
+ else
+ {
+ *lcms_format = TYPE_RGB_8;
+
+ return babl_format ("R'G'B' u8");
+ }
+ }
+ else
+ {
+ g_printerr ("format not supported: %s\n"
+ "has_alpha = %s\n"
+ "type = %s\n"
+ "model = %s\n",
+ babl_get_name (format),
+ has_alpha ? "TRUE" : "FALSE",
+ babl_get_name (type),
+ babl_get_name (model));
+ g_return_val_if_reached (NULL);
+ }
+
+ *lcms_format = 0;
+
+ #define FIND_FORMAT_FOR_TYPE(babl_t, lcms_t) \
+ do \
+ { \
+ if (has_alpha) \
+ { \
+ if (rgb) \
+ { \
+ *lcms_format = TYPE_RGBA_##lcms_t; \
+ \
+ if (linear) \
+ output_format = babl_format ("RGBA " babl_t); \
+ else \
+ output_format = babl_format ("R'G'B'A " babl_t); \
+ } \
+ else if (gray) \
+ { \
+ *lcms_format = TYPE_GRAYA_##lcms_t; \
+ \
+ if (linear) \
+ output_format = babl_format ("YA " babl_t); \
+ else \
+ output_format = babl_format ("Y'A " babl_t); \
+ } \
+ else if (cmyk) \
+ { \
+ *lcms_format = TYPE_CMYKA_##lcms_t; \
+ \
+ output_format = format; \
+ } \
+ } \
+ else \
+ { \
+ if (rgb) \
+ { \
+ *lcms_format = TYPE_RGB_##lcms_t; \
+ \
+ if (linear) \
+ output_format = babl_format ("RGB " babl_t); \
+ else \
+ output_format = babl_format ("R'G'B' " babl_t); \
+ } \
+ else if (gray) \
+ { \
+ *lcms_format = TYPE_GRAY_##lcms_t; \
+ \
+ if (linear) \
+ output_format = babl_format ("Y " babl_t); \
+ else \
+ output_format = babl_format ("Y' " babl_t); \
+ } \
+ else if (cmyk) \
+ { \
+ *lcms_format = TYPE_CMYK_##lcms_t; \
+ \
+ output_format = format; \
+ } \
+ } \
+ } \
+ while (FALSE)
+
+ if (type == babl_type ("u8"))
+ FIND_FORMAT_FOR_TYPE ("u8", 8);
+ else if (type == babl_type ("u16"))
+ FIND_FORMAT_FOR_TYPE ("u16", 16);
+ else if (type == babl_type ("half")) /* 16-bit floating point (half) */
+ FIND_FORMAT_FOR_TYPE ("half", HALF_FLT);
+ else if (type == babl_type ("float"))
+ FIND_FORMAT_FOR_TYPE ("float", FLT);
+ else if (type == babl_type ("double"))
+ FIND_FORMAT_FOR_TYPE ("double", DBL);
+
+ if (*lcms_format == 0)
+ {
+ g_printerr ("%s: format %s not supported, "
+ "falling back to float\n",
+ G_STRFUNC, babl_get_name (format));
+
+ rgb = ! gray;
+
+ FIND_FORMAT_FOR_TYPE ("float", FLT);
+
+ g_return_val_if_fail (output_format != NULL, NULL);
+ }
+
+ #undef FIND_FORMAT_FOR_TYPE
+
+ return output_format;
+}
diff --git a/libgimpcolor/gimpcolorprofile.h b/libgimpcolor/gimpcolorprofile.h
new file mode 100644
index 0000000..c09470b
--- /dev/null
+++ b/libgimpcolor/gimpcolorprofile.h
@@ -0,0 +1,128 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimpcolorprofile.h
+ * Copyright (C) 2014 Michael Natterer <mitch@gimp.org>
+ * Elle Stone <ellestone@ninedegreesbelow.com>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_COLOR_H_INSIDE__) && !defined (GIMP_COLOR_COMPILATION)
+#error "Only <libgimpcolor/gimpcolor.h> can be included directly."
+#endif
+
+#ifndef __GIMP_COLOR_PROFILE_H__
+#define __GIMP_COLOR_PROFILE_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+#define GIMP_TYPE_COLOR_PROFILE (gimp_color_profile_get_type ())
+#define GIMP_COLOR_PROFILE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_COLOR_PROFILE, GimpColorProfile))
+#define GIMP_COLOR_PROFILE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_COLOR_PROFILE, GimpColorProfileClass))
+#define GIMP_IS_COLOR_PROFILE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_COLOR_PROFILE))
+#define GIMP_IS_COLOR_PROFILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_COLOR_PROFILE))
+#define GIMP_COLOR_PROFILE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_COLOR_PROFILE, GimpColorProfileClass))
+
+
+typedef struct _GimpColorProfileClass GimpColorProfileClass;
+typedef struct _GimpColorProfilePrivate GimpColorProfilePrivate;
+
+struct _GimpColorProfile
+{
+ GObject parent_instance;
+
+ GimpColorProfilePrivate *priv;
+};
+
+struct _GimpColorProfileClass
+{
+ GObjectClass parent_class;
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_color_profile_get_type (void) G_GNUC_CONST;
+
+GimpColorProfile * gimp_color_profile_new_rgb_srgb (void);
+GimpColorProfile * gimp_color_profile_new_rgb_srgb_linear (void);
+GimpColorProfile * gimp_color_profile_new_rgb_adobe (void);
+
+GimpColorProfile * gimp_color_profile_new_d65_gray_srgb_trc (void);
+GimpColorProfile * gimp_color_profile_new_d65_gray_linear (void);
+GimpColorProfile * gimp_color_profile_new_d50_gray_lab_trc (void);
+
+GimpColorProfile *
+ gimp_color_profile_new_srgb_trc_from_color_profile (GimpColorProfile *profile);
+GimpColorProfile *
+ gimp_color_profile_new_linear_from_color_profile (GimpColorProfile *profile);
+
+GimpColorProfile * gimp_color_profile_new_from_file (GFile *file,
+ GError **error);
+
+GimpColorProfile * gimp_color_profile_new_from_icc_profile (const guint8 *data,
+ gsize length,
+ GError **error);
+GimpColorProfile * gimp_color_profile_new_from_lcms_profile (gpointer lcms_profile,
+ GError **error);
+
+gboolean gimp_color_profile_save_to_file (GimpColorProfile *profile,
+ GFile *file,
+ GError **error);
+
+const guint8 * gimp_color_profile_get_icc_profile (GimpColorProfile *profile,
+ gsize *length);
+gpointer gimp_color_profile_get_lcms_profile (GimpColorProfile *profile);
+
+const gchar * gimp_color_profile_get_description (GimpColorProfile *profile);
+const gchar * gimp_color_profile_get_manufacturer (GimpColorProfile *profile);
+const gchar * gimp_color_profile_get_model (GimpColorProfile *profile);
+const gchar * gimp_color_profile_get_copyright (GimpColorProfile *profile);
+
+const gchar * gimp_color_profile_get_label (GimpColorProfile *profile);
+const gchar * gimp_color_profile_get_summary (GimpColorProfile *profile);
+
+gboolean gimp_color_profile_is_equal (GimpColorProfile *profile1,
+ GimpColorProfile *profile2);
+
+gboolean gimp_color_profile_is_rgb (GimpColorProfile *profile);
+gboolean gimp_color_profile_is_gray (GimpColorProfile *profile);
+gboolean gimp_color_profile_is_cmyk (GimpColorProfile *profile);
+
+gboolean gimp_color_profile_is_linear (GimpColorProfile *profile);
+
+const Babl * gimp_color_profile_get_space (GimpColorProfile *profile,
+ GimpColorRenderingIntent intent,
+ GError **error);
+const Babl * gimp_color_profile_get_format (GimpColorProfile *profile,
+ const Babl *format,
+ GimpColorRenderingIntent intent,
+ GError **error);
+
+const Babl * gimp_color_profile_get_lcms_format (const Babl *format,
+ guint32 *lcms_format);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_COLOR_PROFILE_H__ */
diff --git a/libgimpcolor/gimpcolorspace.c b/libgimpcolor/gimpcolorspace.c
new file mode 100644
index 0000000..b8885ce
--- /dev/null
+++ b/libgimpcolor/gimpcolorspace.c
@@ -0,0 +1,1103 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <babl/babl.h>
+#include <glib-object.h>
+
+#include "libgimpmath/gimpmath.h"
+
+#include "gimpcolortypes.h"
+
+#include "gimpcolorspace.h"
+#include "gimprgb.h"
+#include "gimphsv.h"
+
+
+
+/**
+ * SECTION: gimpcolorspace
+ * @title: GimpColorSpace
+ * @short_description: Utility functions which convert colors between
+ * different color models.
+ *
+ * When programming pixel data manipulation functions you will often
+ * use algorithms operating on a color model different from the one
+ * GIMP uses. This file provides utility functions to convert colors
+ * between different color spaces.
+ **/
+
+
+#define GIMP_HSV_UNDEFINED -1.0
+#define GIMP_HSL_UNDEFINED -1.0
+
+/*********************************
+ * color conversion routines *
+ *********************************/
+
+
+/* GimpRGB functions */
+
+
+/**
+ * gimp_rgb_to_hsv:
+ * @rgb: A color value in the RGB colorspace
+ * @hsv: The value converted to the HSV colorspace
+ *
+ * Does a conversion from RGB to HSV (Hue, Saturation,
+ * Value) colorspace.
+ **/
+void
+gimp_rgb_to_hsv (const GimpRGB *rgb,
+ GimpHSV *hsv)
+{
+ gdouble max, min, delta;
+
+ g_return_if_fail (rgb != NULL);
+ g_return_if_fail (hsv != NULL);
+
+ max = gimp_rgb_max (rgb);
+ min = gimp_rgb_min (rgb);
+
+ hsv->v = max;
+ delta = max - min;
+
+ if (delta > 0.0001)
+ {
+ hsv->s = delta / max;
+
+ if (rgb->r == max)
+ {
+ hsv->h = (rgb->g - rgb->b) / delta;
+ if (hsv->h < 0.0)
+ hsv->h += 6.0;
+ }
+ else if (rgb->g == max)
+ {
+ hsv->h = 2.0 + (rgb->b - rgb->r) / delta;
+ }
+ else
+ {
+ hsv->h = 4.0 + (rgb->r - rgb->g) / delta;
+ }
+
+ hsv->h /= 6.0;
+ }
+ else
+ {
+ hsv->s = 0.0;
+ hsv->h = 0.0;
+ }
+
+ hsv->a = rgb->a;
+}
+
+/**
+ * gimp_hsv_to_rgb:
+ * @hsv: A color value in the HSV colorspace
+ * @rgb: The returned RGB value.
+ *
+ * Converts a color value from HSV to RGB colorspace
+ **/
+void
+gimp_hsv_to_rgb (const GimpHSV *hsv,
+ GimpRGB *rgb)
+{
+ gint i;
+ gdouble f, w, q, t;
+
+ gdouble hue;
+
+ g_return_if_fail (rgb != NULL);
+ g_return_if_fail (hsv != NULL);
+
+ if (hsv->s == 0.0)
+ {
+ rgb->r = hsv->v;
+ rgb->g = hsv->v;
+ rgb->b = hsv->v;
+ }
+ else
+ {
+ hue = hsv->h;
+
+ if (hue == 1.0)
+ hue = 0.0;
+
+ hue *= 6.0;
+
+ i = (gint) hue;
+ f = hue - i;
+ w = hsv->v * (1.0 - hsv->s);
+ q = hsv->v * (1.0 - (hsv->s * f));
+ t = hsv->v * (1.0 - (hsv->s * (1.0 - f)));
+
+ switch (i)
+ {
+ case 0:
+ rgb->r = hsv->v;
+ rgb->g = t;
+ rgb->b = w;
+ break;
+ case 1:
+ rgb->r = q;
+ rgb->g = hsv->v;
+ rgb->b = w;
+ break;
+ case 2:
+ rgb->r = w;
+ rgb->g = hsv->v;
+ rgb->b = t;
+ break;
+ case 3:
+ rgb->r = w;
+ rgb->g = q;
+ rgb->b = hsv->v;
+ break;
+ case 4:
+ rgb->r = t;
+ rgb->g = w;
+ rgb->b = hsv->v;
+ break;
+ case 5:
+ rgb->r = hsv->v;
+ rgb->g = w;
+ rgb->b = q;
+ break;
+ }
+ }
+
+ rgb->a = hsv->a;
+}
+
+
+/**
+ * gimp_rgb_to_hsl:
+ * @rgb: A color value in the RGB colorspace
+ * @hsl: The value converted to HSL
+ *
+ * Convert an RGB color value to a HSL (Hue, Saturation, Lightness)
+ * color value.
+ **/
+void
+gimp_rgb_to_hsl (const GimpRGB *rgb,
+ GimpHSL *hsl)
+{
+ gdouble max, min, delta;
+
+ g_return_if_fail (rgb != NULL);
+ g_return_if_fail (hsl != NULL);
+
+ max = gimp_rgb_max (rgb);
+ min = gimp_rgb_min (rgb);
+
+ hsl->l = (max + min) / 2.0;
+
+ if (max == min)
+ {
+ hsl->s = 0.0;
+ hsl->h = GIMP_HSL_UNDEFINED;
+ }
+ else
+ {
+ if (hsl->l <= 0.5)
+ hsl->s = (max - min) / (max + min);
+ else
+ hsl->s = (max - min) / (2.0 - max - min);
+
+ delta = max - min;
+
+ if (delta == 0.0)
+ delta = 1.0;
+
+ if (rgb->r == max)
+ {
+ hsl->h = (rgb->g - rgb->b) / delta;
+ }
+ else if (rgb->g == max)
+ {
+ hsl->h = 2.0 + (rgb->b - rgb->r) / delta;
+ }
+ else
+ {
+ hsl->h = 4.0 + (rgb->r - rgb->g) / delta;
+ }
+
+ hsl->h /= 6.0;
+
+ if (hsl->h < 0.0)
+ hsl->h += 1.0;
+ }
+
+ hsl->a = rgb->a;
+}
+
+static inline gdouble
+gimp_hsl_value (gdouble n1,
+ gdouble n2,
+ gdouble hue)
+{
+ gdouble val;
+
+ if (hue > 6.0)
+ hue -= 6.0;
+ else if (hue < 0.0)
+ hue += 6.0;
+
+ if (hue < 1.0)
+ val = n1 + (n2 - n1) * hue;
+ else if (hue < 3.0)
+ val = n2;
+ else if (hue < 4.0)
+ val = n1 + (n2 - n1) * (4.0 - hue);
+ else
+ val = n1;
+
+ return val;
+}
+
+
+/**
+ * gimp_hsl_to_rgb:
+ * @hsl: A color value in the HSL colorspace
+ * @rgb: The value converted to a value in the RGB colorspace
+ *
+ * Convert a HSL color value to an RGB color value.
+ **/
+void
+gimp_hsl_to_rgb (const GimpHSL *hsl,
+ GimpRGB *rgb)
+{
+ g_return_if_fail (hsl != NULL);
+ g_return_if_fail (rgb != NULL);
+
+ if (hsl->s == 0)
+ {
+ /* achromatic case */
+ rgb->r = hsl->l;
+ rgb->g = hsl->l;
+ rgb->b = hsl->l;
+ }
+ else
+ {
+ gdouble m1, m2;
+
+ if (hsl->l <= 0.5)
+ m2 = hsl->l * (1.0 + hsl->s);
+ else
+ m2 = hsl->l + hsl->s - hsl->l * hsl->s;
+
+ m1 = 2.0 * hsl->l - m2;
+
+ rgb->r = gimp_hsl_value (m1, m2, hsl->h * 6.0 + 2.0);
+ rgb->g = gimp_hsl_value (m1, m2, hsl->h * 6.0);
+ rgb->b = gimp_hsl_value (m1, m2, hsl->h * 6.0 - 2.0);
+ }
+
+ rgb->a = hsl->a;
+}
+
+
+/**
+ * gimp_rgb_to_cmyk:
+ * @rgb: A value in the RGB colorspace
+ * @pullout: A scaling value (0-1) indicating how much black should be
+ * pulled out
+ * @cmyk: The input value naively converted to the CMYK colorspace
+ *
+ * Does a naive conversion from RGB to CMYK colorspace. A simple
+ * formula that doesn't take any color-profiles into account is used.
+ * The amount of black pullout how can be controlled via the @pullout
+ * parameter. A @pullout value of 0 makes this a conversion to CMY.
+ * A value of 1 causes the maximum amount of black to be pulled out.
+ **/
+void
+gimp_rgb_to_cmyk (const GimpRGB *rgb,
+ gdouble pullout,
+ GimpCMYK *cmyk)
+{
+ gdouble c, m, y, k;
+
+ g_return_if_fail (rgb != NULL);
+ g_return_if_fail (cmyk != NULL);
+
+ c = 1.0 - rgb->r;
+ m = 1.0 - rgb->g;
+ y = 1.0 - rgb->b;
+
+ k = 1.0;
+ if (c < k) k = c;
+ if (m < k) k = m;
+ if (y < k) k = y;
+
+ k *= pullout;
+
+ if (k < 1.0)
+ {
+ cmyk->c = (c - k) / (1.0 - k);
+ cmyk->m = (m - k) / (1.0 - k);
+ cmyk->y = (y - k) / (1.0 - k);
+ }
+ else
+ {
+ cmyk->c = 0.0;
+ cmyk->m = 0.0;
+ cmyk->y = 0.0;
+ }
+
+ cmyk->k = k;
+ cmyk->a = rgb->a;
+}
+
+/**
+ * gimp_cmyk_to_rgb:
+ * @cmyk: A color value in the CMYK colorspace
+ * @rgb: The value converted to the RGB colorspace
+ *
+ * Does a simple transformation from the CMYK colorspace to the RGB
+ * colorspace, without taking color profiles into account.
+ **/
+void
+gimp_cmyk_to_rgb (const GimpCMYK *cmyk,
+ GimpRGB *rgb)
+{
+ gdouble c, m, y, k;
+
+ g_return_if_fail (cmyk != NULL);
+ g_return_if_fail (rgb != NULL);
+
+ k = cmyk->k;
+
+ if (k < 1.0)
+ {
+ c = cmyk->c * (1.0 - k) + k;
+ m = cmyk->m * (1.0 - k) + k;
+ y = cmyk->y * (1.0 - k) + k;
+ }
+ else
+ {
+ c = 1.0;
+ m = 1.0;
+ y = 1.0;
+ }
+
+ rgb->r = 1.0 - c;
+ rgb->g = 1.0 - m;
+ rgb->b = 1.0 - y;
+ rgb->a = cmyk->a;
+}
+
+
+#define GIMP_RETURN_RGB(x, y, z) { rgb->r = x; rgb->g = y; rgb->b = z; return; }
+
+/****************************************************************************
+ * Theoretically, hue 0 (pure red) is identical to hue 6 in these transforms.
+ * Pure red always maps to 6 in this implementation. Therefore UNDEFINED can
+ * be defined as 0 in situations where only unsigned numbers are desired.
+ ****************************************************************************/
+
+/**
+ * gimp_rgb_to_hwb:
+ * @rgb: A color value in the RGB colorspace
+ * @hue: The hue value of the above color, in the range 0 to 6
+ * @whiteness: The whiteness value of the above color, in the range 0 to 1
+ * @blackness: The blackness value of the above color, in the range 0 to 1
+ *
+ * Theoretically, hue 0 (pure red) is identical to hue 6 in these transforms.
+ * Pure red always maps to 6 in this implementation. Therefore UNDEFINED can
+ * be defined as 0 in situations where only unsigned numbers are desired.
+ *
+ * RGB are each on [0, 1]. Whiteness and Blackness are returned in the
+ * range [0, 1] and H is returned in the range [0, 6]. If W == 1 - B, H is
+ * undefined.
+ **/
+void
+gimp_rgb_to_hwb (const GimpRGB *rgb,
+ gdouble *hue,
+ gdouble *whiteness,
+ gdouble *blackness)
+{
+ /* RGB are each on [0, 1]. W and B are returned on [0, 1] and H is */
+ /* returned on [0, 6]. Exception: H is returned UNDEFINED if W == 1 - B. */
+ /* ====================================================================== */
+
+ gdouble R = rgb->r, G = rgb->g, B = rgb->b, w, v, b, f;
+ gint i;
+
+ w = gimp_rgb_min (rgb);
+ v = gimp_rgb_max (rgb);
+ b = 1.0 - v;
+
+ if (v == w)
+ {
+ *hue = GIMP_HSV_UNDEFINED;
+ *whiteness = w;
+ *blackness = b;
+ }
+ else
+ {
+ f = (R == w) ? G - B : ((G == w) ? B - R : R - G);
+ i = (R == w) ? 3.0 : ((G == w) ? 5.0 : 1.0);
+
+ *hue = (360.0 / 6.0) * (i - f / (v - w));
+ *whiteness = w;
+ *blackness = b;
+ }
+}
+
+/**
+ * gimp_hwb_to_rgb:
+ * @hue: A hue value, in the range 0 to 6
+ * @whiteness: A whiteness value, in the range 0 to 1
+ * @blackness: A blackness value, in the range 0 to 1
+ * @rgb: The above color converted to the RGB colorspace
+ *
+ * H is defined in the range [0, 6] or UNDEFINED, B and W are both in the
+ * range [0, 1]. The returned RGB values are all in the range [0, 1].
+ **/
+void
+gimp_hwb_to_rgb (gdouble hue,
+ gdouble whiteness,
+ gdouble blackness,
+ GimpRGB *rgb)
+{
+ /* H is given on [0, 6] or UNDEFINED. whiteness and
+ * blackness are given on [0, 1].
+ * RGB are each returned on [0, 1].
+ */
+
+ gdouble h = hue, w = whiteness, b = blackness, v, n, f;
+ gint i;
+
+ h = 6.0 * h/ 360.0;
+
+ v = 1.0 - b;
+ if (h == GIMP_HSV_UNDEFINED)
+ {
+ rgb->r = v;
+ rgb->g = v;
+ rgb->b = v;
+ }
+ else
+ {
+ i = floor (h);
+ f = h - i;
+
+ if (i & 1)
+ f = 1.0 - f; /* if i is odd */
+
+ n = w + f * (v - w); /* linear interpolation between w and v */
+
+ switch (i)
+ {
+ case 6:
+ case 0: GIMP_RETURN_RGB (v, n, w);
+ break;
+ case 1: GIMP_RETURN_RGB (n, v, w);
+ break;
+ case 2: GIMP_RETURN_RGB (w, v, n);
+ break;
+ case 3: GIMP_RETURN_RGB (w, n, v);
+ break;
+ case 4: GIMP_RETURN_RGB (n, w, v);
+ break;
+ case 5: GIMP_RETURN_RGB (v, w, n);
+ break;
+ }
+ }
+
+}
+
+
+/* gint functions */
+
+/**
+ * gimp_rgb_to_hsv_int:
+ * @red: The red channel value, returns the Hue channel
+ * @green: The green channel value, returns the Saturation channel
+ * @blue: The blue channel value, returns the Value channel
+ *
+ * The arguments are pointers to int representing channel values in
+ * the RGB colorspace, and the values pointed to are all in the range
+ * [0, 255].
+ *
+ * The function changes the arguments to point to the HSV value
+ * corresponding, with the returned values in the following
+ * ranges: H [0, 359], S [0, 255], V [0, 255].
+ **/
+void
+gimp_rgb_to_hsv_int (gint *red,
+ gint *green,
+ gint *blue)
+{
+ gdouble r, g, b;
+ gdouble h, s, v;
+ gint min;
+ gdouble delta;
+
+ r = *red;
+ g = *green;
+ b = *blue;
+
+ if (r > g)
+ {
+ v = MAX (r, b);
+ min = MIN (g, b);
+ }
+ else
+ {
+ v = MAX (g, b);
+ min = MIN (r, b);
+ }
+
+ delta = v - min;
+
+ if (v == 0.0)
+ s = 0.0;
+ else
+ s = delta / v;
+
+ if (s == 0.0)
+ {
+ h = 0.0;
+ }
+ else
+ {
+ if (r == v)
+ h = 60.0 * (g - b) / delta;
+ else if (g == v)
+ h = 120 + 60.0 * (b - r) / delta;
+ else
+ h = 240 + 60.0 * (r - g) / delta;
+
+ if (h < 0.0)
+ h += 360.0;
+
+ if (h > 360.0)
+ h -= 360.0;
+ }
+
+ *red = ROUND (h);
+ *green = ROUND (s * 255.0);
+ *blue = ROUND (v);
+
+ /* avoid the ambiguity of returning different values for the same color */
+ if (*red == 360)
+ *red = 0;
+}
+
+/**
+ * gimp_hsv_to_rgb_int:
+ * @hue: The hue channel, returns the red channel
+ * @saturation: The saturation channel, returns the green channel
+ * @value: The value channel, returns the blue channel
+ *
+ * The arguments are pointers to int, with the values pointed to in the
+ * following ranges: H [0, 360], S [0, 255], V [0, 255].
+ *
+ * The function changes the arguments to point to the RGB value
+ * corresponding, with the returned values all in the range [0, 255].
+ **/
+void
+gimp_hsv_to_rgb_int (gint *hue,
+ gint *saturation,
+ gint *value)
+{
+ gdouble h, s, v, h_temp;
+ gdouble f, p, q, t;
+ gint i;
+
+ if (*saturation == 0)
+ {
+ *hue = *value;
+ *saturation = *value;
+ *value = *value;
+ }
+ else
+ {
+ h = *hue;
+ s = *saturation / 255.0;
+ v = *value / 255.0;
+
+ if (h == 360)
+ h_temp = 0;
+ else
+ h_temp = h;
+
+ h_temp = h_temp / 60.0;
+ i = floor (h_temp);
+ f = h_temp - i;
+ p = v * (1.0 - s);
+ q = v * (1.0 - (s * f));
+ t = v * (1.0 - (s * (1.0 - f)));
+
+ switch (i)
+ {
+ case 0:
+ *hue = ROUND (v * 255.0);
+ *saturation = ROUND (t * 255.0);
+ *value = ROUND (p * 255.0);
+ break;
+
+ case 1:
+ *hue = ROUND (q * 255.0);
+ *saturation = ROUND (v * 255.0);
+ *value = ROUND (p * 255.0);
+ break;
+
+ case 2:
+ *hue = ROUND (p * 255.0);
+ *saturation = ROUND (v * 255.0);
+ *value = ROUND (t * 255.0);
+ break;
+
+ case 3:
+ *hue = ROUND (p * 255.0);
+ *saturation = ROUND (q * 255.0);
+ *value = ROUND (v * 255.0);
+ break;
+
+ case 4:
+ *hue = ROUND (t * 255.0);
+ *saturation = ROUND (p * 255.0);
+ *value = ROUND (v * 255.0);
+ break;
+
+ case 5:
+ *hue = ROUND (v * 255.0);
+ *saturation = ROUND (p * 255.0);
+ *value = ROUND (q * 255.0);
+ break;
+ }
+ }
+}
+
+/**
+ * gimp_rgb_to_hsl_int:
+ * @red: Red channel, returns Hue channel
+ * @green: Green channel, returns Lightness channel
+ * @blue: Blue channel, returns Saturation channel
+ *
+ * The arguments are pointers to int representing channel values in the
+ * RGB colorspace, and the values pointed to are all in the range [0, 255].
+ *
+ * The function changes the arguments to point to the corresponding HLS
+ * value with the values pointed to in the following ranges: H [0, 360],
+ * L [0, 255], S [0, 255].
+ **/
+void
+gimp_rgb_to_hsl_int (gint *red,
+ gint *green,
+ gint *blue)
+{
+ gint r, g, b;
+ gdouble h, s, l;
+ gint min, max;
+ gint delta;
+
+ r = *red;
+ g = *green;
+ b = *blue;
+
+ if (r > g)
+ {
+ max = MAX (r, b);
+ min = MIN (g, b);
+ }
+ else
+ {
+ max = MAX (g, b);
+ min = MIN (r, b);
+ }
+
+ l = (max + min) / 2.0;
+
+ if (max == min)
+ {
+ s = 0.0;
+ h = 0.0;
+ }
+ else
+ {
+ delta = (max - min);
+
+ if (l < 128)
+ s = 255 * (gdouble) delta / (gdouble) (max + min);
+ else
+ s = 255 * (gdouble) delta / (gdouble) (511 - max - min);
+
+ if (r == max)
+ h = (g - b) / (gdouble) delta;
+ else if (g == max)
+ h = 2 + (b - r) / (gdouble) delta;
+ else
+ h = 4 + (r - g) / (gdouble) delta;
+
+ h = h * 42.5;
+
+ if (h < 0)
+ h += 255;
+ else if (h > 255)
+ h -= 255;
+ }
+
+ *red = ROUND (h);
+ *green = ROUND (s);
+ *blue = ROUND (l);
+}
+
+/**
+ * gimp_rgb_to_l_int:
+ * @red: Red channel
+ * @green: Green channel
+ * @blue: Blue channel
+ *
+ * Calculates the lightness value of an RGB triplet with the formula
+ * L = (max(R, G, B) + min (R, G, B)) / 2
+ *
+ * Return value: Luminance value corresponding to the input RGB value
+ **/
+gint
+gimp_rgb_to_l_int (gint red,
+ gint green,
+ gint blue)
+{
+ gint min, max;
+
+ if (red > green)
+ {
+ max = MAX (red, blue);
+ min = MIN (green, blue);
+ }
+ else
+ {
+ max = MAX (green, blue);
+ min = MIN (red, blue);
+ }
+
+ return ROUND ((max + min) / 2.0);
+}
+
+static inline gint
+gimp_hsl_value_int (gdouble n1,
+ gdouble n2,
+ gdouble hue)
+{
+ gdouble value;
+
+ if (hue > 255)
+ hue -= 255;
+ else if (hue < 0)
+ hue += 255;
+
+ if (hue < 42.5)
+ value = n1 + (n2 - n1) * (hue / 42.5);
+ else if (hue < 127.5)
+ value = n2;
+ else if (hue < 170)
+ value = n1 + (n2 - n1) * ((170 - hue) / 42.5);
+ else
+ value = n1;
+
+ return ROUND (value * 255.0);
+}
+
+/**
+ * gimp_hsl_to_rgb_int:
+ * @hue: Hue channel, returns Red channel
+ * @saturation: Saturation channel, returns Green channel
+ * @lightness: Lightness channel, returns Blue channel
+ *
+ * The arguments are pointers to int, with the values pointed to in the
+ * following ranges: H [0, 360], L [0, 255], S [0, 255].
+ *
+ * The function changes the arguments to point to the RGB value
+ * corresponding, with the returned values all in the range [0, 255].
+ **/
+void
+gimp_hsl_to_rgb_int (gint *hue,
+ gint *saturation,
+ gint *lightness)
+{
+ gdouble h, s, l;
+
+ h = *hue;
+ s = *saturation;
+ l = *lightness;
+
+ if (s == 0)
+ {
+ /* achromatic case */
+ *hue = l;
+ *lightness = l;
+ *saturation = l;
+ }
+ else
+ {
+ gdouble m1, m2;
+
+ if (l < 128)
+ m2 = (l * (255 + s)) / 65025.0;
+ else
+ m2 = (l + s - (l * s) / 255.0) / 255.0;
+
+ m1 = (l / 127.5) - m2;
+
+ /* chromatic case */
+ *hue = gimp_hsl_value_int (m1, m2, h + 85);
+ *saturation = gimp_hsl_value_int (m1, m2, h);
+ *lightness = gimp_hsl_value_int (m1, m2, h - 85);
+ }
+}
+
+/**
+ * gimp_rgb_to_cmyk_int:
+ * @red: the red channel; returns the cyan value (0-255)
+ * @green: the green channel; returns the magenta value (0-255)
+ * @blue: the blue channel; returns the yellow value (0-255)
+ * @pullout: the percentage of black to pull out (0-100); returns
+ * the black value (0-255)
+ *
+ * Does a naive conversion from RGB to CMYK colorspace. A simple
+ * formula that doesn't take any color-profiles into account is used.
+ * The amount of black pullout how can be controlled via the @pullout
+ * parameter. A @pullout value of 0 makes this a conversion to CMY.
+ * A value of 100 causes the maximum amount of black to be pulled out.
+ **/
+void
+gimp_rgb_to_cmyk_int (gint *red,
+ gint *green,
+ gint *blue,
+ gint *pullout)
+{
+ gint c, m, y;
+
+ c = 255 - *red;
+ m = 255 - *green;
+ y = 255 - *blue;
+
+ if (*pullout == 0)
+ {
+ *red = c;
+ *green = m;
+ *blue = y;
+ }
+ else
+ {
+ gint k = 255;
+
+ if (c < k) k = c;
+ if (m < k) k = m;
+ if (y < k) k = y;
+
+ k = (k * CLAMP (*pullout, 0, 100)) / 100;
+
+ *red = ((c - k) << 8) / (256 - k);
+ *green = ((m - k) << 8) / (256 - k);
+ *blue = ((y - k) << 8) / (256 - k);
+ *pullout = k;
+ }
+}
+
+/**
+ * gimp_cmyk_to_rgb_int:
+ * @cyan: the cyan channel; returns the red value (0-255)
+ * @magenta: the magenta channel; returns the green value (0-255)
+ * @yellow: the yellow channel; returns the blue value (0-255)
+ * @black: the black channel (0-255); doesn't change
+ *
+ * Does a naive conversion from CMYK to RGB colorspace. A simple
+ * formula that doesn't take any color-profiles into account is used.
+ **/
+void
+gimp_cmyk_to_rgb_int (gint *cyan,
+ gint *magenta,
+ gint *yellow,
+ gint *black)
+{
+ gint c, m, y, k;
+
+ c = *cyan;
+ m = *magenta;
+ y = *yellow;
+ k = *black;
+
+ if (k)
+ {
+ c = ((c * (256 - k)) >> 8) + k;
+ m = ((m * (256 - k)) >> 8) + k;
+ y = ((y * (256 - k)) >> 8) + k;
+ }
+
+ *cyan = 255 - c;
+ *magenta = 255 - m;
+ *yellow = 255 - y;
+}
+
+/**
+ * gimp_rgb_to_hsv4:
+ * @rgb: RGB triplet, rgb[0] is red channel, rgb[1] is green,
+ * rgb[2] is blue (0..255)
+ * @hue: Pointer to hue channel (0..1)
+ * @saturation: Pointer to saturation channel (0..1)
+ * @value: Pointer to value channel (0..1)
+ **/
+void
+gimp_rgb_to_hsv4 (const guchar *rgb,
+ gdouble *hue,
+ gdouble *saturation,
+ gdouble *value)
+{
+ gdouble red, green, blue;
+ gdouble h, s, v;
+ gdouble min, max;
+ gdouble delta;
+
+ red = rgb[0] / 255.0;
+ green = rgb[1] / 255.0;
+ blue = rgb[2] / 255.0;
+
+ if (red > green)
+ {
+ max = MAX (red, blue);
+ min = MIN (green, blue);
+ }
+ else
+ {
+ max = MAX (green, blue);
+ min = MIN (red, blue);
+ }
+
+ v = max;
+
+ if (max != 0.0)
+ s = (max - min) / max;
+ else
+ s = 0.0;
+
+ if (s == 0.0)
+ h = 0.0;
+ else
+ {
+ delta = max - min;
+
+ if (delta == 0.0)
+ delta = 1.0;
+
+ if (red == max)
+ h = (green - blue) / delta;
+ else if (green == max)
+ h = 2 + (blue - red) / delta;
+ else
+ h = 4 + (red - green) / delta;
+
+ h /= 6.0;
+
+ if (h < 0.0)
+ h += 1.0;
+ else if (h > 1.0)
+ h -= 1.0;
+ }
+
+ *hue = h;
+ *saturation = s;
+ *value = v;
+}
+
+/**
+ * gimp_hsv_to_rgb4:
+ * @rgb: RGB triplet, rgb[0] is red channel, rgb[1] is green,
+ * rgb[2] is blue (0..255)
+ * @hue: Hue channel (0..1)
+ * @saturation: Saturation channel (0..1)
+ * @value: Value channel (0..1)
+ **/
+void
+gimp_hsv_to_rgb4 (guchar *rgb,
+ gdouble hue,
+ gdouble saturation,
+ gdouble value)
+{
+ gdouble h, s, v;
+ gdouble f, p, q, t;
+
+ if (saturation == 0.0)
+ {
+ hue = value;
+ saturation = value;
+ /*value = value;*/
+ }
+ else
+ {
+ h = hue * 6.0;
+ s = saturation;
+ v = value;
+
+ if (h == 6.0)
+ h = 0.0;
+
+ f = h - (gint) h;
+ p = v * (1.0 - s);
+ q = v * (1.0 - s * f);
+ t = v * (1.0 - s * (1.0 - f));
+
+ switch ((int) h)
+ {
+ case 0:
+ hue = v;
+ saturation = t;
+ value = p;
+ break;
+
+ case 1:
+ hue = q;
+ saturation = v;
+ value = p;
+ break;
+
+ case 2:
+ hue = p;
+ saturation = v;
+ value = t;
+ break;
+
+ case 3:
+ hue = p;
+ saturation = q;
+ value = v;
+ break;
+
+ case 4:
+ hue = t;
+ saturation = p;
+ value = v;
+ break;
+
+ case 5:
+ hue = v;
+ saturation = p;
+ value = q;
+ break;
+ }
+ }
+
+ rgb[0] = ROUND (hue * 255.0);
+ rgb[1] = ROUND (saturation * 255.0);
+ rgb[2] = ROUND (value * 255.0);
+}
diff --git a/libgimpcolor/gimpcolorspace.h b/libgimpcolor/gimpcolorspace.h
new file mode 100644
index 0000000..ae06536
--- /dev/null
+++ b/libgimpcolor/gimpcolorspace.h
@@ -0,0 +1,121 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_COLOR_H_INSIDE__) && !defined (GIMP_COLOR_COMPILATION)
+#error "Only <libgimpcolor/gimpcolor.h> can be included directly."
+#endif
+
+#ifndef __GIMP_COLOR_SPACE_H__
+#define __GIMP_COLOR_SPACE_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+/* Color conversion routines */
+
+
+/* GimpRGB function */
+
+void gimp_rgb_to_hsv (const GimpRGB *rgb,
+ GimpHSV *hsv);
+void gimp_rgb_to_hsl (const GimpRGB *rgb,
+ GimpHSL *hsl);
+void gimp_rgb_to_cmyk (const GimpRGB *rgb,
+ gdouble pullout,
+ GimpCMYK *cmyk);
+
+void gimp_hsv_to_rgb (const GimpHSV *hsv,
+ GimpRGB *rgb);
+void gimp_hsl_to_rgb (const GimpHSL *hsl,
+ GimpRGB *rgb);
+void gimp_cmyk_to_rgb (const GimpCMYK *cmyk,
+ GimpRGB *rgb);
+
+GIMP_DEPRECATED
+void gimp_rgb_to_hwb (const GimpRGB *rgb,
+ gdouble *hue,
+ gdouble *whiteness,
+ gdouble *blackness);
+
+GIMP_DEPRECATED
+void gimp_hwb_to_rgb (gdouble hue,
+ gdouble whiteness,
+ gdouble blackness,
+ GimpRGB *rgb);
+
+
+/* gint functions */
+
+GIMP_DEPRECATED_FOR (gimp_rgb_to_hsv)
+void gimp_rgb_to_hsv_int (gint *red /* returns hue */,
+ gint *green /* returns saturation */,
+ gint *blue /* returns value */);
+
+GIMP_DEPRECATED_FOR (gimp_hsv_to_rgb)
+void gimp_hsv_to_rgb_int (gint *hue /* returns red */,
+ gint *saturation /* returns green */,
+ gint *value /* returns blue */);
+
+GIMP_DEPRECATED_FOR (gimp_rgb_to_cmyk)
+void gimp_rgb_to_cmyk_int (gint *red /* returns cyan */,
+ gint *green /* returns magenta */,
+ gint *blue /* returns yellow */,
+ gint *pullout /* returns black */);
+
+GIMP_DEPRECATED_FOR (gimp_cmyk_to_rgb)
+void gimp_cmyk_to_rgb_int (gint *cyan /* returns red */,
+ gint *magenta /* returns green */,
+ gint *yellow /* returns blue */,
+ gint *black /* not changed */);
+
+GIMP_DEPRECATED_FOR (gimp_rgb_to_hsl)
+void gimp_rgb_to_hsl_int (gint *red /* returns hue */,
+ gint *green /* returns saturation */,
+ gint *blue /* returns lightness */);
+
+GIMP_DEPRECATED_FOR (gimp_rgb_to_hsl)
+gint gimp_rgb_to_l_int (gint red,
+ gint green,
+ gint blue);
+
+GIMP_DEPRECATED_FOR (gimp_hsl_to_rgb)
+void gimp_hsl_to_rgb_int (gint *hue /* returns red */,
+ gint *saturation /* returns green */,
+ gint *lightness /* returns blue */);
+
+
+/* gdouble functions */
+
+GIMP_DEPRECATED_FOR (gimp_rgb_to_hsv)
+void gimp_rgb_to_hsv4 (const guchar *rgb,
+ gdouble *hue,
+ gdouble *saturation,
+ gdouble *value);
+
+GIMP_DEPRECATED_FOR (gimp_hsv_to_rgb)
+void gimp_hsv_to_rgb4 (guchar *rgb,
+ gdouble hue,
+ gdouble saturation,
+ gdouble value);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_COLOR_SPACE_H__ */
diff --git a/libgimpcolor/gimpcolortransform.c b/libgimpcolor/gimpcolortransform.c
new file mode 100644
index 0000000..b01f739
--- /dev/null
+++ b/libgimpcolor/gimpcolortransform.c
@@ -0,0 +1,596 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimpcolortransform.c
+ * Copyright (C) 2014 Michael Natterer <mitch@gimp.org>
+ * Elle Stone <ellestone@ninedegreesbelow.com>
+ * Øyvind Kolås <pippin@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <lcms2.h>
+
+#include <gio/gio.h>
+#include <gegl.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "gimpcolortypes.h"
+
+#include "gimpcolorprofile.h"
+#include "gimpcolortransform.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpcolortransform
+ * @title: GimpColorTransform
+ * @short_description: Definitions and Functions relating to LCMS.
+ *
+ * Definitions and Functions relating to LCMS.
+ **/
+
+/**
+ * GimpColorTransform:
+ *
+ * Simply a typedef to #gpointer, but actually is a cmsHTRANSFORM. It's
+ * used in public GIMP APIs in order to avoid having to include LCMS
+ * headers.
+ **/
+
+
+enum
+{
+ PROGRESS,
+ LAST_SIGNAL
+};
+
+
+struct _GimpColorTransformPrivate
+{
+ GimpColorProfile *src_profile;
+ const Babl *src_format;
+ const Babl *src_space_format;
+
+ GimpColorProfile *dest_profile;
+ const Babl *dest_format;
+ const Babl *dest_space_format;
+
+ cmsHTRANSFORM transform;
+ const Babl *fish;
+};
+
+
+static void gimp_color_transform_finalize (GObject *object);
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpColorTransform, gimp_color_transform,
+ G_TYPE_OBJECT)
+
+#define parent_class gimp_color_transform_parent_class
+
+static guint gimp_color_transform_signals[LAST_SIGNAL] = { 0 };
+
+static gchar *lcms_last_error = NULL;
+
+
+static void
+lcms_error_clear (void)
+{
+ if (lcms_last_error)
+ {
+ g_free (lcms_last_error);
+ lcms_last_error = NULL;
+ }
+}
+
+static void
+lcms_error_handler (cmsContext ContextID,
+ cmsUInt32Number ErrorCode,
+ const gchar *text)
+{
+ lcms_error_clear ();
+
+ lcms_last_error = g_strdup_printf ("lcms2 error %d: %s", ErrorCode, text);
+}
+
+static void
+gimp_color_transform_class_init (GimpColorTransformClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gimp_color_transform_finalize;
+
+ gimp_color_transform_signals[PROGRESS] =
+ g_signal_new ("progress",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpColorTransformClass,
+ progress),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__DOUBLE,
+ G_TYPE_NONE, 1,
+ G_TYPE_DOUBLE);
+
+ cmsSetLogErrorHandler (lcms_error_handler);
+}
+
+static void
+gimp_color_transform_init (GimpColorTransform *transform)
+{
+ transform->priv = gimp_color_transform_get_instance_private (transform);
+}
+
+static void
+gimp_color_transform_finalize (GObject *object)
+{
+ GimpColorTransform *transform = GIMP_COLOR_TRANSFORM (object);
+
+ g_clear_object (&transform->priv->src_profile);
+ g_clear_object (&transform->priv->dest_profile);
+
+ g_clear_pointer (&transform->priv->transform, cmsDeleteTransform);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+/**
+ * gimp_color_transform_new:
+ * @src_profile: the source #GimpColorProfile
+ * @src_format: the source #Babl format
+ * @dest_profile: the destination #GimpColorProfile
+ * @dest_format: the destination #Babl format
+ * @rendering_intent: the rendering intent
+ * @flags: transform flags
+ *
+ * This function creates an color transform.
+ *
+ * Return value: the #GimpColorTransform, or %NULL if no transform is needed
+ * to convert between pixels of @src_profile and @dest_profile.
+ *
+ * Since: 2.10
+ **/
+GimpColorTransform *
+gimp_color_transform_new (GimpColorProfile *src_profile,
+ const Babl *src_format,
+ GimpColorProfile *dest_profile,
+ const Babl *dest_format,
+ GimpColorRenderingIntent rendering_intent,
+ GimpColorTransformFlags flags)
+{
+ GimpColorTransform *transform;
+ GimpColorTransformPrivate *priv;
+ cmsHPROFILE src_lcms;
+ cmsHPROFILE dest_lcms;
+ cmsUInt32Number lcms_src_format;
+ cmsUInt32Number lcms_dest_format;
+ GError *error = NULL;
+
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (src_profile), NULL);
+ g_return_val_if_fail (src_format != NULL, NULL);
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (dest_profile), NULL);
+ g_return_val_if_fail (dest_format != NULL, NULL);
+
+ if (gimp_color_transform_can_gegl_copy (src_profile, dest_profile))
+ return NULL;
+
+ transform = g_object_new (GIMP_TYPE_COLOR_TRANSFORM, NULL);
+
+ priv = transform->priv;
+
+ priv->src_space_format = gimp_color_profile_get_format (src_profile,
+ src_format,
+ BABL_ICC_INTENT_RELATIVE_COLORIMETRIC,
+ &error);
+ if (! priv->src_space_format)
+ {
+ g_printerr ("%s: error making src format: %s\n",
+ G_STRFUNC, error->message);
+ g_clear_error (&error);
+ }
+
+ priv->dest_space_format = gimp_color_profile_get_format (dest_profile,
+ dest_format,
+ rendering_intent,
+ &error);
+ if (! priv->dest_space_format)
+ {
+ g_printerr ("%s: error making dest format: %s\n",
+ G_STRFUNC, error->message);
+ g_clear_error (&error);
+ }
+
+ if (! g_getenv ("GIMP_COLOR_TRANSFORM_DISABLE_BABL") &&
+ priv->src_space_format && priv->dest_space_format)
+ {
+ priv->src_format = src_format;
+ priv->dest_format = dest_format;
+ priv->fish = babl_fish (priv->src_space_format,
+ priv->dest_space_format);
+
+ g_printerr ("%s: using babl for '%s' -> '%s'\n",
+ G_STRFUNC,
+ gimp_color_profile_get_label (src_profile),
+ gimp_color_profile_get_label (dest_profile));
+
+ return transform;
+ }
+
+ priv->src_space_format = NULL;
+ priv->dest_space_format = NULL;
+
+ priv->src_format = gimp_color_profile_get_lcms_format (src_format,
+ &lcms_src_format);
+ priv->dest_format = gimp_color_profile_get_lcms_format (dest_format,
+ &lcms_dest_format);
+
+ src_lcms = gimp_color_profile_get_lcms_profile (src_profile);
+ dest_lcms = gimp_color_profile_get_lcms_profile (dest_profile);
+
+ lcms_error_clear ();
+
+ priv->transform = cmsCreateTransform (src_lcms, lcms_src_format,
+ dest_lcms, lcms_dest_format,
+ rendering_intent,
+ flags |
+ cmsFLAGS_COPY_ALPHA);
+
+ if (lcms_last_error)
+ {
+ if (priv->transform)
+ {
+ cmsDeleteTransform (priv->transform);
+ priv->transform = NULL;
+ }
+
+ g_printerr ("%s\n", lcms_last_error);
+ }
+
+ if (! priv->transform)
+ {
+ g_object_unref (transform);
+ transform = NULL;
+ }
+
+ return transform;
+}
+
+/**
+ * gimp_color_transform_new_proofing:
+ * @src_profile: the source #GimpColorProfile
+ * @src_format: the source #Babl format
+ * @dest_profile: the destination #GimpColorProfile
+ * @dest_format: the destination #Babl format
+ * @proof_profile: the proof #GimpColorProfile
+ * @proof_intent: the proof intent
+ * @display_intent: the display intent
+ * @flags: transform flags
+ *
+ * This function creates a simulation / proofing color transform.
+ *
+ * Return value: the #GimpColorTransform, or %NULL.
+ *
+ * Since: 2.10
+ **/
+GimpColorTransform *
+gimp_color_transform_new_proofing (GimpColorProfile *src_profile,
+ const Babl *src_format,
+ GimpColorProfile *dest_profile,
+ const Babl *dest_format,
+ GimpColorProfile *proof_profile,
+ GimpColorRenderingIntent proof_intent,
+ GimpColorRenderingIntent display_intent,
+ GimpColorTransformFlags flags)
+{
+ GimpColorTransform *transform;
+ GimpColorTransformPrivate *priv;
+ cmsHPROFILE src_lcms;
+ cmsHPROFILE dest_lcms;
+ cmsHPROFILE proof_lcms;
+ cmsUInt32Number lcms_src_format;
+ cmsUInt32Number lcms_dest_format;
+
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (src_profile), NULL);
+ g_return_val_if_fail (src_format != NULL, NULL);
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (dest_profile), NULL);
+ g_return_val_if_fail (dest_format != NULL, NULL);
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (proof_profile), NULL);
+
+ transform = g_object_new (GIMP_TYPE_COLOR_TRANSFORM, NULL);
+
+ priv = transform->priv;
+
+ src_lcms = gimp_color_profile_get_lcms_profile (src_profile);
+ dest_lcms = gimp_color_profile_get_lcms_profile (dest_profile);
+ proof_lcms = gimp_color_profile_get_lcms_profile (proof_profile);
+
+ priv->src_format = gimp_color_profile_get_lcms_format (src_format,
+ &lcms_src_format);
+ priv->dest_format = gimp_color_profile_get_lcms_format (dest_format,
+ &lcms_dest_format);
+
+ lcms_error_clear ();
+
+ priv->transform = cmsCreateProofingTransform (src_lcms, lcms_src_format,
+ dest_lcms, lcms_dest_format,
+ proof_lcms,
+ proof_intent,
+ display_intent,
+ flags |
+ cmsFLAGS_SOFTPROOFING |
+ cmsFLAGS_COPY_ALPHA);
+
+ if (lcms_last_error)
+ {
+ if (priv->transform)
+ {
+ cmsDeleteTransform (priv->transform);
+ priv->transform = NULL;
+ }
+
+ g_printerr ("%s\n", lcms_last_error);
+ }
+
+ if (! priv->transform)
+ {
+ g_object_unref (transform);
+ transform = NULL;
+ }
+
+ return transform;
+}
+
+/**
+ * gimp_color_transform_process_pixels:
+ * @transform: a #GimpColorTransform
+ * @src_format: #Babl format of @src_pixels
+ * @src_pixels: pointer to the source pixels
+ * @dest_format: #Babl format of @dest_pixels
+ * @dest_pixels: pointer to the destination pixels
+ * @length: number of pixels to process
+ *
+ * This function transforms a contiguous line of pixels.
+ *
+ * Since: 2.10
+ **/
+void
+gimp_color_transform_process_pixels (GimpColorTransform *transform,
+ const Babl *src_format,
+ gconstpointer src_pixels,
+ const Babl *dest_format,
+ gpointer dest_pixels,
+ gsize length)
+{
+ GimpColorTransformPrivate *priv;
+ gpointer *src;
+ gpointer *dest;
+
+ g_return_if_fail (GIMP_IS_COLOR_TRANSFORM (transform));
+ g_return_if_fail (src_format != NULL);
+ g_return_if_fail (src_pixels != NULL);
+ g_return_if_fail (dest_format != NULL);
+ g_return_if_fail (dest_pixels != NULL);
+
+ priv = transform->priv;
+
+ if (src_format != priv->src_format)
+ {
+ src = g_malloc (length * babl_format_get_bytes_per_pixel (priv->src_format));
+
+ babl_process (babl_fish (src_format,
+ priv->src_format),
+ src_pixels, src, length);
+ }
+ else
+ {
+ src = (gpointer) src_pixels;
+ }
+
+ if (dest_format != priv->dest_format)
+ {
+ dest = g_malloc (length * babl_format_get_bytes_per_pixel (priv->dest_format));
+ }
+ else
+ {
+ dest = dest_pixels;
+ }
+
+ if (priv->transform)
+ {
+ cmsDoTransform (priv->transform, src, dest, length);
+ }
+ else
+ {
+ babl_process (priv->fish, src, dest, length);
+ }
+
+ if (src_format != priv->src_format)
+ {
+ g_free (src);
+ }
+
+ if (dest_format != priv->dest_format)
+ {
+ babl_process (babl_fish (priv->dest_format,
+ dest_format),
+ dest, dest_pixels, length);
+
+ g_free (dest);
+ }
+}
+
+/**
+ * gimp_color_transform_process_buffer:
+ * @transform: a #GimpColorTransform
+ * @src_buffer: source #GeglBuffer
+ * @src_rect: rectangle in @src_buffer
+ * @dest_buffer: destination #GeglBuffer
+ * @dest_rect: rectangle in @dest_buffer
+ *
+ * This function transforms buffer into another buffer.
+ *
+ * Since: 2.10
+ **/
+void
+gimp_color_transform_process_buffer (GimpColorTransform *transform,
+ GeglBuffer *src_buffer,
+ const GeglRectangle *src_rect,
+ GeglBuffer *dest_buffer,
+ const GeglRectangle *dest_rect)
+{
+ GimpColorTransformPrivate *priv;
+ GeglBufferIterator *iter;
+ gint total_pixels;
+ gint done_pixels = 0;
+
+ g_return_if_fail (GIMP_IS_COLOR_TRANSFORM (transform));
+ g_return_if_fail (GEGL_IS_BUFFER (src_buffer));
+ g_return_if_fail (GEGL_IS_BUFFER (dest_buffer));
+
+ priv = transform->priv;
+
+ if (src_rect)
+ {
+ total_pixels = src_rect->width * src_rect->height;
+ }
+ else
+ {
+ total_pixels = (gegl_buffer_get_width (src_buffer) *
+ gegl_buffer_get_height (src_buffer));
+ }
+
+ if (src_buffer != dest_buffer)
+ {
+ iter = gegl_buffer_iterator_new (src_buffer, src_rect, 0,
+ priv->src_format,
+ GEGL_ACCESS_READ,
+ GEGL_ABYSS_NONE, 2);
+
+ gegl_buffer_iterator_add (iter, dest_buffer, dest_rect, 0,
+ priv->dest_format,
+ GEGL_ACCESS_WRITE,
+ GEGL_ABYSS_NONE);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ if (priv->transform)
+ {
+ cmsDoTransform (priv->transform,
+ iter->items[0].data, iter->items[1].data, iter->length);
+ }
+ else
+ {
+ babl_process (priv->fish,
+ iter->items[0].data, iter->items[1].data, iter->length);
+ }
+
+ done_pixels += iter->items[0].roi.width * iter->items[0].roi.height;
+
+ g_signal_emit (transform, gimp_color_transform_signals[PROGRESS], 0,
+ (gdouble) done_pixels /
+ (gdouble) total_pixels);
+ }
+ }
+ else
+ {
+ iter = gegl_buffer_iterator_new (src_buffer, src_rect, 0,
+ priv->src_format,
+ GEGL_ACCESS_READWRITE,
+ GEGL_ABYSS_NONE, 1);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ if (priv->transform)
+ {
+ cmsDoTransform (priv->transform,
+ iter->items[0].data, iter->items[0].data, iter->length);
+ }
+ else
+ {
+ babl_process (priv->fish,
+ iter->items[0].data, iter->items[0].data, iter->length);
+ }
+
+ done_pixels += iter->items[0].roi.width * iter->items[0].roi.height;
+
+ g_signal_emit (transform, gimp_color_transform_signals[PROGRESS], 0,
+ (gdouble) done_pixels /
+ (gdouble) total_pixels);
+ }
+ }
+
+ g_signal_emit (transform, gimp_color_transform_signals[PROGRESS], 0,
+ 1.0);
+}
+
+/**
+ * gimp_color_transform_can_gegl_copy:
+ * @src_profile: source #GimpColorProfile
+ * @dest_profile: destination #GimpColorProfile
+ *
+ * This function checks if a GimpColorTransform is needed at all.
+ *
+ * Return value: %TRUE if pixels can be correctly converted between
+ * @src_profile and @dest_profile by simply using
+ * gegl_buffer_copy(), babl_process() or similar.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_color_transform_can_gegl_copy (GimpColorProfile *src_profile,
+ GimpColorProfile *dest_profile)
+{
+ static GimpColorProfile *srgb_profile = NULL;
+ static GimpColorProfile *srgb_linear_profile = NULL;
+ static GimpColorProfile *gray_profile = NULL;
+ static GimpColorProfile *gray_linear_profile = NULL;
+
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (src_profile), FALSE);
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (dest_profile), FALSE);
+
+ if (gimp_color_profile_is_equal (src_profile, dest_profile))
+ return TRUE;
+
+ if (! srgb_profile)
+ {
+ srgb_profile = gimp_color_profile_new_rgb_srgb ();
+ srgb_linear_profile = gimp_color_profile_new_rgb_srgb_linear ();
+ gray_profile = gimp_color_profile_new_d65_gray_srgb_trc ();
+ gray_linear_profile = gimp_color_profile_new_d65_gray_linear ();
+ }
+
+ if ((gimp_color_profile_is_equal (src_profile, srgb_profile) ||
+ gimp_color_profile_is_equal (src_profile, srgb_linear_profile) ||
+ gimp_color_profile_is_equal (src_profile, gray_profile) ||
+ gimp_color_profile_is_equal (src_profile, gray_linear_profile))
+ &&
+ (gimp_color_profile_is_equal (dest_profile, srgb_profile) ||
+ gimp_color_profile_is_equal (dest_profile, srgb_linear_profile) ||
+ gimp_color_profile_is_equal (dest_profile, gray_profile) ||
+ gimp_color_profile_is_equal (dest_profile, gray_linear_profile)))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/libgimpcolor/gimpcolortransform.h b/libgimpcolor/gimpcolortransform.h
new file mode 100644
index 0000000..c579470
--- /dev/null
+++ b/libgimpcolor/gimpcolortransform.h
@@ -0,0 +1,127 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimpcolortransform.h
+ * Copyright (C) 2014 Michael Natterer <mitch@gimp.org>
+ * Elle Stone <ellestone@ninedegreesbelow.com>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_COLOR_H_INSIDE__) && !defined (GIMP_COLOR_COMPILATION)
+#error "Only <libgimpcolor/gimpcolor.h> can be included directly."
+#endif
+
+#ifndef __GIMP_COLOR_TRANSFORM_H__
+#define __GIMP_COLOR_TRANSFORM_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+/**
+ * GimpColorTransformFlags:
+ * @GIMP_COLOR_TRANSFORM_FLAGS_NOOPTIMIZE: optimize for accuracy rather
+ * than for speed
+ * @GIMP_COLOR_TRANSFORM_FLAGS_GAMUT_CHECK: mark out of gamut colors in the
+ * transform result
+ * @GIMP_COLOR_TRANSFORM_FLAGS_BLACK_POINT_COMPENSATION: do black point
+ * compensation
+ *
+ * Flags for modifying #GimpColorTransform's behavior.
+ **/
+typedef enum
+{
+ GIMP_COLOR_TRANSFORM_FLAGS_NOOPTIMIZE = 0x0100,
+ GIMP_COLOR_TRANSFORM_FLAGS_GAMUT_CHECK = 0x1000,
+ GIMP_COLOR_TRANSFORM_FLAGS_BLACK_POINT_COMPENSATION = 0x2000,
+} GimpColorTransformFlags;
+
+
+#define GIMP_TYPE_COLOR_TRANSFORM (gimp_color_transform_get_type ())
+#define GIMP_COLOR_TRANSFORM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_COLOR_TRANSFORM, GimpColorTransform))
+#define GIMP_COLOR_TRANSFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_COLOR_TRANSFORM, GimpColorTransformClass))
+#define GIMP_IS_COLOR_TRANSFORM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_COLOR_TRANSFORM))
+#define GIMP_IS_COLOR_TRANSFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_COLOR_TRANSFORM))
+#define GIMP_COLOR_TRANSFORM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_COLOR_TRANSFORM, GimpColorTransformClass))
+
+
+typedef struct _GimpColorTransformClass GimpColorTransformClass;
+typedef struct _GimpColorTransformPrivate GimpColorTransformPrivate;
+
+struct _GimpColorTransform
+{
+ GObject parent_instance;
+
+ GimpColorTransformPrivate *priv;
+};
+
+struct _GimpColorTransformClass
+{
+ GObjectClass parent_class;
+
+ /* signals */
+ void (* progress) (GimpColorTransform *transform,
+ gdouble fraction);
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_color_transform_get_type (void) G_GNUC_CONST;
+
+GimpColorTransform *
+ gimp_color_transform_new (GimpColorProfile *src_profile,
+ const Babl *src_format,
+ GimpColorProfile *dest_profile,
+ const Babl *dest_format,
+ GimpColorRenderingIntent rendering_intent,
+ GimpColorTransformFlags flags);
+
+GimpColorTransform *
+ gimp_color_transform_new_proofing (GimpColorProfile *src_profile,
+ const Babl *src_format,
+ GimpColorProfile *dest_profile,
+ const Babl *dest_format,
+ GimpColorProfile *proof_profile,
+ GimpColorRenderingIntent proof_intent,
+ GimpColorRenderingIntent display_intent,
+ GimpColorTransformFlags flags);
+
+void gimp_color_transform_process_pixels (GimpColorTransform *transform,
+ const Babl *src_format,
+ gconstpointer src_pixels,
+ const Babl *dest_format,
+ gpointer dest_pixels,
+ gsize length);
+
+void gimp_color_transform_process_buffer (GimpColorTransform *transform,
+ GeglBuffer *src_buffer,
+ const GeglRectangle *src_rect,
+ GeglBuffer *dest_buffer,
+ const GeglRectangle *dest_rect);
+
+gboolean gimp_color_transform_can_gegl_copy (GimpColorProfile *src_profile,
+ GimpColorProfile *dest_profile);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_COLOR_TRANSFORM_H__ */
diff --git a/libgimpcolor/gimpcolortypes.h b/libgimpcolor/gimpcolortypes.h
new file mode 100644
index 0000000..565df1b
--- /dev/null
+++ b/libgimpcolor/gimpcolortypes.h
@@ -0,0 +1,126 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_COLOR_TYPES_H__
+#define __GIMP_COLOR_TYPES_H__
+
+
+#include <libgimpbase/gimpbasetypes.h>
+#include <libgimpconfig/gimpconfigtypes.h>
+
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+typedef struct _GimpColorManaged GimpColorManaged; /* dummy typedef */
+typedef struct _GimpColorProfile GimpColorProfile;
+typedef struct _GimpColorTransform GimpColorTransform;
+
+
+/* usually we don't keep the structure definitions in the types file
+ * but GimpRGB appears in too many header files...
+ */
+
+typedef struct _GimpRGB GimpRGB;
+typedef struct _GimpHSV GimpHSV;
+typedef struct _GimpHSL GimpHSL;
+typedef struct _GimpCMYK GimpCMYK;
+
+/**
+ * GimpRGB:
+ * @r: the red component
+ * @g: the green component
+ * @b: the blue component
+ * @a: the alpha component
+ *
+ * Used to keep RGB and RGBA colors. All components are in a range of
+ * [0.0..1.0].
+ **/
+struct _GimpRGB
+{
+ gdouble r, g, b, a;
+};
+
+/**
+ * GimpHSV:
+ * @h: the hue component
+ * @s: the saturation component
+ * @v: the value component
+ * @a: the alpha component
+ *
+ * Used to keep HSV and HSVA colors. All components are in a range of
+ * [0.0..1.0].
+ **/
+struct _GimpHSV
+{
+ gdouble h, s, v, a;
+};
+
+/**
+ * GimpHSL:
+ * @h: the hue component
+ * @s: the saturation component
+ * @l: the lightness component
+ * @a: the alpha component
+ *
+ * Used to keep HSL and HSLA colors. All components are in a range of
+ * [0.0..1.0].
+ **/
+struct _GimpHSL
+{
+ gdouble h, s, l, a;
+};
+
+/**
+ * GimpCMYK:
+ * @c: the cyan component
+ * @m: the magenta component
+ * @y: the yellow component
+ * @k: the black component
+ * @a: the alpha component
+ *
+ * Used to keep CMYK and CMYKA colors. All components are in a range
+ * of [0.0..1.0]. An alpha value is somewhat useless in the CMYK
+ * colorspace, but we keep one around anyway so color conversions
+ * going to CMYK and back can preserve alpha.
+ **/
+struct _GimpCMYK
+{
+ gdouble c, m, y, k, a;
+};
+
+
+typedef void (* GimpRenderFunc) (gdouble x,
+ gdouble y,
+ GimpRGB *color,
+ gpointer data);
+typedef void (* GimpPutPixelFunc) (gint x,
+ gint y,
+ GimpRGB *color,
+ gpointer data);
+typedef void (* GimpProgressFunc) (gint min,
+ gint max,
+ gint current,
+ gpointer data);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_COLOR_TYPES_H__ */
diff --git a/libgimpcolor/gimphsl.c b/libgimpcolor/gimphsl.c
new file mode 100644
index 0000000..050a40d
--- /dev/null
+++ b/libgimpcolor/gimphsl.c
@@ -0,0 +1,93 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+
+#include "gimpcolortypes.h"
+
+#include "gimphsl.h"
+
+
+/*
+ * GIMP_TYPE_HSL
+ */
+
+static GimpHSL * gimp_hsl_copy (const GimpHSL *hsl);
+
+
+GType
+gimp_hsl_get_type (void)
+{
+ static GType hsl_type = 0;
+
+ if (!hsl_type)
+ hsl_type = g_boxed_type_register_static ("GimpHSL",
+ (GBoxedCopyFunc) gimp_hsl_copy,
+ (GBoxedFreeFunc) g_free);
+
+ return hsl_type;
+}
+
+static GimpHSL *
+gimp_hsl_copy (const GimpHSL *hsl)
+{
+ return g_memdup (hsl, sizeof (GimpHSL));
+}
+
+
+/* HSL functions */
+
+/**
+ * gimp_hsl_set:
+ * @hsl:
+ * @h:
+ * @s:
+ * @l:
+ *
+ * Since: 2.8
+ **/
+void
+gimp_hsl_set (GimpHSL *hsl,
+ gdouble h,
+ gdouble s,
+ gdouble l)
+{
+ g_return_if_fail (hsl != NULL);
+
+ hsl->h = h;
+ hsl->s = s;
+ hsl->l = l;
+}
+
+/**
+ * gimp_hsl_set_alpha:
+ * @hsl:
+ * @a:
+ *
+ * Since: 2.10
+ **/
+void
+gimp_hsl_set_alpha (GimpHSL *hsl,
+ gdouble a)
+{
+ g_return_if_fail (hsl != NULL);
+
+ hsl->a = a;
+}
diff --git a/libgimpcolor/gimphsl.h b/libgimpcolor/gimphsl.h
new file mode 100644
index 0000000..f2a4cfb
--- /dev/null
+++ b/libgimpcolor/gimphsl.h
@@ -0,0 +1,49 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_COLOR_H_INSIDE__) && !defined (GIMP_COLOR_COMPILATION)
+#error "Only <libgimpcolor/gimpcolor.h> can be included directly."
+#endif
+
+#ifndef __GIMP_HSL_H__
+#define __GIMP_HSL_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+/*
+ * GIMP_TYPE_HSL
+ */
+
+#define GIMP_TYPE_HSL (gimp_hsl_get_type ())
+
+GType gimp_hsl_get_type (void) G_GNUC_CONST;
+
+void gimp_hsl_set (GimpHSL *hsl,
+ gdouble h,
+ gdouble s,
+ gdouble l);
+void gimp_hsl_set_alpha (GimpHSL *hsl,
+ gdouble a);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_HSL_H__ */
diff --git a/libgimpcolor/gimphsv.c b/libgimpcolor/gimphsv.c
new file mode 100644
index 0000000..56bfbd7
--- /dev/null
+++ b/libgimpcolor/gimphsv.c
@@ -0,0 +1,107 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+
+#include "gimpcolortypes.h"
+
+#include "gimphsv.h"
+
+
+/**
+ * SECTION: gimphsv
+ * @title: GimpHSV
+ * @short_description: Definitions and Functions relating to HSV colors.
+ *
+ * Definitions and Functions relating to HSV colors.
+ **/
+
+
+/*
+ * GIMP_TYPE_HSV
+ */
+
+static GimpHSV * gimp_hsv_copy (const GimpHSV *hsv);
+
+
+GType
+gimp_hsv_get_type (void)
+{
+ static GType hsv_type = 0;
+
+ if (!hsv_type)
+ hsv_type = g_boxed_type_register_static ("GimpHSV",
+ (GBoxedCopyFunc) gimp_hsv_copy,
+ (GBoxedFreeFunc) g_free);
+
+ return hsv_type;
+}
+
+static GimpHSV *
+gimp_hsv_copy (const GimpHSV *hsv)
+{
+ return g_memdup (hsv, sizeof (GimpHSV));
+}
+
+
+/* HSV functions */
+
+void
+gimp_hsv_set (GimpHSV *hsv,
+ gdouble h,
+ gdouble s,
+ gdouble v)
+{
+ g_return_if_fail (hsv != NULL);
+
+ hsv->h = h;
+ hsv->s = s;
+ hsv->v = v;
+}
+
+void
+gimp_hsv_clamp (GimpHSV *hsv)
+{
+ g_return_if_fail (hsv != NULL);
+
+ hsv->h -= (gint) hsv->h;
+
+ if (hsv->h < 0)
+ hsv->h += 1.0;
+
+ hsv->s = CLAMP (hsv->s, 0.0, 1.0);
+ hsv->v = CLAMP (hsv->v, 0.0, 1.0);
+ hsv->a = CLAMP (hsv->a, 0.0, 1.0);
+}
+
+void
+gimp_hsva_set (GimpHSV *hsva,
+ gdouble h,
+ gdouble s,
+ gdouble v,
+ gdouble a)
+{
+ g_return_if_fail (hsva != NULL);
+
+ hsva->h = h;
+ hsva->s = s;
+ hsva->v = v;
+ hsva->a = a;
+}
diff --git a/libgimpcolor/gimphsv.h b/libgimpcolor/gimphsv.h
new file mode 100644
index 0000000..7063d7b
--- /dev/null
+++ b/libgimpcolor/gimphsv.h
@@ -0,0 +1,54 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_COLOR_H_INSIDE__) && !defined (GIMP_COLOR_COMPILATION)
+#error "Only <libgimpcolor/gimpcolor.h> can be included directly."
+#endif
+
+#ifndef __GIMP_HSV_H__
+#define __GIMP_HSV_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+/*
+ * GIMP_TYPE_HSV
+ */
+
+#define GIMP_TYPE_HSV (gimp_hsv_get_type ())
+
+GType gimp_hsv_get_type (void) G_GNUC_CONST;
+
+void gimp_hsv_set (GimpHSV *hsv,
+ gdouble hue,
+ gdouble saturation,
+ gdouble value);
+void gimp_hsv_clamp (GimpHSV *hsv);
+
+void gimp_hsva_set (GimpHSV *hsva,
+ gdouble hue,
+ gdouble saturation,
+ gdouble value,
+ gdouble alpha);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_HSV_H__ */
diff --git a/libgimpcolor/gimppixbuf.c b/libgimpcolor/gimppixbuf.c
new file mode 100644
index 0000000..235b84d
--- /dev/null
+++ b/libgimpcolor/gimppixbuf.c
@@ -0,0 +1,152 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppixbuf.c
+ * Copyright (C) 2012 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "gimpcolortypes.h"
+
+#include "gimppixbuf.h"
+
+
+/**
+ * SECTION: gimppixbuf
+ * @title: GimpPixbuf
+ * @short_description: Definitions and Functions relating to GdkPixbuf.
+ *
+ * Definitions and Functions relating to GdkPixbuf.
+ **/
+
+/**
+ * gimp_pixbuf_get_format:
+ * @pixbuf: a #GdkPixbuf
+ *
+ * Returns the Babl format that corresponds to the @pixbuf's pixel format.
+ *
+ * Return value: the @pixbuf's pixel format
+ *
+ * Since: 2.10
+ **/
+const Babl *
+gimp_pixbuf_get_format (GdkPixbuf *pixbuf)
+{
+ g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
+
+ switch (gdk_pixbuf_get_n_channels (pixbuf))
+ {
+ case 3: return babl_format ("R'G'B' u8");
+ case 4: return babl_format ("R'G'B'A u8");
+ }
+
+ g_return_val_if_reached (NULL);
+}
+
+/**
+ * gimp_pixbuf_create_buffer:
+ * @pixbuf: a #GdkPixbuf
+ *
+ * Returns a #GeglBuffer that's either backed by the @pixbuf's pixels,
+ * or a copy of them. This function tries to not copy the @pixbuf's
+ * pixels. If the pixbuf's rowstride is a multiple of its bpp, a
+ * simple reference to the @pixbuf's pixels is made and @pixbuf will
+ * be kept around for as long as the buffer exists; otherwise the
+ * pixels are copied.
+ *
+ * Return value: a new #GeglBuffer.
+ *
+ * Since: 2.10
+ **/
+GeglBuffer *
+gimp_pixbuf_create_buffer (GdkPixbuf *pixbuf)
+{
+ gint width;
+ gint height;
+ gint rowstride;
+ gint bpp;
+
+ g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
+
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+ rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+ bpp = gdk_pixbuf_get_n_channels (pixbuf);
+
+ if ((rowstride % bpp) == 0)
+ {
+ return gegl_buffer_linear_new_from_data (gdk_pixbuf_get_pixels (pixbuf),
+ gimp_pixbuf_get_format (pixbuf),
+ GEGL_RECTANGLE (0, 0,
+ width, height),
+ rowstride,
+ (GDestroyNotify) g_object_unref,
+ g_object_ref (pixbuf));
+ }
+ else
+ {
+ GeglBuffer *buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
+ width, height),
+ gimp_pixbuf_get_format (pixbuf));
+
+ gegl_buffer_set (buffer, NULL, 0, NULL,
+ gdk_pixbuf_get_pixels (pixbuf),
+ gdk_pixbuf_get_rowstride (pixbuf));
+
+ return buffer;
+ }
+}
+
+/**
+ * gimp_pixbuf_get_icc_profile:
+ * @pixbuf: a #GdkPixbuf
+ * @length: return location for the ICC profile's length
+ *
+ * Returns the ICC profile attached to the @pixbuf, or %NULL if there
+ * is none.
+ *
+ * Return value: The ICC profile data, or %NULL. The value should be freed
+ * with g_free().
+ *
+ * Since: 2.10
+ **/
+guint8 *
+gimp_pixbuf_get_icc_profile (GdkPixbuf *pixbuf,
+ gsize *length)
+{
+ const gchar *icc_base64;
+
+ g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
+ g_return_val_if_fail (length != NULL, NULL);
+
+ icc_base64 = gdk_pixbuf_get_option (pixbuf, "icc-profile");
+
+ if (icc_base64)
+ {
+ guint8 *icc_data;
+
+ icc_data = g_base64_decode (icc_base64, length);
+
+ return icc_data;
+ }
+
+ return NULL;
+}
diff --git a/libgimpcolor/gimppixbuf.h b/libgimpcolor/gimppixbuf.h
new file mode 100644
index 0000000..6cc590c
--- /dev/null
+++ b/libgimpcolor/gimppixbuf.h
@@ -0,0 +1,43 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppixbuf.h
+ * Copyright (C) 2012 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_COLOR_H_INSIDE__) && !defined (GIMP_COLOR_COMPILATION)
+#error "Only <libgimpcolor/gimpcolor.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PIXBUF_H__
+#define __GIMP_PIXBUF_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+const Babl * gimp_pixbuf_get_format (GdkPixbuf *pixbuf);
+GeglBuffer * gimp_pixbuf_create_buffer (GdkPixbuf *pixbuf);
+
+guint8 * gimp_pixbuf_get_icc_profile (GdkPixbuf *pixbuf,
+ gsize *length);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PIXBUF_H__ */
diff --git a/libgimpcolor/gimprgb-parse.c b/libgimpcolor/gimprgb-parse.c
new file mode 100644
index 0000000..ba16432
--- /dev/null
+++ b/libgimpcolor/gimprgb-parse.c
@@ -0,0 +1,651 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimprgb-parse.c
+ * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
+ *
+ * Some of the code in here was inspired and partly copied from pango
+ * and librsvg.
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <babl/babl.h>
+#include <glib-object.h>
+
+#include "gimpcolortypes.h"
+
+#include "gimpcolorspace.h"
+#include "gimprgb.h"
+
+
+static gchar * gimp_rgb_parse_strip (const gchar *str,
+ gint len);
+
+static gboolean gimp_rgb_parse_name_internal (GimpRGB *rgb,
+ const gchar *name);
+static gboolean gimp_rgb_parse_hex_internal (GimpRGB *rgb,
+ const gchar *hex);
+static gboolean gimp_rgb_parse_css_internal (GimpRGB *rgb,
+ const gchar *css);
+static gboolean gimp_rgba_parse_css_internal (GimpRGB *rgb,
+ const gchar *css);
+
+
+typedef struct
+{
+ const gchar *name;
+ const guchar red;
+ const guchar green;
+ const guchar blue;
+} ColorEntry;
+
+static const ColorEntry named_colors[] =
+{
+ { "aliceblue", 240, 248, 255 },
+ { "antiquewhite", 250, 235, 215 },
+ { "aqua", 0, 255, 255 },
+ { "aquamarine", 127, 255, 212 },
+ { "azure", 240, 255, 255 },
+ { "beige", 245, 245, 220 },
+ { "bisque", 255, 228, 196 },
+ { "black", 0, 0, 0 },
+ { "blanchedalmond", 255, 235, 205 },
+ { "blue", 0, 0, 255 },
+ { "blueviolet", 138, 43, 226 },
+ { "brown", 165, 42, 42 },
+ { "burlywood", 222, 184, 135 },
+ { "cadetblue", 95, 158, 160 },
+ { "chartreuse", 127, 255, 0 },
+ { "chocolate", 210, 105, 30 },
+ { "coral", 255, 127, 80 },
+ { "cornflowerblue", 100, 149, 237 },
+ { "cornsilk", 255, 248, 220 },
+ { "crimson", 220, 20, 60 },
+ { "cyan", 0, 255, 255 },
+ { "darkblue", 0, 0, 139 },
+ { "darkcyan", 0, 139, 139 },
+ { "darkgoldenrod", 184, 134, 11 },
+ { "darkgray", 169, 169, 169 },
+ { "darkgreen", 0, 100, 0 },
+ { "darkgrey", 169, 169, 169 },
+ { "darkkhaki", 189, 183, 107 },
+ { "darkmagenta", 139, 0, 139 },
+ { "darkolivegreen", 85, 107, 47 },
+ { "darkorange", 255, 140, 0 },
+ { "darkorchid", 153, 50, 204 },
+ { "darkred", 139, 0, 0 },
+ { "darksalmon", 233, 150, 122 },
+ { "darkseagreen", 143, 188, 143 },
+ { "darkslateblue", 72, 61, 139 },
+ { "darkslategray", 47, 79, 79 },
+ { "darkslategrey", 47, 79, 79 },
+ { "darkturquoise", 0, 206, 209 },
+ { "darkviolet", 148, 0, 211 },
+ { "deeppink", 255, 20, 147 },
+ { "deepskyblue", 0, 191, 255 },
+ { "dimgray", 105, 105, 105 },
+ { "dimgrey", 105, 105, 105 },
+ { "dodgerblue", 30, 144, 255 },
+ { "firebrick", 178, 34, 34 },
+ { "floralwhite" , 255, 250, 240 },
+ { "forestgreen", 34, 139, 34 },
+ { "fuchsia", 255, 0, 255 },
+ { "gainsboro", 220, 220, 220 },
+ { "ghostwhite", 248, 248, 255 },
+ { "gold", 255, 215, 0 },
+ { "goldenrod", 218, 165, 32 },
+ { "gray", 128, 128, 128 },
+ { "green", 0, 128, 0 },
+ { "greenyellow", 173, 255, 47 },
+ { "grey", 128, 128, 128 },
+ { "honeydew", 240, 255, 240 },
+ { "hotpink", 255, 105, 180 },
+ { "indianred", 205, 92, 92 },
+ { "indigo", 75, 0, 130 },
+ { "ivory", 255, 255, 240 },
+ { "khaki", 240, 230, 140 },
+ { "lavender", 230, 230, 250 },
+ { "lavenderblush", 255, 240, 245 },
+ { "lawngreen", 124, 252, 0 },
+ { "lemonchiffon", 255, 250, 205 },
+ { "lightblue", 173, 216, 230 },
+ { "lightcoral", 240, 128, 128 },
+ { "lightcyan", 224, 255, 255 },
+ { "lightgoldenrodyellow", 250, 250, 210 },
+ { "lightgray", 211, 211, 211 },
+ { "lightgreen", 144, 238, 144 },
+ { "lightgrey", 211, 211, 211 },
+ { "lightpink", 255, 182, 193 },
+ { "lightsalmon", 255, 160, 122 },
+ { "lightseagreen", 32, 178, 170 },
+ { "lightskyblue", 135, 206, 250 },
+ { "lightslategray", 119, 136, 153 },
+ { "lightslategrey", 119, 136, 153 },
+ { "lightsteelblue", 176, 196, 222 },
+ { "lightyellow", 255, 255, 224 },
+ { "lime", 0, 255, 0 },
+ { "limegreen", 50, 205, 50 },
+ { "linen", 250, 240, 230 },
+ { "magenta", 255, 0, 255 },
+ { "maroon", 128, 0, 0 },
+ { "mediumaquamarine", 102, 205, 170 },
+ { "mediumblue", 0, 0, 205 },
+ { "mediumorchid", 186, 85, 211 },
+ { "mediumpurple", 147, 112, 219 },
+ { "mediumseagreen", 60, 179, 113 },
+ { "mediumslateblue", 123, 104, 238 },
+ { "mediumspringgreen", 0, 250, 154 },
+ { "mediumturquoise", 72, 209, 204 },
+ { "mediumvioletred", 199, 21, 133 },
+ { "midnightblue", 25, 25, 112 },
+ { "mintcream", 245, 255, 250 },
+ { "mistyrose", 255, 228, 225 },
+ { "moccasin", 255, 228, 181 },
+ { "navajowhite", 255, 222, 173 },
+ { "navy", 0, 0, 128 },
+ { "oldlace", 253, 245, 230 },
+ { "olive", 128, 128, 0 },
+ { "olivedrab", 107, 142, 35 },
+ { "orange", 255, 165, 0 },
+ { "orangered", 255, 69, 0 },
+ { "orchid", 218, 112, 214 },
+ { "palegoldenrod", 238, 232, 170 },
+ { "palegreen", 152, 251, 152 },
+ { "paleturquoise", 175, 238, 238 },
+ { "palevioletred", 219, 112, 147 },
+ { "papayawhip", 255, 239, 213 },
+ { "peachpuff", 255, 218, 185 },
+ { "peru", 205, 133, 63 },
+ { "pink", 255, 192, 203 },
+ { "plum", 221, 160, 221 },
+ { "powderblue", 176, 224, 230 },
+ { "purple", 128, 0, 128 },
+ { "red", 255, 0, 0 },
+ { "rosybrown", 188, 143, 143 },
+ { "royalblue", 65, 105, 225 },
+ { "saddlebrown", 139, 69, 19 },
+ { "salmon", 250, 128, 114 },
+ { "sandybrown", 244, 164, 96 },
+ { "seagreen", 46, 139, 87 },
+ { "seashell", 255, 245, 238 },
+ { "sienna", 160, 82, 45 },
+ { "silver", 192, 192, 192 },
+ { "skyblue", 135, 206, 235 },
+ { "slateblue", 106, 90, 205 },
+ { "slategray", 112, 128, 144 },
+ { "slategrey", 112, 128, 144 },
+ { "snow", 255, 250, 250 },
+ { "springgreen", 0, 255, 127 },
+ { "steelblue", 70, 130, 180 },
+ { "tan", 210, 180, 140 },
+ { "teal", 0, 128, 128 },
+ { "thistle", 216, 191, 216 },
+ { "tomato", 255, 99, 71 },
+ { "turquoise", 64, 224, 208 },
+ { "violet", 238, 130, 238 },
+ { "wheat", 245, 222, 179 },
+ { "white", 255, 255, 255 },
+ { "whitesmoke", 245, 245, 245 },
+ { "yellow", 255, 255, 0 },
+ { "yellowgreen", 154, 205, 50 }
+};
+
+
+/**
+ * gimp_rgb_parse_name:
+ * @rgb: a #GimpRGB struct used to return the parsed color
+ * @name: a color name (in UTF-8 encoding)
+ * @len: the length of @name, in bytes. or -1 if @name is nul-terminated
+ *
+ * Attempts to parse a color name. This function accepts <ulink
+ * url="https://www.w3.org/TR/SVG/types.html#ColorKeywords">SVG 1.0
+ * color keywords</ulink>.
+ *
+ * This function does not touch the alpha component of @rgb.
+ *
+ * Return value: %TRUE if @name was parsed successfully and @rgb has
+ * been set, %FALSE otherwise
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_rgb_parse_name (GimpRGB *rgb,
+ const gchar *name,
+ gint len)
+{
+ gchar *tmp;
+ gboolean result;
+
+ g_return_val_if_fail (rgb != NULL, FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ tmp = gimp_rgb_parse_strip (name, len);
+
+ result = gimp_rgb_parse_name_internal (rgb, tmp);
+
+ g_free (tmp);
+
+ return result;
+}
+
+/**
+ * gimp_rgb_parse_hex:
+ * @rgb: a #GimpRGB struct used to return the parsed color
+ * @hex: a string describing a color in hexadecimal notation
+ * @len: the length of @hex, in bytes. or -1 if @hex is nul-terminated
+ *
+ * Attempts to parse a string describing an RGB color in hexadecimal
+ * notation (optionally prefixed with a '#').
+ *
+ * This function does not touch the alpha component of @rgb.
+ *
+ * Return value: %TRUE if @hex was parsed successfully and @rgb has
+ * been set, %FALSE otherwise
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_rgb_parse_hex (GimpRGB *rgb,
+ const gchar *hex,
+ gint len)
+{
+ gchar *tmp;
+ gboolean result;
+
+ g_return_val_if_fail (rgb != NULL, FALSE);
+ g_return_val_if_fail (hex != NULL, FALSE);
+
+ tmp = gimp_rgb_parse_strip (hex, len);
+
+ result = gimp_rgb_parse_hex_internal (rgb, tmp);
+
+ g_free (tmp);
+
+ return result;
+}
+
+/**
+ * gimp_rgb_parse_css:
+ * @rgb: a #GimpRGB struct used to return the parsed color
+ * @css: a string describing a color in CSS notation
+ * @len: the length of @hex, in bytes. or -1 if @hex is nul-terminated
+ *
+ * Attempts to parse a string describing an RGB color in CSS
+ * notation. This can be either a numerical representation
+ * (<code>rgb(255,0,0)</code> or <code>rgb(100%,0%,0%)</code>)
+ * or a hexadecimal notation as parsed by gimp_rgb_parse_hex()
+ * (<code>##ff0000</code>) or a color name as parsed by
+ * gimp_rgb_parse_name() (<code>red</code>).
+ *
+ * This function does not touch the alpha component of @rgb.
+ *
+ * Return value: %TRUE if @css was parsed successfully and @rgb has been
+ * set, %FALSE otherwise
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_rgb_parse_css (GimpRGB *rgb,
+ const gchar *css,
+ gint len)
+{
+ gchar *tmp;
+ gboolean result;
+
+ g_return_val_if_fail (rgb != NULL, FALSE);
+ g_return_val_if_fail (css != NULL, FALSE);
+
+ tmp = gimp_rgb_parse_strip (css, len);
+
+ result = gimp_rgb_parse_css_internal (rgb, tmp);
+
+ g_free (tmp);
+
+ return result;
+}
+
+/**
+ * gimp_rgba_parse_css:
+ * @rgba: a #GimpRGB struct used to return the parsed color
+ * @css: a string describing a color in CSS notation
+ * @len: the length of @hex, in bytes. or -1 if @hex is nul-terminated
+ *
+ * Similar to gimp_rgb_parse_css() but handles RGB colors with alpha
+ * channel in the numerical CSS notation (<code>rgba(255,0,0,255)</code>
+ * or <code>rgba(100%,0%,0%,1000%)</code>).
+ *
+ * It doesn't handle the hexadecimal notation or color names because
+ * they leave the alpha channel unspecified.
+ *
+ * Return value: %TRUE if @css was parsed successfully and @rgb has been
+ * set, %FALSE otherwise
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_rgba_parse_css (GimpRGB *rgba,
+ const gchar *css,
+ gint len)
+{
+ gchar *tmp;
+ gboolean result;
+
+ g_return_val_if_fail (rgba != NULL, FALSE);
+ g_return_val_if_fail (css != NULL, FALSE);
+
+ if (len < 0)
+ len = strlen (css);
+
+ tmp = gimp_rgb_parse_strip (css, len);
+
+ if (strcmp (tmp, "transparent") == 0)
+ {
+ gimp_rgba_set (rgba, 0.0, 0.0, 0.0, 0.0);
+ result = TRUE;
+ }
+ else
+ {
+ result = gimp_rgba_parse_css_internal (rgba, tmp);
+ }
+
+ g_free (tmp);
+
+ return result;
+}
+
+
+/**
+ * gimp_rgb_list_names:
+ * @names: return location for an array of color names
+ * @colors: return location for an array of GimpRGB structs
+ *
+ * Returns the list of <ulink
+ * url="https://www.w3.org/TR/SVG/types.html">SVG 1.0 color
+ * keywords</ulink> that is used by gimp_rgb_parse_name().
+ *
+ * The returned strings are const and must not be freed. Only the two
+ * arrays are allocated dynamically. You must call g_free() on the
+ * @names and @colors arrays when they are not any longer needed.
+ *
+ * Return value: the number of named colors
+ * (i.e. the length of the returned arrays)
+ *
+ * Since: 2.2
+ **/
+gint
+gimp_rgb_list_names (const gchar ***names,
+ GimpRGB **colors)
+{
+ gint i;
+
+ g_return_val_if_fail (names != NULL, 0);
+ g_return_val_if_fail (colors != NULL, 0);
+
+ *names = g_new (const gchar *, G_N_ELEMENTS (named_colors));
+ *colors = g_new (GimpRGB, G_N_ELEMENTS (named_colors));
+
+ for (i = 0; i < G_N_ELEMENTS (named_colors); i++)
+ {
+ (*names)[i] = named_colors[i].name;
+
+ gimp_rgba_set_uchar ((*colors) + i,
+ named_colors[i].red,
+ named_colors[i].green,
+ named_colors[i].blue,
+ 0xFF);
+ }
+
+ return G_N_ELEMENTS (named_colors);
+}
+
+
+static gchar *
+gimp_rgb_parse_strip (const gchar *str,
+ gint len)
+{
+ gchar *result;
+
+ while (len > 0 && g_ascii_isspace (*str))
+ {
+ str++;
+ len--;
+ }
+
+ if (len < 0)
+ {
+ while (g_ascii_isspace (*str))
+ str++;
+
+ len = strlen (str);
+ }
+
+ while (len > 0 && g_ascii_isspace (str[len - 1]))
+ len--;
+
+ result = g_malloc (len + 1);
+
+ memcpy (result, str, len);
+ result[len] = '\0';
+
+ return result;
+}
+
+static gint
+gimp_rgb_color_entry_compare (gconstpointer a,
+ gconstpointer b)
+{
+ const gchar *name = a;
+ const ColorEntry *entry = b;
+
+ return g_ascii_strcasecmp (name, entry->name);
+}
+
+static gboolean
+gimp_rgb_parse_name_internal (GimpRGB *rgb,
+ const gchar *name)
+{
+ ColorEntry *entry = bsearch (name, named_colors,
+ G_N_ELEMENTS (named_colors), sizeof (ColorEntry),
+ gimp_rgb_color_entry_compare);
+
+ if (entry)
+ {
+ gimp_rgb_set_uchar (rgb, entry->red, entry->green, entry->blue);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static gboolean
+gimp_rgb_parse_hex_component (const gchar *hex,
+ gint len,
+ gdouble *value)
+{
+ gint i;
+ guint c = 0;
+
+ for (i = 0; i < len; i++, hex++)
+ {
+ if (!*hex || !g_ascii_isxdigit (*hex))
+ return FALSE;
+
+ c = (c << 4) | g_ascii_xdigit_value (*hex);
+ }
+
+ switch (len)
+ {
+ case 1: *value = (gdouble) c / 15.0; break;
+ case 2: *value = (gdouble) c / 255.0; break;
+ case 3: *value = (gdouble) c / 4095.0; break;
+ case 4: *value = (gdouble) c / 65535.0; break;
+ default:
+ g_return_val_if_reached (FALSE);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gimp_rgb_parse_hex_internal (GimpRGB *rgb,
+ const gchar *hex)
+{
+ gint i;
+ gsize len;
+ gdouble val[3];
+
+ if (hex[0] == '#')
+ hex++;
+
+ len = strlen (hex);
+ if (len % 3 || len < 3 || len > 12)
+ return FALSE;
+
+ len /= 3;
+
+ for (i = 0; i < 3; i++, hex += len)
+ {
+ if (! gimp_rgb_parse_hex_component (hex, len, val + i))
+ return FALSE;
+ }
+
+ gimp_rgb_set (rgb, val[0], val[1], val[2]);
+
+ return TRUE;
+}
+
+
+static gboolean
+gimp_rgb_parse_css_numeric (GimpRGB *rgb,
+ const gchar *css)
+{
+ gdouble values[4];
+ gboolean alpha;
+ gboolean hsl;
+ gint i;
+
+ if (css[0] == 'r' && css[1] == 'g' && css[2] == 'b')
+ hsl = FALSE;
+ else if (css[0] == 'h' && css[1] == 's' && css[2] == 'l')
+ hsl = TRUE;
+ else
+ return FALSE;
+
+ if (css[3] == 'a' && css[4] == '(')
+ alpha = TRUE;
+else if (css[3] == '(')
+ alpha = FALSE;
+ else
+ return FALSE;
+
+ css += (alpha ? 5 : 4);
+
+ for (i = 0; i < (alpha ? 4 : 3); i++)
+ {
+ const gchar *end = css;
+
+ while (*end && *end != ',' && *end != '%' && *end != ')')
+ end++;
+
+ if (i == 3 || *end == '%')
+ {
+ values[i] = g_ascii_strtod (css, (gchar **) &end);
+
+ if (errno == ERANGE)
+ return FALSE;
+
+ if (*end == '%')
+ {
+ end++;
+ values[i] /= 100.0;
+ }
+ }
+ else
+ {
+ glong value = strtol (css, (gchar **) &end, 10);
+
+ if (errno == ERANGE)
+ return FALSE;
+
+ if (hsl)
+ values[i] = value / (i == 0 ? 360.0 : 100.0);
+ else
+ values[i] = value / 255.0;
+ }
+
+ while (*end == ',' || g_ascii_isspace (*end))
+ end++;
+
+ css = end;
+ }
+
+ if (*css != ')')
+ return FALSE;
+
+ if (alpha)
+ gimp_rgba_set (rgb, values[0], values[1], values[2], values[3]);
+ else
+ gimp_rgb_set (rgb, values[0], values[1], values[2]);
+
+ gimp_rgb_clamp (rgb);
+
+ if (hsl)
+ {
+ GimpHSL tmp = (*((GimpHSL *) rgb));
+
+ gimp_hsl_to_rgb (&tmp, rgb);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gimp_rgb_parse_css_internal (GimpRGB *rgb,
+ const gchar *css)
+{
+ if (css[0] == '#')
+ {
+ return gimp_rgb_parse_hex_internal (rgb, css);
+ }
+ else if (strncmp (css, "rgb(", 4) == 0 ||
+ strncmp (css, "hsl(", 4) == 0)
+ {
+ return gimp_rgb_parse_css_numeric (rgb, css);
+ }
+ else
+ {
+ return gimp_rgb_parse_name_internal (rgb, css);
+ }
+}
+
+static gboolean
+gimp_rgba_parse_css_internal (GimpRGB *rgba,
+ const gchar *css)
+{
+ if (strncmp (css, "rgba(", 5) != 0 &&
+ strncmp (css, "hsla(", 5) != 0)
+ return FALSE;
+
+ return gimp_rgb_parse_css_numeric (rgba, css);
+}
diff --git a/libgimpcolor/gimprgb.c b/libgimpcolor/gimprgb.c
new file mode 100644
index 0000000..b9ab4be
--- /dev/null
+++ b/libgimpcolor/gimprgb.c
@@ -0,0 +1,846 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <babl/babl.h>
+#include <glib-object.h>
+
+#define GIMP_DISABLE_DEPRECATION_WARNINGS /* for GIMP_RGB_INTENSITY() */
+#include "libgimpmath/gimpmath.h"
+
+#include "gimpcolortypes.h"
+
+#undef GIMP_DISABLE_DEPRECATED /* for GIMP_RGB_INTENSITY() */
+#include "gimprgb.h"
+
+
+/**
+ * SECTION: gimprgb
+ * @title: GimpRGB
+ * @short_description: Definitions and Functions relating to RGB colors.
+ *
+ * Definitions and Functions relating to RGB colors.
+ **/
+
+
+/*
+ * GIMP_TYPE_RGB
+ */
+
+static GimpRGB * gimp_rgb_copy (const GimpRGB *rgb);
+
+
+GType
+gimp_rgb_get_type (void)
+{
+ static GType rgb_type = 0;
+
+ if (!rgb_type)
+ rgb_type = g_boxed_type_register_static ("GimpRGB",
+ (GBoxedCopyFunc) gimp_rgb_copy,
+ (GBoxedFreeFunc) g_free);
+
+ return rgb_type;
+}
+
+void
+gimp_value_get_rgb (const GValue *value,
+ GimpRGB *rgb)
+{
+ g_return_if_fail (GIMP_VALUE_HOLDS_RGB (value));
+ g_return_if_fail (rgb != NULL);
+
+ if (value->data[0].v_pointer)
+ *rgb = *((GimpRGB *) value->data[0].v_pointer);
+ else
+ gimp_rgba_set (rgb, 0.0, 0.0, 0.0, 1.0);
+}
+
+void
+gimp_value_set_rgb (GValue *value,
+ const GimpRGB *rgb)
+{
+ g_return_if_fail (GIMP_VALUE_HOLDS_RGB (value));
+ g_return_if_fail (rgb != NULL);
+
+ g_value_set_boxed (value, rgb);
+}
+
+static GimpRGB *
+gimp_rgb_copy (const GimpRGB *rgb)
+{
+ return g_memdup (rgb, sizeof (GimpRGB));
+}
+
+
+/* RGB functions */
+
+/**
+ * gimp_rgb_set:
+ * @rgb: a #GimpRGB struct
+ * @red: the red component
+ * @green: the green component
+ * @blue: the blue component
+ *
+ * Sets the red, green and blue components of @rgb and leaves the
+ * alpha component unchanged. The color values should be between 0.0
+ * and 1.0 but there is no check to enforce this and the values are
+ * set exactly as they are passed in.
+ **/
+void
+gimp_rgb_set (GimpRGB *rgb,
+ gdouble r,
+ gdouble g,
+ gdouble b)
+{
+ g_return_if_fail (rgb != NULL);
+
+ rgb->r = r;
+ rgb->g = g;
+ rgb->b = b;
+}
+
+/**
+ * gimp_rgb_set_alpha:
+ * @rgb: a #GimpRGB struct
+ * @alpha: the alpha component
+ *
+ * Sets the alpha component of @rgb and leaves the RGB components unchanged.
+ **/
+void
+gimp_rgb_set_alpha (GimpRGB *rgb,
+ gdouble a)
+{
+ g_return_if_fail (rgb != NULL);
+
+ rgb->a = a;
+}
+
+/**
+ * gimp_rgb_set_pixel:
+ * @rgb: a #GimpRGB struct
+ * @format: a Babl format
+ * @pixel: pointer to the source pixel
+ *
+ * Sets the red, green and blue components of @rgb from the color
+ * stored in @pixel. The pixel format of @pixel is determined by
+ * @format.
+ *
+ * Since: 2.10
+ **/
+void
+gimp_rgb_set_pixel (GimpRGB *rgb,
+ const Babl *format,
+ gconstpointer pixel)
+{
+ g_return_if_fail (rgb != NULL);
+ g_return_if_fail (format != NULL);
+ g_return_if_fail (pixel != NULL);
+
+ babl_process (babl_fish (format,
+ babl_format ("R'G'B' double")),
+ pixel, rgb, 1);
+}
+
+/**
+ * gimp_rgb_get_pixel:
+ * @rgb: a #GimpRGB struct
+ * @format: a Babl format
+ * @pixel: pointer to the destination pixel
+ *
+ * Writes the red, green, blue and alpha components of @rgb to the
+ * color stored in @pixel. The pixel format of @pixel is determined by
+ * @format.
+ *
+ * Since: 2.10
+ **/
+void
+gimp_rgb_get_pixel (const GimpRGB *rgb,
+ const Babl *format,
+ gpointer pixel)
+{
+ g_return_if_fail (rgb != NULL);
+ g_return_if_fail (format != NULL);
+ g_return_if_fail (pixel != NULL);
+
+ babl_process (babl_fish (babl_format ("R'G'B' double"),
+ format),
+ rgb, pixel, 1);
+}
+
+/**
+ * gimp_rgb_set_uchar:
+ * @rgb: a #GimpRGB struct
+ * @red: the red component
+ * @green: the green component
+ * @blue: the blue component
+ *
+ * Sets the red, green and blue components of @rgb from 8bit values
+ * (0 to 255) and leaves the alpha component unchanged.
+ **/
+void
+gimp_rgb_set_uchar (GimpRGB *rgb,
+ guchar r,
+ guchar g,
+ guchar b)
+{
+ g_return_if_fail (rgb != NULL);
+
+ rgb->r = (gdouble) r / 255.0;
+ rgb->g = (gdouble) g / 255.0;
+ rgb->b = (gdouble) b / 255.0;
+}
+
+void
+gimp_rgb_get_uchar (const GimpRGB *rgb,
+ guchar *r,
+ guchar *g,
+ guchar *b)
+{
+ g_return_if_fail (rgb != NULL);
+
+ if (r) *r = ROUND (CLAMP (rgb->r, 0.0, 1.0) * 255.0);
+ if (g) *g = ROUND (CLAMP (rgb->g, 0.0, 1.0) * 255.0);
+ if (b) *b = ROUND (CLAMP (rgb->b, 0.0, 1.0) * 255.0);
+}
+
+void
+gimp_rgb_add (GimpRGB *rgb1,
+ const GimpRGB *rgb2)
+{
+ g_return_if_fail (rgb1 != NULL);
+ g_return_if_fail (rgb2 != NULL);
+
+ rgb1->r += rgb2->r;
+ rgb1->g += rgb2->g;
+ rgb1->b += rgb2->b;
+}
+
+void
+gimp_rgb_subtract (GimpRGB *rgb1,
+ const GimpRGB *rgb2)
+{
+ g_return_if_fail (rgb1 != NULL);
+ g_return_if_fail (rgb2 != NULL);
+
+ rgb1->r -= rgb2->r;
+ rgb1->g -= rgb2->g;
+ rgb1->b -= rgb2->b;
+}
+
+void
+gimp_rgb_multiply (GimpRGB *rgb,
+ gdouble factor)
+{
+ g_return_if_fail (rgb != NULL);
+
+ rgb->r *= factor;
+ rgb->g *= factor;
+ rgb->b *= factor;
+}
+
+gdouble
+gimp_rgb_distance (const GimpRGB *rgb1,
+ const GimpRGB *rgb2)
+{
+ g_return_val_if_fail (rgb1 != NULL, 0.0);
+ g_return_val_if_fail (rgb2 != NULL, 0.0);
+
+ return (fabs (rgb1->r - rgb2->r) +
+ fabs (rgb1->g - rgb2->g) +
+ fabs (rgb1->b - rgb2->b));
+}
+
+gdouble
+gimp_rgb_max (const GimpRGB *rgb)
+{
+ g_return_val_if_fail (rgb != NULL, 0.0);
+
+ if (rgb->r > rgb->g)
+ return (rgb->r > rgb->b) ? rgb->r : rgb->b;
+ else
+ return (rgb->g > rgb->b) ? rgb->g : rgb->b;
+}
+
+gdouble
+gimp_rgb_min (const GimpRGB *rgb)
+{
+ g_return_val_if_fail (rgb != NULL, 0.0);
+
+ if (rgb->r < rgb->g)
+ return (rgb->r < rgb->b) ? rgb->r : rgb->b;
+ else
+ return (rgb->g < rgb->b) ? rgb->g : rgb->b;
+}
+
+void
+gimp_rgb_clamp (GimpRGB *rgb)
+{
+ g_return_if_fail (rgb != NULL);
+
+ rgb->r = CLAMP (rgb->r, 0.0, 1.0);
+ rgb->g = CLAMP (rgb->g, 0.0, 1.0);
+ rgb->b = CLAMP (rgb->b, 0.0, 1.0);
+ rgb->a = CLAMP (rgb->a, 0.0, 1.0);
+}
+
+void
+gimp_rgb_gamma (GimpRGB *rgb,
+ gdouble gamma)
+{
+ gdouble ig;
+
+ g_return_if_fail (rgb != NULL);
+
+ if (gamma != 0.0)
+ ig = 1.0 / gamma;
+ else
+ ig = 0.0;
+
+ rgb->r = pow (rgb->r, ig);
+ rgb->g = pow (rgb->g, ig);
+ rgb->b = pow (rgb->b, ig);
+}
+
+/**
+ * gimp_rgb_luminance:
+ * @rgb: a #GimpRGB struct
+ *
+ * Return value: the luminous intensity of the range from 0.0 to 1.0.
+ *
+ * Since: 2.4
+ **/
+gdouble
+gimp_rgb_luminance (const GimpRGB *rgb)
+{
+ gdouble luminance;
+
+ g_return_val_if_fail (rgb != NULL, 0.0);
+
+ luminance = GIMP_RGB_LUMINANCE (rgb->r, rgb->g, rgb->b);
+
+ return CLAMP (luminance, 0.0, 1.0);
+}
+
+/**
+ * gimp_rgb_luminance_uchar:
+ * @rgb: a #GimpRGB struct
+ *
+ * Return value: the luminous intensity in the range from 0 to 255.
+ *
+ * Since: 2.4
+ **/
+guchar
+gimp_rgb_luminance_uchar (const GimpRGB *rgb)
+{
+ g_return_val_if_fail (rgb != NULL, 0);
+
+ return ROUND (gimp_rgb_luminance (rgb) * 255.0);
+}
+
+/**
+ * gimp_rgb_intensity:
+ * @rgb: a #GimpRGB struct
+ *
+ * This function is deprecated! Use gimp_rgb_luminance() instead.
+ *
+ * Return value: the intensity in the range from 0.0 to 1.0.
+ **/
+gdouble
+gimp_rgb_intensity (const GimpRGB *rgb)
+{
+ gdouble intensity;
+
+ g_return_val_if_fail (rgb != NULL, 0.0);
+
+ intensity = GIMP_RGB_INTENSITY (rgb->r, rgb->g, rgb->b);
+
+ return CLAMP (intensity, 0.0, 1.0);
+}
+
+/**
+ * gimp_rgb_intensity_uchar:
+ * @rgb: a #GimpRGB struct
+ *
+ * This function is deprecated! Use gimp_rgb_luminance_uchar() instead.
+ *
+ * Return value: the intensity in the range from 0 to 255.
+ **/
+guchar
+gimp_rgb_intensity_uchar (const GimpRGB *rgb)
+{
+ g_return_val_if_fail (rgb != NULL, 0);
+
+ return ROUND (gimp_rgb_intensity (rgb) * 255.0);
+}
+
+void
+gimp_rgb_composite (GimpRGB *color1,
+ const GimpRGB *color2,
+ GimpRGBCompositeMode mode)
+{
+ g_return_if_fail (color1 != NULL);
+ g_return_if_fail (color2 != NULL);
+
+ switch (mode)
+ {
+ case GIMP_RGB_COMPOSITE_NONE:
+ break;
+
+ case GIMP_RGB_COMPOSITE_NORMAL:
+ /* put color2 on top of color1 */
+ if (color2->a == 1.0)
+ {
+ *color1 = *color2;
+ }
+ else
+ {
+ gdouble factor = color1->a * (1.0 - color2->a);
+
+ color1->r = color1->r * factor + color2->r * color2->a;
+ color1->g = color1->g * factor + color2->g * color2->a;
+ color1->b = color1->b * factor + color2->b * color2->a;
+ color1->a = factor + color2->a;
+ }
+ break;
+
+ case GIMP_RGB_COMPOSITE_BEHIND:
+ /* put color2 below color1 */
+ if (color1->a < 1.0)
+ {
+ gdouble factor = color2->a * (1.0 - color1->a);
+
+ color1->r = color2->r * factor + color1->r * color1->a;
+ color1->g = color2->g * factor + color1->g * color1->a;
+ color1->b = color2->b * factor + color1->b * color1->a;
+ color1->a = factor + color1->a;
+ }
+ break;
+ }
+}
+
+/* RGBA functions */
+
+/**
+ * gimp_rgba_set_pixel:
+ * @rgba: a #GimpRGB struct
+ * @format: a Babl format
+ * @pixel: pointer to the source pixel
+ *
+ * Sets the red, green, blue and alpha components of @rgba from the
+ * color stored in @pixel. The pixel format of @pixel is determined
+ * by @format.
+ *
+ * Since: 2.10
+ **/
+void
+gimp_rgba_set_pixel (GimpRGB *rgba,
+ const Babl *format,
+ gconstpointer pixel)
+{
+ g_return_if_fail (rgba != NULL);
+ g_return_if_fail (format != NULL);
+ g_return_if_fail (pixel != NULL);
+
+ babl_process (babl_fish (format,
+ babl_format ("R'G'B'A double")),
+ pixel, rgba, 1);
+}
+
+/**
+ * gimp_rgba_get_pixel:
+ * @rgba: a #GimpRGB struct
+ * @format: a Babl format
+ * @pixel: pointer to the destination pixel
+ *
+ * Writes the red, green, blue and alpha components of @rgba to the
+ * color stored in @pixel. The pixel format of @pixel is determined by
+ * @format.
+ *
+ * Since: 2.10
+ **/
+void
+gimp_rgba_get_pixel (const GimpRGB *rgba,
+ const Babl *format,
+ gpointer pixel)
+{
+ g_return_if_fail (rgba != NULL);
+ g_return_if_fail (format != NULL);
+ g_return_if_fail (pixel != NULL);
+
+ babl_process (babl_fish (babl_format ("R'G'B'A double"),
+ format),
+ rgba, pixel, 1);
+}
+
+/**
+ * gimp_rgba_set:
+ * @rgba: a #GimpRGB struct
+ * @red: the red component
+ * @green: the green component
+ * @blue: the blue component
+ * @alpha: the alpha component
+ *
+ * Sets the red, green, blue and alpha components of @rgb. The values
+ * should be between 0.0 and 1.0 but there is no check to enforce this
+ * and the values are set exactly as they are passed in.
+ **/
+void
+gimp_rgba_set (GimpRGB *rgba,
+ gdouble r,
+ gdouble g,
+ gdouble b,
+ gdouble a)
+{
+ g_return_if_fail (rgba != NULL);
+
+ rgba->r = r;
+ rgba->g = g;
+ rgba->b = b;
+ rgba->a = a;
+}
+
+/**
+ * gimp_rgba_set_uchar:
+ * @rgba: a #GimpRGB struct
+ * @red: the red component
+ * @green: the green component
+ * @blue: the blue component
+ * @alpha: the alpha component
+ *
+ * Sets the red, green, blue and alpha components of @rgb from 8bit
+ * values (0 to 255).
+ **/
+void
+gimp_rgba_set_uchar (GimpRGB *rgba,
+ guchar r,
+ guchar g,
+ guchar b,
+ guchar a)
+{
+ g_return_if_fail (rgba != NULL);
+
+ rgba->r = (gdouble) r / 255.0;
+ rgba->g = (gdouble) g / 255.0;
+ rgba->b = (gdouble) b / 255.0;
+ rgba->a = (gdouble) a / 255.0;
+}
+
+void
+gimp_rgba_get_uchar (const GimpRGB *rgba,
+ guchar *r,
+ guchar *g,
+ guchar *b,
+ guchar *a)
+{
+ g_return_if_fail (rgba != NULL);
+
+ if (r) *r = ROUND (CLAMP (rgba->r, 0.0, 1.0) * 255.0);
+ if (g) *g = ROUND (CLAMP (rgba->g, 0.0, 1.0) * 255.0);
+ if (b) *b = ROUND (CLAMP (rgba->b, 0.0, 1.0) * 255.0);
+ if (a) *a = ROUND (CLAMP (rgba->a, 0.0, 1.0) * 255.0);
+}
+
+void
+gimp_rgba_add (GimpRGB *rgba1,
+ const GimpRGB *rgba2)
+{
+ g_return_if_fail (rgba1 != NULL);
+ g_return_if_fail (rgba2 != NULL);
+
+ rgba1->r += rgba2->r;
+ rgba1->g += rgba2->g;
+ rgba1->b += rgba2->b;
+ rgba1->a += rgba2->a;
+}
+
+void
+gimp_rgba_subtract (GimpRGB *rgba1,
+ const GimpRGB *rgba2)
+{
+ g_return_if_fail (rgba1 != NULL);
+ g_return_if_fail (rgba2 != NULL);
+
+ rgba1->r -= rgba2->r;
+ rgba1->g -= rgba2->g;
+ rgba1->b -= rgba2->b;
+ rgba1->a -= rgba2->a;
+}
+
+void
+gimp_rgba_multiply (GimpRGB *rgba,
+ gdouble factor)
+{
+ g_return_if_fail (rgba != NULL);
+
+ rgba->r *= factor;
+ rgba->g *= factor;
+ rgba->b *= factor;
+ rgba->a *= factor;
+}
+
+gdouble
+gimp_rgba_distance (const GimpRGB *rgba1,
+ const GimpRGB *rgba2)
+{
+ g_return_val_if_fail (rgba1 != NULL, 0.0);
+ g_return_val_if_fail (rgba2 != NULL, 0.0);
+
+ return (fabs (rgba1->r - rgba2->r) +
+ fabs (rgba1->g - rgba2->g) +
+ fabs (rgba1->b - rgba2->b) +
+ fabs (rgba1->a - rgba2->a));
+}
+
+
+/*
+ * GIMP_TYPE_PARAM_RGB
+ */
+
+#define GIMP_PARAM_SPEC_RGB(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_RGB, GimpParamSpecRGB))
+
+typedef struct _GimpParamSpecRGB GimpParamSpecRGB;
+
+struct _GimpParamSpecRGB
+{
+ GParamSpecBoxed parent_instance;
+
+ gboolean has_alpha;
+ gboolean validate; /* change this to enable [0.0...1.0] */
+ GimpRGB default_value;
+};
+
+static void gimp_param_rgb_class_init (GParamSpecClass *class);
+static void gimp_param_rgb_init (GParamSpec *pspec);
+static void gimp_param_rgb_set_default (GParamSpec *pspec,
+ GValue *value);
+static gboolean gimp_param_rgb_validate (GParamSpec *pspec,
+ GValue *value);
+static gint gimp_param_rgb_values_cmp (GParamSpec *pspec,
+ const GValue *value1,
+ const GValue *value2);
+
+/**
+ * gimp_param_rgb_get_type:
+ *
+ * Reveals the object type
+ *
+ * Returns: the #GType for a GimpParamRGB object
+ *
+ * Since: 2.4
+ **/
+GType
+gimp_param_rgb_get_type (void)
+{
+ static GType spec_type = 0;
+
+ if (! spec_type)
+ {
+ const GTypeInfo type_info =
+ {
+ sizeof (GParamSpecClass),
+ NULL, NULL,
+ (GClassInitFunc) gimp_param_rgb_class_init,
+ NULL, NULL,
+ sizeof (GimpParamSpecRGB),
+ 0,
+ (GInstanceInitFunc) gimp_param_rgb_init
+ };
+
+ spec_type = g_type_register_static (G_TYPE_PARAM_BOXED,
+ "GimpParamRGB",
+ &type_info, 0);
+ }
+
+ return spec_type;
+}
+
+static void
+gimp_param_rgb_class_init (GParamSpecClass *class)
+{
+ class->value_type = GIMP_TYPE_RGB;
+ class->value_set_default = gimp_param_rgb_set_default;
+ class->value_validate = gimp_param_rgb_validate;
+ class->values_cmp = gimp_param_rgb_values_cmp;
+}
+
+static void
+gimp_param_rgb_init (GParamSpec *pspec)
+{
+ GimpParamSpecRGB *cspec = GIMP_PARAM_SPEC_RGB (pspec);
+
+ gimp_rgba_set (&cspec->default_value, 0.0, 0.0, 0.0, 1.0);
+}
+
+static void
+gimp_param_rgb_set_default (GParamSpec *pspec,
+ GValue *value)
+{
+ GimpParamSpecRGB *cspec = GIMP_PARAM_SPEC_RGB (pspec);
+
+ g_value_set_static_boxed (value, &cspec->default_value);
+}
+
+static gboolean
+gimp_param_rgb_validate (GParamSpec *pspec,
+ GValue *value)
+{
+ GimpParamSpecRGB *rgb_spec = GIMP_PARAM_SPEC_RGB (pspec);
+ GimpRGB *rgb = value->data[0].v_pointer;
+
+ if (rgb_spec->validate && rgb)
+ {
+ GimpRGB oval = *rgb;
+
+ gimp_rgb_clamp (rgb);
+
+ return (oval.r != rgb->r ||
+ oval.g != rgb->g ||
+ oval.b != rgb->b ||
+ (rgb_spec->has_alpha && oval.a != rgb->a));
+ }
+
+ return FALSE;
+}
+
+static gint
+gimp_param_rgb_values_cmp (GParamSpec *pspec,
+ const GValue *value1,
+ const GValue *value2)
+{
+ GimpRGB *rgb1 = value1->data[0].v_pointer;
+ GimpRGB *rgb2 = value2->data[0].v_pointer;
+
+ /* try to return at least *something*, it's useless anyway... */
+
+ if (! rgb1)
+ {
+ return rgb2 != NULL ? -1 : 0;
+ }
+ else if (! rgb2)
+ {
+ return rgb1 != NULL;
+ }
+ else
+ {
+ guint32 int1 = 0;
+ guint32 int2 = 0;
+
+ if (GIMP_PARAM_SPEC_RGB (pspec)->has_alpha)
+ {
+ gimp_rgba_get_uchar (rgb1,
+ ((guchar *) &int1) + 0,
+ ((guchar *) &int1) + 1,
+ ((guchar *) &int1) + 2,
+ ((guchar *) &int1) + 3);
+ gimp_rgba_get_uchar (rgb2,
+ ((guchar *) &int2) + 0,
+ ((guchar *) &int2) + 1,
+ ((guchar *) &int2) + 2,
+ ((guchar *) &int2) + 3);
+ }
+ else
+ {
+ gimp_rgb_get_uchar (rgb1,
+ ((guchar *) &int1) + 0,
+ ((guchar *) &int1) + 1,
+ ((guchar *) &int1) + 2);
+ gimp_rgb_get_uchar (rgb2,
+ ((guchar *) &int2) + 0,
+ ((guchar *) &int2) + 1,
+ ((guchar *) &int2) + 2);
+ }
+
+ return int1 - int2;
+ }
+}
+
+/**
+ * gimp_param_spec_rgb:
+ * @name: Canonical name of the param
+ * @nick: Nickname of the param
+ * @blurb: Brief description of param.
+ * @has_alpha: %TRUE if the alpha channel has relevance.
+ * @default_value: Value to use if none is assigned.
+ * @flags: a combination of #GParamFlags
+ *
+ * Creates a param spec to hold an #GimpRGB value.
+ * See g_param_spec_internal() for more information.
+ *
+ * Returns: a newly allocated #GParamSpec instance
+ *
+ * Since: 2.4
+ **/
+GParamSpec *
+gimp_param_spec_rgb (const gchar *name,
+ const gchar *nick,
+ const gchar *blurb,
+ gboolean has_alpha,
+ const GimpRGB *default_value,
+ GParamFlags flags)
+{
+ GimpParamSpecRGB *cspec;
+
+ cspec = g_param_spec_internal (GIMP_TYPE_PARAM_RGB,
+ name, nick, blurb, flags);
+
+ cspec->has_alpha = has_alpha;
+
+ if (default_value)
+ cspec->default_value = *default_value;
+ else
+ gimp_rgba_set (&cspec->default_value, 0.0, 0.0, 0.0, 1.0);
+
+ return G_PARAM_SPEC (cspec);
+}
+
+/**
+ * gimp_param_spec_rgb_get_default:
+ * @pspec: a #GimpParamSpecRGB.
+ * @default_value: return location for @pspec's default value
+ *
+ * Returns the @pspec's default color value.
+ *
+ * Since: 2.10.14
+ **/
+void
+gimp_param_spec_rgb_get_default (GParamSpec *pspec,
+ GimpRGB *default_value)
+{
+ g_return_if_fail (GIMP_IS_PARAM_SPEC_RGB (pspec));
+ g_return_if_fail (default_value != NULL);
+
+ *default_value = GIMP_PARAM_SPEC_RGB (pspec)->default_value;
+}
+
+/**
+ * gimp_param_spec_rgb_has_alpha:
+ * @pspec: a #GParamSpec to hold an #GimpRGB value.
+ *
+ * Returns: %TRUE if the alpha channel is relevant.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_param_spec_rgb_has_alpha (GParamSpec *pspec)
+{
+ g_return_val_if_fail (GIMP_IS_PARAM_SPEC_RGB (pspec), FALSE);
+
+ return GIMP_PARAM_SPEC_RGB (pspec)->has_alpha;
+}
diff --git a/libgimpcolor/gimprgb.h b/libgimpcolor/gimprgb.h
new file mode 100644
index 0000000..089d97c
--- /dev/null
+++ b/libgimpcolor/gimprgb.h
@@ -0,0 +1,233 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_COLOR_H_INSIDE__) && !defined (GIMP_COLOR_COMPILATION)
+#error "Only <libgimpcolor/gimpcolor.h> can be included directly."
+#endif
+
+#ifndef __GIMP_RGB_H__
+#define __GIMP_RGB_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+/*
+ * GIMP_TYPE_RGB
+ */
+
+#define GIMP_TYPE_RGB (gimp_rgb_get_type ())
+#define GIMP_VALUE_HOLDS_RGB(value) (G_TYPE_CHECK_VALUE_TYPE ((value), GIMP_TYPE_RGB))
+
+GType gimp_rgb_get_type (void) G_GNUC_CONST;
+
+void gimp_value_get_rgb (const GValue *value,
+ GimpRGB *rgb);
+void gimp_value_set_rgb (GValue *value,
+ const GimpRGB *rgb);
+
+
+/*
+ * GIMP_TYPE_PARAM_RGB
+ */
+
+#define GIMP_TYPE_PARAM_RGB (gimp_param_rgb_get_type ())
+#define GIMP_IS_PARAM_SPEC_RGB(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_RGB))
+
+
+GType gimp_param_rgb_get_type (void) G_GNUC_CONST;
+
+GParamSpec * gimp_param_spec_rgb (const gchar *name,
+ const gchar *nick,
+ const gchar *blurb,
+ gboolean has_alpha,
+ const GimpRGB *default_value,
+ GParamFlags flags);
+
+void gimp_param_spec_rgb_get_default (GParamSpec *pspec,
+ GimpRGB *default_value);
+gboolean gimp_param_spec_rgb_has_alpha (GParamSpec *pspec);
+
+
+/* RGB and RGBA color types and operations taken from LibGCK */
+
+/**
+ * GimpRGBCompositeMode:
+ * @GIMP_RGB_COMPOSITE_NONE: don't do compositing
+ * @GIMP_RGB_COMPOSITE_NORMAL: composite on top
+ * @GIMP_RGB_COMPOSITE_BEHIND: composite behind
+ **/
+typedef enum
+{
+ GIMP_RGB_COMPOSITE_NONE = 0,
+ GIMP_RGB_COMPOSITE_NORMAL,
+ GIMP_RGB_COMPOSITE_BEHIND
+} GimpRGBCompositeMode;
+
+
+void gimp_rgb_set (GimpRGB *rgb,
+ gdouble red,
+ gdouble green,
+ gdouble blue);
+void gimp_rgb_set_alpha (GimpRGB *rgb,
+ gdouble alpha);
+
+void gimp_rgb_set_pixel (GimpRGB *rgb,
+ const Babl *format,
+ gconstpointer pixel);
+void gimp_rgb_get_pixel (const GimpRGB *rgb,
+ const Babl *format,
+ gpointer pixel);
+
+void gimp_rgb_set_uchar (GimpRGB *rgb,
+ guchar red,
+ guchar green,
+ guchar blue);
+void gimp_rgb_get_uchar (const GimpRGB *rgb,
+ guchar *red,
+ guchar *green,
+ guchar *blue);
+
+gboolean gimp_rgb_parse_name (GimpRGB *rgb,
+ const gchar *name,
+ gint len);
+gboolean gimp_rgb_parse_hex (GimpRGB *rgb,
+ const gchar *hex,
+ gint len);
+gboolean gimp_rgb_parse_css (GimpRGB *rgb,
+ const gchar *css,
+ gint len);
+
+void gimp_rgb_add (GimpRGB *rgb1,
+ const GimpRGB *rgb2);
+void gimp_rgb_subtract (GimpRGB *rgb1,
+ const GimpRGB *rgb2);
+void gimp_rgb_multiply (GimpRGB *rgb1,
+ gdouble factor);
+gdouble gimp_rgb_distance (const GimpRGB *rgb1,
+ const GimpRGB *rgb2);
+
+gdouble gimp_rgb_max (const GimpRGB *rgb);
+gdouble gimp_rgb_min (const GimpRGB *rgb);
+void gimp_rgb_clamp (GimpRGB *rgb);
+
+void gimp_rgb_gamma (GimpRGB *rgb,
+ gdouble gamma);
+
+gdouble gimp_rgb_luminance (const GimpRGB *rgb);
+guchar gimp_rgb_luminance_uchar (const GimpRGB *rgb);
+
+GIMP_DEPRECATED_FOR(gimp_rgb_luminance)
+gdouble gimp_rgb_intensity (const GimpRGB *rgb);
+GIMP_DEPRECATED_FOR(gimp_rgb_luminance_uchar)
+guchar gimp_rgb_intensity_uchar (const GimpRGB *rgb);
+
+void gimp_rgb_composite (GimpRGB *color1,
+ const GimpRGB *color2,
+ GimpRGBCompositeMode mode);
+
+/* access to the list of color names */
+gint gimp_rgb_list_names (const gchar ***names,
+ GimpRGB **colors);
+
+
+void gimp_rgba_set (GimpRGB *rgba,
+ gdouble red,
+ gdouble green,
+ gdouble blue,
+ gdouble alpha);
+
+void gimp_rgba_set_pixel (GimpRGB *rgba,
+ const Babl *format,
+ gconstpointer pixel);
+void gimp_rgba_get_pixel (const GimpRGB *rgba,
+ const Babl *format,
+ gpointer pixel);
+
+void gimp_rgba_set_uchar (GimpRGB *rgba,
+ guchar red,
+ guchar green,
+ guchar blue,
+ guchar alpha);
+void gimp_rgba_get_uchar (const GimpRGB *rgba,
+ guchar *red,
+ guchar *green,
+ guchar *blue,
+ guchar *alpha);
+
+gboolean gimp_rgba_parse_css (GimpRGB *rgba,
+ const gchar *css,
+ gint len);
+
+void gimp_rgba_add (GimpRGB *rgba1,
+ const GimpRGB *rgba2);
+void gimp_rgba_subtract (GimpRGB *rgba1,
+ const GimpRGB *rgba2);
+void gimp_rgba_multiply (GimpRGB *rgba,
+ gdouble factor);
+
+gdouble gimp_rgba_distance (const GimpRGB *rgba1,
+ const GimpRGB *rgba2);
+
+
+
+/* Map D50-adapted sRGB to luminance */
+
+/*
+ * The weights to compute true CIE luminance from linear red, green
+ * and blue as defined by the sRGB color space specs in an ICC profile
+ * color managed application. The weights below have been chromatically
+ * adapted from D65 (as specified by the sRGB color space specs)
+ * to D50 (as specified by D50 illuminant values in the ICC V4 specs).
+ */
+
+#define GIMP_RGB_LUMINANCE_RED (0.22248840)
+#define GIMP_RGB_LUMINANCE_GREEN (0.71690369)
+#define GIMP_RGB_LUMINANCE_BLUE (0.06060791)
+
+#define GIMP_RGB_LUMINANCE(r,g,b) ((r) * GIMP_RGB_LUMINANCE_RED + \
+ (g) * GIMP_RGB_LUMINANCE_GREEN + \
+ (b) * GIMP_RGB_LUMINANCE_BLUE)
+
+
+#ifndef GIMP_DISABLE_DEPRECATED
+
+/*
+ * The coefficients below properly computed luminance for monitors
+ * having phosphors that were contemporary at the introduction of NTSC
+ * television in 1953. They are still appropriate for computing video
+ * luma. However, these coefficients do not accurately compute
+ * luminance for contemporary monitors. The use of these definitions
+ * is deprecated.
+ */
+
+#define GIMP_RGB_INTENSITY_RED (0.30)
+#define GIMP_RGB_INTENSITY_GREEN (0.59)
+#define GIMP_RGB_INTENSITY_BLUE (0.11)
+
+#define GIMP_RGB_INTENSITY(r,g,b) ((r) * GIMP_RGB_INTENSITY_RED + \
+ (g) * GIMP_RGB_INTENSITY_GREEN + \
+ (b) * GIMP_RGB_INTENSITY_BLUE)
+
+#endif
+
+
+G_END_DECLS
+
+#endif /* __GIMP_RGB_H__ */
diff --git a/libgimpcolor/test-color-parser.c b/libgimpcolor/test-color-parser.c
new file mode 100644
index 0000000..4ce7c50
--- /dev/null
+++ b/libgimpcolor/test-color-parser.c
@@ -0,0 +1,119 @@
+/* unit tests for the color parsing routines in gimprgb-parse.c
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <babl/babl.h>
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include <glib-object.h>
+#include <cairo.h>
+
+#include "gimpcolor.h"
+
+
+#define DBL(c) ((gdouble)(c) / 255.0)
+
+
+typedef struct
+{
+ const gchar *str;
+ gboolean alpha;
+ gboolean fail;
+ const gdouble r;
+ const gdouble g;
+ const gdouble b;
+ const gdouble a;
+} ColorSample;
+
+static const ColorSample samples[] =
+{
+ /* sample alpha fail red green blue alpha */
+
+ { "#000000", FALSE, FALSE, 0.0, 0.0, 0.0, 0.0 },
+ { "#FFff00", FALSE, FALSE, 1.0, 1.0, 0.0, 0.0 },
+ { "#6495ed", FALSE, FALSE, DBL(100), DBL(149), DBL(237), 0.0 },
+ { "#fff", FALSE, FALSE, 1.0, 1.0, 1.0, 0.0 },
+ { "#64649595eded", FALSE, FALSE, 1.0, 1.0, 0.0, 0.0 },
+ { "rgb(0,0,0)", FALSE, FALSE, 0.0, 0.0, 0.0, 0.0 },
+ { "rgb(100,149,237)", FALSE, FALSE, DBL(100), DBL(149), DBL(237), 0.0 },
+ { "rgba(100%,0,100%,0.5)", TRUE, FALSE, 255.0, 0.0, 255.0, 0.5 },
+ { "rgba(100%,0,100%,0.5)", FALSE, TRUE, 255.0, 0.0, 255.0, 0.5 },
+ { "rgb(100%,149,20%)", FALSE, FALSE, 1.0, DBL(149), 0.2, 0.0 },
+ { "rgb(100%,149,20%)", TRUE, TRUE, 1.0, DBL(149), 0.2, 0.0 },
+ { "rgb(foobar)", FALSE, TRUE, 0.0, 0.0, 0.0, 0.0 },
+ { "rgb(100,149,237", FALSE, TRUE, 0.0, 0.0, 0.0, 0.0 },
+ { "rED", FALSE, FALSE, 1.0, 0.0, 0.0, 0.0 },
+ { "cornflowerblue", FALSE, FALSE, DBL(100), DBL(149), DBL(237), 0.0 },
+ { " red", FALSE, FALSE, 1.0, 0.0, 0.0, 0.0 },
+ { "red ", FALSE, FALSE, 1.0, 0.0, 0.0, 0.0 },
+ { "red", TRUE, TRUE, 1.0, 0.0, 0.0, 0.0 },
+ { "red blue", FALSE, TRUE, 0.0, 0.0, 0.0, 0.0 },
+ { "transparent", FALSE, TRUE, 0.0, 0.0, 0.0, 0.0 },
+ { "transparent", TRUE, FALSE, 0.0, 0.0, 0.0, 0.0 },
+ { "23foobar", FALSE, TRUE, 0.0, 0.0, 0.0, 0.0 },
+ { "", FALSE, TRUE, 0.0, 0.0, 0.0, 0.0 }
+};
+
+
+static gint
+check_failure (const ColorSample *sample,
+ gboolean success,
+ GimpRGB *rgb)
+{
+ if (success && sample->fail)
+ {
+ g_print ("Parser succeeded for sample \"%s\" but should have failed!\n"
+ " parsed color: (%g, %g, %g, %g)\n",
+ sample->str, rgb->r, rgb->g, rgb->b, rgb->a);
+ return 1;
+ }
+
+ if (!success && !sample->fail)
+ {
+ g_print ("Parser failed for sample \"%s\" but should have succeeded!\n"
+ " parsed color: (%g, %g, %g, %g)\n",
+ sample->str, rgb->r, rgb->g, rgb->b, rgb->a);
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+main (void)
+{
+ gint failures = 0;
+ gint i;
+
+ g_print ("\nTesting the GIMP color parser ...\n");
+
+ for (i = 0; i < G_N_ELEMENTS (samples); i++)
+ {
+ GimpRGB rgb = { 0.0, 0.0, 0.0, 0.0 };
+ gboolean success;
+
+ if (samples[i].alpha)
+ success = gimp_rgba_parse_css (&rgb, samples[i].str, -1);
+ else
+ success = gimp_rgb_parse_css (&rgb, samples[i].str, -1);
+
+ failures += check_failure (samples + i, success, &rgb);
+ }
+
+ if (failures)
+ {
+ g_print ("%d out of %d samples failed!\n\n",
+ failures, (int)G_N_ELEMENTS (samples));
+ return EXIT_FAILURE;
+ }
+ else
+ {
+ g_print ("All %d samples passed.\n\n", (int)G_N_ELEMENTS (samples));
+ return EXIT_SUCCESS;
+ }
+}
+
diff --git a/libgimpconfig/Makefile.am b/libgimpconfig/Makefile.am
new file mode 100644
index 0000000..40d30ca
--- /dev/null
+++ b/libgimpconfig/Makefile.am
@@ -0,0 +1,163 @@
+## Process this file with automake to produce Makefile.in
+
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+
+if PLATFORM_WIN32
+no_undefined = -no-undefined
+endif
+
+if OS_WIN32
+gimpconfig_def = gimpconfig.def
+libgimpconfig_export_symbols = -export-symbols $(srcdir)/gimpconfig.def
+
+install-libtool-import-lib:
+ $(INSTALL) .libs/libgimpconfig-$(GIMP_API_VERSION).dll.a $(DESTDIR)$(libdir)
+ $(INSTALL) $(srcdir)/gimpconfig.def $(DESTDIR)$(libdir)
+
+uninstall-libtool-import-lib:
+ -rm $(DESTDIR)$(libdir)/libgimpconfig-$(GIMP_API_VERSION).dll.a
+ -rm $(DESTDIR)$(libdir)/gimpconfig.def
+else
+install-libtool-import-lib:
+uninstall-libtool-import-lib:
+endif
+
+if MS_LIB_AVAILABLE
+noinst_DATA = gimpconfig-$(GIMP_API_VERSION).lib
+
+install-ms-lib:
+ $(INSTALL) gimpconfig-$(GIMP_API_VERSION).lib $(DESTDIR)$(libdir)
+
+uninstall-ms-lib:
+ -rm $(DESTDIR)$(libdir)/gimpconfig-$(GIMP_API_VERSION).lib
+
+gimpconfig-@GIMP_API_VERSION@.lib: gimpconfig.def
+ lib -name:libgimpconfig-$(GIMP_API_VERSION)-@LT_CURRENT_MINUS_AGE@.dll -def:gimpconfig.def -out:$@
+
+else
+install-ms-lib:
+uninstall-ms-lib:
+endif
+
+libgimpconfigincludedir = $(includedir)/gimp-$(GIMP_API_VERSION)/libgimpconfig
+
+AM_CPPFLAGS = \
+ -DG_LOG_DOMAIN=\"LibGimpConfig\" \
+ -DGIMP_CONFIG_COMPILATION \
+ -I$(top_srcdir) \
+ $(GIO_UNIX_CFLAGS) \
+ $(GIO_WINDOWS_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(CAIRO_CFLAGS) \
+ $(GDK_PIXBUF_CFLAGS) \
+ -I$(includedir)
+
+EXTRA_DIST = \
+ gimpconfig.def
+
+lib_LTLIBRARIES = libgimpconfig-@GIMP_API_VERSION@.la
+
+libgimpconfig_sources = \
+ gimpconfig.h \
+ gimpconfigenums.h \
+ gimpconfigtypes.h \
+ \
+ gimpconfig-iface.c \
+ gimpconfig-iface.h \
+ gimpconfig-deserialize.c \
+ gimpconfig-deserialize.h \
+ gimpconfig-error.c \
+ gimpconfig-error.h \
+ gimpconfig-params.h \
+ gimpconfig-path.c \
+ gimpconfig-path.h \
+ gimpconfig-serialize.c \
+ gimpconfig-serialize.h \
+ gimpconfig-utils.c \
+ gimpconfig-utils.h \
+ gimpconfigwriter.c \
+ gimpconfigwriter.h \
+ gimpscanner.c \
+ gimpscanner.h \
+ gimpcolorconfig.c \
+ gimpcolorconfig.h
+
+libgimpconfig_built_sources = \
+ gimpconfigenums.c
+
+libgimpconfig_@GIMP_API_VERSION@_la_SOURCES = \
+ $(libgimpconfig_sources) \
+ $(libgimpconfig_built_sources)
+
+
+libgimpconfiginclude_HEADERS = \
+ gimpconfig.h \
+ gimpconfigenums.h \
+ gimpconfigtypes.h \
+ gimpconfig-iface.h \
+ gimpconfig-deserialize.h \
+ gimpconfig-error.h \
+ gimpconfig-params.h \
+ gimpconfig-path.h \
+ gimpconfig-serialize.h \
+ gimpconfig-utils.h \
+ gimpconfigwriter.h \
+ gimpscanner.h \
+ gimpcolorconfig.h
+
+
+libgimpconfig_@GIMP_API_VERSION@_la_LDFLAGS = \
+ -version-info $(LT_VERSION_INFO) \
+ $(no_undefined) \
+ $(libgimpconfig_export_symbols)
+
+EXTRA_libgimpconfig_@GIMP_API_VERSION@_la_DEPENDENCIES = $(gimpconfig_def)
+
+libgimpconfig_@GIMP_API_VERSION@_la_LIBADD = \
+ $(libgimpbase) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(GIO_UNIX_LIBS) \
+ $(GIO_WINDOWS_LIBS) \
+ $(GEGL_LIBS) \
+ $(CAIRO_LIBS) \
+ $(GDK_PIXBUF_LIBS)
+
+
+install-data-local: install-ms-lib install-libtool-import-lib
+
+uninstall-local: uninstall-ms-lib uninstall-libtool-import-lib
+
+
+#
+# rules to generate built sources
+#
+
+gen_sources = xgen-cec
+CLEANFILES = $(gen_sources)
+
+xgen-cec: $(srcdir)/gimpconfigenums.h $(GIMP_MKENUMS) Makefile.am
+ $(AM_V_GEN) $(GIMP_MKENUMS) \
+ --fhead "#include \"config.h\"\n#include <gio/gio.h>\n#include \"libgimpbase/gimpbase.h\"\n#include \"gimpconfigenums.h\"\n#include \"libgimp/libgimp-intl.h\"" \
+ --fprod "\n/* enumerations from \"@basename@\" */" \
+ --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \
+ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \
+ --vtail " { 0, NULL, NULL }\n };\n" \
+ --dhead " static const Gimp@Type@Desc descs[] =\n {" \
+ --dprod " { @VALUENAME@, @valuedesc@, @valuehelp@ },@if ('@valueabbrev@' ne 'NULL')@\n /* Translators: this is an abbreviated version of @valueudesc@.\n Keep it short. */\n { @VALUENAME@, @valueabbrev@, NULL },@endif@" \
+ --dtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n {\n type = g_@type@_register_static (\"@EnumName@\", values);\n gimp_type_set_translation_domain (type, GETTEXT_PACKAGE \"-libgimp\");\n gimp_type_set_translation_context (type, \"@enumnick@\");\n gimp_@type@_set_value_descriptions (type, descs);\n }\n\n return type;\n}\n" \
+ $< > $@
+
+# copy the generated enum file back to the source directory only if it's
+# changed; otherwise, only update its timestamp, so that the recipe isn't
+# executed again on the next build, however, allow this to (harmlessly) fail,
+# to support building from a read-only source tree.
+$(srcdir)/gimpconfigenums.c: xgen-cec
+ $(AM_V_GEN) if ! cmp -s $< $@; then \
+ cp $< $@; \
+ else \
+ touch $@ 2> /dev/null \
+ || true; \
+ fi
diff --git a/libgimpconfig/Makefile.in b/libgimpconfig/Makefile.in
new file mode 100644
index 0000000..4afaa42
--- /dev/null
+++ b/libgimpconfig/Makefile.in
@@ -0,0 +1,1179 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = libgimpconfig
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(libgimpconfiginclude_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(libdir)" \
+ "$(DESTDIR)$(libgimpconfigincludedir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libgimpconfig_@GIMP_API_VERSION@_la_DEPENDENCIES = $(libgimpbase) \
+ $(libgimpcolor) $(libgimpmath) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am__objects_1 = gimpconfig-iface.lo gimpconfig-deserialize.lo \
+ gimpconfig-error.lo gimpconfig-path.lo gimpconfig-serialize.lo \
+ gimpconfig-utils.lo gimpconfigwriter.lo gimpscanner.lo \
+ gimpcolorconfig.lo
+am__objects_2 = gimpconfigenums.lo
+am_libgimpconfig_@GIMP_API_VERSION@_la_OBJECTS = $(am__objects_1) \
+ $(am__objects_2)
+libgimpconfig_@GIMP_API_VERSION@_la_OBJECTS = \
+ $(am_libgimpconfig_@GIMP_API_VERSION@_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libgimpconfig_@GIMP_API_VERSION@_la_LINK = $(LIBTOOL) $(AM_V_lt) \
+ --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libgimpconfig_@GIMP_API_VERSION@_la_LDFLAGS) $(LDFLAGS) -o \
+ $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/gimpcolorconfig.Plo \
+ ./$(DEPDIR)/gimpconfig-deserialize.Plo \
+ ./$(DEPDIR)/gimpconfig-error.Plo \
+ ./$(DEPDIR)/gimpconfig-iface.Plo \
+ ./$(DEPDIR)/gimpconfig-path.Plo \
+ ./$(DEPDIR)/gimpconfig-serialize.Plo \
+ ./$(DEPDIR)/gimpconfig-utils.Plo \
+ ./$(DEPDIR)/gimpconfigenums.Plo \
+ ./$(DEPDIR)/gimpconfigwriter.Plo ./$(DEPDIR)/gimpscanner.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libgimpconfig_@GIMP_API_VERSION@_la_SOURCES)
+DIST_SOURCES = $(libgimpconfig_@GIMP_API_VERSION@_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+DATA = $(noinst_DATA)
+HEADERS = $(libgimpconfiginclude_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+@PLATFORM_WIN32_TRUE@no_undefined = -no-undefined
+@OS_WIN32_TRUE@gimpconfig_def = gimpconfig.def
+@OS_WIN32_TRUE@libgimpconfig_export_symbols = -export-symbols $(srcdir)/gimpconfig.def
+@MS_LIB_AVAILABLE_TRUE@noinst_DATA = gimpconfig-$(GIMP_API_VERSION).lib
+libgimpconfigincludedir = $(includedir)/gimp-$(GIMP_API_VERSION)/libgimpconfig
+AM_CPPFLAGS = \
+ -DG_LOG_DOMAIN=\"LibGimpConfig\" \
+ -DGIMP_CONFIG_COMPILATION \
+ -I$(top_srcdir) \
+ $(GIO_UNIX_CFLAGS) \
+ $(GIO_WINDOWS_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(CAIRO_CFLAGS) \
+ $(GDK_PIXBUF_CFLAGS) \
+ -I$(includedir)
+
+EXTRA_DIST = \
+ gimpconfig.def
+
+lib_LTLIBRARIES = libgimpconfig-@GIMP_API_VERSION@.la
+libgimpconfig_sources = \
+ gimpconfig.h \
+ gimpconfigenums.h \
+ gimpconfigtypes.h \
+ \
+ gimpconfig-iface.c \
+ gimpconfig-iface.h \
+ gimpconfig-deserialize.c \
+ gimpconfig-deserialize.h \
+ gimpconfig-error.c \
+ gimpconfig-error.h \
+ gimpconfig-params.h \
+ gimpconfig-path.c \
+ gimpconfig-path.h \
+ gimpconfig-serialize.c \
+ gimpconfig-serialize.h \
+ gimpconfig-utils.c \
+ gimpconfig-utils.h \
+ gimpconfigwriter.c \
+ gimpconfigwriter.h \
+ gimpscanner.c \
+ gimpscanner.h \
+ gimpcolorconfig.c \
+ gimpcolorconfig.h
+
+libgimpconfig_built_sources = \
+ gimpconfigenums.c
+
+libgimpconfig_@GIMP_API_VERSION@_la_SOURCES = \
+ $(libgimpconfig_sources) \
+ $(libgimpconfig_built_sources)
+
+libgimpconfiginclude_HEADERS = \
+ gimpconfig.h \
+ gimpconfigenums.h \
+ gimpconfigtypes.h \
+ gimpconfig-iface.h \
+ gimpconfig-deserialize.h \
+ gimpconfig-error.h \
+ gimpconfig-params.h \
+ gimpconfig-path.h \
+ gimpconfig-serialize.h \
+ gimpconfig-utils.h \
+ gimpconfigwriter.h \
+ gimpscanner.h \
+ gimpcolorconfig.h
+
+libgimpconfig_@GIMP_API_VERSION@_la_LDFLAGS = \
+ -version-info $(LT_VERSION_INFO) \
+ $(no_undefined) \
+ $(libgimpconfig_export_symbols)
+
+EXTRA_libgimpconfig_@GIMP_API_VERSION@_la_DEPENDENCIES = $(gimpconfig_def)
+libgimpconfig_@GIMP_API_VERSION@_la_LIBADD = \
+ $(libgimpbase) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(GIO_UNIX_LIBS) \
+ $(GIO_WINDOWS_LIBS) \
+ $(GEGL_LIBS) \
+ $(CAIRO_LIBS) \
+ $(GDK_PIXBUF_LIBS)
+
+
+#
+# rules to generate built sources
+#
+gen_sources = xgen-cec
+CLEANFILES = $(gen_sources)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu libgimpconfig/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu libgimpconfig/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libgimpconfig-@GIMP_API_VERSION@.la: $(libgimpconfig_@GIMP_API_VERSION@_la_OBJECTS) $(libgimpconfig_@GIMP_API_VERSION@_la_DEPENDENCIES) $(EXTRA_libgimpconfig_@GIMP_API_VERSION@_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libgimpconfig_@GIMP_API_VERSION@_la_LINK) -rpath $(libdir) $(libgimpconfig_@GIMP_API_VERSION@_la_OBJECTS) $(libgimpconfig_@GIMP_API_VERSION@_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcolorconfig.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpconfig-deserialize.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpconfig-error.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpconfig-iface.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpconfig-path.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpconfig-serialize.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpconfig-utils.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpconfigenums.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpconfigwriter.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpscanner.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-libgimpconfigincludeHEADERS: $(libgimpconfiginclude_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(libgimpconfiginclude_HEADERS)'; test -n "$(libgimpconfigincludedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libgimpconfigincludedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libgimpconfigincludedir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(libgimpconfigincludedir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(libgimpconfigincludedir)" || exit $$?; \
+ done
+
+uninstall-libgimpconfigincludeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libgimpconfiginclude_HEADERS)'; test -n "$(libgimpconfigincludedir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(libgimpconfigincludedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(DATA) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libgimpconfigincludedir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/gimpcolorconfig.Plo
+ -rm -f ./$(DEPDIR)/gimpconfig-deserialize.Plo
+ -rm -f ./$(DEPDIR)/gimpconfig-error.Plo
+ -rm -f ./$(DEPDIR)/gimpconfig-iface.Plo
+ -rm -f ./$(DEPDIR)/gimpconfig-path.Plo
+ -rm -f ./$(DEPDIR)/gimpconfig-serialize.Plo
+ -rm -f ./$(DEPDIR)/gimpconfig-utils.Plo
+ -rm -f ./$(DEPDIR)/gimpconfigenums.Plo
+ -rm -f ./$(DEPDIR)/gimpconfigwriter.Plo
+ -rm -f ./$(DEPDIR)/gimpscanner.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-data-local \
+ install-libgimpconfigincludeHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/gimpcolorconfig.Plo
+ -rm -f ./$(DEPDIR)/gimpconfig-deserialize.Plo
+ -rm -f ./$(DEPDIR)/gimpconfig-error.Plo
+ -rm -f ./$(DEPDIR)/gimpconfig-iface.Plo
+ -rm -f ./$(DEPDIR)/gimpconfig-path.Plo
+ -rm -f ./$(DEPDIR)/gimpconfig-serialize.Plo
+ -rm -f ./$(DEPDIR)/gimpconfig-utils.Plo
+ -rm -f ./$(DEPDIR)/gimpconfigenums.Plo
+ -rm -f ./$(DEPDIR)/gimpconfigwriter.Plo
+ -rm -f ./$(DEPDIR)/gimpscanner.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libLTLIBRARIES \
+ uninstall-libgimpconfigincludeHEADERS uninstall-local
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libLTLIBRARIES clean-libtool cscopelist-am \
+ ctags ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-data-local install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libLTLIBRARIES \
+ install-libgimpconfigincludeHEADERS install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am uninstall-libLTLIBRARIES \
+ uninstall-libgimpconfigincludeHEADERS uninstall-local
+
+.PRECIOUS: Makefile
+
+
+@OS_WIN32_TRUE@install-libtool-import-lib:
+@OS_WIN32_TRUE@ $(INSTALL) .libs/libgimpconfig-$(GIMP_API_VERSION).dll.a $(DESTDIR)$(libdir)
+@OS_WIN32_TRUE@ $(INSTALL) $(srcdir)/gimpconfig.def $(DESTDIR)$(libdir)
+
+@OS_WIN32_TRUE@uninstall-libtool-import-lib:
+@OS_WIN32_TRUE@ -rm $(DESTDIR)$(libdir)/libgimpconfig-$(GIMP_API_VERSION).dll.a
+@OS_WIN32_TRUE@ -rm $(DESTDIR)$(libdir)/gimpconfig.def
+@OS_WIN32_FALSE@install-libtool-import-lib:
+@OS_WIN32_FALSE@uninstall-libtool-import-lib:
+
+@MS_LIB_AVAILABLE_TRUE@install-ms-lib:
+@MS_LIB_AVAILABLE_TRUE@ $(INSTALL) gimpconfig-$(GIMP_API_VERSION).lib $(DESTDIR)$(libdir)
+
+@MS_LIB_AVAILABLE_TRUE@uninstall-ms-lib:
+@MS_LIB_AVAILABLE_TRUE@ -rm $(DESTDIR)$(libdir)/gimpconfig-$(GIMP_API_VERSION).lib
+
+@MS_LIB_AVAILABLE_TRUE@gimpconfig-@GIMP_API_VERSION@.lib: gimpconfig.def
+@MS_LIB_AVAILABLE_TRUE@ lib -name:libgimpconfig-$(GIMP_API_VERSION)-@LT_CURRENT_MINUS_AGE@.dll -def:gimpconfig.def -out:$@
+
+@MS_LIB_AVAILABLE_FALSE@install-ms-lib:
+@MS_LIB_AVAILABLE_FALSE@uninstall-ms-lib:
+
+install-data-local: install-ms-lib install-libtool-import-lib
+
+uninstall-local: uninstall-ms-lib uninstall-libtool-import-lib
+
+xgen-cec: $(srcdir)/gimpconfigenums.h $(GIMP_MKENUMS) Makefile.am
+ $(AM_V_GEN) $(GIMP_MKENUMS) \
+ --fhead "#include \"config.h\"\n#include <gio/gio.h>\n#include \"libgimpbase/gimpbase.h\"\n#include \"gimpconfigenums.h\"\n#include \"libgimp/libgimp-intl.h\"" \
+ --fprod "\n/* enumerations from \"@basename@\" */" \
+ --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \
+ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \
+ --vtail " { 0, NULL, NULL }\n };\n" \
+ --dhead " static const Gimp@Type@Desc descs[] =\n {" \
+ --dprod " { @VALUENAME@, @valuedesc@, @valuehelp@ },@if ('@valueabbrev@' ne 'NULL')@\n /* Translators: this is an abbreviated version of @valueudesc@.\n Keep it short. */\n { @VALUENAME@, @valueabbrev@, NULL },@endif@" \
+ --dtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n {\n type = g_@type@_register_static (\"@EnumName@\", values);\n gimp_type_set_translation_domain (type, GETTEXT_PACKAGE \"-libgimp\");\n gimp_type_set_translation_context (type, \"@enumnick@\");\n gimp_@type@_set_value_descriptions (type, descs);\n }\n\n return type;\n}\n" \
+ $< > $@
+
+# copy the generated enum file back to the source directory only if it's
+# changed; otherwise, only update its timestamp, so that the recipe isn't
+# executed again on the next build, however, allow this to (harmlessly) fail,
+# to support building from a read-only source tree.
+$(srcdir)/gimpconfigenums.c: xgen-cec
+ $(AM_V_GEN) if ! cmp -s $< $@; then \
+ cp $< $@; \
+ else \
+ touch $@ 2> /dev/null \
+ || true; \
+ fi
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libgimpconfig/gimpcolorconfig.c b/libgimpconfig/gimpcolorconfig.c
new file mode 100644
index 0000000..ccb8476
--- /dev/null
+++ b/libgimpconfig/gimpcolorconfig.c
@@ -0,0 +1,1087 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpColorConfig class
+ * Copyright (C) 2004 Stefan Döhla <stefan@doehla.de>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <cairo.h>
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpcolor/gimpcolor.h"
+
+#include "gimpconfigtypes.h"
+
+#include "gimpcolorconfig.h"
+#include "gimpconfig-error.h"
+#include "gimpconfig-iface.h"
+#include "gimpconfig-params.h"
+#include "gimpconfig-path.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpcolorconfig
+ * @title: GimpColorConfig
+ * @short_description: Color management settings.
+ *
+ * Color management settings.
+ **/
+
+
+#define COLOR_MANAGEMENT_MODE_BLURB \
+ _("How images are displayed on screen.")
+
+#define DISPLAY_PROFILE_BLURB \
+ _("The color profile of your (primary) monitor.")
+
+#define DISPLAY_PROFILE_FROM_GDK_BLURB \
+ _("When enabled, GIMP will try to use the display color profile from " \
+ "the windowing system. The configured monitor profile is then only " \
+ "used as a fallback.")
+
+#define RGB_PROFILE_BLURB \
+ _("The preferred RGB working space color profile. It will be offered " \
+ "next to the built-in RGB profile when a color profile can be chosen.")
+
+#define GRAY_PROFILE_BLURB \
+ _("The preferred grayscale working space color profile. It will be offered " \
+ "next to the built-in grayscale profile when a color profile can be chosen.")
+
+#define CMYK_PROFILE_BLURB \
+ _("The CMYK color profile used to convert between RGB and CMYK.")
+
+#define SIMULATION_PROFILE_BLURB \
+ _("The color profile to use for soft-proofing from your image's " \
+ "color space to some other color space, including " \
+ "soft-proofing to a printer or other output device profile. ")
+
+#define DISPLAY_RENDERING_INTENT_BLURB \
+ _("How colors are converted from your image's color space to your " \
+ "display device. Relative colorimetric is usually the best choice. " \
+ "Unless you use a LUT monitor profile (most monitor profiles are " \
+ "matrix), choosing perceptual intent really gives you relative " \
+ "colorimetric." )
+
+#define DISPLAY_USE_BPC_BLURB \
+ _("Do use black point compensation (unless you know you have a reason " \
+ "not to).")
+
+#define DISPLAY_OPTIMIZE_BLURB \
+ _("When disabled, image display might be of better quality " \
+ "at the cost of speed.")
+
+#define SIMULATION_RENDERING_INTENT_BLURB \
+ _("How colors are converted from your image's color space to the " \
+ "output simulation device (usually your monitor). " \
+ "Try them all and choose what looks the best. ")
+
+#define SIMULATION_USE_BPC_BLURB \
+ _("Try with and without black point compensation "\
+ "and choose what looks best. ")
+
+#define SIMULATION_OPTIMIZE_BLURB \
+ _("When disabled, soft-proofing might be of better quality " \
+ "at the cost of speed.")
+
+#define SIMULATION_GAMUT_CHECK_BLURB \
+ _("When enabled, the soft-proofing will mark colors " \
+ "which can not be represented in the target color space.")
+
+#define OUT_OF_GAMUT_COLOR_BLURB \
+ _("The color to use for marking colors which are out of gamut.")
+
+#define SHOW_RGB_U8_BLURB \
+ _("When enabled, set the color scales to display 0...255 instead " \
+ "of percentages")
+
+#define SHOW_HSV_BLURB \
+ _("When enabled, set the color scales to display HSV blend mode instead " \
+ "of LCh")
+
+enum
+{
+ PROP_0,
+ PROP_MODE,
+ PROP_RGB_PROFILE,
+ PROP_GRAY_PROFILE,
+ PROP_CMYK_PROFILE,
+ PROP_DISPLAY_PROFILE,
+ PROP_DISPLAY_PROFILE_FROM_GDK,
+ PROP_SIMULATION_PROFILE,
+ PROP_DISPLAY_RENDERING_INTENT,
+ PROP_DISPLAY_USE_BPC,
+ PROP_DISPLAY_OPTIMIZE,
+ PROP_SIMULATION_RENDERING_INTENT,
+ PROP_SIMULATION_USE_BPC,
+ PROP_SIMULATION_OPTIMIZE,
+ PROP_SIMULATION_GAMUT_CHECK,
+ PROP_OUT_OF_GAMUT_COLOR,
+ PROP_SHOW_RGB_U8,
+ PROP_SHOW_HSV,
+ PROP_DISPLAY_MODULE
+};
+
+
+typedef struct _GimpColorConfigPrivate GimpColorConfigPrivate;
+
+struct _GimpColorConfigPrivate
+{
+ gboolean display_optimize;
+ gboolean simulation_optimize;
+
+ gboolean show_rgb_u8;
+ gboolean show_hsv;
+};
+
+#define GET_PRIVATE(obj) \
+ ((GimpColorConfigPrivate *) gimp_color_config_get_instance_private ((GimpColorConfig *) (obj)))
+
+
+static void gimp_color_config_finalize (GObject *object);
+static void gimp_color_config_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_color_config_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_color_config_set_rgb_profile (GimpColorConfig *config,
+ const gchar *filename,
+ GError **error);
+static void gimp_color_config_set_gray_profile (GimpColorConfig *config,
+ const gchar *filename,
+ GError **error);
+static void gimp_color_config_set_cmyk_profile (GimpColorConfig *config,
+ const gchar *filename,
+ GError **error);
+static void gimp_color_config_set_display_profile (GimpColorConfig *config,
+ const gchar *filename,
+ GError **error);
+static void gimp_color_config_set_simulation_profile (GimpColorConfig *config,
+ const gchar *filename,
+ GError **error);
+
+
+G_DEFINE_TYPE_WITH_CODE (GimpColorConfig, gimp_color_config, G_TYPE_OBJECT,
+ G_ADD_PRIVATE (GimpColorConfig)
+ G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, NULL)
+ gimp_type_set_translation_domain (g_define_type_id,
+ GETTEXT_PACKAGE "-libgimp"))
+
+#define parent_class gimp_color_config_parent_class
+
+
+static void
+gimp_color_config_class_init (GimpColorConfigClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpRGB color;
+
+ gimp_rgba_set (&color, 1.0, 0.0, 1.0, 1.0); /* magenta */
+
+ object_class->finalize = gimp_color_config_finalize;
+ object_class->set_property = gimp_color_config_set_property;
+ object_class->get_property = gimp_color_config_get_property;
+
+ GIMP_CONFIG_PROP_ENUM (object_class, PROP_MODE,
+ "mode",
+ _("Mode of operation"),
+ COLOR_MANAGEMENT_MODE_BLURB,
+ GIMP_TYPE_COLOR_MANAGEMENT_MODE,
+ GIMP_COLOR_MANAGEMENT_DISPLAY,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_PATH (object_class, PROP_RGB_PROFILE,
+ "rgb-profile",
+ _("Preferred RGB profile"),
+ RGB_PROFILE_BLURB,
+ GIMP_CONFIG_PATH_FILE, NULL,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_PATH (object_class, PROP_GRAY_PROFILE,
+ "gray-profile",
+ _("Preferred grayscale profile"),
+ GRAY_PROFILE_BLURB,
+ GIMP_CONFIG_PATH_FILE, NULL,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_PATH (object_class, PROP_CMYK_PROFILE,
+ "cmyk-profile",
+ _("CMYK profile"),
+ CMYK_PROFILE_BLURB,
+ GIMP_CONFIG_PATH_FILE, NULL,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_PATH (object_class, PROP_DISPLAY_PROFILE,
+ "display-profile",
+ _("Monitor profile"),
+ DISPLAY_PROFILE_BLURB,
+ GIMP_CONFIG_PATH_FILE, NULL,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_DISPLAY_PROFILE_FROM_GDK,
+ "display-profile-from-gdk",
+ _("Use the system monitor profile"),
+ DISPLAY_PROFILE_FROM_GDK_BLURB,
+ FALSE,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_PATH (object_class, PROP_SIMULATION_PROFILE,
+ /* FIXME: 3.0: change to simulation-profile */
+ "printer-profile",
+ _("Simulation profile for soft-proofing"),
+ SIMULATION_PROFILE_BLURB,
+ GIMP_CONFIG_PATH_FILE, NULL,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_ENUM (object_class, PROP_DISPLAY_RENDERING_INTENT,
+ "display-rendering-intent",
+ _("Display rendering intent"),
+ DISPLAY_RENDERING_INTENT_BLURB,
+ GIMP_TYPE_COLOR_RENDERING_INTENT,
+ GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_DISPLAY_USE_BPC,
+ "display-use-black-point-compensation",
+ _("Use black point compensation for the display"),
+ DISPLAY_USE_BPC_BLURB,
+ TRUE,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_DISPLAY_OPTIMIZE,
+ "display-optimize",
+ _("Optimize display color transformations"),
+ DISPLAY_OPTIMIZE_BLURB,
+ TRUE,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_ENUM (object_class, PROP_SIMULATION_RENDERING_INTENT,
+ "simulation-rendering-intent",
+ _("Soft-proofing rendering intent"),
+ SIMULATION_RENDERING_INTENT_BLURB,
+ GIMP_TYPE_COLOR_RENDERING_INTENT,
+ GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SIMULATION_USE_BPC,
+ "simulation-use-black-point-compensation",
+ _("Use black point compensation for soft-proofing"),
+ SIMULATION_USE_BPC_BLURB,
+ FALSE,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SIMULATION_OPTIMIZE,
+ "simulation-optimize",
+ _("Optimize soft-proofing color transformations"),
+ SIMULATION_OPTIMIZE_BLURB,
+ TRUE,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SIMULATION_GAMUT_CHECK,
+ "simulation-gamut-check",
+ _("Mark out of gamut colors"),
+ SIMULATION_GAMUT_CHECK_BLURB,
+ FALSE,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_RGB (object_class, PROP_OUT_OF_GAMUT_COLOR,
+ "out-of-gamut-color",
+ _("Out of gamut warning color"),
+ OUT_OF_GAMUT_COLOR_BLURB,
+ FALSE, &color,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_RGB_U8,
+ "show-rgb-u8",
+ "Show RGB 0..255",
+ _("Show RGB 0..255 scales"),
+ FALSE,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_HSV,
+ "show-hsv",
+ "Show HSV",
+ _("Show HSV instead of LCH"),
+ FALSE,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_STRING (object_class, PROP_DISPLAY_MODULE,
+ "display-module",
+ "Display module",
+ "This property is deprecated and its value ignored",
+ "CdisplayLcms",
+ GIMP_PARAM_STATIC_STRINGS);
+}
+
+static void
+gimp_color_config_init (GimpColorConfig *config)
+{
+}
+
+static void
+gimp_color_config_finalize (GObject *object)
+{
+ GimpColorConfig *color_config = GIMP_COLOR_CONFIG (object);
+
+ if (color_config->rgb_profile)
+ g_free (color_config->rgb_profile);
+
+ if (color_config->gray_profile)
+ g_free (color_config->gray_profile);
+
+ if (color_config->cmyk_profile)
+ g_free (color_config->cmyk_profile);
+
+ if (color_config->display_profile)
+ g_free (color_config->display_profile);
+
+ if (color_config->printer_profile)
+ g_free (color_config->printer_profile);
+
+ if (color_config->display_module)
+ g_free (color_config->display_module);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_color_config_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpColorConfig *color_config = GIMP_COLOR_CONFIG (object);
+ GimpColorConfigPrivate *priv = GET_PRIVATE (object);
+ GError *error = NULL;
+
+ switch (property_id)
+ {
+ case PROP_MODE:
+ color_config->mode = g_value_get_enum (value);
+ break;
+ case PROP_RGB_PROFILE:
+ gimp_color_config_set_rgb_profile (color_config,
+ g_value_get_string (value),
+ &error);
+ break;
+ case PROP_GRAY_PROFILE:
+ gimp_color_config_set_gray_profile (color_config,
+ g_value_get_string (value),
+ &error);
+ break;
+ case PROP_CMYK_PROFILE:
+ gimp_color_config_set_cmyk_profile (color_config,
+ g_value_get_string (value),
+ &error);
+ break;
+ case PROP_DISPLAY_PROFILE:
+ gimp_color_config_set_display_profile (color_config,
+ g_value_get_string (value),
+ &error);
+ break;
+ case PROP_DISPLAY_PROFILE_FROM_GDK:
+ color_config->display_profile_from_gdk = g_value_get_boolean (value);
+ break;
+ case PROP_SIMULATION_PROFILE:
+ gimp_color_config_set_simulation_profile (color_config,
+ g_value_get_string (value),
+ &error);
+ break;
+ case PROP_DISPLAY_RENDERING_INTENT:
+ color_config->display_intent = g_value_get_enum (value);
+ break;
+ case PROP_DISPLAY_USE_BPC:
+ color_config->display_use_black_point_compensation = g_value_get_boolean (value);
+ break;
+ case PROP_DISPLAY_OPTIMIZE:
+ priv->display_optimize = g_value_get_boolean (value);
+ break;
+ case PROP_SIMULATION_RENDERING_INTENT:
+ color_config->simulation_intent = g_value_get_enum (value);
+ break;
+ case PROP_SIMULATION_USE_BPC:
+ color_config->simulation_use_black_point_compensation = g_value_get_boolean (value);
+ break;
+ case PROP_SIMULATION_OPTIMIZE:
+ priv->simulation_optimize = g_value_get_boolean (value);
+ break;
+ case PROP_SIMULATION_GAMUT_CHECK:
+ color_config->simulation_gamut_check = g_value_get_boolean (value);
+ break;
+ case PROP_OUT_OF_GAMUT_COLOR:
+ color_config->out_of_gamut_color = *(GimpRGB *) g_value_get_boxed (value);
+ break;
+ case PROP_SHOW_RGB_U8:
+ priv->show_rgb_u8 = g_value_get_boolean (value);
+ break;
+ case PROP_SHOW_HSV:
+ priv->show_hsv = g_value_get_boolean (value);
+ break;
+ case PROP_DISPLAY_MODULE:
+ g_free (color_config->display_module);
+ color_config->display_module = g_value_dup_string (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+
+ if (error)
+ {
+ g_message ("%s", error->message);
+ g_clear_error (&error);
+ }
+}
+
+static void
+gimp_color_config_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpColorConfig *color_config = GIMP_COLOR_CONFIG (object);
+ GimpColorConfigPrivate *priv = GET_PRIVATE (object);
+
+ switch (property_id)
+ {
+ case PROP_MODE:
+ g_value_set_enum (value, color_config->mode);
+ break;
+ case PROP_RGB_PROFILE:
+ g_value_set_string (value, color_config->rgb_profile);
+ break;
+ case PROP_GRAY_PROFILE:
+ g_value_set_string (value, color_config->gray_profile);
+ break;
+ case PROP_CMYK_PROFILE:
+ g_value_set_string (value, color_config->cmyk_profile);
+ break;
+ case PROP_DISPLAY_PROFILE:
+ g_value_set_string (value, color_config->display_profile);
+ break;
+ case PROP_DISPLAY_PROFILE_FROM_GDK:
+ g_value_set_boolean (value, color_config->display_profile_from_gdk);
+ break;
+ case PROP_SIMULATION_PROFILE:
+ g_value_set_string (value, color_config->printer_profile);
+ break;
+ case PROP_DISPLAY_RENDERING_INTENT:
+ g_value_set_enum (value, color_config->display_intent);
+ break;
+ case PROP_DISPLAY_USE_BPC:
+ g_value_set_boolean (value, color_config->display_use_black_point_compensation);
+ break;
+ case PROP_DISPLAY_OPTIMIZE:
+ g_value_set_boolean (value, priv->display_optimize);
+ break;
+ case PROP_SIMULATION_RENDERING_INTENT:
+ g_value_set_enum (value, color_config->simulation_intent);
+ break;
+ case PROP_SIMULATION_USE_BPC:
+ g_value_set_boolean (value, color_config->simulation_use_black_point_compensation);
+ break;
+ case PROP_SIMULATION_OPTIMIZE:
+ g_value_set_boolean (value, priv->simulation_optimize);
+ break;
+ case PROP_SIMULATION_GAMUT_CHECK:
+ g_value_set_boolean (value, color_config->simulation_gamut_check);
+ break;
+ case PROP_OUT_OF_GAMUT_COLOR:
+ g_value_set_boxed (value, &color_config->out_of_gamut_color);
+ break;
+ case PROP_SHOW_RGB_U8:
+ g_value_set_boolean (value, priv->show_rgb_u8);
+ break;
+ case PROP_SHOW_HSV:
+ g_value_set_boolean (value, priv->show_hsv);
+ break;
+ case PROP_DISPLAY_MODULE:
+ g_value_set_string (value, color_config->display_module);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+
+/* public functions */
+
+/**
+ * gimp_color_config_get_mode:
+ * @config: a #GimpColorConfig
+ *
+ * Since: 2.10
+ **/
+GimpColorManagementMode
+gimp_color_config_get_mode (GimpColorConfig *config)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_CONFIG (config),
+ GIMP_COLOR_MANAGEMENT_OFF);
+
+ return config->mode;
+}
+
+/**
+ * gimp_color_config_get_display_intent:
+ * @config: a #GimpColorConfig
+ *
+ * Since: 2.10
+ **/
+GimpColorRenderingIntent
+gimp_color_config_get_display_intent (GimpColorConfig *config)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_CONFIG (config),
+ GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL);
+
+ return config->display_intent;
+}
+
+/**
+ * gimp_color_config_get_display_bpc:
+ * @config: a #GimpColorConfig
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_color_config_get_display_bpc (GimpColorConfig *config)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_CONFIG (config), FALSE);
+
+ return config->display_use_black_point_compensation;
+}
+
+/**
+ * gimp_color_config_get_display_optimize:
+ * @config: a #GimpColorConfig
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_color_config_get_display_optimize (GimpColorConfig *config)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_CONFIG (config), FALSE);
+
+ return GET_PRIVATE (config)->display_optimize;
+}
+
+/**
+ * gimp_color_config_get_display_profile_from_gdk:
+ * @config: a #GimpColorConfig
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_color_config_get_display_profile_from_gdk (GimpColorConfig *config)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_CONFIG (config), FALSE);
+
+ return config->display_profile_from_gdk;
+}
+
+/**
+ * gimp_color_config_get_simulation_intent:
+ * @config: a #GimpColorConfig
+ *
+ * Since: 2.10
+ **/
+GimpColorRenderingIntent
+gimp_color_config_get_simulation_intent (GimpColorConfig *config)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_CONFIG (config),
+ GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL);
+
+ return config->simulation_intent;
+}
+
+/**
+ * gimp_color_config_get_simulation_bpc:
+ * @config: a #GimpColorConfig
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_color_config_get_simulation_bpc (GimpColorConfig *config)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_CONFIG (config), FALSE);
+
+ return config->simulation_use_black_point_compensation;
+}
+
+/**
+ * gimp_color_config_get_simulation_optimize:
+ * @config: a #GimpColorConfig
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_color_config_get_simulation_optimize (GimpColorConfig *config)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_CONFIG (config), FALSE);
+
+ return GET_PRIVATE (config)->simulation_optimize;
+}
+
+/**
+ * gimp_color_config_get_simulation_gamut_check:
+ * @config: a #GimpColorConfig
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_color_config_get_simulation_gamut_check (GimpColorConfig *config)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_CONFIG (config), FALSE);
+
+ return config->simulation_gamut_check;
+}
+
+/**
+ * gimp_color_config_get_rgb_color_profile:
+ * @config: a #GimpColorConfig
+ * @error: return location for a #GError
+ *
+ * Since: 2.10
+ **/
+GimpColorProfile *
+gimp_color_config_get_rgb_color_profile (GimpColorConfig *config,
+ GError **error)
+{
+ GimpColorProfile *profile = NULL;
+
+ g_return_val_if_fail (GIMP_IS_COLOR_CONFIG (config), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ if (config->rgb_profile)
+ {
+ GFile *file = gimp_file_new_for_config_path (config->rgb_profile,
+ error);
+
+ if (file)
+ {
+ profile = gimp_color_profile_new_from_file (file, error);
+
+ if (profile && ! gimp_color_profile_is_rgb (profile))
+ {
+ g_object_unref (profile);
+ profile = NULL;
+
+ g_set_error (error, GIMP_CONFIG_ERROR, 0,
+ _("Color profile '%s' is not for RGB color space."),
+ gimp_file_get_utf8_name (file));
+ }
+
+ g_object_unref (file);
+ }
+ }
+
+ return profile;
+}
+
+/**
+ * gimp_color_config_get_gray_color_profile:
+ * @config: a #GimpColorConfig
+ * @error: return location for a #GError
+ *
+ * Since: 2.10
+ **/
+GimpColorProfile *
+gimp_color_config_get_gray_color_profile (GimpColorConfig *config,
+ GError **error)
+{
+ GimpColorProfile *profile = NULL;
+
+ g_return_val_if_fail (GIMP_IS_COLOR_CONFIG (config), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ if (config->gray_profile)
+ {
+ GFile *file = gimp_file_new_for_config_path (config->gray_profile,
+ error);
+
+ if (file)
+ {
+ profile = gimp_color_profile_new_from_file (file, error);
+
+ if (profile && ! gimp_color_profile_is_gray (profile))
+ {
+ g_object_unref (profile);
+ profile = NULL;
+
+ g_set_error (error, GIMP_CONFIG_ERROR, 0,
+ _("Color profile '%s' is not for GRAY color space."),
+ gimp_file_get_utf8_name (file));
+ }
+
+ g_object_unref (file);
+ }
+ }
+
+ return profile;
+}
+
+/**
+ * gimp_color_config_get_cmyk_color_profile:
+ * @config: a #GimpColorConfig
+ * @error: return location for a #GError
+ *
+ * Since: 2.10
+ **/
+GimpColorProfile *
+gimp_color_config_get_cmyk_color_profile (GimpColorConfig *config,
+ GError **error)
+{
+ GimpColorProfile *profile = NULL;
+
+ g_return_val_if_fail (GIMP_IS_COLOR_CONFIG (config), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ if (config->cmyk_profile)
+ {
+ GFile *file = gimp_file_new_for_config_path (config->cmyk_profile,
+ error);
+
+ if (file)
+ {
+ profile = gimp_color_profile_new_from_file (file, error);
+
+ if (profile && ! gimp_color_profile_is_cmyk (profile))
+ {
+ g_object_unref (profile);
+ profile = NULL;
+
+ g_set_error (error, GIMP_CONFIG_ERROR, 0,
+ _("Color profile '%s' is not for CMYK color space."),
+ gimp_file_get_utf8_name (file));
+ }
+
+ g_object_unref (file);
+ }
+ }
+
+ return profile;
+}
+
+/**
+ * gimp_color_config_get_display_color_profile:
+ * @config: a #GimpColorConfig
+ * @error: return location for a #GError
+ *
+ * Since: 2.10
+ **/
+GimpColorProfile *
+gimp_color_config_get_display_color_profile (GimpColorConfig *config,
+ GError **error)
+{
+ GimpColorProfile *profile = NULL;
+
+ g_return_val_if_fail (GIMP_IS_COLOR_CONFIG (config), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ if (config->display_profile)
+ {
+ GFile *file = gimp_file_new_for_config_path (config->display_profile,
+ error);
+
+ if (file)
+ {
+ profile = gimp_color_profile_new_from_file (file, error);
+
+ g_object_unref (file);
+ }
+ }
+
+ return profile;
+}
+
+/**
+ * gimp_color_config_get_simulation_color_profile:
+ * @config: a #GimpColorConfig
+ * @error: return location for a #GError
+ *
+ * Since: 2.10
+ **/
+GimpColorProfile *
+gimp_color_config_get_simulation_color_profile (GimpColorConfig *config,
+ GError **error)
+{
+ GimpColorProfile *profile = NULL;
+
+ g_return_val_if_fail (GIMP_IS_COLOR_CONFIG (config), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ if (config->printer_profile)
+ {
+ GFile *file = gimp_file_new_for_config_path (config->printer_profile,
+ error);
+
+ if (file)
+ {
+ profile = gimp_color_profile_new_from_file (file, error);
+
+ g_object_unref (file);
+ }
+ }
+
+ return profile;
+}
+
+
+/* private functions */
+
+static void
+gimp_color_config_set_rgb_profile (GimpColorConfig *config,
+ const gchar *filename,
+ GError **error)
+{
+ gboolean success = TRUE;
+
+ if (filename)
+ {
+ GFile *file = gimp_file_new_for_config_path (filename, error);
+
+ if (file)
+ {
+ GimpColorProfile *profile;
+
+ profile = gimp_color_profile_new_from_file (file, error);
+
+ if (profile)
+ {
+ if (! gimp_color_profile_is_rgb (profile))
+ {
+ g_set_error (error, GIMP_CONFIG_ERROR, 0,
+ _("Color profile '%s' is not for RGB "
+ "color space."),
+ gimp_file_get_utf8_name (file));
+ success = FALSE;
+ }
+
+ g_object_unref (profile);
+ }
+ else
+ {
+ success = FALSE;
+ }
+
+ g_object_unref (file);
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+
+ if (success)
+ {
+ g_free (config->rgb_profile);
+ config->rgb_profile = g_strdup (filename);
+ }
+}
+
+static void
+gimp_color_config_set_gray_profile (GimpColorConfig *config,
+ const gchar *filename,
+ GError **error)
+{
+ gboolean success = TRUE;
+
+ if (filename)
+ {
+ GFile *file = gimp_file_new_for_config_path (filename, error);
+
+ if (file)
+ {
+ GimpColorProfile *profile;
+
+ profile = gimp_color_profile_new_from_file (file, error);
+
+ if (profile)
+ {
+ if (! gimp_color_profile_is_gray (profile))
+ {
+ g_set_error (error, GIMP_CONFIG_ERROR, 0,
+ _("Color profile '%s' is not for GRAY "
+ "color space."),
+ gimp_file_get_utf8_name (file));
+ success = FALSE;
+ }
+
+ g_object_unref (profile);
+ }
+ else
+ {
+ success = FALSE;
+ }
+
+ g_object_unref (file);
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+
+ if (success)
+ {
+ g_free (config->gray_profile);
+ config->gray_profile = g_strdup (filename);
+ }
+}
+
+static void
+gimp_color_config_set_cmyk_profile (GimpColorConfig *config,
+ const gchar *filename,
+ GError **error)
+{
+ gboolean success = TRUE;
+
+ if (filename)
+ {
+ GFile *file = gimp_file_new_for_config_path (filename, error);
+
+ if (file)
+ {
+ GimpColorProfile *profile;
+
+ profile = gimp_color_profile_new_from_file (file, error);
+
+ if (profile)
+ {
+ if (! gimp_color_profile_is_cmyk (profile))
+ {
+ g_set_error (error, GIMP_CONFIG_ERROR, 0,
+ _("Color profile '%s' is not for CMYK "
+ "color space."),
+ gimp_file_get_utf8_name (file));
+ success = FALSE;
+ }
+
+ g_object_unref (profile);
+ }
+ else
+ {
+ success = FALSE;
+ }
+
+ g_object_unref (file);
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+
+ if (success)
+ {
+ g_free (config->cmyk_profile);
+ config->cmyk_profile = g_strdup (filename);
+ }
+}
+
+static void
+gimp_color_config_set_display_profile (GimpColorConfig *config,
+ const gchar *filename,
+ GError **error)
+{
+ gboolean success = TRUE;
+
+ if (filename)
+ {
+ GFile *file = gimp_file_new_for_config_path (filename, error);
+
+ if (file)
+ {
+ GimpColorProfile *profile;
+
+ profile = gimp_color_profile_new_from_file (file, error);
+
+ if (profile)
+ {
+ g_object_unref (profile);
+ }
+ else
+ {
+ success = FALSE;
+ }
+
+ g_object_unref (file);
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+
+ if (success)
+ {
+ g_free (config->display_profile);
+ config->display_profile = g_strdup (filename);
+ }
+}
+
+static void
+gimp_color_config_set_simulation_profile (GimpColorConfig *config,
+ const gchar *filename,
+ GError **error)
+{
+ gboolean success = TRUE;
+
+ if (filename)
+ {
+ GFile *file = gimp_file_new_for_config_path (filename, error);
+
+ if (file)
+ {
+ GimpColorProfile *profile;
+
+ profile = gimp_color_profile_new_from_file (file, error);
+
+ if (profile)
+ {
+ g_object_unref (profile);
+ }
+ else
+ {
+ success = FALSE;
+ }
+
+ g_object_unref (file);
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+
+ if (success)
+ {
+ g_free (config->printer_profile);
+ config->printer_profile = g_strdup (filename);
+ }
+}
diff --git a/libgimpconfig/gimpcolorconfig.h b/libgimpconfig/gimpcolorconfig.h
new file mode 100644
index 0000000..b4fdf28
--- /dev/null
+++ b/libgimpconfig/gimpcolorconfig.h
@@ -0,0 +1,110 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpColorConfig class
+ * Copyright (C) 2004 Stefan Döhla <stefan@doehla.de>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_CONFIG_H_INSIDE__) && !defined (GIMP_CONFIG_COMPILATION)
+#error "Only <libgimpconfig/gimpconfig.h> can be included directly."
+#endif
+
+#ifndef __GIMP_COLOR_CONFIG_H__
+#define __GIMP_COLOR_CONFIG_H__
+
+
+#define GIMP_TYPE_COLOR_CONFIG (gimp_color_config_get_type ())
+#define GIMP_COLOR_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_COLOR_CONFIG, GimpColorConfig))
+#define GIMP_COLOR_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_COLOR_CONFIG, GimpColorConfigClass))
+#define GIMP_IS_COLOR_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_COLOR_CONFIG))
+#define GIMP_IS_COLOR_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_COLOR_CONFIG))
+
+
+typedef struct _GimpColorConfigClass GimpColorConfigClass;
+
+struct _GimpColorConfig
+{
+ GObject parent_instance;
+
+ /*< public >*/
+ GimpColorManagementMode mode;
+ gchar *rgb_profile;
+ gchar *cmyk_profile;
+ gchar *display_profile;
+ gboolean display_profile_from_gdk;
+ gchar *printer_profile;
+ GimpColorRenderingIntent display_intent;
+ GimpColorRenderingIntent simulation_intent;
+
+ gchar *display_module;
+
+ gboolean simulation_gamut_check;
+ GimpRGB out_of_gamut_color;
+
+ gboolean display_use_black_point_compensation;
+ gboolean simulation_use_black_point_compensation;
+
+ gchar *gray_profile;
+
+ /*< private >*/
+ /* Padding for future expansion */
+#if (GLIB_SIZEOF_VOID_P == 8)
+ void (* _gimp_reserved3) (void);
+#endif
+ void (* _gimp_reserved4) (void);
+ void (* _gimp_reserved5) (void);
+ void (* _gimp_reserved6) (void);
+ void (* _gimp_reserved7) (void);
+ void (* _gimp_reserved8) (void);
+};
+
+struct _GimpColorConfigClass
+{
+ GObjectClass parent_class;
+};
+
+
+GType gimp_color_config_get_type (void) G_GNUC_CONST;
+
+GimpColorManagementMode
+ gimp_color_config_get_mode (GimpColorConfig *config);
+
+GimpColorRenderingIntent
+ gimp_color_config_get_display_intent (GimpColorConfig *config);
+gboolean gimp_color_config_get_display_bpc (GimpColorConfig *config);
+gboolean gimp_color_config_get_display_optimize (GimpColorConfig *config);
+gboolean gimp_color_config_get_display_profile_from_gdk (GimpColorConfig *config);
+
+GimpColorRenderingIntent
+ gimp_color_config_get_simulation_intent (GimpColorConfig *config);
+gboolean gimp_color_config_get_simulation_bpc (GimpColorConfig *config);
+gboolean gimp_color_config_get_simulation_optimize (GimpColorConfig *config);
+gboolean gimp_color_config_get_simulation_gamut_check (GimpColorConfig *config);
+
+GimpColorProfile * gimp_color_config_get_rgb_color_profile (GimpColorConfig *config,
+ GError **error);
+GimpColorProfile * gimp_color_config_get_gray_color_profile (GimpColorConfig *config,
+ GError **error);
+GimpColorProfile * gimp_color_config_get_cmyk_color_profile (GimpColorConfig *config,
+ GError **error);
+GimpColorProfile * gimp_color_config_get_display_color_profile (GimpColorConfig *config,
+ GError **error);
+GimpColorProfile * gimp_color_config_get_simulation_color_profile (GimpColorConfig *config,
+ GError **error);
+
+
+#endif /* GIMP_COLOR_CONFIG_H__ */
diff --git a/libgimpconfig/gimpconfig-deserialize.c b/libgimpconfig/gimpconfig-deserialize.c
new file mode 100644
index 0000000..55cbf50
--- /dev/null
+++ b/libgimpconfig/gimpconfig-deserialize.c
@@ -0,0 +1,1019 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * Object properties deserialization routines
+ * Copyright (C) 2001-2002 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <cairo.h>
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpmath/gimpmath.h"
+
+#include "gimpconfigtypes.h"
+
+#include "gimpconfigwriter.h"
+#include "gimpconfig-iface.h"
+#include "gimpconfig-deserialize.h"
+#include "gimpconfig-params.h"
+#include "gimpconfig-path.h"
+#include "gimpscanner.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpconfig-deserialize
+ * @title: GimpConfig-deserialize
+ * @short_description: Deserializing code for libgimpconfig.
+ *
+ * Deserializing code for libgimpconfig.
+ **/
+
+
+/*
+ * All functions return G_TOKEN_RIGHT_PAREN on success,
+ * the GTokenType they would have expected but didn't get
+ * or G_TOKEN_NONE if they got the expected token but
+ * couldn't parse it.
+ */
+
+static GTokenType gimp_config_deserialize_value (GValue *value,
+ GimpConfig *config,
+ GParamSpec *prop_spec,
+ GScanner *scanner);
+static GTokenType gimp_config_deserialize_fundamental (GValue *value,
+ GParamSpec *prop_spec,
+ GScanner *scanner);
+static GTokenType gimp_config_deserialize_enum (GValue *value,
+ GParamSpec *prop_spec,
+ GScanner *scanner);
+static GTokenType gimp_config_deserialize_memsize (GValue *value,
+ GParamSpec *prop_spec,
+ GScanner *scanner);
+static GTokenType gimp_config_deserialize_path (GValue *value,
+ GParamSpec *prop_spec,
+ GScanner *scanner);
+static GTokenType gimp_config_deserialize_rgb (GValue *value,
+ GParamSpec *prop_spec,
+ GScanner *scanner);
+static GTokenType gimp_config_deserialize_matrix2 (GValue *value,
+ GParamSpec *prop_spec,
+ GScanner *scanner);
+static GTokenType gimp_config_deserialize_object (GValue *value,
+ GimpConfig *config,
+ GParamSpec *prop_spec,
+ GScanner *scanner,
+ gint nest_level);
+static GTokenType gimp_config_deserialize_value_array (GValue *value,
+ GimpConfig *config,
+ GParamSpec *prop_spec,
+ GScanner *scanner);
+static GTokenType gimp_config_deserialize_unit (GValue *value,
+ GParamSpec *prop_spec,
+ GScanner *scanner);
+static GTokenType gimp_config_deserialize_file_value (GValue *value,
+ GParamSpec *prop_spec,
+ GScanner *scanner);
+static GTokenType gimp_config_deserialize_any (GValue *value,
+ GParamSpec *prop_spec,
+ GScanner *scanner);
+static GTokenType gimp_config_skip_unknown_property (GScanner *scanner);
+
+static inline gboolean scanner_string_utf8_valid (GScanner *scanner,
+ const gchar *token_name);
+
+static inline gboolean
+scanner_string_utf8_valid (GScanner *scanner,
+ const gchar *token_name)
+{
+ if (g_utf8_validate (scanner->value.v_string, -1, NULL))
+ return TRUE;
+
+ g_scanner_error (scanner,
+ _("value for token %s is not a valid UTF-8 string"),
+ token_name);
+
+ return FALSE;
+}
+
+/**
+ * gimp_config_deserialize_properties:
+ * @config: a #GimpConfig.
+ * @scanner: a #GScanner.
+ * @nest_level: the nest level
+ *
+ * This function uses the @scanner to configure the properties of @config.
+ *
+ * Return value: %TRUE on success, %FALSE otherwise.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_config_deserialize_properties (GimpConfig *config,
+ GScanner *scanner,
+ gint nest_level)
+{
+ GObjectClass *klass;
+ GParamSpec **property_specs;
+ guint n_property_specs;
+ guint i;
+ guint scope_id;
+ guint old_scope_id;
+ GTokenType token;
+
+ g_return_val_if_fail (GIMP_IS_CONFIG (config), FALSE);
+
+ klass = G_OBJECT_GET_CLASS (config);
+ property_specs = g_object_class_list_properties (klass, &n_property_specs);
+
+ if (! property_specs)
+ return TRUE;
+
+ scope_id = g_type_qname (G_TYPE_FROM_INSTANCE (config));
+ old_scope_id = g_scanner_set_scope (scanner, scope_id);
+
+ for (i = 0; i < n_property_specs; i++)
+ {
+ GParamSpec *prop_spec = property_specs[i];
+
+ if (prop_spec->flags & GIMP_CONFIG_PARAM_SERIALIZE)
+ {
+ g_scanner_scope_add_symbol (scanner, scope_id,
+ prop_spec->name, prop_spec);
+ }
+ }
+
+ g_free (property_specs);
+
+ g_object_freeze_notify (G_OBJECT (config));
+
+ token = G_TOKEN_LEFT_PAREN;
+
+ while (TRUE)
+ {
+ GTokenType next = g_scanner_peek_next_token (scanner);
+
+ if (next == G_TOKEN_EOF)
+ break;
+
+ if (G_UNLIKELY (next != token &&
+ ! (token == G_TOKEN_SYMBOL &&
+ next == G_TOKEN_IDENTIFIER)))
+ {
+ break;
+ }
+
+ token = g_scanner_get_next_token (scanner);
+
+ switch (token)
+ {
+ case G_TOKEN_LEFT_PAREN:
+ token = G_TOKEN_SYMBOL;
+ break;
+
+ case G_TOKEN_IDENTIFIER:
+ token = gimp_config_skip_unknown_property (scanner);
+ break;
+
+ case G_TOKEN_SYMBOL:
+ token = gimp_config_deserialize_property (config,
+ scanner, nest_level);
+ break;
+
+ case G_TOKEN_RIGHT_PAREN:
+ token = G_TOKEN_LEFT_PAREN;
+ break;
+
+ default: /* do nothing */
+ break;
+ }
+ }
+
+ g_scanner_set_scope (scanner, old_scope_id);
+
+ g_object_thaw_notify (G_OBJECT (config));
+
+ if (token == G_TOKEN_NONE)
+ return FALSE;
+
+ return gimp_config_deserialize_return (scanner, token, nest_level);
+}
+
+/**
+ * gimp_config_deserialize_property:
+ * @config: a #GimpConfig.
+ * @scanner: a #GScanner.
+ * @nest_level: the nest level
+ *
+ * This function deserializes a single property of @config. You
+ * shouldn't need to call this function directly. If possible, use
+ * gimp_config_deserialize_properties() instead.
+ *
+ * Return value: %G_TOKEN_RIGHT_PAREN on success, otherwise the
+ * expected #GTokenType or %G_TOKEN_NONE if the expected token was
+ * found but couldn't be parsed.
+ *
+ * Since: 2.4
+ **/
+GTokenType
+gimp_config_deserialize_property (GimpConfig *config,
+ GScanner *scanner,
+ gint nest_level)
+{
+ GimpConfigInterface *config_iface = NULL;
+ GimpConfigInterface *parent_iface = NULL;
+ GParamSpec *prop_spec;
+ GTokenType token = G_TOKEN_RIGHT_PAREN;
+ GValue value = G_VALUE_INIT;
+ guint old_scope_id;
+
+ old_scope_id = g_scanner_set_scope (scanner, 0);
+
+ prop_spec = G_PARAM_SPEC (scanner->value.v_symbol);
+
+ g_value_init (&value, prop_spec->value_type);
+
+ if (G_TYPE_IS_OBJECT (prop_spec->owner_type))
+ {
+ GTypeClass *owner_class = g_type_class_peek (prop_spec->owner_type);
+
+ config_iface = g_type_interface_peek (owner_class, GIMP_TYPE_CONFIG);
+
+ /* We must call deserialize_property() *only* if the *exact* class
+ * which implements it is param_spec->owner_type's class.
+ *
+ * Therefore, we ask param_spec->owner_type's immediate parent class
+ * for it's GimpConfigInterface and check if we get a different
+ * pointer.
+ *
+ * (if the pointers are the same, param_spec->owner_type's
+ * GimpConfigInterface is inherited from one of it's parent classes
+ * and thus not able to handle param_spec->owner_type's properties).
+ */
+ if (config_iface)
+ {
+ GTypeClass *owner_parent_class;
+
+ owner_parent_class = g_type_class_peek_parent (owner_class);
+
+ parent_iface = g_type_interface_peek (owner_parent_class,
+ GIMP_TYPE_CONFIG);
+ }
+ }
+
+ if (config_iface &&
+ config_iface != parent_iface && /* see comment above */
+ config_iface->deserialize_property &&
+ config_iface->deserialize_property (config,
+ prop_spec->param_id,
+ &value,
+ prop_spec,
+ scanner,
+ &token))
+ {
+ /* nop */
+ }
+ else
+ {
+ if (G_VALUE_HOLDS_OBJECT (&value) &&
+ G_VALUE_TYPE (&value) != G_TYPE_FILE)
+ {
+ token = gimp_config_deserialize_object (&value,
+ config, prop_spec,
+ scanner, nest_level);
+ }
+ else
+ {
+ token = gimp_config_deserialize_value (&value,
+ config, prop_spec, scanner);
+ }
+ }
+
+ if (token == G_TOKEN_RIGHT_PAREN &&
+ g_scanner_peek_next_token (scanner) == token)
+ {
+ if (! (G_VALUE_HOLDS_OBJECT (&value) &&
+ (prop_spec->flags & GIMP_CONFIG_PARAM_AGGREGATE)))
+ g_object_set_property (G_OBJECT (config), prop_spec->name, &value);
+ }
+#ifdef CONFIG_DEBUG
+ else
+ {
+ g_warning ("%s: couldn't deserialize property %s::%s of type %s",
+ G_STRFUNC,
+ g_type_name (G_TYPE_FROM_INSTANCE (config)),
+ prop_spec->name,
+ g_type_name (prop_spec->value_type));
+ }
+#endif
+
+ g_value_unset (&value);
+
+ g_scanner_set_scope (scanner, old_scope_id);
+
+ return token;
+}
+
+static GTokenType
+gimp_config_deserialize_value (GValue *value,
+ GimpConfig *config,
+ GParamSpec *prop_spec,
+ GScanner *scanner)
+{
+ if (G_TYPE_FUNDAMENTAL (prop_spec->value_type) == G_TYPE_ENUM)
+ {
+ return gimp_config_deserialize_enum (value, prop_spec, scanner);
+ }
+ else if (G_TYPE_IS_FUNDAMENTAL (prop_spec->value_type))
+ {
+ return gimp_config_deserialize_fundamental (value, prop_spec, scanner);
+ }
+ else if (prop_spec->value_type == GIMP_TYPE_MEMSIZE)
+ {
+ return gimp_config_deserialize_memsize (value, prop_spec, scanner);
+ }
+ else if (prop_spec->value_type == GIMP_TYPE_CONFIG_PATH)
+ {
+ return gimp_config_deserialize_path (value, prop_spec, scanner);
+ }
+ else if (prop_spec->value_type == GIMP_TYPE_RGB)
+ {
+ return gimp_config_deserialize_rgb (value, prop_spec, scanner);
+ }
+ else if (prop_spec->value_type == GIMP_TYPE_MATRIX2)
+ {
+ return gimp_config_deserialize_matrix2 (value, prop_spec, scanner);
+ }
+ else if (prop_spec->value_type == GIMP_TYPE_VALUE_ARRAY)
+ {
+ return gimp_config_deserialize_value_array (value,
+ config, prop_spec, scanner);
+ }
+ else if (prop_spec->value_type == GIMP_TYPE_UNIT)
+ {
+ return gimp_config_deserialize_unit (value, prop_spec, scanner);
+ }
+ else if (prop_spec->value_type == G_TYPE_FILE)
+ {
+ return gimp_config_deserialize_file_value (value, prop_spec, scanner);
+ }
+
+ /* This fallback will only work for value_types that
+ * can be transformed from a string value.
+ */
+ return gimp_config_deserialize_any (value, prop_spec, scanner);
+}
+
+static GTokenType
+gimp_config_deserialize_fundamental (GValue *value,
+ GParamSpec *prop_spec,
+ GScanner *scanner)
+{
+ GTokenType token;
+ GTokenType next_token;
+ GType value_type;
+ gboolean negate = FALSE;
+
+ value_type = G_TYPE_FUNDAMENTAL (prop_spec->value_type);
+
+ switch (value_type)
+ {
+ case G_TYPE_STRING:
+ token = G_TOKEN_STRING;
+ break;
+
+ case G_TYPE_BOOLEAN:
+ token = G_TOKEN_IDENTIFIER;
+ break;
+
+ case G_TYPE_INT:
+ case G_TYPE_LONG:
+ case G_TYPE_INT64:
+ if (g_scanner_peek_next_token (scanner) == '-')
+ {
+ negate = TRUE;
+ g_scanner_get_next_token (scanner);
+ }
+ /* fallthrough */
+ case G_TYPE_UINT:
+ case G_TYPE_ULONG:
+ case G_TYPE_UINT64:
+ token = G_TOKEN_INT;
+ break;
+
+ case G_TYPE_FLOAT:
+ case G_TYPE_DOUBLE:
+ if (g_scanner_peek_next_token (scanner) == '-')
+ {
+ negate = TRUE;
+ g_scanner_get_next_token (scanner);
+ }
+ token = G_TOKEN_FLOAT;
+ break;
+
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ next_token = g_scanner_peek_next_token (scanner);
+
+ /* we parse integers into floats too, because g_ascii_dtostr()
+ * serialized whole number without decimal point
+ */
+ if (next_token != token &&
+ ! (token == G_TOKEN_FLOAT && next_token == G_TOKEN_INT))
+ {
+ return token;
+ }
+
+ g_scanner_get_next_token (scanner);
+
+ switch (value_type)
+ {
+ case G_TYPE_STRING:
+ if (scanner_string_utf8_valid (scanner, prop_spec->name))
+ g_value_set_string (value, scanner->value.v_string);
+ else
+ return G_TOKEN_NONE;
+ break;
+
+ case G_TYPE_BOOLEAN:
+ if (! g_ascii_strcasecmp (scanner->value.v_identifier, "yes") ||
+ ! g_ascii_strcasecmp (scanner->value.v_identifier, "true"))
+ g_value_set_boolean (value, TRUE);
+ else if (! g_ascii_strcasecmp (scanner->value.v_identifier, "no") ||
+ ! g_ascii_strcasecmp (scanner->value.v_identifier, "false"))
+ g_value_set_boolean (value, FALSE);
+ else
+ {
+ g_scanner_error
+ (scanner,
+ /* please don't translate 'yes' and 'no' */
+ _("expected 'yes' or 'no' for boolean token %s, got '%s'"),
+ prop_spec->name, scanner->value.v_identifier);
+ return G_TOKEN_NONE;
+ }
+ break;
+
+ case G_TYPE_INT:
+ g_value_set_int (value, (negate ?
+ - (gint) scanner->value.v_int64 :
+ (gint) scanner->value.v_int64));
+ break;
+ case G_TYPE_UINT:
+ g_value_set_uint (value, scanner->value.v_int64);
+ break;
+
+ case G_TYPE_LONG:
+ g_value_set_long (value, (negate ?
+ - (glong) scanner->value.v_int64 :
+ (glong) scanner->value.v_int64));
+ break;
+ case G_TYPE_ULONG:
+ g_value_set_ulong (value, scanner->value.v_int64);
+ break;
+
+ case G_TYPE_INT64:
+ g_value_set_int64 (value, (negate ?
+ - (gint64) scanner->value.v_int64 :
+ (gint64) scanner->value.v_int64));
+ break;
+ case G_TYPE_UINT64:
+ g_value_set_uint64 (value, scanner->value.v_int64);
+ break;
+
+ case G_TYPE_FLOAT:
+ if (next_token == G_TOKEN_FLOAT)
+ g_value_set_float (value, negate ?
+ - scanner->value.v_float :
+ scanner->value.v_float);
+ else
+ g_value_set_float (value, negate ?
+ - (gfloat) scanner->value.v_int :
+ (gfloat) scanner->value.v_int);
+ break;
+
+ case G_TYPE_DOUBLE:
+ if (next_token == G_TOKEN_FLOAT)
+ g_value_set_double (value, negate ?
+ - scanner->value.v_float:
+ scanner->value.v_float);
+ else
+ g_value_set_double (value, negate ?
+ - (gdouble) scanner->value.v_int:
+ (gdouble) scanner->value.v_int);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ return G_TOKEN_RIGHT_PAREN;
+}
+
+static GTokenType
+gimp_config_deserialize_enum (GValue *value,
+ GParamSpec *prop_spec,
+ GScanner *scanner)
+{
+ GEnumClass *enum_class;
+ GEnumValue *enum_value;
+
+ enum_class = g_type_class_peek (G_VALUE_TYPE (value));
+
+ switch (g_scanner_peek_next_token (scanner))
+ {
+ case G_TOKEN_IDENTIFIER:
+ g_scanner_get_next_token (scanner);
+
+ enum_value = g_enum_get_value_by_nick (enum_class,
+ scanner->value.v_identifier);
+ if (! enum_value)
+ enum_value = g_enum_get_value_by_name (enum_class,
+ scanner->value.v_identifier);
+ if (! enum_value)
+ {
+ /* if the value was not found, check if we have a compat
+ * enum to find the ideitifier
+ */
+ GQuark quark = g_quark_from_static_string ("gimp-compat-enum");
+ GType compat_type = (GType) g_type_get_qdata (G_VALUE_TYPE (value),
+ quark);
+
+ if (compat_type)
+ {
+ GEnumClass *compat_class = g_type_class_ref (compat_type);
+
+ enum_value = g_enum_get_value_by_nick (compat_class,
+ scanner->value.v_identifier);
+ if (! enum_value)
+ enum_value = g_enum_get_value_by_name (compat_class,
+ scanner->value.v_identifier);
+
+ /* finally, if we found a compat value, make sure the
+ * same value exists in the original enum
+ */
+ if (enum_value)
+ enum_value = g_enum_get_value (enum_class, enum_value->value);
+
+ g_type_class_unref (compat_class);
+ }
+ }
+
+ if (! enum_value)
+ {
+ g_scanner_error (scanner,
+ _("invalid value '%s' for token %s"),
+ scanner->value.v_identifier, prop_spec->name);
+ return G_TOKEN_NONE;
+ }
+ break;
+
+ case G_TOKEN_INT:
+ g_scanner_get_next_token (scanner);
+
+ enum_value = g_enum_get_value (enum_class,
+ (gint) scanner->value.v_int64);
+
+ if (! enum_value)
+ {
+ g_scanner_error (scanner,
+ _("invalid value '%ld' for token %s"),
+ (glong) scanner->value.v_int64, prop_spec->name);
+ return G_TOKEN_NONE;
+ }
+ break;
+
+ default:
+ return G_TOKEN_IDENTIFIER;
+ }
+
+ g_value_set_enum (value, enum_value->value);
+
+ return G_TOKEN_RIGHT_PAREN;
+}
+
+static GTokenType
+gimp_config_deserialize_memsize (GValue *value,
+ GParamSpec *prop_spec,
+ GScanner *scanner)
+{
+ gchar *orig_cset_first = scanner->config->cset_identifier_first;
+ gchar *orig_cset_nth = scanner->config->cset_identifier_nth;
+ guint64 memsize;
+
+ scanner->config->cset_identifier_first = G_CSET_DIGITS;
+ scanner->config->cset_identifier_nth = G_CSET_DIGITS "gGmMkKbB";
+
+ if (g_scanner_peek_next_token (scanner) != G_TOKEN_IDENTIFIER)
+ return G_TOKEN_IDENTIFIER;
+
+ g_scanner_get_next_token (scanner);
+
+ scanner->config->cset_identifier_first = orig_cset_first;
+ scanner->config->cset_identifier_nth = orig_cset_nth;
+
+ if (! gimp_memsize_deserialize (scanner->value.v_identifier, &memsize))
+ return G_TOKEN_NONE;
+
+ g_value_set_uint64 (value, memsize);
+
+ return G_TOKEN_RIGHT_PAREN;
+}
+
+static GTokenType
+gimp_config_deserialize_path (GValue *value,
+ GParamSpec *prop_spec,
+ GScanner *scanner)
+{
+ GError *error = NULL;
+
+ if (g_scanner_peek_next_token (scanner) != G_TOKEN_STRING)
+ return G_TOKEN_STRING;
+
+ g_scanner_get_next_token (scanner);
+
+ if (!scanner_string_utf8_valid (scanner, prop_spec->name))
+ return G_TOKEN_NONE;
+
+ if (scanner->value.v_string)
+ {
+ /* Check if the string can be expanded
+ * and converted to the filesystem encoding.
+ */
+ gchar *expand = gimp_config_path_expand (scanner->value.v_string,
+ TRUE, &error);
+
+ if (!expand)
+ {
+ g_scanner_error (scanner,
+ _("while parsing token '%s': %s"),
+ prop_spec->name, error->message);
+ g_error_free (error);
+
+ return G_TOKEN_NONE;
+ }
+
+ g_free (expand);
+
+ g_value_set_static_string (value, scanner->value.v_string);
+ }
+
+ return G_TOKEN_RIGHT_PAREN;
+}
+
+static GTokenType
+gimp_config_deserialize_rgb (GValue *value,
+ GParamSpec *prop_spec,
+ GScanner *scanner)
+{
+ GimpRGB rgb;
+
+ if (! gimp_scanner_parse_color (scanner, &rgb))
+ return G_TOKEN_NONE;
+
+ g_value_set_boxed (value, &rgb);
+
+ return G_TOKEN_RIGHT_PAREN;
+}
+
+static GTokenType
+gimp_config_deserialize_matrix2 (GValue *value,
+ GParamSpec *prop_spec,
+ GScanner *scanner)
+{
+ GimpMatrix2 matrix;
+
+ if (! gimp_scanner_parse_matrix2 (scanner, &matrix))
+ return G_TOKEN_NONE;
+
+ g_value_set_boxed (value, &matrix);
+
+ return G_TOKEN_RIGHT_PAREN;
+}
+
+static GTokenType
+gimp_config_deserialize_object (GValue *value,
+ GimpConfig *config,
+ GParamSpec *prop_spec,
+ GScanner *scanner,
+ gint nest_level)
+{
+ GimpConfigInterface *config_iface;
+ GimpConfig *prop_object;
+
+ g_object_get_property (G_OBJECT (config), prop_spec->name, value);
+
+ prop_object = g_value_get_object (value);
+
+ if (! prop_object)
+ {
+ /* if the object property is not GIMP_CONFIG_PARAM_AGGREGATE, read
+ * the type of the object and create it
+ */
+ if (! (prop_spec->flags & GIMP_CONFIG_PARAM_AGGREGATE))
+ {
+ gchar *type_name;
+ GType type;
+
+ if (! gimp_scanner_parse_string (scanner, &type_name))
+ return G_TOKEN_STRING;
+
+ if (! (type_name && *type_name))
+ {
+ g_scanner_error (scanner, "Type name is empty");
+ g_free (type_name);
+ return G_TOKEN_NONE;
+ }
+
+ type = g_type_from_name (type_name);
+ g_free (type_name);
+
+ if (! g_type_is_a (type, prop_spec->value_type))
+ return G_TOKEN_STRING;
+
+ prop_object = g_object_new (type, NULL);
+
+ g_value_take_object (value, prop_object);
+ }
+ else
+ {
+ return G_TOKEN_RIGHT_PAREN;
+ }
+ }
+
+ config_iface = GIMP_CONFIG_GET_INTERFACE (prop_object);
+
+ if (! config_iface)
+ return gimp_config_deserialize_any (value, prop_spec, scanner);
+
+ if (! config_iface->deserialize (prop_object, scanner, nest_level + 1, NULL))
+ return G_TOKEN_NONE;
+
+ return G_TOKEN_RIGHT_PAREN;
+}
+
+static GTokenType
+gimp_config_deserialize_value_array (GValue *value,
+ GimpConfig *config,
+ GParamSpec *prop_spec,
+ GScanner *scanner)
+{
+ GimpParamSpecValueArray *array_spec;
+ GimpValueArray *array;
+ GValue array_value = G_VALUE_INIT;
+ gint n_values;
+ GTokenType token;
+ gint i;
+
+ array_spec = GIMP_PARAM_SPEC_VALUE_ARRAY (prop_spec);
+
+ if (! gimp_scanner_parse_int (scanner, &n_values))
+ return G_TOKEN_INT;
+
+ array = gimp_value_array_new (n_values);
+
+ for (i = 0; i < n_values; i++)
+ {
+ g_value_init (&array_value, array_spec->element_spec->value_type);
+
+ token = gimp_config_deserialize_value (&array_value,
+ config,
+ array_spec->element_spec,
+ scanner);
+
+ if (token == G_TOKEN_RIGHT_PAREN)
+ gimp_value_array_append (array, &array_value);
+
+ g_value_unset (&array_value);
+
+ if (token != G_TOKEN_RIGHT_PAREN)
+ return token;
+ }
+
+ g_value_take_boxed (value, array);
+
+ return G_TOKEN_RIGHT_PAREN;
+}
+
+/* This function is entirely sick, so is our method of serializing
+ * units, which we write out as (unit foo bar) instead of
+ * (unit "foo bar"). The assumption that caused this shit was that a
+ * unit's "identifier" is really an identifier in the C-ish sense,
+ * when in fact it's just a random user entered string.
+ *
+ * Here, we try to parse at least the default units shipped with gimp,
+ * and we add code to parse (unit "foo bar") in order to be compatible
+ * with future correct unit serializing.
+ */
+static GTokenType
+gimp_config_deserialize_unit (GValue *value,
+ GParamSpec *prop_spec,
+ GScanner *scanner)
+{
+ gchar *old_cset_skip_characters;
+ gchar *old_cset_identifier_first;
+ gchar *old_cset_identifier_nth;
+ GString *buffer;
+ GValue src = G_VALUE_INIT;
+ GTokenType token;
+
+ /* parse the next token *before* reconfiguring the scanner, so it
+ * skips whitespace first
+ */
+ token = g_scanner_peek_next_token (scanner);
+
+ if (token == G_TOKEN_STRING)
+ return gimp_config_deserialize_any (value, prop_spec, scanner);
+
+ old_cset_skip_characters = scanner->config->cset_skip_characters;
+ old_cset_identifier_first = scanner->config->cset_identifier_first;
+ old_cset_identifier_nth = scanner->config->cset_identifier_nth;
+
+ scanner->config->cset_skip_characters = "";
+ scanner->config->cset_identifier_first = ( G_CSET_a_2_z G_CSET_A_2_Z "." );
+ scanner->config->cset_identifier_nth = ( G_CSET_a_2_z G_CSET_A_2_Z
+ G_CSET_DIGITS "-_." );
+
+ buffer = g_string_new ("");
+
+ while (g_scanner_peek_next_token (scanner) != G_TOKEN_RIGHT_PAREN)
+ {
+ token = g_scanner_peek_next_token (scanner);
+
+ if (token == G_TOKEN_IDENTIFIER)
+ {
+ g_scanner_get_next_token (scanner);
+ g_string_append (buffer, scanner->value.v_identifier);
+ }
+ else if (token == G_TOKEN_CHAR)
+ {
+ g_scanner_get_next_token (scanner);
+ g_string_append_c (buffer, scanner->value.v_char);
+ }
+ else if (token == ' ')
+ {
+ g_scanner_get_next_token (scanner);
+ g_string_append_c (buffer, token);
+ }
+ else
+ {
+ token = G_TOKEN_IDENTIFIER;
+ goto cleanup;
+ }
+ }
+
+ g_value_init (&src, G_TYPE_STRING);
+ g_value_set_static_string (&src, buffer->str);
+ g_value_transform (&src, value);
+ g_value_unset (&src);
+
+ token = G_TOKEN_RIGHT_PAREN;
+
+ cleanup:
+
+ g_string_free (buffer, TRUE);
+
+ scanner->config->cset_skip_characters = old_cset_skip_characters;
+ scanner->config->cset_identifier_first = old_cset_identifier_first;
+ scanner->config->cset_identifier_nth = old_cset_identifier_nth;
+
+ return token;
+}
+
+static GTokenType
+gimp_config_deserialize_file_value (GValue *value,
+ GParamSpec *prop_spec,
+ GScanner *scanner)
+{
+ GTokenType token;
+
+ token = g_scanner_peek_next_token (scanner);
+
+ if (token != G_TOKEN_IDENTIFIER &&
+ token != G_TOKEN_STRING)
+ {
+ return G_TOKEN_STRING;
+ }
+
+ g_scanner_get_next_token (scanner);
+
+ if (token == G_TOKEN_IDENTIFIER)
+ {
+ /* this is supposed to parse a literal "NULL" only, but so what... */
+ g_value_set_object (value, NULL);
+ }
+ else
+ {
+ gchar *path = gimp_config_path_expand (scanner->value.v_string, TRUE,
+ NULL);
+
+ if (path)
+ {
+ GFile *file = g_file_new_for_path (path);
+
+ g_value_take_object (value, file);
+ g_free (path);
+ }
+ else
+ {
+ g_value_set_object (value, NULL);
+ }
+ }
+
+ return G_TOKEN_RIGHT_PAREN;
+}
+
+static GTokenType
+gimp_config_deserialize_any (GValue *value,
+ GParamSpec *prop_spec,
+ GScanner *scanner)
+{
+ GValue src = G_VALUE_INIT;
+ GTokenType token;
+
+ if (!g_value_type_transformable (G_TYPE_STRING, prop_spec->value_type))
+ {
+ g_warning ("%s: %s can not be transformed from a string",
+ G_STRFUNC, g_type_name (prop_spec->value_type));
+ return G_TOKEN_NONE;
+ }
+
+ token = g_scanner_peek_next_token (scanner);
+
+ if (token != G_TOKEN_IDENTIFIER &&
+ token != G_TOKEN_STRING)
+ {
+ return G_TOKEN_IDENTIFIER;
+ }
+
+ g_scanner_get_next_token (scanner);
+
+ g_value_init (&src, G_TYPE_STRING);
+
+ if (token == G_TOKEN_IDENTIFIER)
+ g_value_set_static_string (&src, scanner->value.v_identifier);
+ else
+ g_value_set_static_string (&src, scanner->value.v_string);
+
+ g_value_transform (&src, value);
+ g_value_unset (&src);
+
+ return G_TOKEN_RIGHT_PAREN;
+}
+
+static GTokenType
+gimp_config_skip_unknown_property (GScanner *scanner)
+{
+ gint open_paren = 0;
+
+ while (TRUE)
+ {
+ GTokenType token = g_scanner_peek_next_token (scanner);
+
+ switch (token)
+ {
+ case G_TOKEN_LEFT_PAREN:
+ open_paren++;
+ g_scanner_get_next_token (scanner);
+ break;
+
+ case G_TOKEN_RIGHT_PAREN:
+ if (open_paren == 0)
+ return token;
+
+ open_paren--;
+ g_scanner_get_next_token (scanner);
+ break;
+
+ case G_TOKEN_EOF:
+ return token;
+
+ default:
+ g_scanner_get_next_token (scanner);
+ break;
+ }
+ }
+}
diff --git a/libgimpconfig/gimpconfig-deserialize.h b/libgimpconfig/gimpconfig-deserialize.h
new file mode 100644
index 0000000..271a20d
--- /dev/null
+++ b/libgimpconfig/gimpconfig-deserialize.h
@@ -0,0 +1,44 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Object properties deserialization routines
+ * Copyright (C) 2001-2002 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_CONFIG_H_INSIDE__) && !defined (GIMP_CONFIG_COMPILATION)
+#error "Only <libgimpconfig/gimpconfig.h> can be included directly."
+#endif
+
+#ifndef __GIMP_CONFIG_DESERIALIZE_H__
+#define __GIMP_CONFIG_DESERIALIZE_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_config_deserialize_properties (GimpConfig *config,
+ GScanner *scanner,
+ gint nest_level);
+GTokenType gimp_config_deserialize_property (GimpConfig *config,
+ GScanner *scanner,
+ gint nest_level);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_CONFIG_DESERIALIZE_H__ */
diff --git a/libgimpconfig/gimpconfig-error.c b/libgimpconfig/gimpconfig-error.c
new file mode 100644
index 0000000..ed57cb0
--- /dev/null
+++ b/libgimpconfig/gimpconfig-error.c
@@ -0,0 +1,51 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * Config file serialization and deserialization interface
+ * Copyright (C) 2001-2002 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+
+#include "gimpconfig-error.h"
+
+
+/**
+ * SECTION: gimpconfig-error
+ * @title: GimpConfig-error
+ * @short_description: Error utils for libgimpconfig.
+ *
+ * Error utils for libgimpconfig.
+ **/
+
+
+/**
+ * gimp_config_error_quark:
+ *
+ * This function is never called directly. Use GIMP_CONFIG_ERROR() instead.
+ *
+ * Return value: the #GQuark that defines the GimpConfig error domain.
+ *
+ * Since: 2.4
+ **/
+GQuark
+gimp_config_error_quark (void)
+{
+ return g_quark_from_static_string ("gimp-config-error-quark");
+}
diff --git a/libgimpconfig/gimpconfig-error.h b/libgimpconfig/gimpconfig-error.h
new file mode 100644
index 0000000..a20884e
--- /dev/null
+++ b/libgimpconfig/gimpconfig-error.h
@@ -0,0 +1,59 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * Copyright (C) 2003 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_CONFIG_H_INSIDE__) && !defined (GIMP_CONFIG_COMPILATION)
+#error "Only <libgimpconfig/gimpconfig.h> can be included directly."
+#endif
+
+#ifndef __GIMP_CONFIG_ERROR_H__
+#define __GIMP_CONFIG_ERROR_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+/**
+ * GimpConfigError:
+ * @GIMP_CONFIG_ERROR_OPEN: open failed
+ * @GIMP_CONFIG_ERROR_OPEN_ENOENT: file does not exist
+ * @GIMP_CONFIG_ERROR_WRITE: write failed
+ * @GIMP_CONFIG_ERROR_PARSE: parser error
+ * @GIMP_CONFIG_ERROR_VERSION: parser failed due to version mismatch
+ *
+ * The possible values of a #GError thrown by libgimpconfig.
+ **/
+typedef enum
+{
+ GIMP_CONFIG_ERROR_OPEN,
+ GIMP_CONFIG_ERROR_OPEN_ENOENT,
+ GIMP_CONFIG_ERROR_WRITE,
+ GIMP_CONFIG_ERROR_PARSE,
+ GIMP_CONFIG_ERROR_VERSION
+} GimpConfigError;
+
+#define GIMP_CONFIG_ERROR (gimp_config_error_quark ())
+
+GQuark gimp_config_error_quark (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif /* __GIMP_CONFIG_ERROR_H__ */
diff --git a/libgimpconfig/gimpconfig-iface.c b/libgimpconfig/gimpconfig-iface.c
new file mode 100644
index 0000000..6604aca
--- /dev/null
+++ b/libgimpconfig/gimpconfig-iface.c
@@ -0,0 +1,859 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * Config file serialization and deserialization interface
+ * Copyright (C) 2001-2002 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gio/gio.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "gimpconfigtypes.h"
+
+#include "gimpconfigwriter.h"
+#include "gimpconfig-iface.h"
+#include "gimpconfig-deserialize.h"
+#include "gimpconfig-serialize.h"
+#include "gimpconfig-params.h"
+#include "gimpconfig-utils.h"
+#include "gimpscanner.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpconfig-iface
+ * @title: GimpConfig-iface
+ * @short_description: High-level API for libgimpconfig.
+ *
+ * High-level API for libgimpconfig.
+ **/
+
+
+/*
+ * The GimpConfig serialization and deserialization interface.
+ */
+
+
+/* local function prototypes */
+
+static void gimp_config_iface_default_init (GimpConfigInterface *iface);
+static void gimp_config_iface_base_init (GimpConfigInterface *iface);
+
+static gboolean gimp_config_iface_serialize (GimpConfig *config,
+ GimpConfigWriter *writer,
+ gpointer data);
+static gboolean gimp_config_iface_deserialize (GimpConfig *config,
+ GScanner *scanner,
+ gint nest_level,
+ gpointer data);
+static GimpConfig * gimp_config_iface_duplicate (GimpConfig *config);
+static gboolean gimp_config_iface_equal (GimpConfig *a,
+ GimpConfig *b);
+static void gimp_config_iface_reset (GimpConfig *config);
+static gboolean gimp_config_iface_copy (GimpConfig *src,
+ GimpConfig *dest,
+ GParamFlags flags);
+
+
+/* private functions */
+
+
+GType
+gimp_config_get_type (void)
+{
+ static GType config_iface_type = 0;
+
+ if (! config_iface_type)
+ {
+ const GTypeInfo config_iface_info =
+ {
+ sizeof (GimpConfigInterface),
+ (GBaseInitFunc) gimp_config_iface_base_init,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gimp_config_iface_default_init,
+ (GClassFinalizeFunc) NULL,
+ };
+
+ config_iface_type = g_type_register_static (G_TYPE_INTERFACE,
+ "GimpConfigInterface",
+ &config_iface_info,
+ 0);
+
+ g_type_interface_add_prerequisite (config_iface_type, G_TYPE_OBJECT);
+ }
+
+ return config_iface_type;
+}
+
+GType
+gimp_config_interface_get_type (void)
+{
+ return gimp_config_get_type ();
+}
+
+static void
+gimp_config_iface_default_init (GimpConfigInterface *iface)
+{
+ iface->serialize = gimp_config_iface_serialize;
+ iface->deserialize = gimp_config_iface_deserialize;
+ iface->duplicate = gimp_config_iface_duplicate;
+ iface->equal = gimp_config_iface_equal;
+ iface->reset = gimp_config_iface_reset;
+ iface->copy = gimp_config_iface_copy;
+}
+
+static void
+gimp_config_iface_base_init (GimpConfigInterface *iface)
+{
+ /* always set these to NULL since we don't want to inherit them
+ * from parent classes
+ */
+ iface->serialize_property = NULL;
+ iface->deserialize_property = NULL;
+}
+
+static gboolean
+gimp_config_iface_serialize (GimpConfig *config,
+ GimpConfigWriter *writer,
+ gpointer data)
+{
+ return gimp_config_serialize_properties (config, writer);
+}
+
+static gboolean
+gimp_config_iface_deserialize (GimpConfig *config,
+ GScanner *scanner,
+ gint nest_level,
+ gpointer data)
+{
+ return gimp_config_deserialize_properties (config, scanner, nest_level);
+}
+
+static GimpConfig *
+gimp_config_iface_duplicate (GimpConfig *config)
+{
+ GObject *object = G_OBJECT (config);
+ GObjectClass *klass = G_OBJECT_GET_CLASS (object);
+ GParamSpec **property_specs;
+ guint n_property_specs;
+ gint n_construct_properties = 0;
+ const gchar **construct_names = NULL;
+ GValue *construct_values = NULL;
+ guint i;
+ GObject *dup;
+
+ property_specs = g_object_class_list_properties (klass, &n_property_specs);
+
+ construct_names = g_new0 (const gchar *, n_property_specs);
+ construct_values = g_new0 (GValue, n_property_specs);
+
+ for (i = 0; i < n_property_specs; i++)
+ {
+ GParamSpec *prop_spec = property_specs[i];
+
+ if ((prop_spec->flags & G_PARAM_READABLE) &&
+ (prop_spec->flags & G_PARAM_WRITABLE) &&
+ (prop_spec->flags & G_PARAM_CONSTRUCT_ONLY))
+ {
+ construct_names[n_construct_properties] = prop_spec->name;
+
+ g_value_init (&construct_values[n_construct_properties],
+ prop_spec->value_type);
+ g_object_get_property (object, prop_spec->name,
+ &construct_values[n_construct_properties]);
+
+ n_construct_properties++;
+ }
+ }
+
+ g_free (property_specs);
+
+ dup = g_object_new_with_properties (G_TYPE_FROM_INSTANCE (object),
+ n_construct_properties,
+ (const gchar **) construct_names,
+ (const GValue *) construct_values);
+
+ for (i = 0; i < n_construct_properties; i++)
+ g_value_unset (&construct_values[i]);
+
+ g_free (construct_names);
+ g_free (construct_values);
+
+ gimp_config_copy (config, GIMP_CONFIG (dup), 0);
+
+ return GIMP_CONFIG (dup);
+}
+
+static gboolean
+gimp_config_iface_equal (GimpConfig *a,
+ GimpConfig *b)
+{
+ GObjectClass *klass;
+ GParamSpec **property_specs;
+ guint n_property_specs;
+ guint i;
+ gboolean equal = TRUE;
+
+ klass = G_OBJECT_GET_CLASS (a);
+
+ property_specs = g_object_class_list_properties (klass, &n_property_specs);
+
+ for (i = 0; equal && i < n_property_specs; i++)
+ {
+ GParamSpec *prop_spec;
+ GValue a_value = G_VALUE_INIT;
+ GValue b_value = G_VALUE_INIT;
+
+ prop_spec = property_specs[i];
+
+ if (! (prop_spec->flags & G_PARAM_READABLE))
+ continue;
+
+ g_value_init (&a_value, prop_spec->value_type);
+ g_value_init (&b_value, prop_spec->value_type);
+ g_object_get_property (G_OBJECT (a), prop_spec->name, &a_value);
+ g_object_get_property (G_OBJECT (b), prop_spec->name, &b_value);
+
+ if (g_param_values_cmp (prop_spec, &a_value, &b_value))
+ {
+ if ((prop_spec->flags & GIMP_CONFIG_PARAM_AGGREGATE) &&
+ G_IS_PARAM_SPEC_OBJECT (prop_spec) &&
+ g_type_interface_peek (g_type_class_peek (prop_spec->value_type),
+ GIMP_TYPE_CONFIG))
+ {
+ if (! gimp_config_is_equal_to (g_value_get_object (&a_value),
+ g_value_get_object (&b_value)))
+ {
+ equal = FALSE;
+ }
+ }
+ else
+ {
+ equal = FALSE;
+ }
+ }
+
+ g_value_unset (&a_value);
+ g_value_unset (&b_value);
+ }
+
+ g_free (property_specs);
+
+ return equal;
+}
+
+static void
+gimp_config_iface_reset (GimpConfig *config)
+{
+ gimp_config_reset_properties (G_OBJECT (config));
+}
+
+static gboolean
+gimp_config_iface_copy (GimpConfig *src,
+ GimpConfig *dest,
+ GParamFlags flags)
+{
+ return gimp_config_sync (G_OBJECT (src), G_OBJECT (dest), flags);
+}
+
+
+/* public functions */
+
+
+/**
+ * gimp_config_serialize_to_file:
+ * @config: a #GObject that implements the #GimpConfigInterface.
+ * @filename: the name of the file to write the configuration to.
+ * @header: optional file header (must be ASCII only)
+ * @footer: optional file footer (must be ASCII only)
+ * @data: user data passed to the serialize implementation.
+ * @error: return location for a possible error
+ *
+ * Serializes the object properties of @config to the file specified
+ * by @filename. If a file with that name already exists, it is
+ * overwritten. Basically this function opens @filename for you and
+ * calls the serialize function of the @config's #GimpConfigInterface.
+ *
+ * Return value: %TRUE if serialization succeeded, %FALSE otherwise.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_config_serialize_to_file (GimpConfig *config,
+ const gchar *filename,
+ const gchar *header,
+ const gchar *footer,
+ gpointer data,
+ GError **error)
+{
+ GimpConfigWriter *writer;
+
+ g_return_val_if_fail (GIMP_IS_CONFIG (config), FALSE);
+ g_return_val_if_fail (filename != NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ writer = gimp_config_writer_new_file (filename, TRUE, header, error);
+ if (!writer)
+ return FALSE;
+
+ GIMP_CONFIG_GET_INTERFACE (config)->serialize (config, writer, data);
+
+ return gimp_config_writer_finish (writer, footer, error);
+}
+
+/**
+ * gimp_config_serialize_to_gfile:
+ * @config: a #GObject that implements the #GimpConfigInterface.
+ * @file: the #GFile to write the configuration to.
+ * @header: optional file header (must be ASCII only)
+ * @footer: optional file footer (must be ASCII only)
+ * @data: user data passed to the serialize implementation.
+ * @error: return location for a possible error
+ *
+ * Serializes the object properties of @config to the file specified
+ * by @file. If a file with that name already exists, it is
+ * overwritten. Basically this function opens @file for you and calls
+ * the serialize function of the @config's #GimpConfigInterface.
+ *
+ * Return value: %TRUE if serialization succeeded, %FALSE otherwise.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_config_serialize_to_gfile (GimpConfig *config,
+ GFile *file,
+ const gchar *header,
+ const gchar *footer,
+ gpointer data,
+ GError **error)
+{
+ GimpConfigWriter *writer;
+
+ g_return_val_if_fail (GIMP_IS_CONFIG (config), FALSE);
+ g_return_val_if_fail (G_IS_FILE (file), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ writer = gimp_config_writer_new_gfile (file, TRUE, header, error);
+ if (!writer)
+ return FALSE;
+
+ GIMP_CONFIG_GET_INTERFACE (config)->serialize (config, writer, data);
+
+ return gimp_config_writer_finish (writer, footer, error);
+}
+
+/**
+ * gimp_config_serialize_to_stream:
+ * @config: a #GObject that implements the #GimpConfigInterface.
+ * @output: the #GOutputStream to write the configuration to.
+ * @header: optional file header (must be ASCII only)
+ * @footer: optional file footer (must be ASCII only)
+ * @data: user data passed to the serialize implementation.
+ * @error: return location for a possible error
+ *
+ * Serializes the object properties of @config to the stream specified
+ * by @output.
+ *
+ * Return value: %TRUE if serialization succeeded, %FALSE otherwise.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_config_serialize_to_stream (GimpConfig *config,
+ GOutputStream *output,
+ const gchar *header,
+ const gchar *footer,
+ gpointer data,
+ GError **error)
+{
+ GimpConfigWriter *writer;
+
+ g_return_val_if_fail (GIMP_IS_CONFIG (config), FALSE);
+ g_return_val_if_fail (G_IS_OUTPUT_STREAM (output), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ writer = gimp_config_writer_new_stream (output, header, error);
+ if (!writer)
+ return FALSE;
+
+ GIMP_CONFIG_GET_INTERFACE (config)->serialize (config, writer, data);
+
+ return gimp_config_writer_finish (writer, footer, error);
+}
+
+/**
+ * gimp_config_serialize_to_fd:
+ * @config: a #GObject that implements the #GimpConfigInterface.
+ * @fd: a file descriptor, opened for writing
+ * @data: user data passed to the serialize implementation.
+ *
+ * Serializes the object properties of @config to the given file
+ * descriptor.
+ *
+ * Return value: %TRUE if serialization succeeded, %FALSE otherwise.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_config_serialize_to_fd (GimpConfig *config,
+ gint fd,
+ gpointer data)
+{
+ GimpConfigWriter *writer;
+
+ g_return_val_if_fail (GIMP_IS_CONFIG (config), FALSE);
+ g_return_val_if_fail (fd > 0, FALSE);
+
+ writer = gimp_config_writer_new_fd (fd);
+ if (!writer)
+ return FALSE;
+
+ GIMP_CONFIG_GET_INTERFACE (config)->serialize (config, writer, data);
+
+ return gimp_config_writer_finish (writer, NULL, NULL);
+}
+
+/**
+ * gimp_config_serialize_to_string:
+ * @config: a #GObject that implements the #GimpConfigInterface.
+ * @data: user data passed to the serialize implementation.
+ *
+ * Serializes the object properties of @config to a string.
+ *
+ * Return value: a newly allocated NUL-terminated string.
+ *
+ * Since: 2.4
+ **/
+gchar *
+gimp_config_serialize_to_string (GimpConfig *config,
+ gpointer data)
+{
+ GimpConfigWriter *writer;
+ GString *str;
+
+ g_return_val_if_fail (GIMP_IS_CONFIG (config), NULL);
+
+ str = g_string_new (NULL);
+ writer = gimp_config_writer_new_string (str);
+
+ GIMP_CONFIG_GET_INTERFACE (config)->serialize (config, writer, data);
+
+ gimp_config_writer_finish (writer, NULL, NULL);
+
+ return g_string_free (str, FALSE);
+}
+
+/**
+ * gimp_config_deserialize_file:
+ * @config: a #GObject that implements the #GimpConfigInterface.
+ * @filename: the name of the file to read configuration from.
+ * @data: user data passed to the deserialize implementation.
+ * @error: return location for a possible error
+ *
+ * Opens the file specified by @filename, reads configuration data
+ * from it and configures @config accordingly. Basically this function
+ * creates a properly configured #GScanner for you and calls the
+ * deserialize function of the @config's #GimpConfigInterface.
+ *
+ * Return value: %TRUE if deserialization succeeded, %FALSE otherwise.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_config_deserialize_file (GimpConfig *config,
+ const gchar *filename,
+ gpointer data,
+ GError **error)
+{
+ GScanner *scanner;
+ gboolean success;
+
+ g_return_val_if_fail (GIMP_IS_CONFIG (config), FALSE);
+ g_return_val_if_fail (filename != NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ scanner = gimp_scanner_new_file (filename, error);
+ if (! scanner)
+ return FALSE;
+
+ g_object_freeze_notify (G_OBJECT (config));
+
+ success = GIMP_CONFIG_GET_INTERFACE (config)->deserialize (config,
+ scanner, 0, data);
+
+ g_object_thaw_notify (G_OBJECT (config));
+
+ gimp_scanner_destroy (scanner);
+
+ if (! success)
+ g_assert (error == NULL || *error != NULL);
+
+ return success;
+}
+
+/**
+ * gimp_config_deserialize_gfile:
+ * @config: a #GObject that implements the #GimpConfigInterface.
+ * @file: the #GFile to read configuration from.
+ * @data: user data passed to the deserialize implementation.
+ * @error: return location for a possible error
+ *
+ * Opens the file specified by @file, reads configuration data from it
+ * and configures @config accordingly. Basically this function creates
+ * a properly configured #GScanner for you and calls the deserialize
+ * function of the @config's #GimpConfigInterface.
+ *
+ * Return value: %TRUE if deserialization succeeded, %FALSE otherwise.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_config_deserialize_gfile (GimpConfig *config,
+ GFile *file,
+ gpointer data,
+ GError **error)
+{
+ GScanner *scanner;
+ gboolean success;
+
+ g_return_val_if_fail (GIMP_IS_CONFIG (config), FALSE);
+ g_return_val_if_fail (G_IS_FILE (file), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ scanner = gimp_scanner_new_gfile (file, error);
+ if (! scanner)
+ return FALSE;
+
+ g_object_freeze_notify (G_OBJECT (config));
+
+ success = GIMP_CONFIG_GET_INTERFACE (config)->deserialize (config,
+ scanner, 0, data);
+
+ g_object_thaw_notify (G_OBJECT (config));
+
+ gimp_scanner_destroy (scanner);
+
+ if (! success)
+ g_assert (error == NULL || *error != NULL);
+
+ return success;
+}
+
+/**
+ * gimp_config_deserialize_stream:
+ * @config: a #GObject that implements the #GimpConfigInterface.
+ * @input: the #GInputStream to read configuration from.
+ * @data: user data passed to the deserialize implementation.
+ * @error: return location for a possible error
+ *
+ * Reads configuration data from @input and configures @config
+ * accordingly. Basically this function creates a properly configured
+ * #GScanner for you and calls the deserialize function of the
+ * @config's #GimpConfigInterface.
+ *
+ * Return value: %TRUE if deserialization succeeded, %FALSE otherwise.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_config_deserialize_stream (GimpConfig *config,
+ GInputStream *input,
+ gpointer data,
+ GError **error)
+{
+ GScanner *scanner;
+ gboolean success;
+
+ g_return_val_if_fail (GIMP_IS_CONFIG (config), FALSE);
+ g_return_val_if_fail (G_IS_INPUT_STREAM (input), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ scanner = gimp_scanner_new_stream (input, error);
+ if (! scanner)
+ return FALSE;
+
+ g_object_freeze_notify (G_OBJECT (config));
+
+ success = GIMP_CONFIG_GET_INTERFACE (config)->deserialize (config,
+ scanner, 0, data);
+
+ g_object_thaw_notify (G_OBJECT (config));
+
+ gimp_scanner_destroy (scanner);
+
+ if (! success)
+ g_assert (error == NULL || *error != NULL);
+
+ return success;
+}
+
+/**
+ * gimp_config_deserialize_string:
+ * @config: a #GObject that implements the #GimpConfigInterface.
+ * @text: string to deserialize (in UTF-8 encoding)
+ * @text_len: length of @text in bytes or -1
+ * @data: client data
+ * @error: return location for a possible error
+ *
+ * Configures @config from @text. Basically this function creates a
+ * properly configured #GScanner for you and calls the deserialize
+ * function of the @config's #GimpConfigInterface.
+ *
+ * Returns: %TRUE if deserialization succeeded, %FALSE otherwise.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_config_deserialize_string (GimpConfig *config,
+ const gchar *text,
+ gint text_len,
+ gpointer data,
+ GError **error)
+{
+ GScanner *scanner;
+ gboolean success;
+
+ g_return_val_if_fail (GIMP_IS_CONFIG (config), FALSE);
+ g_return_val_if_fail (text != NULL || text_len == 0, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ scanner = gimp_scanner_new_string (text, text_len, error);
+
+ g_object_freeze_notify (G_OBJECT (config));
+
+ success = GIMP_CONFIG_GET_INTERFACE (config)->deserialize (config,
+ scanner, 0, data);
+
+ g_object_thaw_notify (G_OBJECT (config));
+
+ gimp_scanner_destroy (scanner);
+
+ if (! success)
+ g_assert (error == NULL || *error != NULL);
+
+ return success;
+}
+
+/**
+ * gimp_config_deserialize_return:
+ * @scanner: a #GScanner
+ * @expected_token: the expected token
+ * @nest_level: the nest level
+ *
+ * Returns:
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_config_deserialize_return (GScanner *scanner,
+ GTokenType expected_token,
+ gint nest_level)
+{
+ GTokenType next_token;
+
+ g_return_val_if_fail (scanner != NULL, FALSE);
+
+ next_token = g_scanner_peek_next_token (scanner);
+
+ if (expected_token != G_TOKEN_LEFT_PAREN)
+ {
+ g_scanner_get_next_token (scanner);
+ g_scanner_unexp_token (scanner, expected_token, NULL, NULL, NULL,
+ _("fatal parse error"), TRUE);
+ return FALSE;
+ }
+ else
+ {
+ if (nest_level > 0 && next_token == G_TOKEN_RIGHT_PAREN)
+ {
+ return TRUE;
+ }
+ else if (next_token != G_TOKEN_EOF)
+ {
+ g_scanner_get_next_token (scanner);
+ g_scanner_unexp_token (scanner, expected_token, NULL, NULL, NULL,
+ _("fatal parse error"), TRUE);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+/**
+ * gimp_config_serialize:
+ * @config: a #GObject that implements the #GimpConfigInterface.
+ * @writer: the #GimpConfigWriter to use.
+ * @data: client data
+ *
+ * Serialize the #GimpConfig object.
+ *
+ * Returns: %TRUE if serialization succeeded, %FALSE otherwise.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_config_serialize (GimpConfig *config,
+ GimpConfigWriter *writer,
+ gpointer data)
+{
+ g_return_val_if_fail (GIMP_IS_CONFIG (config), FALSE);
+
+ return GIMP_CONFIG_GET_INTERFACE (config)->serialize (config,
+ writer,
+ data);
+}
+
+/**
+ * gimp_config_deserialize:
+ * @config: a #GObject that implements the #GimpConfigInterface.
+ * @scanner: the #GScanner to use.
+ * @nest_level: the nest level.
+ * @data: client data.
+ *
+ * Deserialize the #GimpConfig object.
+ *
+ * Returns: %TRUE if deserialization succeeded, %FALSE otherwise.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_config_deserialize (GimpConfig *config,
+ GScanner *scanner,
+ gint nest_level,
+ gpointer data)
+{
+ g_return_val_if_fail (GIMP_IS_CONFIG (config), FALSE);
+
+ return GIMP_CONFIG_GET_INTERFACE (config)->deserialize (config,
+ scanner,
+ nest_level,
+ data);
+}
+
+/**
+ * gimp_config_duplicate:
+ * @config: a #GObject that implements the #GimpConfigInterface.
+ *
+ * Creates a copy of the passed object by copying all object
+ * properties. The default implementation of the #GimpConfigInterface
+ * only works for objects that are completely defined by their
+ * properties.
+ *
+ * Return value: the duplicated #GimpConfig object
+ *
+ * Since: 2.4
+ **/
+gpointer
+gimp_config_duplicate (GimpConfig *config)
+{
+ g_return_val_if_fail (GIMP_IS_CONFIG (config), NULL);
+
+ return GIMP_CONFIG_GET_INTERFACE (config)->duplicate (config);
+}
+
+/**
+ * gimp_config_is_equal_to:
+ * @a: a #GObject that implements the #GimpConfigInterface.
+ * @b: another #GObject of the same type as @a.
+ *
+ * Compares the two objects. The default implementation of the
+ * #GimpConfigInterface compares the object properties and thus only
+ * works for objects that are completely defined by their
+ * properties.
+ *
+ * Return value: %TRUE if the two objects are equal.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_config_is_equal_to (GimpConfig *a,
+ GimpConfig *b)
+{
+ g_return_val_if_fail (GIMP_IS_CONFIG (a), FALSE);
+ g_return_val_if_fail (GIMP_IS_CONFIG (b), FALSE);
+ g_return_val_if_fail (G_TYPE_FROM_INSTANCE (a) == G_TYPE_FROM_INSTANCE (b),
+ FALSE);
+
+ return GIMP_CONFIG_GET_INTERFACE (a)->equal (a, b);
+}
+
+/**
+ * gimp_config_reset:
+ * @config: a #GObject that implements the #GimpConfigInterface.
+ *
+ * Resets the object to its default state. The default implementation of the
+ * #GimpConfigInterface only works for objects that are completely defined by
+ * their properties.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_config_reset (GimpConfig *config)
+{
+ g_return_if_fail (GIMP_IS_CONFIG (config));
+
+ g_object_freeze_notify (G_OBJECT (config));
+
+ GIMP_CONFIG_GET_INTERFACE (config)->reset (config);
+
+ g_object_thaw_notify (G_OBJECT (config));
+}
+
+/**
+ * gimp_config_copy:
+ * @src: a #GObject that implements the #GimpConfigInterface.
+ * @dest: another #GObject of the same type as @a.
+ * @flags: a mask of GParamFlags
+ *
+ * Compares all read- and write-able properties from @src and @dest
+ * that have all @flags set. Differing values are then copied from
+ * @src to @dest. If @flags is 0, all differing read/write properties.
+ *
+ * Properties marked as "construct-only" are not touched.
+ *
+ * Return value: %TRUE if @dest was modified, %FALSE otherwise
+ *
+ * Since: 2.6
+ **/
+gboolean
+gimp_config_copy (GimpConfig *src,
+ GimpConfig *dest,
+ GParamFlags flags)
+{
+ gboolean changed;
+
+ g_return_val_if_fail (GIMP_IS_CONFIG (src), FALSE);
+ g_return_val_if_fail (GIMP_IS_CONFIG (dest), FALSE);
+ g_return_val_if_fail (G_TYPE_FROM_INSTANCE (src) == G_TYPE_FROM_INSTANCE (dest),
+ FALSE);
+
+ g_object_freeze_notify (G_OBJECT (dest));
+
+ changed = GIMP_CONFIG_GET_INTERFACE (src)->copy (src, dest, flags);
+
+ g_object_thaw_notify (G_OBJECT (dest));
+
+ return changed;
+}
diff --git a/libgimpconfig/gimpconfig-iface.h b/libgimpconfig/gimpconfig-iface.h
new file mode 100644
index 0000000..eae41b5
--- /dev/null
+++ b/libgimpconfig/gimpconfig-iface.h
@@ -0,0 +1,141 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * Config file serialization and deserialization interface
+ * Copyright (C) 2001-2003 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_CONFIG_H_INSIDE__) && !defined (GIMP_CONFIG_COMPILATION)
+#error "Only <libgimpconfig/gimpconfig.h> can be included directly."
+#endif
+
+#ifndef __GIMP_CONFIG_IFACE_H__
+#define __GIMP_CONFIG_IFACE_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+#define GIMP_TYPE_CONFIG (gimp_config_get_type ())
+#define GIMP_IS_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CONFIG))
+#define GIMP_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CONFIG, GimpConfig))
+#define GIMP_CONFIG_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GIMP_TYPE_CONFIG, GimpConfigInterface))
+
+
+typedef struct _GimpConfigInterface GimpConfigInterface;
+
+struct _GimpConfigInterface
+{
+ GTypeInterface base_iface;
+
+ gboolean (* serialize) (GimpConfig *config,
+ GimpConfigWriter *writer,
+ gpointer data);
+ gboolean (* deserialize) (GimpConfig *config,
+ GScanner *scanner,
+ gint nest_level,
+ gpointer data);
+ gboolean (* serialize_property) (GimpConfig *config,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec,
+ GimpConfigWriter *writer);
+ gboolean (* deserialize_property) (GimpConfig *config,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec,
+ GScanner *scanner,
+ GTokenType *expected);
+ GimpConfig * (* duplicate) (GimpConfig *config);
+ gboolean (* equal) (GimpConfig *a,
+ GimpConfig *b);
+ void (* reset) (GimpConfig *config);
+ gboolean (* copy) (GimpConfig *src,
+ GimpConfig *dest,
+ GParamFlags flags);
+};
+
+
+GType gimp_config_get_type (void) G_GNUC_CONST;
+
+GIMP_DEPRECATED_FOR (gimp_config_get_type)
+GType gimp_config_interface_get_type (void) G_GNUC_CONST;
+
+gboolean gimp_config_serialize_to_file (GimpConfig *config,
+ const gchar *filename,
+ const gchar *header,
+ const gchar *footer,
+ gpointer data,
+ GError **error);
+gboolean gimp_config_serialize_to_gfile (GimpConfig *config,
+ GFile *file,
+ const gchar *header,
+ const gchar *footer,
+ gpointer data,
+ GError **error);
+gboolean gimp_config_serialize_to_stream (GimpConfig *config,
+ GOutputStream *output,
+ const gchar *header,
+ const gchar *footer,
+ gpointer data,
+ GError **error);
+gboolean gimp_config_serialize_to_fd (GimpConfig *config,
+ gint fd,
+ gpointer data);
+gchar * gimp_config_serialize_to_string (GimpConfig *config,
+ gpointer data);
+gboolean gimp_config_deserialize_file (GimpConfig *config,
+ const gchar *filename,
+ gpointer data,
+ GError **error);
+gboolean gimp_config_deserialize_gfile (GimpConfig *config,
+ GFile *file,
+ gpointer data,
+ GError **error);
+gboolean gimp_config_deserialize_stream (GimpConfig *config,
+ GInputStream *input,
+ gpointer data,
+ GError **error);
+gboolean gimp_config_deserialize_string (GimpConfig *config,
+ const gchar *text,
+ gint text_len,
+ gpointer data,
+ GError **error);
+gboolean gimp_config_deserialize_return (GScanner *scanner,
+ GTokenType expected_token,
+ gint nest_level);
+
+gboolean gimp_config_serialize (GimpConfig *config,
+ GimpConfigWriter *writer,
+ gpointer data);
+gboolean gimp_config_deserialize (GimpConfig *config,
+ GScanner *scanner,
+ gint nest_level,
+ gpointer data);
+gpointer gimp_config_duplicate (GimpConfig *config);
+gboolean gimp_config_is_equal_to (GimpConfig *a,
+ GimpConfig *b);
+void gimp_config_reset (GimpConfig *config);
+gboolean gimp_config_copy (GimpConfig *src,
+ GimpConfig *dest,
+ GParamFlags flags);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_CONFIG_IFACE_H__ */
diff --git a/libgimpconfig/gimpconfig-params.h b/libgimpconfig/gimpconfig-params.h
new file mode 100644
index 0000000..e63631a
--- /dev/null
+++ b/libgimpconfig/gimpconfig-params.h
@@ -0,0 +1,240 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * ParamSpecs for config objects
+ * Copyright (C) 2001 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_CONFIG_H_INSIDE__) && !defined (GIMP_CONFIG_COMPILATION)
+#error "Only <libgimpconfig/gimpconfig.h> can be included directly."
+#endif
+
+#ifndef __GIMP_CONFIG_PARAMS_H__
+#define __GIMP_CONFIG_PARAMS_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+/**
+ * SECTION: gimpconfig-params
+ * @title: GimpConfig-params
+ * @short_description: Macros and defines to install config properties.
+ *
+ * Macros and defines to install config properties.
+ **/
+
+
+/*
+ * GIMP_CONFIG_PARAM_SERIALIZE - A property that can and should be
+ * serialized and deserialized.
+ * GIMP_CONFIG_PARAM_AGGREGATE - The object property is to be treated as
+ * part of the parent object.
+ * GIMP_CONFIG_PARAM_RESTART - Changes to this property take effect only
+ * after a restart.
+ * GIMP_CONFIG_PARAM_CONFIRM - Changes to this property should be
+ * confirmed by the user before being applied.
+ * GIMP_CONFIG_PARAM_DEFAULTS - Don't serialize this property if it has the
+ * default value.
+ * GIMP_CONFIG_PARAM_IGNORE - This property exists for obscure reasons
+ * or is needed for backward compatibility.
+ * Ignore the value read and don't serialize it.
+ */
+
+#define GIMP_CONFIG_PARAM_SERIALIZE (1 << (0 + G_PARAM_USER_SHIFT))
+#define GIMP_CONFIG_PARAM_AGGREGATE (1 << (1 + G_PARAM_USER_SHIFT))
+#define GIMP_CONFIG_PARAM_RESTART (1 << (2 + G_PARAM_USER_SHIFT))
+#define GIMP_CONFIG_PARAM_CONFIRM (1 << (3 + G_PARAM_USER_SHIFT))
+#define GIMP_CONFIG_PARAM_DEFAULTS (1 << (4 + G_PARAM_USER_SHIFT))
+#define GIMP_CONFIG_PARAM_IGNORE (1 << (5 + G_PARAM_USER_SHIFT))
+
+#define GIMP_CONFIG_PARAM_FLAGS (G_PARAM_READWRITE | \
+ G_PARAM_CONSTRUCT | \
+ GIMP_CONFIG_PARAM_SERIALIZE)
+
+
+
+/* some convenience macros to install object properties */
+
+#define GIMP_CONFIG_PROP_BOOLEAN(class, id, name, nick, blurb, default, flags) \
+ g_object_class_install_property (class, id,\
+ g_param_spec_boolean (name, nick, blurb,\
+ default,\
+ flags | GIMP_CONFIG_PARAM_FLAGS))
+
+#define GIMP_CONFIG_PROP_INT(class, id, name, nick, blurb, min, max, default, flags) \
+ g_object_class_install_property (class, id,\
+ g_param_spec_int (name, nick, blurb,\
+ min, max, default,\
+ flags | GIMP_CONFIG_PARAM_FLAGS))
+
+#define GIMP_CONFIG_PROP_UINT(class, id, name, nick, blurb, min, max, default, flags) \
+ g_object_class_install_property (class, id,\
+ g_param_spec_uint (name, nick, blurb,\
+ min, max, default,\
+ flags | GIMP_CONFIG_PARAM_FLAGS))
+
+#define GIMP_CONFIG_PROP_INT64(class, id, name, nick, blurb, min, max, default, flags) \
+ g_object_class_install_property (class, id,\
+ g_param_spec_int64 (name, nick, blurb,\
+ min, max, default,\
+ flags | GIMP_CONFIG_PARAM_FLAGS))
+
+#define GIMP_CONFIG_PROP_UINT64(class, id, name, nick, blurb, min, max, default, flags) \
+ g_object_class_install_property (class, id,\
+ g_param_spec_uint64 (name, nick, blurb,\
+ min, max, default,\
+ flags | GIMP_CONFIG_PARAM_FLAGS))
+
+#define GIMP_CONFIG_PROP_UNIT(class, id, name, nick, blurb, pixels, percent, default, flags) \
+ g_object_class_install_property (class, id,\
+ gimp_param_spec_unit (name, nick, blurb,\
+ pixels, percent, default,\
+ flags | GIMP_CONFIG_PARAM_FLAGS))
+
+#define GIMP_CONFIG_PROP_MEMSIZE(class, id, name, nick, blurb, min, max, default, flags) \
+ g_object_class_install_property (class, id,\
+ gimp_param_spec_memsize (name, nick, blurb,\
+ min, max, default,\
+ flags | GIMP_CONFIG_PARAM_FLAGS))
+
+#define GIMP_CONFIG_PROP_DOUBLE(class, id, name, nick, blurb, min, max, default, flags) \
+ g_object_class_install_property (class, id,\
+ g_param_spec_double (name, nick, blurb,\
+ min, max, default,\
+ flags | GIMP_CONFIG_PARAM_FLAGS))
+
+#define GIMP_CONFIG_PROP_RESOLUTION(class, id, name, nick, blurb, default, flags) \
+ g_object_class_install_property (class, id,\
+ g_param_spec_double (name, nick, blurb,\
+ GIMP_MIN_RESOLUTION, GIMP_MAX_RESOLUTION, \
+ default,\
+ flags | GIMP_CONFIG_PARAM_FLAGS))
+
+#define GIMP_CONFIG_PROP_ENUM(class, id, name, nick, blurb, enum_type, default, flags) \
+ g_object_class_install_property (class, id,\
+ g_param_spec_enum (name, nick, blurb,\
+ enum_type, default,\
+ flags | GIMP_CONFIG_PARAM_FLAGS))
+
+#define GIMP_CONFIG_PROP_STRING(class, id, name, nick, blurb, default, flags) \
+ g_object_class_install_property (class, id,\
+ g_param_spec_string (name, nick, blurb,\
+ default,\
+ flags | GIMP_CONFIG_PARAM_FLAGS))
+
+#define GIMP_CONFIG_PROP_PATH(class, id, name, nick, blurb, path_type, default, flags) \
+ g_object_class_install_property (class, id,\
+ gimp_param_spec_config_path (name, nick, blurb,\
+ path_type, default,\
+ flags | GIMP_CONFIG_PARAM_FLAGS))
+
+#define GIMP_CONFIG_PROP_RGB(class, id, name, nick, blurb, has_alpha, default, flags) \
+ g_object_class_install_property (class, id,\
+ gimp_param_spec_rgb (name, nick, blurb,\
+ has_alpha, default, \
+ flags | GIMP_CONFIG_PARAM_FLAGS))
+
+#define GIMP_CONFIG_PROP_MATRIX2(class, id, name, nick, blurb, default, flags) \
+ g_object_class_install_property (class, id,\
+ gimp_param_spec_matrix2 (name, nick, blurb,\
+ default,\
+ flags | GIMP_CONFIG_PARAM_FLAGS))
+
+
+/* object, boxed and pointer properties are _not_ G_PARAM_CONSTRUCT */
+
+#define GIMP_CONFIG_PROP_OBJECT(class, id, name, nick, blurb, object_type, flags) \
+ g_object_class_install_property (class, id,\
+ g_param_spec_object (name, nick, blurb,\
+ object_type,\
+ flags |\
+ G_PARAM_READWRITE |\
+ GIMP_CONFIG_PARAM_SERIALIZE))
+
+#define GIMP_CONFIG_PROP_BOXED(class, id, name, nick, blurb, boxed_type, flags) \
+ g_object_class_install_property (class, id,\
+ g_param_spec_boxed (name, nick, blurb,\
+ boxed_type,\
+ flags |\
+ G_PARAM_READWRITE |\
+ GIMP_CONFIG_PARAM_SERIALIZE))
+
+#define GIMP_CONFIG_PROP_POINTER(class, id, name, nick, blurb, flags) \
+ g_object_class_install_property (class, id,\
+ g_param_spec_pointer (name, nick, blurb,\
+ flags |\
+ G_PARAM_READWRITE |\
+ GIMP_CONFIG_PARAM_SERIALIZE))
+
+
+/* deprecated macros, they all lack the "nick" parameter */
+
+#ifndef GIMP_DISABLE_DEPRECATED
+
+#define GIMP_CONFIG_INSTALL_PROP_BOOLEAN(class, id, name, blurb, default, flags) \
+ GIMP_CONFIG_PROP_BOOLEAN(class, id, name, NULL, blurb, default, flags);
+
+#define GIMP_CONFIG_INSTALL_PROP_INT(class, id, name, blurb, min, max, default, flags) \
+ GIMP_CONFIG_PROP_INT(class, id, name, NULL, blurb, min, max, default, flags)
+
+#define GIMP_CONFIG_INSTALL_PROP_UINT(class, id, name, blurb, min, max, default, flags) \
+ GIMP_CONFIG_PROP_UINT(class, id, name, NULL, blurb, min, max, default, flags)
+
+#define GIMP_CONFIG_INSTALL_PROP_UNIT(class, id, name, blurb, pixels, percent, default, flags) \
+ GIMP_CONFIG_PROP_UNIT(class, id, name, NULL, blurb, pixels, percent, default, flags)
+
+#define GIMP_CONFIG_INSTALL_PROP_MEMSIZE(class, id, name, blurb, min, max, default, flags) \
+ GIMP_CONFIG_PROP_MEMSIZE(class, id, name, NULL, blurb, min, max, default, flags)
+
+#define GIMP_CONFIG_INSTALL_PROP_DOUBLE(class, id, name, blurb, min, max, default, flags) \
+ GIMP_CONFIG_PROP_DOUBLE(class, id, name, NULL, blurb, min, max, default, flags)
+
+#define GIMP_CONFIG_INSTALL_PROP_RESOLUTION(class, id, name, blurb, default, flags) \
+ GIMP_CONFIG_PROP_RESOLUTION(class, id, name, NULL, blurb, default, flags)
+
+#define GIMP_CONFIG_INSTALL_PROP_ENUM(class, id, name, blurb, enum_type, default, flags) \
+ GIMP_CONFIG_PROP_ENUM(class, id, name, NULL, blurb, enum_type, default, flags)
+
+#define GIMP_CONFIG_INSTALL_PROP_STRING(class, id, name, blurb, default, flags) \
+ GIMP_CONFIG_PROP_STRING(class, id, name, NULL, blurb, default, flags)
+
+#define GIMP_CONFIG_INSTALL_PROP_PATH(class, id, name, blurb, path_type, default, flags) \
+ GIMP_CONFIG_PROP_PATH(class, id, name, NULL, blurb, path_type, default, flags)
+
+#define GIMP_CONFIG_INSTALL_PROP_RGB(class, id, name, blurb, has_alpha, default, flags) \
+ GIMP_CONFIG_PROP_RGB(class, id, name, NULL, blurb, has_alpha, default, flags)
+
+#define GIMP_CONFIG_INSTALL_PROP_MATRIX2(class, id, name, blurb, default, flags) \
+ GIMP_CONFIG_PROP_MATRIX2(class, id, name, NULL, blurb, default, flags)
+
+#define GIMP_CONFIG_INSTALL_PROP_OBJECT(class, id, name, blurb, object_type, flags) \
+ GIMP_CONFIG_PROP_OBJECT(class, id, name, NULL, blurb, object_type, flags)
+
+#define GIMP_CONFIG_INSTALL_PROP_BOXED(class, id, name, blurb, boxed_type, flags) \
+ GIMP_CONFIG_PROP_BOXED(class, id, name, NULL, blurb, boxed_type, flags)
+
+#define GIMP_CONFIG_INSTALL_PROP_POINTER(class, id, name, blurb, flags) \
+ GIMP_CONFIG_PROP_POINTER(class, id, name, NULL, blurb, flags)
+
+#endif /* GIMP_DISABLE_DEPRECATED */
+
+
+G_END_DECLS
+
+#endif /* __GIMP_CONFIG_PARAMS_H__ */
diff --git a/libgimpconfig/gimpconfig-path.c b/libgimpconfig/gimpconfig-path.c
new file mode 100644
index 0000000..8d87e4a
--- /dev/null
+++ b/libgimpconfig/gimpconfig-path.c
@@ -0,0 +1,720 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimpconfig-path.c
+ * Copyright (C) 2001 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <gio/gio.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "gimpconfig-error.h"
+#include "gimpconfig-path.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpconfig-path
+ * @title: GimpConfig-path
+ * @short_description: File path utilities for libgimpconfig.
+ *
+ * File path utilities for libgimpconfig.
+ **/
+
+
+/**
+ * gimp_config_path_get_type:
+ *
+ * Reveals the object type
+ *
+ * Returns: the #GType for a GimpConfigPath string property
+ *
+ * Since: 2.4
+ **/
+GType
+gimp_config_path_get_type (void)
+{
+ static GType path_type = 0;
+
+ if (! path_type)
+ {
+ const GTypeInfo type_info = { 0, };
+
+ path_type = g_type_register_static (G_TYPE_STRING, "GimpConfigPath",
+ &type_info, 0);
+ }
+
+ return path_type;
+}
+
+
+/*
+ * GIMP_TYPE_PARAM_CONFIG_PATH
+ */
+
+#define GIMP_PARAM_SPEC_CONFIG_PATH(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_CONFIG_PATH, GimpParamSpecConfigPath))
+
+typedef struct _GimpParamSpecConfigPath GimpParamSpecConfigPath;
+
+struct _GimpParamSpecConfigPath
+{
+ GParamSpecString parent_instance;
+
+ GimpConfigPathType type;
+};
+
+static void gimp_param_config_path_class_init (GParamSpecClass *class);
+
+/**
+ * gimp_param_config_path_get_type:
+ *
+ * Reveals the object type
+ *
+ * Returns: the #GType for a directory path object
+ *
+ * Since: 2.4
+ **/
+GType
+gimp_param_config_path_get_type (void)
+{
+ static GType spec_type = 0;
+
+ if (! spec_type)
+ {
+ const GTypeInfo type_info =
+ {
+ sizeof (GParamSpecClass),
+ NULL, NULL,
+ (GClassInitFunc) gimp_param_config_path_class_init,
+ NULL, NULL,
+ sizeof (GimpParamSpecConfigPath),
+ 0, NULL, NULL
+ };
+
+ spec_type = g_type_register_static (G_TYPE_PARAM_STRING,
+ "GimpParamConfigPath",
+ &type_info, 0);
+ }
+
+ return spec_type;
+}
+
+static void
+gimp_param_config_path_class_init (GParamSpecClass *class)
+{
+ class->value_type = GIMP_TYPE_CONFIG_PATH;
+}
+
+/**
+ * gimp_param_spec_config_path:
+ * @name: Canonical name of the param
+ * @nick: Nickname of the param
+ * @blurb: Brief description of param.
+ * @type: a #GimpConfigPathType value.
+ * @default_value: Value to use if none is assigned.
+ * @flags: a combination of #GParamFlags
+ *
+ * Creates a param spec to hold a filename, dir name,
+ * or list of file or dir names.
+ * See g_param_spec_internal() for more information.
+ *
+ * Returns: a newly allocated #GParamSpec instance
+ *
+ * Since: 2.4
+ **/
+GParamSpec *
+gimp_param_spec_config_path (const gchar *name,
+ const gchar *nick,
+ const gchar *blurb,
+ GimpConfigPathType type,
+ const gchar *default_value,
+ GParamFlags flags)
+{
+ GParamSpecString *pspec;
+
+ pspec = g_param_spec_internal (GIMP_TYPE_PARAM_CONFIG_PATH,
+ name, nick, blurb, flags);
+
+ pspec->default_value = g_strdup (default_value);
+
+ GIMP_PARAM_SPEC_CONFIG_PATH (pspec)->type = type;
+
+ return G_PARAM_SPEC (pspec);
+}
+
+/**
+ * gimp_param_spec_config_path_type:
+ * @pspec: A #GParamSpec for a path param
+ *
+ * Tells whether the path param encodes a filename,
+ * dir name, or list of file or dir names.
+ *
+ * Returns: a #GimpConfigPathType value
+ *
+ * Since: 2.4
+ **/
+GimpConfigPathType
+gimp_param_spec_config_path_type (GParamSpec *pspec)
+{
+ g_return_val_if_fail (GIMP_IS_PARAM_SPEC_CONFIG_PATH (pspec), 0);
+
+ return GIMP_PARAM_SPEC_CONFIG_PATH (pspec)->type;
+}
+
+
+/*
+ * GimpConfig path utilities
+ */
+
+static gchar * gimp_config_path_expand_only (const gchar *path,
+ GError **error) G_GNUC_MALLOC;
+static inline gchar * gimp_config_path_extract_token (const gchar **str);
+static gchar * gimp_config_path_unexpand_only (const gchar *path) G_GNUC_MALLOC;
+
+
+/**
+ * gimp_config_build_data_path:
+ * @name: directory name (in UTF-8 encoding)
+ *
+ * Creates a search path as it is used in the gimprc file. The path
+ * returned by gimp_config_build_data_path() includes a directory
+ * below the user's gimp directory and one in the system-wide data
+ * directory.
+ *
+ * Note that you cannot use this path directly with gimp_path_parse().
+ * As it is in the gimprc notation, you first need to expand and
+ * recode it using gimp_config_path_expand().
+ *
+ * Returns: a newly allocated string
+ *
+ * Since: 2.4
+ **/
+gchar *
+gimp_config_build_data_path (const gchar *name)
+{
+ return g_strconcat ("${gimp_dir}", G_DIR_SEPARATOR_S, name,
+ G_SEARCHPATH_SEPARATOR_S,
+ "${gimp_data_dir}", G_DIR_SEPARATOR_S, name,
+ NULL);
+}
+
+/**
+ * gimp_config_build_plug_in_path:
+ * @name: directory name (in UTF-8 encoding)
+ *
+ * Creates a search path as it is used in the gimprc file. The path
+ * returned by gimp_config_build_plug_in_path() includes a directory
+ * below the user's gimp directory and one in the system-wide plug-in
+ * directory.
+ *
+ * Note that you cannot use this path directly with gimp_path_parse().
+ * As it is in the gimprc notation, you first need to expand and
+ * recode it using gimp_config_path_expand().
+ *
+ * Returns: a newly allocated string
+ *
+ * Since: 2.4
+ **/
+gchar *
+gimp_config_build_plug_in_path (const gchar *name)
+{
+ return g_strconcat ("${gimp_dir}", G_DIR_SEPARATOR_S, name,
+ G_SEARCHPATH_SEPARATOR_S,
+ "${gimp_plug_in_dir}", G_DIR_SEPARATOR_S, name,
+ NULL);
+}
+
+/**
+ * gimp_config_build_writable_path:
+ * @name: directory name (in UTF-8 encoding)
+ *
+ * Creates a search path as it is used in the gimprc file. The path
+ * returned by gimp_config_build_writable_path() is just the writable
+ * parts of the search path constructed by gimp_config_build_data_path().
+ *
+ * Note that you cannot use this path directly with gimp_path_parse().
+ * As it is in the gimprc notation, you first need to expand and
+ * recode it using gimp_config_path_expand().
+ *
+ * Returns: a newly allocated string
+ *
+ * Since: 2.4
+ **/
+gchar *
+gimp_config_build_writable_path (const gchar *name)
+{
+ return g_strconcat ("${gimp_dir}", G_DIR_SEPARATOR_S, name, NULL);
+}
+
+
+/**
+ * gimp_config_path_expand:
+ * @path: a NUL-terminated string in UTF-8 encoding
+ * @recode: whether to convert to the filesystem's encoding
+ * @error: return location for errors
+ *
+ * Paths as stored in gimprc and other config files have to be treated
+ * special. The string may contain special identifiers such as for
+ * example ${gimp_dir} that have to be substituted before use. Also
+ * the user's filesystem may be in a different encoding than UTF-8
+ * (which is what is used for the gimprc). This function does the
+ * variable substitution for you and can also attempt to convert to
+ * the filesystem encoding.
+ *
+ * To reverse the expansion, use gimp_config_path_unexpand().
+ *
+ * Return value: a newly allocated NUL-terminated string
+ *
+ * Since: 2.4
+ **/
+gchar *
+gimp_config_path_expand (const gchar *path,
+ gboolean recode,
+ GError **error)
+{
+ g_return_val_if_fail (path != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ if (recode)
+ {
+ gchar *retval;
+ gchar *expanded = gimp_config_path_expand_only (path, error);
+
+ if (! expanded)
+ return NULL;
+
+ retval = g_filename_from_utf8 (expanded, -1, NULL, NULL, error);
+
+ g_free (expanded);
+
+ return retval;
+ }
+
+ return gimp_config_path_expand_only (path, error);
+}
+
+/**
+ * gimp_config_path_expand_to_files:
+ * @path: a NUL-terminated string in UTF-8 encoding
+ * @error: return location for errors
+ *
+ * Paths as stored in the gimprc have to be treated special. The
+ * string may contain special identifiers such as for example
+ * ${gimp_dir} that have to be substituted before use. Also the user's
+ * filesystem may be in a different encoding than UTF-8 (which is what
+ * is used for the gimprc).
+ *
+ * This function runs @path through gimp_config_path_expand() and
+ * gimp_path_parse(), then turns the filenames returned by
+ * gimp_path_parse() into GFile using g_file_new_for_path().
+ *
+ * Return value: a #GList of newly allocated #GFile objects.
+ *
+ * Since: 2.10
+ **/
+GList *
+gimp_config_path_expand_to_files (const gchar *path,
+ GError **error)
+{
+ GList *files;
+ GList *list;
+ gchar *expanded;
+
+ g_return_val_if_fail (path != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ expanded = gimp_config_path_expand (path, TRUE, error);
+
+ if (! expanded)
+ return NULL;
+
+ files = gimp_path_parse (expanded, 256, FALSE, NULL);
+
+ g_free (expanded);
+
+ for (list = files; list; list = g_list_next (list))
+ {
+ gchar *dir = list->data;
+
+ list->data = g_file_new_for_path (dir);
+ g_free (dir);
+ }
+
+ return files;
+}
+
+/**
+ * gimp_config_path_unexpand:
+ * @path: a NUL-terminated string
+ * @recode: whether @path is in filesystem encoding or UTF-8
+ * @error: return location for errors
+ *
+ * The inverse operation of gimp_config_path_expand()
+ *
+ * This function takes a @path and tries to substitute the first
+ * elements by well-known special identifiers such as for example
+ * ${gimp_dir}. The unexpanded path can then be stored in gimprc and
+ * other config files.
+ *
+ * If @recode is %TRUE then @path is in local filesystem encoding,
+ * if @recode is %FALSE then @path is assumed to be UTF-8.
+ *
+ * Return value: a newly allocated NUL-terminated UTF-8 string
+ *
+ * Since: 2.10
+ **/
+gchar *
+gimp_config_path_unexpand (const gchar *path,
+ gboolean recode,
+ GError **error)
+{
+ g_return_val_if_fail (path != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ if (recode)
+ {
+ gchar *retval;
+ gchar *utf8 = g_filename_to_utf8 (path, -1, NULL, NULL, error);
+
+ if (! utf8)
+ return NULL;
+
+ retval = gimp_config_path_unexpand_only (utf8);
+
+ g_free (utf8);
+
+ return retval;
+ }
+
+ return gimp_config_path_unexpand_only (path);
+}
+
+/**
+ * gimp_file_new_for_config_path:
+ * @path: a NUL-terminated string in UTF-8 encoding
+ * @error: return location for errors
+ *
+ * Expands @path using gimp_config_path_expand() and returns a #GFile
+ * for the expanded path.
+ *
+ * To reverse the expansion, use gimp_file_get_config_path().
+ *
+ * Return value: a newly allocated #GFile, or %NULL if the expansion failed.
+ *
+ * Since: 2.10
+ **/
+GFile *
+gimp_file_new_for_config_path (const gchar *path,
+ GError **error)
+{
+ GFile *file = NULL;
+ gchar *expanded;
+
+ g_return_val_if_fail (path != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ expanded = gimp_config_path_expand (path, TRUE, error);
+
+ if (expanded)
+ {
+ file = g_file_new_for_path (expanded);
+ g_free (expanded);
+ }
+
+ return file;
+}
+
+/**
+ * gimp_file_get_config_path:
+ * @file: a #GFile
+ * @error: return location for errors
+ *
+ * Unexpands @file's path using gimp_config_path_unexpand() and
+ * returns the unexpanded path.
+ *
+ * The inverse operation of gimp_file_new_for_config_path().
+ *
+ * Return value: a newly allocated NUL-terminated UTF-8 string, or %NULL if
+ * unexpanding failed.
+ *
+ * Since: 2.10
+ **/
+gchar *
+gimp_file_get_config_path (GFile *file,
+ GError **error)
+{
+ gchar *unexpanded = NULL;
+ gchar *path;
+
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ path = g_file_get_path (file);
+
+ if (path)
+ {
+ unexpanded = gimp_config_path_unexpand (path, TRUE, error);
+ g_free (path);
+ }
+ else
+ {
+ g_set_error_literal (error, 0, 0,
+ _("File has no path representation"));
+ }
+
+ return unexpanded;
+}
+
+
+/* private functions */
+
+#define SUBSTS_ALLOC 4
+
+static gchar *
+gimp_config_path_expand_only (const gchar *path,
+ GError **error)
+{
+ const gchar *home;
+ const gchar *p;
+ const gchar *s;
+ gchar *n;
+ gchar *token;
+ gchar *filename = NULL;
+ gchar *expanded = NULL;
+ gchar **substs = NULL;
+ guint n_substs = 0;
+ gint length = 0;
+ gint i;
+
+ home = g_get_home_dir ();
+ if (home)
+ home = gimp_filename_to_utf8 (home);
+
+ p = path;
+
+ while (*p)
+ {
+ if (*p == '~' && home)
+ {
+ length += strlen (home);
+ p += 1;
+ }
+ else if ((token = gimp_config_path_extract_token (&p)) != NULL)
+ {
+ for (i = 0; i < n_substs; i++)
+ if (strcmp (substs[2*i], token) == 0)
+ break;
+
+ if (i < n_substs)
+ {
+ s = substs[2*i+1];
+ }
+ else
+ {
+ s = NULL;
+
+ if (strcmp (token, "gimp_dir") == 0)
+ s = gimp_directory ();
+ else if (strcmp (token, "gimp_data_dir") == 0)
+ s = gimp_data_directory ();
+ else if (strcmp (token, "gimp_plug_in_dir") == 0 ||
+ strcmp (token, "gimp_plugin_dir") == 0)
+ s = gimp_plug_in_directory ();
+ else if (strcmp (token, "gimp_sysconf_dir") == 0)
+ s = gimp_sysconf_directory ();
+ else if (strcmp (token, "gimp_installation_dir") == 0)
+ s = gimp_installation_directory ();
+ else if (strcmp (token, "gimp_cache_dir") == 0)
+ s = gimp_cache_directory ();
+ else if (strcmp (token, "gimp_temp_dir") == 0)
+ s = gimp_temp_directory ();
+
+ if (!s)
+ s = g_getenv (token);
+
+#ifdef G_OS_WIN32
+ /* The default user gimprc on Windows references
+ * ${TEMP}, but not all Windows installations have that
+ * environment variable, even if it should be kinda
+ * standard. So special-case it.
+ */
+ if (!s && strcmp (token, "TEMP") == 0)
+ s = g_get_tmp_dir ();
+#endif /* G_OS_WIN32 */
+ }
+
+ if (!s)
+ {
+ g_set_error (error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_PARSE,
+ _("Cannot expand ${%s}"), token);
+ g_free (token);
+ goto cleanup;
+ }
+
+ if (n_substs % SUBSTS_ALLOC == 0)
+ substs = g_renew (gchar *, substs, 2 * (n_substs + SUBSTS_ALLOC));
+
+ substs[2*n_substs] = token;
+ substs[2*n_substs + 1] = (gchar *) gimp_filename_to_utf8 (s);
+
+ length += strlen (substs[2*n_substs + 1]);
+
+ n_substs++;
+ }
+ else
+ {
+ length += g_utf8_skip[(const guchar) *p];
+ p = g_utf8_next_char (p);
+ }
+ }
+
+ if (n_substs == 0)
+ return g_strdup (path);
+
+ expanded = g_new (gchar, length + 1);
+
+ p = path;
+ n = expanded;
+
+ while (*p)
+ {
+ if (*p == '~' && home)
+ {
+ *n = '\0';
+ strcat (n, home);
+ n += strlen (home);
+ p += 1;
+ }
+ else if ((token = gimp_config_path_extract_token (&p)) != NULL)
+ {
+ for (i = 0; i < n_substs; i++)
+ {
+ if (strcmp (substs[2*i], token) == 0)
+ {
+ s = substs[2*i+1];
+
+ *n = '\0';
+ strcat (n, s);
+ n += strlen (s);
+
+ break;
+ }
+ }
+
+ g_free (token);
+ }
+ else
+ {
+ *n++ = *p++;
+ }
+ }
+
+ *n = '\0';
+
+ cleanup:
+ for (i = 0; i < n_substs; i++)
+ g_free (substs[2*i]);
+
+ g_free (substs);
+ g_free (filename);
+
+ return expanded;
+}
+
+static inline gchar *
+gimp_config_path_extract_token (const gchar **str)
+{
+ const gchar *p;
+ gchar *token;
+
+ if (strncmp (*str, "${", 2))
+ return NULL;
+
+ p = *str + 2;
+
+ while (*p && (*p != '}'))
+ p = g_utf8_next_char (p);
+
+ if (! *p)
+ return NULL;
+
+ token = g_strndup (*str + 2, g_utf8_pointer_to_offset (*str + 2, p));
+
+ *str = p + 1; /* after the closing bracket */
+
+ return token;
+}
+
+static gchar *
+gimp_config_path_unexpand_only (const gchar *path)
+{
+ const struct
+ {
+ const gchar *id;
+ const gchar *prefix;
+ }
+ identifiers[] =
+ {
+ { "${gimp_plug_in_dir}", gimp_plug_in_directory () },
+ { "${gimp_data_dir}", gimp_data_directory () },
+ { "${gimp_sysconf_dir}", gimp_sysconf_directory () },
+ { "${gimp_installation_dir}", gimp_installation_directory () },
+ { "${gimp_cache_dir}", gimp_cache_directory () },
+ { "${gimp_temp_dir}", gimp_temp_directory () },
+ { "${gimp_dir}", gimp_directory () }
+ };
+
+ GList *files;
+ GList *list;
+ gchar *unexpanded;
+
+ files = gimp_path_parse (path, 256, FALSE, NULL);
+
+ for (list = files; list; list = g_list_next (list))
+ {
+ gchar *dir = list->data;
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS (identifiers); i++)
+ {
+ if (g_str_has_prefix (dir, identifiers[i].prefix))
+ {
+ gchar *tmp = g_strconcat (identifiers[i].id,
+ dir + strlen (identifiers[i].prefix),
+ NULL);
+
+ g_free (dir);
+ list->data = tmp;
+
+ break;
+ }
+ }
+ }
+
+ unexpanded = gimp_path_to_str (files);
+
+ gimp_path_free (files);
+
+ return unexpanded;
+}
diff --git a/libgimpconfig/gimpconfig-path.h b/libgimpconfig/gimpconfig-path.h
new file mode 100644
index 0000000..1b157f9
--- /dev/null
+++ b/libgimpconfig/gimpconfig-path.h
@@ -0,0 +1,108 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpconfig-path.h
+ * Copyright (C) 2001-2002 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_CONFIG_H_INSIDE__) && !defined (GIMP_CONFIG_COMPILATION)
+#error "Only <libgimpconfig/gimpconfig.h> can be included directly."
+#endif
+
+#ifndef __GIMP_CONFIG_PATH_H__
+#define __GIMP_CONFIG_PATH_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+/*
+ * GIMP_TYPE_CONFIG_PATH
+ */
+
+#define GIMP_TYPE_CONFIG_PATH (gimp_config_path_get_type ())
+#define GIMP_VALUE_HOLDS_CONFIG_PATH(value) (G_TYPE_CHECK_VALUE_TYPE ((value), GIMP_TYPE_CONFIG_PATH))
+
+GType gimp_config_path_get_type (void) G_GNUC_CONST;
+
+
+
+/*
+ * GIMP_TYPE_PARAM_CONFIG_PATH
+ */
+
+/**
+ * GimpConfigPathType:
+ * @GIMP_CONFIG_PATH_FILE: A single file
+ * @GIMP_CONFIG_PATH_FILE_LIST: A list of files
+ * @GIMP_CONFIG_PATH_DIR: A single folder
+ * @GIMP_CONFIG_PATH_DIR_LIST: A list of folders
+ *
+ * Types of config paths.
+ **/
+typedef enum
+{
+ GIMP_CONFIG_PATH_FILE,
+ GIMP_CONFIG_PATH_FILE_LIST,
+ GIMP_CONFIG_PATH_DIR,
+ GIMP_CONFIG_PATH_DIR_LIST
+} GimpConfigPathType;
+
+
+#define GIMP_TYPE_PARAM_CONFIG_PATH (gimp_param_config_path_get_type ())
+#define GIMP_IS_PARAM_SPEC_CONFIG_PATH(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_CONFIG_PATH))
+
+GType gimp_param_config_path_get_type (void) G_GNUC_CONST;
+
+GParamSpec * gimp_param_spec_config_path (const gchar *name,
+ const gchar *nick,
+ const gchar *blurb,
+ GimpConfigPathType type,
+ const gchar *default_value,
+ GParamFlags flags);
+
+GimpConfigPathType gimp_param_spec_config_path_type (GParamSpec *pspec);
+
+
+/*
+ * GimpConfigPath utilities
+ */
+
+gchar * gimp_config_path_expand (const gchar *path,
+ gboolean recode,
+ GError **error) G_GNUC_MALLOC;
+GList * gimp_config_path_expand_to_files (const gchar *path,
+ GError **error) G_GNUC_MALLOC;
+
+gchar * gimp_config_path_unexpand (const gchar *path,
+ gboolean recode,
+ GError **error) G_GNUC_MALLOC;
+
+GFile * gimp_file_new_for_config_path (const gchar *path,
+ GError **error) G_GNUC_MALLOC;
+gchar * gimp_file_get_config_path (GFile *file,
+ GError **error) G_GNUC_MALLOC;
+
+gchar * gimp_config_build_data_path (const gchar *name) G_GNUC_MALLOC;
+gchar * gimp_config_build_writable_path (const gchar *name) G_GNUC_MALLOC;
+gchar * gimp_config_build_plug_in_path (const gchar *name) G_GNUC_MALLOC;
+
+
+G_END_DECLS
+
+#endif /* __GIMP_CONFIG_PATH_H__ */
diff --git a/libgimpconfig/gimpconfig-serialize.c b/libgimpconfig/gimpconfig-serialize.c
new file mode 100644
index 0000000..0df5bbc
--- /dev/null
+++ b/libgimpconfig/gimpconfig-serialize.c
@@ -0,0 +1,576 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * Object properties serialization routines
+ * Copyright (C) 2001-2002 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <cairo.h>
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpmath/gimpmath.h"
+#include "libgimpcolor/gimpcolor.h"
+
+#include "gimpconfigtypes.h"
+
+#include "gimpconfigwriter.h"
+#include "gimpconfig-iface.h"
+#include "gimpconfig-params.h"
+#include "gimpconfig-path.h"
+#include "gimpconfig-serialize.h"
+#include "gimpconfig-utils.h"
+
+
+/**
+ * SECTION: gimpconfig-serialize
+ * @title: GimpConfig-serialize
+ * @short_description: Serializing for libgimpconfig.
+ *
+ * Serializing interface for libgimpconfig.
+ **/
+
+
+static gboolean gimp_config_serialize_rgb (const GValue *value,
+ GString *str,
+ gboolean has_alpha);
+
+
+/**
+ * gimp_config_serialize_properties:
+ * @config: a #GimpConfig.
+ * @writer: a #GimpConfigWriter.
+ *
+ * This function writes all object properties to the @writer.
+ *
+ * Returns: %TRUE if serialization succeeded, %FALSE otherwise
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_config_serialize_properties (GimpConfig *config,
+ GimpConfigWriter *writer)
+{
+ GObjectClass *klass;
+ GParamSpec **property_specs;
+ guint n_property_specs;
+ guint i;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), FALSE);
+
+ klass = G_OBJECT_GET_CLASS (config);
+
+ property_specs = g_object_class_list_properties (klass, &n_property_specs);
+
+ if (! property_specs)
+ return TRUE;
+
+ for (i = 0; i < n_property_specs; i++)
+ {
+ GParamSpec *prop_spec = property_specs[i];
+
+ if (! (prop_spec->flags & GIMP_CONFIG_PARAM_SERIALIZE))
+ continue;
+
+ if (! gimp_config_serialize_property (config, prop_spec, writer))
+ return FALSE;
+ }
+
+ g_free (property_specs);
+
+ return TRUE;
+}
+
+/**
+ * gimp_config_serialize_changed_properties:
+ * @config: a #GimpConfig.
+ * @writer: a #GimpConfigWriter.
+ *
+ * This function writes all object properties that have been changed from
+ * their default values to the @writer.
+ *
+ * Returns: %TRUE if serialization succeeded, %FALSE otherwise
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_config_serialize_changed_properties (GimpConfig *config,
+ GimpConfigWriter *writer)
+{
+ GObjectClass *klass;
+ GParamSpec **property_specs;
+ guint n_property_specs;
+ guint i;
+ GValue value = G_VALUE_INIT;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), FALSE);
+
+ klass = G_OBJECT_GET_CLASS (config);
+
+ property_specs = g_object_class_list_properties (klass, &n_property_specs);
+
+ if (! property_specs)
+ return TRUE;
+
+ for (i = 0; i < n_property_specs; i++)
+ {
+ GParamSpec *prop_spec = property_specs[i];
+
+ if (! (prop_spec->flags & GIMP_CONFIG_PARAM_SERIALIZE))
+ continue;
+
+ g_value_init (&value, prop_spec->value_type);
+ g_object_get_property (G_OBJECT (config), prop_spec->name, &value);
+
+ if (! g_param_value_defaults (prop_spec, &value))
+ {
+ if (! gimp_config_serialize_property (config, prop_spec, writer))
+ return FALSE;
+ }
+
+ g_value_unset (&value);
+ }
+
+ g_free (property_specs);
+
+ return TRUE;
+}
+
+/**
+ * gimp_config_serialize_property:
+ * @config: a #GimpConfig.
+ * @param_spec: a #GParamSpec.
+ * @writer: a #GimpConfigWriter.
+ *
+ * This function serializes a single object property to the @writer.
+ *
+ * Returns: %TRUE if serialization succeeded, %FALSE otherwise
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_config_serialize_property (GimpConfig *config,
+ GParamSpec *param_spec,
+ GimpConfigWriter *writer)
+{
+ GimpConfigInterface *config_iface = NULL;
+ GimpConfigInterface *parent_iface = NULL;
+ GValue value = G_VALUE_INIT;
+ gboolean success = FALSE;
+
+ if (! (param_spec->flags & GIMP_CONFIG_PARAM_SERIALIZE))
+ return FALSE;
+
+ if (param_spec->flags & GIMP_CONFIG_PARAM_IGNORE)
+ return TRUE;
+
+ g_value_init (&value, param_spec->value_type);
+ g_object_get_property (G_OBJECT (config), param_spec->name, &value);
+
+ if (param_spec->flags & GIMP_CONFIG_PARAM_DEFAULTS &&
+ g_param_value_defaults (param_spec, &value))
+ {
+ g_value_unset (&value);
+ return TRUE;
+ }
+
+ if (G_TYPE_IS_OBJECT (param_spec->owner_type))
+ {
+ GTypeClass *owner_class = g_type_class_peek (param_spec->owner_type);
+
+ config_iface = g_type_interface_peek (owner_class, GIMP_TYPE_CONFIG);
+
+ /* We must call serialize_property() *only* if the *exact* class
+ * which implements it is param_spec->owner_type's class.
+ *
+ * Therefore, we ask param_spec->owner_type's immediate parent class
+ * for it's GimpConfigInterface and check if we get a different
+ * pointer.
+ *
+ * (if the pointers are the same, param_spec->owner_type's
+ * GimpConfigInterface is inherited from one of it's parent classes
+ * and thus not able to handle param_spec->owner_type's properties).
+ */
+ if (config_iface)
+ {
+ GTypeClass *owner_parent_class;
+
+ owner_parent_class = g_type_class_peek_parent (owner_class);
+
+ parent_iface = g_type_interface_peek (owner_parent_class,
+ GIMP_TYPE_CONFIG);
+ }
+ }
+
+ if (config_iface &&
+ config_iface != parent_iface && /* see comment above */
+ config_iface->serialize_property &&
+ config_iface->serialize_property (config,
+ param_spec->param_id,
+ (const GValue *) &value,
+ param_spec,
+ writer))
+ {
+ success = TRUE;
+ }
+
+ /* If there is no serialize_property() method *or* if it returned
+ * FALSE, continue with the default implementation
+ */
+
+ if (! success)
+ {
+ if (G_VALUE_HOLDS_OBJECT (&value) &&
+ G_VALUE_TYPE (&value) != G_TYPE_FILE)
+ {
+ GimpConfigInterface *config_iface = NULL;
+ GimpConfig *prop_object;
+
+ prop_object = g_value_get_object (&value);
+
+ if (prop_object)
+ config_iface = GIMP_CONFIG_GET_INTERFACE (prop_object);
+ else
+ success = TRUE;
+
+ if (config_iface)
+ {
+ gimp_config_writer_open (writer, param_spec->name);
+
+ /* if the object property is not GIMP_CONFIG_PARAM_AGGREGATE,
+ * deserializing will need to know the exact type
+ * in order to create the object
+ */
+ if (! (param_spec->flags & GIMP_CONFIG_PARAM_AGGREGATE))
+ {
+ GType object_type = G_TYPE_FROM_INSTANCE (prop_object);
+
+ gimp_config_writer_string (writer, g_type_name (object_type));
+ }
+
+ success = config_iface->serialize (prop_object, writer, NULL);
+
+ if (success)
+ gimp_config_writer_close (writer);
+ else
+ gimp_config_writer_revert (writer);
+ }
+ }
+ else
+ {
+ GString *str = g_string_new (NULL);
+
+ if (GIMP_VALUE_HOLDS_RGB (&value))
+ {
+ gboolean has_alpha = gimp_param_spec_rgb_has_alpha (param_spec);
+
+ success = gimp_config_serialize_rgb (&value, str, has_alpha);
+ }
+ else
+ {
+ success = gimp_config_serialize_value (&value, str, TRUE);
+ }
+
+ if (success)
+ {
+ gimp_config_writer_open (writer, param_spec->name);
+ gimp_config_writer_print (writer, str->str, str->len);
+ gimp_config_writer_close (writer);
+ }
+
+ g_string_free (str, TRUE);
+ }
+
+ if (! success)
+ {
+ /* don't warn for empty string properties */
+ if (G_VALUE_HOLDS_STRING (&value))
+ {
+ success = TRUE;
+ }
+ else
+ {
+ g_warning ("couldn't serialize property %s::%s of type %s",
+ g_type_name (G_TYPE_FROM_INSTANCE (config)),
+ param_spec->name,
+ g_type_name (param_spec->value_type));
+ }
+ }
+ }
+
+ g_value_unset (&value);
+
+ return success;
+}
+
+/**
+ * gimp_config_serialize_property_by_name:
+ * @config: a #GimpConfig.
+ * @prop_name: the property's name.
+ * @writer: a #GimpConfigWriter.
+ *
+ * This function serializes a single object property to the @writer.
+ *
+ * Returns: %TRUE if serialization succeeded, %FALSE otherwise
+ *
+ * Since: 2.6
+ **/
+gboolean
+gimp_config_serialize_property_by_name (GimpConfig *config,
+ const gchar *prop_name,
+ GimpConfigWriter *writer)
+{
+ GParamSpec *pspec;
+
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (config),
+ prop_name);
+
+ if (! pspec)
+ return FALSE;
+
+ return gimp_config_serialize_property (config, pspec, writer);
+}
+
+/**
+ * gimp_config_serialize_value:
+ * @value: a #GValue.
+ * @str: a #GString.
+ * @escaped: whether to escape string values.
+ *
+ * This utility function appends a string representation of #GValue to @str.
+ *
+ * Return value: %TRUE if serialization succeeded, %FALSE otherwise.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_config_serialize_value (const GValue *value,
+ GString *str,
+ gboolean escaped)
+{
+ if (G_VALUE_HOLDS_BOOLEAN (value))
+ {
+ gboolean bool;
+
+ bool = g_value_get_boolean (value);
+ g_string_append (str, bool ? "yes" : "no");
+ return TRUE;
+ }
+
+ if (G_VALUE_HOLDS_ENUM (value))
+ {
+ GEnumClass *enum_class = g_type_class_peek (G_VALUE_TYPE (value));
+ GEnumValue *enum_value = g_enum_get_value (enum_class,
+ g_value_get_enum (value));
+
+ if (enum_value && enum_value->value_nick)
+ {
+ g_string_append (str, enum_value->value_nick);
+ return TRUE;
+ }
+ else
+ {
+ g_warning ("Couldn't get nick for enum_value of %s",
+ G_ENUM_CLASS_TYPE_NAME (enum_class));
+ return FALSE;
+ }
+ }
+
+ if (G_VALUE_HOLDS_STRING (value))
+ {
+ const gchar *cstr = g_value_get_string (value);
+
+ if (!cstr)
+ return FALSE;
+
+ if (escaped)
+ gimp_config_string_append_escaped (str, cstr);
+ else
+ g_string_append (str, cstr);
+
+ return TRUE;
+ }
+
+ if (G_VALUE_HOLDS_DOUBLE (value) || G_VALUE_HOLDS_FLOAT (value))
+ {
+ gdouble v_double;
+ gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
+
+ if (G_VALUE_HOLDS_DOUBLE (value))
+ v_double = g_value_get_double (value);
+ else
+ v_double = (gdouble) g_value_get_float (value);
+
+ g_ascii_dtostr (buf, sizeof (buf), v_double);
+ g_string_append (str, buf);
+ return TRUE;
+ }
+
+ if (GIMP_VALUE_HOLDS_RGB (value))
+ {
+ return gimp_config_serialize_rgb (value, str, TRUE);
+ }
+
+ if (GIMP_VALUE_HOLDS_MATRIX2 (value))
+ {
+ GimpMatrix2 *trafo;
+
+ trafo = g_value_get_boxed (value);
+
+ if (trafo)
+ {
+ gchar buf[4][G_ASCII_DTOSTR_BUF_SIZE];
+ gint i, j, k;
+
+ for (i = 0, k = 0; i < 2; i++)
+ for (j = 0; j < 2; j++, k++)
+ g_ascii_dtostr (buf[k], G_ASCII_DTOSTR_BUF_SIZE,
+ trafo->coeff[i][j]);
+
+ g_string_append_printf (str, "(matrix %s %s %s %s)",
+ buf[0], buf[1], buf[2], buf[3]);
+ }
+ else
+ {
+ g_string_append (str, "(matrix 1.0 1.0 1.0 1.0)");
+ }
+
+ return TRUE;
+ }
+
+ if (G_VALUE_TYPE (value) == GIMP_TYPE_VALUE_ARRAY)
+ {
+ GimpValueArray *array;
+
+ array = g_value_get_boxed (value);
+
+ if (array)
+ {
+ gint length = gimp_value_array_length (array);
+ gint i;
+
+ g_string_append_printf (str, "%d", length);
+
+ for (i = 0; i < length; i++)
+ {
+ g_string_append (str, " ");
+
+ if (! gimp_config_serialize_value (gimp_value_array_index (array,
+ i),
+ str, TRUE))
+ return FALSE;
+ }
+ }
+ else
+ {
+ g_string_append (str, "0");
+ }
+
+ return TRUE;
+ }
+
+ if (G_VALUE_TYPE (value) == G_TYPE_FILE)
+ {
+ GFile *file = g_value_get_object (value);
+
+ if (file)
+ {
+ gchar *path = g_file_get_path (file);
+ gchar *unexpand = NULL;
+
+ if (path)
+ {
+ unexpand = gimp_config_path_unexpand (path, TRUE, NULL);
+ g_free (path);
+ }
+
+ if (unexpand)
+ {
+ if (escaped)
+ gimp_config_string_append_escaped (str, unexpand);
+ else
+ g_string_append (str, unexpand);
+
+ g_free (unexpand);
+ }
+ else
+ {
+ g_string_append (str, "NULL");
+ }
+ }
+ else
+ {
+ g_string_append (str, "NULL");
+ }
+
+ return TRUE;
+ }
+
+ if (g_value_type_transformable (G_VALUE_TYPE (value), G_TYPE_STRING))
+ {
+ GValue tmp_value = G_VALUE_INIT;
+
+ g_value_init (&tmp_value, G_TYPE_STRING);
+ g_value_transform (value, &tmp_value);
+
+ g_string_append (str, g_value_get_string (&tmp_value));
+
+ g_value_unset (&tmp_value);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gimp_config_serialize_rgb (const GValue *value,
+ GString *str,
+ gboolean has_alpha)
+{
+ GimpRGB *rgb;
+
+ rgb = g_value_get_boxed (value);
+
+ if (rgb)
+ {
+ gchar buf[4][G_ASCII_DTOSTR_BUF_SIZE];
+
+ g_ascii_dtostr (buf[0], G_ASCII_DTOSTR_BUF_SIZE, rgb->r);
+ g_ascii_dtostr (buf[1], G_ASCII_DTOSTR_BUF_SIZE, rgb->g);
+ g_ascii_dtostr (buf[2], G_ASCII_DTOSTR_BUF_SIZE, rgb->b);
+
+ if (has_alpha)
+ {
+ g_ascii_dtostr (buf[3], G_ASCII_DTOSTR_BUF_SIZE, rgb->a);
+
+ g_string_append_printf (str, "(color-rgba %s %s %s %s)",
+ buf[0], buf[1], buf[2], buf[3]);
+ }
+ else
+ {
+ g_string_append_printf (str, "(color-rgb %s %s %s)",
+ buf[0], buf[1], buf[2]);
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/libgimpconfig/gimpconfig-serialize.h b/libgimpconfig/gimpconfig-serialize.h
new file mode 100644
index 0000000..c25ab3b
--- /dev/null
+++ b/libgimpconfig/gimpconfig-serialize.h
@@ -0,0 +1,52 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Object properties serialization routines
+ * Copyright (C) 2001-2002 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_CONFIG_H_INSIDE__) && !defined (GIMP_CONFIG_COMPILATION)
+#error "Only <libgimpconfig/gimpconfig.h> can be included directly."
+#endif
+
+#ifndef __GIMP_CONFIG_SERIALIZE_H__
+#define __GIMP_CONFIG_SERIALIZE_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_config_serialize_properties (GimpConfig *config,
+ GimpConfigWriter *writer);
+gboolean gimp_config_serialize_changed_properties (GimpConfig *config,
+ GimpConfigWriter *writer);
+
+gboolean gimp_config_serialize_property (GimpConfig *config,
+ GParamSpec *param_spec,
+ GimpConfigWriter *writer);
+gboolean gimp_config_serialize_property_by_name (GimpConfig *config,
+ const gchar *prop_name,
+ GimpConfigWriter *writer);
+gboolean gimp_config_serialize_value (const GValue *value,
+ GString *str,
+ gboolean escaped);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_CONFIG_SERIALIZE_H__ */
diff --git a/libgimpconfig/gimpconfig-utils.c b/libgimpconfig/gimpconfig-utils.c
new file mode 100644
index 0000000..7cef219
--- /dev/null
+++ b/libgimpconfig/gimpconfig-utils.c
@@ -0,0 +1,482 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * Utility functions for GimpConfig.
+ * Copyright (C) 2001-2003 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "gimpconfigtypes.h"
+
+#include "gimpconfigwriter.h"
+#include "gimpconfig-iface.h"
+#include "gimpconfig-params.h"
+#include "gimpconfig-utils.h"
+
+
+/**
+ * SECTION: gimpconfig-utils
+ * @title: GimpConfig-utils
+ * @short_description: Miscellaneous utility functions for libgimpconfig.
+ *
+ * Miscellaneous utility functions for libgimpconfig.
+ **/
+
+
+static gboolean
+gimp_config_diff_property (GObject *a,
+ GObject *b,
+ GParamSpec *prop_spec)
+{
+ GValue a_value = G_VALUE_INIT;
+ GValue b_value = G_VALUE_INIT;
+ gboolean retval = FALSE;
+
+ g_value_init (&a_value, prop_spec->value_type);
+ g_value_init (&b_value, prop_spec->value_type);
+
+ g_object_get_property (a, prop_spec->name, &a_value);
+ g_object_get_property (b, prop_spec->name, &b_value);
+
+ if (g_param_values_cmp (prop_spec, &a_value, &b_value))
+ {
+ if ((prop_spec->flags & GIMP_CONFIG_PARAM_AGGREGATE) &&
+ G_IS_PARAM_SPEC_OBJECT (prop_spec) &&
+ g_type_interface_peek (g_type_class_peek (prop_spec->value_type),
+ GIMP_TYPE_CONFIG))
+ {
+ if (! gimp_config_is_equal_to (g_value_get_object (&a_value),
+ g_value_get_object (&b_value)))
+ {
+ retval = TRUE;
+ }
+ }
+ else
+ {
+ retval = TRUE;
+ }
+ }
+
+ g_value_unset (&a_value);
+ g_value_unset (&b_value);
+
+ return retval;
+}
+
+static GList *
+gimp_config_diff_same (GObject *a,
+ GObject *b,
+ GParamFlags flags)
+{
+ GParamSpec **param_specs;
+ guint n_param_specs;
+ gint i;
+ GList *list = NULL;
+
+ param_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (a),
+ &n_param_specs);
+
+ for (i = 0; i < n_param_specs; i++)
+ {
+ GParamSpec *prop_spec = param_specs[i];
+
+ if (! flags || ((prop_spec->flags & flags) == flags))
+ {
+ if (gimp_config_diff_property (a, b, prop_spec))
+ list = g_list_prepend (list, prop_spec);
+ }
+ }
+
+ g_free (param_specs);
+
+ return list;
+}
+
+static GList *
+gimp_config_diff_other (GObject *a,
+ GObject *b,
+ GParamFlags flags)
+{
+ GParamSpec **param_specs;
+ guint n_param_specs;
+ gint i;
+ GList *list = NULL;
+
+ param_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (a),
+ &n_param_specs);
+
+ for (i = 0; i < n_param_specs; i++)
+ {
+ GParamSpec *a_spec = param_specs[i];
+ GParamSpec *b_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (b),
+ a_spec->name);
+
+ if (b_spec &&
+ (a_spec->value_type == b_spec->value_type) &&
+ (! flags || (a_spec->flags & b_spec->flags & flags) == flags))
+ {
+ if (gimp_config_diff_property (a, b, b_spec))
+ list = g_list_prepend (list, b_spec);
+ }
+ }
+
+ g_free (param_specs);
+
+ return list;
+}
+
+
+/**
+ * gimp_config_diff:
+ * @a: a #GObject
+ * @b: another #GObject object
+ * @flags: a mask of GParamFlags
+ *
+ * Compares all properties of @a and @b that have all @flags set. If
+ * @flags is 0, all properties are compared.
+ *
+ * If the two objects are not of the same type, only properties that
+ * exist in both object classes and are of the same value_type are
+ * compared.
+ *
+ * Return value: a GList of differing GParamSpecs.
+ *
+ * Since: 2.4
+ **/
+GList *
+gimp_config_diff (GObject *a,
+ GObject *b,
+ GParamFlags flags)
+{
+ GList *diff;
+
+ g_return_val_if_fail (G_IS_OBJECT (a), NULL);
+ g_return_val_if_fail (G_IS_OBJECT (b), NULL);
+
+ if (G_TYPE_FROM_INSTANCE (a) == G_TYPE_FROM_INSTANCE (b))
+ diff = gimp_config_diff_same (a, b, flags);
+ else
+ diff = gimp_config_diff_other (a, b, flags);
+
+ return g_list_reverse (diff);
+}
+
+/**
+ * gimp_config_sync:
+ * @src: a #GObject
+ * @dest: another #GObject
+ * @flags: a mask of GParamFlags
+ *
+ * Compares all read- and write-able properties from @src and @dest
+ * that have all @flags set. Differing values are then copied from
+ * @src to @dest. If @flags is 0, all differing read/write properties.
+ *
+ * Properties marked as "construct-only" are not touched.
+ *
+ * If the two objects are not of the same type, only properties that
+ * exist in both object classes and are of the same value_type are
+ * synchronized
+ *
+ * Return value: %TRUE if @dest was modified, %FALSE otherwise
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_config_sync (GObject *src,
+ GObject *dest,
+ GParamFlags flags)
+{
+ GList *diff;
+ GList *list;
+
+ g_return_val_if_fail (G_IS_OBJECT (src), FALSE);
+ g_return_val_if_fail (G_IS_OBJECT (dest), FALSE);
+
+ /* we use the internal versions here for a number of reasons:
+ * - it saves a g_list_reverse()
+ * - it avoids duplicated parameter checks
+ */
+ if (G_TYPE_FROM_INSTANCE (src) == G_TYPE_FROM_INSTANCE (dest))
+ diff = gimp_config_diff_same (src, dest, (flags | G_PARAM_READWRITE));
+ else
+ diff = gimp_config_diff_other (src, dest, flags);
+
+ if (!diff)
+ return FALSE;
+
+ g_object_freeze_notify (G_OBJECT (dest));
+
+ for (list = diff; list; list = list->next)
+ {
+ GParamSpec *prop_spec = list->data;
+
+ if (! (prop_spec->flags & G_PARAM_CONSTRUCT_ONLY))
+ {
+ GValue value = G_VALUE_INIT;
+
+ g_value_init (&value, prop_spec->value_type);
+
+ g_object_get_property (src, prop_spec->name, &value);
+ g_object_set_property (dest, prop_spec->name, &value);
+
+ g_value_unset (&value);
+ }
+ }
+
+ g_object_thaw_notify (G_OBJECT (dest));
+
+ g_list_free (diff);
+
+ return TRUE;
+}
+
+/**
+ * gimp_config_reset_properties:
+ * @object: a #GObject
+ *
+ * Resets all writable properties of @object to the default values as
+ * defined in their #GParamSpec. Properties marked as "construct-only"
+ * are not touched.
+ *
+ * If you want to reset a #GimpConfig object, please use gimp_config_reset().
+ *
+ * Since: 2.4
+ **/
+void
+gimp_config_reset_properties (GObject *object)
+{
+ GObjectClass *klass;
+ GParamSpec **property_specs;
+ guint n_property_specs;
+ guint i;
+
+ g_return_if_fail (G_IS_OBJECT (object));
+
+ klass = G_OBJECT_GET_CLASS (object);
+
+ property_specs = g_object_class_list_properties (klass, &n_property_specs);
+ if (!property_specs)
+ return;
+
+ g_object_freeze_notify (object);
+
+ for (i = 0; i < n_property_specs; i++)
+ {
+ GParamSpec *prop_spec;
+ GValue value = G_VALUE_INIT;
+
+ prop_spec = property_specs[i];
+
+ if ((prop_spec->flags & G_PARAM_WRITABLE) &&
+ ! (prop_spec->flags & G_PARAM_CONSTRUCT_ONLY))
+ {
+ if (G_IS_PARAM_SPEC_OBJECT (prop_spec))
+ {
+ if ((prop_spec->flags & GIMP_CONFIG_PARAM_SERIALIZE) &&
+ (prop_spec->flags & GIMP_CONFIG_PARAM_AGGREGATE) &&
+ g_type_interface_peek (g_type_class_peek (prop_spec->value_type),
+ GIMP_TYPE_CONFIG))
+ {
+ g_value_init (&value, prop_spec->value_type);
+
+ g_object_get_property (object, prop_spec->name, &value);
+
+ gimp_config_reset (g_value_get_object (&value));
+
+ g_value_unset (&value);
+ }
+ }
+ else
+ {
+ GValue default_value = G_VALUE_INIT;
+
+ g_value_init (&default_value, prop_spec->value_type);
+ g_value_init (&value, prop_spec->value_type);
+
+ g_param_value_set_default (prop_spec, &default_value);
+ g_object_get_property (object, prop_spec->name, &value);
+
+ if (g_param_values_cmp (prop_spec, &default_value, &value))
+ {
+ g_object_set_property (object, prop_spec->name,
+ &default_value);
+ }
+
+ g_value_unset (&value);
+ g_value_unset (&default_value);
+ }
+ }
+ }
+
+ g_object_thaw_notify (object);
+
+ g_free (property_specs);
+}
+
+
+/**
+ * gimp_config_reset_property:
+ * @object: a #GObject
+ * @property_name: name of the property to reset
+ *
+ * Resets the property named @property_name to its default value. The
+ * property must be writable and must not be marked as "construct-only".
+ *
+ * Since: 2.4
+ **/
+void
+gimp_config_reset_property (GObject *object,
+ const gchar *property_name)
+{
+ GObjectClass *klass;
+ GParamSpec *prop_spec;
+
+ g_return_if_fail (G_IS_OBJECT (object));
+ g_return_if_fail (property_name != NULL);
+
+ klass = G_OBJECT_GET_CLASS (object);
+
+ prop_spec = g_object_class_find_property (klass, property_name);
+
+ if (!prop_spec)
+ return;
+
+ if ((prop_spec->flags & G_PARAM_WRITABLE) &&
+ ! (prop_spec->flags & G_PARAM_CONSTRUCT_ONLY))
+ {
+ GValue value = G_VALUE_INIT;
+
+ if (G_IS_PARAM_SPEC_OBJECT (prop_spec))
+ {
+ if ((prop_spec->flags & GIMP_CONFIG_PARAM_SERIALIZE) &&
+ (prop_spec->flags & GIMP_CONFIG_PARAM_AGGREGATE) &&
+ g_type_interface_peek (g_type_class_peek (prop_spec->value_type),
+ GIMP_TYPE_CONFIG))
+ {
+ g_value_init (&value, prop_spec->value_type);
+
+ g_object_get_property (object, prop_spec->name, &value);
+
+ gimp_config_reset (g_value_get_object (&value));
+
+ g_value_unset (&value);
+ }
+ }
+ else
+ {
+ g_value_init (&value, prop_spec->value_type);
+ g_param_value_set_default (prop_spec, &value);
+
+ g_object_set_property (object, prop_spec->name, &value);
+
+ g_value_unset (&value);
+ }
+ }
+}
+
+
+/*
+ * GimpConfig string utilities
+ */
+
+/**
+ * gimp_config_string_append_escaped:
+ * @string: pointer to a #GString
+ * @val: a string to append or %NULL
+ *
+ * Escapes and quotes @val and appends it to @string. The escape
+ * algorithm is different from the one used by g_strescape() since it
+ * leaves non-ASCII characters intact and thus preserves UTF-8
+ * strings. Only control characters and quotes are being escaped.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_config_string_append_escaped (GString *string,
+ const gchar *val)
+{
+ g_return_if_fail (string != NULL);
+
+ if (val)
+ {
+ const guchar *p;
+ gchar buf[4] = { '\\', 0, 0, 0 };
+ gint len;
+
+ g_string_append_c (string, '\"');
+
+ for (p = (const guchar *) val, len = 0; *p; p++)
+ {
+ if (*p < ' ' || *p == '\\' || *p == '\"')
+ {
+ g_string_append_len (string, val, len);
+
+ len = 2;
+ switch (*p)
+ {
+ case '\b':
+ buf[1] = 'b';
+ break;
+ case '\f':
+ buf[1] = 'f';
+ break;
+ case '\n':
+ buf[1] = 'n';
+ break;
+ case '\r':
+ buf[1] = 'r';
+ break;
+ case '\t':
+ buf[1] = 't';
+ break;
+ case '\\':
+ case '"':
+ buf[1] = *p;
+ break;
+
+ default:
+ len = 4;
+ buf[1] = '0' + (((*p) >> 6) & 07);
+ buf[2] = '0' + (((*p) >> 3) & 07);
+ buf[3] = '0' + ((*p) & 07);
+ break;
+ }
+
+ g_string_append_len (string, buf, len);
+
+ val = (const gchar *) p + 1;
+ len = 0;
+ }
+ else
+ {
+ len++;
+ }
+ }
+
+ g_string_append_len (string, val, len);
+ g_string_append_c (string, '\"');
+ }
+ else
+ {
+ g_string_append_len (string, "\"\"", 2);
+ }
+}
diff --git a/libgimpconfig/gimpconfig-utils.h b/libgimpconfig/gimpconfig-utils.h
new file mode 100644
index 0000000..6975b64
--- /dev/null
+++ b/libgimpconfig/gimpconfig-utils.h
@@ -0,0 +1,52 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * Utility functions for GimpConfig.
+ * Copyright (C) 2001-2003 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_CONFIG_H_INSIDE__) && !defined (GIMP_CONFIG_COMPILATION)
+#error "Only <libgimpconfig/gimpconfig.h> can be included directly."
+#endif
+
+#ifndef __GIMP_CONFIG_UTILS_H__
+#define __GIMP_CONFIG_UTILS_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+GList * gimp_config_diff (GObject *a,
+ GObject *b,
+ GParamFlags flags);
+
+gboolean gimp_config_sync (GObject *src,
+ GObject *dest,
+ GParamFlags flags);
+
+void gimp_config_reset_properties (GObject *object);
+void gimp_config_reset_property (GObject *object,
+ const gchar *property_name);
+
+void gimp_config_string_append_escaped (GString *string,
+ const gchar *val);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_CONFIG_UTILS_H__ */
diff --git a/libgimpconfig/gimpconfig.def b/libgimpconfig/gimpconfig.def
new file mode 100644
index 0000000..582edab
--- /dev/null
+++ b/libgimpconfig/gimpconfig.def
@@ -0,0 +1,94 @@
+EXPORTS
+ gimp_color_config_get_cmyk_color_profile
+ gimp_color_config_get_display_bpc
+ gimp_color_config_get_display_color_profile
+ gimp_color_config_get_display_intent
+ gimp_color_config_get_display_optimize
+ gimp_color_config_get_display_profile_from_gdk
+ gimp_color_config_get_gray_color_profile
+ gimp_color_config_get_mode
+ gimp_color_config_get_rgb_color_profile
+ gimp_color_config_get_simulation_bpc
+ gimp_color_config_get_simulation_color_profile
+ gimp_color_config_get_simulation_gamut_check
+ gimp_color_config_get_simulation_intent
+ gimp_color_config_get_simulation_optimize
+ gimp_color_config_get_type
+ gimp_color_management_mode_get_type
+ gimp_color_rendering_intent_get_type
+ gimp_config_build_data_path
+ gimp_config_build_plug_in_path
+ gimp_config_build_writable_path
+ gimp_config_copy
+ gimp_config_deserialize
+ gimp_config_deserialize_file
+ gimp_config_deserialize_gfile
+ gimp_config_deserialize_properties
+ gimp_config_deserialize_property
+ gimp_config_deserialize_return
+ gimp_config_deserialize_stream
+ gimp_config_deserialize_string
+ gimp_config_diff
+ gimp_config_duplicate
+ gimp_config_error_quark
+ gimp_config_get_type
+ gimp_config_interface_get_type
+ gimp_config_is_equal_to
+ gimp_config_path_expand
+ gimp_config_path_expand_to_files
+ gimp_config_path_get_type
+ gimp_config_path_unexpand
+ gimp_config_reset
+ gimp_config_reset_properties
+ gimp_config_reset_property
+ gimp_config_serialize
+ gimp_config_serialize_changed_properties
+ gimp_config_serialize_properties
+ gimp_config_serialize_property
+ gimp_config_serialize_property_by_name
+ gimp_config_serialize_to_fd
+ gimp_config_serialize_to_file
+ gimp_config_serialize_to_gfile
+ gimp_config_serialize_to_stream
+ gimp_config_serialize_to_string
+ gimp_config_serialize_value
+ gimp_config_string_append_escaped
+ gimp_config_sync
+ gimp_config_writer_close
+ gimp_config_writer_comment
+ gimp_config_writer_comment_mode
+ gimp_config_writer_data
+ gimp_config_writer_finish
+ gimp_config_writer_identifier
+ gimp_config_writer_linefeed
+ gimp_config_writer_new_fd
+ gimp_config_writer_new_file
+ gimp_config_writer_new_gfile
+ gimp_config_writer_new_stream
+ gimp_config_writer_new_string
+ gimp_config_writer_open
+ gimp_config_writer_print
+ gimp_config_writer_printf
+ gimp_config_writer_revert
+ gimp_config_writer_string
+ gimp_file_get_config_path
+ gimp_file_new_for_config_path
+ gimp_param_config_path_get_type
+ gimp_param_spec_config_path
+ gimp_param_spec_config_path_type
+ gimp_scanner_destroy
+ gimp_scanner_new_file
+ gimp_scanner_new_gfile
+ gimp_scanner_new_stream
+ gimp_scanner_new_string
+ gimp_scanner_parse_boolean
+ gimp_scanner_parse_color
+ gimp_scanner_parse_data
+ gimp_scanner_parse_float
+ gimp_scanner_parse_identifier
+ gimp_scanner_parse_int
+ gimp_scanner_parse_int64
+ gimp_scanner_parse_matrix2
+ gimp_scanner_parse_string
+ gimp_scanner_parse_string_no_validate
+ gimp_scanner_parse_token
diff --git a/libgimpconfig/gimpconfig.h b/libgimpconfig/gimpconfig.h
new file mode 100644
index 0000000..1b2bb8e
--- /dev/null
+++ b/libgimpconfig/gimpconfig.h
@@ -0,0 +1,40 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_CONFIG_H__
+#define __GIMP_CONFIG_H__
+
+#define __GIMP_CONFIG_H_INSIDE__
+
+#include <libgimpconfig/gimpconfigtypes.h>
+
+#include <libgimpconfig/gimpconfigwriter.h>
+#include <libgimpconfig/gimpconfig-iface.h>
+#include <libgimpconfig/gimpconfig-error.h>
+#include <libgimpconfig/gimpconfig-serialize.h>
+#include <libgimpconfig/gimpconfig-deserialize.h>
+#include <libgimpconfig/gimpconfig-utils.h>
+#include <libgimpconfig/gimpconfig-params.h>
+#include <libgimpconfig/gimpconfig-path.h>
+#include <libgimpconfig/gimpscanner.h>
+
+#include <libgimpconfig/gimpcolorconfig.h>
+
+#undef __GIMP_CONFIG_H_INSIDE__
+
+#endif /* __GIMP_CONFIG_H__ */
diff --git a/libgimpconfig/gimpconfigenums.c b/libgimpconfig/gimpconfigenums.c
new file mode 100644
index 0000000..294d7c0
--- /dev/null
+++ b/libgimpconfig/gimpconfigenums.c
@@ -0,0 +1,79 @@
+
+/* Generated data (by gimp-mkenums) */
+
+#include "config.h"
+#include <gio/gio.h>
+#include "libgimpbase/gimpbase.h"
+#include "gimpconfigenums.h"
+#include "libgimp/libgimp-intl.h"
+
+/* enumerations from "gimpconfigenums.h" */
+GType
+gimp_color_management_mode_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_COLOR_MANAGEMENT_OFF, "GIMP_COLOR_MANAGEMENT_OFF", "off" },
+ { GIMP_COLOR_MANAGEMENT_DISPLAY, "GIMP_COLOR_MANAGEMENT_DISPLAY", "display" },
+ { GIMP_COLOR_MANAGEMENT_SOFTPROOF, "GIMP_COLOR_MANAGEMENT_SOFTPROOF", "softproof" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_COLOR_MANAGEMENT_OFF, NC_("color-management-mode", "No color management"), NULL },
+ { GIMP_COLOR_MANAGEMENT_DISPLAY, NC_("color-management-mode", "Color-managed display"), NULL },
+ { GIMP_COLOR_MANAGEMENT_SOFTPROOF, NC_("color-management-mode", "Soft-proofing"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpColorManagementMode", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "color-management-mode");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_color_rendering_intent_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL, "GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL", "perceptual" },
+ { GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC, "GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC", "relative-colorimetric" },
+ { GIMP_COLOR_RENDERING_INTENT_SATURATION, "GIMP_COLOR_RENDERING_INTENT_SATURATION", "saturation" },
+ { GIMP_COLOR_RENDERING_INTENT_ABSOLUTE_COLORIMETRIC, "GIMP_COLOR_RENDERING_INTENT_ABSOLUTE_COLORIMETRIC", "absolute-colorimetric" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL, NC_("color-rendering-intent", "Perceptual"), NULL },
+ { GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC, NC_("color-rendering-intent", "Relative colorimetric"), NULL },
+ { GIMP_COLOR_RENDERING_INTENT_SATURATION, NC_("color-rendering-intent", "Saturation"), NULL },
+ { GIMP_COLOR_RENDERING_INTENT_ABSOLUTE_COLORIMETRIC, NC_("color-rendering-intent", "Absolute colorimetric"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpColorRenderingIntent", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "color-rendering-intent");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+
+/* Generated data ends here */
+
diff --git a/libgimpconfig/gimpconfigenums.h b/libgimpconfig/gimpconfigenums.h
new file mode 100644
index 0000000..ec5c759
--- /dev/null
+++ b/libgimpconfig/gimpconfigenums.h
@@ -0,0 +1,68 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpconfigenums.h
+ * Copyright (C) 2004 Stefan Döhla <stefan@doehla.de>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_CONFIG_ENUMS_H__
+#define __GIMP_CONFIG_ENUMS_H__
+
+
+#define GIMP_TYPE_COLOR_MANAGEMENT_MODE (gimp_color_management_mode_get_type ())
+
+GType gimp_color_management_mode_get_type (void) G_GNUC_CONST;
+
+/**
+ * GimpColorManagementMode:
+ * @GIMP_COLOR_MANAGEMENT_OFF: Color management is off
+ * @GIMP_COLOR_MANAGEMENT_DISPLAY: Color managed display
+ * @GIMP_COLOR_MANAGEMENT_SOFTPROOF: Soft-proofing
+ *
+ * Modes of color management.
+ **/
+typedef enum
+{
+ GIMP_COLOR_MANAGEMENT_OFF, /*< desc="No color management" >*/
+ GIMP_COLOR_MANAGEMENT_DISPLAY, /*< desc="Color-managed display" >*/
+ GIMP_COLOR_MANAGEMENT_SOFTPROOF /*< desc="Soft-proofing" >*/
+} GimpColorManagementMode;
+
+
+#define GIMP_TYPE_COLOR_RENDERING_INTENT (gimp_color_rendering_intent_get_type ())
+
+GType gimp_color_rendering_intent_get_type (void) G_GNUC_CONST;
+
+/**
+ * GimpColorRenderingIntent:
+ * @GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL: Preceptual
+ * @GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC: Relative colorimetric
+ * @GIMP_COLOR_RENDERING_INTENT_SATURATION: Saturation
+ * @GIMP_COLOR_RENDERING_INTENT_ABSOLUTE_COLORIMETRIC: Absolute colorimetric
+ *
+ * Intents for color management.
+ **/
+typedef enum
+{
+ GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL, /*< desc="Perceptual" >*/
+ GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC, /*< desc="Relative colorimetric" >*/
+ GIMP_COLOR_RENDERING_INTENT_SATURATION, /*< desc="Saturation" >*/
+ GIMP_COLOR_RENDERING_INTENT_ABSOLUTE_COLORIMETRIC /*< desc="Absolute colorimetric" >*/
+} GimpColorRenderingIntent;
+
+
+#endif /* __GIMP_CONFIG_ENUMS_H__ */
diff --git a/libgimpconfig/gimpconfigtypes.h b/libgimpconfig/gimpconfigtypes.h
new file mode 100644
index 0000000..df2fc93
--- /dev/null
+++ b/libgimpconfig/gimpconfigtypes.h
@@ -0,0 +1,38 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * Config file serialization and deserialization interface
+ * Copyright (C) 2001-2003 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_CONFIG_TYPES_H__
+#define __GIMP_CONFIG_TYPES_H__
+
+
+#include <libgimpbase/gimpbasetypes.h>
+
+
+typedef struct _GimpConfig GimpConfig; /* dummy typedef */
+typedef struct _GimpConfigWriter GimpConfigWriter;
+typedef gchar * GimpConfigPath; /* to satisfy docs */
+
+typedef struct _GimpColorConfig GimpColorConfig;
+
+#include <libgimpconfig/gimpconfigenums.h>
+
+
+#endif /* __GIMP_CONFIG_TYPES_H__ */
diff --git a/libgimpconfig/gimpconfigwriter.c b/libgimpconfig/gimpconfigwriter.c
new file mode 100644
index 0000000..bac83f3
--- /dev/null
+++ b/libgimpconfig/gimpconfigwriter.c
@@ -0,0 +1,799 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpConfigWriter
+ * Copyright (C) 2003 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gio/gio.h>
+
+#ifdef G_OS_WIN32
+#include <gio/gwin32outputstream.h>
+#else
+#include <gio/gunixoutputstream.h>
+#endif
+
+#include "libgimpbase/gimpbase.h"
+
+#include "gimpconfigtypes.h"
+
+#include "gimpconfigwriter.h"
+#include "gimpconfig-iface.h"
+#include "gimpconfig-error.h"
+#include "gimpconfig-serialize.h"
+#include "gimpconfig-utils.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpconfigwriter
+ * @title: GimpConfigWriter
+ * @short_description: Functions for writing config info to a file for
+ * libgimpconfig.
+ *
+ * Functions for writing config info to a file for libgimpconfig.
+ **/
+
+
+struct _GimpConfigWriter
+{
+ GOutputStream *output;
+ GFile *file;
+ GError *error;
+ GString *buffer;
+ gboolean comment;
+ gint depth;
+ gint marker;
+};
+
+
+static inline void gimp_config_writer_flush (GimpConfigWriter *writer);
+static inline void gimp_config_writer_newline (GimpConfigWriter *writer);
+static gboolean gimp_config_writer_close_output (GimpConfigWriter *writer,
+ GError **error);
+
+static inline void
+gimp_config_writer_flush (GimpConfigWriter *writer)
+{
+ GError *error = NULL;
+
+ if (! writer->output)
+ return;
+
+ if (! g_output_stream_write_all (writer->output,
+ writer->buffer->str,
+ writer->buffer->len,
+ NULL, NULL, &error))
+ {
+ g_set_error (&writer->error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_WRITE,
+ _("Error writing to '%s': %s"),
+ writer->file ?
+ gimp_file_get_utf8_name (writer->file) : "output stream",
+ error->message);
+ g_clear_error (&error);
+ }
+
+ g_string_truncate (writer->buffer, 0);
+}
+
+static inline void
+gimp_config_writer_newline (GimpConfigWriter *writer)
+{
+ gint i;
+
+ g_string_append_c (writer->buffer, '\n');
+
+ if (writer->comment)
+ g_string_append_len (writer->buffer, "# ", 2);
+
+ for (i = 0; i < writer->depth; i++)
+ g_string_append_len (writer->buffer, " ", 4);
+}
+
+/**
+ * gimp_config_writer_new_file:
+ * @filename: a filename
+ * @atomic: if %TRUE the file is written atomically
+ * @header: text to include as comment at the top of the file
+ * @error: return location for errors
+ *
+ * Creates a new #GimpConfigWriter and sets it up to write to
+ * @filename. If @atomic is %TRUE, a temporary file is used to avoid
+ * possible race conditions. The temporary file is then moved to
+ * @filename when the writer is closed.
+ *
+ * Return value: a new #GimpConfigWriter or %NULL in case of an error
+ *
+ * Since: 2.4
+ **/
+GimpConfigWriter *
+gimp_config_writer_new_file (const gchar *filename,
+ gboolean atomic,
+ const gchar *header,
+ GError **error)
+{
+ GimpConfigWriter *writer;
+ GFile *file;
+
+ g_return_val_if_fail (filename != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ file = g_file_new_for_path (filename);
+
+ writer = gimp_config_writer_new_gfile (file, atomic, header, error);
+
+ g_object_unref (file);
+
+ return writer;
+}
+
+/**
+ * gimp_config_writer_new_gfile:
+ * @file: a #GFile
+ * @atomic: if %TRUE the file is written atomically
+ * @header: text to include as comment at the top of the file
+ * @error: return location for errors
+ *
+ * Creates a new #GimpConfigWriter and sets it up to write to
+ * @file. If @atomic is %TRUE, a temporary file is used to avoid
+ * possible race conditions. The temporary file is then moved to @file
+ * when the writer is closed.
+ *
+ * Return value: a new #GimpConfigWriter or %NULL in case of an error
+ *
+ * Since: 2.10
+ **/
+GimpConfigWriter *
+gimp_config_writer_new_gfile (GFile *file,
+ gboolean atomic,
+ const gchar *header,
+ GError **error)
+{
+ GimpConfigWriter *writer;
+ GOutputStream *output;
+ GFile *dir;
+
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ dir = g_file_get_parent (file);
+ if (dir && ! g_file_query_exists (dir, NULL))
+ {
+ if (! g_file_make_directory_with_parents (dir, NULL, error))
+ g_prefix_error (error,
+ _("Could not create directory '%s' for '%s': "),
+ gimp_file_get_utf8_name (dir),
+ gimp_file_get_utf8_name (file));
+ }
+ g_object_unref (dir);
+
+ if (error && *error)
+ return NULL;
+
+ if (atomic)
+ {
+ output = G_OUTPUT_STREAM (g_file_replace (file,
+ NULL, FALSE, G_FILE_CREATE_NONE,
+ NULL, error));
+ if (! output)
+ g_prefix_error (error,
+ _("Could not create temporary file for '%s': "),
+ gimp_file_get_utf8_name (file));
+ }
+ else
+ {
+ output = G_OUTPUT_STREAM (g_file_replace (file,
+ NULL, FALSE,
+ G_FILE_CREATE_REPLACE_DESTINATION,
+ NULL, error));
+ }
+
+ if (! output)
+ return NULL;
+
+ writer = g_slice_new0 (GimpConfigWriter);
+
+ writer->output = output;
+ writer->file = g_object_ref (file);
+ writer->buffer = g_string_new (NULL);
+
+ if (header)
+ {
+ gimp_config_writer_comment (writer, header);
+ gimp_config_writer_linefeed (writer);
+ }
+
+ return writer;
+}
+
+/**
+ * gimp_config_writer_new_stream:
+ * @output: a #GOutputStream
+ * @header: text to include as comment at the top of the file
+ * @error: return location for errors
+ *
+ * Creates a new #GimpConfigWriter and sets it up to write to
+ * @output.
+ *
+ * Return value: a new #GimpConfigWriter or %NULL in case of an error
+ *
+ * Since: 2.10
+ **/
+GimpConfigWriter *
+gimp_config_writer_new_stream (GOutputStream *output,
+ const gchar *header,
+ GError **error)
+{
+ GimpConfigWriter *writer;
+
+ g_return_val_if_fail (G_IS_OUTPUT_STREAM (output), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ writer = g_slice_new0 (GimpConfigWriter);
+
+ writer->output = g_object_ref (output);
+ writer->buffer = g_string_new (NULL);
+
+ if (header)
+ {
+ gimp_config_writer_comment (writer, header);
+ gimp_config_writer_linefeed (writer);
+ }
+
+ return writer;
+}
+
+/**
+ * gimp_config_writer_new_fd:
+ * @fd:
+ *
+ * Return value: a new #GimpConfigWriter or %NULL in case of an error
+ *
+ * Since: 2.4
+ **/
+GimpConfigWriter *
+gimp_config_writer_new_fd (gint fd)
+{
+ GimpConfigWriter *writer;
+
+ g_return_val_if_fail (fd > 0, NULL);
+
+ writer = g_slice_new0 (GimpConfigWriter);
+
+#ifdef G_OS_WIN32
+ writer->output = g_win32_output_stream_new ((gpointer) fd, FALSE);
+#else
+ writer->output = g_unix_output_stream_new (fd, FALSE);
+#endif
+
+ writer->buffer = g_string_new (NULL);
+
+ return writer;
+}
+
+/**
+ * gimp_config_writer_new_string:
+ * @string:
+ *
+ * Return value: a new #GimpConfigWriter or %NULL in case of an error
+ *
+ * Since: 2.4
+ **/
+GimpConfigWriter *
+gimp_config_writer_new_string (GString *string)
+{
+ GimpConfigWriter *writer;
+
+ g_return_val_if_fail (string != NULL, NULL);
+
+ writer = g_slice_new0 (GimpConfigWriter);
+
+ writer->buffer = string;
+
+ return writer;
+}
+
+/**
+ * gimp_config_writer_comment_mode:
+ * @writer: a #GimpConfigWriter
+ * @enable: %TRUE to enable comment mode, %FALSE to disable it
+ *
+ * This function toggles whether the @writer should create commented
+ * or uncommented output. This feature is used to generate the
+ * system-wide installed gimprc that documents the default settings.
+ *
+ * Since comments have to start at the beginning of a line, this
+ * function will insert a newline if necessary.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_config_writer_comment_mode (GimpConfigWriter *writer,
+ gboolean enable)
+{
+ g_return_if_fail (writer != NULL);
+
+ if (writer->error)
+ return;
+
+ enable = (enable ? TRUE : FALSE);
+
+ if (writer->comment == enable)
+ return;
+
+ writer->comment = enable;
+
+ if (enable)
+ {
+ if (writer->buffer->len == 0)
+ g_string_append_len (writer->buffer, "# ", 2);
+ else
+ gimp_config_writer_newline (writer);
+ }
+}
+
+
+/**
+ * gimp_config_writer_open:
+ * @writer: a #GimpConfigWriter
+ * @name: name of the element to open
+ *
+ * This function writes the opening parenthesis followed by @name.
+ * It also increases the indentation level and sets a mark that
+ * can be used by gimp_config_writer_revert().
+ *
+ * Since: 2.4
+ **/
+void
+gimp_config_writer_open (GimpConfigWriter *writer,
+ const gchar *name)
+{
+ g_return_if_fail (writer != NULL);
+ g_return_if_fail (name != NULL);
+
+ if (writer->error)
+ return;
+
+ /* store the current buffer length so we can revert to this state */
+ writer->marker = writer->buffer->len;
+
+ if (writer->depth > 0)
+ gimp_config_writer_newline (writer);
+
+ writer->depth++;
+
+ g_string_append_printf (writer->buffer, "(%s", name);
+}
+
+/**
+ * gimp_config_writer_print:
+ * @writer: a #GimpConfigWriter
+ * @string: a string to write
+ * @len: number of bytes from @string or -1 if @string is NUL-terminated.
+ *
+ * Appends a space followed by @string to the @writer. Note that string
+ * must not contain any special characters that might need to be escaped.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_config_writer_print (GimpConfigWriter *writer,
+ const gchar *string,
+ gint len)
+{
+ g_return_if_fail (writer != NULL);
+ g_return_if_fail (len == 0 || string != NULL);
+
+ if (writer->error)
+ return;
+
+ if (len < 0)
+ len = strlen (string);
+
+ if (len)
+ {
+ g_string_append_c (writer->buffer, ' ');
+ g_string_append_len (writer->buffer, string, len);
+ }
+}
+
+/**
+ * gimp_config_writer_printf:
+ * @writer: a #GimpConfigWriter
+ * @format: a format string as described for g_strdup_printf().
+ * @...: list of arguments according to @format
+ *
+ * A printf-like function for #GimpConfigWriter.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_config_writer_printf (GimpConfigWriter *writer,
+ const gchar *format,
+ ...)
+{
+ gchar *buffer;
+ va_list args;
+
+ g_return_if_fail (writer != NULL);
+ g_return_if_fail (format != NULL);
+
+ if (writer->error)
+ return;
+
+ va_start (args, format);
+ buffer = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ g_string_append_c (writer->buffer, ' ');
+ g_string_append (writer->buffer, buffer);
+
+ g_free (buffer);
+}
+
+/**
+ * gimp_config_writer_string:
+ * @writer: a #GimpConfigWriter
+ * @string: a NUL-terminated string
+ *
+ * Writes a string value to @writer. The @string is quoted and special
+ * characters are escaped.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_config_writer_string (GimpConfigWriter *writer,
+ const gchar *string)
+{
+ g_return_if_fail (writer != NULL);
+
+ if (writer->error)
+ return;
+
+ g_string_append_c (writer->buffer, ' ');
+ gimp_config_string_append_escaped (writer->buffer, string);
+}
+
+/**
+ * gimp_config_writer_identifier:
+ * @writer: a #GimpConfigWriter
+ * @identifier: a NUL-terminated string
+ *
+ * Writes an identifier to @writer. The @string is *not* quoted and special
+ * characters are *not* escaped.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_config_writer_identifier (GimpConfigWriter *writer,
+ const gchar *identifier)
+{
+ g_return_if_fail (writer != NULL);
+ g_return_if_fail (identifier != NULL);
+
+ if (writer->error)
+ return;
+
+ g_string_append_printf (writer->buffer, " %s", identifier);
+}
+
+
+/**
+ * gimp_config_writer_data:
+ * @writer: a #GimpConfigWriter
+ * @length:
+ * @data:
+ *
+ * Since: 2.4
+ **/
+void
+gimp_config_writer_data (GimpConfigWriter *writer,
+ gint length,
+ const guint8 *data)
+{
+ gint i;
+
+ g_return_if_fail (writer != NULL);
+ g_return_if_fail (length >= 0);
+ g_return_if_fail (data != NULL || length == 0);
+
+ if (writer->error)
+ return;
+
+ g_string_append (writer->buffer, " \"");
+
+ for (i = 0; i < length; i++)
+ {
+ if (g_ascii_isalpha (data[i]))
+ g_string_append_c (writer->buffer, data[i]);
+ else
+ g_string_append_printf (writer->buffer, "\\%o", data[i]);
+ }
+
+ g_string_append (writer->buffer, "\"");
+}
+
+/**
+ * gimp_config_writer_revert:
+ * @writer: a #GimpConfigWriter
+ *
+ * Reverts all changes to @writer that were done since the last call
+ * to gimp_config_writer_open(). This can only work if you didn't call
+ * gimp_config_writer_close() yet.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_config_writer_revert (GimpConfigWriter *writer)
+{
+ g_return_if_fail (writer != NULL);
+
+ if (writer->error)
+ return;
+
+ g_return_if_fail (writer->depth > 0);
+ g_return_if_fail (writer->marker != -1);
+
+ g_string_truncate (writer->buffer, writer->marker);
+
+ writer->depth--;
+ writer->marker = -1;
+}
+
+/**
+ * gimp_config_writer_close:
+ * @writer: a #GimpConfigWriter
+ *
+ * Closes an element opened with gimp_config_writer_open().
+ *
+ * Since: 2.4
+ **/
+void
+gimp_config_writer_close (GimpConfigWriter *writer)
+{
+ g_return_if_fail (writer != NULL);
+
+ if (writer->error)
+ return;
+
+ g_return_if_fail (writer->depth > 0);
+
+ g_string_append_c (writer->buffer, ')');
+
+ if (--writer->depth == 0)
+ {
+ g_string_append_c (writer->buffer, '\n');
+
+ gimp_config_writer_flush (writer);
+ }
+}
+
+/**
+ * gimp_config_writer_finish:
+ * @writer: a #GimpConfigWriter
+ * @footer: text to include as comment at the bottom of the file
+ * @error: return location for possible errors
+ *
+ * This function finishes the work of @writer and frees it afterwards.
+ * It closes all open elements, appends an optional comment and
+ * releases all resources allocated by @writer. You must not access
+ * the @writer afterwards.
+ *
+ * Return value: %TRUE if everything could be successfully written,
+ * %FALSE otherwise
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_config_writer_finish (GimpConfigWriter *writer,
+ const gchar *footer,
+ GError **error)
+{
+ gboolean success = TRUE;
+
+ g_return_val_if_fail (writer != NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ if (writer->depth < 0)
+ {
+ g_warning ("gimp_config_writer_finish: depth < 0 !!");
+ }
+ else
+ {
+ while (writer->depth)
+ gimp_config_writer_close (writer);
+ }
+
+ if (footer)
+ {
+ gimp_config_writer_linefeed (writer);
+ gimp_config_writer_comment (writer, footer);
+ }
+
+ if (writer->output)
+ {
+ success = gimp_config_writer_close_output (writer, error);
+
+ if (writer->file)
+ g_object_unref (writer->file);
+
+ g_string_free (writer->buffer, TRUE);
+ }
+
+ if (writer->error)
+ {
+ if (error && *error == NULL)
+ g_propagate_error (error, writer->error);
+ else
+ g_clear_error (&writer->error);
+
+ success = FALSE;
+ }
+
+ g_slice_free (GimpConfigWriter, writer);
+
+ return success;
+}
+
+void
+gimp_config_writer_linefeed (GimpConfigWriter *writer)
+{
+ g_return_if_fail (writer != NULL);
+
+ if (writer->error)
+ return;
+
+ if (writer->output && writer->buffer->len == 0 && !writer->comment)
+ {
+ GError *error = NULL;
+
+ if (! g_output_stream_write_all (writer->output, "\n", 1,
+ NULL, NULL, &error))
+ {
+ g_set_error (&writer->error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_WRITE,
+ _("Error writing to '%s': %s"),
+ writer->file ?
+ gimp_file_get_utf8_name (writer->file) : "output stream",
+ error->message);
+ g_clear_error (&error);
+ }
+ }
+ else
+ {
+ gimp_config_writer_newline (writer);
+ }
+}
+
+/**
+ * gimp_config_writer_comment:
+ * @writer: a #GimpConfigWriter
+ * @comment: the comment to write (ASCII only)
+ *
+ * Appends the @comment to @str and inserts linebreaks and hash-marks to
+ * format it as a comment. Note that this function does not handle non-ASCII
+ * characters.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_config_writer_comment (GimpConfigWriter *writer,
+ const gchar *comment)
+{
+ const gchar *s;
+ gboolean comment_mode;
+ gint i, len, space;
+
+#define LINE_LENGTH 75
+
+ g_return_if_fail (writer != NULL);
+
+ if (writer->error)
+ return;
+
+ g_return_if_fail (writer->depth == 0);
+
+ if (!comment)
+ return;
+
+ comment_mode = writer->comment;
+ gimp_config_writer_comment_mode (writer, TRUE);
+
+ len = strlen (comment);
+
+ while (len > 0)
+ {
+ for (s = comment, i = 0, space = 0;
+ *s != '\n' && (i <= LINE_LENGTH || space == 0) && i < len;
+ s++, i++)
+ {
+ if (g_ascii_isspace (*s))
+ space = i;
+ }
+
+ if (i > LINE_LENGTH && space && *s != '\n')
+ i = space;
+
+ g_string_append_len (writer->buffer, comment, i);
+
+ i++;
+
+ comment += i;
+ len -= i;
+
+ if (len > 0)
+ gimp_config_writer_newline (writer);
+ }
+
+ gimp_config_writer_comment_mode (writer, comment_mode);
+ gimp_config_writer_newline (writer);
+
+ if (writer->depth == 0)
+ gimp_config_writer_flush (writer);
+
+#undef LINE_LENGTH
+}
+
+static gboolean
+gimp_config_writer_close_output (GimpConfigWriter *writer,
+ GError **error)
+{
+ g_return_val_if_fail (writer->output != NULL, FALSE);
+
+ if (writer->error)
+ {
+ GCancellable *cancellable = g_cancellable_new ();
+
+ /* Cancel the overwrite initiated by g_file_replace(). */
+ g_cancellable_cancel (cancellable);
+ g_output_stream_close (writer->output, cancellable, NULL);
+ g_object_unref (cancellable);
+
+ g_object_unref (writer->output);
+ writer->output = NULL;
+
+ return FALSE;
+ }
+
+ if (writer->file)
+ {
+ GError *my_error = NULL;
+
+ if (! g_output_stream_close (writer->output, NULL, &my_error))
+ {
+ g_set_error (error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_WRITE,
+ _("Error writing '%s': %s"),
+ gimp_file_get_utf8_name (writer->file),
+ my_error->message);
+ g_clear_error (&my_error);
+
+ g_object_unref (writer->output);
+ writer->output = NULL;
+
+ return FALSE;
+ }
+ }
+
+ g_object_unref (writer->output);
+ writer->output = NULL;
+
+ return TRUE;
+}
diff --git a/libgimpconfig/gimpconfigwriter.h b/libgimpconfig/gimpconfigwriter.h
new file mode 100644
index 0000000..91a1a10
--- /dev/null
+++ b/libgimpconfig/gimpconfigwriter.h
@@ -0,0 +1,74 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpConfigWriter
+ * Copyright (C) 2003 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_CONFIG_H_INSIDE__) && !defined (GIMP_CONFIG_COMPILATION)
+#error "Only <libgimpconfig/gimpconfig.h> can be included directly."
+#endif
+
+#ifndef __GIMP_CONFIG_WRITER_H__
+#define __GIMP_CONFIG_WRITER_H__
+
+
+GimpConfigWriter * gimp_config_writer_new_file (const gchar *filename,
+ gboolean atomic,
+ const gchar *header,
+ GError **error);
+GimpConfigWriter * gimp_config_writer_new_gfile (GFile *file,
+ gboolean atomic,
+ const gchar *header,
+ GError **error);
+GimpConfigWriter * gimp_config_writer_new_stream (GOutputStream *output,
+ const gchar *header,
+ GError **error);
+GimpConfigWriter * gimp_config_writer_new_fd (gint fd);
+GimpConfigWriter * gimp_config_writer_new_string (GString *string);
+
+void gimp_config_writer_open (GimpConfigWriter *writer,
+ const gchar *name);
+void gimp_config_writer_comment_mode (GimpConfigWriter *writer,
+ gboolean enable);
+
+void gimp_config_writer_print (GimpConfigWriter *writer,
+ const gchar *string,
+ gint len);
+void gimp_config_writer_printf (GimpConfigWriter *writer,
+ const gchar *format,
+ ...) G_GNUC_PRINTF (2, 3);
+void gimp_config_writer_identifier (GimpConfigWriter *writer,
+ const gchar *identifier);
+void gimp_config_writer_string (GimpConfigWriter *writer,
+ const gchar *string);
+void gimp_config_writer_data (GimpConfigWriter *writer,
+ gint length,
+ const guint8 *data);
+void gimp_config_writer_comment (GimpConfigWriter *writer,
+ const gchar *comment);
+void gimp_config_writer_linefeed (GimpConfigWriter *writer);
+
+
+void gimp_config_writer_revert (GimpConfigWriter *writer);
+void gimp_config_writer_close (GimpConfigWriter *writer);
+gboolean gimp_config_writer_finish (GimpConfigWriter *writer,
+ const gchar *footer,
+ GError **error);
+
+
+#endif /* __GIMP_CONFIG_WRITER_H__ */
diff --git a/libgimpconfig/gimpscanner.c b/libgimpconfig/gimpscanner.c
new file mode 100644
index 0000000..8f65d08
--- /dev/null
+++ b/libgimpconfig/gimpscanner.c
@@ -0,0 +1,871 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpscanner.c
+ * Copyright (C) 2002 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <errno.h>
+
+#include <cairo.h>
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpmath/gimpmath.h"
+
+#include "gimpconfig-error.h"
+#include "gimpscanner.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpscanner
+ * @title: GimpScanner
+ * @short_description: A wrapper around #GScanner with some convenience API.
+ *
+ * A wrapper around #GScanner with some convenience API.
+ **/
+
+
+typedef struct
+{
+ gchar *name;
+ GMappedFile *mapped;
+ gchar *text;
+ GError **error;
+} GimpScannerData;
+
+
+/* local function prototypes */
+
+static GScanner * gimp_scanner_new (const gchar *name,
+ GMappedFile *mapped,
+ gchar *text,
+ GError **error);
+static void gimp_scanner_message (GScanner *scanner,
+ gchar *message,
+ gboolean is_error);
+
+
+/* public functions */
+
+/**
+ * gimp_scanner_new_file:
+ * @filename:
+ * @error:
+ *
+ * Return value:
+ *
+ * Since: 2.4
+ **/
+GScanner *
+gimp_scanner_new_file (const gchar *filename,
+ GError **error)
+{
+ GScanner *scanner;
+ GFile *file;
+
+ g_return_val_if_fail (filename != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ file = g_file_new_for_path (filename);
+ scanner = gimp_scanner_new_gfile (file, error);
+ g_object_unref (file);
+
+ return scanner;
+}
+
+/**
+ * gimp_scanner_new_gfile:
+ * @file: a #GFile
+ * @error: return location for #GError, or %NULL
+ *
+ * Return value: The new #GScanner.
+ *
+ * Since: 2.10
+ **/
+GScanner *
+gimp_scanner_new_gfile (GFile *file,
+ GError **error)
+{
+ GScanner *scanner;
+ gchar *path;
+
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ path = g_file_get_path (file);
+
+ if (path)
+ {
+ GMappedFile *mapped;
+
+ mapped = g_mapped_file_new (path, FALSE, error);
+ g_free (path);
+
+ if (! mapped)
+ {
+ if (error)
+ {
+ (*error)->domain = GIMP_CONFIG_ERROR;
+ (*error)->code = ((*error)->code == G_FILE_ERROR_NOENT ?
+ GIMP_CONFIG_ERROR_OPEN_ENOENT :
+ GIMP_CONFIG_ERROR_OPEN);
+ }
+
+ return NULL;
+ }
+
+ /* gimp_scanner_new() takes a "name" for the scanner, not a filename */
+ scanner = gimp_scanner_new (gimp_file_get_utf8_name (file),
+ mapped, NULL, error);
+
+ g_scanner_input_text (scanner,
+ g_mapped_file_get_contents (mapped),
+ g_mapped_file_get_length (mapped));
+ }
+ else
+ {
+ GInputStream *input;
+
+ input = G_INPUT_STREAM (g_file_read (file, NULL, error));
+
+ if (! input)
+ {
+ if (error)
+ {
+ (*error)->domain = GIMP_CONFIG_ERROR;
+ (*error)->code = ((*error)->code == G_IO_ERROR_NOT_FOUND ?
+ GIMP_CONFIG_ERROR_OPEN_ENOENT :
+ GIMP_CONFIG_ERROR_OPEN);
+ }
+
+ return NULL;
+ }
+
+ g_object_set_data (G_OBJECT (input), "gimp-data", file);
+
+ scanner = gimp_scanner_new_stream (input, error);
+
+ g_object_unref (input);
+ }
+
+ return scanner;
+}
+
+/**
+ * gimp_scanner_new_stream:
+ * @input: a #GInputStream
+ * @error: return location for #GError, or %NULL
+ *
+ * Return value: The new #GScanner.
+ *
+ * Since: 2.10
+ **/
+GScanner *
+gimp_scanner_new_stream (GInputStream *input,
+ GError **error)
+{
+ GScanner *scanner;
+ GFile *file;
+ const gchar *path;
+ GString *string;
+ gchar buffer[4096];
+ gsize bytes_read;
+
+ g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ file = g_object_get_data (G_OBJECT (input), "gimp-file");
+ if (file)
+ path = gimp_file_get_utf8_name (file);
+ else
+ path = "stream";
+
+ string = g_string_new (NULL);
+
+ do
+ {
+ GError *my_error = NULL;
+ gboolean success;
+
+ success = g_input_stream_read_all (input, buffer, sizeof (buffer),
+ &bytes_read, NULL, &my_error);
+
+ if (bytes_read > 0)
+ g_string_append_len (string, buffer, bytes_read);
+
+ if (! success)
+ {
+ if (string->len > 0)
+ {
+ g_printerr ("%s: read error in '%s', trying to scan "
+ "partial content: %s",
+ G_STRFUNC, path, my_error->message);
+ g_clear_error (&my_error);
+ break;
+ }
+
+ g_string_free (string, TRUE);
+
+ g_propagate_error (error, my_error);
+
+ return NULL;
+ }
+ }
+ while (bytes_read == sizeof (buffer));
+
+ /* gimp_scanner_new() takes a "name" for the scanner, not a filename */
+ scanner = gimp_scanner_new (path, NULL, string->str, error);
+
+ bytes_read = string->len;
+
+ g_scanner_input_text (scanner, g_string_free (string, FALSE), bytes_read);
+
+ return scanner;
+}
+
+/**
+ * gimp_scanner_new_string:
+ * @text:
+ * @text_len:
+ * @error:
+ *
+ * Return value:
+ *
+ * Since: 2.4
+ **/
+GScanner *
+gimp_scanner_new_string (const gchar *text,
+ gint text_len,
+ GError **error)
+{
+ GScanner *scanner;
+
+ g_return_val_if_fail (text != NULL || text_len <= 0, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ if (text_len < 0)
+ text_len = text ? strlen (text) : 0;
+
+ scanner = gimp_scanner_new (NULL, NULL, NULL, error);
+
+ g_scanner_input_text (scanner, text, text_len);
+
+ return scanner;
+}
+
+static GScanner *
+gimp_scanner_new (const gchar *name,
+ GMappedFile *mapped,
+ gchar *text,
+ GError **error)
+{
+ GScanner *scanner;
+ GimpScannerData *data;
+
+ scanner = g_scanner_new (NULL);
+
+ data = g_slice_new0 (GimpScannerData);
+
+ data->name = g_strdup (name);
+ data->mapped = mapped;
+ data->text = text;
+ data->error = error;
+
+ scanner->user_data = data;
+ scanner->msg_handler = gimp_scanner_message;
+
+ scanner->config->cset_identifier_first = ( G_CSET_a_2_z G_CSET_A_2_Z );
+ scanner->config->cset_identifier_nth = ( G_CSET_a_2_z G_CSET_A_2_Z
+ G_CSET_DIGITS "-_" );
+ scanner->config->scan_identifier_1char = TRUE;
+
+ scanner->config->store_int64 = TRUE;
+
+ return scanner;
+}
+
+/**
+ * gimp_scanner_destroy:
+ * @scanner: A #GScanner created by gimp_scanner_new_file() or
+ * gimp_scanner_new_string()
+ *
+ * Since: 2.4
+ **/
+void
+gimp_scanner_destroy (GScanner *scanner)
+{
+ GimpScannerData *data;
+
+ g_return_if_fail (scanner != NULL);
+
+ data = scanner->user_data;
+
+ if (data->mapped)
+ g_mapped_file_unref (data->mapped);
+
+ if (data->text)
+ g_free (data->text);
+
+ g_free (data->name);
+ g_slice_free (GimpScannerData, data);
+
+ g_scanner_destroy (scanner);
+}
+
+/**
+ * gimp_scanner_parse_token:
+ * @scanner: A #GScanner created by gimp_scanner_new_file() or
+ * gimp_scanner_new_string()
+ * @token: the #GTokenType expected as next token.
+ *
+ * Return value: %TRUE if the next token is @token, %FALSE otherwise.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_scanner_parse_token (GScanner *scanner,
+ GTokenType token)
+{
+ if (g_scanner_peek_next_token (scanner) != token)
+ return FALSE;
+
+ g_scanner_get_next_token (scanner);
+
+ return TRUE;
+}
+
+/**
+ * gimp_scanner_parse_identifier:
+ * @scanner: A #GScanner created by gimp_scanner_new_file() or
+ * gimp_scanner_new_string()
+ * @identifier: the expected identifier.
+ *
+ * Return value: %TRUE if the next token is an identifier and if its
+ * value matches @identifier.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_scanner_parse_identifier (GScanner *scanner,
+ const gchar *identifier)
+{
+ if (g_scanner_peek_next_token (scanner) != G_TOKEN_IDENTIFIER)
+ return FALSE;
+
+ g_scanner_get_next_token (scanner);
+
+ if (strcmp (scanner->value.v_identifier, identifier))
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * gimp_scanner_parse_string:
+ * @scanner: A #GScanner created by gimp_scanner_new_file() or
+ * gimp_scanner_new_string()
+ * @dest: Return location for the parsed string
+ *
+ * Return value: %TRUE on success
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_scanner_parse_string (GScanner *scanner,
+ gchar **dest)
+{
+ if (g_scanner_peek_next_token (scanner) != G_TOKEN_STRING)
+ return FALSE;
+
+ g_scanner_get_next_token (scanner);
+
+ if (*scanner->value.v_string)
+ {
+ if (! g_utf8_validate (scanner->value.v_string, -1, NULL))
+ {
+ g_scanner_warn (scanner, _("invalid UTF-8 string"));
+ return FALSE;
+ }
+
+ *dest = g_strdup (scanner->value.v_string);
+ }
+ else
+ {
+ *dest = NULL;
+ }
+
+ return TRUE;
+}
+
+/**
+ * gimp_scanner_parse_string_no_validate:
+ * @scanner: A #GScanner created by gimp_scanner_new_file() or
+ * gimp_scanner_new_string()
+ * @dest: Return location for the parsed string
+ *
+ * Return value: %TRUE on success
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_scanner_parse_string_no_validate (GScanner *scanner,
+ gchar **dest)
+{
+ if (g_scanner_peek_next_token (scanner) != G_TOKEN_STRING)
+ return FALSE;
+
+ g_scanner_get_next_token (scanner);
+
+ if (*scanner->value.v_string)
+ *dest = g_strdup (scanner->value.v_string);
+ else
+ *dest = NULL;
+
+ return TRUE;
+}
+
+/**
+ * gimp_scanner_parse_data:
+ * @scanner: A #GScanner created by gimp_scanner_new_file() or
+ * gimp_scanner_new_string()
+ * @length: Length of the data to parse
+ * @dest: Return location for the parsed data
+ *
+ * Return value: %TRUE on success
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_scanner_parse_data (GScanner *scanner,
+ gint length,
+ guint8 **dest)
+{
+ if (g_scanner_peek_next_token (scanner) != G_TOKEN_STRING)
+ return FALSE;
+
+ g_scanner_get_next_token (scanner);
+
+ if (scanner->value.v_string)
+ *dest = g_memdup (scanner->value.v_string, length);
+ else
+ *dest = NULL;
+
+ return TRUE;
+}
+
+/**
+ * gimp_scanner_parse_int:
+ * @scanner: A #GScanner created by gimp_scanner_new_file() or
+ * gimp_scanner_new_string()
+ * @dest: Return location for the parsed integer
+ *
+ * Return value: %TRUE on success
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_scanner_parse_int (GScanner *scanner,
+ gint *dest)
+{
+ gboolean negate = FALSE;
+
+ if (g_scanner_peek_next_token (scanner) == '-')
+ {
+ negate = TRUE;
+ g_scanner_get_next_token (scanner);
+ }
+
+ if (g_scanner_peek_next_token (scanner) != G_TOKEN_INT)
+ return FALSE;
+
+ g_scanner_get_next_token (scanner);
+
+ if (negate)
+ *dest = -scanner->value.v_int64;
+ else
+ *dest = scanner->value.v_int64;
+
+ return TRUE;
+}
+
+/**
+ * gimp_scanner_parse_int64:
+ * @scanner: A #GScanner created by gimp_scanner_new_file() or
+ * gimp_scanner_new_string()
+ * @dest: Return location for the parsed integer
+ *
+ * Return value: %TRUE on success
+ *
+ * Since: 2.8
+ **/
+gboolean
+gimp_scanner_parse_int64 (GScanner *scanner,
+ gint64 *dest)
+{
+ gboolean negate = FALSE;
+
+ if (g_scanner_peek_next_token (scanner) == '-')
+ {
+ negate = TRUE;
+ g_scanner_get_next_token (scanner);
+ }
+
+ if (g_scanner_peek_next_token (scanner) != G_TOKEN_INT)
+ return FALSE;
+
+ g_scanner_get_next_token (scanner);
+
+ if (negate)
+ *dest = -scanner->value.v_int64;
+ else
+ *dest = scanner->value.v_int64;
+
+ return TRUE;
+}
+
+/**
+ * gimp_scanner_parse_float:
+ * @scanner: A #GScanner created by gimp_scanner_new_file() or
+ * gimp_scanner_new_string()
+ * @dest: Return location for the parsed float
+ *
+ * Return value: %TRUE on success
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_scanner_parse_float (GScanner *scanner,
+ gdouble *dest)
+{
+ gboolean negate = FALSE;
+
+ if (g_scanner_peek_next_token (scanner) == '-')
+ {
+ negate = TRUE;
+ g_scanner_get_next_token (scanner);
+ }
+
+ if (g_scanner_peek_next_token (scanner) == G_TOKEN_FLOAT)
+ {
+ g_scanner_get_next_token (scanner);
+
+ if (negate)
+ *dest = -scanner->value.v_float;
+ else
+ *dest = scanner->value.v_float;
+
+ return TRUE;
+ }
+ else if (g_scanner_peek_next_token (scanner) == G_TOKEN_INT)
+ {
+ g_scanner_get_next_token (scanner);
+
+ if (negate)
+ *dest = -scanner->value.v_int;
+ else
+ *dest = scanner->value.v_int;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * gimp_scanner_parse_boolean:
+ * @scanner: A #GScanner created by gimp_scanner_new_file() or
+ * gimp_scanner_new_string()
+ * @dest: Return location for the parsed boolean
+ *
+ * Return value: %TRUE on success
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_scanner_parse_boolean (GScanner *scanner,
+ gboolean *dest)
+{
+ if (g_scanner_peek_next_token (scanner) != G_TOKEN_IDENTIFIER)
+ return FALSE;
+
+ g_scanner_get_next_token (scanner);
+
+ if (! g_ascii_strcasecmp (scanner->value.v_identifier, "yes") ||
+ ! g_ascii_strcasecmp (scanner->value.v_identifier, "true"))
+ {
+ *dest = TRUE;
+ }
+ else if (! g_ascii_strcasecmp (scanner->value.v_identifier, "no") ||
+ ! g_ascii_strcasecmp (scanner->value.v_identifier, "false"))
+ {
+ *dest = FALSE;
+ }
+ else
+ {
+ g_scanner_error
+ (scanner,
+ /* please don't translate 'yes' and 'no' */
+ _("expected 'yes' or 'no' for boolean token, got '%s'"),
+ scanner->value.v_identifier);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+enum
+{
+ COLOR_RGB = 1,
+ COLOR_RGBA,
+ COLOR_HSV,
+ COLOR_HSVA
+};
+
+/**
+ * gimp_scanner_parse_color:
+ * @scanner: A #GScanner created by gimp_scanner_new_file() or
+ * gimp_scanner_new_string()
+ * @dest: Pointer to a color to store the result
+ *
+ * Return value: %TRUE on success
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_scanner_parse_color (GScanner *scanner,
+ GimpRGB *dest)
+{
+ guint scope_id;
+ guint old_scope_id;
+ GTokenType token;
+ GimpRGB color = { 0.0, 0.0, 0.0, 1.0 };
+
+ scope_id = g_quark_from_static_string ("gimp_scanner_parse_color");
+ old_scope_id = g_scanner_set_scope (scanner, scope_id);
+
+ if (! g_scanner_scope_lookup_symbol (scanner, scope_id, "color-rgb"))
+ {
+ g_scanner_scope_add_symbol (scanner, scope_id,
+ "color-rgb", GINT_TO_POINTER (COLOR_RGB));
+ g_scanner_scope_add_symbol (scanner, scope_id,
+ "color-rgba", GINT_TO_POINTER (COLOR_RGBA));
+ g_scanner_scope_add_symbol (scanner, scope_id,
+ "color-hsv", GINT_TO_POINTER (COLOR_HSV));
+ g_scanner_scope_add_symbol (scanner, scope_id,
+ "color-hsva", GINT_TO_POINTER (COLOR_HSVA));
+ }
+
+ token = G_TOKEN_LEFT_PAREN;
+
+ while (g_scanner_peek_next_token (scanner) == token)
+ {
+ token = g_scanner_get_next_token (scanner);
+
+ switch (token)
+ {
+ case G_TOKEN_LEFT_PAREN:
+ token = G_TOKEN_SYMBOL;
+ break;
+
+ case G_TOKEN_SYMBOL:
+ {
+ gdouble col[4] = { 0.0, 0.0, 0.0, 1.0 };
+ gint n_channels = 4;
+ gboolean is_hsv = FALSE;
+ gint i;
+
+ switch (GPOINTER_TO_INT (scanner->value.v_symbol))
+ {
+ case COLOR_RGB:
+ n_channels = 3;
+ /* fallthrough */
+ case COLOR_RGBA:
+ break;
+
+ case COLOR_HSV:
+ n_channels = 3;
+ /* fallthrough */
+ case COLOR_HSVA:
+ is_hsv = TRUE;
+ break;
+ }
+
+ token = G_TOKEN_FLOAT;
+
+ for (i = 0; i < n_channels; i++)
+ {
+ if (! gimp_scanner_parse_float (scanner, &col[i]))
+ goto finish;
+ }
+
+ if (is_hsv)
+ {
+ GimpHSV hsv;
+
+ gimp_hsva_set (&hsv, col[0], col[1], col[2], col[3]);
+ gimp_hsv_to_rgb (&hsv, &color);
+ }
+ else
+ {
+ gimp_rgba_set (&color, col[0], col[1], col[2], col[3]);
+ }
+
+ token = G_TOKEN_RIGHT_PAREN;
+ }
+ break;
+
+ case G_TOKEN_RIGHT_PAREN:
+ token = G_TOKEN_NONE; /* indicates success */
+ goto finish;
+
+ default: /* do nothing */
+ break;
+ }
+ }
+
+ finish:
+
+ if (token != G_TOKEN_NONE)
+ {
+ g_scanner_get_next_token (scanner);
+ g_scanner_unexp_token (scanner, token, NULL, NULL, NULL,
+ _("fatal parse error"), TRUE);
+ }
+ else
+ {
+ *dest = color;
+ }
+
+ g_scanner_set_scope (scanner, old_scope_id);
+
+ return (token == G_TOKEN_NONE);
+}
+
+/**
+ * gimp_scanner_parse_matrix2:
+ * @scanner: A #GScanner created by gimp_scanner_new_file() or
+ * gimp_scanner_new_string()
+ * @dest: Pointer to a matrix to store the result
+ *
+ * Return value: %TRUE on success
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_scanner_parse_matrix2 (GScanner *scanner,
+ GimpMatrix2 *dest)
+{
+ guint scope_id;
+ guint old_scope_id;
+ GTokenType token;
+ GimpMatrix2 matrix;
+
+ scope_id = g_quark_from_static_string ("gimp_scanner_parse_matrix");
+ old_scope_id = g_scanner_set_scope (scanner, scope_id);
+
+ if (! g_scanner_scope_lookup_symbol (scanner, scope_id, "matrix"))
+ g_scanner_scope_add_symbol (scanner, scope_id,
+ "matrix", GINT_TO_POINTER (0));
+
+ token = G_TOKEN_LEFT_PAREN;
+
+ while (g_scanner_peek_next_token (scanner) == token)
+ {
+ token = g_scanner_get_next_token (scanner);
+
+ switch (token)
+ {
+ case G_TOKEN_LEFT_PAREN:
+ token = G_TOKEN_SYMBOL;
+ break;
+
+ case G_TOKEN_SYMBOL:
+ {
+ token = G_TOKEN_FLOAT;
+
+ if (! gimp_scanner_parse_float (scanner, &matrix.coeff[0][0]))
+ goto finish;
+ if (! gimp_scanner_parse_float (scanner, &matrix.coeff[0][1]))
+ goto finish;
+ if (! gimp_scanner_parse_float (scanner, &matrix.coeff[1][0]))
+ goto finish;
+ if (! gimp_scanner_parse_float (scanner, &matrix.coeff[1][1]))
+ goto finish;
+
+ token = G_TOKEN_RIGHT_PAREN;
+ }
+ break;
+
+ case G_TOKEN_RIGHT_PAREN:
+ token = G_TOKEN_NONE; /* indicates success */
+ goto finish;
+
+ default: /* do nothing */
+ break;
+ }
+ }
+
+ finish:
+
+ if (token != G_TOKEN_NONE)
+ {
+ g_scanner_get_next_token (scanner);
+ g_scanner_unexp_token (scanner, token, NULL, NULL, NULL,
+ _("fatal parse error"), TRUE);
+ }
+ else
+ {
+ *dest = matrix;
+ }
+
+ g_scanner_set_scope (scanner, old_scope_id);
+
+ return (token == G_TOKEN_NONE);
+}
+
+
+/* private functions */
+
+static void
+gimp_scanner_message (GScanner *scanner,
+ gchar *message,
+ gboolean is_error)
+{
+ GimpScannerData *data = scanner->user_data;
+
+ /* we don't expect warnings */
+ g_return_if_fail (is_error);
+
+ if (data->name)
+ g_set_error (data->error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_PARSE,
+ _("Error while parsing '%s' in line %d: %s"),
+ data->name, scanner->line, message);
+ else
+ /* should never happen, thus not marked for translation */
+ g_set_error (data->error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_PARSE,
+ "Error parsing internal buffer: %s", message);
+}
diff --git a/libgimpconfig/gimpscanner.h b/libgimpconfig/gimpscanner.h
new file mode 100644
index 0000000..36ce8d9
--- /dev/null
+++ b/libgimpconfig/gimpscanner.h
@@ -0,0 +1,67 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpscanner.h
+ * Copyright (C) 2002 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_CONFIG_H_INSIDE__) && !defined (GIMP_CONFIG_COMPILATION)
+#error "Only <libgimpconfig/gimpconfig.h> can be included directly."
+#endif
+
+#ifndef __GIMP_SCANNER_H__
+#define __GIMP_SCANNER_H__
+
+
+GScanner * gimp_scanner_new_file (const gchar *filename,
+ GError **error);
+GScanner * gimp_scanner_new_gfile (GFile *file,
+ GError **error);
+GScanner * gimp_scanner_new_stream (GInputStream *input,
+ GError **error);
+GScanner * gimp_scanner_new_string (const gchar *text,
+ gint text_len,
+ GError **error);
+void gimp_scanner_destroy (GScanner *scanner);
+
+gboolean gimp_scanner_parse_token (GScanner *scanner,
+ GTokenType token);
+gboolean gimp_scanner_parse_identifier (GScanner *scanner,
+ const gchar *identifier);
+gboolean gimp_scanner_parse_string (GScanner *scanner,
+ gchar **dest);
+gboolean gimp_scanner_parse_string_no_validate (GScanner *scanner,
+ gchar **dest);
+gboolean gimp_scanner_parse_data (GScanner *scanner,
+ gint length,
+ guint8 **dest);
+gboolean gimp_scanner_parse_int (GScanner *scanner,
+ gint *dest);
+gboolean gimp_scanner_parse_int64 (GScanner *scanner,
+ gint64 *dest);
+gboolean gimp_scanner_parse_float (GScanner *scanner,
+ gdouble *dest);
+gboolean gimp_scanner_parse_boolean (GScanner *scanner,
+ gboolean *dest);
+gboolean gimp_scanner_parse_color (GScanner *scanner,
+ GimpRGB *dest);
+gboolean gimp_scanner_parse_matrix2 (GScanner *scanner,
+ GimpMatrix2 *dest);
+
+
+#endif /* __GIMP_SCANNER_H__ */
diff --git a/libgimpmath/Makefile.am b/libgimpmath/Makefile.am
new file mode 100644
index 0000000..be67ba8
--- /dev/null
+++ b/libgimpmath/Makefile.am
@@ -0,0 +1,93 @@
+## Process this file with automake to produce Makefile.in
+
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+
+if PLATFORM_WIN32
+no_undefined = -no-undefined
+endif
+
+if PLATFORM_WIN32
+else
+libm = -lm
+endif
+
+if OS_WIN32
+gimpmath_def = gimpmath.def
+libgimpmath_export_symbols = -export-symbols $(srcdir)/gimpmath.def
+
+install-libtool-import-lib:
+ $(INSTALL) .libs/libgimpmath-$(GIMP_API_VERSION).dll.a $(DESTDIR)$(libdir)
+ $(INSTALL) $(srcdir)/gimpmath.def $(DESTDIR)$(libdir)
+
+uninstall-libtool-import-lib:
+ -rm $(DESTDIR)$(libdir)/libgimpmath-$(GIMP_API_VERSION).dll.a
+ -rm $(DESTDIR)$(libdir)/gimpmath.def
+else
+install-libtool-import-lib:
+uninstall-libtool-import-lib:
+endif
+
+if MS_LIB_AVAILABLE
+noinst_DATA = gimpmath-$(GIMP_API_VERSION).lib
+
+install-ms-lib:
+ $(INSTALL) gimpmath-$(GIMP_API_VERSION).lib $(DESTDIR)$(libdir)
+
+uninstall-ms-lib:
+ -rm $(DESTDIR)$(libdir)/gimpmath-$(GIMP_API_VERSION).lib
+
+gimpmath-@GIMP_API_VERSION@.lib: gimpmath.def
+ lib -name:libgimpmath-$(GIMP_API_VERSION)-@LT_CURRENT_MINUS_AGE@.dll -def:gimpmath.def -out:$@
+
+else
+install-ms-lib:
+uninstall-ms-lib:
+endif
+
+libgimpmathincludedir = $(includedir)/gimp-$(GIMP_API_VERSION)/libgimpmath
+
+AM_CPPFLAGS = \
+ -DG_LOG_DOMAIN=\"LibGimpMath\" \
+ -DGIMP_MATH_COMPILATION \
+ -I$(top_srcdir) \
+ $(GLIB_CFLAGS) \
+ -I$(includedir)
+
+EXTRA_DIST = \
+ gimpmath.def
+
+lib_LTLIBRARIES = libgimpmath-@GIMP_API_VERSION@.la
+
+libgimpmath_@GIMP_API_VERSION@_la_SOURCES = \
+ gimpmath.h \
+ gimpmathtypes.h \
+ gimpmatrix.c \
+ gimpmatrix.h \
+ gimpmd5.c \
+ gimpmd5.h \
+ gimpvector.c \
+ gimpvector.h
+
+libgimpmathinclude_HEADERS = \
+ gimpmath.h \
+ gimpmathtypes.h \
+ gimpmatrix.h \
+ gimpmd5.h \
+ gimpvector.h
+
+libgimpmath_@GIMP_API_VERSION@_la_LDFLAGS = \
+ -version-info $(LT_VERSION_INFO) \
+ $(no_undefined) \
+ $(libgimpmath_export_symbols)
+
+EXTRA_libgimpmath_@GIMP_API_VERSION@_la_DEPENDENCIES = $(gimpmath_def)
+
+libgimpmath_@GIMP_API_VERSION@_la_LIBADD = \
+ $(libgimpbase) \
+ $(GLIB_LIBS) \
+ $(libm)
+
+
+install-data-local: install-ms-lib install-libtool-import-lib
+
+uninstall-local: uninstall-ms-lib uninstall-libtool-import-lib
diff --git a/libgimpmath/Makefile.in b/libgimpmath/Makefile.in
new file mode 100644
index 0000000..e51bab4
--- /dev/null
+++ b/libgimpmath/Makefile.in
@@ -0,0 +1,1071 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = libgimpmath
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(libgimpmathinclude_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(libdir)" \
+ "$(DESTDIR)$(libgimpmathincludedir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libgimpmath_@GIMP_API_VERSION@_la_DEPENDENCIES = $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am_libgimpmath_@GIMP_API_VERSION@_la_OBJECTS = gimpmatrix.lo \
+ gimpmd5.lo gimpvector.lo
+libgimpmath_@GIMP_API_VERSION@_la_OBJECTS = \
+ $(am_libgimpmath_@GIMP_API_VERSION@_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libgimpmath_@GIMP_API_VERSION@_la_LINK = $(LIBTOOL) $(AM_V_lt) \
+ --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libgimpmath_@GIMP_API_VERSION@_la_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/gimpmatrix.Plo \
+ ./$(DEPDIR)/gimpmd5.Plo ./$(DEPDIR)/gimpvector.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libgimpmath_@GIMP_API_VERSION@_la_SOURCES)
+DIST_SOURCES = $(libgimpmath_@GIMP_API_VERSION@_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+DATA = $(noinst_DATA)
+HEADERS = $(libgimpmathinclude_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+@PLATFORM_WIN32_TRUE@no_undefined = -no-undefined
+@PLATFORM_WIN32_FALSE@libm = -lm
+@OS_WIN32_TRUE@gimpmath_def = gimpmath.def
+@OS_WIN32_TRUE@libgimpmath_export_symbols = -export-symbols $(srcdir)/gimpmath.def
+@MS_LIB_AVAILABLE_TRUE@noinst_DATA = gimpmath-$(GIMP_API_VERSION).lib
+libgimpmathincludedir = $(includedir)/gimp-$(GIMP_API_VERSION)/libgimpmath
+AM_CPPFLAGS = \
+ -DG_LOG_DOMAIN=\"LibGimpMath\" \
+ -DGIMP_MATH_COMPILATION \
+ -I$(top_srcdir) \
+ $(GLIB_CFLAGS) \
+ -I$(includedir)
+
+EXTRA_DIST = \
+ gimpmath.def
+
+lib_LTLIBRARIES = libgimpmath-@GIMP_API_VERSION@.la
+libgimpmath_@GIMP_API_VERSION@_la_SOURCES = \
+ gimpmath.h \
+ gimpmathtypes.h \
+ gimpmatrix.c \
+ gimpmatrix.h \
+ gimpmd5.c \
+ gimpmd5.h \
+ gimpvector.c \
+ gimpvector.h
+
+libgimpmathinclude_HEADERS = \
+ gimpmath.h \
+ gimpmathtypes.h \
+ gimpmatrix.h \
+ gimpmd5.h \
+ gimpvector.h
+
+libgimpmath_@GIMP_API_VERSION@_la_LDFLAGS = \
+ -version-info $(LT_VERSION_INFO) \
+ $(no_undefined) \
+ $(libgimpmath_export_symbols)
+
+EXTRA_libgimpmath_@GIMP_API_VERSION@_la_DEPENDENCIES = $(gimpmath_def)
+libgimpmath_@GIMP_API_VERSION@_la_LIBADD = \
+ $(libgimpbase) \
+ $(GLIB_LIBS) \
+ $(libm)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu libgimpmath/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu libgimpmath/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libgimpmath-@GIMP_API_VERSION@.la: $(libgimpmath_@GIMP_API_VERSION@_la_OBJECTS) $(libgimpmath_@GIMP_API_VERSION@_la_DEPENDENCIES) $(EXTRA_libgimpmath_@GIMP_API_VERSION@_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libgimpmath_@GIMP_API_VERSION@_la_LINK) -rpath $(libdir) $(libgimpmath_@GIMP_API_VERSION@_la_OBJECTS) $(libgimpmath_@GIMP_API_VERSION@_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpmatrix.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpmd5.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpvector.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-libgimpmathincludeHEADERS: $(libgimpmathinclude_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(libgimpmathinclude_HEADERS)'; test -n "$(libgimpmathincludedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libgimpmathincludedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libgimpmathincludedir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(libgimpmathincludedir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(libgimpmathincludedir)" || exit $$?; \
+ done
+
+uninstall-libgimpmathincludeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libgimpmathinclude_HEADERS)'; test -n "$(libgimpmathincludedir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(libgimpmathincludedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(DATA) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libgimpmathincludedir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/gimpmatrix.Plo
+ -rm -f ./$(DEPDIR)/gimpmd5.Plo
+ -rm -f ./$(DEPDIR)/gimpvector.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-data-local install-libgimpmathincludeHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/gimpmatrix.Plo
+ -rm -f ./$(DEPDIR)/gimpmd5.Plo
+ -rm -f ./$(DEPDIR)/gimpvector.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libLTLIBRARIES \
+ uninstall-libgimpmathincludeHEADERS uninstall-local
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libLTLIBRARIES clean-libtool cscopelist-am \
+ ctags ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-data-local install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libLTLIBRARIES \
+ install-libgimpmathincludeHEADERS install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am uninstall-libLTLIBRARIES \
+ uninstall-libgimpmathincludeHEADERS uninstall-local
+
+.PRECIOUS: Makefile
+
+
+@OS_WIN32_TRUE@install-libtool-import-lib:
+@OS_WIN32_TRUE@ $(INSTALL) .libs/libgimpmath-$(GIMP_API_VERSION).dll.a $(DESTDIR)$(libdir)
+@OS_WIN32_TRUE@ $(INSTALL) $(srcdir)/gimpmath.def $(DESTDIR)$(libdir)
+
+@OS_WIN32_TRUE@uninstall-libtool-import-lib:
+@OS_WIN32_TRUE@ -rm $(DESTDIR)$(libdir)/libgimpmath-$(GIMP_API_VERSION).dll.a
+@OS_WIN32_TRUE@ -rm $(DESTDIR)$(libdir)/gimpmath.def
+@OS_WIN32_FALSE@install-libtool-import-lib:
+@OS_WIN32_FALSE@uninstall-libtool-import-lib:
+
+@MS_LIB_AVAILABLE_TRUE@install-ms-lib:
+@MS_LIB_AVAILABLE_TRUE@ $(INSTALL) gimpmath-$(GIMP_API_VERSION).lib $(DESTDIR)$(libdir)
+
+@MS_LIB_AVAILABLE_TRUE@uninstall-ms-lib:
+@MS_LIB_AVAILABLE_TRUE@ -rm $(DESTDIR)$(libdir)/gimpmath-$(GIMP_API_VERSION).lib
+
+@MS_LIB_AVAILABLE_TRUE@gimpmath-@GIMP_API_VERSION@.lib: gimpmath.def
+@MS_LIB_AVAILABLE_TRUE@ lib -name:libgimpmath-$(GIMP_API_VERSION)-@LT_CURRENT_MINUS_AGE@.dll -def:gimpmath.def -out:$@
+
+@MS_LIB_AVAILABLE_FALSE@install-ms-lib:
+@MS_LIB_AVAILABLE_FALSE@uninstall-ms-lib:
+
+install-data-local: install-ms-lib install-libtool-import-lib
+
+uninstall-local: uninstall-ms-lib uninstall-libtool-import-lib
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libgimpmath/gimpmath.def b/libgimpmath/gimpmath.def
new file mode 100644
index 0000000..a32a5ba
--- /dev/null
+++ b/libgimpmath/gimpmath.def
@@ -0,0 +1,78 @@
+EXPORTS
+ gimp_matrix2_determinant
+ gimp_matrix2_get_type
+ gimp_matrix2_identity
+ gimp_matrix2_invert
+ gimp_matrix2_mult
+ gimp_matrix2_transform_point
+ gimp_matrix3_affine
+ gimp_matrix3_determinant
+ gimp_matrix3_equal
+ gimp_matrix3_get_type
+ gimp_matrix3_identity
+ gimp_matrix3_invert
+ gimp_matrix3_is_affine
+ gimp_matrix3_is_diagonal
+ gimp_matrix3_is_identity
+ gimp_matrix3_is_simple
+ gimp_matrix3_mult
+ gimp_matrix3_rotate
+ gimp_matrix3_scale
+ gimp_matrix3_transform_point
+ gimp_matrix3_translate
+ gimp_matrix3_xshear
+ gimp_matrix3_yshear
+ gimp_matrix4_identity
+ gimp_matrix4_mult
+ gimp_matrix4_to_deg
+ gimp_matrix4_transform_point
+ gimp_md5_get_digest
+ gimp_param_matrix2_get_type
+ gimp_param_matrix3_get_type
+ gimp_param_spec_matrix2
+ gimp_param_spec_matrix3
+ gimp_vector2_add
+ gimp_vector2_add_val
+ gimp_vector2_cross_product
+ gimp_vector2_cross_product_val
+ gimp_vector2_inner_product
+ gimp_vector2_inner_product_val
+ gimp_vector2_length
+ gimp_vector2_length_val
+ gimp_vector2_mul
+ gimp_vector2_mul_val
+ gimp_vector2_neg
+ gimp_vector2_neg_val
+ gimp_vector2_new
+ gimp_vector2_normal
+ gimp_vector2_normal_val
+ gimp_vector2_normalize
+ gimp_vector2_normalize_val
+ gimp_vector2_rotate
+ gimp_vector2_rotate_val
+ gimp_vector2_set
+ gimp_vector2_sub
+ gimp_vector2_sub_val
+ gimp_vector3_add
+ gimp_vector3_add_val
+ gimp_vector3_cross_product
+ gimp_vector3_cross_product_val
+ gimp_vector3_inner_product
+ gimp_vector3_inner_product_val
+ gimp_vector3_length
+ gimp_vector3_length_val
+ gimp_vector3_mul
+ gimp_vector3_mul_val
+ gimp_vector3_neg
+ gimp_vector3_neg_val
+ gimp_vector3_new
+ gimp_vector3_normalize
+ gimp_vector3_normalize_val
+ gimp_vector3_rotate
+ gimp_vector3_rotate_val
+ gimp_vector3_set
+ gimp_vector3_sub
+ gimp_vector3_sub_val
+ gimp_vector_2d_to_3d
+ gimp_vector_2d_to_3d_val
+ gimp_vector_3d_to_2d
diff --git a/libgimpmath/gimpmath.h b/libgimpmath/gimpmath.h
new file mode 100644
index 0000000..c20b16f
--- /dev/null
+++ b/libgimpmath/gimpmath.h
@@ -0,0 +1,153 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpmath.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_MATH_H__
+#define __GIMP_MATH_H__
+
+
+#include <math.h>
+
+#ifdef HAVE_IEEEFP_H
+#include <ieeefp.h>
+#endif
+
+#ifdef G_OS_WIN32
+#include <float.h>
+#endif
+
+#define __GIMP_MATH_H_INSIDE__
+
+#include <libgimpmath/gimpmathtypes.h>
+
+#include <libgimpmath/gimpmatrix.h>
+#include <libgimpmath/gimpmd5.h>
+#include <libgimpmath/gimpvector.h>
+
+#undef __GIMP_MATH_H_INSIDE__
+
+
+G_BEGIN_DECLS
+
+
+/**
+ * SECTION: gimpmath
+ * @title: GimpMath
+ * @short_description: Mathematical definitions and macros.
+ *
+ * Mathematical definitions and macros for use both by the GIMP
+ * application and plug-ins. These macros should be used rather than
+ * the ones from &lt;math.h&gt; for enhanced portability.
+ **/
+
+
+/**
+ * RINT:
+ * @x: the value to be rounded
+ *
+ * This macro rounds its argument @x to an integer value in floating
+ * point format. Use RINT() instead of rint().
+ **/
+#if defined (HAVE_RINT) && 0
+/* note: rint() depends on the current floating-point rounding mode. when the
+ * rounding mode is FE_TONEAREST, it, in parctice, breaks ties to even. this
+ * is different from 'floor (x + 0.5)', which breaks ties up. in other words
+ * 'rint (2.5) == 2.0', while 'floor (2.5 + 0.5) == 3.0'. this is asking for
+ * trouble, so let's just use the latter.
+ */
+#define RINT(x) rint(x)
+#else
+#define RINT(x) floor ((x) + 0.5)
+#endif
+
+/**
+ * ROUND:
+ * @x: the value to be rounded.
+ *
+ * This macro rounds its positive argument @x to the nearest integer.
+ **/
+#define ROUND(x) ((int) ((x) + 0.5))
+
+/**
+ * SIGNED_ROUND:
+ * @x: the value to be rounded.
+ *
+ * This macro rounds its argument @x to the nearest integer.
+ **/
+#define SIGNED_ROUND(x) ((int) RINT (x))
+
+/**
+ * SQR:
+ * @x: the value to be squared.
+ *
+ * This macro squares its argument @x.
+ **/
+#define SQR(x) ((x) * (x))
+
+/**
+ * MAX255:
+ * @a: the value to be limited.
+ *
+ * This macro limits it argument @a, an (0-511) int, to 255.
+ **/
+#define MAX255(a) ((a) | (((a) & 256) - (((a) & 256) >> 8)))
+
+/**
+ * CLAMP0255:
+ * @a: the value to be clamped.
+ *
+ * This macro clamps its argument @a, an int32-range int, between 0
+ * and 255 inclusive.
+ **/
+#define CLAMP0255(a) CLAMP(a,0,255)
+
+/**
+ * SAFE_CLAMP:
+ * @x: the value to be limited.
+ * @low: the lower limit.
+ * @high: the upper limit.
+ *
+ * Ensures that @x is between the limits set by @low and @high,
+ * even if @x is NaN. If @low is greater than @high, or if either
+ * of them is NaN, the result is undefined.
+ *
+ * Since: 2.10
+ **/
+#define SAFE_CLAMP(x, low, high) ((x) > (low) ? (x) < (high) ? (x) : (high) : (low))
+
+/**
+ * gimp_deg_to_rad:
+ * @angle: the angle to be converted.
+ *
+ * This macro converts its argument @angle from degree to radian.
+ **/
+#define gimp_deg_to_rad(angle) ((angle) * (2.0 * G_PI) / 360.0)
+
+/**
+ * gimp_rad_to_deg:
+ * @angle: the angle to be converted.
+ *
+ * This macro converts its argument @angle from radian to degree.
+ **/
+#define gimp_rad_to_deg(angle) ((angle) * 360.0 / (2.0 * G_PI))
+
+
+G_END_DECLS
+
+#endif /* __GIMP_MATH_H__ */
diff --git a/libgimpmath/gimpmathtypes.h b/libgimpmath/gimpmathtypes.h
new file mode 100644
index 0000000..a6f4628
--- /dev/null
+++ b/libgimpmath/gimpmathtypes.h
@@ -0,0 +1,114 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpmathtypes.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_MATH_TYPES_H__
+#define __GIMP_MATH_TYPES_H__
+
+
+#include <libgimpbase/gimpbasetypes.h>
+
+
+G_BEGIN_DECLS
+
+typedef struct _GimpMatrix2 GimpMatrix2;
+typedef struct _GimpMatrix3 GimpMatrix3;
+typedef struct _GimpMatrix4 GimpMatrix4;
+
+/**
+ * GimpMatrix2
+ * @coeff: the coefficients
+ *
+ * A two by two matrix.
+ **/
+struct _GimpMatrix2
+{
+ gdouble coeff[2][2];
+};
+
+/**
+ * GimpMatrix3
+ * @coeff: the coefficients
+ *
+ * A three by three matrix.
+ **/
+struct _GimpMatrix3
+{
+ gdouble coeff[3][3];
+};
+
+/**
+ * GimpMatrix4
+ * @coeff: the coefficients
+ *
+ * A four by four matrix.
+ **/
+struct _GimpMatrix4
+{
+ gdouble coeff[4][4];
+};
+
+
+typedef struct _GimpVector2 GimpVector2;
+typedef struct _GimpVector3 GimpVector3;
+typedef struct _GimpVector4 GimpVector4;
+
+/**
+ * GimpVector2:
+ * @x: the x axis
+ * @y: the y axis
+ *
+ * A two dimensional vector.
+ **/
+struct _GimpVector2
+{
+ gdouble x, y;
+};
+
+/**
+ * GimpVector3:
+ * @x: the x axis
+ * @y: the y axis
+ * @z: the z axis
+ *
+ * A three dimensional vector.
+ **/
+struct _GimpVector3
+{
+ gdouble x, y, z;
+};
+
+/**
+ * GimpVector4:
+ * @x: the x axis
+ * @y: the y axis
+ * @z: the z axis
+ * @w: the w axis
+ *
+ * A four dimensional vector.
+ **/
+struct _GimpVector4
+{
+ gdouble x, y, z, w;
+};
+
+
+G_END_DECLS
+
+#endif /* __GIMP_MATH_TYPES_H__ */
diff --git a/libgimpmath/gimpmatrix.c b/libgimpmath/gimpmatrix.c
new file mode 100644
index 0000000..ebee392
--- /dev/null
+++ b/libgimpmath/gimpmatrix.c
@@ -0,0 +1,1080 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpmatrix.c
+ * Copyright (C) 1998 Jay Cox <jaycox@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+
+#include "gimpmath.h"
+
+
+/**
+ * SECTION: gimpmatrix
+ * @title: GimpMatrix
+ * @short_description: Utilities to set up and manipulate 3x3
+ * transformation matrices.
+ * @see_also: #GimpVector2, #GimpVector3, #GimpVector4
+ *
+ * When doing image manipulation you will often need 3x3
+ * transformation matrices that define translation, rotation, scaling,
+ * shearing and arbitrary perspective transformations using a 3x3
+ * matrix. Here you'll find a set of utility functions to set up those
+ * matrices and to perform basic matrix manipulations and tests.
+ *
+ * Each matrix class has a 2 dimensional gdouble coeff member. The
+ * element for row r and column c of the matrix is coeff[r][c].
+ **/
+
+
+#define EPSILON 1e-6
+
+
+static GimpMatrix2 * matrix2_copy (const GimpMatrix2 *matrix);
+
+/**
+ * gimp_matrix2_get_type:
+ *
+ * Reveals the object type
+ *
+ * Returns: the #GType for Matrix2 objects
+ *
+ * Since: 2.4
+ **/
+GType
+gimp_matrix2_get_type (void)
+{
+ static GType matrix_type = 0;
+
+ if (!matrix_type)
+ matrix_type = g_boxed_type_register_static ("GimpMatrix2",
+ (GBoxedCopyFunc) matrix2_copy,
+ (GBoxedFreeFunc) g_free);
+
+ return matrix_type;
+}
+
+
+/*
+ * GIMP_TYPE_PARAM_MATRIX2
+ */
+
+#define GIMP_PARAM_SPEC_MATRIX2(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_MATRIX2, GimpParamSpecMatrix2))
+
+static void gimp_param_matrix2_class_init (GParamSpecClass *class);
+static void gimp_param_matrix2_init (GParamSpec *pspec);
+static void gimp_param_matrix2_set_default (GParamSpec *pspec,
+ GValue *value);
+static gint gimp_param_matrix2_values_cmp (GParamSpec *pspec,
+ const GValue *value1,
+ const GValue *value2);
+
+typedef struct _GimpParamSpecMatrix2 GimpParamSpecMatrix2;
+
+struct _GimpParamSpecMatrix2
+{
+ GParamSpecBoxed parent_instance;
+
+ GimpMatrix2 default_value;
+};
+
+/**
+ * gimp_param_matrix2_get_type:
+ *
+ * Reveals the object type
+ *
+ * Returns: the #GType for a GimpMatrix2 object
+ *
+ * Since: 2.4
+ **/
+GType
+gimp_param_matrix2_get_type (void)
+{
+ static GType spec_type = 0;
+
+ if (!spec_type)
+ {
+ static const GTypeInfo type_info =
+ {
+ sizeof (GParamSpecClass),
+ NULL, NULL,
+ (GClassInitFunc) gimp_param_matrix2_class_init,
+ NULL, NULL,
+ sizeof (GimpParamSpecMatrix2),
+ 0,
+ (GInstanceInitFunc) gimp_param_matrix2_init
+ };
+
+ spec_type = g_type_register_static (G_TYPE_PARAM_BOXED,
+ "GimpParamMatrix2",
+ &type_info, 0);
+ }
+
+ return spec_type;
+}
+
+static void
+gimp_param_matrix2_class_init (GParamSpecClass *class)
+{
+ class->value_type = GIMP_TYPE_MATRIX2;
+ class->value_set_default = gimp_param_matrix2_set_default;
+ class->values_cmp = gimp_param_matrix2_values_cmp;
+}
+
+static void
+gimp_param_matrix2_init (GParamSpec *pspec)
+{
+ GimpParamSpecMatrix2 *cspec = GIMP_PARAM_SPEC_MATRIX2 (pspec);
+
+ gimp_matrix2_identity (&cspec->default_value);
+}
+
+static void
+gimp_param_matrix2_set_default (GParamSpec *pspec,
+ GValue *value)
+{
+ GimpParamSpecMatrix2 *cspec = GIMP_PARAM_SPEC_MATRIX2 (pspec);
+
+ g_value_set_static_boxed (value, &cspec->default_value);
+}
+
+static gint
+gimp_param_matrix2_values_cmp (GParamSpec *pspec,
+ const GValue *value1,
+ const GValue *value2)
+{
+ GimpMatrix2 *matrix1;
+ GimpMatrix2 *matrix2;
+ gint i, j;
+
+ matrix1 = value1->data[0].v_pointer;
+ matrix2 = value2->data[0].v_pointer;
+
+ /* try to return at least *something*, it's useless anyway... */
+
+ if (! matrix1)
+ return matrix2 != NULL ? -1 : 0;
+ else if (! matrix2)
+ return matrix1 != NULL;
+
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 2; j++)
+ if (matrix1->coeff[i][j] != matrix2->coeff[i][j])
+ return 1;
+
+ return 0;
+}
+
+/**
+ * gimp_param_spec_matrix2:
+ * @name: Canonical name of the param
+ * @nick: Nickname of the param
+ * @blurb: Brief description of param.
+ * @default_value: Value to use if none is assigned.
+ * @flags: a combination of #GParamFlags
+ *
+ * Creates a param spec to hold a #GimpMatrix2 value.
+ * See g_param_spec_internal() for more information.
+ *
+ * Returns: a newly allocated #GParamSpec instance
+ *
+ * Since: 2.4
+ **/
+GParamSpec *
+gimp_param_spec_matrix2 (const gchar *name,
+ const gchar *nick,
+ const gchar *blurb,
+ const GimpMatrix2 *default_value,
+ GParamFlags flags)
+{
+ GimpParamSpecMatrix2 *cspec;
+
+ g_return_val_if_fail (default_value != NULL, NULL);
+
+ cspec = g_param_spec_internal (GIMP_TYPE_PARAM_MATRIX2,
+ name, nick, blurb, flags);
+
+ cspec->default_value = *default_value;
+
+ return G_PARAM_SPEC (cspec);
+}
+
+
+static GimpMatrix2 *
+matrix2_copy (const GimpMatrix2 *matrix)
+{
+ return (GimpMatrix2 *) g_memdup (matrix, sizeof (GimpMatrix2));
+}
+
+
+/**
+ * gimp_matrix2_identity:
+ * @matrix: A matrix.
+ *
+ * Sets the matrix to the identity matrix.
+ */
+void
+gimp_matrix2_identity (GimpMatrix2 *matrix)
+{
+ static const GimpMatrix2 identity = { { { 1.0, 0.0 },
+ { 0.0, 1.0 } } };
+
+ *matrix = identity;
+}
+
+/**
+ * gimp_matrix2_mult:
+ * @matrix1: The first input matrix.
+ * @matrix2: The second input matrix which will be overwritten by the result.
+ *
+ * Multiplies two matrices and puts the result into the second one.
+ */
+void
+gimp_matrix2_mult (const GimpMatrix2 *matrix1,
+ GimpMatrix2 *matrix2)
+{
+ GimpMatrix2 tmp;
+
+ tmp.coeff[0][0] = (matrix1->coeff[0][0] * matrix2->coeff[0][0] +
+ matrix1->coeff[0][1] * matrix2->coeff[1][0]);
+ tmp.coeff[0][1] = (matrix1->coeff[0][0] * matrix2->coeff[0][1] +
+ matrix1->coeff[0][1] * matrix2->coeff[1][1]);
+ tmp.coeff[1][0] = (matrix1->coeff[1][0] * matrix2->coeff[0][0] +
+ matrix1->coeff[1][1] * matrix2->coeff[1][0]);
+ tmp.coeff[1][1] = (matrix1->coeff[1][0] * matrix2->coeff[0][1] +
+ matrix1->coeff[1][1] * matrix2->coeff[1][1]);
+
+ *matrix2 = tmp;
+}
+
+/**
+ * gimp_matrix2_determinant:
+ * @matrix: The input matrix.
+ *
+ * Calculates the determinant of the given matrix.
+ *
+ * Returns: The determinant.
+ *
+ * Since: 2.10.16
+ */
+
+gdouble
+gimp_matrix2_determinant (const GimpMatrix2 *matrix)
+{
+ return matrix->coeff[0][0] * matrix->coeff[1][1] -
+ matrix->coeff[0][1] * matrix->coeff[1][0];
+}
+
+/**
+ * gimp_matrix2_invert:
+ * @matrix: The matrix that is to be inverted.
+ *
+ * Inverts the given matrix.
+ *
+ * Since: 2.10.16
+ */
+void
+gimp_matrix2_invert (GimpMatrix2 *matrix)
+{
+ gdouble det = gimp_matrix2_determinant (matrix);
+ gdouble temp;
+
+ if (fabs (det) <= EPSILON)
+ return;
+
+ temp = matrix->coeff[0][0];
+
+ matrix->coeff[0][0] = matrix->coeff[1][1] / det;
+ matrix->coeff[0][1] /= -det;
+ matrix->coeff[1][0] /= -det;
+ matrix->coeff[1][1] = temp / det;
+}
+
+/**
+ * gimp_matrix2_transform_point:
+ * @matrix: The transformation matrix.
+ * @x: The source X coordinate.
+ * @y: The source Y coordinate.
+ * @newx: The transformed X coordinate.
+ * @newy: The transformed Y coordinate.
+ *
+ * Transforms a point in 2D as specified by the transformation matrix.
+ *
+ * Since: 2.10.16
+ */
+void
+gimp_matrix2_transform_point (const GimpMatrix2 *matrix,
+ gdouble x,
+ gdouble y,
+ gdouble *newx,
+ gdouble *newy)
+{
+ *newx = matrix->coeff[0][0] * x + matrix->coeff[0][1] * y;
+ *newy = matrix->coeff[1][0] * x + matrix->coeff[1][1] * y;
+}
+
+
+static GimpMatrix3 * matrix3_copy (const GimpMatrix3 *matrix);
+
+/**
+ * gimp_matrix3_get_type:
+ *
+ * Reveals the object type
+ *
+ * Returns: the #GType for Matrix3 objects
+ *
+ * Since: 2.8
+ **/
+GType
+gimp_matrix3_get_type (void)
+{
+ static GType matrix_type = 0;
+
+ if (!matrix_type)
+ matrix_type = g_boxed_type_register_static ("GimpMatrix3",
+ (GBoxedCopyFunc) matrix3_copy,
+ (GBoxedFreeFunc) g_free);
+
+ return matrix_type;
+}
+
+
+/*
+ * GIMP_TYPE_PARAM_MATRIX3
+ */
+
+#define GIMP_PARAM_SPEC_MATRIX3(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_MATRIX3, GimpParamSpecMatrix3))
+
+static void gimp_param_matrix3_class_init (GParamSpecClass *class);
+static void gimp_param_matrix3_init (GParamSpec *pspec);
+static void gimp_param_matrix3_set_default (GParamSpec *pspec,
+ GValue *value);
+static gint gimp_param_matrix3_values_cmp (GParamSpec *pspec,
+ const GValue *value1,
+ const GValue *value2);
+
+typedef struct _GimpParamSpecMatrix3 GimpParamSpecMatrix3;
+
+struct _GimpParamSpecMatrix3
+{
+ GParamSpecBoxed parent_instance;
+
+ GimpMatrix3 default_value;
+};
+
+/**
+ * gimp_param_matrix3_get_type:
+ *
+ * Reveals the object type
+ *
+ * Returns: the #GType for a GimpMatrix3 object
+ *
+ * Since: 2.8
+ **/
+GType
+gimp_param_matrix3_get_type (void)
+{
+ static GType spec_type = 0;
+
+ if (!spec_type)
+ {
+ static const GTypeInfo type_info =
+ {
+ sizeof (GParamSpecClass),
+ NULL, NULL,
+ (GClassInitFunc) gimp_param_matrix3_class_init,
+ NULL, NULL,
+ sizeof (GimpParamSpecMatrix3),
+ 0,
+ (GInstanceInitFunc) gimp_param_matrix3_init
+ };
+
+ spec_type = g_type_register_static (G_TYPE_PARAM_BOXED,
+ "GimpParamMatrix3",
+ &type_info, 0);
+ }
+
+ return spec_type;
+}
+
+static void
+gimp_param_matrix3_class_init (GParamSpecClass *class)
+{
+ class->value_type = GIMP_TYPE_MATRIX3;
+ class->value_set_default = gimp_param_matrix3_set_default;
+ class->values_cmp = gimp_param_matrix3_values_cmp;
+}
+
+static void
+gimp_param_matrix3_init (GParamSpec *pspec)
+{
+ GimpParamSpecMatrix3 *cspec = GIMP_PARAM_SPEC_MATRIX3 (pspec);
+
+ gimp_matrix3_identity (&cspec->default_value);
+}
+
+static void
+gimp_param_matrix3_set_default (GParamSpec *pspec,
+ GValue *value)
+{
+ GimpParamSpecMatrix3 *cspec = GIMP_PARAM_SPEC_MATRIX3 (pspec);
+
+ g_value_set_static_boxed (value, &cspec->default_value);
+}
+
+static gint
+gimp_param_matrix3_values_cmp (GParamSpec *pspec,
+ const GValue *value1,
+ const GValue *value2)
+{
+ GimpMatrix3 *matrix1;
+ GimpMatrix3 *matrix2;
+ gint i, j;
+
+ matrix1 = value1->data[0].v_pointer;
+ matrix2 = value2->data[0].v_pointer;
+
+ /* try to return at least *something*, it's useless anyway... */
+
+ if (! matrix1)
+ return matrix2 != NULL ? -1 : 0;
+ else if (! matrix2)
+ return matrix1 != NULL;
+
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ if (matrix1->coeff[i][j] != matrix2->coeff[i][j])
+ return 1;
+
+ return 0;
+}
+
+/**
+ * gimp_param_spec_matrix3:
+ * @name: Canonical name of the param
+ * @nick: Nickname of the param
+ * @blurb: Brief description of param.
+ * @default_value: Value to use if none is assigned.
+ * @flags: a combination of #GParamFlags
+ *
+ * Creates a param spec to hold a #GimpMatrix3 value.
+ * See g_param_spec_internal() for more information.
+ *
+ * Returns: a newly allocated #GParamSpec instance
+ *
+ * Since: 2.8
+ **/
+GParamSpec *
+gimp_param_spec_matrix3 (const gchar *name,
+ const gchar *nick,
+ const gchar *blurb,
+ const GimpMatrix3 *default_value,
+ GParamFlags flags)
+{
+ GimpParamSpecMatrix3 *cspec;
+
+ cspec = g_param_spec_internal (GIMP_TYPE_PARAM_MATRIX3,
+ name, nick, blurb, flags);
+
+ if (default_value)
+ cspec->default_value = *default_value;
+
+ return G_PARAM_SPEC (cspec);
+}
+
+
+static GimpMatrix3 *
+matrix3_copy (const GimpMatrix3 *matrix)
+{
+ return (GimpMatrix3 *) g_memdup (matrix, sizeof (GimpMatrix3));
+}
+
+
+/**
+ * gimp_matrix3_identity:
+ * @matrix: A matrix.
+ *
+ * Sets the matrix to the identity matrix.
+ */
+void
+gimp_matrix3_identity (GimpMatrix3 *matrix)
+{
+ static const GimpMatrix3 identity = { { { 1.0, 0.0, 0.0 },
+ { 0.0, 1.0, 0.0 },
+ { 0.0, 0.0, 1.0 } } };
+
+ *matrix = identity;
+}
+
+/**
+ * gimp_matrix3_transform_point:
+ * @matrix: The transformation matrix.
+ * @x: The source X coordinate.
+ * @y: The source Y coordinate.
+ * @newx: The transformed X coordinate.
+ * @newy: The transformed Y coordinate.
+ *
+ * Transforms a point in 2D as specified by the transformation matrix.
+ */
+void
+gimp_matrix3_transform_point (const GimpMatrix3 *matrix,
+ gdouble x,
+ gdouble y,
+ gdouble *newx,
+ gdouble *newy)
+{
+ gdouble w;
+
+ w = matrix->coeff[2][0] * x + matrix->coeff[2][1] * y + matrix->coeff[2][2];
+
+ if (w == 0.0)
+ w = 1.0;
+ else
+ w = 1.0/w;
+
+ *newx = (matrix->coeff[0][0] * x +
+ matrix->coeff[0][1] * y +
+ matrix->coeff[0][2]) * w;
+ *newy = (matrix->coeff[1][0] * x +
+ matrix->coeff[1][1] * y +
+ matrix->coeff[1][2]) * w;
+}
+
+/**
+ * gimp_matrix3_mult:
+ * @matrix1: The first input matrix.
+ * @matrix2: The second input matrix which will be overwritten by the result.
+ *
+ * Multiplies two matrices and puts the result into the second one.
+ */
+void
+gimp_matrix3_mult (const GimpMatrix3 *matrix1,
+ GimpMatrix3 *matrix2)
+{
+ gint i, j;
+ GimpMatrix3 tmp;
+ gdouble t1, t2, t3;
+
+ for (i = 0; i < 3; i++)
+ {
+ t1 = matrix1->coeff[i][0];
+ t2 = matrix1->coeff[i][1];
+ t3 = matrix1->coeff[i][2];
+
+ for (j = 0; j < 3; j++)
+ {
+ tmp.coeff[i][j] = t1 * matrix2->coeff[0][j];
+ tmp.coeff[i][j] += t2 * matrix2->coeff[1][j];
+ tmp.coeff[i][j] += t3 * matrix2->coeff[2][j];
+ }
+ }
+
+ *matrix2 = tmp;
+}
+
+/**
+ * gimp_matrix3_translate:
+ * @matrix: The matrix that is to be translated.
+ * @x: Translation in X direction.
+ * @y: Translation in Y direction.
+ *
+ * Translates the matrix by x and y.
+ */
+void
+gimp_matrix3_translate (GimpMatrix3 *matrix,
+ gdouble x,
+ gdouble y)
+{
+ gdouble g, h, i;
+
+ g = matrix->coeff[2][0];
+ h = matrix->coeff[2][1];
+ i = matrix->coeff[2][2];
+
+ matrix->coeff[0][0] += x * g;
+ matrix->coeff[0][1] += x * h;
+ matrix->coeff[0][2] += x * i;
+ matrix->coeff[1][0] += y * g;
+ matrix->coeff[1][1] += y * h;
+ matrix->coeff[1][2] += y * i;
+}
+
+/**
+ * gimp_matrix3_scale:
+ * @matrix: The matrix that is to be scaled.
+ * @x: X scale factor.
+ * @y: Y scale factor.
+ *
+ * Scales the matrix by x and y
+ */
+void
+gimp_matrix3_scale (GimpMatrix3 *matrix,
+ gdouble x,
+ gdouble y)
+{
+ matrix->coeff[0][0] *= x;
+ matrix->coeff[0][1] *= x;
+ matrix->coeff[0][2] *= x;
+
+ matrix->coeff[1][0] *= y;
+ matrix->coeff[1][1] *= y;
+ matrix->coeff[1][2] *= y;
+}
+
+/**
+ * gimp_matrix3_rotate:
+ * @matrix: The matrix that is to be rotated.
+ * @theta: The angle of rotation (in radians).
+ *
+ * Rotates the matrix by theta degrees.
+ */
+void
+gimp_matrix3_rotate (GimpMatrix3 *matrix,
+ gdouble theta)
+{
+ gdouble t1, t2;
+ gdouble cost, sint;
+
+ cost = cos (theta);
+ sint = sin (theta);
+
+ t1 = matrix->coeff[0][0];
+ t2 = matrix->coeff[1][0];
+ matrix->coeff[0][0] = cost * t1 - sint * t2;
+ matrix->coeff[1][0] = sint * t1 + cost * t2;
+
+ t1 = matrix->coeff[0][1];
+ t2 = matrix->coeff[1][1];
+ matrix->coeff[0][1] = cost * t1 - sint * t2;
+ matrix->coeff[1][1] = sint * t1 + cost * t2;
+
+ t1 = matrix->coeff[0][2];
+ t2 = matrix->coeff[1][2];
+ matrix->coeff[0][2] = cost * t1 - sint * t2;
+ matrix->coeff[1][2] = sint * t1 + cost * t2;
+}
+
+/**
+ * gimp_matrix3_xshear:
+ * @matrix: The matrix that is to be sheared.
+ * @amount: X shear amount.
+ *
+ * Shears the matrix in the X direction.
+ */
+void
+gimp_matrix3_xshear (GimpMatrix3 *matrix,
+ gdouble amount)
+{
+ matrix->coeff[0][0] += amount * matrix->coeff[1][0];
+ matrix->coeff[0][1] += amount * matrix->coeff[1][1];
+ matrix->coeff[0][2] += amount * matrix->coeff[1][2];
+}
+
+/**
+ * gimp_matrix3_yshear:
+ * @matrix: The matrix that is to be sheared.
+ * @amount: Y shear amount.
+ *
+ * Shears the matrix in the Y direction.
+ */
+void
+gimp_matrix3_yshear (GimpMatrix3 *matrix,
+ gdouble amount)
+{
+ matrix->coeff[1][0] += amount * matrix->coeff[0][0];
+ matrix->coeff[1][1] += amount * matrix->coeff[0][1];
+ matrix->coeff[1][2] += amount * matrix->coeff[0][2];
+}
+
+/**
+ * gimp_matrix3_affine:
+ * @matrix: The input matrix.
+ * @a: the 'a' coefficient
+ * @b: the 'b' coefficient
+ * @c: the 'c' coefficient
+ * @d: the 'd' coefficient
+ * @e: the 'e' coefficient
+ * @f: the 'f' coefficient
+ *
+ * Applies the affine transformation given by six values to @matrix.
+ * The six values form define an affine transformation matrix as
+ * illustrated below:
+ *
+ * ( a c e )
+ * ( b d f )
+ * ( 0 0 1 )
+ **/
+void
+gimp_matrix3_affine (GimpMatrix3 *matrix,
+ gdouble a,
+ gdouble b,
+ gdouble c,
+ gdouble d,
+ gdouble e,
+ gdouble f)
+{
+ GimpMatrix3 affine;
+
+ affine.coeff[0][0] = a;
+ affine.coeff[1][0] = b;
+ affine.coeff[2][0] = 0.0;
+
+ affine.coeff[0][1] = c;
+ affine.coeff[1][1] = d;
+ affine.coeff[2][1] = 0.0;
+
+ affine.coeff[0][2] = e;
+ affine.coeff[1][2] = f;
+ affine.coeff[2][2] = 1.0;
+
+ gimp_matrix3_mult (&affine, matrix);
+}
+
+/**
+ * gimp_matrix3_determinant:
+ * @matrix: The input matrix.
+ *
+ * Calculates the determinant of the given matrix.
+ *
+ * Returns: The determinant.
+ */
+gdouble
+gimp_matrix3_determinant (const GimpMatrix3 *matrix)
+{
+ gdouble determinant;
+
+ determinant = (matrix->coeff[0][0] *
+ (matrix->coeff[1][1] * matrix->coeff[2][2] -
+ matrix->coeff[1][2] * matrix->coeff[2][1]));
+ determinant -= (matrix->coeff[1][0] *
+ (matrix->coeff[0][1] * matrix->coeff[2][2] -
+ matrix->coeff[0][2] * matrix->coeff[2][1]));
+ determinant += (matrix->coeff[2][0] *
+ (matrix->coeff[0][1] * matrix->coeff[1][2] -
+ matrix->coeff[0][2] * matrix->coeff[1][1]));
+
+ return determinant;
+}
+
+/**
+ * gimp_matrix3_invert:
+ * @matrix: The matrix that is to be inverted.
+ *
+ * Inverts the given matrix.
+ */
+void
+gimp_matrix3_invert (GimpMatrix3 *matrix)
+{
+ GimpMatrix3 inv;
+ gdouble det;
+
+ det = gimp_matrix3_determinant (matrix);
+
+ if (det == 0.0)
+ return;
+
+ det = 1.0 / det;
+
+ inv.coeff[0][0] = (matrix->coeff[1][1] * matrix->coeff[2][2] -
+ matrix->coeff[1][2] * matrix->coeff[2][1]) * det;
+
+ inv.coeff[1][0] = - (matrix->coeff[1][0] * matrix->coeff[2][2] -
+ matrix->coeff[1][2] * matrix->coeff[2][0]) * det;
+
+ inv.coeff[2][0] = (matrix->coeff[1][0] * matrix->coeff[2][1] -
+ matrix->coeff[1][1] * matrix->coeff[2][0]) * det;
+
+ inv.coeff[0][1] = - (matrix->coeff[0][1] * matrix->coeff[2][2] -
+ matrix->coeff[0][2] * matrix->coeff[2][1]) * det;
+
+ inv.coeff[1][1] = (matrix->coeff[0][0] * matrix->coeff[2][2] -
+ matrix->coeff[0][2] * matrix->coeff[2][0]) * det;
+
+ inv.coeff[2][1] = - (matrix->coeff[0][0] * matrix->coeff[2][1] -
+ matrix->coeff[0][1] * matrix->coeff[2][0]) * det;
+
+ inv.coeff[0][2] = (matrix->coeff[0][1] * matrix->coeff[1][2] -
+ matrix->coeff[0][2] * matrix->coeff[1][1]) * det;
+
+ inv.coeff[1][2] = - (matrix->coeff[0][0] * matrix->coeff[1][2] -
+ matrix->coeff[0][2] * matrix->coeff[1][0]) * det;
+
+ inv.coeff[2][2] = (matrix->coeff[0][0] * matrix->coeff[1][1] -
+ matrix->coeff[0][1] * matrix->coeff[1][0]) * det;
+
+ *matrix = inv;
+}
+
+
+/* functions to test for matrix properties */
+
+/**
+ * gimp_matrix3_is_identity:
+ * @matrix: The matrix that is to be tested.
+ *
+ * Checks if the given matrix is the identity matrix.
+ *
+ * Returns: %TRUE if the matrix is the identity matrix, %FALSE otherwise
+ */
+gboolean
+gimp_matrix3_is_identity (const GimpMatrix3 *matrix)
+{
+ gint i, j;
+
+ for (i = 0; i < 3; i++)
+ {
+ for (j = 0; j < 3; j++)
+ {
+ if (i == j)
+ {
+ if (fabs (matrix->coeff[i][j] - 1.0) > EPSILON)
+ return FALSE;
+ }
+ else
+ {
+ if (fabs (matrix->coeff[i][j]) > EPSILON)
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * gimp_matrix3_is_diagonal:
+ * @matrix: The matrix that is to be tested.
+ *
+ * Checks if the given matrix is diagonal.
+ *
+ * Returns: %TRUE if the matrix is diagonal, %FALSE otherwise
+ */
+gboolean
+gimp_matrix3_is_diagonal (const GimpMatrix3 *matrix)
+{
+ gint i, j;
+
+ for (i = 0; i < 3; i++)
+ {
+ for (j = 0; j < 3; j++)
+ {
+ if (i != j && fabs (matrix->coeff[i][j]) > EPSILON)
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * gimp_matrix3_is_affine:
+ * @matrix: The matrix that is to be tested.
+ *
+ * Checks if the given matrix defines an affine transformation.
+ *
+ * Returns: %TRUE if the matrix defines an affine transformation,
+ * %FALSE otherwise
+ *
+ * Since: 2.4
+ */
+gboolean
+gimp_matrix3_is_affine (const GimpMatrix3 *matrix)
+{
+ return (fabs (matrix->coeff[2][0]) < EPSILON &&
+ fabs (matrix->coeff[2][1]) < EPSILON &&
+ fabs (matrix->coeff[2][2] - 1.0) < EPSILON);
+}
+
+/**
+ * gimp_matrix3_is_simple:
+ * @matrix: The matrix that is to be tested.
+ *
+ * Checks if we'll need to interpolate when applying this matrix as
+ * a transformation.
+ *
+ * Returns: %TRUE if all entries of the upper left 2x2 matrix are
+ * either 0 or 1, %FALSE otherwise
+ */
+gboolean
+gimp_matrix3_is_simple (const GimpMatrix3 *matrix)
+{
+ gdouble absm;
+ gint i, j;
+
+ for (i = 0; i < 2; i++)
+ {
+ for (j = 0; j < 2; j++)
+ {
+ absm = fabs (matrix->coeff[i][j]);
+ if (absm > EPSILON && fabs (absm - 1.0) > EPSILON)
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * gimp_matrix3_equal:
+ * @matrix1: The first matrix
+ * @matrix2: The second matrix
+ *
+ * Checks if two matrices are equal.
+ *
+ * Returns: %TRUE the matrices are equal, %FALSE otherwise
+ *
+ * Since: 2.10.16
+ */
+gboolean
+gimp_matrix3_equal (const GimpMatrix3 *matrix1,
+ const GimpMatrix3 *matrix2)
+{
+ gint i, j;
+
+ for (i = 0; i < 3; i++)
+ {
+ for (j = 0; j < 3; j++)
+ {
+ if (fabs (matrix1->coeff[i][j] - matrix2->coeff[i][j]) > EPSILON)
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * gimp_matrix4_identity:
+ * @matrix: A matrix.
+ *
+ * Sets the matrix to the identity matrix.
+ *
+ * Since: 2.10.16
+ */
+void
+gimp_matrix4_identity (GimpMatrix4 *matrix)
+{
+ gint i, j;
+
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 0; j < 4; j++)
+ matrix->coeff[i][j] = i == j;
+ }
+}
+
+/**
+ * gimp_matrix4_mult:
+ * @matrix1: The first input matrix.
+ * @matrix2: The second input matrix which will be overwritten by the result.
+ *
+ * Multiplies two matrices and puts the result into the second one.
+ *
+ * Since: 2.10.16
+ */
+void
+gimp_matrix4_mult (const GimpMatrix4 *matrix1,
+ GimpMatrix4 *matrix2)
+{
+ GimpMatrix4 result = {};
+ gint i, j, k;
+
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 0; j < 4; j++)
+ {
+ for (k = 0; k < 4; k++)
+ result.coeff[i][j] += matrix1->coeff[i][k] * matrix2->coeff[k][j];
+ }
+ }
+
+ *matrix2 = result;
+}
+
+/**
+ * gimp_matrix4_to_deg:
+ * @matrix:
+ * @a:
+ * @b:
+ * @c:
+ *
+ *
+ **/
+void
+gimp_matrix4_to_deg (const GimpMatrix4 *matrix,
+ gdouble *a,
+ gdouble *b,
+ gdouble *c)
+{
+ *a = 180 * (asin (matrix->coeff[1][0]) / G_PI_2);
+ *b = 180 * (asin (matrix->coeff[2][0]) / G_PI_2);
+ *c = 180 * (asin (matrix->coeff[2][1]) / G_PI_2);
+}
+
+/**
+ * gimp_matrix4_transform_point:
+ * @matrix: The transformation matrix.
+ * @x: The source X coordinate.
+ * @y: The source Y coordinate.
+ * @z: The source Z coordinate.
+ * @newx: The transformed X coordinate.
+ * @newy: The transformed Y coordinate.
+ * @newz: The transformed Z coordinate.
+ *
+ * Transforms a point in 3D as specified by the transformation matrix.
+ *
+ * Returns: The transformed W coordinate.
+ *
+ * Since: 2.10.16
+ */
+gdouble
+gimp_matrix4_transform_point (const GimpMatrix4 *matrix,
+ gdouble x,
+ gdouble y,
+ gdouble z,
+ gdouble *newx,
+ gdouble *newy,
+ gdouble *newz)
+{
+ gdouble neww;
+
+ *newx = matrix->coeff[0][0] * x +
+ matrix->coeff[0][1] * y +
+ matrix->coeff[0][2] * z +
+ matrix->coeff[0][3];
+ *newy = matrix->coeff[1][0] * x +
+ matrix->coeff[1][1] * y +
+ matrix->coeff[1][2] * z +
+ matrix->coeff[1][3];
+ *newz = matrix->coeff[2][0] * x +
+ matrix->coeff[2][1] * y +
+ matrix->coeff[2][2] * z +
+ matrix->coeff[2][3];
+ neww = matrix->coeff[3][0] * x +
+ matrix->coeff[3][1] * y +
+ matrix->coeff[3][2] * z +
+ matrix->coeff[3][3];
+
+ *newx /= neww;
+ *newy /= neww;
+ *newz /= neww;
+
+ return neww;
+}
diff --git a/libgimpmath/gimpmatrix.h b/libgimpmath/gimpmatrix.h
new file mode 100644
index 0000000..a71a218
--- /dev/null
+++ b/libgimpmath/gimpmatrix.h
@@ -0,0 +1,157 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpmatrix.h
+ * Copyright (C) 1998 Jay Cox <jaycox@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_MATH_H_INSIDE__) && !defined (GIMP_MATH_COMPILATION)
+#error "Only <libgimpmath/gimpmath.h> can be included directly."
+#endif
+
+#ifndef __GIMP_MATRIX_H__
+#define __GIMP_MATRIX_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+/*****************/
+/* GimpMatrix2 */
+/*****************/
+
+#define GIMP_TYPE_MATRIX2 (gimp_matrix2_get_type ())
+#define GIMP_VALUE_HOLDS_MATRIX2(value) (G_TYPE_CHECK_VALUE_TYPE ((value), GIMP_TYPE_MATRIX2))
+
+GType gimp_matrix2_get_type (void) G_GNUC_CONST;
+
+
+#define GIMP_TYPE_PARAM_MATRIX2 (gimp_param_matrix2_get_type ())
+#define GIMP_IS_PARAM_SPEC_MATRIX2(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_MATRIX2))
+
+GType gimp_param_matrix2_get_type (void) G_GNUC_CONST;
+
+GParamSpec * gimp_param_spec_matrix2 (const gchar *name,
+ const gchar *nick,
+ const gchar *blurb,
+ const GimpMatrix2 *default_value,
+ GParamFlags flags);
+
+
+void gimp_matrix2_identity (GimpMatrix2 *matrix);
+void gimp_matrix2_mult (const GimpMatrix2 *matrix1,
+ GimpMatrix2 *matrix2);
+
+gdouble gimp_matrix2_determinant (const GimpMatrix2 *matrix);
+void gimp_matrix2_invert (GimpMatrix2 *matrix);
+
+void gimp_matrix2_transform_point (const GimpMatrix2 *matrix,
+ gdouble x,
+ gdouble y,
+ gdouble *newx,
+ gdouble *newy);
+
+
+/*****************/
+/* GimpMatrix3 */
+/*****************/
+
+#define GIMP_TYPE_MATRIX3 (gimp_matrix3_get_type ())
+#define GIMP_VALUE_HOLDS_MATRIX3(value) (G_TYPE_CHECK_VALUE_TYPE ((value), GIMP_TYPE_MATRIX3))
+
+GType gimp_matrix3_get_type (void) G_GNUC_CONST;
+
+
+#define GIMP_TYPE_PARAM_MATRIX3 (gimp_param_matrix3_get_type ())
+#define GIMP_IS_PARAM_SPEC_MATRIX3(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_MATRIX3))
+
+GType gimp_param_matrix3_get_type (void) G_GNUC_CONST;
+
+GParamSpec * gimp_param_spec_matrix3 (const gchar *name,
+ const gchar *nick,
+ const gchar *blurb,
+ const GimpMatrix3 *default_value,
+ GParamFlags flags);
+
+
+void gimp_matrix3_identity (GimpMatrix3 *matrix);
+void gimp_matrix3_mult (const GimpMatrix3 *matrix1,
+ GimpMatrix3 *matrix2);
+void gimp_matrix3_translate (GimpMatrix3 *matrix,
+ gdouble x,
+ gdouble y);
+void gimp_matrix3_scale (GimpMatrix3 *matrix,
+ gdouble x,
+ gdouble y);
+void gimp_matrix3_rotate (GimpMatrix3 *matrix,
+ gdouble theta);
+void gimp_matrix3_xshear (GimpMatrix3 *matrix,
+ gdouble amount);
+void gimp_matrix3_yshear (GimpMatrix3 *matrix,
+ gdouble amount);
+void gimp_matrix3_affine (GimpMatrix3 *matrix,
+ gdouble a,
+ gdouble b,
+ gdouble c,
+ gdouble d,
+ gdouble e,
+ gdouble f);
+
+gdouble gimp_matrix3_determinant (const GimpMatrix3 *matrix);
+void gimp_matrix3_invert (GimpMatrix3 *matrix);
+
+gboolean gimp_matrix3_is_identity (const GimpMatrix3 *matrix);
+gboolean gimp_matrix3_is_diagonal (const GimpMatrix3 *matrix);
+gboolean gimp_matrix3_is_affine (const GimpMatrix3 *matrix);
+gboolean gimp_matrix3_is_simple (const GimpMatrix3 *matrix);
+
+gboolean gimp_matrix3_equal (const GimpMatrix3 *matrix1,
+ const GimpMatrix3 *matrix2);
+
+void gimp_matrix3_transform_point (const GimpMatrix3 *matrix,
+ gdouble x,
+ gdouble y,
+ gdouble *newx,
+ gdouble *newy);
+
+
+/*****************/
+/* GimpMatrix4 */
+/*****************/
+
+void gimp_matrix4_identity (GimpMatrix4 *matrix);
+void gimp_matrix4_mult (const GimpMatrix4 *matrix1,
+ GimpMatrix4 *matrix2);
+
+void gimp_matrix4_to_deg (const GimpMatrix4 *matrix,
+ gdouble *a,
+ gdouble *b,
+ gdouble *c);
+
+gdouble gimp_matrix4_transform_point (const GimpMatrix4 *matrix,
+ gdouble x,
+ gdouble y,
+ gdouble z,
+ gdouble *newx,
+ gdouble *newy,
+ gdouble *newz);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_MATRIX_H__ */
diff --git a/libgimpmath/gimpmd5.c b/libgimpmath/gimpmd5.c
new file mode 100644
index 0000000..712c3a9
--- /dev/null
+++ b/libgimpmath/gimpmd5.c
@@ -0,0 +1,53 @@
+/* LIBGIMP - The GIMP Library
+ *
+ * gimpmd5.c
+ *
+ * Use of this code is deprecated! Use %GChecksum from GLib instead.
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+
+#include "gimpmathtypes.h"
+
+#include "gimpmd5.h"
+
+
+/**
+ * SECTION: gimpmd5
+ * @title: GimpMD5
+ * @short_description: The MD5 message-digest algorithm
+ *
+ * The MD5 message-digest algorithm
+ **/
+
+
+/**
+ * gimp_md5_get_digest:
+ * @buffer: byte buffer
+ * @buffer_size: buffer size (in bytes) or -1 if @buffer is nul-terminated.
+ * @digest: 16 bytes buffer receiving the hash code.
+ *
+ * This function is deprecated! Use %GChecksum from GLib instead.
+ *
+ * Get the md5 hash of a buffer. The result is put in the 16 bytes
+ * buffer @digest. For more information see RFC 1321.
+ **/
+void
+gimp_md5_get_digest (const gchar *buffer,
+ gint buffer_size,
+ guchar digest[16])
+{
+ GChecksum *checksum;
+ gsize len = 16;
+
+ g_return_if_fail (buffer != NULL);
+ g_return_if_fail (digest != NULL);
+
+ checksum = g_checksum_new (G_CHECKSUM_MD5);
+
+ g_checksum_update (checksum, (const guchar *) buffer, buffer_size);
+ g_checksum_get_digest (checksum, digest, &len);
+ g_checksum_free (checksum);
+}
diff --git a/libgimpmath/gimpmd5.h b/libgimpmath/gimpmd5.h
new file mode 100644
index 0000000..1e04e91
--- /dev/null
+++ b/libgimpmath/gimpmd5.h
@@ -0,0 +1,26 @@
+/* LIBGIMP - The GIMP Library
+ *
+ * gimpmd5.h
+ *
+ * Use of this code is deprecated! Use %GChecksum from GLib instead.
+ */
+
+#if !defined (__GIMP_MATH_H_INSIDE__) && !defined (GIMP_MATH_COMPILATION)
+#error "Only <libgimpmath/gimpmath.h> can be included directly."
+#endif
+
+#ifndef __GIMP_MD5_H__
+#define __GIMP_MD5_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+GIMP_DEPRECATED_FOR(GChecksum)
+void gimp_md5_get_digest (const gchar *buffer,
+ gint buffer_size,
+ guchar digest[16]);
+
+G_END_DECLS
+
+#endif /* __GIMP_MD5_H__ */
diff --git a/libgimpmath/gimpvector.c b/libgimpmath/gimpvector.c
new file mode 100644
index 0000000..5f2b174
--- /dev/null
+++ b/libgimpmath/gimpvector.c
@@ -0,0 +1,1129 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpvector.c
+ *
+ * The gimp_vector* functions were taken from:
+ * GCK - The General Convenience Kit
+ * Copyright (C) 1996 Tom Bech
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/**********************************************/
+/* A little collection of useful vector stuff */
+/**********************************************/
+
+#include "config.h"
+
+#include <glib-object.h>
+
+#include "gimpmath.h"
+
+
+/**
+ * SECTION: gimpvector
+ * @title: GimpVector
+ * @short_description: Utilities to set up and manipulate vectors.
+ * @see_also: #GimpMatrix2, #GimpMatrix3, #GimpMatrix4
+ *
+ * Utilities to set up and manipulate vectors.
+ **/
+
+
+/*************************/
+/* Some useful constants */
+/*************************/
+
+static const GimpVector2 gimp_vector2_zero = { 0.0, 0.0 };
+#if 0
+static const GimpVector2 gimp_vector2_unit_x = { 1.0, 0.0 };
+static const GimpVector2 gimp_vector2_unit_y = { 0.0, 1.0 };
+#endif
+
+static const GimpVector3 gimp_vector3_zero = { 0.0, 0.0, 0.0 };
+#if 0
+static const GimpVector3 gimp_vector3_unit_x = { 1.0, 0.0, 0.0 };
+static const GimpVector3 gimp_vector3_unit_y = { 0.0, 1.0, 0.0 };
+static const GimpVector3 gimp_vector3_unit_z = { 0.0, 0.0, 1.0 };
+#endif
+
+/**************************************/
+/* Two dimensional vector functions */
+/**************************************/
+
+/**
+ * gimp_vector2_new:
+ * @x: the X coordinate.
+ * @y: the Y coordinate.
+ *
+ * Creates a #GimpVector2 of coordinates @x and @y.
+ *
+ * Returns: the resulting #GimpVector2.
+ **/
+GimpVector2
+gimp_vector2_new (gdouble x,
+ gdouble y)
+{
+ GimpVector2 vector;
+
+ vector.x = x;
+ vector.y = y;
+
+ return vector;
+}
+
+/**
+ * gimp_vector2_set:
+ * @vector: a pointer to a #GimpVector2.
+ * @x: the X coordinate.
+ * @y: the Y coordinate.
+ *
+ * Sets the X and Y coordinates of @vector to @x and @y.
+ **/
+void
+gimp_vector2_set (GimpVector2 *vector,
+ gdouble x,
+ gdouble y)
+{
+ vector->x = x;
+ vector->y = y;
+}
+
+/**
+ * gimp_vector2_length:
+ * @vector: a pointer to a #GimpVector2.
+ *
+ * Computes the length of a 2D vector.
+ *
+ * Returns: the length of @vector (a positive gdouble).
+ **/
+gdouble
+gimp_vector2_length (const GimpVector2 *vector)
+{
+ return (sqrt (vector->x * vector->x + vector->y * vector->y));
+}
+
+/**
+ * gimp_vector2_length_val:
+ * @vector: a #GimpVector2.
+ *
+ * This function is identical to gimp_vector2_length() but the
+ * vector is passed by value rather than by reference.
+ *
+ * Returns: the length of @vector (a positive gdouble).
+ **/
+gdouble
+gimp_vector2_length_val (GimpVector2 vector)
+{
+ return (sqrt (vector.x * vector.x + vector.y * vector.y));
+}
+
+/**
+ * gimp_vector2_mul:
+ * @vector: a pointer to a #GimpVector2.
+ * @factor: a scalar.
+ *
+ * Multiplies each component of the @vector by @factor. Note that this
+ * is equivalent to multiplying the vectors length by @factor.
+ **/
+void
+gimp_vector2_mul (GimpVector2 *vector,
+ gdouble factor)
+{
+ vector->x *= factor;
+ vector->y *= factor;
+}
+
+/**
+ * gimp_vector2_mul_val:
+ * @vector: a #GimpVector2.
+ * @factor: a scalar.
+ *
+ * This function is identical to gimp_vector2_mul() but the vector is
+ * passed by value rather than by reference.
+ *
+ * Returns: the resulting #GimpVector2.
+ **/
+GimpVector2
+gimp_vector2_mul_val (GimpVector2 vector,
+ gdouble factor)
+{
+ GimpVector2 result;
+
+ result.x = vector.x * factor;
+ result.y = vector.y * factor;
+
+ return result;
+}
+
+
+/**
+ * gimp_vector2_normalize:
+ * @vector: a pointer to a #GimpVector2.
+ *
+ * Normalizes the @vector so the length of the @vector is 1.0. The nul
+ * vector will not be changed.
+ **/
+void
+gimp_vector2_normalize (GimpVector2 *vector)
+{
+ gdouble len;
+
+ len = gimp_vector2_length (vector);
+
+ if (len != 0.0)
+ {
+ len = 1.0 / len;
+ vector->x *= len;
+ vector->y *= len;
+ }
+ else
+ {
+ *vector = gimp_vector2_zero;
+ }
+}
+
+/**
+ * gimp_vector2_normalize_val:
+ * @vector: a #GimpVector2.
+ *
+ * This function is identical to gimp_vector2_normalize() but the
+ * vector is passed by value rather than by reference.
+ *
+ * Returns: a #GimpVector2 parallel to @vector, pointing in the same
+ * direction but with a length of 1.0.
+ **/
+GimpVector2
+gimp_vector2_normalize_val (GimpVector2 vector)
+{
+ GimpVector2 normalized;
+ gdouble len;
+
+ len = gimp_vector2_length_val (vector);
+
+ if (len != 0.0)
+ {
+ len = 1.0 / len;
+ normalized.x = vector.x * len;
+ normalized.y = vector.y * len;
+ return normalized;
+ }
+ else
+ {
+ return gimp_vector2_zero;
+ }
+}
+
+/**
+ * gimp_vector2_neg:
+ * @vector: a pointer to a #GimpVector2.
+ *
+ * Negates the @vector (i.e. negate all its coordinates).
+ **/
+void
+gimp_vector2_neg (GimpVector2 *vector)
+{
+ vector->x *= -1.0;
+ vector->y *= -1.0;
+}
+
+/**
+ * gimp_vector2_neg_val:
+ * @vector: a #GimpVector2.
+ *
+ * This function is identical to gimp_vector2_neg() but the vector
+ * is passed by value rather than by reference.
+ *
+ * Returns: the negated #GimpVector2.
+ **/
+GimpVector2
+gimp_vector2_neg_val (GimpVector2 vector)
+{
+ GimpVector2 result;
+
+ result.x = vector.x * -1.0;
+ result.y = vector.y * -1.0;
+
+ return result;
+}
+
+/**
+ * gimp_vector2_add:
+ * @result: destination for the resulting #GimpVector2.
+ * @vector1: a pointer to the first #GimpVector2.
+ * @vector2: a pointer to the second #GimpVector2.
+ *
+ * Computes the sum of two 2D vectors. The resulting #GimpVector2 is
+ * stored in @result.
+ **/
+void
+gimp_vector2_add (GimpVector2 *result,
+ const GimpVector2 *vector1,
+ const GimpVector2 *vector2)
+{
+ result->x = vector1->x + vector2->x;
+ result->y = vector1->y + vector2->y;
+}
+
+/**
+ * gimp_vector2_add_val:
+ * @vector1: the first #GimpVector2.
+ * @vector2: the second #GimpVector2.
+ *
+ * This function is identical to gimp_vector2_add() but the vectors
+ * are passed by value rather than by reference.
+ *
+ * Returns: the resulting #GimpVector2.
+ **/
+GimpVector2
+gimp_vector2_add_val (GimpVector2 vector1,
+ GimpVector2 vector2)
+{
+ GimpVector2 result;
+
+ result.x = vector1.x + vector2.x;
+ result.y = vector1.y + vector2.y;
+
+ return result;
+}
+
+/**
+ * gimp_vector2_sub:
+ * @result: the destination for the resulting #GimpVector2.
+ * @vector1: a pointer to the first #GimpVector2.
+ * @vector2: a pointer to the second #GimpVector2.
+ *
+ * Computes the difference of two 2D vectors (@vector1 minus @vector2).
+ * The resulting #GimpVector2 is stored in @result.
+ **/
+void
+gimp_vector2_sub (GimpVector2 *result,
+ const GimpVector2 *vector1,
+ const GimpVector2 *vector2)
+{
+ result->x = vector1->x - vector2->x;
+ result->y = vector1->y - vector2->y;
+}
+
+/**
+ * gimp_vector2_sub_val:
+ * @vector1: the first #GimpVector2.
+ * @vector2: the second #GimpVector2.
+ *
+ * This function is identical to gimp_vector2_sub() but the vectors
+ * are passed by value rather than by reference.
+ *
+ * Returns: the resulting #GimpVector2.
+ **/
+GimpVector2
+gimp_vector2_sub_val (GimpVector2 vector1,
+ GimpVector2 vector2)
+{
+ GimpVector2 result;
+
+ result.x = vector1.x - vector2.x;
+ result.y = vector1.y - vector2.y;
+
+ return result;
+}
+
+/**
+ * gimp_vector2_inner_product:
+ * @vector1: a pointer to the first #GimpVector2.
+ * @vector2: a pointer to the second #GimpVector2.
+ *
+ * Computes the inner (dot) product of two 2D vectors.
+ * This product is zero if and only if the two vectors are orthogonal.
+ *
+ * Returns: The inner product.
+ **/
+gdouble
+gimp_vector2_inner_product (const GimpVector2 *vector1,
+ const GimpVector2 *vector2)
+{
+ return (vector1->x * vector2->x + vector1->y * vector2->y);
+}
+
+/**
+ * gimp_vector2_inner_product_val:
+ * @vector1: the first #GimpVector2.
+ * @vector2: the second #GimpVector2.
+ *
+ * This function is identical to gimp_vector2_inner_product() but the
+ * vectors are passed by value rather than by reference.
+ *
+ * Returns: The inner product.
+ **/
+gdouble
+gimp_vector2_inner_product_val (GimpVector2 vector1,
+ GimpVector2 vector2)
+{
+ return (vector1.x * vector2.x + vector1.y * vector2.y);
+}
+
+/**
+ * gimp_vector2_cross_product:
+ * @vector1: a pointer to the first #GimpVector2.
+ * @vector2: a pointer to the second #GimpVector2.
+ *
+ * Compute the cross product of two vectors. The result is a
+ * #GimpVector2 which is orthogonal to both @vector1 and @vector2. If
+ * @vector1 and @vector2 are parallel, the result will be the nul
+ * vector.
+ *
+ * Note that in 2D, this function is useful to test if two vectors are
+ * parallel or not, or to compute the area spawned by two vectors.
+ *
+ * Returns: The cross product.
+ **/
+GimpVector2
+gimp_vector2_cross_product (const GimpVector2 *vector1,
+ const GimpVector2 *vector2)
+{
+ GimpVector2 normal;
+
+ normal.x = vector1->x * vector2->y - vector1->y * vector2->x;
+ normal.y = vector1->y * vector2->x - vector1->x * vector2->y;
+
+ return normal;
+}
+
+/**
+ * gimp_vector2_cross_product_val:
+ * @vector1: the first #GimpVector2.
+ * @vector2: the second #GimpVector2.
+ *
+ * This function is identical to gimp_vector2_cross_product() but the
+ * vectors are passed by value rather than by reference.
+ *
+ * Returns: The cross product.
+ **/
+GimpVector2
+gimp_vector2_cross_product_val (GimpVector2 vector1,
+ GimpVector2 vector2)
+{
+ GimpVector2 normal;
+
+ normal.x = vector1.x * vector2.y - vector1.y * vector2.x;
+ normal.y = vector1.y * vector2.x - vector1.x * vector2.y;
+
+ return normal;
+}
+
+/**
+ * gimp_vector2_rotate:
+ * @vector: a pointer to a #GimpVector2.
+ * @alpha: an angle (in radians).
+ *
+ * Rotates the @vector counterclockwise by @alpha radians.
+ **/
+void
+gimp_vector2_rotate (GimpVector2 *vector,
+ gdouble alpha)
+{
+ GimpVector2 result;
+
+ result.x = cos (alpha) * vector->x + sin (alpha) * vector->y;
+ result.y = cos (alpha) * vector->y - sin (alpha) * vector->x;
+
+ *vector = result;
+}
+
+/**
+ * gimp_vector2_rotate_val:
+ * @vector: a #GimpVector2.
+ * @alpha: an angle (in radians).
+ *
+ * This function is identical to gimp_vector2_rotate() but the vector
+ * is passed by value rather than by reference.
+ *
+ * Returns: a #GimpVector2 representing @vector rotated by @alpha
+ * radians.
+ **/
+GimpVector2
+gimp_vector2_rotate_val (GimpVector2 vector,
+ gdouble alpha)
+{
+ GimpVector2 result;
+
+ result.x = cos (alpha) * vector.x + sin (alpha) * vector.y;
+ result.y = cos (alpha) * vector.y - sin (alpha) * vector.x;
+
+ return result;
+}
+
+/**
+ * gimp_vector2_normal:
+ * @vector: a pointer to a #GimpVector2.
+ *
+ * Compute a normalized perpendicular vector to @vector
+ *
+ * Returns: a #GimpVector2 perpendicular to @vector, with a length of 1.0.
+ *
+ * Since: 2.8
+ **/
+GimpVector2
+gimp_vector2_normal (GimpVector2 *vector)
+{
+ GimpVector2 result;
+
+ result.x = - vector->y;
+ result.y = vector->x;
+
+ gimp_vector2_normalize (&result);
+
+ return result;
+}
+
+/**
+ * gimp_vector2_normal_val:
+ * @vector: a #GimpVector2.
+ *
+ * This function is identical to gimp_vector2_normal() but the vector
+ * is passed by value rather than by reference.
+ *
+ * Returns: a #GimpVector2 perpendicular to @vector, with a length of 1.0.
+ *
+ * Since: 2.8
+ **/
+GimpVector2
+gimp_vector2_normal_val (GimpVector2 vector)
+{
+ GimpVector2 result;
+
+ result.x = - vector.y;
+ result.y = vector.x;
+
+ gimp_vector2_normalize (&result);
+
+ return result;
+}
+/**************************************/
+/* Three dimensional vector functions */
+/**************************************/
+
+/**
+ * gimp_vector3_new:
+ * @x: the X coordinate.
+ * @y: the Y coordinate.
+ * @z: the Z coordinate.
+ *
+ * Creates a #GimpVector3 of coordinate @x, @y and @z.
+ *
+ * Returns: the resulting #GimpVector3.
+ **/
+GimpVector3
+gimp_vector3_new (gdouble x,
+ gdouble y,
+ gdouble z)
+{
+ GimpVector3 vector;
+
+ vector.x = x;
+ vector.y = y;
+ vector.z = z;
+
+ return vector;
+}
+
+/**
+ * gimp_vector3_set:
+ * @vector: a pointer to a #GimpVector3.
+ * @x: the X coordinate.
+ * @y: the Y coordinate.
+ * @z: the Z coordinate.
+ *
+ * Sets the X, Y and Z coordinates of @vector to @x, @y and @z.
+ **/
+void
+gimp_vector3_set (GimpVector3 *vector,
+ gdouble x,
+ gdouble y,
+ gdouble z)
+{
+ vector->x = x;
+ vector->y = y;
+ vector->z = z;
+}
+
+/**
+ * gimp_vector3_length:
+ * @vector: a pointer to a #GimpVector3.
+ *
+ * Computes the length of a 3D vector.
+ *
+ * Returns: the length of @vector (a positive gdouble).
+ **/
+gdouble
+gimp_vector3_length (const GimpVector3 *vector)
+{
+ return (sqrt (vector->x * vector->x +
+ vector->y * vector->y +
+ vector->z * vector->z));
+}
+
+/**
+ * gimp_vector3_length_val:
+ * @vector: a #GimpVector3.
+ *
+ * This function is identical to gimp_vector3_length() but the vector
+ * is passed by value rather than by reference.
+ *
+ * Returns: the length of @vector (a positive gdouble).
+ **/
+gdouble
+gimp_vector3_length_val (GimpVector3 vector)
+{
+ return (sqrt (vector.x * vector.x +
+ vector.y * vector.y +
+ vector.z * vector.z));
+}
+
+/**
+ * gimp_vector3_mul:
+ * @vector: a pointer to a #GimpVector3.
+ * @factor: a scalar.
+ *
+ * Multiplies each component of the @vector by @factor. Note that
+ * this is equivalent to multiplying the vectors length by @factor.
+ **/
+void
+gimp_vector3_mul (GimpVector3 *vector,
+ gdouble factor)
+{
+ vector->x *= factor;
+ vector->y *= factor;
+ vector->z *= factor;
+}
+
+/**
+ * gimp_vector3_mul_val:
+ * @vector: a #GimpVector3.
+ * @factor: a scalar.
+ *
+ * This function is identical to gimp_vector3_mul() but the vector is
+ * passed by value rather than by reference.
+ *
+ * Returns: the resulting #GimpVector3.
+ **/
+GimpVector3
+gimp_vector3_mul_val (GimpVector3 vector,
+ gdouble factor)
+{
+ GimpVector3 result;
+
+ result.x = vector.x * factor;
+ result.y = vector.y * factor;
+ result.z = vector.z * factor;
+
+ return result;
+}
+
+/**
+ * gimp_vector3_normalize:
+ * @vector: a pointer to a #GimpVector3.
+ *
+ * Normalizes the @vector so the length of the @vector is 1.0. The nul
+ * vector will not be changed.
+ **/
+void
+gimp_vector3_normalize (GimpVector3 *vector)
+{
+ gdouble len;
+
+ len = gimp_vector3_length (vector);
+
+ if (len != 0.0)
+ {
+ len = 1.0 / len;
+ vector->x *= len;
+ vector->y *= len;
+ vector->z *= len;
+ }
+ else
+ {
+ *vector = gimp_vector3_zero;
+ }
+}
+
+/**
+ * gimp_vector3_normalize_val:
+ * @vector: a #GimpVector3.
+ *
+ * This function is identical to gimp_vector3_normalize() but the
+ * vector is passed by value rather than by reference.
+ *
+ * Returns: a #GimpVector3 parallel to @vector, pointing in the same
+ * direction but with a length of 1.0.
+ **/
+GimpVector3
+gimp_vector3_normalize_val (GimpVector3 vector)
+{
+ GimpVector3 result;
+ gdouble len;
+
+ len = gimp_vector3_length_val (vector);
+
+ if (len != 0.0)
+ {
+ len = 1.0 / len;
+ result.x = vector.x * len;
+ result.y = vector.y * len;
+ result.z = vector.z * len;
+ return result;
+ }
+ else
+ {
+ return gimp_vector3_zero;
+ }
+}
+
+/**
+ * gimp_vector3_neg:
+ * @vector: a pointer to a #GimpVector3.
+ *
+ * Negates the @vector (i.e. negate all its coordinates).
+ **/
+void
+gimp_vector3_neg (GimpVector3 *vector)
+{
+ vector->x *= -1.0;
+ vector->y *= -1.0;
+ vector->z *= -1.0;
+}
+
+/**
+ * gimp_vector3_neg_val:
+ * @vector: a #GimpVector3.
+ *
+ * This function is identical to gimp_vector3_neg() but the vector
+ * is passed by value rather than by reference.
+ *
+ * Returns: the negated #GimpVector3.
+ **/
+GimpVector3
+gimp_vector3_neg_val (GimpVector3 vector)
+{
+ GimpVector3 result;
+
+ result.x = vector.x * -1.0;
+ result.y = vector.y * -1.0;
+ result.z = vector.z * -1.0;
+
+ return result;
+}
+
+/**
+ * gimp_vector3_add:
+ * @result: destination for the resulting #GimpVector3.
+ * @vector1: a pointer to the first #GimpVector3.
+ * @vector2: a pointer to the second #GimpVector3.
+ *
+ * Computes the sum of two 3D vectors. The resulting #GimpVector3 is
+ * stored in @result.
+ **/
+void
+gimp_vector3_add (GimpVector3 *result,
+ const GimpVector3 *vector1,
+ const GimpVector3 *vector2)
+{
+ result->x = vector1->x + vector2->x;
+ result->y = vector1->y + vector2->y;
+ result->z = vector1->z + vector2->z;
+}
+
+/**
+ * gimp_vector3_add_val:
+ * @vector1: a #GimpVector3.
+ * @vector2: a #GimpVector3.
+ *
+ * This function is identical to gimp_vector3_add() but the vectors
+ * are passed by value rather than by reference.
+ *
+ * Returns: the resulting #GimpVector3.
+ **/
+GimpVector3
+gimp_vector3_add_val (GimpVector3 vector1,
+ GimpVector3 vector2)
+{
+ GimpVector3 result;
+
+ result.x = vector1.x + vector2.x;
+ result.y = vector1.y + vector2.y;
+ result.z = vector1.z + vector2.z;
+
+ return result;
+}
+
+/**
+ * gimp_vector3_sub:
+ * @result: the destination for the resulting #GimpVector3.
+ * @vector1: a pointer to the first #GimpVector3.
+ * @vector2: a pointer to the second #GimpVector3.
+ *
+ * Computes the difference of two 3D vectors (@vector1 minus @vector2).
+ * The resulting #GimpVector3 is stored in @result.
+ **/
+void
+gimp_vector3_sub (GimpVector3 *result,
+ const GimpVector3 *vector1,
+ const GimpVector3 *vector2)
+{
+ result->x = vector1->x - vector2->x;
+ result->y = vector1->y - vector2->y;
+ result->z = vector1->z - vector2->z;
+}
+
+/**
+ * gimp_vector3_sub_val:
+ * @vector1: a #GimpVector3.
+ * @vector2: a #GimpVector3.
+ *
+ * This function is identical to gimp_vector3_sub() but the vectors
+ * are passed by value rather than by reference.
+ *
+ * Returns: the resulting #GimpVector3.
+ **/
+GimpVector3
+gimp_vector3_sub_val (GimpVector3 vector1,
+ GimpVector3 vector2)
+{
+ GimpVector3 result;
+
+ result.x = vector1.x - vector2.x;
+ result.y = vector1.y - vector2.y;
+ result.z = vector1.z - vector2.z;
+
+ return result;
+}
+
+/**
+ * gimp_vector3_inner_product:
+ * @vector1: a pointer to the first #GimpVector3.
+ * @vector2: a pointer to the second #GimpVector3.
+ *
+ * Computes the inner (dot) product of two 3D vectors. This product
+ * is zero if and only if the two vectors are orthogonal.
+ *
+ * Returns: The inner product.
+ **/
+gdouble
+gimp_vector3_inner_product (const GimpVector3 *vector1,
+ const GimpVector3 *vector2)
+{
+ return (vector1->x * vector2->x +
+ vector1->y * vector2->y +
+ vector1->z * vector2->z);
+}
+
+/**
+ * gimp_vector3_inner_product_val:
+ * @vector1: the first #GimpVector3.
+ * @vector2: the second #GimpVector3.
+ *
+ * This function is identical to gimp_vector3_inner_product() but the
+ * vectors are passed by value rather than by reference.
+ *
+ * Returns: The inner product.
+ **/
+gdouble
+gimp_vector3_inner_product_val (GimpVector3 vector1,
+ GimpVector3 vector2)
+{
+ return (vector1.x * vector2.x +
+ vector1.y * vector2.y +
+ vector1.z * vector2.z);
+}
+
+/**
+ * gimp_vector3_cross_product:
+ * @vector1: a pointer to the first #GimpVector3.
+ * @vector2: a pointer to the second #GimpVector3.
+ *
+ * Compute the cross product of two vectors. The result is a
+ * #GimpVector3 which is orthogonal to both @vector1 and @vector2. If
+ * @vector1 and @vector2 and parallel, the result will be the nul
+ * vector.
+ *
+ * This function can be used to compute the normal of the plane
+ * defined by @vector1 and @vector2.
+ *
+ * Returns: The cross product.
+ **/
+GimpVector3
+gimp_vector3_cross_product (const GimpVector3 *vector1,
+ const GimpVector3 *vector2)
+{
+ GimpVector3 normal;
+
+ normal.x = vector1->y * vector2->z - vector1->z * vector2->y;
+ normal.y = vector1->z * vector2->x - vector1->x * vector2->z;
+ normal.z = vector1->x * vector2->y - vector1->y * vector2->x;
+
+ return normal;
+}
+
+/**
+ * gimp_vector3_cross_product_val:
+ * @vector1: the first #GimpVector3.
+ * @vector2: the second #GimpVector3.
+ *
+ * This function is identical to gimp_vector3_cross_product() but the
+ * vectors are passed by value rather than by reference.
+ *
+ * Returns: The cross product.
+ **/
+GimpVector3
+gimp_vector3_cross_product_val (GimpVector3 vector1,
+ GimpVector3 vector2)
+{
+ GimpVector3 normal;
+
+ normal.x = vector1.y * vector2.z - vector1.z * vector2.y;
+ normal.y = vector1.z * vector2.x - vector1.x * vector2.z;
+ normal.z = vector1.x * vector2.y - vector1.y * vector2.x;
+
+ return normal;
+}
+
+/**
+ * gimp_vector3_rotate:
+ * @vector: a pointer to a #GimpVector3.
+ * @alpha: the angle (in radian) of rotation around the Z axis.
+ * @beta: the angle (in radian) of rotation around the Y axis.
+ * @gamma: the angle (in radian) of rotation around the X axis.
+ *
+ * Rotates the @vector around the three axis (Z, Y, and X) by @alpha,
+ * @beta and @gamma, respectively.
+ *
+ * Note that the order of the rotation is very important. If you
+ * expect a vector to be rotated around X, and then around Y, you will
+ * have to call this function twice. Also, it is often wise to call
+ * this function with only one of @alpha, @beta and @gamma non-zero.
+ **/
+void
+gimp_vector3_rotate (GimpVector3 *vector,
+ gdouble alpha,
+ gdouble beta,
+ gdouble gamma)
+{
+ GimpVector3 s, t;
+
+ /* First we rotate it around the Z axis (XY plane).. */
+ /* ================================================= */
+
+ s.x = cos (alpha) * vector->x + sin (alpha) * vector->y;
+ s.y = cos (alpha) * vector->y - sin (alpha) * vector->x;
+
+ /* ..then around the Y axis (XZ plane).. */
+ /* ===================================== */
+
+ t = s;
+
+ vector->x = cos (beta) *t.x + sin (beta) * vector->z;
+ s.z = cos (beta) *vector->z - sin (beta) * t.x;
+
+ /* ..and at last around the X axis (YZ plane) */
+ /* ========================================== */
+
+ vector->y = cos (gamma) * t.y + sin (gamma) * s.z;
+ vector->z = cos (gamma) * s.z - sin (gamma) * t.y;
+}
+
+/**
+ * gimp_vector3_rotate_val:
+ * @vector: a #GimpVector3.
+ * @alpha: the angle (in radian) of rotation around the Z axis.
+ * @beta: the angle (in radian) of rotation around the Y axis.
+ * @gamma: the angle (in radian) of rotation around the X axis.
+ *
+ * This function is identical to gimp_vector3_rotate() but the vectors
+ * are passed by value rather than by reference.
+ *
+ * Returns: the rotated vector.
+ **/
+GimpVector3
+gimp_vector3_rotate_val (GimpVector3 vector,
+ gdouble alpha,
+ gdouble beta,
+ gdouble gamma)
+{
+ GimpVector3 s, t, result;
+
+ /* First we rotate it around the Z axis (XY plane).. */
+ /* ================================================= */
+
+ s.x = cos (alpha) * vector.x + sin (alpha) * vector.y;
+ s.y = cos (alpha) * vector.y - sin (alpha) * vector.x;
+
+ /* ..then around the Y axis (XZ plane).. */
+ /* ===================================== */
+
+ t = s;
+
+ result.x = cos (beta) *t.x + sin (beta) * vector.z;
+ s.z = cos (beta) *vector.z - sin (beta) * t.x;
+
+ /* ..and at last around the X axis (YZ plane) */
+ /* ========================================== */
+
+ result.y = cos (gamma) * t.y + sin (gamma) * s.z;
+ result.z = cos (gamma) * s.z - sin (gamma) * t.y;
+
+ return result;
+}
+
+/**
+ * gimp_vector_2d_to_3d:
+ * @sx: the abscissa of the upper-left screen rectangle.
+ * @sy: the ordinate of the upper-left screen rectangle.
+ * @w: the width of the screen rectangle.
+ * @h: the height of the screen rectangle.
+ * @x: the abscissa of the point in the screen rectangle to map.
+ * @y: the ordinate of the point in the screen rectangle to map.
+ * @vp: the position of the observer.
+ * @p: the resulting point.
+ *
+ * \"Compute screen (sx, sy) - (sx + w, sy + h) to 3D unit square
+ * mapping. The plane to map to is given in the z field of p. The
+ * observer is located at position vp (vp->z != 0.0).\"
+ *
+ * In other words, this computes the projection of the point (@x, @y)
+ * to the plane z = @p->z (parallel to XY), from the @vp point of view
+ * through the screen (@sx, @sy)->(@sx + @w, @sy + @h)
+ **/
+
+void
+gimp_vector_2d_to_3d (gint sx,
+ gint sy,
+ gint w,
+ gint h,
+ gint x,
+ gint y,
+ const GimpVector3 *vp,
+ GimpVector3 *p)
+{
+ gdouble t = 0.0;
+
+ if (vp->x != 0.0)
+ t = (p->z - vp->z) / vp->z;
+
+ if (t != 0.0)
+ {
+ p->x = vp->x + t * (vp->x - ((gdouble) (x - sx) / (gdouble) w));
+ p->y = vp->y + t * (vp->y - ((gdouble) (y - sy) / (gdouble) h));
+ }
+ else
+ {
+ p->x = (gdouble) (x - sx) / (gdouble) w;
+ p->y = (gdouble) (y - sy) / (gdouble) h;
+ }
+}
+
+/**
+ * gimp_vector_2d_to_3d_val:
+ * @sx: the abscissa of the upper-left screen rectangle.
+ * @sy: the ordinate of the upper-left screen rectangle.
+ * @w: the width of the screen rectangle.
+ * @h: the height of the screen rectangle.
+ * @x: the abscissa of the point in the screen rectangle to map.
+ * @y: the ordinate of the point in the screen rectangle to map.
+ * @vp: position of the observer.
+ * @p: the resulting point.
+ *
+ * This function is identical to gimp_vector_2d_to_3d() but the
+ * position of the @observer and the resulting point @p are passed by
+ * value rather than by reference.
+ *
+ * Returns: the computed #GimpVector3 point.
+ **/
+GimpVector3
+gimp_vector_2d_to_3d_val (gint sx,
+ gint sy,
+ gint w,
+ gint h,
+ gint x,
+ gint y,
+ GimpVector3 vp,
+ GimpVector3 p)
+{
+ GimpVector3 result;
+ gdouble t = 0.0;
+
+ if (vp.x != 0.0)
+ t = (p.z - vp.z) / vp.z;
+
+ if (t != 0.0)
+ {
+ result.x = vp.x + t * (vp.x - ((gdouble) (x - sx) / (gdouble) w));
+ result.y = vp.y + t * (vp.y - ((gdouble) (y - sy) / (gdouble) h));
+ }
+ else
+ {
+ result.x = (gdouble) (x - sx) / (gdouble) w;
+ result.y = (gdouble) (y - sy) / (gdouble) h;
+ }
+
+ result.z = 0;
+ return result;
+}
+
+/**
+ * gimp_vector_3d_to_2d:
+ * @sx: the abscissa of the upper-left screen rectangle.
+ * @sy: the ordinate of the upper-left screen rectangle.
+ * @w: the width of the screen rectangle.
+ * @h: the height of the screen rectangle.
+ * @x: the abscissa of the point in the screen rectangle to map (return value).
+ * @y: the ordinate of the point in the screen rectangle to map (return value).
+ * @vp: position of the observer.
+ * @p: the 3D point to project to the plane.
+ *
+ * Convert the given 3D point to 2D (project it onto the viewing
+ * plane, (sx, sy, 0) - (sx + w, sy + h, 0). The input is assumed to
+ * be in the unit square (0, 0, z) - (1, 1, z). The viewpoint of the
+ * observer is passed in vp.
+ *
+ * This is basically the opposite of gimp_vector_2d_to_3d().
+ **/
+void
+gimp_vector_3d_to_2d (gint sx,
+ gint sy,
+ gint w,
+ gint h,
+ gdouble *x,
+ gdouble *y,
+ const GimpVector3 *vp,
+ const GimpVector3 *p)
+{
+ gdouble t;
+ GimpVector3 dir;
+
+ gimp_vector3_sub (&dir, p, vp);
+ gimp_vector3_normalize (&dir);
+
+ if (dir.z != 0.0)
+ {
+ t = (-1.0 * vp->z) / dir.z;
+ *x = (gdouble) sx + ((vp->x + t * dir.x) * (gdouble) w);
+ *y = (gdouble) sy + ((vp->y + t * dir.y) * (gdouble) h);
+ }
+ else
+ {
+ *x = (gdouble) sx + (p->x * (gdouble) w);
+ *y = (gdouble) sy + (p->y * (gdouble) h);
+ }
+}
diff --git a/libgimpmath/gimpvector.h b/libgimpmath/gimpvector.h
new file mode 100644
index 0000000..77230da
--- /dev/null
+++ b/libgimpmath/gimpvector.h
@@ -0,0 +1,160 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpvector.h
+ *
+ * The gimp_vector* functions were taken from:
+ * GCK - The General Convenience Kit
+ * Copyright (C) 1996 Tom Bech
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_MATH_H_INSIDE__) && !defined (GIMP_MATH_COMPILATION)
+#error "Only <libgimpmath/gimpmath.h> can be included directly."
+#endif
+
+#ifndef __GIMP_VECTOR_H__
+#define __GIMP_VECTOR_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+/* Two dimensional vector functions */
+/* ================================ */
+
+GimpVector2 gimp_vector2_new (gdouble x,
+ gdouble y);
+void gimp_vector2_set (GimpVector2 *vector,
+ gdouble x,
+ gdouble y);
+gdouble gimp_vector2_length (const GimpVector2 *vector);
+gdouble gimp_vector2_length_val (GimpVector2 vector);
+void gimp_vector2_mul (GimpVector2 *vector,
+ gdouble factor);
+GimpVector2 gimp_vector2_mul_val (GimpVector2 vector,
+ gdouble factor);
+void gimp_vector2_normalize (GimpVector2 *vector);
+GimpVector2 gimp_vector2_normalize_val (GimpVector2 vector);
+void gimp_vector2_neg (GimpVector2 *vector);
+GimpVector2 gimp_vector2_neg_val (GimpVector2 vector);
+void gimp_vector2_add (GimpVector2 *result,
+ const GimpVector2 *vector1,
+ const GimpVector2 *vector2);
+GimpVector2 gimp_vector2_add_val (GimpVector2 vector1,
+ GimpVector2 vector2);
+void gimp_vector2_sub (GimpVector2 *result,
+ const GimpVector2 *vector1,
+ const GimpVector2 *vector2);
+GimpVector2 gimp_vector2_sub_val (GimpVector2 vector1,
+ GimpVector2 vector2);
+gdouble gimp_vector2_inner_product (const GimpVector2 *vector1,
+ const GimpVector2 *vector2);
+gdouble gimp_vector2_inner_product_val (GimpVector2 vector1,
+ GimpVector2 vector2);
+GimpVector2 gimp_vector2_cross_product (const GimpVector2 *vector1,
+ const GimpVector2 *vector2);
+GimpVector2 gimp_vector2_cross_product_val (GimpVector2 vector1,
+ GimpVector2 vector2);
+void gimp_vector2_rotate (GimpVector2 *vector,
+ gdouble alpha);
+GimpVector2 gimp_vector2_rotate_val (GimpVector2 vector,
+ gdouble alpha);
+GimpVector2 gimp_vector2_normal (GimpVector2 *vector);
+GimpVector2 gimp_vector2_normal_val (GimpVector2 vector);
+
+/* Three dimensional vector functions */
+/* ================================== */
+
+GimpVector3 gimp_vector3_new (gdouble x,
+ gdouble y,
+ gdouble z);
+void gimp_vector3_set (GimpVector3 *vector,
+ gdouble x,
+ gdouble y,
+ gdouble z);
+gdouble gimp_vector3_length (const GimpVector3 *vector);
+gdouble gimp_vector3_length_val (GimpVector3 vector);
+void gimp_vector3_mul (GimpVector3 *vector,
+ gdouble factor);
+GimpVector3 gimp_vector3_mul_val (GimpVector3 vector,
+ gdouble factor);
+void gimp_vector3_normalize (GimpVector3 *vector);
+GimpVector3 gimp_vector3_normalize_val (GimpVector3 vector);
+void gimp_vector3_neg (GimpVector3 *vector);
+GimpVector3 gimp_vector3_neg_val (GimpVector3 vector);
+void gimp_vector3_add (GimpVector3 *result,
+ const GimpVector3 *vector1,
+ const GimpVector3 *vector2);
+GimpVector3 gimp_vector3_add_val (GimpVector3 vector1,
+ GimpVector3 vector2);
+void gimp_vector3_sub (GimpVector3 *result,
+ const GimpVector3 *vector1,
+ const GimpVector3 *vector2);
+GimpVector3 gimp_vector3_sub_val (GimpVector3 vector1,
+ GimpVector3 vector2);
+gdouble gimp_vector3_inner_product (const GimpVector3 *vector1,
+ const GimpVector3 *vector2);
+gdouble gimp_vector3_inner_product_val (GimpVector3 vector1,
+ GimpVector3 vector2);
+GimpVector3 gimp_vector3_cross_product (const GimpVector3 *vector1,
+ const GimpVector3 *vector2);
+GimpVector3 gimp_vector3_cross_product_val (GimpVector3 vector1,
+ GimpVector3 vector2);
+void gimp_vector3_rotate (GimpVector3 *vector,
+ gdouble alpha,
+ gdouble beta,
+ gdouble gamma);
+GimpVector3 gimp_vector3_rotate_val (GimpVector3 vector,
+ gdouble alpha,
+ gdouble beta,
+ gdouble gamma);
+
+/* 2d <-> 3d Vector projection functions */
+/* ===================================== */
+
+void gimp_vector_2d_to_3d (gint sx,
+ gint sy,
+ gint w,
+ gint h,
+ gint x,
+ gint y,
+ const GimpVector3 *vp,
+ GimpVector3 *p);
+
+GimpVector3 gimp_vector_2d_to_3d_val (gint sx,
+ gint sy,
+ gint w,
+ gint h,
+ gint x,
+ gint y,
+ GimpVector3 vp,
+ GimpVector3 p);
+
+void gimp_vector_3d_to_2d (gint sx,
+ gint sy,
+ gint w,
+ gint h,
+ gdouble *x,
+ gdouble *y,
+ const GimpVector3 *vp,
+ const GimpVector3 *p);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_VECTOR_H__ */
diff --git a/libgimpmodule/Makefile.am b/libgimpmodule/Makefile.am
new file mode 100644
index 0000000..4a3bd5e
--- /dev/null
+++ b/libgimpmodule/Makefile.am
@@ -0,0 +1,87 @@
+## Process this file with automake to produce Makefile.in
+
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+
+if PLATFORM_WIN32
+no_undefined = -no-undefined
+endif
+
+if OS_WIN32
+gimpmodule_def = gimpmodule.def
+libgimpmodule_export_symbols = -export-symbols $(srcdir)/gimpmodule.def
+
+install-libtool-import-lib:
+ $(INSTALL) .libs/libgimpmodule-$(GIMP_API_VERSION).dll.a $(DESTDIR)$(libdir)
+ $(INSTALL) $(srcdir)/gimpmodule.def $(DESTDIR)$(libdir)
+
+uninstall-libtool-import-lib:
+ -rm $(DESTDIR)$(libdir)/libgimpmodule-$(GIMP_API_VERSION).dll.a
+ -rm $(DESTDIR)$(libdir)/gimpmodule.def
+else
+install-libtool-import-lib:
+uninstall-libtool-import-lib:
+endif
+
+if MS_LIB_AVAILABLE
+noinst_DATA = gimpmodule-$(GIMP_API_VERSION).lib
+
+install-ms-lib:
+ $(INSTALL) gimpmodule-$(GIMP_API_VERSION).lib $(DESTDIR)$(libdir)
+
+uninstall-ms-lib:
+ -rm $(DESTDIR)$(libdir)/gimpmodule-$(GIMP_API_VERSION).lib
+
+gimpmodule-@GIMP_API_VERSION@.lib: gimpmodule.def
+ lib -name:libgimpmodule-$(GIMP_API_VERSION)-@LT_CURRENT_MINUS_AGE@.dll -def:gimpmodule.def -out:$@
+
+else
+install-ms-lib:
+uninstall-ms-lib:
+endif
+
+libgimpmoduleincludedir = $(includedir)/gimp-$(GIMP_API_VERSION)/libgimpmodule
+
+AM_CPPFLAGS = \
+ -DG_LOG_DOMAIN=\"LibGimpModule\" \
+ -DGIMP_MODULE_COMPILATION \
+ -I$(top_srcdir) \
+ $(GIO_CFLAGS) \
+ $(GLIB_CFLAGS) \
+ $(GMODULE_NO_EXPORT_CFLAGS) \
+ -I$(includedir)
+
+EXTRA_DIST = \
+ gimpmodule.def
+
+lib_LTLIBRARIES = libgimpmodule-@GIMP_API_VERSION@.la
+
+libgimpmodule_@GIMP_API_VERSION@_la_SOURCES = \
+ gimpmoduletypes.h \
+ gimpmodule.c \
+ gimpmodule.h \
+ gimpmoduledb.c \
+ gimpmoduledb.h
+
+libgimpmoduleinclude_HEADERS = \
+ gimpmoduletypes.h \
+ gimpmodule.h \
+ gimpmoduledb.h
+
+libgimpmodule_@GIMP_API_VERSION@_la_LDFLAGS = \
+ -version-info $(LT_VERSION_INFO) \
+ $(no_undefined) \
+ $(libgimpmodule_export_symbols)
+
+EXTRA_libgimpmodule_@GIMP_API_VERSION@_la_DEPENDENCIES = $(gimpmodule_def)
+
+libgimpmodule_@GIMP_API_VERSION@_la_LIBADD = \
+ $(libgimpbase) \
+ $(libgimpconfig) \
+ $(GMODULE_NO_EXPORT_LIBS) \
+ $(GIO_LIBS) \
+ $(GLIB_LIBS)
+
+install-data-local: install-ms-lib install-libtool-import-lib
+
+uninstall-local: uninstall-ms-lib uninstall-libtool-import-lib
diff --git a/libgimpmodule/Makefile.in b/libgimpmodule/Makefile.in
new file mode 100644
index 0000000..6ac2f13
--- /dev/null
+++ b/libgimpmodule/Makefile.in
@@ -0,0 +1,1070 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = libgimpmodule
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(libgimpmoduleinclude_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(libdir)" \
+ "$(DESTDIR)$(libgimpmoduleincludedir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libgimpmodule_@GIMP_API_VERSION@_la_DEPENDENCIES = $(libgimpbase) \
+ $(libgimpconfig) $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+am_libgimpmodule_@GIMP_API_VERSION@_la_OBJECTS = gimpmodule.lo \
+ gimpmoduledb.lo
+libgimpmodule_@GIMP_API_VERSION@_la_OBJECTS = \
+ $(am_libgimpmodule_@GIMP_API_VERSION@_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libgimpmodule_@GIMP_API_VERSION@_la_LINK = $(LIBTOOL) $(AM_V_lt) \
+ --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libgimpmodule_@GIMP_API_VERSION@_la_LDFLAGS) $(LDFLAGS) -o \
+ $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/gimpmodule.Plo \
+ ./$(DEPDIR)/gimpmoduledb.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libgimpmodule_@GIMP_API_VERSION@_la_SOURCES)
+DIST_SOURCES = $(libgimpmodule_@GIMP_API_VERSION@_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+DATA = $(noinst_DATA)
+HEADERS = $(libgimpmoduleinclude_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+@PLATFORM_WIN32_TRUE@no_undefined = -no-undefined
+@OS_WIN32_TRUE@gimpmodule_def = gimpmodule.def
+@OS_WIN32_TRUE@libgimpmodule_export_symbols = -export-symbols $(srcdir)/gimpmodule.def
+@MS_LIB_AVAILABLE_TRUE@noinst_DATA = gimpmodule-$(GIMP_API_VERSION).lib
+libgimpmoduleincludedir = $(includedir)/gimp-$(GIMP_API_VERSION)/libgimpmodule
+AM_CPPFLAGS = \
+ -DG_LOG_DOMAIN=\"LibGimpModule\" \
+ -DGIMP_MODULE_COMPILATION \
+ -I$(top_srcdir) \
+ $(GIO_CFLAGS) \
+ $(GLIB_CFLAGS) \
+ $(GMODULE_NO_EXPORT_CFLAGS) \
+ -I$(includedir)
+
+EXTRA_DIST = \
+ gimpmodule.def
+
+lib_LTLIBRARIES = libgimpmodule-@GIMP_API_VERSION@.la
+libgimpmodule_@GIMP_API_VERSION@_la_SOURCES = \
+ gimpmoduletypes.h \
+ gimpmodule.c \
+ gimpmodule.h \
+ gimpmoduledb.c \
+ gimpmoduledb.h
+
+libgimpmoduleinclude_HEADERS = \
+ gimpmoduletypes.h \
+ gimpmodule.h \
+ gimpmoduledb.h
+
+libgimpmodule_@GIMP_API_VERSION@_la_LDFLAGS = \
+ -version-info $(LT_VERSION_INFO) \
+ $(no_undefined) \
+ $(libgimpmodule_export_symbols)
+
+EXTRA_libgimpmodule_@GIMP_API_VERSION@_la_DEPENDENCIES = $(gimpmodule_def)
+libgimpmodule_@GIMP_API_VERSION@_la_LIBADD = \
+ $(libgimpbase) \
+ $(libgimpconfig) \
+ $(GMODULE_NO_EXPORT_LIBS) \
+ $(GIO_LIBS) \
+ $(GLIB_LIBS)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu libgimpmodule/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu libgimpmodule/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libgimpmodule-@GIMP_API_VERSION@.la: $(libgimpmodule_@GIMP_API_VERSION@_la_OBJECTS) $(libgimpmodule_@GIMP_API_VERSION@_la_DEPENDENCIES) $(EXTRA_libgimpmodule_@GIMP_API_VERSION@_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libgimpmodule_@GIMP_API_VERSION@_la_LINK) -rpath $(libdir) $(libgimpmodule_@GIMP_API_VERSION@_la_OBJECTS) $(libgimpmodule_@GIMP_API_VERSION@_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpmodule.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpmoduledb.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-libgimpmoduleincludeHEADERS: $(libgimpmoduleinclude_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(libgimpmoduleinclude_HEADERS)'; test -n "$(libgimpmoduleincludedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libgimpmoduleincludedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libgimpmoduleincludedir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(libgimpmoduleincludedir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(libgimpmoduleincludedir)" || exit $$?; \
+ done
+
+uninstall-libgimpmoduleincludeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libgimpmoduleinclude_HEADERS)'; test -n "$(libgimpmoduleincludedir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(libgimpmoduleincludedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(DATA) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libgimpmoduleincludedir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/gimpmodule.Plo
+ -rm -f ./$(DEPDIR)/gimpmoduledb.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-data-local \
+ install-libgimpmoduleincludeHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/gimpmodule.Plo
+ -rm -f ./$(DEPDIR)/gimpmoduledb.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libLTLIBRARIES \
+ uninstall-libgimpmoduleincludeHEADERS uninstall-local
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libLTLIBRARIES clean-libtool cscopelist-am \
+ ctags ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-data-local install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libLTLIBRARIES \
+ install-libgimpmoduleincludeHEADERS install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am uninstall-libLTLIBRARIES \
+ uninstall-libgimpmoduleincludeHEADERS uninstall-local
+
+.PRECIOUS: Makefile
+
+
+@OS_WIN32_TRUE@install-libtool-import-lib:
+@OS_WIN32_TRUE@ $(INSTALL) .libs/libgimpmodule-$(GIMP_API_VERSION).dll.a $(DESTDIR)$(libdir)
+@OS_WIN32_TRUE@ $(INSTALL) $(srcdir)/gimpmodule.def $(DESTDIR)$(libdir)
+
+@OS_WIN32_TRUE@uninstall-libtool-import-lib:
+@OS_WIN32_TRUE@ -rm $(DESTDIR)$(libdir)/libgimpmodule-$(GIMP_API_VERSION).dll.a
+@OS_WIN32_TRUE@ -rm $(DESTDIR)$(libdir)/gimpmodule.def
+@OS_WIN32_FALSE@install-libtool-import-lib:
+@OS_WIN32_FALSE@uninstall-libtool-import-lib:
+
+@MS_LIB_AVAILABLE_TRUE@install-ms-lib:
+@MS_LIB_AVAILABLE_TRUE@ $(INSTALL) gimpmodule-$(GIMP_API_VERSION).lib $(DESTDIR)$(libdir)
+
+@MS_LIB_AVAILABLE_TRUE@uninstall-ms-lib:
+@MS_LIB_AVAILABLE_TRUE@ -rm $(DESTDIR)$(libdir)/gimpmodule-$(GIMP_API_VERSION).lib
+
+@MS_LIB_AVAILABLE_TRUE@gimpmodule-@GIMP_API_VERSION@.lib: gimpmodule.def
+@MS_LIB_AVAILABLE_TRUE@ lib -name:libgimpmodule-$(GIMP_API_VERSION)-@LT_CURRENT_MINUS_AGE@.dll -def:gimpmodule.def -out:$@
+
+@MS_LIB_AVAILABLE_FALSE@install-ms-lib:
+@MS_LIB_AVAILABLE_FALSE@uninstall-ms-lib:
+
+install-data-local: install-ms-lib install-libtool-import-lib
+
+uninstall-local: uninstall-ms-lib uninstall-libtool-import-lib
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libgimpmodule/gimpmodule.c b/libgimpmodule/gimpmodule.c
new file mode 100644
index 0000000..83a5e90
--- /dev/null
+++ b/libgimpmodule/gimpmodule.c
@@ -0,0 +1,536 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpmodule.c
+ * (C) 1999 Austin Donnelly <austin@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gio/gio.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "gimpmodule.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpmodule
+ * @title: GimpModule
+ * @short_description: A #GTypeModule subclass which implements module
+ * loading using #GModule.
+ * @see_also: #GModule, #GTypeModule
+ *
+ * A #GTypeModule subclass which implements module loading using #GModule.
+ **/
+
+
+enum
+{
+ MODIFIED,
+ LAST_SIGNAL
+};
+
+
+static void gimp_module_finalize (GObject *object);
+
+static gboolean gimp_module_load (GTypeModule *module);
+static void gimp_module_unload (GTypeModule *module);
+
+static gboolean gimp_module_open (GimpModule *module);
+static gboolean gimp_module_close (GimpModule *module);
+static void gimp_module_set_last_error (GimpModule *module,
+ const gchar *error_str);
+
+
+G_DEFINE_TYPE (GimpModule, gimp_module, G_TYPE_TYPE_MODULE)
+
+#define parent_class gimp_module_parent_class
+
+static guint module_signals[LAST_SIGNAL];
+
+
+static void
+gimp_module_class_init (GimpModuleClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (klass);
+
+ module_signals[MODIFIED] =
+ g_signal_new ("modified",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpModuleClass, modified),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ object_class->finalize = gimp_module_finalize;
+
+ module_class->load = gimp_module_load;
+ module_class->unload = gimp_module_unload;
+
+ klass->modified = NULL;
+}
+
+static void
+gimp_module_init (GimpModule *module)
+{
+ module->filename = NULL;
+ module->verbose = FALSE;
+ module->state = GIMP_MODULE_STATE_ERROR;
+ module->on_disk = FALSE;
+ module->load_inhibit = FALSE;
+
+ module->module = NULL;
+ module->info = NULL;
+ module->last_module_error = NULL;
+
+ module->query_module = NULL;
+ module->register_module = NULL;
+}
+
+static void
+gimp_module_finalize (GObject *object)
+{
+ GimpModule *module = GIMP_MODULE (object);
+
+ g_clear_pointer (&module->info, gimp_module_info_free);
+ g_clear_pointer (&module->last_module_error, g_free);
+ g_clear_pointer (&module->filename, g_free);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gboolean
+gimp_module_load (GTypeModule *module)
+{
+ GimpModule *gimp_module = GIMP_MODULE (module);
+ gpointer func;
+
+ g_return_val_if_fail (gimp_module->filename != NULL, FALSE);
+ g_return_val_if_fail (gimp_module->module == NULL, FALSE);
+
+ if (gimp_module->verbose)
+ g_print ("Loading module '%s'\n",
+ gimp_filename_to_utf8 (gimp_module->filename));
+
+ if (! gimp_module_open (gimp_module))
+ return FALSE;
+
+ if (! gimp_module_query_module (gimp_module))
+ return FALSE;
+
+ /* find the gimp_module_register symbol */
+ if (! g_module_symbol (gimp_module->module, "gimp_module_register", &func))
+ {
+ gimp_module_set_last_error (gimp_module,
+ "Missing gimp_module_register() symbol");
+
+ g_message (_("Module '%s' load error: %s"),
+ gimp_filename_to_utf8 (gimp_module->filename),
+ gimp_module->last_module_error);
+
+ gimp_module_close (gimp_module);
+
+ gimp_module->state = GIMP_MODULE_STATE_ERROR;
+
+ return FALSE;
+ }
+
+ gimp_module->register_module = func;
+
+ if (! gimp_module->register_module (module))
+ {
+ gimp_module_set_last_error (gimp_module,
+ "gimp_module_register() returned FALSE");
+
+ g_message (_("Module '%s' load error: %s"),
+ gimp_filename_to_utf8 (gimp_module->filename),
+ gimp_module->last_module_error);
+
+ gimp_module_close (gimp_module);
+
+ gimp_module->state = GIMP_MODULE_STATE_LOAD_FAILED;
+
+ return FALSE;
+ }
+
+ gimp_module->state = GIMP_MODULE_STATE_LOADED;
+
+ return TRUE;
+}
+
+static void
+gimp_module_unload (GTypeModule *module)
+{
+ GimpModule *gimp_module = GIMP_MODULE (module);
+
+ g_return_if_fail (gimp_module->module != NULL);
+
+ if (gimp_module->verbose)
+ g_print ("Unloading module '%s'\n",
+ gimp_filename_to_utf8 (gimp_module->filename));
+
+ gimp_module_close (gimp_module);
+}
+
+
+/* public functions */
+
+/**
+ * gimp_module_new:
+ * @filename: The filename of a loadable module.
+ * @load_inhibit: Pass %TRUE to exclude this module from auto-loading.
+ * @verbose: Pass %TRUE to enable debugging output.
+ *
+ * Creates a new #GimpModule instance.
+ *
+ * Return value: The new #GimpModule object.
+ **/
+GimpModule *
+gimp_module_new (const gchar *filename,
+ gboolean load_inhibit,
+ gboolean verbose)
+{
+ GimpModule *module;
+
+ g_return_val_if_fail (filename != NULL, NULL);
+
+ module = g_object_new (GIMP_TYPE_MODULE, NULL);
+
+ module->filename = g_strdup (filename);
+ module->load_inhibit = load_inhibit ? TRUE : FALSE;
+ module->verbose = verbose ? TRUE : FALSE;
+ module->on_disk = TRUE;
+
+ if (! module->load_inhibit)
+ {
+ if (gimp_module_load (G_TYPE_MODULE (module)))
+ gimp_module_unload (G_TYPE_MODULE (module));
+ }
+ else
+ {
+ if (verbose)
+ g_print ("Skipping module '%s'\n",
+ gimp_filename_to_utf8 (filename));
+
+ module->state = GIMP_MODULE_STATE_NOT_LOADED;
+ }
+
+ return module;
+}
+
+/**
+ * gimp_module_query_module:
+ * @module: A #GimpModule.
+ *
+ * Queries the module without actually registering any of the types it
+ * may implement. After successful query, the @info field of the
+ * #GimpModule struct will be available for further inspection.
+ *
+ * Return value: %TRUE on success.
+ **/
+gboolean
+gimp_module_query_module (GimpModule *module)
+{
+ const GimpModuleInfo *info;
+ gboolean close_module = FALSE;
+ gpointer func;
+
+ g_return_val_if_fail (GIMP_IS_MODULE (module), FALSE);
+
+ if (! module->module)
+ {
+ if (! gimp_module_open (module))
+ return FALSE;
+
+ close_module = TRUE;
+ }
+
+ /* find the gimp_module_query symbol */
+ if (! g_module_symbol (module->module, "gimp_module_query", &func))
+ {
+ gimp_module_set_last_error (module,
+ "Missing gimp_module_query() symbol");
+
+ g_message (_("Module '%s' load error: %s"),
+ gimp_filename_to_utf8 (module->filename),
+ module->last_module_error);
+
+ gimp_module_close (module);
+
+ module->state = GIMP_MODULE_STATE_ERROR;
+ return FALSE;
+ }
+
+ module->query_module = func;
+
+ if (module->info)
+ {
+ gimp_module_info_free (module->info);
+ module->info = NULL;
+ }
+
+ info = module->query_module (G_TYPE_MODULE (module));
+
+ if (! info || info->abi_version != GIMP_MODULE_ABI_VERSION)
+ {
+ gimp_module_set_last_error (module,
+ info ?
+ "module ABI version does not match" :
+ "gimp_module_query() returned NULL");
+
+ g_message (_("Module '%s' load error: %s"),
+ gimp_filename_to_utf8 (module->filename),
+ module->last_module_error);
+
+ gimp_module_close (module);
+
+ module->state = GIMP_MODULE_STATE_ERROR;
+ return FALSE;
+ }
+
+ module->info = gimp_module_info_copy (info);
+
+ if (close_module)
+ return gimp_module_close (module);
+
+ return TRUE;
+}
+
+/**
+ * gimp_module_modified:
+ * @module: A #GimpModule.
+ *
+ * Emits the "modified" signal. Call it whenever you have modified the module
+ * manually (which you shouldn't do).
+ **/
+void
+gimp_module_modified (GimpModule *module)
+{
+ g_return_if_fail (GIMP_IS_MODULE (module));
+
+ g_signal_emit (module, module_signals[MODIFIED], 0);
+}
+
+/**
+ * gimp_module_set_load_inhibit:
+ * @module: A #GimpModule.
+ * @load_inhibit: Pass %TRUE to exclude this module from auto-loading.
+ *
+ * Sets the @load_inhibit property if the module. Emits "modified".
+ **/
+void
+gimp_module_set_load_inhibit (GimpModule *module,
+ gboolean load_inhibit)
+{
+ g_return_if_fail (GIMP_IS_MODULE (module));
+
+ if (load_inhibit != module->load_inhibit)
+ {
+ module->load_inhibit = load_inhibit ? TRUE : FALSE;
+
+ gimp_module_modified (module);
+ }
+}
+
+/**
+ * gimp_module_state_name:
+ * @state: A #GimpModuleState.
+ *
+ * Returns the translated textual representation of a #GimpModuleState.
+ * The returned string must not be freed.
+ *
+ * Return value: The @state's name.
+ **/
+const gchar *
+gimp_module_state_name (GimpModuleState state)
+{
+ static const gchar * const statenames[] =
+ {
+ N_("Module error"),
+ N_("Loaded"),
+ N_("Load failed"),
+ N_("Not loaded")
+ };
+
+ g_return_val_if_fail (state >= GIMP_MODULE_STATE_ERROR &&
+ state <= GIMP_MODULE_STATE_NOT_LOADED, NULL);
+
+ return gettext (statenames[state]);
+}
+
+/**
+ * gimp_module_register_enum:
+ * @module: a module
+ * @name: the name of the new enum type
+ * @const_static_values: the enum values
+ *
+ * This function is deprecated! Use g_type_module_register_enum() instead.
+ *
+ * Return value: a new enum #GType
+ **/
+GType
+gimp_module_register_enum (GTypeModule *module,
+ const gchar *name,
+ const GEnumValue *const_static_values)
+{
+ return g_type_module_register_enum (module, name, const_static_values);
+}
+
+/**
+ * gimp_module_error_quark:
+ *
+ * This function is never called directly. Use GIMP_MODULE_ERROR() instead.
+ *
+ * Return value: the #GQuark that defines the GIMP module error domain.
+ *
+ * Since: 2.8
+ **/
+GQuark
+gimp_module_error_quark (void)
+{
+ return g_quark_from_static_string ("gimp-module-error-quark");
+}
+
+
+/* private functions */
+
+static gboolean
+gimp_module_open (GimpModule *module)
+{
+ module->module = g_module_open (module->filename, 0);
+
+ if (! module->module)
+ {
+ module->state = GIMP_MODULE_STATE_ERROR;
+ gimp_module_set_last_error (module, g_module_error ());
+
+ g_message (_("Module '%s' load error: %s"),
+ gimp_filename_to_utf8 (module->filename),
+ module->last_module_error);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gimp_module_close (GimpModule *module)
+{
+ g_module_close (module->module); /* FIXME: error handling */
+ module->module = NULL;
+ module->query_module = NULL;
+ module->register_module = NULL;
+
+ module->state = GIMP_MODULE_STATE_NOT_LOADED;
+
+ return TRUE;
+}
+
+static void
+gimp_module_set_last_error (GimpModule *module,
+ const gchar *error_str)
+{
+ if (module->last_module_error)
+ g_free (module->last_module_error);
+
+ module->last_module_error = g_strdup (error_str);
+}
+
+
+/* GimpModuleInfo functions */
+
+/**
+ * gimp_module_info_new:
+ * @abi_version: The #GIMP_MODULE_ABI_VERSION the module was compiled against.
+ * @purpose: The module's general purpose.
+ * @author: The module's author.
+ * @version: The module's version.
+ * @copyright: The module's copyright.
+ * @date: The module's release date.
+ *
+ * Creates a newly allocated #GimpModuleInfo struct.
+ *
+ * Return value: The new #GimpModuleInfo struct.
+ **/
+GimpModuleInfo *
+gimp_module_info_new (guint32 abi_version,
+ const gchar *purpose,
+ const gchar *author,
+ const gchar *version,
+ const gchar *copyright,
+ const gchar *date)
+{
+ GimpModuleInfo *info = g_slice_new0 (GimpModuleInfo);
+
+ info->abi_version = abi_version;
+ info->purpose = g_strdup (purpose);
+ info->author = g_strdup (author);
+ info->version = g_strdup (version);
+ info->copyright = g_strdup (copyright);
+ info->date = g_strdup (date);
+
+ return info;
+}
+
+/**
+ * gimp_module_info_copy:
+ * @info: The #GimpModuleInfo struct to copy.
+ *
+ * Copies a #GimpModuleInfo struct.
+ *
+ * Return value: The new copy.
+ **/
+GimpModuleInfo *
+gimp_module_info_copy (const GimpModuleInfo *info)
+{
+ g_return_val_if_fail (info != NULL, NULL);
+
+ return gimp_module_info_new (info->abi_version,
+ info->purpose,
+ info->author,
+ info->version,
+ info->copyright,
+ info->date);
+}
+
+/**
+ * gimp_module_info_free:
+ * @info: The #GimpModuleInfo struct to free
+ *
+ * Frees the passed #GimpModuleInfo.
+ **/
+void
+gimp_module_info_free (GimpModuleInfo *info)
+{
+ g_return_if_fail (info != NULL);
+
+ g_free (info->purpose);
+ g_free (info->author);
+ g_free (info->version);
+ g_free (info->copyright);
+ g_free (info->date);
+
+ g_slice_free (GimpModuleInfo, info);
+}
diff --git a/libgimpmodule/gimpmodule.def b/libgimpmodule/gimpmodule.def
new file mode 100644
index 0000000..7b69018
--- /dev/null
+++ b/libgimpmodule/gimpmodule.def
@@ -0,0 +1,18 @@
+EXPORTS
+ gimp_module_db_get_load_inhibit
+ gimp_module_db_get_type
+ gimp_module_db_load
+ gimp_module_db_new
+ gimp_module_db_refresh
+ gimp_module_db_set_load_inhibit
+ gimp_module_error_quark
+ gimp_module_get_type
+ gimp_module_info_copy
+ gimp_module_info_free
+ gimp_module_info_new
+ gimp_module_modified
+ gimp_module_new
+ gimp_module_query_module
+ gimp_module_register_enum
+ gimp_module_set_load_inhibit
+ gimp_module_state_name
diff --git a/libgimpmodule/gimpmodule.h b/libgimpmodule/gimpmodule.h
new file mode 100644
index 0000000..d19ba1f
--- /dev/null
+++ b/libgimpmodule/gimpmodule.h
@@ -0,0 +1,250 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpmodule.h
+ * (C) 1999 Austin Donnelly <austin@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_MODULE_H__
+#define __GIMP_MODULE_H__
+
+#include <gmodule.h>
+
+#define __GIMP_MODULE_H_INSIDE__
+
+#include <libgimpmodule/gimpmoduletypes.h>
+
+#include <libgimpmodule/gimpmoduledb.h>
+
+#undef __GIMP_MODULE_H_INSIDE__
+
+G_BEGIN_DECLS
+
+
+/**
+ * GIMP_MODULE_ABI_VERSION:
+ *
+ * The version of the module system's ABI. Modules put this value into
+ * #GimpModuleInfo's @abi_version field so the code loading the modules
+ * can check if it was compiled against the same module ABI the modules
+ * are compiled against.
+ *
+ * GIMP_MODULE_ABI_VERSION is incremented each time one of the
+ * following changes:
+ *
+ * - the libgimpmodule implementation (if the change affects modules).
+ *
+ * - one of the classes implemented by modules (currently #GimpColorDisplay,
+ * #GimpColorSelector and #GimpController).
+ **/
+#define GIMP_MODULE_ABI_VERSION 0x0004
+
+
+/**
+ * GimpModuleState:
+ * @GIMP_MODULE_STATE_ERROR: Missing gimp_module_register() function
+ * or other error.
+ * @GIMP_MODULE_STATE_LOADED: An instance of a type implemented by
+ * this module is allocated.
+ * @GIMP_MODULE_STATE_LOAD_FAILED: gimp_module_register() returned %FALSE.
+ * @GIMP_MODULE_STATE_NOT_LOADED: There are no instances allocated of
+ * types implemented by this module.
+ *
+ * The possible states a #GimpModule can be in.
+ **/
+typedef enum
+{
+ GIMP_MODULE_STATE_ERROR,
+ GIMP_MODULE_STATE_LOADED,
+ GIMP_MODULE_STATE_LOAD_FAILED,
+ GIMP_MODULE_STATE_NOT_LOADED
+} GimpModuleState;
+
+
+#define GIMP_MODULE_ERROR (gimp_module_error_quark ())
+
+GQuark gimp_module_error_quark (void) G_GNUC_CONST;
+
+/**
+ * GimpModuleError:
+ * @GIMP_MODULE_FAILED: Generic error condition
+ *
+ * Types of errors returned by modules
+ **/
+typedef enum
+{
+ GIMP_MODULE_FAILED
+} GimpModuleError;
+
+
+/**
+ * GimpModuleInfo:
+ * @abi_version: The #GIMP_MODULE_ABI_VERSION the module was compiled against.
+ * @purpose: The module's general purpose.
+ * @author: The module's author.
+ * @version: The module's version.
+ * @copyright: The module's copyright.
+ * @date: The module's release date.
+ *
+ * This structure contains information about a loadable module.
+ **/
+struct _GimpModuleInfo
+{
+ guint32 abi_version;
+ gchar *purpose;
+ gchar *author;
+ gchar *version;
+ gchar *copyright;
+ gchar *date;
+};
+
+
+/**
+ * GimpModuleQueryFunc:
+ * @module: The #GimpModule responsible for this loadable module.
+ * @Returns: The #GimpModuleInfo struct describing the module.
+ *
+ * The signature of the query function a loadable GIMP module must
+ * implement. In the module, the function must be called
+ * gimp_module_query().
+ *
+ * #GimpModule will copy the returned #GimpModuleInfo struct, so the
+ * module doesn't need to keep these values around (however in most
+ * cases the module will just return a pointer to a constant
+ * structure).
+ **/
+typedef const GimpModuleInfo * (* GimpModuleQueryFunc) (GTypeModule *module);
+
+/**
+ * GimpModuleRegisterFunc:
+ * @module: The #GimpModule responsible for this loadable module.
+ * @Returns: %TRUE on success, %FALSE otherwise.
+ *
+ * The signature of the register function a loadable GIMP module must
+ * implement. In the module, the function must be called
+ * gimp_module_register().
+ *
+ * When this function is called, the module should register all the types
+ * it implements with the passed @module.
+ **/
+typedef gboolean (* GimpModuleRegisterFunc) (GTypeModule *module);
+
+
+/* GimpModules have to implement these */
+G_MODULE_EXPORT const GimpModuleInfo * gimp_module_query (GTypeModule *module);
+G_MODULE_EXPORT gboolean gimp_module_register (GTypeModule *module);
+
+
+#define GIMP_TYPE_MODULE (gimp_module_get_type ())
+#define GIMP_MODULE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_MODULE, GimpModule))
+#define GIMP_MODULE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_MODULE, GimpModuleClass))
+#define GIMP_IS_MODULE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_MODULE))
+#define GIMP_IS_MODULE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_MODULE))
+#define GIMP_MODULE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_MODULE, GimpModuleClass))
+
+
+typedef struct _GimpModuleClass GimpModuleClass;
+
+/**
+ * GimpModule:
+ * @filename:
+ * @verbose:
+ * @state:
+ * @on_disk:
+ * @load_inhibit:
+ * @info:
+ * @last_module_error:
+ *
+ * #GimpModule is a generic mechanism to dynamically load modules into
+ * GIMP. It is a #GTypeModule subclass, implementing module loading
+ * using #GModule. #GimpModule does not know which functionality is
+ * implemented by the modules, it just provides a framework to get
+ * arbitrary #GType implementations loaded from disk.
+ **/
+struct _GimpModule
+{
+ GTypeModule parent_instance;
+
+ /*< public >*/
+ gchar *filename; /* path to the module */
+ gboolean verbose; /* verbose error reporting */
+ GimpModuleState state; /* what's happened to the module */
+ gboolean on_disk; /* TRUE if file still exists */
+ gboolean load_inhibit; /* user requests not to load at boot time */
+
+ /* stuff from now on may be NULL depending on the state the module is in */
+ /*< private >*/
+ GModule *module; /* handle on the module */
+
+ /*< public >*/
+ GimpModuleInfo *info; /* returned values from module_query */
+ gchar *last_module_error;
+
+ /*< private >*/
+ GimpModuleQueryFunc query_module;
+ GimpModuleRegisterFunc register_module;
+};
+
+struct _GimpModuleClass
+{
+ GTypeModuleClass parent_class;
+
+ void (* modified) (GimpModule *module);
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_module_get_type (void) G_GNUC_CONST;
+
+GimpModule * gimp_module_new (const gchar *filename,
+ gboolean load_inhibit,
+ gboolean verbose);
+
+gboolean gimp_module_query_module (GimpModule *module);
+
+void gimp_module_modified (GimpModule *module);
+void gimp_module_set_load_inhibit (GimpModule *module,
+ gboolean load_inhibit);
+
+const gchar * gimp_module_state_name (GimpModuleState state);
+
+GIMP_DEPRECATED_FOR(g_type_module_register_enum)
+GType gimp_module_register_enum (GTypeModule *module,
+ const gchar *name,
+ const GEnumValue *const_static_values);
+
+
+/* GimpModuleInfo functions */
+
+GimpModuleInfo * gimp_module_info_new (guint32 abi_version,
+ const gchar *purpose,
+ const gchar *author,
+ const gchar *version,
+ const gchar *copyright,
+ const gchar *date);
+GimpModuleInfo * gimp_module_info_copy (const GimpModuleInfo *info);
+void gimp_module_info_free (GimpModuleInfo *info);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_MODULE_H__ */
diff --git a/libgimpmodule/gimpmoduledb.c b/libgimpmodule/gimpmoduledb.c
new file mode 100644
index 0000000..4bc1c73
--- /dev/null
+++ b/libgimpmodule/gimpmoduledb.c
@@ -0,0 +1,508 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gio/gio.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "gimpmoduletypes.h"
+
+#include "gimpmodule.h"
+#include "gimpmoduledb.h"
+
+
+/**
+ * SECTION: gimpmoduledb
+ * @title: GimpModuleDB
+ * @short_description: Keeps a list of #GimpModule's found in a given
+ * searchpath.
+ *
+ * Keeps a list of #GimpModule's found in a given searchpath.
+ **/
+
+
+enum
+{
+ ADD,
+ REMOVE,
+ MODULE_MODIFIED,
+ LAST_SIGNAL
+};
+
+
+#define DUMP_DB FALSE
+
+
+static void gimp_module_db_finalize (GObject *object);
+
+static void gimp_module_db_load_directory (GimpModuleDB *db,
+ GFile *directory);
+static void gimp_module_db_load_module (GimpModuleDB *db,
+ GFile *file);
+
+static GimpModule * gimp_module_db_module_find_by_path (GimpModuleDB *db,
+ const char *fullpath);
+
+static void gimp_module_db_module_dump_func (gpointer data,
+ gpointer user_data);
+static void gimp_module_db_module_on_disk_func (gpointer data,
+ gpointer user_data);
+static void gimp_module_db_module_remove_func (gpointer data,
+ gpointer user_data);
+
+static void gimp_module_db_module_modified (GimpModule *module,
+ GimpModuleDB *db);
+
+
+G_DEFINE_TYPE (GimpModuleDB, gimp_module_db, G_TYPE_OBJECT)
+
+#define parent_class gimp_module_db_parent_class
+
+static guint db_signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+gimp_module_db_class_init (GimpModuleDBClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ db_signals[ADD] =
+ g_signal_new ("add",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpModuleDBClass, add),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ GIMP_TYPE_MODULE);
+
+ db_signals[REMOVE] =
+ g_signal_new ("remove",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpModuleDBClass, remove),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ GIMP_TYPE_MODULE);
+
+ db_signals[MODULE_MODIFIED] =
+ g_signal_new ("module-modified",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpModuleDBClass, module_modified),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ GIMP_TYPE_MODULE);
+
+ object_class->finalize = gimp_module_db_finalize;
+
+ klass->add = NULL;
+ klass->remove = NULL;
+}
+
+static void
+gimp_module_db_init (GimpModuleDB *db)
+{
+ db->modules = NULL;
+ db->load_inhibit = NULL;
+ db->verbose = FALSE;
+}
+
+static void
+gimp_module_db_finalize (GObject *object)
+{
+ GimpModuleDB *db = GIMP_MODULE_DB (object);
+
+ if (db->modules)
+ {
+ GList *list;
+
+ for (list = db->modules; list; list = g_list_next (list))
+ {
+ g_signal_handlers_disconnect_by_func (list->data,
+ gimp_module_db_module_modified,
+ db);
+ }
+
+ g_list_free (db->modules);
+ db->modules = NULL;
+ }
+
+ if (db->load_inhibit)
+ {
+ g_free (db->load_inhibit);
+ db->load_inhibit = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+/**
+ * gimp_module_db_new:
+ * @verbose: Pass %TRUE to enable debugging output.
+ *
+ * Creates a new #GimpModuleDB instance. The @verbose parameter will be
+ * passed to the created #GimpModule instances using gimp_module_new().
+ *
+ * Return value: The new #GimpModuleDB instance.
+ **/
+GimpModuleDB *
+gimp_module_db_new (gboolean verbose)
+{
+ GimpModuleDB *db;
+
+ db = g_object_new (GIMP_TYPE_MODULE_DB, NULL);
+
+ db->verbose = verbose ? TRUE : FALSE;
+
+ return db;
+}
+
+static gboolean
+is_in_inhibit_list (const gchar *filename,
+ const gchar *inhibit_list)
+{
+ gchar *p;
+ gint pathlen;
+ const gchar *start;
+ const gchar *end;
+
+ if (! inhibit_list || ! strlen (inhibit_list))
+ return FALSE;
+
+ p = strstr (inhibit_list, filename);
+ if (!p)
+ return FALSE;
+
+ /* we have a substring, but check for colons either side */
+ start = p;
+ while (start != inhibit_list && *start != G_SEARCHPATH_SEPARATOR)
+ start--;
+
+ if (*start == G_SEARCHPATH_SEPARATOR)
+ start++;
+
+ end = strchr (p, G_SEARCHPATH_SEPARATOR);
+ if (! end)
+ end = inhibit_list + strlen (inhibit_list);
+
+ pathlen = strlen (filename);
+
+ if ((end - start) == pathlen)
+ return TRUE;
+
+ return FALSE;
+}
+
+/**
+ * gimp_module_db_set_load_inhibit:
+ * @db: A #GimpModuleDB.
+ * @load_inhibit: A #G_SEARCHPATH_SEPARATOR delimited list of module
+ * filenames to exclude from auto-loading.
+ *
+ * Sets the @load_inhibit flag for all #GimpModule's which are kept
+ * by @db (using gimp_module_set_load_inhibit()).
+ **/
+void
+gimp_module_db_set_load_inhibit (GimpModuleDB *db,
+ const gchar *load_inhibit)
+{
+ GList *list;
+
+ g_return_if_fail (GIMP_IS_MODULE_DB (db));
+
+ if (db->load_inhibit)
+ g_free (db->load_inhibit);
+
+ db->load_inhibit = g_strdup (load_inhibit);
+
+ for (list = db->modules; list; list = g_list_next (list))
+ {
+ GimpModule *module = list->data;
+
+ gimp_module_set_load_inhibit (module,
+ is_in_inhibit_list (module->filename,
+ load_inhibit));
+ }
+}
+
+/**
+ * gimp_module_db_get_load_inhibit:
+ * @db: A #GimpModuleDB.
+ *
+ * Return the #G_SEARCHPATH_SEPARATOR delimited list of module filenames
+ * which are excluded from auto-loading.
+ *
+ * Return value: the @db's @load_inhibit string.
+ **/
+const gchar *
+gimp_module_db_get_load_inhibit (GimpModuleDB *db)
+{
+ g_return_val_if_fail (GIMP_IS_MODULE_DB (db), NULL);
+
+ return db->load_inhibit;
+}
+
+/**
+ * gimp_module_db_load:
+ * @db: A #GimpModuleDB.
+ * @module_path: A #G_SEARCHPATH_SEPARATOR delimited list of directories
+ * to load modules from.
+ *
+ * Scans the directories contained in @module_path and creates a
+ * #GimpModule instance for every loadable module contained in the
+ * directories.
+ **/
+void
+gimp_module_db_load (GimpModuleDB *db,
+ const gchar *module_path)
+{
+ g_return_if_fail (GIMP_IS_MODULE_DB (db));
+ g_return_if_fail (module_path != NULL);
+
+ if (g_module_supported ())
+ {
+ GList *path;
+ GList *list;
+
+ path = gimp_config_path_expand_to_files (module_path, NULL);
+
+ for (list = path; list; list = g_list_next (list))
+ {
+ gimp_module_db_load_directory (db, list->data);
+ }
+
+ g_list_free_full (path, (GDestroyNotify) g_object_unref);
+ }
+
+ if (DUMP_DB)
+ g_list_foreach (db->modules, gimp_module_db_module_dump_func, NULL);
+}
+
+/**
+ * gimp_module_db_refresh:
+ * @db: A #GimpModuleDB.
+ * @module_path: A #G_SEARCHPATH_SEPARATOR delimited list of directories
+ * to load modules from.
+ *
+ * Does the same as gimp_module_db_load(), plus removes all #GimpModule
+ * instances whose modules have been deleted from disk.
+ *
+ * Note that the #GimpModule's will just be removed from the internal
+ * list and not freed as this is not possible with #GTypeModule
+ * instances which actually implement types.
+ **/
+void
+gimp_module_db_refresh (GimpModuleDB *db,
+ const gchar *module_path)
+{
+ GList *kill_list = NULL;
+
+ g_return_if_fail (GIMP_IS_MODULE_DB (db));
+ g_return_if_fail (module_path != NULL);
+
+ /* remove modules we don't have on disk anymore */
+ g_list_foreach (db->modules,
+ gimp_module_db_module_on_disk_func,
+ &kill_list);
+ g_list_foreach (kill_list,
+ gimp_module_db_module_remove_func,
+ db);
+ g_list_free (kill_list);
+
+ /* walk filesystem and add new things we find */
+ gimp_module_db_load (db, module_path);
+}
+
+static void
+gimp_module_db_load_directory (GimpModuleDB *db,
+ 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);
+
+ if (file_type == G_FILE_TYPE_REGULAR &&
+ ! g_file_info_get_is_hidden (info))
+ {
+ GFile *child = g_file_enumerator_get_child (enumerator, info);
+
+ gimp_module_db_load_module (db, child);
+
+ g_object_unref (child);
+ }
+
+ g_object_unref (info);
+ }
+
+ g_object_unref (enumerator);
+ }
+}
+
+static void
+gimp_module_db_load_module (GimpModuleDB *db,
+ GFile *file)
+{
+ GimpModule *module;
+ gchar *path;
+ gboolean load_inhibit;
+
+ if (! gimp_file_has_extension (file, "." G_MODULE_SUFFIX))
+ return;
+
+ path = g_file_get_path (file);
+
+ /* don't load if we already know about it */
+ if (gimp_module_db_module_find_by_path (db, path))
+ {
+ g_free (path);
+ return;
+ }
+
+ load_inhibit = is_in_inhibit_list (path, db->load_inhibit);
+
+ module = gimp_module_new (path,
+ load_inhibit,
+ db->verbose);
+
+ g_free (path);
+
+ g_signal_connect (module, "modified",
+ G_CALLBACK (gimp_module_db_module_modified),
+ db);
+
+ db->modules = g_list_append (db->modules, module);
+
+ g_signal_emit (db, db_signals[ADD], 0, module);
+}
+
+static GimpModule *
+gimp_module_db_module_find_by_path (GimpModuleDB *db,
+ const char *fullpath)
+{
+ GList *list;
+
+ for (list = db->modules; list; list = g_list_next (list))
+ {
+ GimpModule *module = list->data;
+
+ if (! strcmp (module->filename, fullpath))
+ return module;
+ }
+
+ return NULL;
+}
+
+static void
+gimp_module_db_module_dump_func (gpointer data,
+ gpointer user_data)
+{
+ GimpModule *module = data;
+
+ g_print ("\n%s: %s\n",
+ gimp_filename_to_utf8 (module->filename),
+ gimp_module_state_name (module->state));
+
+ g_print (" module: %p lasterr: %s query: %p register: %p\n",
+ module->module,
+ module->last_module_error ? module->last_module_error : "NONE",
+ module->query_module,
+ module->register_module);
+
+ if (module->info)
+ {
+ g_print (" purpose: %s\n"
+ " author: %s\n"
+ " version: %s\n"
+ " copyright: %s\n"
+ " date: %s\n",
+ module->info->purpose ? module->info->purpose : "NONE",
+ module->info->author ? module->info->author : "NONE",
+ module->info->version ? module->info->version : "NONE",
+ module->info->copyright ? module->info->copyright : "NONE",
+ module->info->date ? module->info->date : "NONE");
+ }
+}
+
+static void
+gimp_module_db_module_on_disk_func (gpointer data,
+ gpointer user_data)
+{
+ GimpModule *module = data;
+ GList **kill_list = user_data;
+ gboolean old_on_disk;
+
+ old_on_disk = module->on_disk;
+
+ module->on_disk = g_file_test (module->filename, G_FILE_TEST_IS_REGULAR);
+
+ /* if it's not on the disk, and it isn't in memory, mark it to be
+ * removed later.
+ */
+ if (! module->on_disk && ! module->module)
+ {
+ *kill_list = g_list_append (*kill_list, module);
+ module = NULL;
+ }
+
+ if (module && module->on_disk != old_on_disk)
+ gimp_module_modified (module);
+}
+
+static void
+gimp_module_db_module_remove_func (gpointer data,
+ gpointer user_data)
+{
+ GimpModule *module = data;
+ GimpModuleDB *db = user_data;
+
+ g_signal_handlers_disconnect_by_func (module,
+ gimp_module_db_module_modified,
+ db);
+
+ db->modules = g_list_remove (db->modules, module);
+
+ g_signal_emit (db, db_signals[REMOVE], 0, module);
+}
+
+static void
+gimp_module_db_module_modified (GimpModule *module,
+ GimpModuleDB *db)
+{
+ g_signal_emit (db, db_signals[MODULE_MODIFIED], 0, module);
+}
diff --git a/libgimpmodule/gimpmoduledb.h b/libgimpmodule/gimpmoduledb.h
new file mode 100644
index 0000000..c570cee
--- /dev/null
+++ b/libgimpmodule/gimpmoduledb.h
@@ -0,0 +1,84 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_MODULE_H_INSIDE__) && !defined (GIMP_MODULE_COMPILATION)
+#error "Only <libgimpmodule/gimpmodule.h> can be included directly."
+#endif
+
+#ifndef __GIMP_MODULE_DB_H__
+#define __GIMP_MODULE_DB_H__
+
+G_BEGIN_DECLS
+
+
+#define GIMP_TYPE_MODULE_DB (gimp_module_db_get_type ())
+#define GIMP_MODULE_DB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_MODULE_DB, GimpModuleDB))
+#define GIMP_MODULE_DB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_MODULE_DB, GimpModuleDBClass))
+#define GIMP_IS_MODULE_DB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_MODULE_DB))
+#define GIMP_IS_MODULE_DB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_MODULE_DB))
+#define GIMP_MODULE_DB_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_MODULE_DB, GimpModuleDBClass))
+
+
+typedef struct _GimpModuleDBClass GimpModuleDBClass;
+
+struct _GimpModuleDB
+{
+ GObject parent_instance;
+
+ /*< private >*/
+ GList *modules;
+
+ gchar *load_inhibit;
+ gboolean verbose;
+};
+
+struct _GimpModuleDBClass
+{
+ GObjectClass parent_class;
+
+ void (* add) (GimpModuleDB *db,
+ GimpModule *module);
+ void (* remove) (GimpModuleDB *db,
+ GimpModule *module);
+ void (* module_modified) (GimpModuleDB *db,
+ GimpModule *module);
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_module_db_get_type (void) G_GNUC_CONST;
+GimpModuleDB * gimp_module_db_new (gboolean verbose);
+
+void gimp_module_db_set_load_inhibit (GimpModuleDB *db,
+ const gchar *load_inhibit);
+const gchar * gimp_module_db_get_load_inhibit (GimpModuleDB *db);
+
+void gimp_module_db_load (GimpModuleDB *db,
+ const gchar *module_path);
+void gimp_module_db_refresh (GimpModuleDB *db,
+ const gchar *module_path);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_MODULE_DB_H__ */
diff --git a/libgimpmodule/gimpmoduletypes.h b/libgimpmodule/gimpmoduletypes.h
new file mode 100644
index 0000000..416b3a9
--- /dev/null
+++ b/libgimpmodule/gimpmoduletypes.h
@@ -0,0 +1,47 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_MODULE_TYPES_H__
+#define __GIMP_MODULE_TYPES_H__
+
+
+#include <libgimpbase/gimpbasetypes.h>
+
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+#ifndef GIMP_DISABLE_DEPRECATED
+/*
+ * GIMP_MODULE_PARAM_SERIALIZE is deprecated, use
+ * GIMP_CONFIG_PARAM_SERIALIZE instead.
+ */
+#define GIMP_MODULE_PARAM_SERIALIZE (1 << (0 + G_PARAM_USER_SHIFT))
+#endif
+
+
+typedef struct _GimpModule GimpModule;
+typedef struct _GimpModuleInfo GimpModuleInfo;
+typedef struct _GimpModuleDB GimpModuleDB;
+
+
+G_END_DECLS
+
+#endif /* __GIMP_MODULE_TYPES_H__ */
diff --git a/libgimpthumb/Makefile.am b/libgimpthumb/Makefile.am
new file mode 100644
index 0000000..2655379
--- /dev/null
+++ b/libgimpthumb/Makefile.am
@@ -0,0 +1,137 @@
+## Process this file with automake to produce Makefile.in
+
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+
+if PLATFORM_WIN32
+no_undefined = -no-undefined
+endif
+
+if PLATFORM_OSX
+xobjective_c = "-xobjective-c"
+xobjective_cxx = "-xobjective-c++"
+xnone = "-xnone"
+framework_cocoa = -framework Cocoa
+endif
+
+if OS_WIN32
+gimpthumb_def = gimpthumb.def
+libgimpthumb_export_symbols = -export-symbols $(srcdir)/gimpthumb.def
+
+install-libtool-import-lib:
+ $(INSTALL) .libs/libgimpthumb-$(GIMP_API_VERSION).dll.a $(DESTDIR)$(libdir)
+ $(INSTALL) $(srcdir)/gimpthumb.def $(DESTDIR)$(libdir)
+
+uninstall-libtool-import-lib:
+ -rm $(DESTDIR)$(libdir)/libgimpthumb-$(GIMP_API_VERSION).dll.a
+ -rm $(DESTDIR)$(libdir)/gimpthumb.def
+else
+install-libtool-import-lib:
+uninstall-libtool-import-lib:
+endif
+
+if MS_LIB_AVAILABLE
+noinst_DATA = gimpthumb-$(GIMP_API_VERSION).lib
+
+install-ms-lib:
+ $(INSTALL) gimpthumb-$(GIMP_API_VERSION).lib $(DESTDIR)$(libdir)
+
+uninstall-ms-lib:
+ -rm $(DESTDIR)$(libdir)/gimpthumb-$(GIMP_API_VERSION).lib
+
+gimpthumb-@GIMP_API_VERSION@.lib: gimpthumb.def
+ lib -name:libgimpthumb-$(GIMP_API_VERSION)-@LT_CURRENT_MINUS_AGE@.dll -def:gimpthumb.def -out:$@
+
+else
+install-ms-lib:
+uninstall-ms-lib:
+endif
+
+libgimpthumbincludedir = $(includedir)/gimp-$(GIMP_API_VERSION)/libgimpthumb
+
+AM_CPPFLAGS = \
+ -DG_LOG_DOMAIN=\"LibGimpThumb\" \
+ -DGIMP_THUMB_COMPILATION \
+ -I$(top_srcdir) \
+ $(GDK_PIXBUF_CFLAGS) \
+ $(GIO_CFLAGS) \
+ -I$(includedir)
+
+AM_CFLAGS = \
+ $(xobjective_c)
+
+AM_CXXFLAGS = \
+ $(xobjective_cxx)
+
+AM_LDFLAGS = \
+ $(xnone)
+
+EXTRA_DIST = \
+ gimpthumb.def
+
+lib_LTLIBRARIES = libgimpthumb-@GIMP_API_VERSION@.la
+
+libgimpthumb_@GIMP_API_VERSION@_la_SOURCES = \
+ gimpthumb.h \
+ gimpthumb-enums.c \
+ gimpthumb-enums.h \
+ gimpthumb-error.c \
+ gimpthumb-error.h \
+ gimpthumb-types.h \
+ gimpthumb-utils.c \
+ gimpthumb-utils.h \
+ gimpthumbnail.c \
+ gimpthumbnail.h
+
+libgimpthumbinclude_HEADERS = \
+ gimpthumb.h \
+ gimpthumb-enums.h \
+ gimpthumb-error.h \
+ gimpthumb-types.h \
+ gimpthumb-utils.h \
+ gimpthumbnail.h
+
+libgimpthumb_@GIMP_API_VERSION@_la_LDFLAGS = \
+ -version-info $(LT_VERSION_INFO) \
+ $(no_undefined) \
+ $(libgimpbase) \
+ $(libgimpthumb_export_symbols) \
+ $(framework_cocoa) \
+ $(xnone)
+
+libgimpthumb_@GIMP_API_VERSION@_la_LIBADD = \
+ $(GDK_PIXBUF_LIBS) \
+ $(GIO_LIBS)
+
+
+noinst_PROGRAMS = gimp-thumbnail-list
+
+gimp_thumbnail_list_SOURCES = gimp-thumbnail-list.c
+
+gimp_thumbnail_list_LDADD = \
+ libgimpthumb-$(GIMP_API_VERSION).la \
+ $(GDK_PIXBUF_LIBS) \
+ $(GIO_LIBS)
+
+
+install-data-local: install-ms-lib install-libtool-import-lib
+
+uninstall-local: uninstall-ms-lib uninstall-libtool-import-lib
+
+
+#
+# rules to generate built sources
+#
+# setup autogeneration dependencies
+gen_sources = xgen-tec
+CLEANFILES = $(gen_sources)
+
+gimpthumb-enums.c: $(srcdir)/gimpthumb-enums.h $(GIMP_MKENUMS) Makefile.am
+ $(AM_V_GEN) $(GIMP_MKENUMS) \
+ --fhead "#include \"config.h\"\n#include <glib-object.h>\n#include \"gimpthumb-enums.h\"" \
+ --fprod "\n/* enumerations from \"@filename@\" */" \
+ --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \
+ --vprod " { @VALUENAME@, @valuedesc@, \"@valuenick@\" }," \
+ --vtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n type = g_@type@_register_static (\"@EnumName@\", values);\n\n return type;\n}\n" \
+ $< > xgen-tec \
+ && cp xgen-tec $(@F) \
+ && rm -f xgen-tec
diff --git a/libgimpthumb/Makefile.in b/libgimpthumb/Makefile.in
new file mode 100644
index 0000000..9348cb0
--- /dev/null
+++ b/libgimpthumb/Makefile.in
@@ -0,0 +1,1145 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+noinst_PROGRAMS = gimp-thumbnail-list$(EXEEXT)
+subdir = libgimpthumb
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(libgimpthumbinclude_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+PROGRAMS = $(noinst_PROGRAMS)
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(libdir)" \
+ "$(DESTDIR)$(libgimpthumbincludedir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libgimpthumb_@GIMP_API_VERSION@_la_DEPENDENCIES = \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am_libgimpthumb_@GIMP_API_VERSION@_la_OBJECTS = gimpthumb-enums.lo \
+ gimpthumb-error.lo gimpthumb-utils.lo gimpthumbnail.lo
+libgimpthumb_@GIMP_API_VERSION@_la_OBJECTS = \
+ $(am_libgimpthumb_@GIMP_API_VERSION@_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libgimpthumb_@GIMP_API_VERSION@_la_LINK = $(LIBTOOL) $(AM_V_lt) \
+ --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libgimpthumb_@GIMP_API_VERSION@_la_LDFLAGS) $(LDFLAGS) -o $@
+am_gimp_thumbnail_list_OBJECTS = gimp-thumbnail-list.$(OBJEXT)
+gimp_thumbnail_list_OBJECTS = $(am_gimp_thumbnail_list_OBJECTS)
+gimp_thumbnail_list_DEPENDENCIES = \
+ libgimpthumb-$(GIMP_API_VERSION).la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/gimp-thumbnail-list.Po \
+ ./$(DEPDIR)/gimpthumb-enums.Plo \
+ ./$(DEPDIR)/gimpthumb-error.Plo \
+ ./$(DEPDIR)/gimpthumb-utils.Plo ./$(DEPDIR)/gimpthumbnail.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libgimpthumb_@GIMP_API_VERSION@_la_SOURCES) \
+ $(gimp_thumbnail_list_SOURCES)
+DIST_SOURCES = $(libgimpthumb_@GIMP_API_VERSION@_la_SOURCES) \
+ $(gimp_thumbnail_list_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+DATA = $(noinst_DATA)
+HEADERS = $(libgimpthumbinclude_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+@PLATFORM_WIN32_TRUE@no_undefined = -no-undefined
+@PLATFORM_OSX_TRUE@xobjective_c = "-xobjective-c"
+@PLATFORM_OSX_TRUE@xobjective_cxx = "-xobjective-c++"
+@PLATFORM_OSX_TRUE@xnone = "-xnone"
+@PLATFORM_OSX_TRUE@framework_cocoa = -framework Cocoa
+@OS_WIN32_TRUE@gimpthumb_def = gimpthumb.def
+@OS_WIN32_TRUE@libgimpthumb_export_symbols = -export-symbols $(srcdir)/gimpthumb.def
+@MS_LIB_AVAILABLE_TRUE@noinst_DATA = gimpthumb-$(GIMP_API_VERSION).lib
+libgimpthumbincludedir = $(includedir)/gimp-$(GIMP_API_VERSION)/libgimpthumb
+AM_CPPFLAGS = \
+ -DG_LOG_DOMAIN=\"LibGimpThumb\" \
+ -DGIMP_THUMB_COMPILATION \
+ -I$(top_srcdir) \
+ $(GDK_PIXBUF_CFLAGS) \
+ $(GIO_CFLAGS) \
+ -I$(includedir)
+
+AM_CFLAGS = \
+ $(xobjective_c)
+
+AM_CXXFLAGS = \
+ $(xobjective_cxx)
+
+AM_LDFLAGS = \
+ $(xnone)
+
+EXTRA_DIST = \
+ gimpthumb.def
+
+lib_LTLIBRARIES = libgimpthumb-@GIMP_API_VERSION@.la
+libgimpthumb_@GIMP_API_VERSION@_la_SOURCES = \
+ gimpthumb.h \
+ gimpthumb-enums.c \
+ gimpthumb-enums.h \
+ gimpthumb-error.c \
+ gimpthumb-error.h \
+ gimpthumb-types.h \
+ gimpthumb-utils.c \
+ gimpthumb-utils.h \
+ gimpthumbnail.c \
+ gimpthumbnail.h
+
+libgimpthumbinclude_HEADERS = \
+ gimpthumb.h \
+ gimpthumb-enums.h \
+ gimpthumb-error.h \
+ gimpthumb-types.h \
+ gimpthumb-utils.h \
+ gimpthumbnail.h
+
+libgimpthumb_@GIMP_API_VERSION@_la_LDFLAGS = \
+ -version-info $(LT_VERSION_INFO) \
+ $(no_undefined) \
+ $(libgimpbase) \
+ $(libgimpthumb_export_symbols) \
+ $(framework_cocoa) \
+ $(xnone)
+
+libgimpthumb_@GIMP_API_VERSION@_la_LIBADD = \
+ $(GDK_PIXBUF_LIBS) \
+ $(GIO_LIBS)
+
+gimp_thumbnail_list_SOURCES = gimp-thumbnail-list.c
+gimp_thumbnail_list_LDADD = \
+ libgimpthumb-$(GIMP_API_VERSION).la \
+ $(GDK_PIXBUF_LIBS) \
+ $(GIO_LIBS)
+
+
+#
+# rules to generate built sources
+#
+# setup autogeneration dependencies
+gen_sources = xgen-tec
+CLEANFILES = $(gen_sources)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu libgimpthumb/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu libgimpthumb/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstPROGRAMS:
+ @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libgimpthumb-@GIMP_API_VERSION@.la: $(libgimpthumb_@GIMP_API_VERSION@_la_OBJECTS) $(libgimpthumb_@GIMP_API_VERSION@_la_DEPENDENCIES) $(EXTRA_libgimpthumb_@GIMP_API_VERSION@_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libgimpthumb_@GIMP_API_VERSION@_la_LINK) -rpath $(libdir) $(libgimpthumb_@GIMP_API_VERSION@_la_OBJECTS) $(libgimpthumb_@GIMP_API_VERSION@_la_LIBADD) $(LIBS)
+
+gimp-thumbnail-list$(EXEEXT): $(gimp_thumbnail_list_OBJECTS) $(gimp_thumbnail_list_DEPENDENCIES) $(EXTRA_gimp_thumbnail_list_DEPENDENCIES)
+ @rm -f gimp-thumbnail-list$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(gimp_thumbnail_list_OBJECTS) $(gimp_thumbnail_list_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-thumbnail-list.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpthumb-enums.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpthumb-error.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpthumb-utils.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpthumbnail.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-libgimpthumbincludeHEADERS: $(libgimpthumbinclude_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(libgimpthumbinclude_HEADERS)'; test -n "$(libgimpthumbincludedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libgimpthumbincludedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libgimpthumbincludedir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(libgimpthumbincludedir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(libgimpthumbincludedir)" || exit $$?; \
+ done
+
+uninstall-libgimpthumbincludeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libgimpthumbinclude_HEADERS)'; test -n "$(libgimpthumbincludedir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(libgimpthumbincludedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) $(DATA) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libgimpthumbincludedir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ clean-noinstPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/gimp-thumbnail-list.Po
+ -rm -f ./$(DEPDIR)/gimpthumb-enums.Plo
+ -rm -f ./$(DEPDIR)/gimpthumb-error.Plo
+ -rm -f ./$(DEPDIR)/gimpthumb-utils.Plo
+ -rm -f ./$(DEPDIR)/gimpthumbnail.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-data-local install-libgimpthumbincludeHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/gimp-thumbnail-list.Po
+ -rm -f ./$(DEPDIR)/gimpthumb-enums.Plo
+ -rm -f ./$(DEPDIR)/gimpthumb-error.Plo
+ -rm -f ./$(DEPDIR)/gimpthumb-utils.Plo
+ -rm -f ./$(DEPDIR)/gimpthumbnail.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libLTLIBRARIES \
+ uninstall-libgimpthumbincludeHEADERS uninstall-local
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libLTLIBRARIES clean-libtool \
+ clean-noinstPROGRAMS cscopelist-am ctags ctags-am distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am \
+ install-data-local install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-libLTLIBRARIES \
+ install-libgimpthumbincludeHEADERS install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am uninstall-libLTLIBRARIES \
+ uninstall-libgimpthumbincludeHEADERS uninstall-local
+
+.PRECIOUS: Makefile
+
+
+@OS_WIN32_TRUE@install-libtool-import-lib:
+@OS_WIN32_TRUE@ $(INSTALL) .libs/libgimpthumb-$(GIMP_API_VERSION).dll.a $(DESTDIR)$(libdir)
+@OS_WIN32_TRUE@ $(INSTALL) $(srcdir)/gimpthumb.def $(DESTDIR)$(libdir)
+
+@OS_WIN32_TRUE@uninstall-libtool-import-lib:
+@OS_WIN32_TRUE@ -rm $(DESTDIR)$(libdir)/libgimpthumb-$(GIMP_API_VERSION).dll.a
+@OS_WIN32_TRUE@ -rm $(DESTDIR)$(libdir)/gimpthumb.def
+@OS_WIN32_FALSE@install-libtool-import-lib:
+@OS_WIN32_FALSE@uninstall-libtool-import-lib:
+
+@MS_LIB_AVAILABLE_TRUE@install-ms-lib:
+@MS_LIB_AVAILABLE_TRUE@ $(INSTALL) gimpthumb-$(GIMP_API_VERSION).lib $(DESTDIR)$(libdir)
+
+@MS_LIB_AVAILABLE_TRUE@uninstall-ms-lib:
+@MS_LIB_AVAILABLE_TRUE@ -rm $(DESTDIR)$(libdir)/gimpthumb-$(GIMP_API_VERSION).lib
+
+@MS_LIB_AVAILABLE_TRUE@gimpthumb-@GIMP_API_VERSION@.lib: gimpthumb.def
+@MS_LIB_AVAILABLE_TRUE@ lib -name:libgimpthumb-$(GIMP_API_VERSION)-@LT_CURRENT_MINUS_AGE@.dll -def:gimpthumb.def -out:$@
+
+@MS_LIB_AVAILABLE_FALSE@install-ms-lib:
+@MS_LIB_AVAILABLE_FALSE@uninstall-ms-lib:
+
+install-data-local: install-ms-lib install-libtool-import-lib
+
+uninstall-local: uninstall-ms-lib uninstall-libtool-import-lib
+
+gimpthumb-enums.c: $(srcdir)/gimpthumb-enums.h $(GIMP_MKENUMS) Makefile.am
+ $(AM_V_GEN) $(GIMP_MKENUMS) \
+ --fhead "#include \"config.h\"\n#include <glib-object.h>\n#include \"gimpthumb-enums.h\"" \
+ --fprod "\n/* enumerations from \"@filename@\" */" \
+ --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \
+ --vprod " { @VALUENAME@, @valuedesc@, \"@valuenick@\" }," \
+ --vtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n type = g_@type@_register_static (\"@EnumName@\", values);\n\n return type;\n}\n" \
+ $< > xgen-tec \
+ && cp xgen-tec $(@F) \
+ && rm -f xgen-tec
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libgimpthumb/gimp-thumbnail-list.c b/libgimpthumb/gimp-thumbnail-list.c
new file mode 100644
index 0000000..2086af2
--- /dev/null
+++ b/libgimpthumb/gimp-thumbnail-list.c
@@ -0,0 +1,251 @@
+/*
+ * gimp-thumbnail-list.c
+ */
+
+#include <string.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <libgimpthumb/gimpthumb.h>
+
+
+#define STATE_NONE -1
+#define STATE_ERROR -2
+
+
+static gboolean parse_option_state (const gchar *option_name,
+ const gchar *value,
+ gpointer data,
+ GError **error);
+static gboolean parse_option_path (const gchar *option_name,
+ const gchar *value,
+ gpointer data,
+ GError **error);
+static void process_folder (const gchar *folder);
+static void process_thumbnail (const gchar *filename);
+
+
+static GimpThumbState option_state = STATE_NONE;
+static gboolean option_verbose = FALSE;
+static gchar *option_path = NULL;
+
+
+static const GOptionEntry main_entries[] =
+{
+ {
+ "state", 's', 0,
+ G_OPTION_ARG_CALLBACK, parse_option_state,
+ "Filter by thumbnail state "
+ "(unknown|remote|folder|special|not-found|exists|old|failed|ok|error)",
+ "<state>"
+ },
+ {
+ "path", 'p', 0,
+ G_OPTION_ARG_CALLBACK, parse_option_path,
+ "Filter by original file's path",
+ "<path>"
+ },
+ {
+ "verbose", 'v', 0,
+ G_OPTION_ARG_NONE, &option_verbose,
+ "Print additional info per matched file", NULL
+ },
+ { NULL }
+};
+
+
+gint
+main (gint argc,
+ gchar *argv[])
+{
+ GOptionContext *context;
+ GDir *dir;
+ const gchar *thumb_folder;
+ const gchar *folder;
+ GError *error = NULL;
+
+ gimp_thumb_init ("gimp-thumbnail-list", NULL);
+
+ thumb_folder = gimp_thumb_get_thumb_base_dir ();
+
+ context = g_option_context_new (NULL);
+ g_option_context_add_main_entries (context, main_entries, NULL);
+
+ if (! g_option_context_parse (context, &argc, &argv, &error))
+ {
+ g_printerr ("%s\n", error->message);
+ return -1;
+ }
+
+ dir = g_dir_open (thumb_folder, 0, &error);
+
+ if (! dir)
+ g_error ("Error opening %s: %s", thumb_folder, error->message);
+
+ while ((folder = g_dir_read_name (dir)))
+ {
+ gchar *filename;
+
+ filename = g_build_filename (thumb_folder, folder, NULL);
+
+ if (g_file_test (filename, G_FILE_TEST_IS_DIR))
+ process_folder (filename);
+
+ g_free (filename);
+ }
+
+ g_dir_close (dir);
+
+ return 0;
+}
+
+static gboolean
+parse_option_state (const gchar *option_name,
+ const gchar *value,
+ gpointer data,
+ GError **error)
+{
+ if (strcmp (value, "unknown") == 0)
+ option_state = GIMP_THUMB_STATE_UNKNOWN;
+ else if (strcmp (value, "remote") == 0)
+ option_state = GIMP_THUMB_STATE_REMOTE;
+ else if (strcmp (value, "folder") == 0)
+ option_state = GIMP_THUMB_STATE_FOLDER;
+ else if (strcmp (value, "special") == 0)
+ option_state = GIMP_THUMB_STATE_SPECIAL;
+ else if (strcmp (value, "not-found") == 0)
+ option_state = GIMP_THUMB_STATE_NOT_FOUND;
+ else if (strcmp (value, "exists") == 0)
+ option_state = GIMP_THUMB_STATE_EXISTS;
+ else if (strcmp (value, "old") == 0)
+ option_state = GIMP_THUMB_STATE_OLD;
+ else if (strcmp (value, "failed") == 0)
+ option_state = GIMP_THUMB_STATE_FAILED;
+ else if (strcmp (value, "ok") == 0)
+ option_state = GIMP_THUMB_STATE_OK;
+ else if (strcmp (value, "error") == 0)
+ option_state = STATE_ERROR;
+ else
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+parse_option_path (const gchar *option_name,
+ const gchar *value,
+ gpointer data,
+ GError **error)
+{
+ option_path = g_strdup (value);
+
+ return TRUE;
+}
+
+static void
+process_folder (const gchar *folder)
+{
+ GDir *dir;
+ const gchar *name;
+ GError *error = NULL;
+
+#if 0
+ g_print ("processing folder: %s\n", folder);
+#endif
+
+ dir = g_dir_open (folder, 0, &error);
+
+ if (! dir)
+ {
+ g_printerr ("Error opening '%s': %s", folder, error->message);
+ return;
+ }
+
+ while ((name = g_dir_read_name (dir)))
+ {
+ gchar *filename;
+
+ filename = g_build_filename (folder, name, NULL);
+
+ if (g_file_test (filename, G_FILE_TEST_IS_DIR))
+ process_folder (filename);
+ else
+ process_thumbnail (filename);
+
+ g_free (filename);
+ }
+
+ g_dir_close (dir);
+}
+
+static void
+process_thumbnail (const gchar *filename)
+{
+ GimpThumbnail *thumbnail;
+ GError *error = NULL;
+
+ thumbnail = gimp_thumbnail_new ();
+
+ if (! gimp_thumbnail_set_from_thumb (thumbnail, filename, &error))
+ {
+ if (option_state == STATE_ERROR)
+ {
+ if (option_verbose)
+ g_print ("%s '%s'\n", filename, error->message);
+ else
+ g_print ("%s\n", filename);
+ }
+
+ g_clear_error (&error);
+ }
+ else
+ {
+ GimpThumbState state = gimp_thumbnail_peek_image (thumbnail);
+
+ if ((option_state == STATE_NONE || state == option_state)
+
+ &&
+
+ (option_path == NULL ||
+ strstr (thumbnail->image_uri, option_path)))
+ {
+ if (option_verbose)
+ g_print ("%s '%s'\n", filename, thumbnail->image_uri);
+ else
+ g_print ("%s\n", filename);
+ }
+
+#if 0
+ switch (foo)
+ {
+ case GIMP_THUMB_STATE_REMOTE:
+ g_print ("%s Remote image '%s'\n", filename, thumbnail->image_uri);
+ break;
+
+ case GIMP_THUMB_STATE_FOLDER:
+ g_print ("%s Folder '%s'\n", filename, thumbnail->image_uri);
+ break;
+
+ case GIMP_THUMB_STATE_SPECIAL:
+ g_print ("%s Special file '%s'\n", filename, thumbnail->image_uri);
+ break;
+
+ case GIMP_THUMB_STATE_NOT_FOUND:
+ g_print ("%s Image not found '%s'\n", filename, thumbnail->image_uri);
+ break;
+
+ case GIMP_THUMB_STATE_OLD:
+ g_print ("%s Thumbnail old '%s'\n", filename, thumbnail->image_uri);
+ break;
+
+ case GIMP_THUMB_STATE_FAILED:
+ g_print ("%s EEEEEEEEK '%s'\n", filename, thumbnail->image_uri);
+ break;
+
+ default:
+ g_print ("%s '%s'\n", filename, thumbnail->image_uri);
+ break;
+ }
+#endif
+ }
+
+ g_object_unref (thumbnail);
+}
diff --git a/libgimpthumb/gimpthumb-enums.c b/libgimpthumb/gimpthumb-enums.c
new file mode 100644
index 0000000..b7924e8
--- /dev/null
+++ b/libgimpthumb/gimpthumb-enums.c
@@ -0,0 +1,75 @@
+
+/* Generated data (by gimp-mkenums) */
+
+#include "config.h"
+#include <glib-object.h>
+#include "gimpthumb-enums.h"
+
+/* enumerations from "../../libgimpthumb/gimpthumb-enums.h" */
+GType
+gimp_thumb_file_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_THUMB_FILE_TYPE_NONE, "GIMP_THUMB_FILE_TYPE_NONE", "none" },
+ { GIMP_THUMB_FILE_TYPE_REGULAR, "GIMP_THUMB_FILE_TYPE_REGULAR", "regular" },
+ { GIMP_THUMB_FILE_TYPE_FOLDER, "GIMP_THUMB_FILE_TYPE_FOLDER", "folder" },
+ { GIMP_THUMB_FILE_TYPE_SPECIAL, "GIMP_THUMB_FILE_TYPE_SPECIAL", "special" },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ type = g_enum_register_static ("GimpThumbFileType", values);
+
+ return type;
+}
+
+GType
+gimp_thumb_size_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_THUMB_SIZE_FAIL, "GIMP_THUMB_SIZE_FAIL", "fail" },
+ { GIMP_THUMB_SIZE_NORMAL, "GIMP_THUMB_SIZE_NORMAL", "normal" },
+ { GIMP_THUMB_SIZE_LARGE, "GIMP_THUMB_SIZE_LARGE", "large" },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ type = g_enum_register_static ("GimpThumbSize", values);
+
+ return type;
+}
+
+GType
+gimp_thumb_state_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_THUMB_STATE_UNKNOWN, "GIMP_THUMB_STATE_UNKNOWN", "unknown" },
+ { GIMP_THUMB_STATE_REMOTE, "GIMP_THUMB_STATE_REMOTE", "remote" },
+ { GIMP_THUMB_STATE_FOLDER, "GIMP_THUMB_STATE_FOLDER", "folder" },
+ { GIMP_THUMB_STATE_SPECIAL, "GIMP_THUMB_STATE_SPECIAL", "special" },
+ { GIMP_THUMB_STATE_NOT_FOUND, "GIMP_THUMB_STATE_NOT_FOUND", "not-found" },
+ { GIMP_THUMB_STATE_EXISTS, "GIMP_THUMB_STATE_EXISTS", "exists" },
+ { GIMP_THUMB_STATE_OLD, "GIMP_THUMB_STATE_OLD", "old" },
+ { GIMP_THUMB_STATE_FAILED, "GIMP_THUMB_STATE_FAILED", "failed" },
+ { GIMP_THUMB_STATE_OK, "GIMP_THUMB_STATE_OK", "ok" },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ type = g_enum_register_static ("GimpThumbState", values);
+
+ return type;
+}
+
+
+/* Generated data ends here */
+
diff --git a/libgimpthumb/gimpthumb-enums.h b/libgimpthumb/gimpthumb-enums.h
new file mode 100644
index 0000000..8414673
--- /dev/null
+++ b/libgimpthumb/gimpthumb-enums.h
@@ -0,0 +1,118 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * Thumbnail handling according to the Thumbnail Managing Standard.
+ * https://specifications.freedesktop.org/thumbnail-spec/
+ *
+ * Copyright (C) 2001-2003 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_THUMB_ENUMS_H__
+#define __GIMP_THUMB_ENUMS_H__
+
+G_BEGIN_DECLS
+
+
+/**
+ * SECTION: gimpthumb-enums
+ * @title: GimpThumb-enums
+ * @short_description: Enumerations used by libgimpthumb
+ *
+ * Enumerations used by libgimpthumb
+ **/
+
+
+/**
+ * GimpThumbFileType:
+ * @GIMP_THUMB_FILE_TYPE_NONE: file does not exist
+ * @GIMP_THUMB_FILE_TYPE_REGULAR: a regular file
+ * @GIMP_THUMB_FILE_TYPE_FOLDER: a directory
+ * @GIMP_THUMB_FILE_TYPE_SPECIAL: a special file (device node, fifo, socket, ...)
+ *
+ * File types as returned by gimp_thumb_file_test().
+ **/
+#define GIMP_TYPE_THUMB_FILE_TYPE (gimp_thumb_file_type_get_type ())
+
+GType gimp_thumb_file_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_THUMB_FILE_TYPE_NONE,
+ GIMP_THUMB_FILE_TYPE_REGULAR,
+ GIMP_THUMB_FILE_TYPE_FOLDER,
+ GIMP_THUMB_FILE_TYPE_SPECIAL
+} GimpThumbFileType;
+
+
+/**
+ * GimpThumbSize:
+ * @GIMP_THUMB_SIZE_FAIL: special size used to indicate a thumbnail
+ * creation failure
+ * @GIMP_THUMB_SIZE_NORMAL: normal thumbnail size (128 pixels)
+ * @GIMP_THUMB_SIZE_LARGE: large thumbnail size (256 pixels)
+ *
+ * Possible thumbnail sizes as defined by the Thumbnail Managing
+ * Standard.
+ **/
+#define GIMP_TYPE_THUMB_SIZE (gimp_thumb_size_get_type ())
+
+GType gimp_thumb_size_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_THUMB_SIZE_FAIL = 0,
+ GIMP_THUMB_SIZE_NORMAL = 128,
+ GIMP_THUMB_SIZE_LARGE = 256
+} GimpThumbSize;
+
+
+/**
+ * GimpThumbState:
+ * @GIMP_THUMB_STATE_UNKNOWN: nothing is known about the file/thumbnail
+ * @GIMP_THUMB_STATE_REMOTE: the file is on a remote file system
+ * @GIMP_THUMB_STATE_FOLDER: the file is a directory
+ * @GIMP_THUMB_STATE_SPECIAL: the file is a special file
+ * @GIMP_THUMB_STATE_NOT_FOUND: the file/thumbnail doesn't exist
+ * @GIMP_THUMB_STATE_EXISTS: the file/thumbnail exists
+ * @GIMP_THUMB_STATE_OLD: the thumbnail may be outdated
+ * @GIMP_THUMB_STATE_FAILED: the thumbnail couldn't be created
+ * @GIMP_THUMB_STATE_OK: the thumbnail exists and matches the image
+ *
+ * Possible image and thumbnail file states used by libgimpthumb.
+ **/
+#define GIMP_TYPE_THUMB_STATE (gimp_thumb_state_get_type ())
+
+GType gimp_thumb_state_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_THUMB_STATE_UNKNOWN,
+ GIMP_THUMB_STATE_REMOTE,
+ GIMP_THUMB_STATE_FOLDER,
+ GIMP_THUMB_STATE_SPECIAL,
+ GIMP_THUMB_STATE_NOT_FOUND,
+ GIMP_THUMB_STATE_EXISTS,
+ GIMP_THUMB_STATE_OLD,
+ GIMP_THUMB_STATE_FAILED,
+ GIMP_THUMB_STATE_OK
+} GimpThumbState;
+
+
+G_END_DECLS
+
+#endif /* __GIMP_THUMB_ENUMS_H__ */
diff --git a/libgimpthumb/gimpthumb-error.c b/libgimpthumb/gimpthumb-error.c
new file mode 100644
index 0000000..f4ab620
--- /dev/null
+++ b/libgimpthumb/gimpthumb-error.c
@@ -0,0 +1,52 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * Thumbnail handling according to the Thumbnail Managing Standard.
+ * https://specifications.freedesktop.org/thumbnail-spec/
+ *
+ * Copyright (C) 2001-2003 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+
+#include "gimpthumb-error.h"
+
+
+/**
+ * SECTION: gimpthumb-error
+ * @title: GimpThumb-error
+ * @short_description: Error codes used by libgimpthumb
+ *
+ * Error codes used by libgimpthumb
+ **/
+
+
+/**
+ * gimp_thumb_error_quark:
+ *
+ * This function is never called directly. Use GIMP_THUMB_ERROR() instead.
+ *
+ * Return value: the #GQuark that defines the GimpThumb error domain.
+ **/
+GQuark
+gimp_thumb_error_quark (void)
+{
+ return g_quark_from_static_string ("gimp-thumb-error-quark");
+}
diff --git a/libgimpthumb/gimpthumb-error.h b/libgimpthumb/gimpthumb-error.h
new file mode 100644
index 0000000..9dec6e8
--- /dev/null
+++ b/libgimpthumb/gimpthumb-error.h
@@ -0,0 +1,64 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * Thumbnail handling according to the Thumbnail Managing Standard.
+ * https://specifications.freedesktop.org/thumbnail-spec/
+ *
+ * Copyright (C) 2001-2003 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_THUMB_H_INSIDE__) && !defined (GIMP_THUMB_COMPILATION)
+#error "Only <libgimpthumb/gimpthumb.h> can be included directly."
+#endif
+
+#ifndef __GIMP_THUMB_ERROR_H__
+#define __GIMP_THUMB_ERROR_H__
+
+G_BEGIN_DECLS
+
+
+/**
+ * GimpThumbError:
+ * @GIMP_THUMB_ERROR_OPEN: there was a problem opening the file
+ * @GIMP_THUMB_ERROR_OPEN_ENOENT: the file doesn't exist
+ * @GIMP_THUMB_ERROR_MKDIR: there was a problem creating a directory
+ *
+ * These are the possible error codes used when a #GError is set by
+ * libgimpthumb.
+ **/
+typedef enum
+{
+ GIMP_THUMB_ERROR_OPEN,
+ GIMP_THUMB_ERROR_OPEN_ENOENT,
+ GIMP_THUMB_ERROR_MKDIR
+} GimpThumbError;
+
+
+/**
+ * GIMP_THUMB_ERROR:
+ *
+ * Identifier for the libgimpthumb error domain.
+ **/
+#define GIMP_THUMB_ERROR (gimp_thumb_error_quark ())
+
+GQuark gimp_thumb_error_quark (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif /* __GIMP_THUMB_ERROR_H__ */
diff --git a/libgimpthumb/gimpthumb-types.h b/libgimpthumb/gimpthumb-types.h
new file mode 100644
index 0000000..29aa4e3
--- /dev/null
+++ b/libgimpthumb/gimpthumb-types.h
@@ -0,0 +1,38 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * Thumbnail handling according to the Thumbnail Managing Standard.
+ * https://specifications.freedesktop.org/thumbnail-spec/
+ *
+ * Copyright (C) 2001-2003 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_THUMB_TYPES_H__
+#define __GIMP_THUMB_TYPES_H__
+
+
+#include <libgimpthumb/gimpthumb-enums.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GimpThumbnail GimpThumbnail;
+
+G_END_DECLS
+
+
+#endif /* __GIMP_THUMB_TYPES_H__ */
diff --git a/libgimpthumb/gimpthumb-utils.c b/libgimpthumb/gimpthumb-utils.c
new file mode 100644
index 0000000..59dd830
--- /dev/null
+++ b/libgimpthumb/gimpthumb-utils.c
@@ -0,0 +1,870 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * Thumbnail handling according to the Thumbnail Managing Standard.
+ * https://specifications.freedesktop.org/thumbnail-spec/
+ *
+ * Copyright (C) 2001-2003 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+
+#ifdef PLATFORM_OSX
+#include <AppKit/AppKit.h>
+#endif
+
+#include <gio/gio.h>
+#include <glib/gstdio.h>
+
+#ifdef G_OS_WIN32
+#include "libgimpbase/gimpwin32-io.h"
+#endif
+
+#include "gimpthumb-error.h"
+#include "gimpthumb-types.h"
+#include "gimpthumb-utils.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpthumb-utils
+ * @title: GimpThumb-utils
+ * @short_description: Utility functions provided and used by libgimpthumb
+ *
+ * Utility functions provided and used by libgimpthumb
+ **/
+
+
+static gint gimp_thumb_size (GimpThumbSize size);
+static gchar * gimp_thumb_png_lookup (const gchar *name,
+ const gchar *basedir,
+ GimpThumbSize *size) G_GNUC_MALLOC;
+static const gchar * gimp_thumb_png_name (const gchar *uri);
+static void gimp_thumb_exit (void);
+
+
+
+static gboolean gimp_thumb_initialized = FALSE;
+static gint thumb_num_sizes = 0;
+static gint *thumb_sizes = NULL;
+static const gchar **thumb_sizenames = NULL;
+static gchar *thumb_dir = NULL;
+static gchar **thumb_subdirs = NULL;
+static gchar *thumb_fail_subdir = NULL;
+
+
+/**
+ * gimp_thumb_init:
+ * @creator: an ASCII string that identifies the thumbnail creator
+ * @thumb_basedir: an absolute path or %NULL to use the default
+ *
+ * This function initializes the thumbnail system. It must be called
+ * before any other functions from libgimpthumb are used. You may call
+ * it more than once if you want to change the @thumb_basedir but if
+ * you do that, you should make sure that no thread is still using the
+ * library. Apart from this function, libgimpthumb is multi-thread
+ * safe.
+ *
+ * The @creator string must be 7bit ASCII and should contain the name
+ * of the software that creates the thumbnails. It is used to handle
+ * thumbnail creation failures. See the spec for more details.
+ *
+ * Usually you will pass %NULL for @thumb_basedir. Thumbnails will
+ * then be stored in the user's personal thumbnail directory as
+ * defined in the spec. If you wish to use libgimpthumb to store
+ * application-specific thumbnails, you can specify a different base
+ * directory here.
+ *
+ * Return value: %TRUE if the library was successfully initialized.
+ **/
+gboolean
+gimp_thumb_init (const gchar *creator,
+ const gchar *thumb_basedir)
+{
+ GEnumClass *enum_class;
+ GEnumValue *enum_value;
+ gint i;
+
+ g_return_val_if_fail (creator != NULL, FALSE);
+ g_return_val_if_fail (thumb_basedir == NULL ||
+ g_path_is_absolute (thumb_basedir), FALSE);
+
+ if (gimp_thumb_initialized)
+ gimp_thumb_exit ();
+
+ if (thumb_basedir)
+ {
+ thumb_dir = g_strdup (thumb_basedir);
+ }
+ else
+ {
+#ifdef PLATFORM_OSX
+
+ NSAutoreleasePool *pool;
+ NSArray *path;
+ NSString *cache_dir;
+
+ pool = [[NSAutoreleasePool alloc] init];
+
+ path = NSSearchPathForDirectoriesInDomains (NSCachesDirectory,
+ NSUserDomainMask, YES);
+ cache_dir = [path objectAtIndex:0];
+
+ thumb_dir = g_build_filename ([cache_dir UTF8String], "org.freedesktop.thumbnails",
+ NULL);
+
+ [pool drain];
+
+#else
+
+ const gchar *cache_dir = g_get_user_cache_dir ();
+
+ if (cache_dir && g_file_test (cache_dir, G_FILE_TEST_IS_DIR))
+ {
+ thumb_dir = g_build_filename (cache_dir, "thumbnails", NULL);
+ }
+
+#endif
+
+ if (! thumb_dir)
+ {
+ gchar *name = g_filename_display_name (g_get_tmp_dir ());
+
+ g_message (_("Cannot determine a valid thumbnails directory.\n"
+ "Thumbnails will be stored in the folder for "
+ "temporary files (%s) instead."), name);
+ g_free (name);
+
+ thumb_dir = g_build_filename (g_get_tmp_dir (), ".thumbnails", NULL);
+ }
+ }
+
+ enum_class = g_type_class_ref (GIMP_TYPE_THUMB_SIZE);
+
+ thumb_num_sizes = enum_class->n_values;
+ thumb_sizes = g_new (gint, thumb_num_sizes);
+ thumb_sizenames = g_new (const gchar *, thumb_num_sizes);
+ thumb_subdirs = g_new (gchar *, thumb_num_sizes);
+
+ for (i = 0, enum_value = enum_class->values;
+ i < enum_class->n_values;
+ i++, enum_value++)
+ {
+ thumb_sizes[i] = enum_value->value;
+ thumb_sizenames[i] = enum_value->value_nick;
+ thumb_subdirs[i] = g_build_filename (thumb_dir,
+ enum_value->value_nick, NULL);
+ }
+
+ thumb_fail_subdir = thumb_subdirs[0];
+ thumb_subdirs[0] = g_build_filename (thumb_fail_subdir, creator, NULL);
+
+ g_type_class_unref (enum_class);
+
+ gimp_thumb_initialized = TRUE;
+
+ return gimp_thumb_initialized;
+}
+
+/**
+ * gimp_thumb_get_thumb_base_dir:
+ *
+ * Returns the base directory of thumbnails cache.
+ * It uses the Freedesktop Thumbnail Managing Standard on UNIX,
+ * "~/Library/Caches/org.freedesktop.thumbnails" on OSX, and a cache
+ * folder determined by glib on Windows (currently the common repository
+ * for temporary Internet files).
+ * The returned string belongs to GIMP and must not be changed nor freed.
+ *
+ * Returns: the thumbnails cache directory.
+ *
+ * Since: 2.10
+ **/
+const gchar *
+gimp_thumb_get_thumb_base_dir (void)
+{
+ g_return_val_if_fail (gimp_thumb_initialized, NULL);
+
+ return thumb_dir;
+}
+
+/**
+ * gimp_thumb_get_thumb_dir:
+ * @size: a GimpThumbSize
+ *
+ * Retrieve the name of the thumbnail folder for a specific size. The
+ * returned pointer will become invalid if gimp_thumb_init() is used
+ * again. It must not be changed or freed.
+ *
+ * Return value: the thumbnail directory in the encoding of the filesystem
+ **/
+const gchar *
+gimp_thumb_get_thumb_dir (GimpThumbSize size)
+{
+ g_return_val_if_fail (gimp_thumb_initialized, NULL);
+
+ size = gimp_thumb_size (size);
+
+ return thumb_subdirs[size];
+}
+
+/**
+ * gimp_thumb_get_thumb_dir_local:
+ * @dirname: the basename of the dir, without the actual dirname itself
+ * @size: a GimpThumbSize
+ *
+ * Retrieve the name of the local thumbnail folder for a specific
+ * size. Unlike gimp_thumb_get_thumb_dir() the returned string is not
+ * constant and should be free'd when it is not any longer needed.
+ *
+ * Return value: the thumbnail directory in the encoding of the filesystem
+ *
+ * Since: 2.2
+ **/
+gchar *
+gimp_thumb_get_thumb_dir_local (const gchar *dirname,
+ GimpThumbSize size)
+{
+ g_return_val_if_fail (gimp_thumb_initialized, NULL);
+ g_return_val_if_fail (dirname != NULL, NULL);
+ g_return_val_if_fail (size > GIMP_THUMB_SIZE_FAIL, NULL);
+
+ size = gimp_thumb_size (size);
+
+ return g_build_filename (dirname, thumb_sizenames[size], NULL);
+}
+
+/**
+ * gimp_thumb_ensure_thumb_dir:
+ * @size: a GimpThumbSize
+ * @error: return location for possible errors
+ *
+ * This function checks if the directory that is required to store
+ * thumbnails for a particular @size exist and attempts to create it
+ * if necessary.
+ *
+ * You shouldn't have to call this function directly since
+ * gimp_thumbnail_save_thumb() and gimp_thumbnail_save_failure() will
+ * do this for you.
+ *
+ * Return value: %TRUE is the directory exists, %FALSE if it could not
+ * be created
+ **/
+gboolean
+gimp_thumb_ensure_thumb_dir (GimpThumbSize size,
+ GError **error)
+{
+ g_return_val_if_fail (gimp_thumb_initialized, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ size = gimp_thumb_size (size);
+
+ if (g_file_test (thumb_subdirs[size], G_FILE_TEST_IS_DIR))
+ return TRUE;
+
+ if (g_file_test (thumb_dir, G_FILE_TEST_IS_DIR) ||
+ (g_mkdir_with_parents (thumb_dir, S_IRUSR | S_IWUSR | S_IXUSR) == 0))
+ {
+ if (size == 0)
+ g_mkdir_with_parents (thumb_fail_subdir, S_IRUSR | S_IWUSR | S_IXUSR);
+
+ g_mkdir_with_parents (thumb_subdirs[size], S_IRUSR | S_IWUSR | S_IXUSR);
+ }
+
+ if (g_file_test (thumb_subdirs[size], G_FILE_TEST_IS_DIR))
+ return TRUE;
+
+ g_set_error (error,
+ GIMP_THUMB_ERROR, GIMP_THUMB_ERROR_MKDIR,
+ _("Failed to create thumbnail folder '%s'."),
+ thumb_subdirs[size]);
+
+ return FALSE;
+}
+
+/**
+ * gimp_thumb_ensure_thumb_dir_local:
+ * @dirname: the basename of the dir, without the actual dirname itself
+ * @size: a GimpThumbSize
+ * @error: return location for possible errors
+ *
+ * This function checks if the directory that is required to store
+ * local thumbnails for a particular @size exist and attempts to
+ * create it if necessary.
+ *
+ * You shouldn't have to call this function directly since
+ * gimp_thumbnail_save_thumb_local() will do this for you.
+ *
+ * Return value: %TRUE is the directory exists, %FALSE if it could not
+ * be created
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_thumb_ensure_thumb_dir_local (const gchar *dirname,
+ GimpThumbSize size,
+ GError **error)
+{
+ gchar *basedir;
+ gchar *subdir;
+
+ g_return_val_if_fail (gimp_thumb_initialized, FALSE);
+ g_return_val_if_fail (dirname != NULL, FALSE);
+ g_return_val_if_fail (g_path_is_absolute (dirname), FALSE);
+ g_return_val_if_fail (size > GIMP_THUMB_SIZE_FAIL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ size = gimp_thumb_size (size);
+
+ subdir = g_build_filename (dirname,
+ ".thumblocal", thumb_sizenames[size],
+ NULL);
+
+ if (g_file_test (subdir, G_FILE_TEST_IS_DIR))
+ {
+ g_free (subdir);
+ return TRUE;
+ }
+
+ basedir = g_build_filename (dirname, ".thumblocal", NULL);
+
+ if (g_file_test (basedir, G_FILE_TEST_IS_DIR) ||
+ (g_mkdir (thumb_dir, S_IRUSR | S_IWUSR | S_IXUSR) == 0))
+ {
+ g_mkdir (subdir, S_IRUSR | S_IWUSR | S_IXUSR);
+ }
+
+ g_free (basedir);
+
+ if (g_file_test (subdir, G_FILE_TEST_IS_DIR))
+ {
+ g_free (subdir);
+ return TRUE;
+ }
+
+ g_set_error (error,
+ GIMP_THUMB_ERROR, GIMP_THUMB_ERROR_MKDIR,
+ _("Failed to create thumbnail folder '%s'."),
+ subdir);
+ g_free (subdir);
+
+ return FALSE;
+}
+
+/**
+ * gimp_thumb_name_from_uri:
+ * @uri: an escaped URI
+ * @size: a #GimpThumbSize
+ *
+ * Creates the name of the thumbnail file of the specified @size that
+ * belongs to an image file located at the given @uri.
+ *
+ * Return value: a newly allocated filename in the encoding of the
+ * filesystem or %NULL if @uri points to the user's
+ * thumbnail repository.
+ **/
+gchar *
+gimp_thumb_name_from_uri (const gchar *uri,
+ GimpThumbSize size)
+{
+ g_return_val_if_fail (gimp_thumb_initialized, NULL);
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ if (strstr (uri, thumb_dir))
+ return NULL;
+
+ size = gimp_thumb_size (size);
+
+ return g_build_filename (thumb_subdirs[size],
+ gimp_thumb_png_name (uri),
+ NULL);
+}
+
+/**
+ * gimp_thumb_name_from_uri_local:
+ * @uri: an escaped URI
+ * @size: a #GimpThumbSize
+ *
+ * Creates the name of a local thumbnail file of the specified @size
+ * that belongs to an image file located at the given @uri. Local
+ * thumbnails have been introduced with version 0.7 of the spec.
+ *
+ * Return value: a newly allocated filename in the encoding of the
+ * filesystem or %NULL if @uri is a remote file or
+ * points to the user's thumbnail repository.
+ *
+ * Since: 2.2
+ **/
+gchar *
+gimp_thumb_name_from_uri_local (const gchar *uri,
+ GimpThumbSize size)
+{
+ gchar *filename;
+ gchar *result = NULL;
+
+ g_return_val_if_fail (gimp_thumb_initialized, NULL);
+ g_return_val_if_fail (uri != NULL, NULL);
+ g_return_val_if_fail (size > GIMP_THUMB_SIZE_FAIL, NULL);
+
+ if (strstr (uri, thumb_dir))
+ return NULL;
+
+ filename = _gimp_thumb_filename_from_uri (uri);
+
+ if (filename)
+ {
+ const gchar *baseuri = strrchr (uri, '/');
+
+ if (baseuri && baseuri[0] && baseuri[1])
+ {
+ gchar *dirname = g_path_get_dirname (filename);
+ gint i = gimp_thumb_size (size);
+
+ result = g_build_filename (dirname,
+ ".thumblocal", thumb_sizenames[i],
+ gimp_thumb_png_name (uri),
+ NULL);
+
+ g_free (dirname);
+ }
+
+ g_free (filename);
+ }
+
+ return result;
+}
+
+/**
+ * gimp_thumb_find_thumb:
+ * @uri: an escaped URI
+ * @size: pointer to a #GimpThumbSize
+ *
+ * This function attempts to locate a thumbnail for the given
+ * @uri. First it tries the size that is stored at @size. If no
+ * thumbnail of that size is found, it will look for a larger
+ * thumbnail, then falling back to a smaller size.
+ *
+ * If the user's thumbnail repository doesn't provide a thumbnail but
+ * a local thumbnail repository exists for the folder the image is
+ * located in, the same search is done among the local thumbnails (if
+ * there are any).
+ *
+ * If a thumbnail is found, it's size is written to the variable
+ * pointer to by @size and the file location is returned.
+ *
+ * Return value: a newly allocated string in the encoding of the
+ * filesystem or %NULL if no thumbnail for @uri was found
+ **/
+gchar *
+gimp_thumb_find_thumb (const gchar *uri,
+ GimpThumbSize *size)
+{
+ gchar *result;
+
+ g_return_val_if_fail (gimp_thumb_initialized, NULL);
+ g_return_val_if_fail (uri != NULL, NULL);
+ g_return_val_if_fail (size != NULL, NULL);
+ g_return_val_if_fail (*size > GIMP_THUMB_SIZE_FAIL, NULL);
+
+ result = gimp_thumb_png_lookup (gimp_thumb_png_name (uri), NULL, size);
+
+ if (! result)
+ {
+ gchar *filename = _gimp_thumb_filename_from_uri (uri);
+
+ if (filename)
+ {
+ const gchar *baseuri = strrchr (uri, '/');
+
+ if (baseuri && baseuri[0] && baseuri[1])
+ {
+ gchar *dirname = g_path_get_dirname (filename);
+
+ result = gimp_thumb_png_lookup (gimp_thumb_png_name (baseuri + 1),
+ dirname, size);
+
+ g_free (dirname);
+ }
+
+ g_free (filename);
+ }
+ }
+
+ return result;
+}
+
+/**
+ * gimp_thumb_file_test:
+ * @filename: a filename in the encoding of the filesystem
+ * @mtime: return location for modification time
+ * @size: return location for file size
+ * @err_no: return location for system "errno"
+ *
+ * This is a convenience and portability wrapper around stat(). It
+ * checks if the given @filename exists and returns modification time
+ * and file size in 64bit integer values.
+ *
+ * Return value: The type of the file, or #GIMP_THUMB_FILE_TYPE_NONE if
+ * the file doesn't exist.
+ **/
+GimpThumbFileType
+gimp_thumb_file_test (const gchar *filename,
+ gint64 *mtime,
+ gint64 *size,
+ gint *err_no)
+{
+ GimpThumbFileType type = GIMP_THUMB_FILE_TYPE_NONE;
+ GFile *file;
+ GFileInfo *info;
+
+ g_return_val_if_fail (filename != NULL, FALSE);
+
+ file = g_file_new_for_path (filename);
+
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_TYPE ","
+ G_FILE_ATTRIBUTE_STANDARD_SIZE ","
+ G_FILE_ATTRIBUTE_TIME_MODIFIED,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, NULL);
+
+ if (info)
+ {
+ if (mtime)
+ *mtime =
+ g_file_info_get_attribute_uint64 (info,
+ G_FILE_ATTRIBUTE_TIME_MODIFIED);
+
+ if (size)
+ *size = g_file_info_get_size (info);
+
+ if (err_no)
+ *err_no = 0;
+
+ switch (g_file_info_get_attribute_uint32 (info,
+ G_FILE_ATTRIBUTE_STANDARD_TYPE))
+ {
+ case G_FILE_TYPE_REGULAR:
+ type = GIMP_THUMB_FILE_TYPE_REGULAR;
+ break;
+
+ case G_FILE_TYPE_DIRECTORY:
+ type = GIMP_THUMB_FILE_TYPE_FOLDER;
+ break;
+
+ default:
+ type = GIMP_THUMB_FILE_TYPE_SPECIAL;
+ break;
+ }
+
+ g_object_unref (info);
+ }
+ else
+ {
+ if (mtime) *mtime = 0;
+ if (size) *size = 0;
+ if (err_no) *err_no = ENOENT;
+ }
+
+ g_object_unref (file);
+
+ return type;
+}
+
+/**
+ * gimp_thumbs_delete_for_uri:
+ * @uri: an escaped URI
+ *
+ * Deletes all thumbnails for the image file specified by @uri from the
+ * user's thumbnail repository.
+ *
+ * Since: 2.2
+ **/
+void
+gimp_thumbs_delete_for_uri (const gchar *uri)
+{
+ gint i;
+
+ g_return_if_fail (gimp_thumb_initialized);
+ g_return_if_fail (uri != NULL);
+
+ for (i = 0; i < thumb_num_sizes; i++)
+ {
+ gchar *filename = gimp_thumb_name_from_uri (uri, thumb_sizes[i]);
+
+ if (filename)
+ {
+ g_unlink (filename);
+ g_free (filename);
+ }
+ }
+}
+
+/**
+ * gimp_thumbs_delete_for_uri_local:
+ * @uri: an escaped URI
+ *
+ * Deletes all thumbnails for the image file specified by @uri from
+ * the local thumbnail repository.
+ *
+ * Since: 2.2
+ **/
+void
+gimp_thumbs_delete_for_uri_local (const gchar *uri)
+{
+ gint i;
+
+ g_return_if_fail (gimp_thumb_initialized);
+ g_return_if_fail (uri != NULL);
+
+ for (i = 0; i < thumb_num_sizes; i++)
+ {
+ gchar *filename = gimp_thumb_name_from_uri_local (uri, thumb_sizes[i]);
+
+ if (filename)
+ {
+ g_unlink (filename);
+ g_free (filename);
+ }
+ }
+}
+
+void
+_gimp_thumbs_delete_others (const gchar *uri,
+ GimpThumbSize size)
+{
+ gint i;
+
+ g_return_if_fail (gimp_thumb_initialized);
+ g_return_if_fail (uri != NULL);
+
+ size = gimp_thumb_size (size);
+
+ for (i = 0; i < thumb_num_sizes; i++)
+ {
+ gchar *filename;
+
+ if (i == size)
+ continue;
+
+ filename = gimp_thumb_name_from_uri (uri, thumb_sizes[i]);
+ if (filename)
+ {
+ g_unlink (filename);
+ g_free (filename);
+ }
+ }
+}
+
+gchar *
+_gimp_thumb_filename_from_uri (const gchar *uri)
+{
+ gchar *filename;
+ gchar *hostname;
+
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ filename = g_filename_from_uri (uri, &hostname, NULL);
+
+ if (!filename)
+ return NULL;
+
+ if (hostname)
+ {
+ /* we have a file: URI with a hostname */
+
+#ifdef G_OS_WIN32
+ /* on Win32, create a valid UNC path and use it as the filename */
+ gchar *tmp = g_build_filename ("//", hostname, filename, NULL);
+
+ g_free (filename);
+ filename = tmp;
+#else
+ /* otherwise return NULL, caller should use URI then */
+ g_free (filename);
+ filename = NULL;
+#endif
+
+ g_free (hostname);
+ }
+
+ return filename;
+}
+
+static void
+gimp_thumb_exit (void)
+{
+ gint i;
+
+ g_free (thumb_dir);
+ g_free (thumb_sizes);
+ g_free (thumb_sizenames);
+ for (i = 0; i < thumb_num_sizes; i++)
+ g_free (thumb_subdirs[i]);
+ g_free (thumb_subdirs);
+ g_free (thumb_fail_subdir);
+
+ thumb_num_sizes = 0;
+ thumb_sizes = NULL;
+ thumb_sizenames = NULL;
+ thumb_dir = NULL;
+ thumb_subdirs = NULL;
+ thumb_fail_subdir = NULL;
+ gimp_thumb_initialized = FALSE;
+}
+
+static gint
+gimp_thumb_size (GimpThumbSize size)
+{
+ gint i = 0;
+
+ if (size > GIMP_THUMB_SIZE_FAIL)
+ {
+ for (i = 1;
+ i < thumb_num_sizes && thumb_sizes[i] < size;
+ i++)
+ /* nothing */;
+
+ if (i == thumb_num_sizes)
+ i--;
+ }
+
+ return i;
+}
+
+static gchar *
+gimp_thumb_png_lookup (const gchar *name,
+ const gchar *basedir,
+ GimpThumbSize *size)
+{
+ gchar *thumb_name = NULL;
+ gchar **subdirs = NULL;
+ gint i, n;
+
+ if (basedir)
+ {
+ gchar *dir = g_build_filename (basedir, ".thumblocal", NULL);
+
+ if (g_file_test (basedir, G_FILE_TEST_IS_DIR))
+ {
+ gint i;
+
+ subdirs = g_new (gchar *, thumb_num_sizes);
+
+ subdirs[0] = NULL; /* GIMP_THUMB_SIZE_FAIL */
+
+ for (i = 1; i < thumb_num_sizes; i++)
+ subdirs[i] = g_build_filename (dir, thumb_sizenames[i], NULL);
+ }
+
+ g_free (dir);
+ }
+ else
+ {
+ subdirs = thumb_subdirs;
+ }
+
+ if (! subdirs)
+ return NULL;
+
+ i = n = gimp_thumb_size (*size);
+
+ for (; i < thumb_num_sizes; i++)
+ {
+ if (! subdirs[i])
+ continue;
+
+ thumb_name = g_build_filename (subdirs[i], name, NULL);
+
+ if (gimp_thumb_file_test (thumb_name,
+ NULL, NULL,
+ NULL) == GIMP_THUMB_FILE_TYPE_REGULAR)
+ {
+ *size = thumb_sizes[i];
+ goto finish;
+ }
+
+ g_free (thumb_name);
+ }
+
+ for (i = n - 1; i >= 0; i--)
+ {
+ if (! subdirs[i])
+ continue;
+
+ thumb_name = g_build_filename (subdirs[i], name, NULL);
+
+ if (gimp_thumb_file_test (thumb_name,
+ NULL, NULL,
+ NULL) == GIMP_THUMB_FILE_TYPE_REGULAR)
+ {
+ *size = thumb_sizes[i];
+ goto finish;
+ }
+
+ g_free (thumb_name);
+ }
+
+ thumb_name = NULL;
+
+ finish:
+ if (basedir)
+ {
+ for (i = 0; i < thumb_num_sizes; i++)
+ g_free (subdirs[i]);
+ g_free (subdirs);
+ }
+
+ return thumb_name;
+}
+
+static const gchar *
+gimp_thumb_png_name (const gchar *uri)
+{
+ static gchar name[40];
+
+ GChecksum *checksum;
+ guchar digest[16];
+ gsize len = sizeof (digest);
+ gsize i;
+
+ checksum = g_checksum_new (G_CHECKSUM_MD5);
+ g_checksum_update (checksum, (const guchar *) uri, -1);
+ g_checksum_get_digest (checksum, digest, &len);
+ g_checksum_free (checksum);
+
+ for (i = 0; i < len; i++)
+ {
+ guchar n;
+
+ n = (digest[i] >> 4) & 0xF;
+ name[i * 2] = (n > 9) ? 'a' + n - 10 : '0' + n;
+
+ n = digest[i] & 0xF;
+ name[i * 2 + 1] = (n > 9) ? 'a' + n - 10 : '0' + n;
+ }
+
+ strncpy (name + 32, ".png", 5);
+
+ return (const gchar *) name;
+}
diff --git a/libgimpthumb/gimpthumb-utils.h b/libgimpthumb/gimpthumb-utils.h
new file mode 100644
index 0000000..7346e1a
--- /dev/null
+++ b/libgimpthumb/gimpthumb-utils.h
@@ -0,0 +1,73 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * Thumbnail handling according to the Thumbnail Managing Standard.
+ * https://specifications.freedesktop.org/thumbnail-spec/
+ *
+ * Copyright (C) 2001-2003 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_THUMB_H_INSIDE__) && !defined (GIMP_THUMB_COMPILATION)
+#error "Only <libgimpthumb/gimpthumb.h> can be included directly."
+#endif
+
+#ifndef __GIMP_THUMB_UTILS_H__
+#define __GIMP_THUMB_UTILS_H__
+
+G_BEGIN_DECLS
+
+
+gboolean gimp_thumb_init (const gchar *creator,
+ const gchar *thumb_basedir);
+
+const gchar * gimp_thumb_get_thumb_base_dir (void);
+
+gchar * gimp_thumb_find_thumb (const gchar *uri,
+ GimpThumbSize *size) G_GNUC_MALLOC;
+
+GimpThumbFileType gimp_thumb_file_test (const gchar *filename,
+ gint64 *mtime,
+ gint64 *size,
+ gint *err_no);
+
+gchar * gimp_thumb_name_from_uri (const gchar *uri,
+ GimpThumbSize size) G_GNUC_MALLOC;
+const gchar * gimp_thumb_get_thumb_dir (GimpThumbSize size);
+gboolean gimp_thumb_ensure_thumb_dir (GimpThumbSize size,
+ GError **error);
+void gimp_thumbs_delete_for_uri (const gchar *uri);
+
+gchar * gimp_thumb_name_from_uri_local (const gchar *uri,
+ GimpThumbSize size) G_GNUC_MALLOC;
+gchar * gimp_thumb_get_thumb_dir_local (const gchar *dirname,
+ GimpThumbSize size) G_GNUC_MALLOC;
+gboolean gimp_thumb_ensure_thumb_dir_local (const gchar *dirname,
+ GimpThumbSize size,
+ GError **error);
+void gimp_thumbs_delete_for_uri_local (const gchar *uri);
+
+
+/* for internal use only */
+G_GNUC_INTERNAL void _gimp_thumbs_delete_others (const gchar *uri,
+ GimpThumbSize size);
+G_GNUC_INTERNAL gchar * _gimp_thumb_filename_from_uri (const gchar *uri);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_THUMB_UTILS_H__ */
diff --git a/libgimpthumb/gimpthumb.def b/libgimpthumb/gimpthumb.def
new file mode 100644
index 0000000..db8c44d
--- /dev/null
+++ b/libgimpthumb/gimpthumb.def
@@ -0,0 +1,32 @@
+EXPORTS
+ gimp_thumb_ensure_thumb_dir
+ gimp_thumb_ensure_thumb_dir_local
+ gimp_thumb_error_quark
+ gimp_thumb_file_test
+ gimp_thumb_file_type_get_type
+ gimp_thumb_find_thumb
+ gimp_thumb_get_thumb_base_dir
+ gimp_thumb_get_thumb_dir
+ gimp_thumb_get_thumb_dir_local
+ gimp_thumb_init
+ gimp_thumb_name_from_uri
+ gimp_thumb_name_from_uri_local
+ gimp_thumb_size_get_type
+ gimp_thumb_state_get_type
+ gimp_thumbnail_check_thumb
+ gimp_thumbnail_delete_failure
+ gimp_thumbnail_delete_others
+ gimp_thumbnail_get_type
+ gimp_thumbnail_has_failed
+ gimp_thumbnail_load_thumb
+ gimp_thumbnail_new
+ gimp_thumbnail_peek_image
+ gimp_thumbnail_peek_thumb
+ gimp_thumbnail_save_failure
+ gimp_thumbnail_save_thumb
+ gimp_thumbnail_save_thumb_local
+ gimp_thumbnail_set_filename
+ gimp_thumbnail_set_from_thumb
+ gimp_thumbnail_set_uri
+ gimp_thumbs_delete_for_uri
+ gimp_thumbs_delete_for_uri_local
diff --git a/libgimpthumb/gimpthumb.h b/libgimpthumb/gimpthumb.h
new file mode 100644
index 0000000..97a038a
--- /dev/null
+++ b/libgimpthumb/gimpthumb.h
@@ -0,0 +1,38 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * Thumbnail handling according to the Thumbnail Managing Standard.
+ * https://specifications.freedesktop.org/thumbnail-spec/
+ *
+ * Copyright (C) 2001-2003 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_THUMB_H__
+#define __GIMP_THUMB_H__
+
+#define __GIMP_THUMB_H_INSIDE__
+
+#include <libgimpthumb/gimpthumb-types.h>
+
+#include <libgimpthumb/gimpthumb-error.h>
+#include <libgimpthumb/gimpthumb-utils.h>
+#include <libgimpthumb/gimpthumbnail.h>
+
+#undef __GIMP_THUMB_H_INSIDE__
+
+#endif /* __GIMP_THUMB_H__ */
diff --git a/libgimpthumb/gimpthumbnail.c b/libgimpthumb/gimpthumbnail.c
new file mode 100644
index 0000000..e7f9d3d
--- /dev/null
+++ b/libgimpthumb/gimpthumbnail.c
@@ -0,0 +1,1400 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * Thumbnail handling according to the Thumbnail Managing Standard.
+ * https://specifications.freedesktop.org/thumbnail-spec/
+ *
+ * Copyright (C) 2001-2004 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <glib/gstdio.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpbase/gimpparam.h"
+
+#ifdef G_OS_WIN32
+#include "libgimpbase/gimpwin32-io.h"
+#include <process.h>
+#define _getpid getpid
+#endif
+
+#include "gimpthumb-types.h"
+#include "gimpthumb-error.h"
+#include "gimpthumb-utils.h"
+#include "gimpthumbnail.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpthumbnail
+ * @title: GimpThumbnail
+ * @short_description: The GimpThumbnail object
+ *
+ * The GimpThumbnail object
+ **/
+
+
+/* #define GIMP_THUMB_DEBUG */
+
+
+#if defined (GIMP_THUMB_DEBUG) && defined (__GNUC__)
+#define GIMP_THUMB_DEBUG_CALL(t) \
+ g_printerr ("%s: %s\n", \
+ __FUNCTION__, t->image_uri ? t->image_uri : "(null)")
+#else
+#define GIMP_THUMB_DEBUG_CALL(t) ((void)(0))
+#endif
+
+
+#define TAG_DESCRIPTION "tEXt::Description"
+#define TAG_SOFTWARE "tEXt::Software"
+#define TAG_THUMB_URI "tEXt::Thumb::URI"
+#define TAG_THUMB_MTIME "tEXt::Thumb::MTime"
+#define TAG_THUMB_FILESIZE "tEXt::Thumb::Size"
+#define TAG_THUMB_MIMETYPE "tEXt::Thumb::Mimetype"
+#define TAG_THUMB_IMAGE_WIDTH "tEXt::Thumb::Image::Width"
+#define TAG_THUMB_IMAGE_HEIGHT "tEXt::Thumb::Image::Height"
+#define TAG_THUMB_GIMP_TYPE "tEXt::Thumb::X-GIMP::Type"
+#define TAG_THUMB_GIMP_LAYERS "tEXt::Thumb::X-GIMP::Layers"
+
+
+enum
+{
+ PROP_0,
+ PROP_IMAGE_STATE,
+ PROP_IMAGE_URI,
+ PROP_IMAGE_MTIME,
+ PROP_IMAGE_FILESIZE,
+ PROP_IMAGE_MIMETYPE,
+ PROP_IMAGE_WIDTH,
+ PROP_IMAGE_HEIGHT,
+ PROP_IMAGE_TYPE,
+ PROP_IMAGE_NUM_LAYERS,
+ PROP_THUMB_STATE
+};
+
+
+static void gimp_thumbnail_finalize (GObject *object);
+static void gimp_thumbnail_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_thumbnail_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_thumbnail_reset_info (GimpThumbnail *thumbnail);
+
+static void gimp_thumbnail_update_image (GimpThumbnail *thumbnail);
+static void gimp_thumbnail_update_thumb (GimpThumbnail *thumbnail,
+ GimpThumbSize size);
+
+static gboolean gimp_thumbnail_save (GimpThumbnail *thumbnail,
+ GimpThumbSize size,
+ const gchar *filename,
+ GdkPixbuf *pixbuf,
+ const gchar *software,
+ GError **error);
+#ifdef GIMP_THUMB_DEBUG
+static void gimp_thumbnail_debug_notify (GObject *object,
+ GParamSpec *pspec);
+#endif
+
+
+G_DEFINE_TYPE (GimpThumbnail, gimp_thumbnail, G_TYPE_OBJECT)
+
+#define parent_class gimp_thumbnail_parent_class
+
+
+static void
+gimp_thumbnail_class_init (GimpThumbnailClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gimp_thumbnail_finalize;
+ object_class->set_property = gimp_thumbnail_set_property;
+ object_class->get_property = gimp_thumbnail_get_property;
+
+ g_object_class_install_property (object_class,
+ PROP_IMAGE_STATE,
+ g_param_spec_enum ("image-state", NULL,
+ "State of the image associated to the thumbnail object",
+ GIMP_TYPE_THUMB_STATE,
+ GIMP_THUMB_STATE_UNKNOWN,
+ GIMP_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_IMAGE_URI,
+ g_param_spec_string ("image-uri", NULL,
+ "URI of the image file",
+ NULL,
+ GIMP_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_IMAGE_MTIME,
+ g_param_spec_int64 ("image-mtime", NULL,
+ "Modification time of the image file in seconds since the Epoch",
+ G_MININT64, G_MAXINT64, 0,
+ GIMP_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_IMAGE_FILESIZE,
+ g_param_spec_int64 ("image-filesize", NULL,
+ "Size of the image file in bytes",
+ 0, G_MAXINT64, 0,
+ GIMP_PARAM_READWRITE));
+ /**
+ * GimpThumbnail::image-mimetype:
+ *
+ * Image mimetype
+ *
+ * Since: 2.2
+ **/
+ g_object_class_install_property (object_class,
+ PROP_IMAGE_MIMETYPE,
+ g_param_spec_string ("image-mimetype", NULL,
+ "Image mimetype",
+ NULL,
+ GIMP_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_IMAGE_WIDTH,
+ g_param_spec_int ("image-width", NULL,
+ "Width of the image in pixels",
+ 0, G_MAXINT, 0,
+ GIMP_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_IMAGE_HEIGHT,
+ g_param_spec_int ("image-height", NULL,
+ "Height of the image in pixels",
+ 0, G_MAXINT, 0,
+ GIMP_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_IMAGE_TYPE,
+ g_param_spec_string ("image-type", NULL,
+ "String describing the type of the image format",
+ NULL,
+ GIMP_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_IMAGE_NUM_LAYERS,
+ g_param_spec_int ("image-num-layers", NULL,
+ "The number of layers in the image",
+ 0, G_MAXINT, 0,
+ GIMP_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_THUMB_STATE,
+ g_param_spec_enum ("thumb-state", NULL,
+ "State of the thumbnail file",
+ GIMP_TYPE_THUMB_STATE,
+ GIMP_THUMB_STATE_UNKNOWN,
+ GIMP_PARAM_READWRITE));
+}
+
+static void
+gimp_thumbnail_init (GimpThumbnail *thumbnail)
+{
+ thumbnail->image_state = GIMP_THUMB_STATE_UNKNOWN;
+ thumbnail->image_uri = NULL;
+ thumbnail->image_filename = NULL;
+ thumbnail->image_mtime = 0;
+ thumbnail->image_filesize = 0;
+ thumbnail->image_mimetype = NULL;
+ thumbnail->image_width = 0;
+ thumbnail->image_height = 0;
+ thumbnail->image_type = NULL;
+ thumbnail->image_num_layers = 0;
+
+ thumbnail->thumb_state = GIMP_THUMB_STATE_UNKNOWN;
+ thumbnail->thumb_size = -1;
+ thumbnail->thumb_filename = NULL;
+ thumbnail->thumb_mtime = 0;
+ thumbnail->thumb_filesize = 0;
+
+#ifdef GIMP_THUMB_DEBUG
+ g_signal_connect (thumbnail, "notify",
+ G_CALLBACK (gimp_thumbnail_debug_notify),
+ NULL);
+#endif
+}
+
+static void
+gimp_thumbnail_finalize (GObject *object)
+{
+ GimpThumbnail *thumbnail = GIMP_THUMBNAIL (object);
+
+ g_clear_pointer (&thumbnail->image_uri, g_free);
+ g_clear_pointer (&thumbnail->image_filename, g_free);
+ g_clear_pointer (&thumbnail->image_mimetype, g_free);
+ g_clear_pointer (&thumbnail->image_type, g_free);
+ g_clear_pointer (&thumbnail->thumb_filename, g_free);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_thumbnail_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpThumbnail *thumbnail = GIMP_THUMBNAIL (object);
+
+ switch (property_id)
+ {
+ case PROP_IMAGE_STATE:
+ thumbnail->image_state = g_value_get_enum (value);
+ break;
+ case PROP_IMAGE_URI:
+ gimp_thumbnail_set_uri (GIMP_THUMBNAIL (object),
+ g_value_get_string (value));
+ break;
+ case PROP_IMAGE_MTIME:
+ thumbnail->image_mtime = g_value_get_int64 (value);
+ break;
+ case PROP_IMAGE_FILESIZE:
+ thumbnail->image_filesize = g_value_get_int64 (value);
+ break;
+ case PROP_IMAGE_MIMETYPE:
+ g_free (thumbnail->image_mimetype);
+ thumbnail->image_mimetype = g_value_dup_string (value);
+ break;
+ case PROP_IMAGE_WIDTH:
+ thumbnail->image_width = g_value_get_int (value);
+ break;
+ case PROP_IMAGE_HEIGHT:
+ thumbnail->image_height = g_value_get_int (value);
+ break;
+ case PROP_IMAGE_TYPE:
+ g_free (thumbnail->image_type);
+ thumbnail->image_type = g_value_dup_string (value);
+ break;
+ case PROP_IMAGE_NUM_LAYERS:
+ thumbnail->image_num_layers = g_value_get_int (value);
+ break;
+ case PROP_THUMB_STATE:
+ thumbnail->thumb_state = g_value_get_enum (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_thumbnail_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpThumbnail *thumbnail = GIMP_THUMBNAIL (object);
+
+ switch (property_id)
+ {
+ case PROP_IMAGE_STATE:
+ g_value_set_enum (value, thumbnail->image_state);
+ break;
+ case PROP_IMAGE_URI:
+ g_value_set_string (value, thumbnail->image_uri);
+ break;
+ case PROP_IMAGE_MTIME:
+ g_value_set_int64 (value, thumbnail->image_mtime);
+ break;
+ case PROP_IMAGE_FILESIZE:
+ g_value_set_int64 (value, thumbnail->image_filesize);
+ break;
+ case PROP_IMAGE_MIMETYPE:
+ g_value_set_string (value, thumbnail->image_mimetype);
+ break;
+ case PROP_IMAGE_WIDTH:
+ g_value_set_int (value, thumbnail->image_width);
+ break;
+ case PROP_IMAGE_HEIGHT:
+ g_value_set_int (value, thumbnail->image_height);
+ break;
+ case PROP_IMAGE_TYPE:
+ g_value_set_string (value, thumbnail->image_type);
+ break;
+ case PROP_IMAGE_NUM_LAYERS:
+ g_value_set_int (value, thumbnail->image_num_layers);
+ break;
+ case PROP_THUMB_STATE:
+ g_value_set_enum (value, thumbnail->thumb_state);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+/**
+ * gimp_thumbnail_new:
+ *
+ * Creates a new #GimpThumbnail object.
+ *
+ * Return value: a newly allocated GimpThumbnail object
+ **/
+GimpThumbnail *
+gimp_thumbnail_new (void)
+{
+ return g_object_new (GIMP_TYPE_THUMBNAIL, NULL);
+}
+
+/**
+ * gimp_thumbnail_set_uri:
+ * @thumbnail: a #GimpThumbnail object
+ * @uri: an escaped URI
+ *
+ * Sets the location of the image file associated with the #thumbnail.
+ *
+ * All information stored in the #GimpThumbnail is reset.
+ **/
+void
+gimp_thumbnail_set_uri (GimpThumbnail *thumbnail,
+ const gchar *uri)
+{
+ g_return_if_fail (GIMP_IS_THUMBNAIL (thumbnail));
+
+ GIMP_THUMB_DEBUG_CALL (thumbnail);
+
+ if (thumbnail->image_uri)
+ g_free (thumbnail->image_uri);
+
+ thumbnail->image_uri = g_strdup (uri);
+
+ g_clear_pointer (&thumbnail->image_filename, g_free);
+ g_clear_pointer (&thumbnail->thumb_filename, g_free);
+
+ thumbnail->thumb_size = -1;
+ thumbnail->thumb_filesize = 0;
+ thumbnail->thumb_mtime = 0;
+
+ g_object_set (thumbnail,
+ "image-state", GIMP_THUMB_STATE_UNKNOWN,
+ "image-filesize", (gint64) 0,
+ "image-mtime", (gint64) 0,
+ "image-mimetype", NULL,
+ "image-width", 0,
+ "image-height", 0,
+ "image-type", NULL,
+ "image-num-layers", 0,
+ "thumb-state", GIMP_THUMB_STATE_UNKNOWN,
+ NULL);
+}
+
+/**
+ * gimp_thumbnail_set_filename:
+ * @thumbnail: a #GimpThumbnail object
+ * @filename: a local filename in the encoding of the filesystem
+ * @error: return location for possible errors
+ *
+ * Sets the location of the image file associated with the #thumbnail.
+ *
+ * Return value: %TRUE if the filename was successfully set,
+ * %FALSE otherwise
+ **/
+gboolean
+gimp_thumbnail_set_filename (GimpThumbnail *thumbnail,
+ const gchar *filename,
+ GError **error)
+{
+ gchar *uri = NULL;
+
+ g_return_val_if_fail (GIMP_IS_THUMBNAIL (thumbnail), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ GIMP_THUMB_DEBUG_CALL (thumbnail);
+
+ if (filename)
+ uri = g_filename_to_uri (filename, NULL, error);
+
+ gimp_thumbnail_set_uri (thumbnail, uri);
+
+ g_free (uri);
+
+ return (!filename || uri);
+}
+
+/**
+ * gimp_thumbnail_set_from_thumb:
+ * @thumbnail: a #GimpThumbnail object
+ * @filename: filename of a local thumbnail file
+ * @error: return location for possible errors
+ *
+ * This function tries to load the thumbnail file pointed to by
+ * @filename and retrieves the URI of the original image file from
+ * it. This allows you to find the image file associated with a
+ * thumbnail file.
+ *
+ * This will only work with thumbnails from the global thumbnail
+ * directory that contain a valid Thumb::URI tag.
+ *
+ * Return value: %TRUE if the pixbuf could be loaded, %FALSE otherwise
+ **/
+gboolean
+gimp_thumbnail_set_from_thumb (GimpThumbnail *thumbnail,
+ const gchar *filename,
+ GError **error)
+{
+ GdkPixbuf *pixbuf;
+ const gchar *uri;
+
+ g_return_val_if_fail (GIMP_IS_THUMBNAIL (thumbnail), FALSE);
+ g_return_val_if_fail (filename != NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ GIMP_THUMB_DEBUG_CALL (thumbnail);
+
+ pixbuf = gdk_pixbuf_new_from_file (filename, error);
+ if (! pixbuf)
+ return FALSE;
+
+ uri = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_URI);
+ if (! uri)
+ {
+ g_set_error (error, GIMP_THUMB_ERROR, 0,
+ _("Thumbnail contains no Thumb::URI tag"));
+ g_object_unref (pixbuf);
+ return FALSE;
+ }
+
+ gimp_thumbnail_set_uri (thumbnail, uri);
+ g_object_unref (pixbuf);
+
+ return TRUE;
+}
+
+/**
+ * gimp_thumbnail_peek_image:
+ * @thumbnail: a #GimpThumbnail object
+ *
+ * Checks the image file associated with the @thumbnail and updates
+ * information such as state, filesize and modification time.
+ *
+ * Return value: the image's #GimpThumbState after the update
+ **/
+GimpThumbState
+gimp_thumbnail_peek_image (GimpThumbnail *thumbnail)
+{
+ g_return_val_if_fail (GIMP_IS_THUMBNAIL (thumbnail),
+ GIMP_THUMB_STATE_UNKNOWN);
+
+ GIMP_THUMB_DEBUG_CALL (thumbnail);
+
+ g_object_freeze_notify (G_OBJECT (thumbnail));
+
+ gimp_thumbnail_update_image (thumbnail);
+
+ g_object_thaw_notify (G_OBJECT (thumbnail));
+
+ return thumbnail->image_state;
+}
+
+/**
+ * gimp_thumbnail_peek_thumb:
+ * @thumbnail: a #GimpThumbnail object
+ * @size: the preferred size of the thumbnail image
+ *
+ * Checks if a thumbnail file for the @thumbnail exists. It doesn't
+ * load the thumbnail image and thus cannot check if the thumbnail is
+ * valid and uptodate for the image file asosciated with the
+ * @thumbnail.
+ *
+ * If you want to check the thumbnail, either attempt to load it using
+ * gimp_thumbnail_load_thumb(), or, if you don't need the resulting
+ * thumbnail pixbuf, use gimp_thumbnail_check_thumb().
+ *
+ * Return value: the thumbnail's #GimpThumbState after the update
+ **/
+GimpThumbState
+gimp_thumbnail_peek_thumb (GimpThumbnail *thumbnail,
+ GimpThumbSize size)
+{
+ g_return_val_if_fail (GIMP_IS_THUMBNAIL (thumbnail),
+ GIMP_THUMB_STATE_UNKNOWN);
+
+ GIMP_THUMB_DEBUG_CALL (thumbnail);
+
+ g_object_freeze_notify (G_OBJECT (thumbnail));
+
+ gimp_thumbnail_update_image (thumbnail);
+ gimp_thumbnail_update_thumb (thumbnail, size);
+
+ g_object_thaw_notify (G_OBJECT (thumbnail));
+
+ return thumbnail->thumb_state;
+}
+
+/**
+ * gimp_thumbnail_check_thumb:
+ * @thumbnail: a #GimpThumbnail object
+ * @size: the preferred size of the thumbnail image
+ *
+ * Checks if a thumbnail file for the @thumbnail exists, loads it and
+ * verifies it is valid and uptodate for the image file asosciated
+ * with the @thumbnail.
+ *
+ * Return value: the thumbnail's #GimpThumbState after the update
+ *
+ * Since: 2.2
+ **/
+GimpThumbState
+gimp_thumbnail_check_thumb (GimpThumbnail *thumbnail,
+ GimpThumbSize size)
+{
+ GdkPixbuf *pixbuf;
+
+ g_return_val_if_fail (GIMP_IS_THUMBNAIL (thumbnail), FALSE);
+
+ GIMP_THUMB_DEBUG_CALL (thumbnail);
+
+ if (gimp_thumbnail_peek_thumb (thumbnail, size) == GIMP_THUMB_STATE_OK)
+ return GIMP_THUMB_STATE_OK;
+
+ pixbuf = gimp_thumbnail_load_thumb (thumbnail, size, NULL);
+
+ if (pixbuf)
+ g_object_unref (pixbuf);
+
+ return thumbnail->thumb_state;
+}
+
+static void
+gimp_thumbnail_update_image (GimpThumbnail *thumbnail)
+{
+ GimpThumbState state;
+ gint64 mtime = 0;
+ gint64 filesize = 0;
+
+ if (! thumbnail->image_uri)
+ return;
+
+ state = thumbnail->image_state;
+
+ switch (state)
+ {
+ case GIMP_THUMB_STATE_UNKNOWN:
+ g_return_if_fail (thumbnail->image_filename == NULL);
+
+ thumbnail->image_filename =
+ _gimp_thumb_filename_from_uri (thumbnail->image_uri);
+
+ if (! thumbnail->image_filename)
+ state = GIMP_THUMB_STATE_REMOTE;
+
+ break;
+
+ case GIMP_THUMB_STATE_REMOTE:
+ break;
+
+ default:
+ g_return_if_fail (thumbnail->image_filename != NULL);
+ break;
+ }
+
+ switch (state)
+ {
+ case GIMP_THUMB_STATE_REMOTE:
+ break;
+
+ default:
+ switch (gimp_thumb_file_test (thumbnail->image_filename,
+ &mtime, &filesize,
+ &thumbnail->image_not_found_errno))
+ {
+ case GIMP_THUMB_FILE_TYPE_REGULAR:
+ state = GIMP_THUMB_STATE_EXISTS;
+ break;
+
+ case GIMP_THUMB_FILE_TYPE_FOLDER:
+ state = GIMP_THUMB_STATE_FOLDER;
+ break;
+
+ case GIMP_THUMB_FILE_TYPE_SPECIAL:
+ state = GIMP_THUMB_STATE_SPECIAL;
+ break;
+
+ default:
+ state = GIMP_THUMB_STATE_NOT_FOUND;
+ break;
+ }
+ break;
+ }
+
+ if (state != thumbnail->image_state)
+ {
+ g_object_set (thumbnail,
+ "image-state", state,
+ NULL);
+ }
+
+ if (mtime != thumbnail->image_mtime || filesize != thumbnail->image_filesize)
+ {
+ g_object_set (thumbnail,
+ "image-mtime", mtime,
+ "image-filesize", filesize,
+ NULL);
+
+ if (thumbnail->thumb_state == GIMP_THUMB_STATE_OK)
+ g_object_set (thumbnail,
+ "thumb-state", GIMP_THUMB_STATE_OLD,
+ NULL);
+ }
+}
+
+static void
+gimp_thumbnail_update_thumb (GimpThumbnail *thumbnail,
+ GimpThumbSize size)
+{
+ gchar *filename;
+ GimpThumbState state;
+ gint64 filesize = 0;
+ gint64 mtime = 0;
+
+ if (! thumbnail->image_uri)
+ return;
+
+ state = thumbnail->thumb_state;
+
+ filename = gimp_thumb_find_thumb (thumbnail->image_uri, &size);
+
+ /* We don't want to clear the GIMP_THUMB_STATE_FAILED state, because
+ * it is normal to have no filename if thumbnail creation failed. */
+ if (state != GIMP_THUMB_STATE_FAILED && ! filename)
+ state = GIMP_THUMB_STATE_NOT_FOUND;
+
+ switch (state)
+ {
+ case GIMP_THUMB_STATE_EXISTS:
+ case GIMP_THUMB_STATE_OLD:
+ case GIMP_THUMB_STATE_OK:
+ g_return_if_fail (thumbnail->thumb_filename != NULL);
+
+ if (thumbnail->thumb_size == size &&
+ thumbnail->thumb_filesize == filesize &&
+ thumbnail->thumb_mtime == mtime)
+ {
+ g_free (filename);
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (thumbnail->thumb_filename)
+ g_free (thumbnail->thumb_filename);
+
+ thumbnail->thumb_filename = filename;
+
+ if (filename)
+ state = (size > GIMP_THUMB_SIZE_FAIL ?
+ GIMP_THUMB_STATE_EXISTS : GIMP_THUMB_STATE_FAILED);
+
+ thumbnail->thumb_size = size;
+ thumbnail->thumb_filesize = filesize;
+ thumbnail->thumb_mtime = mtime;
+
+ if (state != thumbnail->thumb_state)
+ {
+ g_object_freeze_notify (G_OBJECT (thumbnail));
+
+ g_object_set (thumbnail, "thumb-state", state, NULL);
+ gimp_thumbnail_reset_info (thumbnail);
+
+ g_object_thaw_notify (G_OBJECT (thumbnail));
+ }
+}
+
+static void
+gimp_thumbnail_reset_info (GimpThumbnail *thumbnail)
+{
+ g_object_set (thumbnail,
+ "image-width", 0,
+ "image-height", 0,
+ "image-type", NULL,
+ "image-num-layers", 0,
+ NULL);
+}
+
+static void
+gimp_thumbnail_set_info_from_pixbuf (GimpThumbnail *thumbnail,
+ GdkPixbuf *pixbuf)
+{
+ const gchar *option;
+ gint num;
+
+ g_object_freeze_notify (G_OBJECT (thumbnail));
+
+ gimp_thumbnail_reset_info (thumbnail);
+
+ g_free (thumbnail->image_mimetype);
+ thumbnail->image_mimetype =
+ g_strdup (gdk_pixbuf_get_option (pixbuf, TAG_THUMB_MIMETYPE));
+
+ option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_IMAGE_WIDTH);
+ if (option && sscanf (option, "%d", &num) == 1)
+ thumbnail->image_width = num;
+
+ option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_IMAGE_HEIGHT);
+ if (option && sscanf (option, "%d", &num) == 1)
+ thumbnail->image_height = num;
+
+ thumbnail->image_type =
+ g_strdup (gdk_pixbuf_get_option (pixbuf, TAG_THUMB_GIMP_TYPE));
+
+ option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_GIMP_LAYERS);
+ if (option && sscanf (option, "%d", &num) == 1)
+ thumbnail->image_num_layers = num;
+
+ g_object_thaw_notify (G_OBJECT (thumbnail));
+}
+
+static gboolean
+gimp_thumbnail_save (GimpThumbnail *thumbnail,
+ GimpThumbSize size,
+ const gchar *filename,
+ GdkPixbuf *pixbuf,
+ const gchar *software,
+ GError **error)
+{
+ const gchar *keys[12];
+ gchar *values[12];
+ gchar *basename;
+ gchar *dirname;
+ gchar *tmpname;
+ gboolean success;
+ gint i = 0;
+
+ keys[i] = TAG_DESCRIPTION;
+ values[i] = g_strdup_printf ("Thumbnail of %s", thumbnail->image_uri);
+ i++;
+
+ keys[i] = TAG_SOFTWARE;
+ values[i] = g_strdup (software);
+ i++;
+
+ keys[i] = TAG_THUMB_URI;
+ values[i] = g_strdup (thumbnail->image_uri);
+ i++;
+
+ keys[i] = TAG_THUMB_MTIME;
+ values[i] = g_strdup_printf ("%" G_GINT64_FORMAT, thumbnail->image_mtime);
+ i++;
+
+ keys[i] = TAG_THUMB_FILESIZE;
+ values[i] = g_strdup_printf ("%" G_GINT64_FORMAT, thumbnail->image_filesize);
+ i++;
+
+ if (thumbnail->image_mimetype)
+ {
+ keys[i] = TAG_THUMB_MIMETYPE;
+ values[i] = g_strdup (thumbnail->image_mimetype);
+ i++;
+ }
+
+ if (thumbnail->image_width > 0)
+ {
+ keys[i] = TAG_THUMB_IMAGE_WIDTH;
+ values[i] = g_strdup_printf ("%d", thumbnail->image_width);
+ i++;
+ }
+
+ if (thumbnail->image_height > 0)
+ {
+ keys[i] = TAG_THUMB_IMAGE_HEIGHT;
+ values[i] = g_strdup_printf ("%d", thumbnail->image_height);
+ i++;
+ }
+
+ if (thumbnail->image_type)
+ {
+ keys[i] = TAG_THUMB_GIMP_TYPE;
+ values[i] = g_strdup (thumbnail->image_type);
+ i++;
+ }
+
+ if (thumbnail->image_num_layers > 0)
+ {
+ keys[i] = TAG_THUMB_GIMP_LAYERS;
+ values[i] = g_strdup_printf ("%d", thumbnail->image_num_layers);
+ i++;
+ }
+
+ keys[i] = NULL;
+ values[i] = NULL;
+
+ basename = g_path_get_basename (filename);
+ dirname = g_path_get_dirname (filename);
+
+ tmpname = g_strdup_printf ("%s%cgimp-thumb-%d-%.8s",
+ dirname, G_DIR_SEPARATOR, getpid (), basename);
+
+ g_free (dirname);
+ g_free (basename);
+
+ success = gdk_pixbuf_savev (pixbuf, tmpname, "png",
+ (gchar **) keys, values,
+ error);
+
+ for (i = 0; keys[i]; i++)
+ g_free (values[i]);
+
+ if (success)
+ {
+#ifdef GIMP_THUMB_DEBUG
+ g_printerr ("thumbnail saved to temporary file %s\n", tmpname);
+#endif
+
+ success = (g_rename (tmpname, filename) == 0);
+
+ if (! success)
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not create thumbnail for %s: %s"),
+ thumbnail->image_uri, g_strerror (errno));
+ }
+
+ if (success)
+ {
+#ifdef GIMP_THUMB_DEBUG
+ g_printerr ("temporary thumbnail file renamed to %s\n", filename);
+#endif
+
+ success = (g_chmod (filename, 0600) == 0);
+
+ if (! success)
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ "Could not set permissions of thumbnail for %s: %s",
+ thumbnail->image_uri, g_strerror (errno));
+
+ g_object_freeze_notify (G_OBJECT (thumbnail));
+
+ gimp_thumbnail_update_thumb (thumbnail, size);
+
+ if (success &&
+ thumbnail->thumb_state == GIMP_THUMB_STATE_EXISTS &&
+ strcmp (filename, thumbnail->thumb_filename) == 0)
+ {
+ thumbnail->thumb_state = GIMP_THUMB_STATE_OK;
+ }
+
+ g_object_thaw_notify (G_OBJECT (thumbnail));
+ }
+
+ g_unlink (tmpname);
+ g_free (tmpname);
+
+ return success;
+}
+
+#ifdef GIMP_THUMB_DEBUG
+static void
+gimp_thumbnail_debug_notify (GObject *object,
+ GParamSpec *pspec)
+{
+ GValue value = G_VALUE_INIT;
+ gchar *str = NULL;
+ const gchar *name;
+
+ g_value_init (&value, pspec->value_type);
+ g_object_get_property (object, pspec->name, &value);
+
+ if (G_VALUE_HOLDS_STRING (&value))
+ {
+ str = g_value_dup_string (&value);
+ }
+ else if (g_value_type_transformable (pspec->value_type, G_TYPE_STRING))
+ {
+ GValue tmp = G_VALUE_INIT;
+
+ g_value_init (&tmp, G_TYPE_STRING);
+ g_value_transform (&value, &tmp);
+
+ str = g_value_dup_string (&tmp);
+
+ g_value_unset (&tmp);
+ }
+
+ g_value_unset (&value);
+
+ name = GIMP_THUMBNAIL (object)->image_uri;
+
+ g_printerr (" GimpThumb (%s) %s: %s\n",
+ name ? name : "(null)", pspec->name, str);
+
+ g_free (str);
+}
+#endif
+
+
+/**
+ * gimp_thumbnail_load_thumb:
+ * @thumbnail: a #GimpThumbnail object
+ * @size: the preferred #GimpThumbSize for the preview
+ * @error: return location for possible errors
+ *
+ * Attempts to load a thumbnail preview for the image associated with
+ * @thumbnail. Before you use this function you need need to set an
+ * image location using gimp_thumbnail_set_uri() or
+ * gimp_thumbnail_set_filename(). You can also peek at the thumb
+ * before loading it using gimp_thumbnail_peek_thumb.
+ *
+ * This function will return the best matching pixbuf for the
+ * specified @size. It returns the pixbuf as loaded from disk. It is
+ * left to the caller to scale it to the desired size. The returned
+ * pixbuf may also represent an outdated preview of the image file.
+ * In order to verify if the preview is uptodate, you should check the
+ * "thumb_state" property after calling this function.
+ *
+ * Return value: a preview pixbuf or %NULL if no thumbnail was found
+ **/
+GdkPixbuf *
+gimp_thumbnail_load_thumb (GimpThumbnail *thumbnail,
+ GimpThumbSize size,
+ GError **error)
+{
+ GimpThumbState state;
+ GdkPixbuf *pixbuf;
+ const gchar *option;
+ gint64 image_mtime;
+ gint64 image_size;
+
+ g_return_val_if_fail (GIMP_IS_THUMBNAIL (thumbnail), NULL);
+
+ GIMP_THUMB_DEBUG_CALL (thumbnail);
+
+ if (! thumbnail->image_uri)
+ return NULL;
+
+ state = gimp_thumbnail_peek_thumb (thumbnail, size);
+
+ if (state < GIMP_THUMB_STATE_EXISTS || state == GIMP_THUMB_STATE_FAILED)
+ return NULL;
+
+ pixbuf = gdk_pixbuf_new_from_file (thumbnail->thumb_filename, NULL);
+ if (! pixbuf)
+ return NULL;
+
+#ifdef GIMP_THUMB_DEBUG
+ g_printerr ("thumbnail loaded from %s\n", thumbnail->thumb_filename);
+#endif
+
+ g_object_freeze_notify (G_OBJECT (thumbnail));
+
+ /* URI and mtime from the thumbnail need to match our file */
+ option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_URI);
+ if (!option)
+ goto finish;
+
+ if (strcmp (option, thumbnail->image_uri))
+ {
+ /* might be a local thumbnail, try if the local part matches */
+ const gchar *baseuri = strrchr (thumbnail->image_uri, '/');
+
+ if (!baseuri || strcmp (option, baseuri))
+ goto finish;
+ }
+
+ state = GIMP_THUMB_STATE_OLD;
+
+ option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_MTIME);
+ if (!option || sscanf (option, "%" G_GINT64_FORMAT, &image_mtime) != 1)
+ goto finish;
+
+ option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_FILESIZE);
+ if (option && sscanf (option, "%" G_GINT64_FORMAT, &image_size) != 1)
+ goto finish;
+
+ /* TAG_THUMB_FILESIZE is optional but must match if present */
+ if (image_mtime == thumbnail->image_mtime &&
+ (option == NULL || image_size == thumbnail->image_filesize))
+ {
+ if (thumbnail->thumb_size == GIMP_THUMB_SIZE_FAIL)
+ state = GIMP_THUMB_STATE_FAILED;
+ else
+ state = GIMP_THUMB_STATE_OK;
+ }
+
+ if (state == GIMP_THUMB_STATE_FAILED)
+ gimp_thumbnail_reset_info (thumbnail);
+ else
+ gimp_thumbnail_set_info_from_pixbuf (thumbnail, pixbuf);
+
+ finish:
+ if (thumbnail->thumb_size == GIMP_THUMB_SIZE_FAIL ||
+ (state != GIMP_THUMB_STATE_OLD && state != GIMP_THUMB_STATE_OK))
+ {
+ g_object_unref (pixbuf);
+ pixbuf = NULL;
+ }
+
+ g_object_set (thumbnail,
+ "thumb-state", state,
+ NULL);
+
+ g_object_thaw_notify (G_OBJECT (thumbnail));
+
+ return pixbuf;
+}
+
+/**
+ * gimp_thumbnail_save_thumb:
+ * @thumbnail: a #GimpThumbnail object
+ * @pixbuf: a #GdkPixbuf representing the preview thumbnail
+ * @software: a string describing the software saving the thumbnail
+ * @error: return location for possible errors
+ *
+ * Saves a preview thumbnail for the image associated with @thumbnail.
+ * to the global thumbnail repository.
+ *
+ * The caller is responsible for setting the image file location, it's
+ * filesize, modification time. One way to set this info is to is to
+ * call gimp_thumbnail_set_uri() followed by gimp_thumbnail_peek_image().
+ * Since this won't work for remote images, it is left to the user of
+ * gimp_thumbnail_save_thumb() to do this or to set the information
+ * using the @thumbnail object properties.
+ *
+ * The image format type and the number of layers can optionally be
+ * set in order to be stored with the preview image.
+ *
+ * Return value: %TRUE if a thumbnail was successfully written,
+ * %FALSE otherwise
+ **/
+gboolean
+gimp_thumbnail_save_thumb (GimpThumbnail *thumbnail,
+ GdkPixbuf *pixbuf,
+ const gchar *software,
+ GError **error)
+{
+ GimpThumbSize size;
+ gchar *name;
+ gboolean success;
+
+ g_return_val_if_fail (GIMP_IS_THUMBNAIL (thumbnail), FALSE);
+ g_return_val_if_fail (thumbnail->image_uri != NULL, FALSE);
+ g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), FALSE);
+ g_return_val_if_fail (software != NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ GIMP_THUMB_DEBUG_CALL (thumbnail);
+
+ size = MAX (gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf));
+ if (size < 1)
+ return TRUE;
+
+ name = gimp_thumb_name_from_uri (thumbnail->image_uri, size);
+ if (! name)
+ return TRUE;
+
+ if (! gimp_thumb_ensure_thumb_dir (size, error))
+ {
+ g_free (name);
+ return FALSE;
+ }
+
+ success = gimp_thumbnail_save (thumbnail,
+ size, name, pixbuf, software,
+ error);
+ g_free (name);
+
+ return success;
+}
+
+/**
+ * gimp_thumbnail_save_thumb_local:
+ * @thumbnail: a #GimpThumbnail object
+ * @pixbuf: a #GdkPixbuf representing the preview thumbnail
+ * @software: a string describing the software saving the thumbnail
+ * @error: return location for possible errors
+ *
+ * Saves a preview thumbnail for the image associated with @thumbnail
+ * to the local thumbnail repository. Local thumbnails have been added
+ * with version 0.7 of the spec.
+ *
+ * Please see also gimp_thumbnail_save_thumb(). The notes made there
+ * apply here as well.
+ *
+ * Return value: %TRUE if a thumbnail was successfully written,
+ * %FALSE otherwise
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_thumbnail_save_thumb_local (GimpThumbnail *thumbnail,
+ GdkPixbuf *pixbuf,
+ const gchar *software,
+ GError **error)
+{
+ GimpThumbSize size;
+ gchar *name;
+ gchar *filename;
+ gchar *dirname;
+ gboolean success;
+
+ g_return_val_if_fail (GIMP_IS_THUMBNAIL (thumbnail), FALSE);
+ g_return_val_if_fail (thumbnail->image_uri != NULL, FALSE);
+ g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), FALSE);
+ g_return_val_if_fail (software != NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ GIMP_THUMB_DEBUG_CALL (thumbnail);
+
+ size = MAX (gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf));
+ if (size < 1)
+ return TRUE;
+
+ filename = _gimp_thumb_filename_from_uri (thumbnail->image_uri);
+ if (! filename)
+ return TRUE;
+
+ dirname = g_path_get_dirname (filename);
+ g_free (filename);
+
+ name = gimp_thumb_name_from_uri_local (thumbnail->image_uri, size);
+ if (! name)
+ {
+ g_free (dirname);
+ return TRUE;
+ }
+
+ if (! gimp_thumb_ensure_thumb_dir_local (dirname, size, error))
+ {
+ g_free (name);
+ g_free (dirname);
+ return FALSE;
+ }
+
+ g_free (dirname);
+
+ success = gimp_thumbnail_save (thumbnail,
+ size, name, pixbuf, software,
+ error);
+ g_free (name);
+
+ return success;
+}
+
+/**
+ * gimp_thumbnail_save_failure:
+ * @thumbnail: a #GimpThumbnail object
+ * @software: a string describing the software saving the thumbnail
+ * @error: return location for possible errors
+ *
+ * Saves a failure thumbnail for the image associated with
+ * @thumbnail. This is an empty pixbuf that indicates that an attempt
+ * to create a preview for the image file failed. It should be used to
+ * prevent the software from further attempts to create this thumbnail.
+ *
+ * Return value: %TRUE if a failure thumbnail was successfully written,
+ * %FALSE otherwise
+ **/
+gboolean
+gimp_thumbnail_save_failure (GimpThumbnail *thumbnail,
+ const gchar *software,
+ GError **error)
+{
+ GdkPixbuf *pixbuf;
+ gchar *name;
+ gchar *desc;
+ gchar *time_str;
+ gchar *size_str;
+ gboolean success;
+
+ g_return_val_if_fail (GIMP_IS_THUMBNAIL (thumbnail), FALSE);
+ g_return_val_if_fail (thumbnail->image_uri != NULL, FALSE);
+ g_return_val_if_fail (software != NULL, FALSE);
+
+ GIMP_THUMB_DEBUG_CALL (thumbnail);
+
+ name = gimp_thumb_name_from_uri (thumbnail->image_uri, GIMP_THUMB_SIZE_FAIL);
+ if (! name)
+ return TRUE;
+
+ if (! gimp_thumb_ensure_thumb_dir (GIMP_THUMB_SIZE_FAIL, error))
+ {
+ g_free (name);
+ return FALSE;
+ }
+
+ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 1, 1);
+
+ desc = g_strdup_printf ("Thumbnail failure for %s", thumbnail->image_uri);
+ time_str = g_strdup_printf ("%" G_GINT64_FORMAT, thumbnail->image_mtime);
+ size_str = g_strdup_printf ("%" G_GINT64_FORMAT, thumbnail->image_filesize);
+
+ success = gdk_pixbuf_save (pixbuf, name, "png", error,
+ TAG_DESCRIPTION, desc,
+ TAG_SOFTWARE, software,
+ TAG_THUMB_URI, thumbnail->image_uri,
+ TAG_THUMB_MTIME, time_str,
+ TAG_THUMB_FILESIZE, size_str,
+ NULL);
+ if (success)
+ {
+ success = (g_chmod (name, 0600) == 0);
+
+ if (success)
+ gimp_thumbnail_update_thumb (thumbnail, GIMP_THUMB_SIZE_NORMAL);
+ else
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ "Could not set permissions of thumbnail '%s': %s",
+ name, g_strerror (errno));
+ }
+
+ g_object_unref (pixbuf);
+
+ g_free (size_str);
+ g_free (time_str);
+ g_free (desc);
+ g_free (name);
+
+ return success;
+}
+
+/**
+ * gimp_thumbnail_delete_failure:
+ * @thumbnail: a #GimpThumbnail object
+ *
+ * Removes a failure thumbnail if one exists. This function should be
+ * used after a thumbnail has been successfully created.
+ *
+ * Since: 2.2
+ **/
+void
+gimp_thumbnail_delete_failure (GimpThumbnail *thumbnail)
+{
+ gchar *filename;
+
+ g_return_if_fail (GIMP_IS_THUMBNAIL (thumbnail));
+ g_return_if_fail (thumbnail->image_uri != NULL);
+
+ GIMP_THUMB_DEBUG_CALL (thumbnail);
+
+ filename = gimp_thumb_name_from_uri (thumbnail->image_uri,
+ GIMP_THUMB_SIZE_FAIL);
+ if (filename)
+ {
+ g_unlink (filename);
+ g_free (filename);
+ }
+}
+
+/**
+ * gimp_thumbnail_delete_others:
+ * @thumbnail: a #GimpThumbnail object
+ * @size: the thumbnail size which should not be deleted
+ *
+ * Removes all other thumbnails from the global thumbnail
+ * repository. Only the thumbnail for @size is not deleted. This
+ * function should be used after a thumbnail has been successfully
+ * updated. See the spec for a more detailed description on when to
+ * delete thumbnails.
+ *
+ * Since: 2.2
+ **/
+void
+gimp_thumbnail_delete_others (GimpThumbnail *thumbnail,
+ GimpThumbSize size)
+{
+ g_return_if_fail (GIMP_IS_THUMBNAIL (thumbnail));
+ g_return_if_fail (thumbnail->image_uri != NULL);
+
+ GIMP_THUMB_DEBUG_CALL (thumbnail);
+
+ _gimp_thumbs_delete_others (thumbnail->image_uri, size);
+}
+
+/**
+ * gimp_thumbnail_has_failed:
+ * @thumbnail: a #GimpThumbnail object
+ *
+ * Checks if a valid failure thumbnail for the given thumbnail exists
+ * in the global thumbnail repository. This may be the case even if
+ * gimp_thumbnail_peek_thumb() doesn't return %GIMP_THUMB_STATE_FAILED
+ * since there might be a real thumbnail and a failure thumbnail for
+ * the same image file.
+ *
+ * The application should not attempt to create the thumbnail if a
+ * valid failure thumbnail exists.
+ *
+ * Return value: %TRUE if a failure thumbnail exists or
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_thumbnail_has_failed (GimpThumbnail *thumbnail)
+{
+ GdkPixbuf *pixbuf;
+ const gchar *option;
+ gchar *filename;
+ gint64 image_mtime;
+ gint64 image_size;
+ gboolean failed = FALSE;
+
+ g_return_val_if_fail (GIMP_IS_THUMBNAIL (thumbnail), FALSE);
+ g_return_val_if_fail (thumbnail->image_uri != NULL, FALSE);
+
+ GIMP_THUMB_DEBUG_CALL (thumbnail);
+
+ filename = gimp_thumb_name_from_uri (thumbnail->image_uri,
+ GIMP_THUMB_SIZE_FAIL);
+ if (! filename)
+ return FALSE;
+
+ pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
+ g_free (filename);
+
+ if (! pixbuf)
+ return FALSE;
+
+ if (gimp_thumbnail_peek_image (thumbnail) < GIMP_THUMB_STATE_EXISTS)
+ goto finish;
+
+ /* URI and mtime from the thumbnail need to match our file */
+ option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_URI);
+ if (! option || strcmp (option, thumbnail->image_uri))
+ goto finish;
+
+ option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_MTIME);
+ if (!option || sscanf (option, "%" G_GINT64_FORMAT, &image_mtime) != 1)
+ goto finish;
+
+ option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_FILESIZE);
+ if (option && sscanf (option, "%" G_GINT64_FORMAT, &image_size) != 1)
+ goto finish;
+
+ /* TAG_THUMB_FILESIZE is optional but must match if present */
+ if (image_mtime == thumbnail->image_mtime &&
+ (option == NULL || image_size == thumbnail->image_filesize))
+ {
+ failed = TRUE;
+ }
+
+ finish:
+ g_object_unref (pixbuf);
+
+ return failed;
+}
diff --git a/libgimpthumb/gimpthumbnail.h b/libgimpthumb/gimpthumbnail.h
new file mode 100644
index 0000000..2099ff0
--- /dev/null
+++ b/libgimpthumb/gimpthumbnail.h
@@ -0,0 +1,135 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * Thumbnail handling according to the Thumbnail Managing Standard.
+ * https://specifications.freedesktop.org/thumbnail-spec/
+ *
+ * Copyright (C) 2001-2004 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_THUMB_H_INSIDE__) && !defined (GIMP_THUMB_COMPILATION)
+#error "Only <libgimpthumb/gimpthumb.h> can be included directly."
+#endif
+
+#ifndef __GIMP_THUMBNAIL_H__
+#define __GIMP_THUMBNAIL_H__
+
+G_BEGIN_DECLS
+
+
+#define GIMP_TYPE_THUMBNAIL (gimp_thumbnail_get_type ())
+#define GIMP_THUMBNAIL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_THUMBNAIL, GimpThumbnail))
+#define GIMP_THUMBNAIL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_THUMBNAIL, GimpThumbnailClass))
+#define GIMP_IS_THUMBNAIL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_THUMBNAIL))
+#define GIMP_IS_THUMBNAIL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_THUMBNAIL))
+#define GIMP_THUMBNAIL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_THUMBNAIL, GimpThumbnailClass))
+
+
+typedef struct _GimpThumbnailClass GimpThumbnailClass;
+
+/**
+ * GimpThumbnail:
+ *
+ * All members of #GimpThumbnail are private and should only be accessed
+ * using object properties.
+ **/
+struct _GimpThumbnail
+{
+ GObject parent_instance;
+
+ /*< private >*/
+ GimpThumbState image_state;
+ gchar *image_uri;
+ gchar *image_filename;
+ gint64 image_filesize;
+ gint64 image_mtime;
+ gint image_not_found_errno;
+ gint image_width;
+ gint image_height;
+ gchar *image_type;
+ gint image_num_layers;
+
+ GimpThumbState thumb_state;
+ GimpThumbSize thumb_size;
+ gchar *thumb_filename;
+ gint64 thumb_filesize;
+ gint64 thumb_mtime;
+
+ gchar *image_mimetype;
+
+ gpointer _reserved_2;
+};
+
+struct _GimpThumbnailClass
+{
+ GObjectClass parent_class;
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_thumbnail_get_type (void) G_GNUC_CONST;
+
+GimpThumbnail * gimp_thumbnail_new (void);
+
+void gimp_thumbnail_set_uri (GimpThumbnail *thumbnail,
+ const gchar *uri);
+gboolean gimp_thumbnail_set_filename (GimpThumbnail *thumbnail,
+ const gchar *filename,
+ GError **error);
+gboolean gimp_thumbnail_set_from_thumb (GimpThumbnail *thumbnail,
+ const gchar *filename,
+ GError **error);
+
+GimpThumbState gimp_thumbnail_peek_image (GimpThumbnail *thumbnail);
+GimpThumbState gimp_thumbnail_peek_thumb (GimpThumbnail *thumbnail,
+ GimpThumbSize size);
+
+GimpThumbState gimp_thumbnail_check_thumb (GimpThumbnail *thumbnail,
+ GimpThumbSize size);
+
+GdkPixbuf * gimp_thumbnail_load_thumb (GimpThumbnail *thumbnail,
+ GimpThumbSize size,
+ GError **error);
+
+gboolean gimp_thumbnail_save_thumb (GimpThumbnail *thumbnail,
+ GdkPixbuf *pixbuf,
+ const gchar *software,
+ GError **error);
+gboolean gimp_thumbnail_save_thumb_local (GimpThumbnail *thumbnail,
+ GdkPixbuf *pixbuf,
+ const gchar *software,
+ GError **error);
+
+gboolean gimp_thumbnail_save_failure (GimpThumbnail *thumbnail,
+ const gchar *software,
+ GError **error);
+void gimp_thumbnail_delete_failure (GimpThumbnail *thumbnail);
+void gimp_thumbnail_delete_others (GimpThumbnail *thumbnail,
+ GimpThumbSize size);
+
+gboolean gimp_thumbnail_has_failed (GimpThumbnail *thumbnail);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_THUMBNAIL_H__ */
diff --git a/libgimpwidgets/Makefile.am b/libgimpwidgets/Makefile.am
new file mode 100644
index 0000000..9b4dbf5
--- /dev/null
+++ b/libgimpwidgets/Makefile.am
@@ -0,0 +1,418 @@
+## Process this file with automake to produce Makefile.in
+
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+
+if PLATFORM_WIN32
+no_undefined = -no-undefined
+libgdi32 = -lgdi32
+libmscms = -lmscms
+else
+libm = -lm
+endif
+
+if PLATFORM_OSX
+xobjective_c = "-xobjective-c"
+xobjective_cxx = "-xobjective-c++"
+xnone = "-xnone"
+framework_cocoa = -framework Cocoa
+endif
+
+if OS_WIN32
+gimpwidgets_def = gimpwidgets.def
+libgimpwidgets_export_symbols = -export-symbols $(srcdir)/gimpwidgets.def
+
+install-libtool-import-lib:
+ $(INSTALL) .libs/libgimpwidgets-$(GIMP_API_VERSION).dll.a $(DESTDIR)$(libdir)
+ $(INSTALL) $(srcdir)/gimpwidgets.def $(DESTDIR)$(libdir)
+
+uninstall-libtool-import-lib:
+ -rm $(DESTDIR)$(libdir)/libgimpwidgets-$(GIMP_API_VERSION).dll.a
+ -rm $(DESTDIR)$(libdir)/gimpwidgets.def
+else
+install-libtool-import-lib:
+uninstall-libtool-import-lib:
+endif
+
+if MS_LIB_AVAILABLE
+noinst_DATA = gimpwidgets-$(GIMP_API_VERSION).lib
+
+install-ms-lib:
+ $(INSTALL) gimpwidgets-$(GIMP_API_VERSION).lib $(DESTDIR)$(libdir)
+
+uninstall-ms-lib:
+ -rm $(DESTDIR)$(libdir)/gimpwidgets-$(GIMP_API_VERSION).lib
+
+gimpwidgets-@GIMP_API_VERSION@.lib: gimpwidgets.def
+ lib -name:libgimpwidgets-$(GIMP_API_VERSION)-@LT_CURRENT_MINUS_AGE@.dll -def:gimpwidgets.def -out:$@
+
+else
+install-ms-lib:
+uninstall-ms-lib:
+endif
+
+libgimpwidgetsincludedir = $(includedir)/gimp-$(GIMP_API_VERSION)/libgimpwidgets
+
+AM_CPPFLAGS = \
+ -DG_LOG_DOMAIN=\"LibGimpWidgets\" \
+ -DGIMP_WIDGETS_COMPILATION \
+ -I$(top_srcdir) \
+ $(GEGL_CFLAGS) \
+ $(GTK_CFLAGS) \
+ $(LCMS_CFLAGS) \
+ -I$(includedir)
+
+AM_CFLAGS = \
+ $(xobjective_c)
+
+AM_CXXFLAGS = \
+ $(xobjective_cxx)
+
+AM_LDFLAGS = \
+ $(xnone)
+
+lib_LTLIBRARIES = libgimpwidgets-@GIMP_API_VERSION@.la
+
+libgimpwidgets_sources = \
+ gimpbrowser.c \
+ gimpbrowser.h \
+ gimpbusybox.c \
+ gimpbusybox.h \
+ gimpbutton.c \
+ gimpbutton.h \
+ gimpcairo-utils.c \
+ gimpcairo-utils.h \
+ gimpcellrenderercolor.c \
+ gimpcellrenderercolor.h \
+ gimpcellrenderertoggle.c \
+ gimpcellrenderertoggle.h \
+ gimpchainbutton.c \
+ gimpchainbutton.h \
+ gimpcolorarea.c \
+ gimpcolorarea.h \
+ gimpcolorbutton.c \
+ gimpcolorbutton.h \
+ gimpcolordisplay.c \
+ gimpcolordisplay.h \
+ gimpcolordisplaystack.c \
+ gimpcolordisplaystack.h \
+ gimpcolorhexentry.c \
+ gimpcolorhexentry.h \
+ gimpcolornotebook.c \
+ gimpcolornotebook.h \
+ gimpcolorprofilechooserdialog.c \
+ gimpcolorprofilechooserdialog.h \
+ gimpcolorprofilecombobox.c \
+ gimpcolorprofilecombobox.h \
+ gimpcolorprofilestore-private.h \
+ gimpcolorprofilestore.c \
+ gimpcolorprofilestore.h \
+ gimpcolorprofileview.c \
+ gimpcolorprofileview.h \
+ gimpcolorscale.c \
+ gimpcolorscale.h \
+ gimpcolorscales.c \
+ gimpcolorscales.h \
+ gimpcolorselect.c \
+ gimpcolorselect.h \
+ gimpcolorselection.c \
+ gimpcolorselection.h \
+ gimpcolorselector.c \
+ gimpcolorselector.h \
+ gimpcontroller.c \
+ gimpcontroller.h \
+ gimpdialog.c \
+ gimpdialog.h \
+ gimpeevl.c \
+ gimpeevl.h \
+ gimpenumcombobox.c \
+ gimpenumcombobox.h \
+ gimpenumlabel.c \
+ gimpenumlabel.h \
+ gimpenumstore.c \
+ gimpenumstore.h \
+ gimpenumwidgets.c \
+ gimpenumwidgets.h \
+ gimpfileentry.c \
+ gimpfileentry.h \
+ gimpframe.c \
+ gimpframe.h \
+ gimphelpui.c \
+ gimphelpui.h \
+ gimphintbox.c \
+ gimphintbox.h \
+ gimpicons.c \
+ gimpicons.h \
+ gimpintcombobox.c \
+ gimpintcombobox.h \
+ gimpintstore.c \
+ gimpintstore.h \
+ gimpmemsizeentry.c \
+ gimpmemsizeentry.h \
+ gimpnumberpairentry.c \
+ gimpnumberpairentry.h \
+ gimpoffsetarea.c \
+ gimpoffsetarea.h \
+ gimpoldwidgets.c \
+ gimpoldwidgets.h \
+ gimppageselector.c \
+ gimppageselector.h \
+ gimppatheditor.c \
+ gimppatheditor.h \
+ gimppickbutton.c \
+ gimppickbutton.h \
+ gimppixmap.c \
+ gimppixmap.h \
+ gimppreview.c \
+ gimppreview.h \
+ gimppreviewarea.c \
+ gimppreviewarea.h \
+ gimppropwidgets.c \
+ gimppropwidgets.h \
+ gimpquerybox.c \
+ gimpquerybox.h \
+ gimpruler.c \
+ gimpruler.h \
+ gimpscaleentry.c \
+ gimpscaleentry.h \
+ gimpscrolledpreview.c \
+ gimpscrolledpreview.h \
+ gimpsizeentry.c \
+ gimpsizeentry.h \
+ gimpspinbutton.c \
+ gimpspinbutton.h \
+ gimpstringcombobox.c \
+ gimpstringcombobox.h \
+ gimpunitcombobox.c \
+ gimpunitcombobox.h \
+ gimpunitmenu.c \
+ gimpunitmenu.h \
+ gimpunitstore.c \
+ gimpunitstore.h \
+ gimpwidgets-error.c \
+ gimpwidgets-error.h \
+ gimpwidgets-private.c \
+ gimpwidgets-private.h \
+ gimpwidgets.c \
+ gimpwidgets.h \
+ gimpwidgetsenums.h \
+ gimpwidgetstypes.h \
+ gimpwidgetsutils.c \
+ gimpwidgetsutils.h \
+ gimpzoommodel.c \
+ gimpzoommodel.h \
+ gimp3migration.c \
+ gimp3migration.h
+
+libgimpwidgets_built_sources = \
+ gimpwidgetsenums.c \
+ gimpwidgetsmarshal.c \
+ gimpwidgetsmarshal.h
+
+libgimpwidgets_extra_sources = gimpwidgetsmarshal.list
+
+libgimpwidgets_@GIMP_API_VERSION@_la_SOURCES = \
+ $(libgimpwidgets_built_sources) \
+ $(libgimpwidgets_sources)
+
+libgimpwidgetsinclude_HEADERS = \
+ gimpbrowser.h \
+ gimpbusybox.h \
+ gimpbutton.h \
+ gimpcairo-utils.h \
+ gimpcellrenderercolor.h \
+ gimpcellrenderertoggle.h \
+ gimpchainbutton.h \
+ gimpcolorarea.h \
+ gimpcolorbutton.h \
+ gimpcolordisplay.h \
+ gimpcolordisplaystack.h \
+ gimpcolorhexentry.h \
+ gimpcolornotebook.h \
+ gimpcolorprofilechooserdialog.h \
+ gimpcolorprofilecombobox.h \
+ gimpcolorprofilestore.h \
+ gimpcolorprofileview.h \
+ gimpcolorscale.h \
+ gimpcolorscales.h \
+ gimpcolorselect.h \
+ gimpcolorselection.h \
+ gimpcolorselector.h \
+ gimpcontroller.h \
+ gimpdialog.h \
+ gimpenumcombobox.h \
+ gimpenumlabel.h \
+ gimpenumstore.h \
+ gimpenumwidgets.h \
+ gimpfileentry.h \
+ gimpframe.h \
+ gimphelpui.h \
+ gimphintbox.h \
+ gimpicons.h \
+ gimpintcombobox.h \
+ gimpintstore.h \
+ gimpmemsizeentry.h \
+ gimpnumberpairentry.h \
+ gimpoffsetarea.h \
+ gimpoldwidgets.h \
+ gimppageselector.h \
+ gimppatheditor.h \
+ gimppickbutton.h \
+ gimppixmap.h \
+ gimppreview.h \
+ gimppreviewarea.h \
+ gimppropwidgets.h \
+ gimpquerybox.h \
+ gimpruler.h \
+ gimpscaleentry.h \
+ gimpscrolledpreview.h \
+ gimpsizeentry.h \
+ gimpspinbutton.h \
+ gimpstringcombobox.h \
+ gimpunitcombobox.h \
+ gimpunitmenu.h \
+ gimpunitstore.h \
+ gimpwidgets-error.h \
+ gimpwidgets.h \
+ gimpwidgetsenums.h \
+ gimpwidgetstypes.h \
+ gimpwidgetsutils.h \
+ gimpzoommodel.h \
+ gimp3migration.h
+
+libgimpwidgets_@GIMP_API_VERSION@_la_LDFLAGS = \
+ -version-info $(LT_VERSION_INFO) \
+ $(no_undefined) \
+ $(libgimpwidgets_export_symbols) \
+ $(framework_cocoa) \
+ $(xnone)
+
+EXTRA_libgimpwidgets_@GIMP_API_VERSION@_la_DEPENDENCIES = $(gimpwidgets_def)
+
+libgimpwidgets_@GIMP_API_VERSION@_la_LIBADD = \
+ $(libgimpbase) \
+ $(libgimpcolor) \
+ $(libgimpconfig) \
+ $(GEGL_LIBS) \
+ $(GTK_LIBS) \
+ $(LCMS_LIBS) \
+ $(libm) \
+ $(libgdi32) \
+ $(libmscms)
+
+BUILT_SOURCES = \
+ $(libgimpwidgets_built_sources)
+
+EXTRA_DIST = \
+ gimpwidgets.def \
+ $(libgimpwidgets_extra_sources)
+
+#
+# platform-dependent source files
+#
+
+
+if PLATFORM_OSX_QUARTZ
+libgimpwidgets_sources += gimppickbutton-quartz.c gimppickbutton-quartz.h
+AM_CPPFLAGS += "-xobjective-c"
+else
+
+if PLATFORM_WIN32
+libgimpwidgets_sources += gimppickbutton-win32.c gimppickbutton-win32.h
+else
+libgimpwidgets_sources += \
+ gimppickbutton-default.c \
+ gimppickbutton-default.h \
+ gimppickbutton-kwin.c \
+ gimppickbutton-kwin.h \
+ gimppickbutton-xdg.c \
+ gimppickbutton-xdg.h
+endif
+
+endif
+
+
+#
+# rules to generate built sources
+#
+# setup autogeneration dependencies
+gen_sources = xgen-wec xgen-wmh xgen-wmc
+CLEANFILES = $(gen_sources)
+
+xgen-wec: $(srcdir)/gimpwidgetsenums.h $(GIMP_MKENUMS) Makefile.am
+ $(AM_V_GEN) $(GIMP_MKENUMS) \
+ --fhead "#include \"config.h\"\n#include <gio/gio.h>\n#include \"libgimpbase/gimpbase.h\"\n#include \"gimpwidgetsenums.h\"\n#include \"libgimp/libgimp-intl.h\"" \
+ --fprod "\n/* enumerations from \"@basename@\" */" \
+ --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \
+ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \
+ --vtail " { 0, NULL, NULL }\n };\n" \
+ --dhead " static const Gimp@Type@Desc descs[] =\n {" \
+ --dprod " { @VALUENAME@, @valuedesc@, @valuehelp@ },@if ('@valueabbrev@' ne 'NULL')@\n /* Translators: this is an abbreviated version of @valueudesc@.\n Keep it short. */\n { @VALUENAME@, @valueabbrev@, NULL },@endif@" \
+ --dtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n {\n type = g_@type@_register_static (\"@EnumName@\", values);\n gimp_type_set_translation_domain (type, GETTEXT_PACKAGE \"-libgimp\");\n gimp_type_set_translation_context (type, \"@enumnick@\");\n gimp_@type@_set_value_descriptions (type, descs);\n }\n\n return type;\n}\n" \
+ $< > $@
+
+# copy the generated enum file back to the source directory only if it's
+# changed; otherwise, only update its timestamp, so that the recipe isn't
+# executed again on the next build, however, allow this to (harmlessly) fail,
+# to support building from a read-only source tree.
+$(srcdir)/gimpwidgetsenums.c: xgen-wec
+ $(AM_V_GEN) if ! cmp -s $< $@; then \
+ cp $< $@; \
+ else \
+ touch $@ 2> /dev/null \
+ || true; \
+ fi
+
+gimpwidgetsmarshal.h: $(srcdir)/gimpwidgetsmarshal.list
+ $(AM_V_GEN) $(GLIB_GENMARSHAL) --prefix=_gimp_widgets_marshal $(srcdir)/gimpwidgetsmarshal.list --header >> xgen-wmh \
+ && (cmp -s xgen-wmh $(@F) || cp xgen-wmh $(@F)) \
+ && rm -f xgen-wmh xgen-wmh~
+
+gimpwidgetsmarshal.c: gimpwidgetsmarshal.h
+ $(AM_V_GEN) $(GLIB_GENMARSHAL) --prefix=_gimp_widgets_marshal $(srcdir)/gimpwidgetsmarshal.list --header --body >> xgen-wmc \
+ && cp xgen-wmc $(@F) \
+ && rm -f xgen-wmc xgen-wmc~
+
+
+#
+# test programs, not installed
+#
+
+EXTRA_PROGRAMS = \
+ test-preview-area \
+ test-eevl
+
+
+test_preview_area_SOURCES = test-preview-area.c
+
+test_preview_area_LDADD = \
+ $(GTK_LIBS) \
+ $(libgimpbase) \
+ $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+
+
+test_eevl_SOURCES = \
+ test-eevl.c
+
+test_eevl_LDADD = \
+ $(GLIB_LIBS) \
+ $(libgimpcolor) \
+ $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+
+
+#
+# test programs, not to be built by default and never installed
+#
+
+TESTS = test-eevl$(EXEEXT)
+
+
+
+
+CLEANFILES += $(EXTRA_PROGRAMS)
+
+
+install-data-local: install-ms-lib install-libtool-import-lib
+
+uninstall-local: uninstall-ms-lib uninstall-libtool-import-lib
diff --git a/libgimpwidgets/Makefile.in b/libgimpwidgets/Makefile.in
new file mode 100644
index 0000000..8e1e0d3
--- /dev/null
+++ b/libgimpwidgets/Makefile.in
@@ -0,0 +1,1988 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+
+#
+# platform-dependent source files
+#
+@PLATFORM_OSX_QUARTZ_TRUE@am__append_1 = gimppickbutton-quartz.c gimppickbutton-quartz.h
+@PLATFORM_OSX_QUARTZ_TRUE@am__append_2 = "-xobjective-c"
+@PLATFORM_OSX_QUARTZ_FALSE@@PLATFORM_WIN32_TRUE@am__append_3 = gimppickbutton-win32.c gimppickbutton-win32.h
+@PLATFORM_OSX_QUARTZ_FALSE@@PLATFORM_WIN32_FALSE@am__append_4 = \
+@PLATFORM_OSX_QUARTZ_FALSE@@PLATFORM_WIN32_FALSE@ gimppickbutton-default.c \
+@PLATFORM_OSX_QUARTZ_FALSE@@PLATFORM_WIN32_FALSE@ gimppickbutton-default.h \
+@PLATFORM_OSX_QUARTZ_FALSE@@PLATFORM_WIN32_FALSE@ gimppickbutton-kwin.c \
+@PLATFORM_OSX_QUARTZ_FALSE@@PLATFORM_WIN32_FALSE@ gimppickbutton-kwin.h \
+@PLATFORM_OSX_QUARTZ_FALSE@@PLATFORM_WIN32_FALSE@ gimppickbutton-xdg.c \
+@PLATFORM_OSX_QUARTZ_FALSE@@PLATFORM_WIN32_FALSE@ gimppickbutton-xdg.h
+
+EXTRA_PROGRAMS = test-preview-area$(EXEEXT) test-eevl$(EXEEXT)
+subdir = libgimpwidgets
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(libgimpwidgetsinclude_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(libdir)" \
+ "$(DESTDIR)$(libgimpwidgetsincludedir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libgimpwidgets_@GIMP_API_VERSION@_la_DEPENDENCIES = $(libgimpbase) \
+ $(libgimpcolor) $(libgimpconfig) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+am__libgimpwidgets_@GIMP_API_VERSION@_la_SOURCES_DIST = \
+ gimpwidgetsenums.c gimpwidgetsmarshal.c gimpwidgetsmarshal.h \
+ gimpbrowser.c gimpbrowser.h gimpbusybox.c gimpbusybox.h \
+ gimpbutton.c gimpbutton.h gimpcairo-utils.c gimpcairo-utils.h \
+ gimpcellrenderercolor.c gimpcellrenderercolor.h \
+ gimpcellrenderertoggle.c gimpcellrenderertoggle.h \
+ gimpchainbutton.c gimpchainbutton.h gimpcolorarea.c \
+ gimpcolorarea.h gimpcolorbutton.c gimpcolorbutton.h \
+ gimpcolordisplay.c gimpcolordisplay.h gimpcolordisplaystack.c \
+ gimpcolordisplaystack.h gimpcolorhexentry.c \
+ gimpcolorhexentry.h gimpcolornotebook.c gimpcolornotebook.h \
+ gimpcolorprofilechooserdialog.c \
+ gimpcolorprofilechooserdialog.h gimpcolorprofilecombobox.c \
+ gimpcolorprofilecombobox.h gimpcolorprofilestore-private.h \
+ gimpcolorprofilestore.c gimpcolorprofilestore.h \
+ gimpcolorprofileview.c gimpcolorprofileview.h gimpcolorscale.c \
+ gimpcolorscale.h gimpcolorscales.c gimpcolorscales.h \
+ gimpcolorselect.c gimpcolorselect.h gimpcolorselection.c \
+ gimpcolorselection.h gimpcolorselector.c gimpcolorselector.h \
+ gimpcontroller.c gimpcontroller.h gimpdialog.c gimpdialog.h \
+ gimpeevl.c gimpeevl.h gimpenumcombobox.c gimpenumcombobox.h \
+ gimpenumlabel.c gimpenumlabel.h gimpenumstore.c \
+ gimpenumstore.h gimpenumwidgets.c gimpenumwidgets.h \
+ gimpfileentry.c gimpfileentry.h gimpframe.c gimpframe.h \
+ gimphelpui.c gimphelpui.h gimphintbox.c gimphintbox.h \
+ gimpicons.c gimpicons.h gimpintcombobox.c gimpintcombobox.h \
+ gimpintstore.c gimpintstore.h gimpmemsizeentry.c \
+ gimpmemsizeentry.h gimpnumberpairentry.c gimpnumberpairentry.h \
+ gimpoffsetarea.c gimpoffsetarea.h gimpoldwidgets.c \
+ gimpoldwidgets.h gimppageselector.c gimppageselector.h \
+ gimppatheditor.c gimppatheditor.h gimppickbutton.c \
+ gimppickbutton.h gimppixmap.c gimppixmap.h gimppreview.c \
+ gimppreview.h gimppreviewarea.c gimppreviewarea.h \
+ gimppropwidgets.c gimppropwidgets.h gimpquerybox.c \
+ gimpquerybox.h gimpruler.c gimpruler.h gimpscaleentry.c \
+ gimpscaleentry.h gimpscrolledpreview.c gimpscrolledpreview.h \
+ gimpsizeentry.c gimpsizeentry.h gimpspinbutton.c \
+ gimpspinbutton.h gimpstringcombobox.c gimpstringcombobox.h \
+ gimpunitcombobox.c gimpunitcombobox.h gimpunitmenu.c \
+ gimpunitmenu.h gimpunitstore.c gimpunitstore.h \
+ gimpwidgets-error.c gimpwidgets-error.h gimpwidgets-private.c \
+ gimpwidgets-private.h gimpwidgets.c gimpwidgets.h \
+ gimpwidgetsenums.h gimpwidgetstypes.h gimpwidgetsutils.c \
+ gimpwidgetsutils.h gimpzoommodel.c gimpzoommodel.h \
+ gimp3migration.c gimp3migration.h gimppickbutton-quartz.c \
+ gimppickbutton-quartz.h gimppickbutton-win32.c \
+ gimppickbutton-win32.h gimppickbutton-default.c \
+ gimppickbutton-default.h gimppickbutton-kwin.c \
+ gimppickbutton-kwin.h gimppickbutton-xdg.c \
+ gimppickbutton-xdg.h
+am__objects_1 = gimpwidgetsenums.lo gimpwidgetsmarshal.lo
+@PLATFORM_OSX_QUARTZ_TRUE@am__objects_2 = gimppickbutton-quartz.lo
+@PLATFORM_OSX_QUARTZ_FALSE@@PLATFORM_WIN32_TRUE@am__objects_3 = gimppickbutton-win32.lo
+@PLATFORM_OSX_QUARTZ_FALSE@@PLATFORM_WIN32_FALSE@am__objects_4 = gimppickbutton-default.lo \
+@PLATFORM_OSX_QUARTZ_FALSE@@PLATFORM_WIN32_FALSE@ gimppickbutton-kwin.lo \
+@PLATFORM_OSX_QUARTZ_FALSE@@PLATFORM_WIN32_FALSE@ gimppickbutton-xdg.lo
+am__objects_5 = gimpbrowser.lo gimpbusybox.lo gimpbutton.lo \
+ gimpcairo-utils.lo gimpcellrenderercolor.lo \
+ gimpcellrenderertoggle.lo gimpchainbutton.lo gimpcolorarea.lo \
+ gimpcolorbutton.lo gimpcolordisplay.lo \
+ gimpcolordisplaystack.lo gimpcolorhexentry.lo \
+ gimpcolornotebook.lo gimpcolorprofilechooserdialog.lo \
+ gimpcolorprofilecombobox.lo gimpcolorprofilestore.lo \
+ gimpcolorprofileview.lo gimpcolorscale.lo gimpcolorscales.lo \
+ gimpcolorselect.lo gimpcolorselection.lo gimpcolorselector.lo \
+ gimpcontroller.lo gimpdialog.lo gimpeevl.lo \
+ gimpenumcombobox.lo gimpenumlabel.lo gimpenumstore.lo \
+ gimpenumwidgets.lo gimpfileentry.lo gimpframe.lo gimphelpui.lo \
+ gimphintbox.lo gimpicons.lo gimpintcombobox.lo gimpintstore.lo \
+ gimpmemsizeentry.lo gimpnumberpairentry.lo gimpoffsetarea.lo \
+ gimpoldwidgets.lo gimppageselector.lo gimppatheditor.lo \
+ gimppickbutton.lo gimppixmap.lo gimppreview.lo \
+ gimppreviewarea.lo gimppropwidgets.lo gimpquerybox.lo \
+ gimpruler.lo gimpscaleentry.lo gimpscrolledpreview.lo \
+ gimpsizeentry.lo gimpspinbutton.lo gimpstringcombobox.lo \
+ gimpunitcombobox.lo gimpunitmenu.lo gimpunitstore.lo \
+ gimpwidgets-error.lo gimpwidgets-private.lo gimpwidgets.lo \
+ gimpwidgetsutils.lo gimpzoommodel.lo gimp3migration.lo \
+ $(am__objects_2) $(am__objects_3) $(am__objects_4)
+am_libgimpwidgets_@GIMP_API_VERSION@_la_OBJECTS = $(am__objects_1) \
+ $(am__objects_5)
+libgimpwidgets_@GIMP_API_VERSION@_la_OBJECTS = \
+ $(am_libgimpwidgets_@GIMP_API_VERSION@_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libgimpwidgets_@GIMP_API_VERSION@_la_LINK = $(LIBTOOL) $(AM_V_lt) \
+ --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libgimpwidgets_@GIMP_API_VERSION@_la_LDFLAGS) $(LDFLAGS) -o \
+ $@
+am_test_eevl_OBJECTS = test-eevl.$(OBJEXT)
+test_eevl_OBJECTS = $(am_test_eevl_OBJECTS)
+test_eevl_DEPENDENCIES = $(am__DEPENDENCIES_1) $(libgimpcolor) \
+ $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+am_test_preview_area_OBJECTS = test-preview-area.$(OBJEXT)
+test_preview_area_OBJECTS = $(am_test_preview_area_OBJECTS)
+test_preview_area_DEPENDENCIES = $(am__DEPENDENCIES_1) $(libgimpbase) \
+ $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/gimp3migration.Plo \
+ ./$(DEPDIR)/gimpbrowser.Plo ./$(DEPDIR)/gimpbusybox.Plo \
+ ./$(DEPDIR)/gimpbutton.Plo ./$(DEPDIR)/gimpcairo-utils.Plo \
+ ./$(DEPDIR)/gimpcellrenderercolor.Plo \
+ ./$(DEPDIR)/gimpcellrenderertoggle.Plo \
+ ./$(DEPDIR)/gimpchainbutton.Plo ./$(DEPDIR)/gimpcolorarea.Plo \
+ ./$(DEPDIR)/gimpcolorbutton.Plo \
+ ./$(DEPDIR)/gimpcolordisplay.Plo \
+ ./$(DEPDIR)/gimpcolordisplaystack.Plo \
+ ./$(DEPDIR)/gimpcolorhexentry.Plo \
+ ./$(DEPDIR)/gimpcolornotebook.Plo \
+ ./$(DEPDIR)/gimpcolorprofilechooserdialog.Plo \
+ ./$(DEPDIR)/gimpcolorprofilecombobox.Plo \
+ ./$(DEPDIR)/gimpcolorprofilestore.Plo \
+ ./$(DEPDIR)/gimpcolorprofileview.Plo \
+ ./$(DEPDIR)/gimpcolorscale.Plo ./$(DEPDIR)/gimpcolorscales.Plo \
+ ./$(DEPDIR)/gimpcolorselect.Plo \
+ ./$(DEPDIR)/gimpcolorselection.Plo \
+ ./$(DEPDIR)/gimpcolorselector.Plo \
+ ./$(DEPDIR)/gimpcontroller.Plo ./$(DEPDIR)/gimpdialog.Plo \
+ ./$(DEPDIR)/gimpeevl.Plo ./$(DEPDIR)/gimpenumcombobox.Plo \
+ ./$(DEPDIR)/gimpenumlabel.Plo ./$(DEPDIR)/gimpenumstore.Plo \
+ ./$(DEPDIR)/gimpenumwidgets.Plo ./$(DEPDIR)/gimpfileentry.Plo \
+ ./$(DEPDIR)/gimpframe.Plo ./$(DEPDIR)/gimphelpui.Plo \
+ ./$(DEPDIR)/gimphintbox.Plo ./$(DEPDIR)/gimpicons.Plo \
+ ./$(DEPDIR)/gimpintcombobox.Plo ./$(DEPDIR)/gimpintstore.Plo \
+ ./$(DEPDIR)/gimpmemsizeentry.Plo \
+ ./$(DEPDIR)/gimpnumberpairentry.Plo \
+ ./$(DEPDIR)/gimpoffsetarea.Plo ./$(DEPDIR)/gimpoldwidgets.Plo \
+ ./$(DEPDIR)/gimppageselector.Plo \
+ ./$(DEPDIR)/gimppatheditor.Plo \
+ ./$(DEPDIR)/gimppickbutton-default.Plo \
+ ./$(DEPDIR)/gimppickbutton-kwin.Plo \
+ ./$(DEPDIR)/gimppickbutton-quartz.Plo \
+ ./$(DEPDIR)/gimppickbutton-win32.Plo \
+ ./$(DEPDIR)/gimppickbutton-xdg.Plo \
+ ./$(DEPDIR)/gimppickbutton.Plo ./$(DEPDIR)/gimppixmap.Plo \
+ ./$(DEPDIR)/gimppreview.Plo ./$(DEPDIR)/gimppreviewarea.Plo \
+ ./$(DEPDIR)/gimppropwidgets.Plo ./$(DEPDIR)/gimpquerybox.Plo \
+ ./$(DEPDIR)/gimpruler.Plo ./$(DEPDIR)/gimpscaleentry.Plo \
+ ./$(DEPDIR)/gimpscrolledpreview.Plo \
+ ./$(DEPDIR)/gimpsizeentry.Plo ./$(DEPDIR)/gimpspinbutton.Plo \
+ ./$(DEPDIR)/gimpstringcombobox.Plo \
+ ./$(DEPDIR)/gimpunitcombobox.Plo ./$(DEPDIR)/gimpunitmenu.Plo \
+ ./$(DEPDIR)/gimpunitstore.Plo \
+ ./$(DEPDIR)/gimpwidgets-error.Plo \
+ ./$(DEPDIR)/gimpwidgets-private.Plo \
+ ./$(DEPDIR)/gimpwidgets.Plo ./$(DEPDIR)/gimpwidgetsenums.Plo \
+ ./$(DEPDIR)/gimpwidgetsmarshal.Plo \
+ ./$(DEPDIR)/gimpwidgetsutils.Plo ./$(DEPDIR)/gimpzoommodel.Plo \
+ ./$(DEPDIR)/test-eevl.Po ./$(DEPDIR)/test-preview-area.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libgimpwidgets_@GIMP_API_VERSION@_la_SOURCES) \
+ $(test_eevl_SOURCES) $(test_preview_area_SOURCES)
+DIST_SOURCES = \
+ $(am__libgimpwidgets_@GIMP_API_VERSION@_la_SOURCES_DIST) \
+ $(test_eevl_SOURCES) $(test_preview_area_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+DATA = $(noinst_DATA)
+HEADERS = $(libgimpwidgetsinclude_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__tty_colors_dummy = \
+ mgn= red= grn= lgn= blu= brg= std=; \
+ am__color_tests=no
+am__tty_colors = { \
+ $(am__tty_colors_dummy); \
+ if test "X$(AM_COLOR_TESTS)" = Xno; then \
+ am__color_tests=no; \
+ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+ am__color_tests=yes; \
+ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+ am__color_tests=yes; \
+ fi; \
+ if test $$am__color_tests = yes; then \
+ red=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ fi; \
+}
+am__recheck_rx = ^[ ]*:recheck:[ ]*
+am__global_test_result_rx = ^[ ]*:global-test-result:[ ]*
+am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+ recheck = 1; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ { \
+ if ((getline line2 < ($$0 ".log")) < 0) \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+ { \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+ { \
+ break; \
+ } \
+ }; \
+ if (recheck) \
+ print $$0; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+ print "fatal: making $@: " msg | "cat >&2"; \
+ exit 1; \
+} \
+function rst_section(header) \
+{ \
+ print header; \
+ len = length(header); \
+ for (i = 1; i <= len; i = i + 1) \
+ printf "="; \
+ printf "\n\n"; \
+} \
+{ \
+ copy_in_global_log = 1; \
+ global_test_result = "RUN"; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".trs"); \
+ if (line ~ /$(am__global_test_result_rx)/) \
+ { \
+ sub("$(am__global_test_result_rx)", "", line); \
+ sub("[ ]*$$", "", line); \
+ global_test_result = line; \
+ } \
+ else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+ copy_in_global_log = 0; \
+ }; \
+ if (copy_in_global_log) \
+ { \
+ rst_section(global_test_result ": " $$0); \
+ while ((rc = (getline line < ($$0 ".log"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".log"); \
+ print line; \
+ }; \
+ printf "\n"; \
+ }; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+ --color-tests "$$am__color_tests" \
+ --enable-hard-errors "$$am__enable_hard_errors" \
+ --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test. Creates the
+# directory for the log if needed. Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log. Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT. Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup); \
+$(am__vpath_adj_setup) $(am__vpath_adj) \
+$(am__tty_colors); \
+srcdir=$(srcdir); export srcdir; \
+case "$@" in \
+ */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \
+ *) am__odir=.;; \
+esac; \
+test "x$$am__odir" = x"." || test -d "$$am__odir" \
+ || $(MKDIR_P) "$$am__odir" || exit $$?; \
+if test -f "./$$f"; then dir=./; \
+elif test -f "$$f"; then dir=; \
+else dir="$(srcdir)/"; fi; \
+tst=$$dir$$f; log='$@'; \
+if test -n '$(DISABLE_HARD_ERRORS)'; then \
+ am__enable_hard_errors=no; \
+else \
+ am__enable_hard_errors=yes; \
+fi; \
+case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \
+ am__expect_failure=yes;; \
+ *) \
+ am__expect_failure=no;; \
+esac; \
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed). The result is saved in the shell variable
+# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+ bases='$(TEST_LOGS)'; \
+ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+ bases=`echo $$bases`
+AM_TESTSUITE_SUMMARY_HEADER = ' for $(PACKAGE_STRING)'
+RECHECK_LOGS = $(TEST_LOGS)
+AM_RECURSIVE_TARGETS = check recheck
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+ case '$@' in \
+ */*) \
+ case '$*' in \
+ */*) b='$*';; \
+ *) b=`echo '$@' | sed 's/\.log$$//'`; \
+ esac;; \
+ *) \
+ b='$*';; \
+ esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+ $(TEST_LOG_FLAGS)
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp \
+ $(top_srcdir)/test-driver
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+@PLATFORM_WIN32_TRUE@no_undefined = -no-undefined
+@PLATFORM_WIN32_TRUE@libgdi32 = -lgdi32
+@PLATFORM_WIN32_TRUE@libmscms = -lmscms
+@PLATFORM_WIN32_FALSE@libm = -lm
+@PLATFORM_OSX_TRUE@xobjective_c = "-xobjective-c"
+@PLATFORM_OSX_TRUE@xobjective_cxx = "-xobjective-c++"
+@PLATFORM_OSX_TRUE@xnone = "-xnone"
+@PLATFORM_OSX_TRUE@framework_cocoa = -framework Cocoa
+@OS_WIN32_TRUE@gimpwidgets_def = gimpwidgets.def
+@OS_WIN32_TRUE@libgimpwidgets_export_symbols = -export-symbols $(srcdir)/gimpwidgets.def
+@MS_LIB_AVAILABLE_TRUE@noinst_DATA = gimpwidgets-$(GIMP_API_VERSION).lib
+libgimpwidgetsincludedir = $(includedir)/gimp-$(GIMP_API_VERSION)/libgimpwidgets
+AM_CPPFLAGS = -DG_LOG_DOMAIN=\"LibGimpWidgets\" \
+ -DGIMP_WIDGETS_COMPILATION -I$(top_srcdir) $(GEGL_CFLAGS) \
+ $(GTK_CFLAGS) $(LCMS_CFLAGS) -I$(includedir) $(am__append_2)
+AM_CFLAGS = \
+ $(xobjective_c)
+
+AM_CXXFLAGS = \
+ $(xobjective_cxx)
+
+AM_LDFLAGS = \
+ $(xnone)
+
+lib_LTLIBRARIES = libgimpwidgets-@GIMP_API_VERSION@.la
+libgimpwidgets_sources = gimpbrowser.c gimpbrowser.h gimpbusybox.c \
+ gimpbusybox.h gimpbutton.c gimpbutton.h gimpcairo-utils.c \
+ gimpcairo-utils.h gimpcellrenderercolor.c \
+ gimpcellrenderercolor.h gimpcellrenderertoggle.c \
+ gimpcellrenderertoggle.h gimpchainbutton.c gimpchainbutton.h \
+ gimpcolorarea.c gimpcolorarea.h gimpcolorbutton.c \
+ gimpcolorbutton.h gimpcolordisplay.c gimpcolordisplay.h \
+ gimpcolordisplaystack.c gimpcolordisplaystack.h \
+ gimpcolorhexentry.c gimpcolorhexentry.h gimpcolornotebook.c \
+ gimpcolornotebook.h gimpcolorprofilechooserdialog.c \
+ gimpcolorprofilechooserdialog.h gimpcolorprofilecombobox.c \
+ gimpcolorprofilecombobox.h gimpcolorprofilestore-private.h \
+ gimpcolorprofilestore.c gimpcolorprofilestore.h \
+ gimpcolorprofileview.c gimpcolorprofileview.h gimpcolorscale.c \
+ gimpcolorscale.h gimpcolorscales.c gimpcolorscales.h \
+ gimpcolorselect.c gimpcolorselect.h gimpcolorselection.c \
+ gimpcolorselection.h gimpcolorselector.c gimpcolorselector.h \
+ gimpcontroller.c gimpcontroller.h gimpdialog.c gimpdialog.h \
+ gimpeevl.c gimpeevl.h gimpenumcombobox.c gimpenumcombobox.h \
+ gimpenumlabel.c gimpenumlabel.h gimpenumstore.c \
+ gimpenumstore.h gimpenumwidgets.c gimpenumwidgets.h \
+ gimpfileentry.c gimpfileentry.h gimpframe.c gimpframe.h \
+ gimphelpui.c gimphelpui.h gimphintbox.c gimphintbox.h \
+ gimpicons.c gimpicons.h gimpintcombobox.c gimpintcombobox.h \
+ gimpintstore.c gimpintstore.h gimpmemsizeentry.c \
+ gimpmemsizeentry.h gimpnumberpairentry.c gimpnumberpairentry.h \
+ gimpoffsetarea.c gimpoffsetarea.h gimpoldwidgets.c \
+ gimpoldwidgets.h gimppageselector.c gimppageselector.h \
+ gimppatheditor.c gimppatheditor.h gimppickbutton.c \
+ gimppickbutton.h gimppixmap.c gimppixmap.h gimppreview.c \
+ gimppreview.h gimppreviewarea.c gimppreviewarea.h \
+ gimppropwidgets.c gimppropwidgets.h gimpquerybox.c \
+ gimpquerybox.h gimpruler.c gimpruler.h gimpscaleentry.c \
+ gimpscaleentry.h gimpscrolledpreview.c gimpscrolledpreview.h \
+ gimpsizeentry.c gimpsizeentry.h gimpspinbutton.c \
+ gimpspinbutton.h gimpstringcombobox.c gimpstringcombobox.h \
+ gimpunitcombobox.c gimpunitcombobox.h gimpunitmenu.c \
+ gimpunitmenu.h gimpunitstore.c gimpunitstore.h \
+ gimpwidgets-error.c gimpwidgets-error.h gimpwidgets-private.c \
+ gimpwidgets-private.h gimpwidgets.c gimpwidgets.h \
+ gimpwidgetsenums.h gimpwidgetstypes.h gimpwidgetsutils.c \
+ gimpwidgetsutils.h gimpzoommodel.c gimpzoommodel.h \
+ gimp3migration.c gimp3migration.h $(am__append_1) \
+ $(am__append_3) $(am__append_4)
+libgimpwidgets_built_sources = \
+ gimpwidgetsenums.c \
+ gimpwidgetsmarshal.c \
+ gimpwidgetsmarshal.h
+
+libgimpwidgets_extra_sources = gimpwidgetsmarshal.list
+libgimpwidgets_@GIMP_API_VERSION@_la_SOURCES = \
+ $(libgimpwidgets_built_sources) \
+ $(libgimpwidgets_sources)
+
+libgimpwidgetsinclude_HEADERS = \
+ gimpbrowser.h \
+ gimpbusybox.h \
+ gimpbutton.h \
+ gimpcairo-utils.h \
+ gimpcellrenderercolor.h \
+ gimpcellrenderertoggle.h \
+ gimpchainbutton.h \
+ gimpcolorarea.h \
+ gimpcolorbutton.h \
+ gimpcolordisplay.h \
+ gimpcolordisplaystack.h \
+ gimpcolorhexentry.h \
+ gimpcolornotebook.h \
+ gimpcolorprofilechooserdialog.h \
+ gimpcolorprofilecombobox.h \
+ gimpcolorprofilestore.h \
+ gimpcolorprofileview.h \
+ gimpcolorscale.h \
+ gimpcolorscales.h \
+ gimpcolorselect.h \
+ gimpcolorselection.h \
+ gimpcolorselector.h \
+ gimpcontroller.h \
+ gimpdialog.h \
+ gimpenumcombobox.h \
+ gimpenumlabel.h \
+ gimpenumstore.h \
+ gimpenumwidgets.h \
+ gimpfileentry.h \
+ gimpframe.h \
+ gimphelpui.h \
+ gimphintbox.h \
+ gimpicons.h \
+ gimpintcombobox.h \
+ gimpintstore.h \
+ gimpmemsizeentry.h \
+ gimpnumberpairentry.h \
+ gimpoffsetarea.h \
+ gimpoldwidgets.h \
+ gimppageselector.h \
+ gimppatheditor.h \
+ gimppickbutton.h \
+ gimppixmap.h \
+ gimppreview.h \
+ gimppreviewarea.h \
+ gimppropwidgets.h \
+ gimpquerybox.h \
+ gimpruler.h \
+ gimpscaleentry.h \
+ gimpscrolledpreview.h \
+ gimpsizeentry.h \
+ gimpspinbutton.h \
+ gimpstringcombobox.h \
+ gimpunitcombobox.h \
+ gimpunitmenu.h \
+ gimpunitstore.h \
+ gimpwidgets-error.h \
+ gimpwidgets.h \
+ gimpwidgetsenums.h \
+ gimpwidgetstypes.h \
+ gimpwidgetsutils.h \
+ gimpzoommodel.h \
+ gimp3migration.h
+
+libgimpwidgets_@GIMP_API_VERSION@_la_LDFLAGS = \
+ -version-info $(LT_VERSION_INFO) \
+ $(no_undefined) \
+ $(libgimpwidgets_export_symbols) \
+ $(framework_cocoa) \
+ $(xnone)
+
+EXTRA_libgimpwidgets_@GIMP_API_VERSION@_la_DEPENDENCIES = $(gimpwidgets_def)
+libgimpwidgets_@GIMP_API_VERSION@_la_LIBADD = \
+ $(libgimpbase) \
+ $(libgimpcolor) \
+ $(libgimpconfig) \
+ $(GEGL_LIBS) \
+ $(GTK_LIBS) \
+ $(LCMS_LIBS) \
+ $(libm) \
+ $(libgdi32) \
+ $(libmscms)
+
+BUILT_SOURCES = \
+ $(libgimpwidgets_built_sources)
+
+EXTRA_DIST = \
+ gimpwidgets.def \
+ $(libgimpwidgets_extra_sources)
+
+
+#
+# rules to generate built sources
+#
+# setup autogeneration dependencies
+gen_sources = xgen-wec xgen-wmh xgen-wmc
+CLEANFILES = $(gen_sources) $(EXTRA_PROGRAMS)
+test_preview_area_SOURCES = test-preview-area.c
+test_preview_area_LDADD = \
+ $(GTK_LIBS) \
+ $(libgimpbase) \
+ $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+
+test_eevl_SOURCES = \
+ test-eevl.c
+
+test_eevl_LDADD = \
+ $(GLIB_LIBS) \
+ $(libgimpcolor) \
+ $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+
+
+#
+# test programs, not to be built by default and never installed
+#
+TESTS = test-eevl$(EXEEXT)
+all: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu libgimpwidgets/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu libgimpwidgets/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libgimpwidgets-@GIMP_API_VERSION@.la: $(libgimpwidgets_@GIMP_API_VERSION@_la_OBJECTS) $(libgimpwidgets_@GIMP_API_VERSION@_la_DEPENDENCIES) $(EXTRA_libgimpwidgets_@GIMP_API_VERSION@_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libgimpwidgets_@GIMP_API_VERSION@_la_LINK) -rpath $(libdir) $(libgimpwidgets_@GIMP_API_VERSION@_la_OBJECTS) $(libgimpwidgets_@GIMP_API_VERSION@_la_LIBADD) $(LIBS)
+
+test-eevl$(EXEEXT): $(test_eevl_OBJECTS) $(test_eevl_DEPENDENCIES) $(EXTRA_test_eevl_DEPENDENCIES)
+ @rm -f test-eevl$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_eevl_OBJECTS) $(test_eevl_LDADD) $(LIBS)
+
+test-preview-area$(EXEEXT): $(test_preview_area_OBJECTS) $(test_preview_area_DEPENDENCIES) $(EXTRA_test_preview_area_DEPENDENCIES)
+ @rm -f test-preview-area$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_preview_area_OBJECTS) $(test_preview_area_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp3migration.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbrowser.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbusybox.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpbutton.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcairo-utils.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcellrenderercolor.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcellrenderertoggle.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpchainbutton.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcolorarea.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcolorbutton.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcolordisplay.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcolordisplaystack.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcolorhexentry.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcolornotebook.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcolorprofilechooserdialog.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcolorprofilecombobox.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcolorprofilestore.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcolorprofileview.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcolorscale.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcolorscales.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcolorselect.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcolorselection.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcolorselector.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcontroller.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpdialog.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpeevl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpenumcombobox.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpenumlabel.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpenumstore.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpenumwidgets.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpfileentry.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpframe.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimphelpui.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimphintbox.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpicons.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpintcombobox.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpintstore.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpmemsizeentry.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpnumberpairentry.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoffsetarea.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpoldwidgets.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppageselector.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppatheditor.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppickbutton-default.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppickbutton-kwin.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppickbutton-quartz.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppickbutton-win32.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppickbutton-xdg.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppickbutton.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppixmap.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppreview.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppreviewarea.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppropwidgets.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpquerybox.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpruler.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpscaleentry.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpscrolledpreview.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpsizeentry.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpspinbutton.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpstringcombobox.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpunitcombobox.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpunitmenu.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpunitstore.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpwidgets-error.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpwidgets-private.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpwidgets.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpwidgetsenums.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpwidgetsmarshal.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpwidgetsutils.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpzoommodel.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-eevl.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-preview-area.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-libgimpwidgetsincludeHEADERS: $(libgimpwidgetsinclude_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(libgimpwidgetsinclude_HEADERS)'; test -n "$(libgimpwidgetsincludedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libgimpwidgetsincludedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libgimpwidgetsincludedir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(libgimpwidgetsincludedir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(libgimpwidgetsincludedir)" || exit $$?; \
+ done
+
+uninstall-libgimpwidgetsincludeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libgimpwidgetsinclude_HEADERS)'; test -n "$(libgimpwidgetsincludedir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(libgimpwidgetsincludedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+ rm -f $< $@
+ $(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+ @:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+ @$(am__set_TESTS_bases); \
+ am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+ redo_bases=`for i in $$bases; do \
+ am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+ done`; \
+ if test -n "$$redo_bases"; then \
+ redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+ redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+ if $(am__make_dryrun); then :; else \
+ rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
+ fi; \
+ fi; \
+ if test -n "$$am__remaking_logs"; then \
+ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+ "recursion detected" >&2; \
+ elif test -n "$$redo_logs"; then \
+ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+ fi; \
+ if $(am__make_dryrun); then :; else \
+ st=0; \
+ errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+ for i in $$redo_bases; do \
+ test -f $$i.trs && test -r $$i.trs \
+ || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+ test -f $$i.log && test -r $$i.log \
+ || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+ done; \
+ test $$st -eq 0 || exit 1; \
+ fi
+ @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+ ws='[ ]'; \
+ results=`for b in $$bases; do echo $$b.trs; done`; \
+ test -n "$$results" || results=/dev/null; \
+ all=` grep "^$$ws*:test-result:" $$results | wc -l`; \
+ pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \
+ fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \
+ skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \
+ xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+ xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+ error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+ if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+ success=true; \
+ else \
+ success=false; \
+ fi; \
+ br='==================='; br=$$br$$br$$br$$br; \
+ result_count () \
+ { \
+ if test x"$$1" = x"--maybe-color"; then \
+ maybe_colorize=yes; \
+ elif test x"$$1" = x"--no-color"; then \
+ maybe_colorize=no; \
+ else \
+ echo "$@: invalid 'result_count' usage" >&2; exit 4; \
+ fi; \
+ shift; \
+ desc=$$1 count=$$2; \
+ if test $$maybe_colorize = yes && test $$count -gt 0; then \
+ color_start=$$3 color_end=$$std; \
+ else \
+ color_start= color_end=; \
+ fi; \
+ echo "$${color_start}# $$desc $$count$${color_end}"; \
+ }; \
+ create_testsuite_report () \
+ { \
+ result_count $$1 "TOTAL:" $$all "$$brg"; \
+ result_count $$1 "PASS: " $$pass "$$grn"; \
+ result_count $$1 "SKIP: " $$skip "$$blu"; \
+ result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+ result_count $$1 "FAIL: " $$fail "$$red"; \
+ result_count $$1 "XPASS:" $$xpass "$$red"; \
+ result_count $$1 "ERROR:" $$error "$$mgn"; \
+ }; \
+ { \
+ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \
+ $(am__rst_title); \
+ create_testsuite_report --no-color; \
+ echo; \
+ echo ".. contents:: :depth: 2"; \
+ echo; \
+ for b in $$bases; do echo $$b; done \
+ | $(am__create_global_log); \
+ } >$(TEST_SUITE_LOG).tmp || exit 1; \
+ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \
+ if $$success; then \
+ col="$$grn"; \
+ else \
+ col="$$red"; \
+ test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \
+ fi; \
+ echo "$${col}$$br$${std}"; \
+ echo "$${col}Testsuite summary"$(AM_TESTSUITE_SUMMARY_HEADER)"$${std}"; \
+ echo "$${col}$$br$${std}"; \
+ create_testsuite_report --maybe-color; \
+ echo "$$col$$br$$std"; \
+ if $$success; then :; else \
+ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \
+ if test -n "$(PACKAGE_BUGREPORT)"; then \
+ echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \
+ fi; \
+ echo "$$col$$br$$std"; \
+ fi; \
+ $$success || exit 1
+
+check-TESTS:
+ @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list
+ @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+ log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+ exit $$?;
+recheck: all
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ bases=`for i in $$bases; do echo $$i; done \
+ | $(am__list_recheck_tests)` || exit 1; \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ log_list=`echo $$log_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+ am__force_recheck=am--force-recheck \
+ TEST_LOGS="$$log_list"; \
+ exit $$?
+test-eevl.log: test-eevl$(EXEEXT)
+ @p='test-eevl$(EXEEXT)'; \
+ b='test-eevl'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+ @p='$<'; \
+ $(am__set_b); \
+ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@ @p='$<'; \
+@am__EXEEXT_TRUE@ $(am__set_b); \
+@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT)
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) check-am
+all-am: Makefile $(LTLIBRARIES) $(DATA) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libgimpwidgetsincludedir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-am
+install-exec: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+ -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+ -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+ -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/gimp3migration.Plo
+ -rm -f ./$(DEPDIR)/gimpbrowser.Plo
+ -rm -f ./$(DEPDIR)/gimpbusybox.Plo
+ -rm -f ./$(DEPDIR)/gimpbutton.Plo
+ -rm -f ./$(DEPDIR)/gimpcairo-utils.Plo
+ -rm -f ./$(DEPDIR)/gimpcellrenderercolor.Plo
+ -rm -f ./$(DEPDIR)/gimpcellrenderertoggle.Plo
+ -rm -f ./$(DEPDIR)/gimpchainbutton.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorarea.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorbutton.Plo
+ -rm -f ./$(DEPDIR)/gimpcolordisplay.Plo
+ -rm -f ./$(DEPDIR)/gimpcolordisplaystack.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorhexentry.Plo
+ -rm -f ./$(DEPDIR)/gimpcolornotebook.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorprofilechooserdialog.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorprofilecombobox.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorprofilestore.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorprofileview.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorscale.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorscales.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorselect.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorselection.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorselector.Plo
+ -rm -f ./$(DEPDIR)/gimpcontroller.Plo
+ -rm -f ./$(DEPDIR)/gimpdialog.Plo
+ -rm -f ./$(DEPDIR)/gimpeevl.Plo
+ -rm -f ./$(DEPDIR)/gimpenumcombobox.Plo
+ -rm -f ./$(DEPDIR)/gimpenumlabel.Plo
+ -rm -f ./$(DEPDIR)/gimpenumstore.Plo
+ -rm -f ./$(DEPDIR)/gimpenumwidgets.Plo
+ -rm -f ./$(DEPDIR)/gimpfileentry.Plo
+ -rm -f ./$(DEPDIR)/gimpframe.Plo
+ -rm -f ./$(DEPDIR)/gimphelpui.Plo
+ -rm -f ./$(DEPDIR)/gimphintbox.Plo
+ -rm -f ./$(DEPDIR)/gimpicons.Plo
+ -rm -f ./$(DEPDIR)/gimpintcombobox.Plo
+ -rm -f ./$(DEPDIR)/gimpintstore.Plo
+ -rm -f ./$(DEPDIR)/gimpmemsizeentry.Plo
+ -rm -f ./$(DEPDIR)/gimpnumberpairentry.Plo
+ -rm -f ./$(DEPDIR)/gimpoffsetarea.Plo
+ -rm -f ./$(DEPDIR)/gimpoldwidgets.Plo
+ -rm -f ./$(DEPDIR)/gimppageselector.Plo
+ -rm -f ./$(DEPDIR)/gimppatheditor.Plo
+ -rm -f ./$(DEPDIR)/gimppickbutton-default.Plo
+ -rm -f ./$(DEPDIR)/gimppickbutton-kwin.Plo
+ -rm -f ./$(DEPDIR)/gimppickbutton-quartz.Plo
+ -rm -f ./$(DEPDIR)/gimppickbutton-win32.Plo
+ -rm -f ./$(DEPDIR)/gimppickbutton-xdg.Plo
+ -rm -f ./$(DEPDIR)/gimppickbutton.Plo
+ -rm -f ./$(DEPDIR)/gimppixmap.Plo
+ -rm -f ./$(DEPDIR)/gimppreview.Plo
+ -rm -f ./$(DEPDIR)/gimppreviewarea.Plo
+ -rm -f ./$(DEPDIR)/gimppropwidgets.Plo
+ -rm -f ./$(DEPDIR)/gimpquerybox.Plo
+ -rm -f ./$(DEPDIR)/gimpruler.Plo
+ -rm -f ./$(DEPDIR)/gimpscaleentry.Plo
+ -rm -f ./$(DEPDIR)/gimpscrolledpreview.Plo
+ -rm -f ./$(DEPDIR)/gimpsizeentry.Plo
+ -rm -f ./$(DEPDIR)/gimpspinbutton.Plo
+ -rm -f ./$(DEPDIR)/gimpstringcombobox.Plo
+ -rm -f ./$(DEPDIR)/gimpunitcombobox.Plo
+ -rm -f ./$(DEPDIR)/gimpunitmenu.Plo
+ -rm -f ./$(DEPDIR)/gimpunitstore.Plo
+ -rm -f ./$(DEPDIR)/gimpwidgets-error.Plo
+ -rm -f ./$(DEPDIR)/gimpwidgets-private.Plo
+ -rm -f ./$(DEPDIR)/gimpwidgets.Plo
+ -rm -f ./$(DEPDIR)/gimpwidgetsenums.Plo
+ -rm -f ./$(DEPDIR)/gimpwidgetsmarshal.Plo
+ -rm -f ./$(DEPDIR)/gimpwidgetsutils.Plo
+ -rm -f ./$(DEPDIR)/gimpzoommodel.Plo
+ -rm -f ./$(DEPDIR)/test-eevl.Po
+ -rm -f ./$(DEPDIR)/test-preview-area.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-data-local \
+ install-libgimpwidgetsincludeHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/gimp3migration.Plo
+ -rm -f ./$(DEPDIR)/gimpbrowser.Plo
+ -rm -f ./$(DEPDIR)/gimpbusybox.Plo
+ -rm -f ./$(DEPDIR)/gimpbutton.Plo
+ -rm -f ./$(DEPDIR)/gimpcairo-utils.Plo
+ -rm -f ./$(DEPDIR)/gimpcellrenderercolor.Plo
+ -rm -f ./$(DEPDIR)/gimpcellrenderertoggle.Plo
+ -rm -f ./$(DEPDIR)/gimpchainbutton.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorarea.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorbutton.Plo
+ -rm -f ./$(DEPDIR)/gimpcolordisplay.Plo
+ -rm -f ./$(DEPDIR)/gimpcolordisplaystack.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorhexentry.Plo
+ -rm -f ./$(DEPDIR)/gimpcolornotebook.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorprofilechooserdialog.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorprofilecombobox.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorprofilestore.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorprofileview.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorscale.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorscales.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorselect.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorselection.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorselector.Plo
+ -rm -f ./$(DEPDIR)/gimpcontroller.Plo
+ -rm -f ./$(DEPDIR)/gimpdialog.Plo
+ -rm -f ./$(DEPDIR)/gimpeevl.Plo
+ -rm -f ./$(DEPDIR)/gimpenumcombobox.Plo
+ -rm -f ./$(DEPDIR)/gimpenumlabel.Plo
+ -rm -f ./$(DEPDIR)/gimpenumstore.Plo
+ -rm -f ./$(DEPDIR)/gimpenumwidgets.Plo
+ -rm -f ./$(DEPDIR)/gimpfileentry.Plo
+ -rm -f ./$(DEPDIR)/gimpframe.Plo
+ -rm -f ./$(DEPDIR)/gimphelpui.Plo
+ -rm -f ./$(DEPDIR)/gimphintbox.Plo
+ -rm -f ./$(DEPDIR)/gimpicons.Plo
+ -rm -f ./$(DEPDIR)/gimpintcombobox.Plo
+ -rm -f ./$(DEPDIR)/gimpintstore.Plo
+ -rm -f ./$(DEPDIR)/gimpmemsizeentry.Plo
+ -rm -f ./$(DEPDIR)/gimpnumberpairentry.Plo
+ -rm -f ./$(DEPDIR)/gimpoffsetarea.Plo
+ -rm -f ./$(DEPDIR)/gimpoldwidgets.Plo
+ -rm -f ./$(DEPDIR)/gimppageselector.Plo
+ -rm -f ./$(DEPDIR)/gimppatheditor.Plo
+ -rm -f ./$(DEPDIR)/gimppickbutton-default.Plo
+ -rm -f ./$(DEPDIR)/gimppickbutton-kwin.Plo
+ -rm -f ./$(DEPDIR)/gimppickbutton-quartz.Plo
+ -rm -f ./$(DEPDIR)/gimppickbutton-win32.Plo
+ -rm -f ./$(DEPDIR)/gimppickbutton-xdg.Plo
+ -rm -f ./$(DEPDIR)/gimppickbutton.Plo
+ -rm -f ./$(DEPDIR)/gimppixmap.Plo
+ -rm -f ./$(DEPDIR)/gimppreview.Plo
+ -rm -f ./$(DEPDIR)/gimppreviewarea.Plo
+ -rm -f ./$(DEPDIR)/gimppropwidgets.Plo
+ -rm -f ./$(DEPDIR)/gimpquerybox.Plo
+ -rm -f ./$(DEPDIR)/gimpruler.Plo
+ -rm -f ./$(DEPDIR)/gimpscaleentry.Plo
+ -rm -f ./$(DEPDIR)/gimpscrolledpreview.Plo
+ -rm -f ./$(DEPDIR)/gimpsizeentry.Plo
+ -rm -f ./$(DEPDIR)/gimpspinbutton.Plo
+ -rm -f ./$(DEPDIR)/gimpstringcombobox.Plo
+ -rm -f ./$(DEPDIR)/gimpunitcombobox.Plo
+ -rm -f ./$(DEPDIR)/gimpunitmenu.Plo
+ -rm -f ./$(DEPDIR)/gimpunitstore.Plo
+ -rm -f ./$(DEPDIR)/gimpwidgets-error.Plo
+ -rm -f ./$(DEPDIR)/gimpwidgets-private.Plo
+ -rm -f ./$(DEPDIR)/gimpwidgets.Plo
+ -rm -f ./$(DEPDIR)/gimpwidgetsenums.Plo
+ -rm -f ./$(DEPDIR)/gimpwidgetsmarshal.Plo
+ -rm -f ./$(DEPDIR)/gimpwidgetsutils.Plo
+ -rm -f ./$(DEPDIR)/gimpzoommodel.Plo
+ -rm -f ./$(DEPDIR)/test-eevl.Po
+ -rm -f ./$(DEPDIR)/test-preview-area.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libLTLIBRARIES \
+ uninstall-libgimpwidgetsincludeHEADERS uninstall-local
+
+.MAKE: all check check-am install install-am install-exec \
+ install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-TESTS \
+ check-am clean clean-generic clean-libLTLIBRARIES \
+ clean-libtool cscopelist-am ctags ctags-am distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am \
+ install-data-local install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-libLTLIBRARIES \
+ install-libgimpwidgetsincludeHEADERS install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ recheck tags tags-am uninstall uninstall-am \
+ uninstall-libLTLIBRARIES \
+ uninstall-libgimpwidgetsincludeHEADERS uninstall-local
+
+.PRECIOUS: Makefile
+
+
+@OS_WIN32_TRUE@install-libtool-import-lib:
+@OS_WIN32_TRUE@ $(INSTALL) .libs/libgimpwidgets-$(GIMP_API_VERSION).dll.a $(DESTDIR)$(libdir)
+@OS_WIN32_TRUE@ $(INSTALL) $(srcdir)/gimpwidgets.def $(DESTDIR)$(libdir)
+
+@OS_WIN32_TRUE@uninstall-libtool-import-lib:
+@OS_WIN32_TRUE@ -rm $(DESTDIR)$(libdir)/libgimpwidgets-$(GIMP_API_VERSION).dll.a
+@OS_WIN32_TRUE@ -rm $(DESTDIR)$(libdir)/gimpwidgets.def
+@OS_WIN32_FALSE@install-libtool-import-lib:
+@OS_WIN32_FALSE@uninstall-libtool-import-lib:
+
+@MS_LIB_AVAILABLE_TRUE@install-ms-lib:
+@MS_LIB_AVAILABLE_TRUE@ $(INSTALL) gimpwidgets-$(GIMP_API_VERSION).lib $(DESTDIR)$(libdir)
+
+@MS_LIB_AVAILABLE_TRUE@uninstall-ms-lib:
+@MS_LIB_AVAILABLE_TRUE@ -rm $(DESTDIR)$(libdir)/gimpwidgets-$(GIMP_API_VERSION).lib
+
+@MS_LIB_AVAILABLE_TRUE@gimpwidgets-@GIMP_API_VERSION@.lib: gimpwidgets.def
+@MS_LIB_AVAILABLE_TRUE@ lib -name:libgimpwidgets-$(GIMP_API_VERSION)-@LT_CURRENT_MINUS_AGE@.dll -def:gimpwidgets.def -out:$@
+
+@MS_LIB_AVAILABLE_FALSE@install-ms-lib:
+@MS_LIB_AVAILABLE_FALSE@uninstall-ms-lib:
+
+xgen-wec: $(srcdir)/gimpwidgetsenums.h $(GIMP_MKENUMS) Makefile.am
+ $(AM_V_GEN) $(GIMP_MKENUMS) \
+ --fhead "#include \"config.h\"\n#include <gio/gio.h>\n#include \"libgimpbase/gimpbase.h\"\n#include \"gimpwidgetsenums.h\"\n#include \"libgimp/libgimp-intl.h\"" \
+ --fprod "\n/* enumerations from \"@basename@\" */" \
+ --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \
+ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \
+ --vtail " { 0, NULL, NULL }\n };\n" \
+ --dhead " static const Gimp@Type@Desc descs[] =\n {" \
+ --dprod " { @VALUENAME@, @valuedesc@, @valuehelp@ },@if ('@valueabbrev@' ne 'NULL')@\n /* Translators: this is an abbreviated version of @valueudesc@.\n Keep it short. */\n { @VALUENAME@, @valueabbrev@, NULL },@endif@" \
+ --dtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n {\n type = g_@type@_register_static (\"@EnumName@\", values);\n gimp_type_set_translation_domain (type, GETTEXT_PACKAGE \"-libgimp\");\n gimp_type_set_translation_context (type, \"@enumnick@\");\n gimp_@type@_set_value_descriptions (type, descs);\n }\n\n return type;\n}\n" \
+ $< > $@
+
+# copy the generated enum file back to the source directory only if it's
+# changed; otherwise, only update its timestamp, so that the recipe isn't
+# executed again on the next build, however, allow this to (harmlessly) fail,
+# to support building from a read-only source tree.
+$(srcdir)/gimpwidgetsenums.c: xgen-wec
+ $(AM_V_GEN) if ! cmp -s $< $@; then \
+ cp $< $@; \
+ else \
+ touch $@ 2> /dev/null \
+ || true; \
+ fi
+
+gimpwidgetsmarshal.h: $(srcdir)/gimpwidgetsmarshal.list
+ $(AM_V_GEN) $(GLIB_GENMARSHAL) --prefix=_gimp_widgets_marshal $(srcdir)/gimpwidgetsmarshal.list --header >> xgen-wmh \
+ && (cmp -s xgen-wmh $(@F) || cp xgen-wmh $(@F)) \
+ && rm -f xgen-wmh xgen-wmh~
+
+gimpwidgetsmarshal.c: gimpwidgetsmarshal.h
+ $(AM_V_GEN) $(GLIB_GENMARSHAL) --prefix=_gimp_widgets_marshal $(srcdir)/gimpwidgetsmarshal.list --header --body >> xgen-wmc \
+ && cp xgen-wmc $(@F) \
+ && rm -f xgen-wmc xgen-wmc~
+
+install-data-local: install-ms-lib install-libtool-import-lib
+
+uninstall-local: uninstall-ms-lib uninstall-libtool-import-lib
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libgimpwidgets/gimp3migration.c b/libgimpwidgets/gimp3migration.c
new file mode 100644
index 0000000..640590e
--- /dev/null
+++ b/libgimpwidgets/gimp3migration.c
@@ -0,0 +1,262 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimp3migration.c
+ * Copyright (C) 2011 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <math.h>
+
+#include <gtk/gtk.h>
+
+#include "gimpwidgetstypes.h"
+
+#include "gimp3migration.h"
+
+
+GtkWidget *
+gtk_box_new (GtkOrientation orientation,
+ gint spacing)
+{
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ return gtk_hbox_new (FALSE, spacing);
+ else
+ return gtk_vbox_new (FALSE, spacing);
+}
+
+GtkWidget *
+gtk_button_box_new (GtkOrientation orientation)
+{
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ return gtk_hbutton_box_new ();
+ else
+ return gtk_vbutton_box_new ();
+}
+
+GtkWidget *
+gtk_paned_new (GtkOrientation orientation)
+{
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ return gtk_hpaned_new ();
+ else
+ return gtk_vpaned_new ();
+}
+
+GtkWidget *
+gtk_scale_new (GtkOrientation orientation,
+ GtkAdjustment *adjustment)
+{
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ return gtk_hscale_new (adjustment);
+ else
+ return gtk_vscale_new (adjustment);
+}
+
+GtkWidget *
+gtk_scrollbar_new (GtkOrientation orientation,
+ GtkAdjustment *adjustment)
+{
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ return gtk_hscrollbar_new (adjustment);
+ else
+ return gtk_vscrollbar_new (adjustment);
+}
+
+GtkWidget *
+gtk_separator_new (GtkOrientation orientation)
+{
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ return gtk_hseparator_new ();
+ else
+ return gtk_vseparator_new ();
+}
+
+#if ! GTK_CHECK_VERSION (3, 3, 0)
+
+gboolean
+gdk_event_triggers_context_menu (const GdkEvent *event)
+{
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ if (event->type == GDK_BUTTON_PRESS)
+ {
+ GdkEventButton *bevent = (GdkEventButton *) event;
+
+ if (bevent->button == 3 &&
+ ! (bevent->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK)))
+ return TRUE;
+
+#ifdef GDK_WINDOWING_QUARTZ
+ if (bevent->button == 1 &&
+ ! (bevent->state & (GDK_BUTTON2_MASK | GDK_BUTTON3_MASK)) &&
+ (bevent->state & GDK_CONTROL_MASK))
+ return TRUE;
+#endif
+ }
+
+ return FALSE;
+}
+
+GdkModifierType
+gdk_keymap_get_modifier_mask (GdkKeymap *keymap,
+ GdkModifierIntent intent)
+{
+ g_return_val_if_fail (GDK_IS_KEYMAP (keymap), 0);
+
+#ifdef GDK_WINDOWING_QUARTZ
+ switch (intent)
+ {
+ case GDK_MODIFIER_INTENT_PRIMARY_ACCELERATOR:
+ return GDK_MOD2_MASK;
+
+ case GDK_MODIFIER_INTENT_CONTEXT_MENU:
+ return GDK_CONTROL_MASK;
+
+ case GDK_MODIFIER_INTENT_EXTEND_SELECTION:
+ return GDK_SHIFT_MASK;
+
+ case GDK_MODIFIER_INTENT_MODIFY_SELECTION:
+ return GDK_MOD2_MASK;
+
+ case GDK_MODIFIER_INTENT_NO_TEXT_INPUT:
+ return GDK_MOD2_MASK | GDK_CONTROL_MASK;
+
+ default:
+ g_return_val_if_reached (0);
+ }
+#else
+ switch (intent)
+ {
+ case GDK_MODIFIER_INTENT_PRIMARY_ACCELERATOR:
+ return GDK_CONTROL_MASK;
+
+ case GDK_MODIFIER_INTENT_CONTEXT_MENU:
+ return 0;
+
+ case GDK_MODIFIER_INTENT_EXTEND_SELECTION:
+ return GDK_SHIFT_MASK;
+
+ case GDK_MODIFIER_INTENT_MODIFY_SELECTION:
+ return GDK_CONTROL_MASK;
+
+ case GDK_MODIFIER_INTENT_NO_TEXT_INPUT:
+ return GDK_MOD1_MASK | GDK_CONTROL_MASK;
+
+ default:
+ g_return_val_if_reached (0);
+ }
+#endif
+}
+
+GdkModifierType
+gtk_widget_get_modifier_mask (GtkWidget *widget,
+ GdkModifierIntent intent)
+{
+ GdkDisplay *display;
+
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
+
+ display = gtk_widget_get_display (widget);
+
+ return gdk_keymap_get_modifier_mask (gdk_keymap_get_for_display (display),
+ intent);
+}
+
+#endif /* GTK+ 3.3 */
+
+gboolean
+gdk_cairo_get_clip_rectangle (cairo_t *cr,
+ GdkRectangle *rect)
+{
+ double x1, y1, x2, y2;
+ gboolean clip_exists;
+
+ cairo_clip_extents (cr, &x1, &y1, &x2, &y2);
+
+ clip_exists = x1 < x2 && y1 < y2;
+
+ if (rect)
+ {
+ x1 = floor (x1);
+ y1 = floor (y1);
+ x2 = ceil (x2);
+ y2 = ceil (y2);
+
+ rect->x = CLAMP (x1, G_MININT, G_MAXINT);
+ rect->y = CLAMP (y1, G_MININT, G_MAXINT);
+ rect->width = CLAMP (x2 - x1, G_MININT, G_MAXINT);
+ rect->height = CLAMP (y2 - y1, G_MININT, G_MAXINT);
+ }
+
+ return clip_exists;
+}
+
+void
+gdk_screen_get_monitor_workarea (GdkScreen *screen,
+ gint monitor_num,
+ GdkRectangle *dest)
+{
+ gdk_screen_get_monitor_geometry (screen, monitor_num, dest);
+}
+
+void
+gtk_label_set_xalign (GtkLabel *label,
+ gfloat xalign)
+{
+ g_return_if_fail (GTK_IS_LABEL (label));
+
+ xalign = CLAMP (xalign, 0.0, 1.0);
+
+ g_object_set (label, "xalign", xalign, NULL);
+}
+
+gfloat
+gtk_label_get_xalign (GtkLabel *label)
+{
+ gfloat xalign;
+
+ g_return_val_if_fail (GTK_IS_LABEL (label), 0.5);
+
+ g_object_get (label, "xalign", &xalign, NULL);
+
+ return xalign;
+}
+
+void
+gtk_label_set_yalign (GtkLabel *label,
+ gfloat yalign)
+{
+ g_return_if_fail (GTK_IS_LABEL (label));
+
+ yalign = CLAMP (yalign, 0.0, 1.0);
+
+ g_object_set (label, "yalign", yalign, NULL);
+}
+
+gfloat
+gtk_label_get_yalign (GtkLabel *label)
+{
+ gfloat yalign;
+
+ g_return_val_if_fail (GTK_IS_LABEL (label), 0.5);
+
+ g_object_get (label, "yalign", &yalign, NULL);
+
+ return yalign;
+}
diff --git a/libgimpwidgets/gimp3migration.h b/libgimpwidgets/gimp3migration.h
new file mode 100644
index 0000000..53d4ddc
--- /dev/null
+++ b/libgimpwidgets/gimp3migration.h
@@ -0,0 +1,77 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimp3migration.h
+ * Copyright (C) 2011 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_3_MIGRATION_H__
+#define __GIMP_3_MIGRATION_H__
+
+
+/* This file is evil. Its purpose is to keep GIMP's gtk3-port branch
+ * manageable, and contains functions that are only in GTK+ 3.x but
+ * are *not* in GTK+ 2.x. Please just ignore the uglyness and move
+ * along. This file will be removed in GIMP 3.
+ */
+
+GtkWidget * gtk_box_new (GtkOrientation orientation,
+ gint spacing);
+GtkWidget * gtk_button_box_new (GtkOrientation orientation);
+GtkWidget * gtk_paned_new (GtkOrientation orientation);
+GtkWidget * gtk_scale_new (GtkOrientation orientation,
+ GtkAdjustment *adjustment);
+GtkWidget * gtk_scrollbar_new (GtkOrientation orientation,
+ GtkAdjustment *adjustment);
+GtkWidget * gtk_separator_new (GtkOrientation orientation);
+
+
+typedef enum
+{
+ GDK_MODIFIER_INTENT_PRIMARY_ACCELERATOR,
+ GDK_MODIFIER_INTENT_CONTEXT_MENU,
+ GDK_MODIFIER_INTENT_EXTEND_SELECTION,
+ GDK_MODIFIER_INTENT_MODIFY_SELECTION,
+ GDK_MODIFIER_INTENT_NO_TEXT_INPUT
+} GdkModifierIntent;
+
+gboolean gdk_event_triggers_context_menu (const GdkEvent *event);
+GdkModifierType gdk_keymap_get_modifier_mask (GdkKeymap *keymap,
+ GdkModifierIntent intent);
+GdkModifierType gtk_widget_get_modifier_mask (GtkWidget *widget,
+ GdkModifierIntent intent);
+
+gboolean gdk_cairo_get_clip_rectangle (cairo_t *cr,
+ GdkRectangle *rect);
+void gdk_screen_get_monitor_workarea (GdkScreen *screen,
+ gint monitor_num,
+ GdkRectangle *dest);
+
+void gtk_label_set_xalign (GtkLabel *label,
+ gfloat xalign);
+gfloat gtk_label_get_xalign (GtkLabel *label);
+
+void gtk_label_set_yalign (GtkLabel *label,
+ gfloat yalign);
+gfloat gtk_label_get_yalign (GtkLabel *label);
+
+
+#endif /* __GIMP_3_MIGRATION_H__ */
diff --git a/libgimpwidgets/gimpbrowser.c b/libgimpwidgets/gimpbrowser.c
new file mode 100644
index 0000000..c3321f3
--- /dev/null
+++ b/libgimpwidgets/gimpbrowser.c
@@ -0,0 +1,396 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpbrowser.c
+ * Copyright (C) 2005 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpwidgets.h"
+#include "gimpwidgetsmarshal.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpbrowser
+ * @title: GimpBrowser
+ * @short_description: A base class for a documentation browser.
+ *
+ * A base class for a documentation browser.
+ **/
+
+
+enum
+{
+ SEARCH,
+ LAST_SIGNAL
+};
+
+
+static void gimp_browser_dispose (GObject *object);
+
+static void gimp_browser_combo_changed (GtkComboBox *combo,
+ GimpBrowser *browser);
+static void gimp_browser_entry_changed (GtkEntry *entry,
+ GimpBrowser *browser);
+static void gimp_browser_entry_icon_press (GtkEntry *entry,
+ GtkEntryIconPosition icon_pos,
+ GdkEvent *event,
+ GimpBrowser *browser);
+static gboolean gimp_browser_search_timeout (gpointer data);
+
+
+G_DEFINE_TYPE (GimpBrowser, gimp_browser, GTK_TYPE_HPANED)
+
+#define parent_class gimp_browser_parent_class
+
+static guint browser_signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+gimp_browser_class_init (GimpBrowserClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ browser_signals[SEARCH] =
+ g_signal_new ("search",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GimpBrowserClass, search),
+ NULL, NULL,
+ _gimp_widgets_marshal_VOID__STRING_INT,
+ G_TYPE_NONE, 2,
+ G_TYPE_STRING,
+ G_TYPE_INT);
+
+ object_class->dispose = gimp_browser_dispose;
+
+ klass->search = NULL;
+}
+
+static void
+gimp_browser_init (GimpBrowser *browser)
+{
+ GtkWidget *hbox;
+ GtkWidget *label;
+ GtkWidget *scrolled_window;
+ GtkWidget *viewport;
+
+ browser->search_type = -1;
+
+ browser->left_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_paned_pack1 (GTK_PANED (browser), browser->left_vbox, FALSE, TRUE);
+ gtk_widget_show (browser->left_vbox);
+
+ /* search entry */
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (browser->left_vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ label = gtk_label_new_with_mnemonic (_("_Search:"));
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ browser->search_entry = gtk_entry_new ();
+ gtk_box_pack_start (GTK_BOX (hbox), browser->search_entry, TRUE, TRUE, 0);
+ gtk_widget_show (browser->search_entry);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), browser->search_entry);
+
+ g_signal_connect (browser->search_entry, "changed",
+ G_CALLBACK (gimp_browser_entry_changed),
+ browser);
+
+ gtk_entry_set_icon_from_icon_name (GTK_ENTRY (browser->search_entry),
+ GTK_ENTRY_ICON_SECONDARY, "edit-clear");
+ gtk_entry_set_icon_activatable (GTK_ENTRY (browser->search_entry),
+ GTK_ENTRY_ICON_SECONDARY, TRUE);
+ gtk_entry_set_icon_sensitive (GTK_ENTRY (browser->search_entry),
+ GTK_ENTRY_ICON_SECONDARY, FALSE);
+
+ g_signal_connect (browser->search_entry, "icon-press",
+ G_CALLBACK (gimp_browser_entry_icon_press),
+ browser);
+
+ /* count label */
+
+ browser->count_label = gtk_label_new (_("No matches"));
+ gtk_label_set_xalign (GTK_LABEL (browser->count_label), 0.0);
+ gimp_label_set_attributes (GTK_LABEL (browser->count_label),
+ PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
+ -1);
+ gtk_box_pack_end (GTK_BOX (browser->left_vbox), browser->count_label,
+ FALSE, FALSE, 0);
+ gtk_widget_show (browser->count_label);
+
+ /* scrolled window */
+
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_ALWAYS);
+ gtk_paned_pack2 (GTK_PANED (browser), scrolled_window, TRUE, TRUE);
+ gtk_widget_show (scrolled_window);
+
+ viewport = gtk_viewport_new (NULL, NULL);
+ gtk_container_add (GTK_CONTAINER (scrolled_window), viewport);
+ gtk_widget_show (viewport);
+
+ browser->right_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (browser->right_vbox), 12);
+ gtk_container_add (GTK_CONTAINER (viewport), browser->right_vbox);
+ gtk_widget_show (browser->right_vbox);
+
+ gtk_widget_grab_focus (browser->search_entry);
+}
+
+static void
+gimp_browser_dispose (GObject *object)
+{
+ GimpBrowser *browser = GIMP_BROWSER (object);
+
+ if (browser->search_timeout_id)
+ {
+ g_source_remove (browser->search_timeout_id);
+ browser->search_timeout_id = 0;
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+
+/* public functions */
+
+
+/**
+ * gimp_browser_new:
+ *
+ * Create a new #GimpBrowser widget.
+ *
+ * Return Value: a newly created #GimpBrowser.
+ *
+ * Since: 2.4
+ **/
+GtkWidget *
+gimp_browser_new (void)
+{
+ return g_object_new (GIMP_TYPE_BROWSER, NULL);
+}
+
+/**
+ * gimp_browser_add_search_types:
+ * @browser: a #GimpBrowser widget
+ * @first_type_label: the label of the first search type
+ * @first_type_id: an integer that identifies the first search type
+ * @...: a %NULL-terminated list of more labels and ids.
+ *
+ * Populates the #GtkComboBox with search types.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_browser_add_search_types (GimpBrowser *browser,
+ const gchar *first_type_label,
+ gint first_type_id,
+ ...)
+{
+ g_return_if_fail (GIMP_IS_BROWSER (browser));
+ g_return_if_fail (first_type_label != NULL);
+
+ if (! browser->search_type_combo)
+ {
+ GtkWidget *combo;
+ va_list args;
+
+ va_start (args, first_type_id);
+ combo = gimp_int_combo_box_new_valist (first_type_label,
+ first_type_id,
+ args);
+ va_end (args);
+
+ gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (combo), FALSE);
+
+ browser->search_type_combo = combo;
+ browser->search_type = first_type_id;
+
+ gtk_box_pack_end (GTK_BOX (gtk_widget_get_parent (browser->search_entry)),
+ combo, FALSE, FALSE, 0);
+ gtk_widget_show (combo);
+
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
+ browser->search_type,
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &browser->search_type);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (gimp_browser_combo_changed),
+ browser);
+ }
+ else
+ {
+ gimp_int_combo_box_append (GIMP_INT_COMBO_BOX (browser->search_type_combo),
+ first_type_label, first_type_id,
+ NULL);
+ }
+}
+
+/**
+ * gimp_browser_set_widget:
+ * @browser: a #GimpBrowser widget
+ * @widget: a #GtkWidget
+ *
+ * Sets the widget to appear on the right side of the @browser.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_browser_set_widget (GimpBrowser *browser,
+ GtkWidget *widget)
+{
+ g_return_if_fail (GIMP_IS_BROWSER (browser));
+ g_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget));
+
+ if (widget == browser->right_widget)
+ return;
+
+ if (browser->right_widget)
+ gtk_container_remove (GTK_CONTAINER (browser->right_vbox),
+ browser->right_widget);
+
+ browser->right_widget = widget;
+
+ if (widget)
+ {
+ gtk_box_pack_start (GTK_BOX (browser->right_vbox), widget,
+ FALSE, FALSE, 0);
+ gtk_widget_show (widget);
+ }
+}
+
+/**
+ * gimp_browser_show_message:
+ * @browser: a #GimpBrowser widget
+ * @message: text message
+ *
+ * Displays @message in the right side of the @browser. Unless the right
+ * side already contains a #GtkLabel, the widget previously added with
+ * gimp_browser_set_widget() is removed and replaced by a #GtkLabel.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_browser_show_message (GimpBrowser *browser,
+ const gchar *message)
+{
+ g_return_if_fail (GIMP_IS_BROWSER (browser));
+ g_return_if_fail (message != NULL);
+
+ if (GTK_IS_LABEL (browser->right_widget))
+ {
+ gtk_label_set_text (GTK_LABEL (browser->right_widget), message);
+ }
+ else
+ {
+ GtkWidget *label = gtk_label_new (message);
+
+ gimp_label_set_attributes (GTK_LABEL (label),
+ PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
+ -1);
+ gimp_browser_set_widget (browser, label);
+ }
+
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+}
+
+
+/* private functions */
+
+static void
+gimp_browser_queue_search (GimpBrowser *browser)
+{
+ if (browser->search_timeout_id)
+ g_source_remove (browser->search_timeout_id);
+
+ browser->search_timeout_id =
+ g_timeout_add (100, gimp_browser_search_timeout, browser);
+}
+
+static void
+gimp_browser_combo_changed (GtkComboBox *combo,
+ GimpBrowser *browser)
+{
+ gimp_browser_queue_search (browser);
+}
+
+static void
+gimp_browser_entry_changed (GtkEntry *entry,
+ GimpBrowser *browser)
+{
+ gimp_browser_queue_search (browser);
+
+ gtk_entry_set_icon_sensitive (entry,
+ GTK_ENTRY_ICON_SECONDARY,
+ gtk_entry_get_text_length (entry) > 0);
+}
+
+static void
+gimp_browser_entry_icon_press (GtkEntry *entry,
+ GtkEntryIconPosition icon_pos,
+ GdkEvent *event,
+ GimpBrowser *browser)
+{
+ GdkEventButton *bevent = (GdkEventButton *) event;
+
+ if (icon_pos == GTK_ENTRY_ICON_SECONDARY && bevent->button == 1)
+ {
+ gtk_entry_set_text (entry, "");
+ }
+}
+
+static gboolean
+gimp_browser_search_timeout (gpointer data)
+{
+ GimpBrowser *browser = GIMP_BROWSER (data);
+ const gchar *search_string;
+
+ GDK_THREADS_ENTER();
+
+ search_string = gtk_entry_get_text (GTK_ENTRY (browser->search_entry));
+
+ if (! search_string)
+ search_string = "";
+
+ g_signal_emit (browser, browser_signals[SEARCH], 0,
+ search_string, browser->search_type);
+
+ browser->search_timeout_id = 0;
+
+ GDK_THREADS_LEAVE();
+
+ return FALSE;
+}
diff --git a/libgimpwidgets/gimpbrowser.h b/libgimpwidgets/gimpbrowser.h
new file mode 100644
index 0000000..c72e2b0
--- /dev/null
+++ b/libgimpwidgets/gimpbrowser.h
@@ -0,0 +1,95 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpbrowser.h
+ * Copyright (C) 2005 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_BROWSER_H__
+#define __GIMP_BROWSER_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+#define GIMP_TYPE_BROWSER (gimp_browser_get_type ())
+#define GIMP_BROWSER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_BROWSER, GimpBrowser))
+#define GIMP_BROWSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_BROWSER, GimpBrowserClass))
+#define GIMP_IS_BROWSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_BROWSER))
+#define GIMP_IS_BROWSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_BROWSER))
+#define GIMP_BROWSER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_BROWSER, GimpBrowserClass))
+
+
+typedef struct _GimpBrowserClass GimpBrowserClass;
+
+struct _GimpBrowser
+{
+ GtkHPaned parent_instance;
+
+ GtkWidget *left_vbox;
+
+ GtkWidget *search_entry;
+ guint search_timeout_id;
+
+ GtkWidget *search_type_combo;
+ gint search_type;
+
+ GtkWidget *count_label;
+
+ GtkWidget *right_vbox;
+ GtkWidget *right_widget;
+};
+
+struct _GimpBrowserClass
+{
+ GtkHPanedClass parent_class;
+
+ void (* search) (GimpBrowser *browser,
+ const gchar *search_string,
+ gint search_type);
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_browser_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_browser_new (void);
+
+void gimp_browser_add_search_types (GimpBrowser *browser,
+ const gchar *first_type_label,
+ gint first_type_id,
+ ...) G_GNUC_NULL_TERMINATED;
+
+void gimp_browser_set_widget (GimpBrowser *browser,
+ GtkWidget *widget);
+void gimp_browser_show_message (GimpBrowser *browser,
+ const gchar *message);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_BROWSER_H__ */
diff --git a/libgimpwidgets/gimpbusybox.c b/libgimpwidgets/gimpbusybox.c
new file mode 100644
index 0000000..f47a520
--- /dev/null
+++ b/libgimpwidgets/gimpbusybox.c
@@ -0,0 +1,236 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpbusybox.c
+ * Copyright (C) 2018 Ell
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "gimpwidgetstypes.h"
+
+#include "gimp3migration.h"
+#include "gimpbusybox.h"
+#include "gimpwidgetsutils.h"
+
+
+/**
+ * SECTION: gimpbusybox
+ * @title: GimpBusyBox
+ * @short_description: A widget indicating an ongoing operation
+ *
+ * #GimpBusyBox displays a styled message, providing indication of
+ * an ongoing operation.
+ **/
+
+
+enum
+{
+ PROP_0,
+ PROP_MESSAGE
+};
+
+
+struct _GimpBusyBoxPrivate
+{
+ GtkLabel *label;
+};
+
+
+/* local function prototypes */
+
+static void gimp_busy_box_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_busy_box_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpBusyBox, gimp_busy_box, GTK_TYPE_ALIGNMENT)
+
+#define parent_class gimp_busy_box_parent_class
+
+
+/* private functions */
+
+
+static void
+gimp_busy_box_class_init (GimpBusyBoxClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->set_property = gimp_busy_box_set_property;
+ object_class->get_property = gimp_busy_box_get_property;
+
+ /**
+ * GimpBusyBox:message:
+ *
+ * Specifies the displayed message.
+ *
+ * Since: 2.10.4
+ **/
+ g_object_class_install_property (object_class, PROP_MESSAGE,
+ g_param_spec_string ("message",
+ "Message",
+ "The message to display",
+ NULL,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+gimp_busy_box_init (GimpBusyBox *box)
+{
+ GtkWidget *hbox;
+ GtkWidget *spinner;
+ GtkWidget *label;
+
+ box->priv = gimp_busy_box_get_instance_private (box);
+
+ gtk_alignment_set (GTK_ALIGNMENT (box), 0.5, 0.5, 0.0, 0.0);
+
+ /* the main hbox */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8);
+ gtk_container_add (GTK_CONTAINER (box), hbox);
+ gtk_widget_show (hbox);
+
+ /* the spinner */
+ spinner = gtk_spinner_new ();
+ gtk_widget_set_size_request (spinner, 16, 16);
+ gtk_spinner_start (GTK_SPINNER (spinner));
+ gtk_box_pack_start (GTK_BOX (hbox), spinner, FALSE, FALSE, 0);
+ gtk_widget_show (spinner);
+
+ /* the label */
+ label = gtk_label_new (NULL);
+ box->priv->label = GTK_LABEL (label);
+ gimp_label_set_attributes (GTK_LABEL (label),
+ PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
+ -1);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+}
+
+static void
+gimp_busy_box_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpBusyBox *box = GIMP_BUSY_BOX (object);
+
+ switch (property_id)
+ {
+ case PROP_MESSAGE:
+ gtk_label_set_text (box->priv->label, g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_busy_box_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpBusyBox *box = GIMP_BUSY_BOX (object);
+
+ switch (property_id)
+ {
+ case PROP_MESSAGE:
+ g_value_set_string (value, gtk_label_get_text (box->priv->label));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+
+/* public functions */
+
+
+/**
+ * gimp_busy_box_new:
+ * @message: (allow-none): the displayed message, or %NULL
+ *
+ * Creates a new #GimpBusyBox widget.
+ *
+ * Returns: A pointer to the new #GimpBusyBox widget.
+ *
+ * Since: 2.10.4
+ **/
+GtkWidget *
+gimp_busy_box_new (const gchar *message)
+{
+ if (message == NULL)
+ message = "";
+
+ return g_object_new (GIMP_TYPE_BUSY_BOX,
+ "message", message,
+ NULL);
+}
+
+/**
+ * gimp_busy_box_set_message:
+ * @box: a #GimpBusyBox
+ * @message: the displayed message
+ *
+ * Sets the displayed message og @box to @message.
+ *
+ * Since: 2.10.4
+ **/
+void
+gimp_busy_box_set_message (GimpBusyBox *box,
+ const gchar *message)
+{
+ g_return_if_fail (GIMP_IS_BUSY_BOX (box));
+ g_return_if_fail (message != NULL);
+
+ g_object_set (box,
+ "message", message,
+ NULL);
+}
+
+/**
+ * gimp_busy_box_get_message:
+ * @box: a #GimpBusyBox
+ *
+ * Returns the displayed message of @box.
+ *
+ * Returns: The displayed message.
+ *
+ * Since: 2.10.4
+ **/
+const gchar *
+gimp_busy_box_get_message (GimpBusyBox *box)
+{
+ g_return_val_if_fail (GIMP_IS_BUSY_BOX (box), NULL);
+
+ return gtk_label_get_text (box->priv->label);
+}
diff --git a/libgimpwidgets/gimpbusybox.h b/libgimpwidgets/gimpbusybox.h
new file mode 100644
index 0000000..f2779ad
--- /dev/null
+++ b/libgimpwidgets/gimpbusybox.h
@@ -0,0 +1,76 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpbusybox.h
+ * Copyright (C) 2018 Ell
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_BUSY_BOX_H__
+#define __GIMP_BUSY_BOX_H__
+
+G_BEGIN_DECLS
+
+#define GIMP_TYPE_BUSY_BOX (gimp_busy_box_get_type ())
+#define GIMP_BUSY_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_BUSY_BOX, GimpBusyBox))
+#define GIMP_BUSY_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_BUSY_BOX, GimpBusyBoxClass))
+#define GIMP_IS_BUSY_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_BUSY_BOX))
+#define GIMP_IS_BUSY_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_BUSY_BOX))
+#define GIMP_BUSY_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_BUSY_BOX, GimpBusyBoxClass))
+
+
+typedef struct _GimpBusyBoxPrivate GimpBusyBoxPrivate;
+typedef struct _GimpBusyBoxClass GimpBusyBoxClass;
+
+struct _GimpBusyBox
+{
+ GtkAlignment parent_instance;
+
+ GimpBusyBoxPrivate *priv;
+};
+
+struct _GimpBusyBoxClass
+{
+ GtkAlignmentClass parent_class;
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+ void (* _gimp_reserved5) (void);
+ void (* _gimp_reserved6) (void);
+ void (* _gimp_reserved7) (void);
+ void (* _gimp_reserved8) (void);
+};
+
+
+GType gimp_busy_box_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_busy_box_new (const gchar *message);
+
+void gimp_busy_box_set_message (GimpBusyBox *box,
+ const gchar *message);
+const gchar * gimp_busy_box_get_message (GimpBusyBox *box);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_BUSY_BOX_H__ */
diff --git a/libgimpwidgets/gimpbutton.c b/libgimpwidgets/gimpbutton.c
new file mode 100644
index 0000000..67039ac
--- /dev/null
+++ b/libgimpwidgets/gimpbutton.c
@@ -0,0 +1,165 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpbutton.c
+ * Copyright (C) 2000-2008 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpbutton.h"
+#include "gimp3migration.h"
+
+
+/**
+ * SECTION: gimpbutton
+ * @title: GimpButton
+ * @short_description: A #GtkButton with a little extra functionality.
+ *
+ * #GimpButton adds an extra signal to the #GtkButton widget that
+ * allows the callback to distinguish a normal click from a click that
+ * was performed with modifier keys pressed.
+ **/
+
+
+enum
+{
+ EXTENDED_CLICKED,
+ LAST_SIGNAL
+};
+
+
+static gboolean gimp_button_button_press (GtkWidget *widget,
+ GdkEventButton *event);
+static void gimp_button_clicked (GtkButton *button);
+
+
+G_DEFINE_TYPE (GimpButton, gimp_button, GTK_TYPE_BUTTON)
+
+#define parent_class gimp_button_parent_class
+
+static guint button_signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+gimp_button_class_init (GimpButtonClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GtkButtonClass *button_class = GTK_BUTTON_CLASS (klass);
+
+ /**
+ * GimpButton::extended-clicked:
+ * @gimpbutton: the object that received the signal.
+ * @arg1: the state of modifier keys when the button was clicked
+ *
+ * This signal is emitted when the button is clicked with a modifier
+ * key pressed.
+ **/
+ button_signals[EXTENDED_CLICKED] =
+ g_signal_new ("extended-clicked",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpButtonClass, extended_clicked),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__FLAGS,
+ G_TYPE_NONE, 1,
+ GDK_TYPE_MODIFIER_TYPE);
+
+ widget_class->button_press_event = gimp_button_button_press;
+
+ button_class->clicked = gimp_button_clicked;
+}
+
+static void
+gimp_button_init (GimpButton *button)
+{
+ button->press_state = 0;
+}
+
+/**
+ * gimp_button_new:
+ *
+ * Creates a new #GimpButton widget.
+ *
+ * Returns: A pointer to the new #GimpButton widget.
+ **/
+GtkWidget *
+gimp_button_new (void)
+{
+ return g_object_new (GIMP_TYPE_BUTTON, NULL);
+}
+
+/**
+ * gimp_button_extended_clicked:
+ * @button: a #GimpButton.
+ * @state: a state as found in #GdkEventButton->state, e.g. #GDK_SHIFT_MASK.
+ *
+ * Emits the button's "extended_clicked" signal.
+ **/
+void
+gimp_button_extended_clicked (GimpButton *button,
+ GdkModifierType state)
+{
+ g_return_if_fail (GIMP_IS_BUTTON (button));
+
+ g_signal_emit (button, button_signals[EXTENDED_CLICKED], 0, state);
+}
+
+static gboolean
+gimp_button_button_press (GtkWidget *widget,
+ GdkEventButton *bevent)
+{
+ GimpButton *button = GIMP_BUTTON (widget);
+
+ if (bevent->button == 1)
+ {
+ button->press_state = bevent->state;
+ }
+ else
+ {
+ button->press_state = 0;
+ }
+
+ return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, bevent);
+}
+
+static void
+gimp_button_clicked (GtkButton *button)
+{
+ if (GIMP_BUTTON (button)->press_state &
+ (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK |
+ gtk_widget_get_modifier_mask (GTK_WIDGET (button),
+ GDK_MODIFIER_INTENT_PRIMARY_ACCELERATOR) |
+ gtk_widget_get_modifier_mask (GTK_WIDGET (button),
+ GDK_MODIFIER_INTENT_EXTEND_SELECTION) |
+ gtk_widget_get_modifier_mask (GTK_WIDGET (button),
+ GDK_MODIFIER_INTENT_MODIFY_SELECTION)))
+ {
+ g_signal_stop_emission_by_name (button, "clicked");
+
+ gimp_button_extended_clicked (GIMP_BUTTON (button),
+ GIMP_BUTTON (button)->press_state);
+ }
+ else if (GTK_BUTTON_CLASS (parent_class)->clicked)
+ {
+ GTK_BUTTON_CLASS (parent_class)->clicked (button);
+ }
+}
diff --git a/libgimpwidgets/gimpbutton.h b/libgimpwidgets/gimpbutton.h
new file mode 100644
index 0000000..d1ebc36
--- /dev/null
+++ b/libgimpwidgets/gimpbutton.h
@@ -0,0 +1,77 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpbutton.h
+ * Copyright (C) 2001 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_BUTTON_H__
+#define __GIMP_BUTTON_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+#define GIMP_TYPE_BUTTON (gimp_button_get_type ())
+#define GIMP_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_BUTTON, GimpButton))
+#define GIMP_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_BUTTON, GimpButtonClass))
+#define GIMP_IS_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_BUTTON))
+#define GIMP_IS_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_BUTTON))
+#define GIMP_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_BUTTON, GimpButtonClass))
+
+
+typedef struct _GimpButtonClass GimpButtonClass;
+
+struct _GimpButton
+{
+ GtkButton parent_instance;
+
+ /*< private >*/
+ GdkModifierType press_state;
+};
+
+struct _GimpButtonClass
+{
+ GtkButtonClass parent_class;
+
+ void (* extended_clicked) (GimpButton *button,
+ GdkModifierType modifier_state);
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_button_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_button_new (void);
+
+void gimp_button_extended_clicked (GimpButton *button,
+ GdkModifierType state);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_BUTTON_H__ */
diff --git a/libgimpwidgets/gimpcairo-utils.c b/libgimpwidgets/gimpcairo-utils.c
new file mode 100644
index 0000000..a956f28
--- /dev/null
+++ b/libgimpwidgets/gimpcairo-utils.c
@@ -0,0 +1,202 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcairo-utils.c
+ * Copyright (C) 2007 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpcolor/gimpcolor.h"
+
+#include "gimpcairo-utils.h"
+
+
+/**
+ * SECTION: gimpcairo-utils
+ * @title: GimpCairo-utils
+ * @short_description: Utility functions for cairo
+ *
+ * Utility functions that make cairo easier to use with common
+ * GIMP data types.
+ **/
+
+
+/**
+ * gimp_cairo_set_focus_line_pattern:
+ * @cr: Cairo context
+ * @widget: widget to draw the focus indicator on
+ *
+ * Sets color and dash pattern for stroking a focus line on the given
+ * @cr. The line pattern is taken from @widget.
+ *
+ * Return value: %TRUE if the widget style has a focus line pattern,
+ * %FALSE otherwise
+ *
+ * Since: 2.6
+ **/
+gboolean
+gimp_cairo_set_focus_line_pattern (cairo_t *cr,
+ GtkWidget *widget)
+{
+ gint8 *dash_list;
+ gboolean retval = FALSE;
+
+ g_return_val_if_fail (cr != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+
+ gtk_widget_style_get (widget,
+ "focus-line-pattern", (gchar *) &dash_list,
+ NULL);
+
+ if (dash_list[0])
+ {
+ /* Taken straight from gtk_default_draw_focus()
+ */
+ gint n_dashes = strlen ((const gchar *) dash_list);
+ gdouble *dashes = g_new (gdouble, n_dashes);
+ gdouble total_length = 0;
+ gint i;
+
+ for (i = 0; i < n_dashes; i++)
+ {
+ dashes[i] = dash_list[i];
+ total_length += dash_list[i];
+ }
+
+ cairo_set_dash (cr, dashes, n_dashes, 0.5);
+
+ g_free (dashes);
+
+ retval = TRUE;
+ }
+
+ g_free (dash_list);
+
+ return retval;
+}
+
+/**
+ * gimp_cairo_surface_create_from_pixbuf:
+ * @pixbuf: a #GdkPixbuf
+ *
+ * Create a Cairo image surface from a GdkPixbuf.
+ *
+ * You should avoid calling this function as there are probably more
+ * efficient ways of achieving the result you are looking for.
+ *
+ * Returns: a #cairo_surface_t.
+ *
+ * Since: 2.6
+ **/
+cairo_surface_t *
+gimp_cairo_surface_create_from_pixbuf (GdkPixbuf *pixbuf)
+{
+ cairo_surface_t *surface;
+ cairo_format_t format;
+ guchar *dest;
+ const guchar *src;
+ gint width;
+ gint height;
+ gint src_stride;
+ gint dest_stride;
+ gint y;
+
+ g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
+
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+
+ switch (gdk_pixbuf_get_n_channels (pixbuf))
+ {
+ case 3:
+ format = CAIRO_FORMAT_RGB24;
+ break;
+ case 4:
+ format = CAIRO_FORMAT_ARGB32;
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ surface = cairo_image_surface_create (format, width, height);
+
+ cairo_surface_flush (surface);
+
+ src = gdk_pixbuf_get_pixels (pixbuf);
+ src_stride = gdk_pixbuf_get_rowstride (pixbuf);
+
+ dest = cairo_image_surface_get_data (surface);
+ dest_stride = cairo_image_surface_get_stride (surface);
+
+ switch (format)
+ {
+ case CAIRO_FORMAT_RGB24:
+ for (y = 0; y < height; y++)
+ {
+ const guchar *s = src;
+ guchar *d = dest;
+ gint w = width;
+
+ while (w--)
+ {
+ GIMP_CAIRO_RGB24_SET_PIXEL (d, s[0], s[1], s[2]);
+
+ s += 3;
+ d += 4;
+ }
+
+ src += src_stride;
+ dest += dest_stride;
+ }
+ break;
+
+ case CAIRO_FORMAT_ARGB32:
+ for (y = 0; y < height; y++)
+ {
+ const guchar *s = src;
+ guchar *d = dest;
+ gint w = width;
+
+ while (w--)
+ {
+ GIMP_CAIRO_ARGB32_SET_PIXEL (d, s[0], s[1], s[2], s[3]);
+
+ s += 4;
+ d += 4;
+ }
+
+ src += src_stride;
+ dest += dest_stride;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ cairo_surface_mark_dirty (surface);
+
+ return surface;
+}
diff --git a/libgimpwidgets/gimpcairo-utils.h b/libgimpwidgets/gimpcairo-utils.h
new file mode 100644
index 0000000..85a5ee4
--- /dev/null
+++ b/libgimpwidgets/gimpcairo-utils.h
@@ -0,0 +1,36 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcairo-utils.h
+ * Copyright (C) 2007 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_CAIRO_UTILS_H__
+#define __GIMP_CAIRO_UTILS_H__
+
+
+gboolean gimp_cairo_set_focus_line_pattern (cairo_t *cr,
+ GtkWidget *widget);
+
+cairo_surface_t * gimp_cairo_surface_create_from_pixbuf (GdkPixbuf *pixbuf);
+
+
+#endif /* __GIMP_CAIRO_UTILS_H__ */
diff --git a/libgimpwidgets/gimpcellrenderercolor.c b/libgimpwidgets/gimpcellrenderercolor.c
new file mode 100644
index 0000000..cfda1e7
--- /dev/null
+++ b/libgimpwidgets/gimpcellrenderercolor.c
@@ -0,0 +1,332 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcellrenderercolor.c
+ * Copyright (C) 2004,2007 Sven Neuman <sven1@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpcolor/gimpcolor.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpcairo-utils.h"
+#include "gimpcellrenderercolor.h"
+
+
+/**
+ * SECTION: gimpcellrenderercolor
+ * @title: GimpCellRendererColor
+ * @short_description: A #GtkCellRenderer to display a #GimpRGB color.
+ *
+ * A #GtkCellRenderer to display a #GimpRGB color.
+ **/
+
+
+#define DEFAULT_ICON_SIZE GTK_ICON_SIZE_MENU
+
+
+enum
+{
+ PROP_0,
+ PROP_COLOR,
+ PROP_OPAQUE,
+ PROP_SIZE
+};
+
+
+static void gimp_cell_renderer_color_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_cell_renderer_color_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_cell_renderer_color_get_size (GtkCellRenderer *cell,
+ GtkWidget *widget,
+ GdkRectangle *rectangle,
+ gint *x_offset,
+ gint *y_offset,
+ gint *width,
+ gint *height);
+static void gimp_cell_renderer_color_render (GtkCellRenderer *cell,
+ GdkWindow *window,
+ GtkWidget *widget,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ GdkRectangle *expose_area,
+ GtkCellRendererState flags);
+
+
+
+G_DEFINE_TYPE (GimpCellRendererColor, gimp_cell_renderer_color,
+ GTK_TYPE_CELL_RENDERER)
+
+#define parent_class gimp_cell_renderer_color_parent_class
+
+
+static void
+gimp_cell_renderer_color_class_init (GimpCellRendererColorClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
+
+ object_class->get_property = gimp_cell_renderer_color_get_property;
+ object_class->set_property = gimp_cell_renderer_color_set_property;
+
+ cell_class->get_size = gimp_cell_renderer_color_get_size;
+ cell_class->render = gimp_cell_renderer_color_render;
+
+ g_object_class_install_property (object_class, PROP_COLOR,
+ g_param_spec_boxed ("color",
+ "Color",
+ "The displayed color",
+ GIMP_TYPE_RGB,
+ GIMP_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_OPAQUE,
+ g_param_spec_boolean ("opaque",
+ "Opaque",
+ "Whether to show transparency",
+ TRUE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_SIZE,
+ g_param_spec_int ("icon-size",
+ "Icon Size",
+ "The cell's size",
+ 0, G_MAXINT,
+ DEFAULT_ICON_SIZE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+gimp_cell_renderer_color_init (GimpCellRendererColor *cell)
+{
+ gimp_rgba_set (&cell->color, 0.0, 0.0, 0.0, 1.0);
+}
+
+static void
+gimp_cell_renderer_color_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpCellRendererColor *cell = GIMP_CELL_RENDERER_COLOR (object);
+
+ switch (param_id)
+ {
+ case PROP_COLOR:
+ g_value_set_boxed (value, &cell->color);
+ break;
+ case PROP_OPAQUE:
+ g_value_set_boolean (value, cell->opaque);
+ break;
+ case PROP_SIZE:
+ g_value_set_int (value, cell->size);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_cell_renderer_color_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpCellRendererColor *cell = GIMP_CELL_RENDERER_COLOR (object);
+ GimpRGB *color;
+
+ switch (param_id)
+ {
+ case PROP_COLOR:
+ color = g_value_get_boxed (value);
+ cell->color = *color;
+ break;
+ case PROP_OPAQUE:
+ cell->opaque = g_value_get_boolean (value);
+ break;
+ case PROP_SIZE:
+ cell->size = g_value_get_int (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_cell_renderer_color_get_size (GtkCellRenderer *cell,
+ GtkWidget *widget,
+ GdkRectangle *cell_area,
+ gint *x_offset,
+ gint *y_offset,
+ gint *width,
+ gint *height)
+{
+ GimpCellRendererColor *color = GIMP_CELL_RENDERER_COLOR (cell);
+ gint calc_width;
+ gint calc_height;
+ gfloat xalign;
+ gfloat yalign;
+ gint xpad;
+ gint ypad;
+
+ gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (widget),
+ color->size, &calc_width, &calc_height);
+ gtk_cell_renderer_get_alignment (cell, &xalign, &yalign);
+ gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
+
+ if (cell_area && calc_width > 0 && calc_height > 0)
+ {
+ if (x_offset)
+ {
+ *x_offset = (((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ?
+ 1.0 - xalign : xalign) *
+ (cell_area->width - calc_width));
+ *x_offset = MAX (*x_offset, 0) + xpad;
+ }
+ if (y_offset)
+ {
+ *y_offset = (yalign * (cell_area->height - calc_height));
+ *y_offset = MAX (*y_offset, 0) + ypad;
+ }
+ }
+ else
+ {
+ if (x_offset)
+ *x_offset = 0;
+ if (y_offset)
+ *y_offset = 0;
+ }
+
+ if (width)
+ *width = calc_width + 2 * xpad;
+ if (height)
+ *height = calc_height + 2 * ypad;
+}
+
+static void
+gimp_cell_renderer_color_render (GtkCellRenderer *cell,
+ GdkWindow *window,
+ GtkWidget *widget,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ GdkRectangle *expose_area,
+ GtkCellRendererState flags)
+{
+ GimpCellRendererColor *color = GIMP_CELL_RENDERER_COLOR (cell);
+ GdkRectangle rect;
+ gint xpad;
+ gint ypad;
+
+ gimp_cell_renderer_color_get_size (cell, widget, cell_area,
+ &rect.x,
+ &rect.y,
+ &rect.width,
+ &rect.height);
+
+ gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
+
+ rect.x += cell_area->x + xpad;
+ rect.y += cell_area->y + ypad;
+ rect.width -= 2 * xpad;
+ rect.height -= 2 * ypad;
+
+ if (rect.width > 2 && rect.height > 2)
+ {
+ cairo_t *cr = gdk_cairo_create (window);
+ GtkStyle *style = gtk_widget_get_style (widget);
+ GtkStateType state;
+
+ cairo_rectangle (cr,
+ rect.x + 1, rect.y + 1,
+ rect.width - 2, rect.height - 2);
+
+ gimp_cairo_set_source_rgb (cr, &color->color);
+ cairo_fill (cr);
+
+ if (! color->opaque && color->color.a < 1.0)
+ {
+ cairo_pattern_t *pattern;
+
+ cairo_move_to (cr, rect.x + 1, rect.y + rect.height - 1);
+ cairo_line_to (cr, rect.x + rect.width - 1, rect.y + rect.height - 1);
+ cairo_line_to (cr, rect.x + rect.width - 1, rect.y + 1);
+ cairo_close_path (cr);
+
+ pattern = gimp_cairo_checkerboard_create (cr,
+ GIMP_CHECK_SIZE_SM,
+ NULL, NULL);
+ cairo_set_source (cr, pattern);
+ cairo_pattern_destroy (pattern);
+
+ cairo_fill_preserve (cr);
+
+ gimp_cairo_set_source_rgba (cr, &color->color);
+ cairo_fill (cr);
+ }
+
+ /* draw border */
+ cairo_rectangle (cr,
+ rect.x + 0.5, rect.y + 0.5,
+ rect.width - 1, rect.height - 1);
+
+ if (! gtk_cell_renderer_get_sensitive (cell) ||
+ ! gtk_widget_is_sensitive (widget))
+ {
+ state = GTK_STATE_INSENSITIVE;
+ }
+ else
+ {
+ state = (flags & GTK_CELL_RENDERER_SELECTED ?
+ GTK_STATE_SELECTED : GTK_STATE_NORMAL);
+ }
+
+ cairo_set_line_width (cr, 1);
+ gdk_cairo_set_source_color (cr, &style->fg[state]);
+ cairo_stroke_preserve (cr);
+
+ cairo_destroy (cr);
+ }
+}
+
+/**
+ * gimp_cell_renderer_color_new:
+ *
+ * Creates a #GtkCellRenderer that displays a color.
+ *
+ * Return value: a new #GimpCellRendererColor
+ *
+ * Since: 2.2
+ **/
+GtkCellRenderer *
+gimp_cell_renderer_color_new (void)
+{
+ return g_object_new (GIMP_TYPE_CELL_RENDERER_COLOR, NULL);
+}
diff --git a/libgimpwidgets/gimpcellrenderercolor.h b/libgimpwidgets/gimpcellrenderercolor.h
new file mode 100644
index 0000000..291df40
--- /dev/null
+++ b/libgimpwidgets/gimpcellrenderercolor.h
@@ -0,0 +1,71 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcellrenderercolor.h
+ * Copyright (C) 2004 Sven Neuman <sven1@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_CELL_RENDERER_COLOR_H__
+#define __GIMP_CELL_RENDERER_COLOR_H__
+
+G_BEGIN_DECLS
+
+
+#define GIMP_TYPE_CELL_RENDERER_COLOR (gimp_cell_renderer_color_get_type ())
+#define GIMP_CELL_RENDERER_COLOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CELL_RENDERER_COLOR, GimpCellRendererColor))
+#define GIMP_CELL_RENDERER_COLOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CELL_RENDERER_COLOR, GimpCellRendererColorClass))
+#define GIMP_IS_CELL_RENDERER_COLOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CELL_RENDERER_COLOR))
+#define GIMP_IS_CELL_RENDERER_COLOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CELL_RENDERER_COLOR))
+#define GIMP_CELL_RENDERER_COLOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CELL_RENDERER_COLOR, GimpCellRendererColorClass))
+
+
+typedef struct _GimpCellRendererColorClass GimpCellRendererColorClass;
+
+struct _GimpCellRendererColor
+{
+ GtkCellRenderer parent_instance;
+
+ GimpRGB color;
+ gboolean opaque;
+ GtkIconSize size;
+ gint border;
+};
+
+struct _GimpCellRendererColorClass
+{
+ GtkCellRendererClass parent_class;
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_cell_renderer_color_get_type (void) G_GNUC_CONST;
+
+GtkCellRenderer * gimp_cell_renderer_color_new (void);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_CELL_RENDERER_COLOR_H__ */
diff --git a/libgimpwidgets/gimpcellrenderertoggle.c b/libgimpwidgets/gimpcellrenderertoggle.c
new file mode 100644
index 0000000..84dff32
--- /dev/null
+++ b/libgimpwidgets/gimpcellrenderertoggle.c
@@ -0,0 +1,576 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcellrenderertoggle.c
+ * Copyright (C) 2003-2004 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpwidgetsmarshal.h"
+#include "gimpcellrenderertoggle.h"
+
+
+/**
+ * SECTION: gimpcellrenderertoggle
+ * @title: GimpCellRendererToggle
+ * @short_description: A #GtkCellRendererToggle that displays icons instead
+ * of a checkbox.
+ *
+ * A #GtkCellRendererToggle that displays icons instead of a checkbox.
+ **/
+
+
+#define DEFAULT_ICON_SIZE GTK_ICON_SIZE_BUTTON
+
+
+enum
+{
+ CLICKED,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_ICON_NAME,
+ PROP_STOCK_ID,
+ PROP_STOCK_SIZE,
+ PROP_OVERRIDE_BACKGROUND
+};
+
+
+typedef struct _GimpCellRendererTogglePrivate GimpCellRendererTogglePrivate;
+
+struct _GimpCellRendererTogglePrivate
+{
+ gchar *icon_name;
+ gboolean override_background;
+};
+
+#define GET_PRIVATE(obj) \
+ ((GimpCellRendererTogglePrivate *) gimp_cell_renderer_toggle_get_instance_private ((GimpCellRendererToggle *) (obj)))
+
+
+static void gimp_cell_renderer_toggle_finalize (GObject *object);
+static void gimp_cell_renderer_toggle_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_cell_renderer_toggle_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_cell_renderer_toggle_get_size (GtkCellRenderer *cell,
+ GtkWidget *widget,
+ GdkRectangle *rectangle,
+ gint *x_offset,
+ gint *y_offset,
+ gint *width,
+ gint *height);
+static void gimp_cell_renderer_toggle_render (GtkCellRenderer *cell,
+ GdkWindow *window,
+ GtkWidget *widget,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ GdkRectangle *expose_area,
+ GtkCellRendererState flags);
+static gboolean gimp_cell_renderer_toggle_activate (GtkCellRenderer *cell,
+ GdkEvent *event,
+ GtkWidget *widget,
+ const gchar *path,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ GtkCellRendererState flags);
+static void gimp_cell_renderer_toggle_create_pixbuf (GimpCellRendererToggle *toggle,
+ GtkWidget *widget);
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpCellRendererToggle, gimp_cell_renderer_toggle,
+ GTK_TYPE_CELL_RENDERER_TOGGLE)
+
+#define parent_class gimp_cell_renderer_toggle_parent_class
+
+static guint toggle_cell_signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+gimp_cell_renderer_toggle_class_init (GimpCellRendererToggleClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
+
+ toggle_cell_signals[CLICKED] =
+ g_signal_new ("clicked",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GimpCellRendererToggleClass, clicked),
+ NULL, NULL,
+ _gimp_widgets_marshal_VOID__STRING_FLAGS,
+ G_TYPE_NONE, 2,
+ G_TYPE_STRING,
+ GDK_TYPE_MODIFIER_TYPE);
+
+ object_class->finalize = gimp_cell_renderer_toggle_finalize;
+ object_class->get_property = gimp_cell_renderer_toggle_get_property;
+ object_class->set_property = gimp_cell_renderer_toggle_set_property;
+
+ cell_class->get_size = gimp_cell_renderer_toggle_get_size;
+ cell_class->render = gimp_cell_renderer_toggle_render;
+ cell_class->activate = gimp_cell_renderer_toggle_activate;
+
+ g_object_class_install_property (object_class, PROP_ICON_NAME,
+ g_param_spec_string ("icon-name",
+ "Icon Name",
+ "The icon to display",
+ NULL,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_STOCK_ID,
+ g_param_spec_string ("stock-id",
+ "Stock ID",
+ "The icon to display, deprecated",
+ NULL,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_STOCK_SIZE,
+ g_param_spec_int ("stock-size",
+ "Stock Size",
+ "The icon size to use",
+ 0, G_MAXINT,
+ DEFAULT_ICON_SIZE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_OVERRIDE_BACKGROUND,
+ g_param_spec_boolean ("override-background",
+ "Override Background",
+ "Draw the background if the row is selected",
+ FALSE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+gimp_cell_renderer_toggle_init (GimpCellRendererToggle *toggle)
+{
+}
+
+static void
+gimp_cell_renderer_toggle_finalize (GObject *object)
+{
+ GimpCellRendererToggle *toggle = GIMP_CELL_RENDERER_TOGGLE (object);
+ GimpCellRendererTogglePrivate *priv = GET_PRIVATE (object);
+
+ g_clear_pointer (&priv->icon_name, g_free);
+ g_clear_pointer (&toggle->stock_id, g_free);
+
+ g_clear_object (&toggle->pixbuf);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_cell_renderer_toggle_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpCellRendererToggle *toggle = GIMP_CELL_RENDERER_TOGGLE (object);
+ GimpCellRendererTogglePrivate *priv = GET_PRIVATE (object);
+
+ switch (param_id)
+ {
+ case PROP_ICON_NAME:
+ g_value_set_string (value, priv->icon_name);
+ break;
+
+ case PROP_STOCK_ID:
+ g_value_set_string (value, toggle->stock_id);
+ break;
+
+ case PROP_STOCK_SIZE:
+ g_value_set_int (value, toggle->stock_size);
+ break;
+
+ case PROP_OVERRIDE_BACKGROUND:
+ g_value_set_boolean (value, priv->override_background);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_cell_renderer_toggle_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpCellRendererToggle *toggle = GIMP_CELL_RENDERER_TOGGLE (object);
+ GimpCellRendererTogglePrivate *priv = GET_PRIVATE (object);
+
+ switch (param_id)
+ {
+ case PROP_ICON_NAME:
+ if (priv->icon_name)
+ g_free (priv->icon_name);
+ priv->icon_name = g_value_dup_string (value);
+ break;
+
+ case PROP_STOCK_ID:
+ if (toggle->stock_id)
+ g_free (toggle->stock_id);
+ toggle->stock_id = g_value_dup_string (value);
+ break;
+
+ case PROP_STOCK_SIZE:
+ toggle->stock_size = g_value_get_int (value);
+ break;
+
+ case PROP_OVERRIDE_BACKGROUND:
+ priv->override_background = g_value_get_boolean (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+
+ g_clear_object (&toggle->pixbuf);
+}
+
+static void
+gimp_cell_renderer_toggle_get_size (GtkCellRenderer *cell,
+ GtkWidget *widget,
+ GdkRectangle *cell_area,
+ gint *x_offset,
+ gint *y_offset,
+ gint *width,
+ gint *height)
+{
+ GimpCellRendererToggle *toggle = GIMP_CELL_RENDERER_TOGGLE (cell);
+ GimpCellRendererTogglePrivate *priv = GET_PRIVATE (cell);
+ GtkStyle *style = gtk_widget_get_style (widget);
+ gint calc_width;
+ gint calc_height;
+ gint pixbuf_width;
+ gint pixbuf_height;
+ gfloat xalign;
+ gfloat yalign;
+ gint xpad;
+ gint ypad;
+
+ if (! priv->icon_name && ! toggle->stock_id)
+ {
+ GTK_CELL_RENDERER_CLASS (parent_class)->get_size (cell,
+ widget,
+ cell_area,
+ x_offset, y_offset,
+ width, height);
+ return;
+ }
+
+ gtk_cell_renderer_get_alignment (cell, &xalign, &yalign);
+ gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
+
+ if (! toggle->pixbuf)
+ gimp_cell_renderer_toggle_create_pixbuf (toggle, widget);
+
+ pixbuf_width = gdk_pixbuf_get_width (toggle->pixbuf);
+ pixbuf_height = gdk_pixbuf_get_height (toggle->pixbuf);
+
+ calc_width = (pixbuf_width +
+ (gint) xpad * 2 + style->xthickness * 2);
+ calc_height = (pixbuf_height +
+ (gint) ypad * 2 + style->ythickness * 2);
+
+ if (width)
+ *width = calc_width;
+
+ if (height)
+ *height = calc_height;
+
+ if (cell_area)
+ {
+ if (x_offset)
+ {
+ *x_offset = (((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ?
+ (1.0 - xalign) : xalign) *
+ (cell_area->width - calc_width));
+ *x_offset = MAX (*x_offset, 0);
+ }
+
+ if (y_offset)
+ {
+ *y_offset = yalign * (cell_area->height - calc_height);
+ *y_offset = MAX (*y_offset, 0);
+ }
+ }
+}
+
+static void
+gimp_cell_renderer_toggle_render (GtkCellRenderer *cell,
+ GdkWindow *window,
+ GtkWidget *widget,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ GdkRectangle *expose_area,
+ GtkCellRendererState flags)
+{
+ GimpCellRendererToggle *toggle = GIMP_CELL_RENDERER_TOGGLE (cell);
+ GimpCellRendererTogglePrivate *priv = GET_PRIVATE (cell);
+ GtkStyle *style = gtk_widget_get_style (widget);
+ GdkRectangle toggle_rect;
+ GdkRectangle draw_rect;
+ GtkStateType state;
+ gboolean active;
+ gint xpad;
+ gint ypad;
+
+ if (! priv->icon_name && ! toggle->stock_id)
+ {
+ GTK_CELL_RENDERER_CLASS (parent_class)->render (cell, window, widget,
+ background_area,
+ cell_area, expose_area,
+ flags);
+ return;
+ }
+
+ if ((flags & GTK_CELL_RENDERER_SELECTED) &&
+ priv->override_background)
+ {
+ gboolean background_set;
+
+ g_object_get (cell,
+ "cell-background-set", &background_set,
+ NULL);
+
+ if (background_set)
+ {
+ cairo_t *cr = gdk_cairo_create (window);
+ GdkColor *color;
+
+ g_object_get (cell,
+ "cell-background-gdk", &color,
+ NULL);
+
+ gdk_cairo_rectangle (cr, background_area);
+ gdk_cairo_set_source_color (cr, color);
+ cairo_fill (cr);
+
+ gdk_color_free (color);
+ cairo_destroy (cr);
+ }
+ }
+
+ gimp_cell_renderer_toggle_get_size (cell, widget, cell_area,
+ &toggle_rect.x,
+ &toggle_rect.y,
+ &toggle_rect.width,
+ &toggle_rect.height);
+
+ gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
+
+ toggle_rect.x += cell_area->x + xpad;
+ toggle_rect.y += cell_area->y + ypad;
+ toggle_rect.width -= xpad * 2;
+ toggle_rect.height -= ypad * 2;
+
+ if (toggle_rect.width <= 0 || toggle_rect.height <= 0)
+ return;
+
+ active =
+ gtk_cell_renderer_toggle_get_active (GTK_CELL_RENDERER_TOGGLE (cell));
+
+ if ((flags & GTK_CELL_RENDERER_SELECTED) == GTK_CELL_RENDERER_SELECTED)
+ {
+ if (gtk_widget_has_focus (widget))
+ state = GTK_STATE_SELECTED;
+ else
+ state = GTK_STATE_ACTIVE;
+ }
+ else
+ {
+ if (gtk_cell_renderer_toggle_get_activatable (GTK_CELL_RENDERER_TOGGLE (cell)))
+ state = GTK_STATE_NORMAL;
+ else
+ state = GTK_STATE_INSENSITIVE;
+ }
+
+ if (gdk_rectangle_intersect (expose_area, cell_area, &draw_rect) &&
+ (flags & GTK_CELL_RENDERER_PRELIT))
+ gtk_paint_shadow (style,
+ window,
+ state,
+ active ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
+ &draw_rect,
+ widget, NULL,
+ toggle_rect.x, toggle_rect.y,
+ toggle_rect.width, toggle_rect.height);
+
+ if (active)
+ {
+ toggle_rect.x += style->xthickness;
+ toggle_rect.y += style->ythickness;
+ toggle_rect.width -= style->xthickness * 2;
+ toggle_rect.height -= style->ythickness * 2;
+
+ if (gdk_rectangle_intersect (&draw_rect, &toggle_rect, &draw_rect))
+ {
+ cairo_t *cr = gdk_cairo_create (window);
+ gboolean inconsistent;
+
+ gdk_cairo_rectangle (cr, &draw_rect);
+ cairo_clip (cr);
+
+ gdk_cairo_set_source_pixbuf (cr, toggle->pixbuf,
+ toggle_rect.x, toggle_rect.y);
+ cairo_paint (cr);
+
+ g_object_get (toggle,
+ "inconsistent", &inconsistent,
+ NULL);
+
+ if (inconsistent)
+ {
+ gdk_cairo_set_source_color (cr, &style->fg[state]);
+ cairo_set_line_width (cr, 1.5);
+ cairo_move_to (cr,
+ toggle_rect.x + toggle_rect.width - 1,
+ toggle_rect.y + 1);
+ cairo_line_to (cr,
+ toggle_rect.x + 1,
+ toggle_rect.y + toggle_rect.height - 1);
+ cairo_stroke (cr);
+ }
+
+ cairo_destroy (cr);
+ }
+ }
+}
+
+static gboolean
+gimp_cell_renderer_toggle_activate (GtkCellRenderer *cell,
+ GdkEvent *event,
+ GtkWidget *widget,
+ const gchar *path,
+ GdkRectangle *background_area,
+ GdkRectangle *cell_area,
+ GtkCellRendererState flags)
+{
+ GtkCellRendererToggle *toggle = GTK_CELL_RENDERER_TOGGLE (cell);
+
+ if (gtk_cell_renderer_toggle_get_activatable (toggle))
+ {
+ GdkModifierType state = 0;
+
+ if (event && ((GdkEventAny *) event)->type == GDK_BUTTON_PRESS)
+ state = ((GdkEventButton *) event)->state;
+
+ gimp_cell_renderer_toggle_clicked (GIMP_CELL_RENDERER_TOGGLE (cell),
+ path, state);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+gimp_cell_renderer_toggle_create_pixbuf (GimpCellRendererToggle *toggle,
+ GtkWidget *widget)
+{
+ GimpCellRendererTogglePrivate *priv = GET_PRIVATE (toggle);
+
+ g_clear_object (&toggle->pixbuf);
+
+ if (priv->icon_name)
+ {
+ gint width, height;
+
+ if (! gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (widget),
+ toggle->stock_size,
+ &width, &height))
+ {
+ width = 20;
+ height = 20;
+ }
+
+ toggle->pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
+ priv->icon_name,
+ MIN (width, height), 0, NULL);
+ }
+ else
+ {
+ toggle->pixbuf = gtk_widget_render_icon (widget,
+ toggle->stock_id,
+ toggle->stock_size, NULL);
+ }
+}
+
+
+/**
+ * gimp_cell_renderer_toggle_new:
+ * @icon_name: the icon name of the icon to use for the active state
+ *
+ * Creates a custom version of the #GtkCellRendererToggle. Instead of
+ * showing the standard toggle button, it shows a named icon if the
+ * cell is active and no icon otherwise. This cell renderer is for
+ * example used in the Layers treeview to indicate and control the
+ * layer's visibility by showing %GIMP_STOCK_VISIBLE.
+ *
+ * Return value: a new #GimpCellRendererToggle
+ *
+ * Since: 2.2
+ **/
+GtkCellRenderer *
+gimp_cell_renderer_toggle_new (const gchar *icon_name)
+{
+ return g_object_new (GIMP_TYPE_CELL_RENDERER_TOGGLE,
+ "icon-name", icon_name,
+ NULL);
+}
+
+/**
+ * gimp_cell_renderer_toggle_clicked:
+ * @cell: a #GimpCellRendererToggle
+ * @path: the path to the clicked row
+ * @state: the modifier state
+ *
+ * Emits the "clicked" signal from a #GimpCellRendererToggle.
+ *
+ * Since: 2.2
+ **/
+void
+gimp_cell_renderer_toggle_clicked (GimpCellRendererToggle *cell,
+ const gchar *path,
+ GdkModifierType state)
+{
+ g_return_if_fail (GIMP_IS_CELL_RENDERER_TOGGLE (cell));
+ g_return_if_fail (path != NULL);
+
+ g_signal_emit (cell, toggle_cell_signals[CLICKED], 0, path, state);
+}
diff --git a/libgimpwidgets/gimpcellrenderertoggle.h b/libgimpwidgets/gimpcellrenderertoggle.h
new file mode 100644
index 0000000..368a6a1
--- /dev/null
+++ b/libgimpwidgets/gimpcellrenderertoggle.h
@@ -0,0 +1,78 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcellrenderertoggle.h
+ * Copyright (C) 2003-2004 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_CELL_RENDERER_TOGGLE_H__
+#define __GIMP_CELL_RENDERER_TOGGLE_H__
+
+G_BEGIN_DECLS
+
+
+#define GIMP_TYPE_CELL_RENDERER_TOGGLE (gimp_cell_renderer_toggle_get_type ())
+#define GIMP_CELL_RENDERER_TOGGLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CELL_RENDERER_TOGGLE, GimpCellRendererToggle))
+#define GIMP_CELL_RENDERER_TOGGLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CELL_RENDERER_TOGGLE, GimpCellRendererToggleClass))
+#define GIMP_IS_CELL_RENDERER_TOGGLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CELL_RENDERER_TOGGLE))
+#define GIMP_IS_CELL_RENDERER_TOGGLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CELL_RENDERER_TOGGLE))
+#define GIMP_CELL_RENDERER_TOGGLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CELL_RENDERER_TOGGLE, GimpCellRendererToggleClass))
+
+
+typedef struct _GimpCellRendererToggleClass GimpCellRendererToggleClass;
+
+struct _GimpCellRendererToggle
+{
+ GtkCellRendererToggle parent_instance;
+
+ gchar *stock_id;
+ GtkIconSize stock_size;
+ GdkPixbuf *pixbuf;
+};
+
+struct _GimpCellRendererToggleClass
+{
+ GtkCellRendererToggleClass parent_class;
+
+ void (* clicked) (GimpCellRendererToggle *cell,
+ const gchar *path,
+ GdkModifierType state);
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_cell_renderer_toggle_get_type (void) G_GNUC_CONST;
+
+GtkCellRenderer * gimp_cell_renderer_toggle_new (const gchar *icon_name);
+
+void gimp_cell_renderer_toggle_clicked (GimpCellRendererToggle *cell,
+ const gchar *path,
+ GdkModifierType state);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_CELL_RENDERER_TOGGLE_H__ */
diff --git a/libgimpwidgets/gimpchainbutton.c b/libgimpwidgets/gimpchainbutton.c
new file mode 100644
index 0000000..83ee9f7
--- /dev/null
+++ b/libgimpwidgets/gimpchainbutton.c
@@ -0,0 +1,554 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpchainbutton.c
+ * Copyright (C) 1999-2000 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpchainbutton.h"
+#include "gimpicons.h"
+
+
+/**
+ * SECTION: gimpchainbutton
+ * @title: GimpChainButton
+ * @short_description: Widget to visually connect two entry widgets.
+ * @see_also: You may want to use the convenience function
+ * gimp_coordinates_new() to set up two GimpSizeEntries
+ * (see #GimpSizeEntry) linked with a #GimpChainButton.
+ *
+ * This widget provides a button showing either a linked or a broken
+ * chain that can be used to link two entries, spinbuttons, colors or
+ * other GUI elements and show that they may be locked. Use it for
+ * example to connect X and Y ratios to provide the possibility of a
+ * constrained aspect ratio.
+ *
+ * The #GimpChainButton only gives visual feedback, it does not really
+ * connect widgets. You have to take care of locking the values
+ * yourself by checking the state of the #GimpChainButton whenever a
+ * value changes in one of the connected widgets and adjusting the
+ * other value if necessary.
+ **/
+
+
+enum
+{
+ PROP_0,
+ PROP_POSITION,
+ PROP_ICON_SIZE,
+ PROP_ACTIVE
+};
+
+enum
+{
+ TOGGLED,
+ LAST_SIGNAL
+};
+
+static void gimp_chain_button_constructed (GObject *object);
+static void gimp_chain_button_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_chain_button_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_chain_button_clicked_callback (GtkWidget *widget,
+ GimpChainButton *button);
+static void gimp_chain_button_update_image (GimpChainButton *button);
+
+static GtkWidget * gimp_chain_line_new (GimpChainPosition position,
+ gint which);
+
+
+G_DEFINE_TYPE (GimpChainButton, gimp_chain_button, GTK_TYPE_TABLE)
+
+#define parent_class gimp_chain_button_parent_class
+
+static guint gimp_chain_button_signals[LAST_SIGNAL] = { 0 };
+
+static const gchar * const gimp_chain_icon_names[] =
+{
+ GIMP_ICON_CHAIN_HORIZONTAL,
+ GIMP_ICON_CHAIN_HORIZONTAL_BROKEN,
+ GIMP_ICON_CHAIN_VERTICAL,
+ GIMP_ICON_CHAIN_VERTICAL_BROKEN
+};
+
+
+static void
+gimp_chain_button_class_init (GimpChainButtonClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = gimp_chain_button_constructed;
+ object_class->set_property = gimp_chain_button_set_property;
+ object_class->get_property = gimp_chain_button_get_property;
+
+ gimp_chain_button_signals[TOGGLED] =
+ g_signal_new ("toggled",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpChainButtonClass, toggled),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ klass->toggled = NULL;
+
+ /**
+ * GimpChainButton:position:
+ *
+ * The position in which the chain button will be used.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class, PROP_POSITION,
+ g_param_spec_enum ("position",
+ "Position",
+ "The chain's position",
+ GIMP_TYPE_CHAIN_POSITION,
+ GIMP_CHAIN_TOP,
+ G_PARAM_CONSTRUCT_ONLY |
+ GIMP_PARAM_READWRITE));
+
+ /**
+ * GimpChainButton:icon-size:
+ *
+ * The chain button icon size.
+ *
+ * Since: 2.10.10
+ */
+ g_object_class_install_property (object_class, PROP_ICON_SIZE,
+ g_param_spec_enum ("icon-size",
+ "Icon Size",
+ "The chain's icon size",
+ GTK_TYPE_ICON_SIZE,
+ GTK_ICON_SIZE_BUTTON,
+ G_PARAM_CONSTRUCT |
+ GIMP_PARAM_READWRITE));
+
+ /**
+ * GimpChainButton:active:
+ *
+ * The toggled state of the chain button.
+ *
+ * Since: 2.10.10
+ */
+ g_object_class_install_property (object_class, PROP_ACTIVE,
+ g_param_spec_boolean ("active",
+ "Active",
+ "The chain's toggled state",
+ FALSE,
+ G_PARAM_CONSTRUCT |
+ GIMP_PARAM_READWRITE));
+}
+
+static void
+gimp_chain_button_init (GimpChainButton *button)
+{
+ button->position = GIMP_CHAIN_TOP;
+ button->active = FALSE;
+ button->image = gtk_image_new ();
+ button->button = gtk_button_new ();
+
+ gtk_button_set_relief (GTK_BUTTON (button->button), GTK_RELIEF_NONE);
+ gtk_container_add (GTK_CONTAINER (button->button), button->image);
+ gtk_widget_show (button->image);
+
+ g_signal_connect (button->button, "clicked",
+ G_CALLBACK (gimp_chain_button_clicked_callback),
+ button);
+}
+
+static void
+gimp_chain_button_constructed (GObject *object)
+{
+ GimpChainButton *button = GIMP_CHAIN_BUTTON (object);
+
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ button->line1 = gimp_chain_line_new (button->position, 1);
+ button->line2 = gimp_chain_line_new (button->position, -1);
+
+ gimp_chain_button_update_image (button);
+
+ if (button->position & GIMP_CHAIN_LEFT) /* are we a vertical chainbutton? */
+ {
+ gtk_table_resize (GTK_TABLE (button), 3, 1);
+ gtk_table_attach (GTK_TABLE (button), button->button, 0, 1, 1, 2,
+ GTK_SHRINK, GTK_SHRINK, 0, 0);
+ gtk_table_attach_defaults (GTK_TABLE (button),
+ button->line1, 0, 1, 0, 1);
+ gtk_table_attach_defaults (GTK_TABLE (button),
+ button->line2, 0, 1, 2, 3);
+ }
+ else
+ {
+ gtk_table_resize (GTK_TABLE (button), 1, 3);
+ gtk_table_attach (GTK_TABLE (button), button->button, 1, 2, 0, 1,
+ GTK_SHRINK, GTK_SHRINK, 0, 0);
+ gtk_table_attach_defaults (GTK_TABLE (button),
+ button->line1, 0, 1, 0, 1);
+ gtk_table_attach_defaults (GTK_TABLE (button),
+ button->line2, 2, 3, 0, 1);
+ }
+
+ gtk_widget_show (button->button);
+ gtk_widget_show (button->line1);
+ gtk_widget_show (button->line2);
+}
+
+static void
+gimp_chain_button_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpChainButton *button = GIMP_CHAIN_BUTTON (object);
+
+ switch (property_id)
+ {
+ case PROP_POSITION:
+ button->position = g_value_get_enum (value);
+ break;
+
+ case PROP_ICON_SIZE:
+ g_object_set_property (G_OBJECT (button->image), "icon-size", value);
+ break;
+
+ case PROP_ACTIVE:
+ gimp_chain_button_set_active (button, g_value_get_boolean (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_chain_button_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpChainButton *button = GIMP_CHAIN_BUTTON (object);
+
+ switch (property_id)
+ {
+ case PROP_POSITION:
+ g_value_set_enum (value, button->position);
+ break;
+
+ case PROP_ICON_SIZE:
+ g_object_get_property (G_OBJECT (button->image), "icon-size", value);
+ break;
+
+ case PROP_ACTIVE:
+ g_value_set_boolean (value, gimp_chain_button_get_active (button));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+/**
+ * gimp_chain_button_new:
+ * @position: The position you are going to use for the button
+ * with respect to the widgets you want to chain.
+ *
+ * Creates a new #GimpChainButton widget.
+ *
+ * This returns a button showing either a broken or a linked chain and
+ * small clamps attached to both sides that visually group the two widgets
+ * you want to connect. This widget looks best when attached
+ * to a table taking up two columns (or rows respectively) next
+ * to the widgets that it is supposed to connect. It may work
+ * for more than two widgets, but the look is optimized for two.
+ *
+ * Returns: Pointer to the new #GimpChainButton, which is inactive
+ * by default. Use gimp_chain_button_set_active() to
+ * change its state.
+ */
+GtkWidget *
+gimp_chain_button_new (GimpChainPosition position)
+{
+ return g_object_new (GIMP_TYPE_CHAIN_BUTTON,
+ "position", position,
+ NULL);
+}
+
+/**
+ * gimp_chain_button_set_icon_size:
+ * @button: Pointer to a #GimpChainButton.
+ * @size: The new icon size.
+ *
+ * Sets the icon size of the #GimpChainButton.
+ *
+ * Since: 2.10.10
+ */
+void
+gimp_chain_button_set_icon_size (GimpChainButton *button,
+ GtkIconSize size)
+{
+ g_return_if_fail (GIMP_IS_CHAIN_BUTTON (button));
+
+ g_object_set (button,
+ "icon-size", size,
+ NULL);
+}
+
+/**
+ * gimp_chain_button_get_icon_size:
+ * @button: Pointer to a #GimpChainButton.
+ *
+ * Gets the icon size of the #GimpChainButton.
+ *
+ * Returns: The icon size.
+ *
+ * Since: 2.10.10
+ */
+GtkIconSize
+gimp_chain_button_get_icon_size (GimpChainButton *button)
+{
+ GtkIconSize size;
+
+ g_return_val_if_fail (GIMP_IS_CHAIN_BUTTON (button), GTK_ICON_SIZE_BUTTON);
+
+ g_object_get (button,
+ "icon-size", &size,
+ NULL);
+
+ return size;
+}
+
+/**
+ * gimp_chain_button_set_active:
+ * @button: Pointer to a #GimpChainButton.
+ * @active: The new state.
+ *
+ * Sets the state of the #GimpChainButton to be either locked (%TRUE) or
+ * unlocked (%FALSE) and changes the showed pixmap to reflect the new state.
+ */
+void
+gimp_chain_button_set_active (GimpChainButton *button,
+ gboolean active)
+{
+ g_return_if_fail (GIMP_IS_CHAIN_BUTTON (button));
+
+ if (button->active != active)
+ {
+ button->active = active ? TRUE : FALSE;
+
+ gimp_chain_button_update_image (button);
+
+ g_signal_emit (button, gimp_chain_button_signals[TOGGLED], 0);
+
+ g_object_notify (G_OBJECT (button), "active");
+ }
+}
+
+/**
+ * gimp_chain_button_get_active
+ * @button: Pointer to a #GimpChainButton.
+ *
+ * Checks the state of the #GimpChainButton.
+ *
+ * Returns: %TRUE if the #GimpChainButton is active (locked).
+ */
+gboolean
+gimp_chain_button_get_active (GimpChainButton *button)
+{
+ g_return_val_if_fail (GIMP_IS_CHAIN_BUTTON (button), FALSE);
+
+ return button->active;
+}
+
+static void
+gimp_chain_button_clicked_callback (GtkWidget *widget,
+ GimpChainButton *button)
+{
+ gimp_chain_button_set_active (button, ! button->active);
+}
+
+static void
+gimp_chain_button_update_image (GimpChainButton *button)
+{
+ guint i;
+
+ i = ((button->position & GIMP_CHAIN_LEFT) << 1) + (button->active ? 0 : 1);
+
+ gtk_image_set_from_icon_name (GTK_IMAGE (button->image),
+ gimp_chain_icon_names[i],
+ gimp_chain_button_get_icon_size (button));
+}
+
+
+/* GimpChainLine is a simple no-window widget for drawing the lines.
+ *
+ * Originally this used to be a GtkDrawingArea but this turned out to
+ * be a bad idea. We don't need an extra window to draw on and we also
+ * don't need any input events.
+ */
+
+static GType gimp_chain_line_get_type (void) G_GNUC_CONST;
+static gboolean gimp_chain_line_expose_event (GtkWidget *widget,
+ GdkEventExpose *event);
+
+struct _GimpChainLine
+{
+ GtkWidget parent_instance;
+ GimpChainPosition position;
+ gint which;
+};
+
+typedef struct _GimpChainLine GimpChainLine;
+typedef GtkWidgetClass GimpChainLineClass;
+
+G_DEFINE_TYPE (GimpChainLine, gimp_chain_line, GTK_TYPE_WIDGET)
+
+static void
+gimp_chain_line_class_init (GimpChainLineClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ widget_class->expose_event = gimp_chain_line_expose_event;
+}
+
+static void
+gimp_chain_line_init (GimpChainLine *line)
+{
+ gtk_widget_set_has_window (GTK_WIDGET (line), FALSE);
+}
+
+static GtkWidget *
+gimp_chain_line_new (GimpChainPosition position,
+ gint which)
+{
+ GimpChainLine *line = g_object_new (gimp_chain_line_get_type (), NULL);
+
+ line->position = position;
+ line->which = which;
+
+ return GTK_WIDGET (line);
+}
+
+static gboolean
+gimp_chain_line_expose_event (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ GtkStyle *style = gtk_widget_get_style (widget);
+ GimpChainLine *line = ((GimpChainLine *) widget);
+ GtkAllocation allocation;
+ GdkPoint points[3];
+ GimpChainPosition position;
+ cairo_t *cr;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ cr = gdk_cairo_create (gtk_widget_get_window (widget));
+ gdk_cairo_region (cr, event->region);
+ cairo_translate (cr, allocation.x, allocation.y);
+ cairo_clip (cr);
+
+#define SHORT_LINE 4
+ points[0].x = allocation.width / 2;
+ points[0].y = allocation.height / 2;
+
+ position = line->position;
+
+ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+ {
+ switch (position)
+ {
+ case GIMP_CHAIN_TOP:
+ case GIMP_CHAIN_BOTTOM:
+ break;
+
+ case GIMP_CHAIN_LEFT:
+ position = GIMP_CHAIN_RIGHT;
+ break;
+
+ case GIMP_CHAIN_RIGHT:
+ position = GIMP_CHAIN_LEFT;
+ break;
+ }
+ }
+
+ switch (position)
+ {
+ case GIMP_CHAIN_LEFT:
+ points[0].x += SHORT_LINE;
+ points[1].x = points[0].x - SHORT_LINE;
+ points[1].y = points[0].y;
+ points[2].x = points[1].x;
+ points[2].y = (line->which == 1 ? allocation.height - 1 : 0);
+ break;
+
+ case GIMP_CHAIN_RIGHT:
+ points[0].x -= SHORT_LINE;
+ points[1].x = points[0].x + SHORT_LINE;
+ points[1].y = points[0].y;
+ points[2].x = points[1].x;
+ points[2].y = (line->which == 1 ? allocation.height - 1 : 0);
+ break;
+
+ case GIMP_CHAIN_TOP:
+ points[0].y += SHORT_LINE;
+ points[1].x = points[0].x;
+ points[1].y = points[0].y - SHORT_LINE;
+ points[2].x = (line->which == 1 ? allocation.width - 1 : 0);
+ points[2].y = points[1].y;
+ break;
+
+ case GIMP_CHAIN_BOTTOM:
+ points[0].y -= SHORT_LINE;
+ points[1].x = points[0].x;
+ points[1].y = points[0].y + SHORT_LINE;
+ points[2].x = (line->which == 1 ? allocation.width - 1 : 0);
+ points[2].y = points[1].y;
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ cairo_move_to (cr, points[0].x, points[0].y);
+ cairo_line_to (cr, points[1].x, points[1].y);
+ cairo_line_to (cr, points[2].x, points[2].y);
+
+ cairo_set_line_width (cr, 2.0);
+ cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+ gdk_cairo_set_source_color (cr, &style->fg[GTK_STATE_NORMAL]);
+
+ cairo_stroke (cr);
+
+ cairo_destroy (cr);
+
+ return TRUE;
+}
diff --git a/libgimpwidgets/gimpchainbutton.h b/libgimpwidgets/gimpchainbutton.h
new file mode 100644
index 0000000..b89f7e9
--- /dev/null
+++ b/libgimpwidgets/gimpchainbutton.h
@@ -0,0 +1,92 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpchainbutton.h
+ * Copyright (C) 1999-2000 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * This implements a widget derived from GtkTable that visualizes
+ * it's state with two different pixmaps showing a closed and a
+ * broken chain. It's intended to be used with the GimpSizeEntry
+ * widget. The usage is quite similar to the one the GtkToggleButton
+ * provides.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_CHAIN_BUTTON_H__
+#define __GIMP_CHAIN_BUTTON_H__
+
+G_BEGIN_DECLS
+
+
+#define GIMP_TYPE_CHAIN_BUTTON (gimp_chain_button_get_type ())
+#define GIMP_CHAIN_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CHAIN_BUTTON, GimpChainButton))
+#define GIMP_CHAIN_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CHAIN_BUTTON, GimpChainButtonClass))
+#define GIMP_IS_CHAIN_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CHAIN_BUTTON))
+#define GIMP_IS_CHAIN_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CHAIN_BUTTON))
+#define GIMP_CHAIN_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CHAIN_BUTTON, GimpChainButtonClass))
+
+
+typedef struct _GimpChainButtonClass GimpChainButtonClass;
+
+struct _GimpChainButton
+{
+ GtkTable parent_instance;
+
+ GimpChainPosition position;
+ gboolean active;
+
+ GtkWidget *button;
+ GtkWidget *line1;
+ GtkWidget *line2;
+ GtkWidget *image;
+};
+
+struct _GimpChainButtonClass
+{
+ GtkTableClass parent_class;
+
+ void (* toggled) (GimpChainButton *button);
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_chain_button_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_chain_button_new (GimpChainPosition position);
+
+void gimp_chain_button_set_icon_size (GimpChainButton *button,
+ GtkIconSize size);
+GtkIconSize gimp_chain_button_get_icon_size (GimpChainButton *button);
+
+void gimp_chain_button_set_active (GimpChainButton *button,
+ gboolean active);
+gboolean gimp_chain_button_get_active (GimpChainButton *button);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_CHAIN_BUTTON_H__ */
diff --git a/libgimpwidgets/gimpcolorarea.c b/libgimpwidgets/gimpcolorarea.c
new file mode 100644
index 0000000..75f69c2
--- /dev/null
+++ b/libgimpwidgets/gimpcolorarea.c
@@ -0,0 +1,956 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcolorarea.c
+ * Copyright (C) 2001-2002 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpcairo-utils.h"
+#include "gimpcolorarea.h"
+#include "gimpwidgetsutils.h"
+
+
+/**
+ * SECTION: gimpcolorarea
+ * @title: GimpColorArea
+ * @short_description: Displays a #GimpRGB color, optionally with
+ * alpha-channel.
+ *
+ * Displays a #GimpRGB color, optionally with alpha-channel.
+ **/
+
+
+#define RGBA_EPSILON 1e-6
+#define DRAG_PREVIEW_SIZE 32
+#define DRAG_ICON_OFFSET -8
+
+
+enum
+{
+ COLOR_CHANGED,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_COLOR,
+ PROP_TYPE,
+ PROP_DRAG_MASK,
+ PROP_DRAW_BORDER
+};
+
+
+typedef struct _GimpColorAreaPrivate GimpColorAreaPrivate;
+
+struct _GimpColorAreaPrivate
+{
+ GimpColorConfig *config;
+ GimpColorTransform *transform;
+};
+
+#define GET_PRIVATE(obj) \
+ ((GimpColorAreaPrivate *) gimp_color_area_get_instance_private ((GimpColorArea *) (obj)))
+
+
+static void gimp_color_area_dispose (GObject *object);
+static void gimp_color_area_finalize (GObject *object);
+static void gimp_color_area_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_color_area_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_color_area_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static void gimp_color_area_state_changed (GtkWidget *widget,
+ GtkStateType previous_state);
+static gboolean gimp_color_area_expose (GtkWidget *widget,
+ GdkEventExpose *event);
+static void gimp_color_area_render_buf (GtkWidget *widget,
+ gboolean insensitive,
+ GimpColorAreaType type,
+ guchar *buf,
+ guint width,
+ guint height,
+ guint rowstride,
+ GimpRGB *color);
+static void gimp_color_area_render (GimpColorArea *area);
+
+static void gimp_color_area_drag_begin (GtkWidget *widget,
+ GdkDragContext *context);
+static void gimp_color_area_drag_end (GtkWidget *widget,
+ GdkDragContext *context);
+static void gimp_color_area_drag_data_received (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time);
+static void gimp_color_area_drag_data_get (GtkWidget *widget,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time);
+
+static void gimp_color_area_create_transform (GimpColorArea *area);
+static void gimp_color_area_destroy_transform (GimpColorArea *area);
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpColorArea, gimp_color_area,
+ GTK_TYPE_DRAWING_AREA)
+
+#define parent_class gimp_color_area_parent_class
+
+static guint gimp_color_area_signals[LAST_SIGNAL] = { 0 };
+
+static const GtkTargetEntry target = { "application/x-color", 0 };
+
+
+static void
+gimp_color_area_class_init (GimpColorAreaClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GimpRGB color;
+
+ gimp_color_area_signals[COLOR_CHANGED] =
+ g_signal_new ("color-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpColorAreaClass, color_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ object_class->dispose = gimp_color_area_dispose;
+ object_class->finalize = gimp_color_area_finalize;
+ object_class->get_property = gimp_color_area_get_property;
+ object_class->set_property = gimp_color_area_set_property;
+
+ widget_class->size_allocate = gimp_color_area_size_allocate;
+ widget_class->state_changed = gimp_color_area_state_changed;
+ widget_class->expose_event = gimp_color_area_expose;
+
+ widget_class->drag_begin = gimp_color_area_drag_begin;
+ widget_class->drag_end = gimp_color_area_drag_end;
+ widget_class->drag_data_received = gimp_color_area_drag_data_received;
+ widget_class->drag_data_get = gimp_color_area_drag_data_get;
+
+ klass->color_changed = NULL;
+
+ gimp_rgba_set (&color, 0.0, 0.0, 0.0, 1.0);
+
+ /**
+ * GimpColorArea:color:
+ *
+ * The color displayed in the color area.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class, PROP_COLOR,
+ gimp_param_spec_rgb ("color",
+ "Color",
+ "The displayed color",
+ TRUE, &color,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+ /**
+ * GimpColorArea:type:
+ *
+ * The type of the color area.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class, PROP_TYPE,
+ g_param_spec_enum ("type",
+ "Type",
+ "The type of the color area",
+ GIMP_TYPE_COLOR_AREA_TYPE,
+ GIMP_COLOR_AREA_FLAT,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+ /**
+ * GimpColorArea:drag-type:
+ *
+ * The modifier mask that should trigger drags.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class, PROP_DRAG_MASK,
+ g_param_spec_flags ("drag-mask",
+ "Drag Mask",
+ "The modifier mask that triggers dragging the color",
+ GDK_TYPE_MODIFIER_TYPE,
+ 0,
+ GIMP_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * GimpColorArea:draw-border:
+ *
+ * Whether to draw a thin border in the foreground color around the area.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class, PROP_DRAW_BORDER,
+ g_param_spec_boolean ("draw-border",
+ "Draw Border",
+ "Whether to draw a thin border in the foreground color around the area",
+ FALSE,
+ GIMP_PARAM_READWRITE));
+}
+
+static void
+gimp_color_area_init (GimpColorArea *area)
+{
+ area->buf = NULL;
+ area->width = 0;
+ area->height = 0;
+ area->rowstride = 0;
+ area->draw_border = FALSE;
+
+ gtk_drag_dest_set (GTK_WIDGET (area),
+ GTK_DEST_DEFAULT_HIGHLIGHT |
+ GTK_DEST_DEFAULT_MOTION |
+ GTK_DEST_DEFAULT_DROP,
+ &target, 1,
+ GDK_ACTION_COPY);
+
+ gimp_widget_track_monitor (GTK_WIDGET (area),
+ G_CALLBACK (gimp_color_area_destroy_transform),
+ NULL);
+}
+
+static void
+gimp_color_area_dispose (GObject *object)
+{
+ GimpColorArea *area = GIMP_COLOR_AREA (object);
+
+ gimp_color_area_set_color_config (area, NULL);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gimp_color_area_finalize (GObject *object)
+{
+ GimpColorArea *area = GIMP_COLOR_AREA (object);
+
+ g_clear_pointer (&area->buf, g_free);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_color_area_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpColorArea *area = GIMP_COLOR_AREA (object);
+
+ switch (property_id)
+ {
+ case PROP_COLOR:
+ g_value_set_boxed (value, &area->color);
+ break;
+
+ case PROP_TYPE:
+ g_value_set_enum (value, area->type);
+ break;
+
+ case PROP_DRAW_BORDER:
+ g_value_set_boolean (value, area->draw_border);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_color_area_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpColorArea *area = GIMP_COLOR_AREA (object);
+ GdkModifierType drag_mask;
+
+ switch (property_id)
+ {
+ case PROP_COLOR:
+ gimp_color_area_set_color (area, g_value_get_boxed (value));
+ break;
+
+ case PROP_TYPE:
+ gimp_color_area_set_type (area, g_value_get_enum (value));
+ break;
+
+ case PROP_DRAG_MASK:
+ drag_mask = g_value_get_flags (value) & (GDK_BUTTON1_MASK |
+ GDK_BUTTON2_MASK |
+ GDK_BUTTON3_MASK);
+ if (drag_mask)
+ gtk_drag_source_set (GTK_WIDGET (area),
+ drag_mask,
+ &target, 1,
+ GDK_ACTION_COPY | GDK_ACTION_MOVE);
+ break;
+
+ case PROP_DRAW_BORDER:
+ gimp_color_area_set_draw_border (area, g_value_get_boolean (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_color_area_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GimpColorArea *area = GIMP_COLOR_AREA (widget);
+
+ GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
+
+ if (allocation->width != area->width ||
+ allocation->height != area->height)
+ {
+ area->width = allocation->width;
+ area->height = allocation->height;
+
+ area->rowstride = area->width * 4 + 4;
+
+ g_free (area->buf);
+ area->buf = g_new (guchar, area->rowstride * area->height);
+
+ area->needs_render = TRUE;
+ }
+}
+
+static void
+gimp_color_area_state_changed (GtkWidget *widget,
+ GtkStateType previous_state)
+{
+ if (gtk_widget_get_state (widget) == GTK_STATE_INSENSITIVE ||
+ previous_state == GTK_STATE_INSENSITIVE)
+ {
+ GIMP_COLOR_AREA (widget)->needs_render = TRUE;
+ }
+
+ if (GTK_WIDGET_CLASS (parent_class)->state_changed)
+ GTK_WIDGET_CLASS (parent_class)->state_changed (widget, previous_state);
+}
+
+static gboolean
+gimp_color_area_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ GimpColorArea *area = GIMP_COLOR_AREA (widget);
+ GimpColorAreaPrivate *priv = GET_PRIVATE (widget);
+ GtkStyle *style = gtk_widget_get_style (widget);
+ cairo_t *cr;
+ cairo_surface_t *buffer;
+
+ if (! area->buf || ! gtk_widget_is_drawable (widget))
+ return FALSE;
+
+ if (area->needs_render)
+ gimp_color_area_render (area);
+
+ cr = gdk_cairo_create (gtk_widget_get_window (widget));
+
+ gdk_cairo_region (cr, event->region);
+ cairo_clip (cr);
+
+ if (! priv->transform)
+ gimp_color_area_create_transform (area);
+
+ if (priv->transform)
+ {
+ const Babl *format = babl_format ("cairo-RGB24");
+ guchar *buf = g_new (guchar, area->rowstride * area->height);
+ guchar *src = area->buf;
+ guchar *dest = buf;
+ gint i;
+
+ for (i = 0; i < area->height; i++)
+ {
+ gimp_color_transform_process_pixels (priv->transform,
+ format, src,
+ format, dest,
+ area->width);
+
+ src += area->rowstride;
+ dest += area->rowstride;
+ }
+
+ buffer = cairo_image_surface_create_for_data (buf,
+ CAIRO_FORMAT_RGB24,
+ area->width,
+ area->height,
+ area->rowstride);
+ cairo_surface_set_user_data (buffer, NULL,
+ buf, (cairo_destroy_func_t) g_free);
+ }
+ else
+ {
+ buffer = cairo_image_surface_create_for_data (area->buf,
+ CAIRO_FORMAT_RGB24,
+ area->width,
+ area->height,
+ area->rowstride);
+ }
+
+ cairo_set_source_surface (cr, buffer, 0.0, 0.0);
+ cairo_surface_destroy (buffer);
+ cairo_paint (cr);
+
+ if (priv->config &&
+ (area->color.r < 0.0 || area->color.r > 1.0 ||
+ area->color.g < 0.0 || area->color.g > 1.0 ||
+ area->color.b < 0.0 || area->color.b > 1.0))
+ {
+ gint side = MIN (area->width, area->height) * 2 / 3;
+
+ cairo_move_to (cr, area->width, 0);
+ cairo_line_to (cr, area->width - side, 0);
+ cairo_line_to (cr, area->width, side);
+ cairo_line_to (cr, area->width, 0);
+
+ gimp_cairo_set_source_rgb (cr, &priv->config->out_of_gamut_color);
+ cairo_fill (cr);
+ }
+
+ if (area->draw_border)
+ {
+ cairo_set_line_width (cr, 1.0);
+ gdk_cairo_set_source_color (cr,
+ &style->fg[gtk_widget_get_state (widget)]);
+
+ cairo_rectangle (cr, 0.5, 0.5, area->width - 1, area->height - 1);
+ cairo_stroke (cr);
+ }
+
+ cairo_destroy (cr);
+
+ return FALSE;
+}
+
+/**
+ * gimp_color_area_new:
+ * @color: A pointer to a #GimpRGB struct.
+ * @type: The type of color area to create.
+ * @drag_mask: The event_mask that should trigger drags.
+ *
+ * Creates a new #GimpColorArea widget.
+ *
+ * This returns a preview area showing the color. It handles color
+ * DND. If the color changes, the "color_changed" signal is emitted.
+ *
+ * Returns: Pointer to the new #GimpColorArea widget.
+ **/
+GtkWidget *
+gimp_color_area_new (const GimpRGB *color,
+ GimpColorAreaType type,
+ GdkModifierType drag_mask)
+{
+ return g_object_new (GIMP_TYPE_COLOR_AREA,
+ "color", color,
+ "type", type,
+ "drag-mask", drag_mask,
+ NULL);
+}
+
+/**
+ * gimp_color_area_set_color:
+ * @area: Pointer to a #GimpColorArea.
+ * @color: Pointer to a #GimpRGB struct that defines the new color.
+ *
+ * Sets @area to a different @color.
+ **/
+void
+gimp_color_area_set_color (GimpColorArea *area,
+ const GimpRGB *color)
+{
+ g_return_if_fail (GIMP_IS_COLOR_AREA (area));
+ g_return_if_fail (color != NULL);
+
+ if (gimp_rgba_distance (&area->color, color) < RGBA_EPSILON)
+ return;
+
+ area->color = *color;
+
+ area->needs_render = TRUE;
+ gtk_widget_queue_draw (GTK_WIDGET (area));
+
+ g_object_notify (G_OBJECT (area), "color");
+
+ g_signal_emit (area, gimp_color_area_signals[COLOR_CHANGED], 0);
+}
+
+/**
+ * gimp_color_area_get_color:
+ * @area: Pointer to a #GimpColorArea.
+ * @color: Pointer to a #GimpRGB struct that is used to return the color.
+ *
+ * Retrieves the current color of the @area.
+ **/
+void
+gimp_color_area_get_color (GimpColorArea *area,
+ GimpRGB *color)
+{
+ g_return_if_fail (GIMP_IS_COLOR_AREA (area));
+ g_return_if_fail (color != NULL);
+
+ *color = area->color;
+}
+
+/**
+ * gimp_color_area_has_alpha:
+ * @area: Pointer to a #GimpColorArea.
+ *
+ * Checks whether the @area shows transparency information. This is determined
+ * via the @area's #GimpColorAreaType.
+ *
+ * Returns: %TRUE if @area shows transparency information, %FALSE otherwise.
+ **/
+gboolean
+gimp_color_area_has_alpha (GimpColorArea *area)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_AREA (area), FALSE);
+
+ return area->type != GIMP_COLOR_AREA_FLAT;
+}
+
+/**
+ * gimp_color_area_set_type:
+ * @area: Pointer to a #GimpColorArea.
+ * @type: A #GimpColorAreaType.
+ *
+ * Changes the type of @area. The #GimpColorAreaType determines
+ * whether the widget shows transparency information and chooses the
+ * size of the checkerboard used to do that.
+ **/
+void
+gimp_color_area_set_type (GimpColorArea *area,
+ GimpColorAreaType type)
+{
+ g_return_if_fail (GIMP_IS_COLOR_AREA (area));
+
+ if (area->type != type)
+ {
+ area->type = type;
+
+ area->needs_render = TRUE;
+ gtk_widget_queue_draw (GTK_WIDGET (area));
+
+ g_object_notify (G_OBJECT (area), "type");
+ }
+}
+
+/**
+ * gimp_color_area_set_draw_border:
+ * @area: Pointer to a #GimpColorArea.
+ * @draw_border: whether to draw a border or not
+ *
+ * The @area can draw a thin border in the foreground color around
+ * itself. This function toggles this behaviour on and off. The
+ * default is not draw a border.
+ **/
+void
+gimp_color_area_set_draw_border (GimpColorArea *area,
+ gboolean draw_border)
+{
+ g_return_if_fail (GIMP_IS_COLOR_AREA (area));
+
+ draw_border = draw_border ? TRUE : FALSE;
+
+ if (area->draw_border != draw_border)
+ {
+ area->draw_border = draw_border;
+
+ gtk_widget_queue_draw (GTK_WIDGET (area));
+
+ g_object_notify (G_OBJECT (area), "draw-border");
+ }
+}
+
+/**
+ * gimp_color_area_set_color_config:
+ * @area: a #GimpColorArea widget.
+ * @config: a #GimpColorConfig object.
+ *
+ * Sets the color management configuration to use with this color area.
+ *
+ * Since: 2.10
+ */
+void
+gimp_color_area_set_color_config (GimpColorArea *area,
+ GimpColorConfig *config)
+{
+ GimpColorAreaPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_COLOR_AREA (area));
+ g_return_if_fail (config == NULL || GIMP_IS_COLOR_CONFIG (config));
+
+ priv = GET_PRIVATE (area);
+
+ if (config != priv->config)
+ {
+ if (priv->config)
+ {
+ g_signal_handlers_disconnect_by_func (priv->config,
+ gimp_color_area_destroy_transform,
+ area);
+
+ gimp_color_area_destroy_transform (area);
+ }
+
+ g_set_object (&priv->config, config);
+
+ if (priv->config)
+ {
+ g_signal_connect_swapped (priv->config, "notify",
+ G_CALLBACK (gimp_color_area_destroy_transform),
+ area);
+ }
+ }
+}
+
+
+/* private functions */
+
+static void
+gimp_color_area_render_buf (GtkWidget *widget,
+ gboolean insensitive,
+ GimpColorAreaType type,
+ guchar *buf,
+ guint width,
+ guint height,
+ guint rowstride,
+ GimpRGB *color)
+{
+ GtkStyle *style = gtk_widget_get_style (widget);
+ guint x, y;
+ guint check_size = 0;
+ guchar light[3];
+ guchar dark[3];
+ guchar opaque[3];
+ guchar insens[3];
+ guchar *p;
+ gdouble frac;
+
+ switch (type)
+ {
+ case GIMP_COLOR_AREA_FLAT:
+ check_size = 0;
+ break;
+
+ case GIMP_COLOR_AREA_SMALL_CHECKS:
+ check_size = GIMP_CHECK_SIZE_SM;
+ break;
+
+ case GIMP_COLOR_AREA_LARGE_CHECKS:
+ check_size = GIMP_CHECK_SIZE;
+ break;
+ }
+
+ gimp_rgb_get_uchar (color, opaque, opaque + 1, opaque + 2);
+
+ insens[0] = style->bg[GTK_STATE_INSENSITIVE].red >> 8;
+ insens[1] = style->bg[GTK_STATE_INSENSITIVE].green >> 8;
+ insens[2] = style->bg[GTK_STATE_INSENSITIVE].blue >> 8;
+
+ if (insensitive || check_size == 0 || color->a == 1.0)
+ {
+ for (y = 0; y < height; y++)
+ {
+ p = buf + y * rowstride;
+
+ for (x = 0; x < width; x++)
+ {
+ if (insensitive && ((x + y) % 2))
+ {
+ GIMP_CAIRO_RGB24_SET_PIXEL (p,
+ insens[0],
+ insens[1],
+ insens[2]);
+ }
+ else
+ {
+ GIMP_CAIRO_RGB24_SET_PIXEL (p,
+ opaque[0],
+ opaque[1],
+ opaque[2]);
+ }
+
+ p += 4;
+ }
+ }
+
+ return;
+ }
+
+ light[0] = (GIMP_CHECK_LIGHT +
+ (color->r - GIMP_CHECK_LIGHT) * color->a) * 255.999;
+ light[1] = (GIMP_CHECK_LIGHT +
+ (color->g - GIMP_CHECK_LIGHT) * color->a) * 255.999;
+ light[2] = (GIMP_CHECK_LIGHT +
+ (color->b - GIMP_CHECK_LIGHT) * color->a) * 255.999;
+
+ dark[0] = (GIMP_CHECK_DARK +
+ (color->r - GIMP_CHECK_DARK) * color->a) * 255.999;
+ dark[1] = (GIMP_CHECK_DARK +
+ (color->g - GIMP_CHECK_DARK) * color->a) * 255.999;
+ dark[2] = (GIMP_CHECK_DARK +
+ (color->b - GIMP_CHECK_DARK) * color->a) * 255.999;
+
+ for (y = 0; y < height; y++)
+ {
+ p = buf + y * rowstride;
+
+ for (x = 0; x < width; x++)
+ {
+ if ((width - x) * height > y * width)
+ {
+ GIMP_CAIRO_RGB24_SET_PIXEL (p,
+ opaque[0],
+ opaque[1],
+ opaque[2]);
+ p += 4;
+
+ continue;
+ }
+
+ frac = y - (gdouble) ((width - x) * height) / (gdouble) width;
+
+ if (((x / check_size) ^ (y / check_size)) & 1)
+ {
+ if ((gint) frac)
+ {
+ GIMP_CAIRO_RGB24_SET_PIXEL (p,
+ light[0],
+ light[1],
+ light[2]);
+ }
+ else
+ {
+ GIMP_CAIRO_RGB24_SET_PIXEL (p,
+ ((gdouble) light[0] * frac +
+ (gdouble) opaque[0] * (1.0 - frac)),
+ ((gdouble) light[1] * frac +
+ (gdouble) opaque[1] * (1.0 - frac)),
+ ((gdouble) light[2] * frac +
+ (gdouble) opaque[2] * (1.0 - frac)));
+ }
+ }
+ else
+ {
+ if ((gint) frac)
+ {
+ GIMP_CAIRO_RGB24_SET_PIXEL (p,
+ dark[0],
+ dark[1],
+ dark[2]);
+ }
+ else
+ {
+ GIMP_CAIRO_RGB24_SET_PIXEL (p,
+ ((gdouble) dark[0] * frac +
+ (gdouble) opaque[0] * (1.0 - frac)),
+ ((gdouble) dark[1] * frac +
+ (gdouble) opaque[1] * (1.0 - frac)),
+ ((gdouble) dark[2] * frac +
+ (gdouble) opaque[2] * (1.0 - frac)));
+ }
+ }
+
+ p += 4;
+ }
+ }
+}
+
+static void
+gimp_color_area_render (GimpColorArea *area)
+{
+ if (! area->buf)
+ return;
+
+ gimp_color_area_render_buf (GTK_WIDGET (area),
+ ! gtk_widget_is_sensitive (GTK_WIDGET (area)),
+ area->type,
+ area->buf,
+ area->width, area->height, area->rowstride,
+ &area->color);
+
+ area->needs_render = FALSE;
+}
+
+static void
+gimp_color_area_drag_begin (GtkWidget *widget,
+ GdkDragContext *context)
+{
+ GimpRGB color;
+ GtkWidget *window;
+ GtkWidget *frame;
+ GtkWidget *color_area;
+
+ window = gtk_window_new (GTK_WINDOW_POPUP);
+ gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DND);
+ gtk_window_set_screen (GTK_WINDOW (window), gtk_widget_get_screen (widget));
+
+ gtk_widget_realize (window);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
+ gtk_container_add (GTK_CONTAINER (window), frame);
+
+ gimp_color_area_get_color (GIMP_COLOR_AREA (widget), &color);
+
+ color_area = gimp_color_area_new (&color,
+ GIMP_COLOR_AREA (widget)->type,
+ 0);
+
+ gtk_widget_set_size_request (color_area,
+ DRAG_PREVIEW_SIZE, DRAG_PREVIEW_SIZE);
+ gtk_container_add (GTK_CONTAINER (frame), color_area);
+ gtk_widget_show (color_area);
+ gtk_widget_show (frame);
+
+ g_object_set_data_full (G_OBJECT (widget),
+ "gimp-color-area-drag-window",
+ window,
+ (GDestroyNotify) gtk_widget_destroy);
+
+ gtk_drag_set_icon_widget (context, window,
+ DRAG_ICON_OFFSET, DRAG_ICON_OFFSET);
+}
+
+static void
+gimp_color_area_drag_end (GtkWidget *widget,
+ GdkDragContext *context)
+{
+ g_object_set_data (G_OBJECT (widget),
+ "gimp-color-area-drag-window", NULL);
+}
+
+static void
+gimp_color_area_drag_data_received (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time)
+{
+ GimpColorArea *area = GIMP_COLOR_AREA (widget);
+ const guint16 *vals;
+ GimpRGB color;
+
+ if (gtk_selection_data_get_length (selection_data) != 8 ||
+ gtk_selection_data_get_format (selection_data) != 16)
+ {
+ g_warning ("%s: received invalid color data", G_STRFUNC);
+ return;
+ }
+
+ vals = (const guint16 *) gtk_selection_data_get_data (selection_data);
+
+ gimp_rgba_set (&color,
+ (gdouble) vals[0] / 0xffff,
+ (gdouble) vals[1] / 0xffff,
+ (gdouble) vals[2] / 0xffff,
+ (gdouble) vals[3] / 0xffff);
+
+ gimp_color_area_set_color (area, &color);
+}
+
+static void
+gimp_color_area_drag_data_get (GtkWidget *widget,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time)
+{
+ GimpColorArea *area = GIMP_COLOR_AREA (widget);
+ guint16 vals[4];
+
+ vals[0] = area->color.r * 0xffff;
+ vals[1] = area->color.g * 0xffff;
+ vals[2] = area->color.b * 0xffff;
+
+ if (area->type == GIMP_COLOR_AREA_FLAT)
+ vals[3] = 0xffff;
+ else
+ vals[3] = area->color.a * 0xffff;
+
+ gtk_selection_data_set (selection_data,
+ gdk_atom_intern ("application/x-color", FALSE),
+ 16, (guchar *) vals, 8);
+}
+
+static void
+gimp_color_area_create_transform (GimpColorArea *area)
+{
+ GimpColorAreaPrivate *priv = GET_PRIVATE (area);
+
+ if (priv->config)
+ {
+ static GimpColorProfile *profile = NULL;
+
+ const Babl *format = babl_format ("cairo-RGB24");
+
+ if (G_UNLIKELY (! profile))
+ profile = gimp_color_profile_new_rgb_srgb ();
+
+ priv->transform = gimp_widget_get_color_transform (GTK_WIDGET (area),
+ priv->config,
+ profile,
+ format,
+ format);
+ }
+}
+
+static void
+gimp_color_area_destroy_transform (GimpColorArea *area)
+{
+ GimpColorAreaPrivate *priv = GET_PRIVATE (area);
+
+ g_clear_object (&priv->transform);
+
+ gtk_widget_queue_draw (GTK_WIDGET (area));
+}
diff --git a/libgimpwidgets/gimpcolorarea.h b/libgimpwidgets/gimpcolorarea.h
new file mode 100644
index 0000000..f5d19fb
--- /dev/null
+++ b/libgimpwidgets/gimpcolorarea.h
@@ -0,0 +1,100 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcolorarea.h
+ * Copyright (C) 2001-2002 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* This provides a color preview area. The preview
+ * can handle transparency by showing the checkerboard and
+ * handles drag'n'drop.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_COLOR_AREA_H__
+#define __GIMP_COLOR_AREA_H__
+
+G_BEGIN_DECLS
+
+
+#define GIMP_TYPE_COLOR_AREA (gimp_color_area_get_type ())
+#define GIMP_COLOR_AREA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_COLOR_AREA, GimpColorArea))
+#define GIMP_COLOR_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_COLOR_AREA, GimpColorAreaClass))
+#define GIMP_IS_COLOR_AREA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_COLOR_AREA))
+#define GIMP_IS_COLOR_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_COLOR_AREA))
+#define GIMP_COLOR_AREA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_COLOR_AREA, GimpColorAreaClass))
+
+
+typedef struct _GimpColorAreaClass GimpColorAreaClass;
+
+struct _GimpColorArea
+{
+ GtkDrawingArea parent_instance;
+
+ /*< private >*/
+ guchar *buf;
+ guint width;
+ guint height;
+ guint rowstride;
+
+ GimpColorAreaType type;
+ GimpRGB color;
+ guint draw_border : 1;
+ guint needs_render : 1;
+};
+
+struct _GimpColorAreaClass
+{
+ GtkDrawingAreaClass parent_class;
+
+ void (* color_changed) (GimpColorArea *area);
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_color_area_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_color_area_new (const GimpRGB *color,
+ GimpColorAreaType type,
+ GdkModifierType drag_mask);
+
+void gimp_color_area_set_color (GimpColorArea *area,
+ const GimpRGB *color);
+void gimp_color_area_get_color (GimpColorArea *area,
+ GimpRGB *color);
+
+gboolean gimp_color_area_has_alpha (GimpColorArea *area);
+void gimp_color_area_set_type (GimpColorArea *area,
+ GimpColorAreaType type);
+void gimp_color_area_set_draw_border (GimpColorArea *area,
+ gboolean draw_border);
+
+void gimp_color_area_set_color_config (GimpColorArea *area,
+ GimpColorConfig *config);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_COLOR_AREA_H__ */
diff --git a/libgimpwidgets/gimpcolorbutton.c b/libgimpwidgets/gimpcolorbutton.c
new file mode 100644
index 0000000..6ec562e
--- /dev/null
+++ b/libgimpwidgets/gimpcolorbutton.c
@@ -0,0 +1,1014 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcolorbutton.c
+ * Copyright (C) 1999-2001 Sven Neumann
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpcolorarea.h"
+#include "gimpcolorbutton.h"
+#include "gimpcolornotebook.h"
+#include "gimpcolorselection.h"
+#include "gimpdialog.h"
+#include "gimphelpui.h"
+#include "gimpicons.h"
+#include "gimpwidgets-private.h"
+#include "gimp3migration.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpcolorbutton
+ * @title: GimpColorButton
+ * @short_description: Widget for selecting a color from a simple button.
+ * @see_also: #libgimpcolor-gimpcolorspace
+ *
+ * This widget provides a simple button with a preview showing the
+ * color.
+ *
+ * On click a color selection dialog is opened. Additionally the
+ * button supports Drag and Drop and has a right-click menu that
+ * allows one to choose the color from the current FG or BG color. If
+ * the user changes the color, the "color-changed" signal is emitted.
+ **/
+
+
+#define COLOR_BUTTON_KEY "gimp-color-button"
+#define RESPONSE_RESET 1
+
+#define TODOUBLE(i) (i / 65535.0)
+#define TOUINT16(d) ((guint16) (d * 65535 + 0.5))
+
+
+#define GIMP_COLOR_BUTTON_COLOR_FG "color-button-use-foreground"
+#define GIMP_COLOR_BUTTON_COLOR_BG "color-button-use-background"
+#define GIMP_COLOR_BUTTON_COLOR_BLACK "color-button-use-black"
+#define GIMP_COLOR_BUTTON_COLOR_WHITE "color-button-use-white"
+
+
+enum
+{
+ COLOR_CHANGED,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_TITLE,
+ PROP_COLOR,
+ PROP_TYPE,
+ PROP_UPDATE,
+ PROP_AREA_WIDTH,
+ PROP_AREA_HEIGHT,
+ PROP_COLOR_CONFIG
+};
+
+
+typedef struct _GimpColorButtonPrivate GimpColorButtonPrivate;
+
+struct _GimpColorButtonPrivate
+{
+ GtkWidget *selection;
+
+ GimpColorConfig *config;
+};
+
+#define GET_PRIVATE(obj) (gimp_color_button_get_instance_private (GIMP_COLOR_BUTTON (obj)))
+
+
+static void gimp_color_button_constructed (GObject *object);
+static void gimp_color_button_finalize (GObject *object);
+static void gimp_color_button_dispose (GObject *object);
+static void gimp_color_button_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_color_button_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static gboolean gimp_color_button_button_press (GtkWidget *widget,
+ GdkEventButton *bevent);
+static void gimp_color_button_state_changed (GtkWidget *widget,
+ GtkStateType prev_state);
+static void gimp_color_button_clicked (GtkButton *button);
+static GType gimp_color_button_get_action_type (GimpColorButton *button);
+
+static void gimp_color_button_dialog_response (GtkWidget *dialog,
+ gint response_id,
+ GimpColorButton *button);
+static void gimp_color_button_use_color (GtkAction *action,
+ GimpColorButton *button);
+static void gimp_color_button_area_changed (GtkWidget *color_area,
+ GimpColorButton *button);
+static void gimp_color_button_selection_changed (GtkWidget *selection,
+ GimpColorButton *button);
+static void gimp_color_button_help_func (const gchar *help_id,
+ gpointer help_data);
+
+
+static const GtkActionEntry actions[] =
+{
+ { "color-button-popup", NULL,
+ "Color Button Menu", NULL, NULL,
+ NULL
+ },
+
+ { GIMP_COLOR_BUTTON_COLOR_FG, NULL,
+ N_("_Foreground Color"), NULL, NULL,
+ G_CALLBACK (gimp_color_button_use_color)
+ },
+ { GIMP_COLOR_BUTTON_COLOR_BG, NULL,
+ N_("_Background Color"), NULL, NULL,
+ G_CALLBACK (gimp_color_button_use_color)
+ },
+ { GIMP_COLOR_BUTTON_COLOR_BLACK, NULL,
+ N_("Blac_k"), NULL, NULL,
+ G_CALLBACK (gimp_color_button_use_color)
+ },
+ { GIMP_COLOR_BUTTON_COLOR_WHITE, NULL,
+ N_("_White"), NULL, NULL,
+ G_CALLBACK (gimp_color_button_use_color)
+ }
+};
+
+
+G_DEFINE_TYPE_WITH_CODE (GimpColorButton, gimp_color_button, GIMP_TYPE_BUTTON,
+ G_ADD_PRIVATE (GimpColorButton))
+
+#define parent_class gimp_color_button_parent_class
+
+static guint gimp_color_button_signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+gimp_color_button_class_init (GimpColorButtonClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GtkButtonClass *button_class = GTK_BUTTON_CLASS (klass);
+ GimpRGB color;
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ gimp_color_button_signals[COLOR_CHANGED] =
+ g_signal_new ("color-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpColorButtonClass, color_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ object_class->constructed = gimp_color_button_constructed;
+ object_class->finalize = gimp_color_button_finalize;
+ object_class->dispose = gimp_color_button_dispose;
+ object_class->get_property = gimp_color_button_get_property;
+ object_class->set_property = gimp_color_button_set_property;
+
+ widget_class->button_press_event = gimp_color_button_button_press;
+ widget_class->state_changed = gimp_color_button_state_changed;
+
+ button_class->clicked = gimp_color_button_clicked;
+
+ klass->color_changed = NULL;
+ klass->get_action_type = gimp_color_button_get_action_type;
+
+ gimp_rgba_set (&color, 0.0, 0.0, 0.0, 1.0);
+
+ /**
+ * GimpColorButton:title:
+ *
+ * The title to be used for the color selection dialog.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class, PROP_TITLE,
+ g_param_spec_string ("title",
+ "Title",
+ "The title to be used for the color selection dialog",
+ NULL,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+ /**
+ * GimpColorButton:color:
+ *
+ * The color displayed in the button's color area.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class, PROP_COLOR,
+ gimp_param_spec_rgb ("color",
+ "Color",
+ "The color displayed in the button's color area",
+ TRUE, &color,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+ /**
+ * GimpColorButton:type:
+ *
+ * The type of the button's color area.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class, PROP_TYPE,
+ g_param_spec_enum ("type",
+ "Type",
+ "The type of the button's color area",
+ GIMP_TYPE_COLOR_AREA_TYPE,
+ GIMP_COLOR_AREA_FLAT,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+ /**
+ * GimpColorButton:continuous-update:
+ *
+ * The update policy of the color button.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class, PROP_UPDATE,
+ g_param_spec_boolean ("continuous-update",
+ "Contiguous Update",
+ "The update policy of the color button",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+ /**
+ * GimpColorButton:area-width:
+ *
+ * The minimum width of the button's #GimpColorArea.
+ *
+ * Since: 2.8
+ */
+ g_object_class_install_property (object_class, PROP_AREA_WIDTH,
+ g_param_spec_int ("area-width",
+ "Area Width",
+ "The minimum width of the button's GimpColorArea",
+ 1, G_MAXINT, 16,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT));
+ /**
+ * GimpColorButton:area-height:
+ *
+ * The minimum height of the button's #GimpColorArea.
+ *
+ * Since: 2.8
+ */
+ g_object_class_install_property (object_class, PROP_AREA_HEIGHT,
+ g_param_spec_int ("area-height",
+ "Area Height",
+ "The minimum height of the button's GimpColorArea",
+ 1, G_MAXINT, 16,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT));
+ /**
+ * GimpColorButton:color-config:
+ *
+ * The #GimpColorConfig object used for the button's #GimpColorArea
+ * and #GimpColorSelection.
+ *
+ * Since: 2.10
+ */
+ g_object_class_install_property (object_class, PROP_COLOR_CONFIG,
+ g_param_spec_object ("color-config",
+ "Color Config",
+ "The color config object used",
+ GIMP_TYPE_COLOR_CONFIG,
+ G_PARAM_READWRITE));
+}
+
+static void
+gimp_color_button_init (GimpColorButton *button)
+{
+ button->color_area = g_object_new (GIMP_TYPE_COLOR_AREA,
+ "drag-mask", GDK_BUTTON1_MASK,
+ NULL);
+
+ g_signal_connect (button->color_area, "color-changed",
+ G_CALLBACK (gimp_color_button_area_changed),
+ button);
+
+ gtk_container_add (GTK_CONTAINER (button), button->color_area);
+ gtk_widget_show (button->color_area);
+}
+
+static void
+gimp_color_button_constructed (GObject *object)
+{
+ GimpColorButton *button = GIMP_COLOR_BUTTON (object);
+ GimpColorButtonClass *klass = GIMP_COLOR_BUTTON_GET_CLASS (object);
+ GtkUIManager *ui_manager;
+ GtkActionGroup *group;
+ gint i;
+
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ /* right-click opens a popup */
+ button->popup_menu = ui_manager = gtk_ui_manager_new ();
+
+ group = gtk_action_group_new ("color-button");
+
+ for (i = 0; i < G_N_ELEMENTS (actions); i++)
+ {
+ const gchar *label = gettext (actions[i].label);
+ const gchar *tooltip = gettext (actions[i].tooltip);
+ GtkAction *action;
+
+ action = g_object_new (klass->get_action_type (button),
+ "name", actions[i].name,
+ "label", label,
+ "tooltip", tooltip,
+ "icon-name", actions[i].stock_id,
+ NULL);
+
+ if (actions[i].callback)
+ g_signal_connect (action, "activate",
+ actions[i].callback,
+ button);
+
+ gtk_action_group_add_action_with_accel (group, action,
+ actions[i].accelerator);
+
+ g_object_unref (action);
+ }
+
+ gtk_ui_manager_insert_action_group (ui_manager, group, -1);
+ g_object_unref (group);
+
+ gtk_ui_manager_add_ui_from_string
+ (ui_manager,
+ "<ui>\n"
+ " <popup action=\"color-button-popup\">\n"
+ " <menuitem action=\"" GIMP_COLOR_BUTTON_COLOR_FG "\" />\n"
+ " <menuitem action=\"" GIMP_COLOR_BUTTON_COLOR_BG "\" />\n"
+ " <separator />\n"
+ " <menuitem action=\"" GIMP_COLOR_BUTTON_COLOR_BLACK "\" />\n"
+ " <menuitem action=\"" GIMP_COLOR_BUTTON_COLOR_WHITE "\" />\n"
+ " </popup>\n"
+ "</ui>\n",
+ -1, NULL);
+}
+
+static void
+gimp_color_button_finalize (GObject *object)
+{
+ GimpColorButton *button = GIMP_COLOR_BUTTON (object);
+
+ g_clear_pointer (&button->title, g_free);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_color_button_dispose (GObject *object)
+{
+ GimpColorButton *button = GIMP_COLOR_BUTTON (object);
+ GimpColorButtonPrivate *priv = GET_PRIVATE (button);
+
+ g_clear_pointer (&button->dialog, gtk_widget_destroy);
+ priv->selection = NULL;
+
+ g_clear_pointer (&button->color_area, gtk_widget_destroy);
+
+ g_clear_object (&button->popup_menu);
+
+ gimp_color_button_set_color_config (button, NULL);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gimp_color_button_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpColorButton *button = GIMP_COLOR_BUTTON (object);
+ GimpColorButtonPrivate *priv = GET_PRIVATE (object);
+
+ switch (property_id)
+ {
+ case PROP_TITLE:
+ g_value_set_string (value, button->title);
+ break;
+
+ case PROP_COLOR:
+ g_object_get_property (G_OBJECT (button->color_area), "color", value);
+ break;
+
+ case PROP_TYPE:
+ g_object_get_property (G_OBJECT (button->color_area), "type", value);
+ break;
+
+ case PROP_UPDATE:
+ g_value_set_boolean (value, button->continuous_update);
+ break;
+
+ case PROP_COLOR_CONFIG:
+ g_value_set_object (value, priv->config);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_color_button_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpColorButton *button = GIMP_COLOR_BUTTON (object);
+ gint other;
+
+ switch (property_id)
+ {
+ case PROP_TITLE:
+ gimp_color_button_set_title (button, g_value_get_string (value));
+ break;
+
+ case PROP_COLOR:
+ g_object_set_property (G_OBJECT (button->color_area), "color", value);
+ break;
+
+ case PROP_TYPE:
+ g_object_set_property (G_OBJECT (button->color_area), "type", value);
+ break;
+
+ case PROP_UPDATE:
+ gimp_color_button_set_update (button, g_value_get_boolean (value));
+ break;
+
+ case PROP_AREA_WIDTH:
+ gtk_widget_get_size_request (button->color_area, NULL, &other);
+ gtk_widget_set_size_request (button->color_area,
+ g_value_get_int (value), other);
+ break;
+
+ case PROP_AREA_HEIGHT:
+ gtk_widget_get_size_request (button->color_area, &other, NULL);
+ gtk_widget_set_size_request (button->color_area,
+ other, g_value_get_int (value));
+ break;
+
+ case PROP_COLOR_CONFIG:
+ gimp_color_button_set_color_config (button, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+gimp_color_button_button_press (GtkWidget *widget,
+ GdkEventButton *bevent)
+{
+ GimpColorButton *button = GIMP_COLOR_BUTTON (widget);
+
+ if (gdk_event_triggers_context_menu ((GdkEvent *) bevent))
+ {
+ GtkWidget *menu = gtk_ui_manager_get_widget (button->popup_menu,
+ "/color-button-popup");
+
+ gtk_menu_set_screen (GTK_MENU (menu), gtk_widget_get_screen (widget));
+
+ gtk_menu_popup (GTK_MENU (menu),
+ NULL, NULL, NULL, NULL,
+ bevent->button, bevent->time);
+ }
+
+ if (GTK_WIDGET_CLASS (parent_class)->button_press_event)
+ return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, bevent);
+
+ return FALSE;
+}
+
+static void
+gimp_color_button_state_changed (GtkWidget *widget,
+ GtkStateType prev_state)
+{
+ g_return_if_fail (GIMP_IS_COLOR_BUTTON (widget));
+
+ if (! gtk_widget_is_sensitive (widget) && GIMP_COLOR_BUTTON (widget)->dialog)
+ gtk_widget_hide (GIMP_COLOR_BUTTON (widget)->dialog);
+
+ if (GTK_WIDGET_CLASS (parent_class)->state_changed)
+ GTK_WIDGET_CLASS (parent_class)->state_changed (widget, prev_state);
+}
+
+static void
+gimp_color_button_clicked (GtkButton *button)
+{
+ GimpColorButton *color_button = GIMP_COLOR_BUTTON (button);
+ GimpColorButtonPrivate *priv = GET_PRIVATE (button);
+ GimpRGB color;
+
+ if (! color_button->dialog)
+ {
+ GtkWidget *dialog;
+
+ dialog = color_button->dialog =
+ gimp_dialog_new (color_button->title, "gimp-color-selection",
+ gtk_widget_get_toplevel (GTK_WIDGET (button)), 0,
+ gimp_color_button_help_func, NULL,
+
+ _("_Reset"), RESPONSE_RESET,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ g_object_set_data (G_OBJECT (dialog), COLOR_BUTTON_KEY, button);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ RESPONSE_RESET,
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (gimp_color_button_dialog_response),
+ color_button);
+ g_signal_connect (dialog, "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &color_button->dialog);
+
+ priv->selection = gimp_color_selection_new ();
+ gtk_container_set_border_width (GTK_CONTAINER (priv->selection), 6);
+ gimp_color_selection_set_show_alpha (GIMP_COLOR_SELECTION (priv->selection),
+ gimp_color_button_has_alpha (color_button));
+ gimp_color_selection_set_config (GIMP_COLOR_SELECTION (priv->selection),
+ priv->config);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ priv->selection, TRUE, TRUE, 0);
+ gtk_widget_show (priv->selection);
+
+ g_signal_connect (priv->selection, "color-changed",
+ G_CALLBACK (gimp_color_button_selection_changed),
+ button);
+ }
+
+ gimp_color_button_get_color (color_button, &color);
+
+ g_signal_handlers_block_by_func (priv->selection,
+ gimp_color_button_selection_changed,
+ button);
+
+ gimp_color_selection_set_color (GIMP_COLOR_SELECTION (priv->selection), &color);
+ gimp_color_selection_set_old_color (GIMP_COLOR_SELECTION (priv->selection),
+ &color);
+
+ g_signal_handlers_unblock_by_func (priv->selection,
+ gimp_color_button_selection_changed,
+ button);
+
+ gtk_window_present (GTK_WINDOW (color_button->dialog));
+}
+
+static GType
+gimp_color_button_get_action_type (GimpColorButton *button)
+{
+ return GTK_TYPE_ACTION;
+}
+
+
+/* public functions */
+
+/**
+ * gimp_color_button_new:
+ * @title: String that will be used as title for the color_selector.
+ * @width: Width of the colorpreview in pixels.
+ * @height: Height of the colorpreview in pixels.
+ * @color: A pointer to a #GimpRGB color.
+ * @type: The type of transparency to be displayed.
+ *
+ * Creates a new #GimpColorButton widget.
+ *
+ * This returns a button with a preview showing the color.
+ * When the button is clicked a GtkColorSelectionDialog is opened.
+ * If the user changes the color the new color is written into the
+ * array that was used to pass the initial color and the "color-changed"
+ * signal is emitted.
+ *
+ * Returns: Pointer to the new #GimpColorButton widget.
+ **/
+GtkWidget *
+gimp_color_button_new (const gchar *title,
+ gint width,
+ gint height,
+ const GimpRGB *color,
+ GimpColorAreaType type)
+{
+ g_return_val_if_fail (color != NULL, NULL);
+ g_return_val_if_fail (width > 0, NULL);
+ g_return_val_if_fail (height > 0, NULL);
+
+ return g_object_new (GIMP_TYPE_COLOR_BUTTON,
+ "title", title,
+ "type", type,
+ "color", color,
+ "area-width", width,
+ "area-height", height,
+ NULL);
+}
+
+/**
+ * gimp_color_button_set_title:
+ * @button: a #GimpColorButton.
+ * @title: the new title.
+ *
+ * Sets the @button dialog's title.
+ *
+ * Since: 2.10
+ **/
+void
+gimp_color_button_set_title (GimpColorButton *button,
+ const gchar *title)
+{
+ g_return_if_fail (GIMP_IS_COLOR_BUTTON (button));
+ g_return_if_fail (title != NULL);
+
+ g_free (button->title);
+ button->title = g_strdup (title);
+
+ if (button->dialog)
+ gtk_window_set_title (GTK_WINDOW (button->dialog), title);
+
+ g_object_notify (G_OBJECT (button), "title");
+}
+
+/**
+ * gimp_color_button_get_title:
+ * @button: a #GimpColorButton.
+ *
+ * Returns: The @button dialog's title.
+ *
+ * Since: 2.10
+ **/
+const gchar *
+gimp_color_button_get_title (GimpColorButton *button)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_BUTTON (button), NULL);
+
+ return button->title;
+}
+
+/**
+ * gimp_color_button_set_color:
+ * @button: Pointer to a #GimpColorButton.
+ * @color: Pointer to the new #GimpRGB color.
+ *
+ * Sets the @button to the given @color.
+ **/
+void
+gimp_color_button_set_color (GimpColorButton *button,
+ const GimpRGB *color)
+{
+ g_return_if_fail (GIMP_IS_COLOR_BUTTON (button));
+ g_return_if_fail (color != NULL);
+
+ gimp_color_area_set_color (GIMP_COLOR_AREA (button->color_area), color);
+
+ g_object_notify (G_OBJECT (button), "color");
+}
+
+/**
+ * gimp_color_button_get_color:
+ * @button: Pointer to a #GimpColorButton.
+ * @color: Pointer to a #GimpRGB struct used to return the color.
+ *
+ * Retrieves the currently set color from the @button.
+ **/
+void
+gimp_color_button_get_color (GimpColorButton *button,
+ GimpRGB *color)
+{
+ g_return_if_fail (GIMP_IS_COLOR_BUTTON (button));
+ g_return_if_fail (color != NULL);
+
+ gimp_color_area_get_color (GIMP_COLOR_AREA (button->color_area), color);
+}
+
+/**
+ * gimp_color_button_has_alpha:
+ * @button: Pointer to a #GimpColorButton.
+ *
+ * Checks whether the @buttons shows transparency information.
+ *
+ * Returns: %TRUE if the @button shows transparency information, %FALSE
+ * otherwise.
+ **/
+gboolean
+gimp_color_button_has_alpha (GimpColorButton *button)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_BUTTON (button), FALSE);
+
+ return gimp_color_area_has_alpha (GIMP_COLOR_AREA (button->color_area));
+}
+
+/**
+ * gimp_color_button_set_type:
+ * @button: Pointer to a #GimpColorButton.
+ * @type: the new #GimpColorAreaType
+ *
+ * Sets the @button to the given @type. See also gimp_color_area_set_type().
+ **/
+void
+gimp_color_button_set_type (GimpColorButton *button,
+ GimpColorAreaType type)
+{
+ g_return_if_fail (GIMP_IS_COLOR_BUTTON (button));
+
+ gimp_color_area_set_type (GIMP_COLOR_AREA (button->color_area), type);
+
+ g_object_notify (G_OBJECT (button), "type");
+}
+
+/**
+ * gimp_color_button_get_update:
+ * @button: A #GimpColorButton widget.
+ *
+ * Returns the color button's @continuous_update property.
+ *
+ * Return value: the @continuous_update property.
+ **/
+gboolean
+gimp_color_button_get_update (GimpColorButton *button)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_BUTTON (button), FALSE);
+
+ return button->continuous_update;
+}
+
+/**
+ * gimp_color_button_set_update:
+ * @button: A #GimpColorButton widget.
+ * @continuous: The new setting of the @continuous_update property.
+ *
+ * When set to #TRUE, the @button will emit the "color-changed"
+ * continuously while the color is changed in the color selection
+ * dialog.
+ **/
+void
+gimp_color_button_set_update (GimpColorButton *button,
+ gboolean continuous)
+{
+ GimpColorButtonPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_COLOR_BUTTON (button));
+
+ priv = GET_PRIVATE (button);
+
+ if (continuous != button->continuous_update)
+ {
+ button->continuous_update = continuous ? TRUE : FALSE;
+
+ if (priv->selection)
+ {
+ GimpRGB color;
+
+ if (button->continuous_update)
+ {
+ gimp_color_selection_get_color (GIMP_COLOR_SELECTION (priv->selection),
+ &color);
+ gimp_color_button_set_color (button, &color);
+ }
+ else
+ {
+ gimp_color_selection_get_old_color (GIMP_COLOR_SELECTION (priv->selection),
+ &color);
+ gimp_color_button_set_color (button, &color);
+ }
+ }
+
+ g_object_notify (G_OBJECT (button), "continuous-update");
+ }
+}
+
+/**
+ * gimp_color_button_set_color_config:
+ * @button: a #GimpColorButton widget.
+ * @config: a #GimpColorConfig object.
+ *
+ * Sets the color management configuration to use with this color button's
+ * #GimpColorArea.
+ *
+ * Since: 2.10
+ */
+void
+gimp_color_button_set_color_config (GimpColorButton *button,
+ GimpColorConfig *config)
+{
+ GimpColorButtonPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_COLOR_BUTTON (button));
+ g_return_if_fail (config == NULL || GIMP_IS_COLOR_CONFIG (config));
+
+ priv = GET_PRIVATE (button);
+
+ if (g_set_object (&priv->config, config))
+ {
+ if (button->color_area)
+ gimp_color_area_set_color_config (GIMP_COLOR_AREA (button->color_area),
+ priv->config);
+
+ if (priv->selection)
+ gimp_color_selection_set_config (GIMP_COLOR_SELECTION (priv->selection),
+ priv->config);
+ }
+}
+
+/**
+ * gimp_color_button_get_ui_manager:
+ * @button: a #GimpColorButton.
+ *
+ * Returns: The @button's #GtkUIManager.
+ *
+ * Since: 2.10
+ **/
+GtkUIManager *
+gimp_color_button_get_ui_manager (GimpColorButton *button)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_BUTTON (button), NULL);
+
+ return button->popup_menu;
+}
+
+
+/* private functions */
+
+static void
+gimp_color_button_dialog_response (GtkWidget *dialog,
+ gint response_id,
+ GimpColorButton *button)
+{
+ GimpColorButtonPrivate *priv = GET_PRIVATE (button);
+ GimpRGB color;
+
+ switch (response_id)
+ {
+ case RESPONSE_RESET:
+ gimp_color_selection_reset (GIMP_COLOR_SELECTION (priv->selection));
+ break;
+
+ case GTK_RESPONSE_OK:
+ if (! button->continuous_update)
+ {
+ gimp_color_selection_get_color (GIMP_COLOR_SELECTION (priv->selection),
+ &color);
+ gimp_color_button_set_color (button, &color);
+ }
+
+ gtk_widget_hide (dialog);
+ break;
+
+ default:
+ if (button->continuous_update)
+ {
+ gimp_color_selection_get_old_color (GIMP_COLOR_SELECTION (priv->selection),
+ &color);
+ gimp_color_button_set_color (button, &color);
+ }
+
+ gtk_widget_hide (dialog);
+ break;
+ }
+}
+
+static void
+gimp_color_button_use_color (GtkAction *action,
+ GimpColorButton *button)
+{
+ const gchar *name;
+ GimpRGB color;
+
+ name = gtk_action_get_name (action);
+ gimp_color_button_get_color (button, &color);
+
+ if (! strcmp (name, GIMP_COLOR_BUTTON_COLOR_FG))
+ {
+ if (_gimp_get_foreground_func)
+ _gimp_get_foreground_func (&color);
+ else
+ gimp_rgba_set (&color, 0.0, 0.0, 0.0, 1.0);
+ }
+ else if (! strcmp (name, GIMP_COLOR_BUTTON_COLOR_BG))
+ {
+ if (_gimp_get_background_func)
+ _gimp_get_background_func (&color);
+ else
+ gimp_rgba_set (&color, 1.0, 1.0, 1.0, 1.0);
+ }
+ else if (! strcmp (name, GIMP_COLOR_BUTTON_COLOR_BLACK))
+ {
+ gimp_rgba_set (&color, 0.0, 0.0, 0.0, 1.0);
+ }
+ else if (! strcmp (name, GIMP_COLOR_BUTTON_COLOR_WHITE))
+ {
+ gimp_rgba_set (&color, 1.0, 1.0, 1.0, 1.0);
+ }
+
+ gimp_color_button_set_color (button, &color);
+}
+
+static void
+gimp_color_button_area_changed (GtkWidget *color_area,
+ GimpColorButton *button)
+{
+ GimpColorButtonPrivate *priv = GET_PRIVATE (button);
+
+ if (priv->selection)
+ {
+ GimpRGB color;
+
+ gimp_color_button_get_color (button, &color);
+
+ g_signal_handlers_block_by_func (priv->selection,
+ gimp_color_button_selection_changed,
+ button);
+
+ gimp_color_selection_set_color (GIMP_COLOR_SELECTION (priv->selection),
+ &color);
+
+ g_signal_handlers_unblock_by_func (priv->selection,
+ gimp_color_button_selection_changed,
+ button);
+ }
+
+ g_signal_emit (button, gimp_color_button_signals[COLOR_CHANGED], 0);
+}
+
+static void
+gimp_color_button_selection_changed (GtkWidget *selection,
+ GimpColorButton *button)
+{
+ if (button->continuous_update)
+ {
+ GimpRGB color;
+
+ gimp_color_selection_get_color (GIMP_COLOR_SELECTION (selection), &color);
+
+ g_signal_handlers_block_by_func (button->color_area,
+ gimp_color_button_area_changed,
+ button);
+
+ gimp_color_area_set_color (GIMP_COLOR_AREA (button->color_area), &color);
+
+ g_signal_handlers_unblock_by_func (button->color_area,
+ gimp_color_button_area_changed,
+ button);
+
+ g_signal_emit (button, gimp_color_button_signals[COLOR_CHANGED], 0);
+ }
+}
+
+static void
+gimp_color_button_help_func (const gchar *help_id,
+ gpointer help_data)
+{
+ GimpColorButton *button;
+ GimpColorButtonPrivate *priv = GET_PRIVATE (help_data);
+ GimpColorNotebook *notebook;
+
+ button = g_object_get_data (G_OBJECT (help_data), COLOR_BUTTON_KEY);
+ priv = GET_PRIVATE (button);
+
+ notebook = GIMP_COLOR_NOTEBOOK (GIMP_COLOR_SELECTION (priv->selection)->notebook);
+
+ help_id = GIMP_COLOR_SELECTOR_GET_CLASS (notebook->cur_page)->help_id;
+
+ gimp_standard_help_func (help_id, NULL);
+}
diff --git a/libgimpwidgets/gimpcolorbutton.h b/libgimpwidgets/gimpcolorbutton.h
new file mode 100644
index 0000000..fe7771d
--- /dev/null
+++ b/libgimpwidgets/gimpcolorbutton.h
@@ -0,0 +1,114 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcolorbutton.h
+ * Copyright (C) 1999-2001 Sven Neumann
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* This provides a button with a color preview. The preview
+ * can handle transparency by showing the checkerboard.
+ * On click, a color selector is opened, which is already
+ * fully functional wired to the preview button.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_COLOR_BUTTON_H__
+#define __GIMP_COLOR_BUTTON_H__
+
+#include <libgimpwidgets/gimpbutton.h>
+
+G_BEGIN_DECLS
+
+
+#define GIMP_TYPE_COLOR_BUTTON (gimp_color_button_get_type ())
+#define GIMP_COLOR_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_COLOR_BUTTON, GimpColorButton))
+#define GIMP_COLOR_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_COLOR_BUTTON, GimpColorButtonClass))
+#define GIMP_IS_COLOR_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_COLOR_BUTTON))
+#define GIMP_IS_COLOR_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_COLOR_BUTTON))
+#define GIMP_COLOR_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_COLOR_BUTTON, GimpColorButtonClass))
+
+
+typedef struct _GimpColorButtonClass GimpColorButtonClass;
+
+struct _GimpColorButton
+{
+ GimpButton parent_instance;
+
+ gchar *title;
+ gboolean continuous_update;
+
+ GtkWidget *color_area;
+ GtkWidget *dialog;
+
+ /*< private >*/
+ gpointer popup_menu;
+};
+
+struct _GimpColorButtonClass
+{
+ GimpButtonClass parent_class;
+
+ /* signals */
+ void (* color_changed) (GimpColorButton *button);
+
+ /* virtual functions */
+ GType (* get_action_type) (GimpColorButton *button);
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_color_button_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_color_button_new (const gchar *title,
+ gint width,
+ gint height,
+ const GimpRGB *color,
+ GimpColorAreaType type);
+
+void gimp_color_button_set_title (GimpColorButton *button,
+ const gchar *title);
+const gchar * gimp_color_button_get_title (GimpColorButton *button);
+
+void gimp_color_button_set_color (GimpColorButton *button,
+ const GimpRGB *color);
+void gimp_color_button_get_color (GimpColorButton *button,
+ GimpRGB *color);
+
+gboolean gimp_color_button_has_alpha (GimpColorButton *button);
+void gimp_color_button_set_type (GimpColorButton *button,
+ GimpColorAreaType type);
+
+gboolean gimp_color_button_get_update (GimpColorButton *button);
+void gimp_color_button_set_update (GimpColorButton *button,
+ gboolean continuous);
+
+void gimp_color_button_set_color_config (GimpColorButton *button,
+ GimpColorConfig *config);
+
+GtkUIManager * gimp_color_button_get_ui_manager (GimpColorButton *button);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_COLOR_BUTTON_H__ */
diff --git a/libgimpwidgets/gimpcolordisplay.c b/libgimpwidgets/gimpcolordisplay.c
new file mode 100644
index 0000000..d064e9b
--- /dev/null
+++ b/libgimpwidgets/gimpcolordisplay.c
@@ -0,0 +1,563 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcolordisplay.c
+ * Copyright (C) 2002 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpcolordisplay.h"
+#include "gimpicons.h"
+
+
+/**
+ * SECTION: gimpcolordisplay
+ * @title: GimpColorDisplay
+ * @short_description: Pluggable GIMP display color correction modules.
+ * @see_also: #GModule, #GTypeModule, #GimpModule
+ *
+ * Functions and definitions for creating pluggable GIMP
+ * display color correction modules.
+ **/
+
+
+enum
+{
+ PROP_0,
+ PROP_ENABLED,
+ PROP_COLOR_CONFIG,
+ PROP_COLOR_MANAGED
+};
+
+enum
+{
+ CHANGED,
+ LAST_SIGNAL
+};
+
+
+typedef struct
+{
+ GimpColorConfig *config;
+ GimpColorManaged *managed;
+} GimpColorDisplayPrivate;
+
+#define GIMP_COLOR_DISPLAY_GET_PRIVATE(obj) ((GimpColorDisplayPrivate *) gimp_color_display_get_instance_private ((GimpColorDisplay *) (obj)))
+
+
+static void gimp_color_display_constructed (GObject *object);
+static void gimp_color_display_dispose (GObject *object);
+static void gimp_color_display_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_color_display_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_color_display_set_color_config (GimpColorDisplay *display,
+ GimpColorConfig *config);
+static void gimp_color_display_set_color_managed (GimpColorDisplay *display,
+ GimpColorManaged *managed);
+
+
+G_DEFINE_TYPE_WITH_CODE (GimpColorDisplay, gimp_color_display, G_TYPE_OBJECT,
+ G_ADD_PRIVATE (GimpColorDisplay)
+ G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, NULL))
+
+#define parent_class gimp_color_display_parent_class
+
+static guint display_signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+gimp_color_display_class_init (GimpColorDisplayClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = gimp_color_display_constructed;
+ object_class->dispose = gimp_color_display_dispose;
+ object_class->set_property = gimp_color_display_set_property;
+ object_class->get_property = gimp_color_display_get_property;
+
+ g_object_class_install_property (object_class, PROP_ENABLED,
+ g_param_spec_boolean ("enabled",
+ "Enabled",
+ "Whether this display filter is enabled",
+ TRUE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_COLOR_CONFIG,
+ g_param_spec_object ("color-config",
+ "Color Config",
+ "The color config used for this filter",
+ GIMP_TYPE_COLOR_CONFIG,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (object_class, PROP_COLOR_MANAGED,
+ g_param_spec_object ("color-managed",
+ "Color Managed",
+ "The color managed pixel source that is filtered",
+ GIMP_TYPE_COLOR_MANAGED,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+ display_signals[CHANGED] =
+ g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpColorDisplayClass, changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ klass->name = "Unnamed";
+ klass->help_id = NULL;
+ klass->icon_name = GIMP_ICON_DISPLAY_FILTER;
+
+ klass->clone = NULL;
+ klass->convert_buffer = NULL;
+ klass->convert_surface = NULL;
+ klass->convert = NULL;
+ klass->load_state = NULL;
+ klass->save_state = NULL;
+ klass->configure = NULL;
+ klass->configure_reset = NULL;
+ klass->changed = NULL;
+}
+
+static void
+gimp_color_display_init (GimpColorDisplay *display)
+{
+ display->enabled = FALSE;
+}
+
+static void
+gimp_color_display_constructed (GObject *object)
+{
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ /* emit an initial "changed" signal after all construct properties are set */
+ gimp_color_display_changed (GIMP_COLOR_DISPLAY (object));
+}
+
+static void
+gimp_color_display_dispose (GObject *object)
+{
+ GimpColorDisplayPrivate *private = GIMP_COLOR_DISPLAY_GET_PRIVATE (object);
+
+ if (private->config)
+ {
+ g_signal_handlers_disconnect_by_func (private->config,
+ gimp_color_display_changed,
+ object);
+ g_object_unref (private->config);
+ private->config = NULL;
+ }
+
+ if (private->managed)
+ {
+ g_signal_handlers_disconnect_by_func (private->managed,
+ gimp_color_display_changed,
+ object);
+ g_object_unref (private->managed);
+ private->managed = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gimp_color_display_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpColorDisplay *display = GIMP_COLOR_DISPLAY (object);
+
+ switch (property_id)
+ {
+ case PROP_ENABLED:
+ display->enabled = g_value_get_boolean (value);
+ break;
+
+ case PROP_COLOR_CONFIG:
+ gimp_color_display_set_color_config (display,
+ g_value_get_object (value));
+ break;
+
+ case PROP_COLOR_MANAGED:
+ gimp_color_display_set_color_managed (display,
+ g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_color_display_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpColorDisplay *display = GIMP_COLOR_DISPLAY (object);
+
+ switch (property_id)
+ {
+ case PROP_ENABLED:
+ g_value_set_boolean (value, display->enabled);
+ break;
+
+ case PROP_COLOR_CONFIG:
+ g_value_set_object (value,
+ GIMP_COLOR_DISPLAY_GET_PRIVATE (display)->config);
+ break;
+
+ case PROP_COLOR_MANAGED:
+ g_value_set_object (value,
+ GIMP_COLOR_DISPLAY_GET_PRIVATE (display)->managed);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_color_display_set_color_config (GimpColorDisplay *display,
+ GimpColorConfig *config)
+{
+ GimpColorDisplayPrivate *private = GIMP_COLOR_DISPLAY_GET_PRIVATE (display);
+
+ g_return_if_fail (private->config == NULL);
+
+ if (config)
+ {
+ private->config = g_object_ref (config);
+
+ g_signal_connect_swapped (private->config, "notify",
+ G_CALLBACK (gimp_color_display_changed),
+ display);
+ }
+}
+
+static void
+gimp_color_display_set_color_managed (GimpColorDisplay *display,
+ GimpColorManaged *managed)
+{
+ GimpColorDisplayPrivate *private = GIMP_COLOR_DISPLAY_GET_PRIVATE (display);
+
+ g_return_if_fail (private->managed == NULL);
+
+ if (managed)
+ {
+ private->managed = g_object_ref (managed);
+
+ g_signal_connect_swapped (private->managed, "profile-changed",
+ G_CALLBACK (gimp_color_display_changed),
+ display);
+ }
+}
+
+/**
+ * gimp_color_display_new:
+ * @display_type: the GType of the GimpColorDisplay to instantiate.
+ *
+ * This function is deprecated. Please use g_object_new() directly.
+ *
+ * Return value: a new %GimpColorDisplay object.
+ **/
+GimpColorDisplay *
+gimp_color_display_new (GType display_type)
+{
+ g_return_val_if_fail (g_type_is_a (display_type, GIMP_TYPE_COLOR_DISPLAY),
+ NULL);
+
+ return g_object_new (display_type, NULL);
+}
+
+GimpColorDisplay *
+gimp_color_display_clone (GimpColorDisplay *display)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_DISPLAY (display), NULL);
+
+ /* implementing the clone method is deprecated
+ */
+ if (GIMP_COLOR_DISPLAY_GET_CLASS (display)->clone)
+ {
+ GimpColorDisplay *clone;
+
+ clone = GIMP_COLOR_DISPLAY_GET_CLASS (display)->clone (display);
+
+ if (clone)
+ {
+ GimpColorDisplayPrivate *private;
+
+ private = GIMP_COLOR_DISPLAY_GET_PRIVATE (display);
+
+ g_object_set (clone,
+ "enabled", display->enabled,
+ "color-managed", private->managed,
+ NULL);
+ }
+
+ return clone;
+ }
+
+ return GIMP_COLOR_DISPLAY (gimp_config_duplicate (GIMP_CONFIG (display)));
+}
+
+/**
+ * gimp_color_display_convert_buffer:
+ * @display: a #GimpColorDisplay
+ * @buffer: a #GeglBuffer
+ * @area: area in @buffer to convert
+ *
+ * Converts all pixels in @area of @buffer.
+ *
+ * Since: 2.10
+ **/
+void
+gimp_color_display_convert_buffer (GimpColorDisplay *display,
+ GeglBuffer *buffer,
+ GeglRectangle *area)
+{
+ g_return_if_fail (GIMP_IS_COLOR_DISPLAY (display));
+ g_return_if_fail (GEGL_IS_BUFFER (buffer));
+
+ if (display->enabled &&
+ GIMP_COLOR_DISPLAY_GET_CLASS (display)->convert_buffer)
+ {
+ GIMP_COLOR_DISPLAY_GET_CLASS (display)->convert_buffer (display, buffer,
+ area);
+ }
+}
+
+/**
+ * gimp_color_display_convert_surface:
+ * @display: a #GimpColorDisplay
+ * @surface: a #cairo_image_surface_t of type ARGB32
+ *
+ * Converts all pixels in @surface.
+ *
+ * Since: 2.8
+ *
+ * Deprecated: GIMP 2.8: Use gimp_color_display_convert_buffer() instead.
+ **/
+void
+gimp_color_display_convert_surface (GimpColorDisplay *display,
+ cairo_surface_t *surface)
+{
+ g_return_if_fail (GIMP_IS_COLOR_DISPLAY (display));
+ g_return_if_fail (surface != NULL);
+ g_return_if_fail (cairo_surface_get_type (surface) ==
+ CAIRO_SURFACE_TYPE_IMAGE);
+
+ if (display->enabled &&
+ GIMP_COLOR_DISPLAY_GET_CLASS (display)->convert_surface)
+ {
+ cairo_surface_flush (surface);
+ GIMP_COLOR_DISPLAY_GET_CLASS (display)->convert_surface (display, surface);
+ cairo_surface_mark_dirty (surface);
+ }
+}
+
+/**
+ * gimp_color_display_convert:
+ * @display: a #GimpColorDisplay
+ * @buf: the pixel buffer to convert
+ * @width: the width of the buffer
+ * @height: the height of the buffer
+ * @bpp: the number of bytes per pixel
+ * @bpl: the buffer's rowstride
+ *
+ * Converts all pixels in @buf.
+ *
+ * Deprecated: GIMP 2.8: Use gimp_color_display_convert_buffer() instead.
+ **/
+void
+gimp_color_display_convert (GimpColorDisplay *display,
+ guchar *buf,
+ gint width,
+ gint height,
+ gint bpp,
+ gint bpl)
+{
+ g_return_if_fail (GIMP_IS_COLOR_DISPLAY (display));
+
+ /* implementing the convert method is deprecated
+ */
+ if (display->enabled && GIMP_COLOR_DISPLAY_GET_CLASS (display)->convert)
+ GIMP_COLOR_DISPLAY_GET_CLASS (display)->convert (display, buf,
+ width, height,
+ bpp, bpl);
+}
+
+void
+gimp_color_display_load_state (GimpColorDisplay *display,
+ GimpParasite *state)
+{
+ g_return_if_fail (GIMP_IS_COLOR_DISPLAY (display));
+ g_return_if_fail (state != NULL);
+
+ /* implementing the load_state method is deprecated
+ */
+ if (GIMP_COLOR_DISPLAY_GET_CLASS (display)->load_state)
+ {
+ GIMP_COLOR_DISPLAY_GET_CLASS (display)->load_state (display, state);
+ }
+ else
+ {
+ gimp_config_deserialize_string (GIMP_CONFIG (display),
+ gimp_parasite_data (state),
+ gimp_parasite_data_size (state),
+ NULL, NULL);
+ }
+}
+
+GimpParasite *
+gimp_color_display_save_state (GimpColorDisplay *display)
+{
+ GimpParasite *parasite;
+ gchar *str;
+
+ g_return_val_if_fail (GIMP_IS_COLOR_DISPLAY (display), NULL);
+
+ /* implementing the save_state method is deprecated
+ */
+ if (GIMP_COLOR_DISPLAY_GET_CLASS (display)->save_state)
+ {
+ return GIMP_COLOR_DISPLAY_GET_CLASS (display)->save_state (display);
+ }
+
+ str = gimp_config_serialize_to_string (GIMP_CONFIG (display), NULL);
+
+ parasite = gimp_parasite_new ("Display/Proof",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (str) + 1, str);
+ g_free (str);
+
+ return parasite;
+}
+
+GtkWidget *
+gimp_color_display_configure (GimpColorDisplay *display)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_DISPLAY (display), NULL);
+
+ if (GIMP_COLOR_DISPLAY_GET_CLASS (display)->configure)
+ return GIMP_COLOR_DISPLAY_GET_CLASS (display)->configure (display);
+
+ return NULL;
+}
+
+void
+gimp_color_display_configure_reset (GimpColorDisplay *display)
+{
+ g_return_if_fail (GIMP_IS_COLOR_DISPLAY (display));
+
+ /* implementing the configure_reset method is deprecated
+ */
+ if (GIMP_COLOR_DISPLAY_GET_CLASS (display)->configure_reset)
+ {
+ GIMP_COLOR_DISPLAY_GET_CLASS (display)->configure_reset (display);
+ }
+ else
+ {
+ gimp_config_reset (GIMP_CONFIG (display));
+ }
+}
+
+void
+gimp_color_display_changed (GimpColorDisplay *display)
+{
+ g_return_if_fail (GIMP_IS_COLOR_DISPLAY (display));
+
+ g_signal_emit (display, display_signals[CHANGED], 0);
+}
+
+void
+gimp_color_display_set_enabled (GimpColorDisplay *display,
+ gboolean enabled)
+{
+ g_return_if_fail (GIMP_IS_COLOR_DISPLAY (display));
+
+ if (enabled != display->enabled)
+ {
+ g_object_set (display,
+ "enabled", enabled,
+ NULL);
+ }
+}
+
+gboolean
+gimp_color_display_get_enabled (GimpColorDisplay *display)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_DISPLAY (display), FALSE);
+
+ return display->enabled;
+}
+
+/**
+ * gimp_color_display_get_config:
+ * @display:
+ *
+ * Return value: a pointer to the #GimpColorConfig object or %NULL.
+ *
+ * Since: 2.4
+ **/
+GimpColorConfig *
+gimp_color_display_get_config (GimpColorDisplay *display)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_DISPLAY (display), NULL);
+
+ return GIMP_COLOR_DISPLAY_GET_PRIVATE (display)->config;
+}
+
+/**
+ * gimp_color_display_get_managed:
+ * @display:
+ *
+ * Return value: a pointer to the #GimpColorManaged object or %NULL.
+ *
+ * Since: 2.4
+ **/
+GimpColorManaged *
+gimp_color_display_get_managed (GimpColorDisplay *display)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_DISPLAY (display), NULL);
+
+ return GIMP_COLOR_DISPLAY_GET_PRIVATE (display)->managed;
+}
diff --git a/libgimpwidgets/gimpcolordisplay.h b/libgimpwidgets/gimpcolordisplay.h
new file mode 100644
index 0000000..59c6b0d
--- /dev/null
+++ b/libgimpwidgets/gimpcolordisplay.h
@@ -0,0 +1,142 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcolordisplay.c
+ * Copyright (C) 1999 Manish Singh <yosh@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_COLOR_DISPLAY_H__
+#define __GIMP_COLOR_DISPLAY_H__
+
+G_BEGIN_DECLS
+
+/* For information look at the html documentation */
+
+
+#define GIMP_TYPE_COLOR_DISPLAY (gimp_color_display_get_type ())
+#define GIMP_COLOR_DISPLAY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_COLOR_DISPLAY, GimpColorDisplay))
+#define GIMP_COLOR_DISPLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_COLOR_DISPLAY, GimpColorDisplayClass))
+#define GIMP_IS_COLOR_DISPLAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_COLOR_DISPLAY))
+#define GIMP_IS_COLOR_DISPLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_COLOR_DISPLAY))
+#define GIMP_COLOR_DISPLAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_COLOR_DISPLAY, GimpColorDisplayClass))
+
+
+typedef struct _GimpColorDisplayClass GimpColorDisplayClass;
+
+struct _GimpColorDisplay
+{
+ GObject parent_instance;
+
+ gboolean enabled;
+};
+
+struct _GimpColorDisplayClass
+{
+ GObjectClass parent_class;
+
+ const gchar *name;
+ const gchar *help_id;
+
+ /* virtual functions */
+
+ /* implementing the GimpColorDisplay::clone method is deprecated */
+ GimpColorDisplay * (* clone) (GimpColorDisplay *display);
+
+ /* implementing the GimpColorDisplay::convert method is deprecated */
+ void (* convert) (GimpColorDisplay *display,
+ guchar *buf,
+ gint width,
+ gint height,
+ gint bpp,
+ gint bpl);
+
+ /* implementing the GimpColorDisplay::load_state method is deprecated */
+ void (* load_state) (GimpColorDisplay *display,
+ GimpParasite *state);
+
+ /* implementing the GimpColorDisplay::save_state method is deprecated */
+ GimpParasite * (* save_state) (GimpColorDisplay *display);
+
+ GtkWidget * (* configure) (GimpColorDisplay *display);
+
+ /* implementing the GimpColorDisplay::configure_reset method is deprecated */
+ void (* configure_reset) (GimpColorDisplay *display);
+
+ /* signals */
+ void (* changed) (GimpColorDisplay *display);
+
+#ifdef GIMP_DISABLE_DEPRECATED
+ gpointer deprecated_stock_id;
+#else
+ const gchar *stock_id;
+#endif
+
+ /* implementing the GimpColorDisplay::convert_surface method is deprecated */
+ void (* convert_surface) (GimpColorDisplay *display,
+ cairo_surface_t *surface);
+
+ void (* convert_buffer) (GimpColorDisplay *display,
+ GeglBuffer *buffer,
+ GeglRectangle *area);
+
+ /* icon name */
+ const gchar *icon_name;
+};
+
+
+GType gimp_color_display_get_type (void) G_GNUC_CONST;
+
+GIMP_DEPRECATED_FOR(g_object_new)
+GimpColorDisplay * gimp_color_display_new (GType display_type);
+GimpColorDisplay * gimp_color_display_clone (GimpColorDisplay *display);
+
+void gimp_color_display_convert_buffer (GimpColorDisplay *display,
+ GeglBuffer *buffer,
+ GeglRectangle *area);
+GIMP_DEPRECATED_FOR(gimp_color_display_convert_buffer)
+void gimp_color_display_convert_surface (GimpColorDisplay *display,
+ cairo_surface_t *surface);
+GIMP_DEPRECATED_FOR(gimp_color_display_convert_buffer)
+void gimp_color_display_convert (GimpColorDisplay *display,
+ guchar *buf,
+ gint width,
+ gint height,
+ gint bpp,
+ gint bpl);
+void gimp_color_display_load_state (GimpColorDisplay *display,
+ GimpParasite *state);
+GimpParasite * gimp_color_display_save_state (GimpColorDisplay *display);
+GtkWidget * gimp_color_display_configure (GimpColorDisplay *display);
+void gimp_color_display_configure_reset (GimpColorDisplay *display);
+
+void gimp_color_display_changed (GimpColorDisplay *display);
+
+void gimp_color_display_set_enabled (GimpColorDisplay *display,
+ gboolean enabled);
+gboolean gimp_color_display_get_enabled (GimpColorDisplay *display);
+
+GimpColorConfig * gimp_color_display_get_config (GimpColorDisplay *display);
+GimpColorManaged * gimp_color_display_get_managed (GimpColorDisplay *display);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_COLOR_DISPLAY_H__ */
diff --git a/libgimpwidgets/gimpcolordisplaystack.c b/libgimpwidgets/gimpcolordisplaystack.c
new file mode 100644
index 0000000..0020270
--- /dev/null
+++ b/libgimpwidgets/gimpcolordisplaystack.c
@@ -0,0 +1,412 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcolordisplaystack.c
+ * Copyright (C) 2003 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpcolor/gimpcolor.h"
+
+#include "gimpwidgetstypes.h"
+
+#undef GIMP_DISABLE_DEPRECATED
+#include "gimpcolordisplay.h"
+#include "gimpcolordisplaystack.h"
+#include "gimpwidgetsmarshal.h"
+
+
+/**
+ * SECTION: gimpcolordisplaystack
+ * @title: GimpColorDisplayStack
+ * @short_description: A stack of color correction modules.
+ * @see_also: #GimpColorDisplay
+ *
+ * A stack of color correction modules.
+ **/
+
+
+enum
+{
+ CHANGED,
+ ADDED,
+ REMOVED,
+ REORDERED,
+ LAST_SIGNAL
+};
+
+
+static void gimp_color_display_stack_dispose (GObject *object);
+
+static void gimp_color_display_stack_display_changed (GimpColorDisplay *display,
+ GimpColorDisplayStack *stack);
+static void gimp_color_display_stack_display_enabled (GimpColorDisplay *display,
+ GParamSpec *pspec,
+ GimpColorDisplayStack *stack);
+static void gimp_color_display_stack_disconnect (GimpColorDisplayStack *stack,
+ GimpColorDisplay *display);
+
+
+G_DEFINE_TYPE (GimpColorDisplayStack, gimp_color_display_stack, G_TYPE_OBJECT)
+
+#define parent_class gimp_color_display_stack_parent_class
+
+static guint stack_signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+gimp_color_display_stack_class_init (GimpColorDisplayStackClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ stack_signals[CHANGED] =
+ g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpColorDisplayStackClass, changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ stack_signals[ADDED] =
+ g_signal_new ("added",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpColorDisplayStackClass, added),
+ NULL, NULL,
+ _gimp_widgets_marshal_VOID__OBJECT_INT,
+ G_TYPE_NONE, 2,
+ GIMP_TYPE_COLOR_DISPLAY,
+ G_TYPE_INT);
+
+ stack_signals[REMOVED] =
+ g_signal_new ("removed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpColorDisplayStackClass, removed),
+ NULL, NULL,
+ _gimp_widgets_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ GIMP_TYPE_COLOR_DISPLAY);
+
+ stack_signals[REORDERED] =
+ g_signal_new ("reordered",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpColorDisplayStackClass, reordered),
+ NULL, NULL,
+ _gimp_widgets_marshal_VOID__OBJECT_INT,
+ G_TYPE_NONE, 2,
+ GIMP_TYPE_COLOR_DISPLAY,
+ G_TYPE_INT);
+
+ object_class->dispose = gimp_color_display_stack_dispose;
+
+ klass->changed = NULL;
+ klass->added = NULL;
+ klass->removed = NULL;
+ klass->reordered = NULL;
+}
+
+static void
+gimp_color_display_stack_init (GimpColorDisplayStack *stack)
+{
+ stack->filters = NULL;
+}
+
+static void
+gimp_color_display_stack_dispose (GObject *object)
+{
+ GimpColorDisplayStack *stack = GIMP_COLOR_DISPLAY_STACK (object);
+
+ if (stack->filters)
+ {
+ GList *list;
+
+ for (list = stack->filters; list; list = g_list_next (list))
+ {
+ GimpColorDisplay *display = list->data;
+
+ gimp_color_display_stack_disconnect (stack, display);
+ g_object_unref (display);
+ }
+
+ g_list_free (stack->filters);
+ stack->filters = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+GimpColorDisplayStack *
+gimp_color_display_stack_new (void)
+{
+ return g_object_new (GIMP_TYPE_COLOR_DISPLAY_STACK, NULL);
+}
+
+GimpColorDisplayStack *
+gimp_color_display_stack_clone (GimpColorDisplayStack *stack)
+{
+ GimpColorDisplayStack *clone;
+ GList *list;
+
+ g_return_val_if_fail (GIMP_IS_COLOR_DISPLAY_STACK (stack), NULL);
+
+ clone = g_object_new (GIMP_TYPE_COLOR_DISPLAY_STACK, NULL);
+
+ for (list = stack->filters; list; list = g_list_next (list))
+ {
+ GimpColorDisplay *display;
+
+ display = gimp_color_display_clone (list->data);
+
+ gimp_color_display_stack_add (clone, display);
+ g_object_unref (display);
+ }
+
+ return clone;
+}
+
+void
+gimp_color_display_stack_changed (GimpColorDisplayStack *stack)
+{
+ g_return_if_fail (GIMP_IS_COLOR_DISPLAY_STACK (stack));
+
+ g_signal_emit (stack, stack_signals[CHANGED], 0);
+}
+
+void
+gimp_color_display_stack_add (GimpColorDisplayStack *stack,
+ GimpColorDisplay *display)
+{
+ g_return_if_fail (GIMP_IS_COLOR_DISPLAY_STACK (stack));
+ g_return_if_fail (GIMP_IS_COLOR_DISPLAY (display));
+ g_return_if_fail (g_list_find (stack->filters, display) == NULL);
+
+ stack->filters = g_list_append (stack->filters, g_object_ref (display));
+
+ g_signal_connect (display, "changed",
+ G_CALLBACK (gimp_color_display_stack_display_changed),
+ G_OBJECT (stack));
+ g_signal_connect (display, "notify::enabled",
+ G_CALLBACK (gimp_color_display_stack_display_enabled),
+ G_OBJECT (stack));
+
+ g_signal_emit (stack, stack_signals[ADDED], 0,
+ display, g_list_length (stack->filters) - 1);
+
+ gimp_color_display_stack_changed (stack);
+}
+
+void
+gimp_color_display_stack_remove (GimpColorDisplayStack *stack,
+ GimpColorDisplay *display)
+{
+ g_return_if_fail (GIMP_IS_COLOR_DISPLAY_STACK (stack));
+ g_return_if_fail (GIMP_IS_COLOR_DISPLAY (display));
+ g_return_if_fail (g_list_find (stack->filters, display) != NULL);
+
+ gimp_color_display_stack_disconnect (stack, display);
+
+ stack->filters = g_list_remove (stack->filters, display);
+
+ g_signal_emit (stack, stack_signals[REMOVED], 0, display);
+
+ gimp_color_display_stack_changed (stack);
+
+ g_object_unref (display);
+}
+
+void
+gimp_color_display_stack_reorder_up (GimpColorDisplayStack *stack,
+ GimpColorDisplay *display)
+{
+ GList *list;
+
+ g_return_if_fail (GIMP_IS_COLOR_DISPLAY_STACK (stack));
+ g_return_if_fail (GIMP_IS_COLOR_DISPLAY (display));
+
+ list = g_list_find (stack->filters, display);
+
+ g_return_if_fail (list != NULL);
+
+ if (list->prev)
+ {
+ list->data = list->prev->data;
+ list->prev->data = display;
+
+ g_signal_emit (stack, stack_signals[REORDERED], 0,
+ display, g_list_position (stack->filters, list->prev));
+
+ gimp_color_display_stack_changed (stack);
+ }
+}
+
+void
+gimp_color_display_stack_reorder_down (GimpColorDisplayStack *stack,
+ GimpColorDisplay *display)
+{
+ GList *list;
+
+ g_return_if_fail (GIMP_IS_COLOR_DISPLAY_STACK (stack));
+ g_return_if_fail (GIMP_IS_COLOR_DISPLAY (display));
+
+ list = g_list_find (stack->filters, display);
+
+ g_return_if_fail (list != NULL);
+
+ if (list->next)
+ {
+ list->data = list->next->data;
+ list->next->data = display;
+
+ g_signal_emit (stack, stack_signals[REORDERED], 0,
+ display, g_list_position (stack->filters, list->next));
+
+ gimp_color_display_stack_changed (stack);
+ }
+}
+
+/**
+ * gimp_color_display_stack_convert_buffer:
+ * @stack: a #GimpColorDisplayStack
+ * @buffer: a #GeglBuffer
+ * @area: area of @buffer to convert
+ *
+ * Runs all the stack's filters on all pixels in @area of @buffer.
+ *
+ * Since: 2.10
+ **/
+void
+gimp_color_display_stack_convert_buffer (GimpColorDisplayStack *stack,
+ GeglBuffer *buffer,
+ GeglRectangle *area)
+{
+ GList *list;
+
+ g_return_if_fail (GIMP_IS_COLOR_DISPLAY_STACK (stack));
+ g_return_if_fail (GEGL_IS_BUFFER (buffer));
+
+ for (list = stack->filters; list; list = g_list_next (list))
+ {
+ GimpColorDisplay *display = list->data;
+
+ gimp_color_display_convert_buffer (display, buffer, area);
+ }
+}
+
+/**
+ * gimp_color_display_stack_convert_surface:
+ * @stack: a #GimpColorDisplayStack
+ * @surface: a #cairo_image_surface_t of type ARGB32
+ *
+ * Runs all the stack's filters on all pixels in @surface.
+ *
+ * Since: 2.8
+ *
+ * Deprecated: GIMP 2.10: Use gimp_color_display_stack_convert_buffer() instead.
+ **/
+void
+gimp_color_display_stack_convert_surface (GimpColorDisplayStack *stack,
+ cairo_surface_t *surface)
+{
+ GList *list;
+
+ g_return_if_fail (GIMP_IS_COLOR_DISPLAY_STACK (stack));
+ g_return_if_fail (surface != NULL);
+ g_return_if_fail (cairo_surface_get_type (surface) ==
+ CAIRO_SURFACE_TYPE_IMAGE);
+
+ for (list = stack->filters; list; list = g_list_next (list))
+ {
+ GimpColorDisplay *display = list->data;
+
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+ gimp_color_display_convert_surface (display, surface);
+ G_GNUC_END_IGNORE_DEPRECATIONS
+ }
+}
+
+/**
+ * gimp_color_display_stack_convert:
+ * @stack: a #GimpColorDisplayStack
+ * @buf: the pixel buffer to convert
+ * @width: the width of the buffer
+ * @height: the height of the buffer
+ * @bpp: the number of bytes per pixel
+ * @bpl: the buffer's rowstride
+ *
+ * Converts all pixels in @buf.
+ *
+ * Deprecated: GIMP 2.8: Use gimp_color_display_stack_convert_buffer() instead.
+ **/
+void
+gimp_color_display_stack_convert (GimpColorDisplayStack *stack,
+ guchar *buf,
+ gint width,
+ gint height,
+ gint bpp,
+ gint bpl)
+{
+ GList *list;
+
+ g_return_if_fail (GIMP_IS_COLOR_DISPLAY_STACK (stack));
+
+ for (list = stack->filters; list; list = g_list_next (list))
+ {
+ GimpColorDisplay *display = list->data;
+
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+ gimp_color_display_convert (display, buf, width, height, bpp, bpl);
+ G_GNUC_END_IGNORE_DEPRECATIONS
+ }
+}
+
+
+/* private functions */
+
+static void
+gimp_color_display_stack_display_changed (GimpColorDisplay *display,
+ GimpColorDisplayStack *stack)
+{
+ if (display->enabled)
+ gimp_color_display_stack_changed (stack);
+}
+
+static void
+gimp_color_display_stack_display_enabled (GimpColorDisplay *display,
+ GParamSpec *pspec,
+ GimpColorDisplayStack *stack)
+{
+ gimp_color_display_stack_changed (stack);
+}
+
+static void
+gimp_color_display_stack_disconnect (GimpColorDisplayStack *stack,
+ GimpColorDisplay *display)
+{
+ g_signal_handlers_disconnect_by_func (display,
+ gimp_color_display_stack_display_changed,
+ stack);
+ g_signal_handlers_disconnect_by_func (display,
+ gimp_color_display_stack_display_enabled,
+ stack);
+}
diff --git a/libgimpwidgets/gimpcolordisplaystack.h b/libgimpwidgets/gimpcolordisplaystack.h
new file mode 100644
index 0000000..4009501
--- /dev/null
+++ b/libgimpwidgets/gimpcolordisplaystack.h
@@ -0,0 +1,104 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcolordisplaystack.h
+ * Copyright (C) 2003 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_COLOR_DISPLAY_STACK_H__
+#define __GIMP_COLOR_DISPLAY_STACK_H__
+
+G_BEGIN_DECLS
+
+/* For information look at the html documentation */
+
+
+#define GIMP_TYPE_COLOR_DISPLAY_STACK (gimp_color_display_stack_get_type ())
+#define GIMP_COLOR_DISPLAY_STACK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_COLOR_DISPLAY_STACK, GimpColorDisplayStack))
+#define GIMP_COLOR_DISPLAY_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_COLOR_DISPLAY_STACK, GimpColorDisplayStackClass))
+#define GIMP_IS_COLOR_DISPLAY_STACK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_COLOR_DISPLAY_STACK))
+#define GIMP_IS_COLOR_DISPLAY_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_COLOR_DISPLAY_STACK))
+#define GIMP_COLOR_DISPLAY_STACK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_COLOR_DISPLAY_STACK, GimpColorDisplayStackClass))
+
+
+typedef struct _GimpColorDisplayStackClass GimpColorDisplayStackClass;
+
+struct _GimpColorDisplayStack
+{
+ GObject parent_instance;
+
+ GList *filters;
+};
+
+struct _GimpColorDisplayStackClass
+{
+ GObjectClass parent_class;
+
+ void (* changed) (GimpColorDisplayStack *stack);
+
+ void (* added) (GimpColorDisplayStack *stack,
+ GimpColorDisplay *display,
+ gint position);
+ void (* removed) (GimpColorDisplayStack *stack,
+ GimpColorDisplay *display);
+ void (* reordered) (GimpColorDisplayStack *stack,
+ GimpColorDisplay *display,
+ gint position);
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_color_display_stack_get_type (void) G_GNUC_CONST;
+GimpColorDisplayStack * gimp_color_display_stack_new (void);
+GimpColorDisplayStack * gimp_color_display_stack_clone (GimpColorDisplayStack *stack);
+
+void gimp_color_display_stack_changed (GimpColorDisplayStack *stack);
+
+void gimp_color_display_stack_add (GimpColorDisplayStack *stack,
+ GimpColorDisplay *display);
+void gimp_color_display_stack_remove (GimpColorDisplayStack *stack,
+ GimpColorDisplay *display);
+void gimp_color_display_stack_reorder_up (GimpColorDisplayStack *stack,
+ GimpColorDisplay *display);
+void gimp_color_display_stack_reorder_down (GimpColorDisplayStack *stack,
+ GimpColorDisplay *display);
+void gimp_color_display_stack_convert_buffer (GimpColorDisplayStack *stack,
+ GeglBuffer *buffer,
+ GeglRectangle *area);
+GIMP_DEPRECATED_FOR(gimp_color_display_stack_convert_buffer)
+void gimp_color_display_stack_convert_surface (GimpColorDisplayStack *stack,
+ cairo_surface_t *surface);
+GIMP_DEPRECATED_FOR(gimp_color_display_stack_convert_buffer)
+void gimp_color_display_stack_convert (GimpColorDisplayStack *stack,
+ guchar *buf,
+ gint width,
+ gint height,
+ gint bpp,
+ gint bpl);
+
+G_END_DECLS
+
+#endif /* __GIMP_COLOR_DISPLAY_STACK_H__ */
diff --git a/libgimpwidgets/gimpcolorhexentry.c b/libgimpwidgets/gimpcolorhexentry.c
new file mode 100644
index 0000000..8c7474f
--- /dev/null
+++ b/libgimpwidgets/gimpcolorhexentry.c
@@ -0,0 +1,324 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcolorhexentry.c
+ * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "libgimpcolor/gimpcolor.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpcellrenderercolor.h"
+#include "gimpcolorhexentry.h"
+#include "gimphelpui.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpcolorhexentry
+ * @title: GimpColorHexEntry
+ * @short_description: Widget for entering a color's hex triplet.
+ *
+ * Widget for entering a color's hex triplet.
+ **/
+
+
+enum
+{
+ COLOR_CHANGED,
+ LAST_SIGNAL
+};
+
+enum
+{
+ COLUMN_NAME,
+ COLUMN_COLOR,
+ NUM_COLUMNS
+};
+
+
+static void gimp_color_hex_entry_constructed (GObject *object);
+
+static gboolean gimp_color_hex_entry_events (GtkWidget *widget,
+ GdkEvent *event);
+
+static gboolean gimp_color_hex_entry_matched (GtkEntryCompletion *completion,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ GimpColorHexEntry *entry);
+
+
+G_DEFINE_TYPE (GimpColorHexEntry, gimp_color_hex_entry, GTK_TYPE_ENTRY)
+
+#define parent_class gimp_color_hex_entry_parent_class
+
+static guint entry_signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+gimp_color_hex_entry_class_init (GimpColorHexEntryClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ entry_signals[COLOR_CHANGED] =
+ g_signal_new ("color-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpColorHexEntryClass, color_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ object_class->constructed = gimp_color_hex_entry_constructed;
+
+ klass->color_changed = NULL;
+}
+
+static void
+gimp_color_hex_entry_init (GimpColorHexEntry *entry)
+{
+ GtkEntryCompletion *completion;
+ GtkCellRenderer *cell;
+ GtkListStore *store;
+ GimpRGB *colors;
+ const gchar **names;
+ gint num_colors;
+ gint i;
+
+ /* GtkEntry's minimum size is way too large, set a reasonable one
+ * for our use case
+ */
+ gtk_entry_set_width_chars (GTK_ENTRY (entry), 8);
+
+ gimp_help_set_help_data (GTK_WIDGET (entry),
+ _("Hexadecimal color notation as used in HTML and "
+ "CSS. This entry also accepts CSS color names."),
+ NULL);
+
+ gimp_rgba_set (&entry->color, 0.0, 0.0, 0.0, 1.0);
+
+ store = gtk_list_store_new (NUM_COLUMNS, G_TYPE_STRING, GIMP_TYPE_RGB);
+
+ num_colors = gimp_rgb_list_names (&names, &colors);
+
+ for (i = 0; i < num_colors; i++)
+ {
+ GtkTreeIter iter;
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ COLUMN_NAME, names[i],
+ COLUMN_COLOR, colors + i,
+ -1);
+ }
+
+ g_free (colors);
+ g_free (names);
+
+ completion = g_object_new (GTK_TYPE_ENTRY_COMPLETION,
+ "model", store,
+ NULL);
+ g_object_unref (store);
+
+ cell = gimp_cell_renderer_color_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion), cell, FALSE);
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (completion), cell,
+ "color", COLUMN_COLOR,
+ NULL);
+
+ gtk_entry_completion_set_text_column (completion, COLUMN_NAME);
+
+ gtk_entry_set_completion (GTK_ENTRY (entry), completion);
+ g_object_unref (completion);
+
+ g_signal_connect (entry, "focus-out-event",
+ G_CALLBACK (gimp_color_hex_entry_events),
+ NULL);
+ g_signal_connect (entry, "key-press-event",
+ G_CALLBACK (gimp_color_hex_entry_events),
+ NULL);
+
+ g_signal_connect (completion, "match-selected",
+ G_CALLBACK (gimp_color_hex_entry_matched),
+ entry);
+}
+
+static void
+gimp_color_hex_entry_constructed (GObject *object)
+{
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ gtk_entry_set_text (GTK_ENTRY (object), "000000");
+}
+
+/**
+ * gimp_color_hex_entry_new:
+ *
+ * Return value: a new #GimpColorHexEntry widget
+ *
+ * Since: 2.2
+ **/
+GtkWidget *
+gimp_color_hex_entry_new (void)
+{
+ return g_object_new (GIMP_TYPE_COLOR_HEX_ENTRY, NULL);
+}
+
+/**
+ * gimp_color_hex_entry_set_color:
+ * @entry: a #GimpColorHexEntry widget
+ * @color: pointer to a #GimpRGB
+ *
+ * Sets the color displayed by a #GimpColorHexEntry. If the new color
+ * is different to the previously set color, the "color-changed"
+ * signal is emitted.
+ *
+ * Since: 2.2
+ **/
+void
+gimp_color_hex_entry_set_color (GimpColorHexEntry *entry,
+ const GimpRGB *color)
+{
+ g_return_if_fail (GIMP_IS_COLOR_HEX_ENTRY (entry));
+ g_return_if_fail (color != NULL);
+
+ if (gimp_rgb_distance (&entry->color, color) > 0.0)
+ {
+ gchar buffer[8];
+ guchar r, g, b;
+
+ gimp_rgb_set (&entry->color, color->r, color->g, color->b);
+ gimp_rgb_clamp (&entry->color);
+
+ gimp_rgb_get_uchar (&entry->color, &r, &g, &b);
+ g_snprintf (buffer, sizeof (buffer), "%.2x%.2x%.2x", r, g, b);
+
+ gtk_entry_set_text (GTK_ENTRY (entry), buffer);
+
+ /* move cursor to the end */
+ gtk_editable_set_position (GTK_EDITABLE (entry), -1);
+
+ g_signal_emit (entry, entry_signals[COLOR_CHANGED], 0);
+ }
+}
+
+/**
+ * gimp_color_hex_entry_get_color:
+ * @entry: a #GimpColorHexEntry widget
+ * @color: pointer to a #GimpRGB
+ *
+ * Retrieves the color value displayed by a #GimpColorHexEntry.
+ *
+ * Since: 2.2
+ **/
+void
+gimp_color_hex_entry_get_color (GimpColorHexEntry *entry,
+ GimpRGB *color)
+{
+ g_return_if_fail (GIMP_IS_COLOR_HEX_ENTRY (entry));
+ g_return_if_fail (color != NULL);
+
+ *color = entry->color;
+}
+
+static gboolean
+gimp_color_hex_entry_events (GtkWidget *widget,
+ GdkEvent *event)
+{
+ GimpColorHexEntry *entry = GIMP_COLOR_HEX_ENTRY (widget);
+
+ switch (event->type)
+ {
+ case GDK_KEY_PRESS:
+ {
+ GdkEventKey *kevent = (GdkEventKey *) event;
+
+ if (kevent->keyval != GDK_KEY_Return &&
+ kevent->keyval != GDK_KEY_KP_Enter &&
+ kevent->keyval != GDK_KEY_ISO_Enter)
+ break;
+ /* else fall through */
+ }
+
+ case GDK_FOCUS_CHANGE:
+ {
+ const gchar *text;
+ gchar buffer[8];
+ guchar r, g, b;
+
+ text = gtk_entry_get_text (GTK_ENTRY (widget));
+
+ gimp_rgb_get_uchar (&entry->color, &r, &g, &b);
+ g_snprintf (buffer, sizeof (buffer), "%.2x%.2x%.2x", r, g, b);
+
+ if (g_ascii_strcasecmp (buffer, text) != 0)
+ {
+ GimpRGB color;
+ gsize len = strlen (text);
+
+ if (len > 0 &&
+ (gimp_rgb_parse_hex (&color, text, len) ||
+ gimp_rgb_parse_name (&color, text, -1)))
+ {
+ gimp_color_hex_entry_set_color (entry, &color);
+ }
+ else
+ {
+ gtk_entry_set_text (GTK_ENTRY (entry), buffer);
+ }
+ }
+ }
+ break;
+
+ default:
+ /* do nothing */
+ break;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gimp_color_hex_entry_matched (GtkEntryCompletion *completion,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ GimpColorHexEntry *entry)
+{
+ gchar *name;
+ GimpRGB color;
+
+ gtk_tree_model_get (model, iter,
+ COLUMN_NAME, &name,
+ -1);
+
+ if (gimp_rgb_parse_name (&color, name, -1))
+ gimp_color_hex_entry_set_color (entry, &color);
+
+ g_free (name);
+
+ return TRUE;
+}
diff --git a/libgimpwidgets/gimpcolorhexentry.h b/libgimpwidgets/gimpcolorhexentry.h
new file mode 100644
index 0000000..cdf2119
--- /dev/null
+++ b/libgimpwidgets/gimpcolorhexentry.h
@@ -0,0 +1,75 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcolorhexentry.h
+ * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_COLOR_HEX_ENTRY_H__
+#define __GIMP_COLOR_HEX_ENTRY_H__
+
+G_BEGIN_DECLS
+
+
+#define GIMP_TYPE_COLOR_HEX_ENTRY (gimp_color_hex_entry_get_type ())
+#define GIMP_COLOR_HEX_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_COLOR_HEX_ENTRY, GimpColorHexEntry))
+#define GIMP_COLOR_HEX_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_COLOR_HEX_ENTRY, GimpColorHexEntryClass))
+#define GIMP_IS_COLOR_HEX_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_COLOR_HEX_ENTRY))
+#define GIMP_IS_COLOR_HEX_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_COLOR_HEX_ENTRY))
+#define GIMP_COLOR_HEX_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_COLOR_HEX_AREA, GimpColorHexEntryClass))
+
+
+typedef struct _GimpColorHexEntryClass GimpColorHexEntryClass;
+
+struct _GimpColorHexEntry
+{
+ GtkEntry parent_instance;
+
+ GimpRGB color;
+};
+
+struct _GimpColorHexEntryClass
+{
+ GtkEntryClass parent_class;
+
+ void (* color_changed) (GimpColorHexEntry *entry);
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_color_hex_entry_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_color_hex_entry_new (void);
+
+void gimp_color_hex_entry_set_color (GimpColorHexEntry *entry,
+ const GimpRGB *color);
+void gimp_color_hex_entry_get_color (GimpColorHexEntry *entry,
+ GimpRGB *color);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_COLOR_HEX_ENTRY_H__ */
diff --git a/libgimpwidgets/gimpcolornotebook.c b/libgimpwidgets/gimpcolornotebook.c
new file mode 100644
index 0000000..ec405ce
--- /dev/null
+++ b/libgimpwidgets/gimpcolornotebook.c
@@ -0,0 +1,547 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcolornotebook.c
+ * Copyright (C) 2002 Michael Natterer <mitch@gimp.org>
+ *
+ * based on color_notebook module
+ * Copyright (C) 1998 Austin Donnelly <austin@greenend.org.uk>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpcolor/gimpcolor.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpcolornotebook.h"
+#include "gimpcolorscales.h"
+#include "gimphelpui.h"
+#include "gimpwidgetsmarshal.h"
+#include "gimp3migration.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpcolornotebook
+ * @title: GimpColorNotebook
+ * @short_description: A #GimpColorSelector implementation.
+ *
+ * The #GimpColorNotebook widget is an implementation of a
+ * #GimpColorSelector. It serves as a container for
+ * #GimpColorSelectors.
+ **/
+
+
+#define DEFAULT_TAB_BORDER 0
+#define DEFAULT_TAB_ICON_SIZE GTK_ICON_SIZE_BUTTON
+
+
+static void gimp_color_notebook_style_set (GtkWidget *widget,
+ GtkStyle *prev_style);
+
+static void gimp_color_notebook_togg_visible (GimpColorSelector *selector,
+ gboolean visible);
+static void gimp_color_notebook_togg_sensitive (GimpColorSelector *selector,
+ gboolean sensitive);
+static void gimp_color_notebook_set_show_alpha (GimpColorSelector *selector,
+ gboolean show_alpha);
+static void gimp_color_notebook_set_color (GimpColorSelector *selector,
+ const GimpRGB *rgb,
+ const GimpHSV *hsv);
+static void gimp_color_notebook_set_channel (GimpColorSelector *selector,
+ GimpColorSelectorChannel channel);
+static void gimp_color_notebook_set_model_visible
+ (GimpColorSelector *selector,
+ GimpColorSelectorModel model,
+ gboolean gboolean);
+static void gimp_color_notebook_set_config (GimpColorSelector *selector,
+ GimpColorConfig *config);
+
+
+static void gimp_color_notebook_switch_page (GtkNotebook *gtk_notebook,
+ gpointer page,
+ guint page_num,
+ GimpColorNotebook *notebook);
+
+static void gimp_color_notebook_color_changed (GimpColorSelector *page,
+ const GimpRGB *rgb,
+ const GimpHSV *hsv,
+ GimpColorNotebook *notebook);
+static void gimp_color_notebook_channel_changed (GimpColorSelector *page,
+ GimpColorSelectorChannel channel,
+ GimpColorNotebook *notebook);
+static void gimp_color_notebook_model_visible_changed
+ (GimpColorSelector *page,
+ GimpColorSelectorModel model,
+ gboolean visible,
+ GimpColorNotebook *notebook);
+
+static GtkWidget * gimp_color_notebook_add_page (GimpColorNotebook *notebook,
+ GType page_type);
+static void gimp_color_notebook_remove_selector (GtkContainer *container,
+ GtkWidget *widget,
+ GimpColorNotebook *notebook);
+
+
+G_DEFINE_TYPE (GimpColorNotebook, gimp_color_notebook,
+ GIMP_TYPE_COLOR_SELECTOR)
+
+#define parent_class gimp_color_notebook_parent_class
+
+
+static void
+gimp_color_notebook_class_init (GimpColorNotebookClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GimpColorSelectorClass *selector_class = GIMP_COLOR_SELECTOR_CLASS (klass);
+
+ widget_class->style_set = gimp_color_notebook_style_set;
+
+ selector_class->name = "Notebook";
+ selector_class->help_id = "gimp-colorselector-notebook";
+ selector_class->set_toggles_visible = gimp_color_notebook_togg_visible;
+ selector_class->set_toggles_sensitive = gimp_color_notebook_togg_sensitive;
+ selector_class->set_show_alpha = gimp_color_notebook_set_show_alpha;
+ selector_class->set_color = gimp_color_notebook_set_color;
+ selector_class->set_channel = gimp_color_notebook_set_channel;
+ selector_class->set_model_visible = gimp_color_notebook_set_model_visible;
+ selector_class->set_config = gimp_color_notebook_set_config;
+
+ gtk_widget_class_install_style_property (widget_class,
+ g_param_spec_int ("tab-border",
+ NULL,
+ "Width of the border around the tab contents",
+ 0, G_MAXINT,
+ DEFAULT_TAB_BORDER,
+ G_PARAM_READABLE));
+
+ gtk_widget_class_install_style_property (widget_class,
+ g_param_spec_enum ("tab-icon-size",
+ NULL,
+ "Size for icons displayed in the tab",
+ GTK_TYPE_ICON_SIZE,
+ DEFAULT_TAB_ICON_SIZE,
+ G_PARAM_READABLE));
+}
+
+static void
+gimp_color_notebook_init (GimpColorNotebook *notebook)
+{
+ GType *selector_types;
+ guint n_selector_types;
+ guint i;
+
+ notebook->notebook = gtk_notebook_new ();
+ gtk_notebook_popup_enable (GTK_NOTEBOOK (notebook->notebook));
+ gtk_box_pack_start (GTK_BOX (notebook), notebook->notebook, TRUE, TRUE, 0);
+ gtk_widget_show (notebook->notebook);
+
+ g_signal_connect (notebook->notebook, "switch-page",
+ G_CALLBACK (gimp_color_notebook_switch_page),
+ notebook);
+ g_signal_connect (notebook->notebook, "remove",
+ G_CALLBACK (gimp_color_notebook_remove_selector),
+ notebook);
+
+ selector_types = g_type_children (GIMP_TYPE_COLOR_SELECTOR,
+ &n_selector_types);
+
+ if (n_selector_types == 2)
+ {
+ gtk_notebook_set_show_tabs (GTK_NOTEBOOK (notebook->notebook), FALSE);
+ gtk_notebook_set_show_border (GTK_NOTEBOOK (notebook->notebook), FALSE);
+ }
+
+ for (i = 0; i < n_selector_types; i++)
+ {
+ /* skip ourselves */
+ if (g_type_is_a (selector_types[i], GIMP_TYPE_COLOR_NOTEBOOK))
+ continue;
+
+ /* skip the "Scales" color selector */
+ if (g_type_is_a (selector_types[i], GIMP_TYPE_COLOR_SCALES))
+ continue;
+
+ gimp_color_notebook_add_page (notebook, selector_types[i]);
+ }
+
+ g_free (selector_types);
+}
+
+static void
+gimp_color_notebook_style_set (GtkWidget *widget,
+ GtkStyle *prev_style)
+{
+ GimpColorNotebook *notebook = GIMP_COLOR_NOTEBOOK (widget);
+ GList *list;
+ gint tab_border;
+ GtkIconSize icon_size;
+
+ if (GTK_WIDGET_CLASS (parent_class)->style_set)
+ GTK_WIDGET_CLASS (parent_class)->style_set (widget, prev_style);
+
+ gtk_widget_style_get (widget,
+ "tab-border", &tab_border,
+ "tab-icon_size", &icon_size,
+ NULL);
+
+ g_object_set (notebook->notebook,
+ "tab-border", tab_border,
+ NULL);
+
+ for (list = notebook->selectors; list; list = g_list_next (list))
+ {
+ GimpColorSelectorClass *selector_class;
+ GtkWidget *image;
+
+ selector_class = GIMP_COLOR_SELECTOR_GET_CLASS (list->data);
+
+ image = gtk_image_new_from_icon_name (selector_class->icon_name,
+ icon_size);
+ gimp_help_set_help_data (image, gettext (selector_class->name), NULL);
+
+ gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook->notebook),
+ GTK_WIDGET (list->data),
+ image);
+ }
+}
+
+static void
+gimp_color_notebook_togg_visible (GimpColorSelector *selector,
+ gboolean visible)
+{
+ GimpColorNotebook *notebook = GIMP_COLOR_NOTEBOOK (selector);
+ GList *list;
+
+ for (list = notebook->selectors; list; list = g_list_next (list))
+ {
+ GimpColorSelector *child = list->data;
+
+ gimp_color_selector_set_toggles_visible (child, visible);
+ }
+}
+
+static void
+gimp_color_notebook_togg_sensitive (GimpColorSelector *selector,
+ gboolean sensitive)
+{
+ GimpColorNotebook *notebook = GIMP_COLOR_NOTEBOOK (selector);
+ GList *list;
+
+ for (list = notebook->selectors; list; list = g_list_next (list))
+ {
+ GimpColorSelector *child = list->data;
+
+ gimp_color_selector_set_toggles_sensitive (child, sensitive);
+ }
+}
+
+static void
+gimp_color_notebook_set_show_alpha (GimpColorSelector *selector,
+ gboolean show_alpha)
+{
+ GimpColorNotebook *notebook = GIMP_COLOR_NOTEBOOK (selector);
+ GList *list;
+
+ for (list = notebook->selectors; list; list = g_list_next (list))
+ {
+ GimpColorSelector *child = list->data;
+
+ gimp_color_selector_set_show_alpha (child, show_alpha);
+ }
+}
+
+static void
+gimp_color_notebook_set_color (GimpColorSelector *selector,
+ const GimpRGB *rgb,
+ const GimpHSV *hsv)
+{
+ GimpColorNotebook *notebook = GIMP_COLOR_NOTEBOOK (selector);
+
+ g_signal_handlers_block_by_func (notebook->cur_page,
+ gimp_color_notebook_color_changed,
+ notebook);
+
+ gimp_color_selector_set_color (notebook->cur_page, rgb, hsv);
+
+ g_signal_handlers_unblock_by_func (notebook->cur_page,
+ gimp_color_notebook_color_changed,
+ notebook);
+}
+
+static void
+gimp_color_notebook_set_channel (GimpColorSelector *selector,
+ GimpColorSelectorChannel channel)
+{
+ GimpColorNotebook *notebook = GIMP_COLOR_NOTEBOOK (selector);
+
+ g_signal_handlers_block_by_func (notebook->cur_page,
+ gimp_color_notebook_channel_changed,
+ notebook);
+
+ gimp_color_selector_set_channel (notebook->cur_page, channel);
+
+ g_signal_handlers_unblock_by_func (notebook->cur_page,
+ gimp_color_notebook_channel_changed,
+ notebook);
+}
+
+static void
+gimp_color_notebook_set_model_visible (GimpColorSelector *selector,
+ GimpColorSelectorModel model,
+ gboolean visible)
+{
+ GimpColorNotebook *notebook = GIMP_COLOR_NOTEBOOK (selector);
+
+ g_signal_handlers_block_by_func (notebook->cur_page,
+ gimp_color_notebook_model_visible_changed,
+ notebook);
+
+ gimp_color_selector_set_model_visible (notebook->cur_page, model, visible);
+
+ g_signal_handlers_unblock_by_func (notebook->cur_page,
+ gimp_color_notebook_model_visible_changed,
+ notebook);
+}
+
+static void
+gimp_color_notebook_set_config (GimpColorSelector *selector,
+ GimpColorConfig *config)
+{
+ GimpColorNotebook *notebook = GIMP_COLOR_NOTEBOOK (selector);
+ GList *list;
+
+ for (list = notebook->selectors; list; list = g_list_next (list))
+ {
+ GimpColorSelector *child = list->data;
+
+ gimp_color_selector_set_config (child, config);
+ }
+}
+
+static void
+gimp_color_notebook_switch_page (GtkNotebook *gtk_notebook,
+ gpointer page,
+ guint page_num,
+ GimpColorNotebook *notebook)
+{
+ GimpColorSelector *selector = GIMP_COLOR_SELECTOR (notebook);
+ GtkWidget *page_widget;
+ GimpColorSelectorModel model;
+
+ page_widget = gtk_notebook_get_nth_page (gtk_notebook, page_num);
+
+ notebook->cur_page = GIMP_COLOR_SELECTOR (page_widget);
+
+ g_signal_handlers_block_by_func (notebook->cur_page,
+ gimp_color_notebook_color_changed,
+ notebook);
+ g_signal_handlers_block_by_func (notebook->cur_page,
+ gimp_color_notebook_channel_changed,
+ notebook);
+ g_signal_handlers_block_by_func (notebook->cur_page,
+ gimp_color_notebook_model_visible_changed,
+ notebook);
+
+ gimp_color_selector_set_color (notebook->cur_page,
+ &selector->rgb,
+ &selector->hsv);
+ gimp_color_selector_set_channel (notebook->cur_page,
+ gimp_color_selector_get_channel (selector));
+
+ for (model = GIMP_COLOR_SELECTOR_MODEL_RGB;
+ model <= GIMP_COLOR_SELECTOR_MODEL_HSV;
+ model++)
+ {
+ gboolean visible = gimp_color_selector_get_model_visible (selector, model);
+
+ gimp_color_selector_set_model_visible (notebook->cur_page, model,
+ visible);
+ }
+
+ g_signal_handlers_unblock_by_func (notebook->cur_page,
+ gimp_color_notebook_color_changed,
+ notebook);
+ g_signal_handlers_unblock_by_func (notebook->cur_page,
+ gimp_color_notebook_channel_changed,
+ notebook);
+ g_signal_handlers_unblock_by_func (notebook->cur_page,
+ gimp_color_notebook_model_visible_changed,
+ notebook);
+}
+
+static void
+gimp_color_notebook_color_changed (GimpColorSelector *page,
+ const GimpRGB *rgb,
+ const GimpHSV *hsv,
+ GimpColorNotebook *notebook)
+{
+ GimpColorSelector *selector = GIMP_COLOR_SELECTOR (notebook);
+
+ selector->rgb = *rgb;
+ selector->hsv = *hsv;
+
+ gimp_color_selector_color_changed (selector);
+}
+
+static void
+gimp_color_notebook_channel_changed (GimpColorSelector *page,
+ GimpColorSelectorChannel channel,
+ GimpColorNotebook *notebook)
+{
+ GimpColorSelector *selector = GIMP_COLOR_SELECTOR (notebook);
+
+ gimp_color_selector_set_channel (selector, channel);
+}
+
+static void
+gimp_color_notebook_model_visible_changed (GimpColorSelector *page,
+ GimpColorSelectorModel model,
+ gboolean visible,
+ GimpColorNotebook *notebook)
+{
+ GimpColorSelector *selector = GIMP_COLOR_SELECTOR (notebook);
+
+ gimp_color_selector_set_model_visible (selector, model, visible);
+}
+
+static GtkWidget *
+gimp_color_notebook_add_page (GimpColorNotebook *notebook,
+ GType page_type)
+{
+ GimpColorSelector *selector = GIMP_COLOR_SELECTOR (notebook);
+ GimpColorSelectorClass *selector_class;
+ GtkWidget *page;
+ GtkWidget *menu_widget;
+ GtkWidget *image;
+ GtkWidget *label;
+ gboolean show_alpha;
+
+ page = gimp_color_selector_new (page_type,
+ &selector->rgb,
+ &selector->hsv,
+ gimp_color_selector_get_channel (selector));
+
+ if (! page)
+ return NULL;
+
+ selector_class = GIMP_COLOR_SELECTOR_GET_CLASS (page);
+
+ show_alpha = gimp_color_selector_get_show_alpha (GIMP_COLOR_SELECTOR (notebook));
+ gimp_color_selector_set_show_alpha (GIMP_COLOR_SELECTOR (page), show_alpha);
+
+ menu_widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+
+ image = gtk_image_new_from_icon_name (selector_class->icon_name,
+ GTK_ICON_SIZE_MENU);
+ gtk_box_pack_start (GTK_BOX (menu_widget), image, FALSE, FALSE, 0);
+ gtk_widget_show (image);
+
+ label = gtk_label_new (gettext (selector_class->name));
+ gtk_box_pack_start (GTK_BOX (menu_widget), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ image = gtk_image_new_from_icon_name (selector_class->icon_name,
+ DEFAULT_TAB_ICON_SIZE);
+ gimp_help_set_help_data (image, gettext (selector_class->name), NULL);
+
+ gtk_notebook_append_page_menu (GTK_NOTEBOOK (notebook->notebook),
+ page, image, menu_widget);
+
+ if (! notebook->cur_page)
+ notebook->cur_page = GIMP_COLOR_SELECTOR (page);
+
+ notebook->selectors = g_list_append (notebook->selectors, page);
+
+ gtk_widget_show (page);
+
+ g_signal_connect (page, "color-changed",
+ G_CALLBACK (gimp_color_notebook_color_changed),
+ notebook);
+ g_signal_connect (page, "channel-changed",
+ G_CALLBACK (gimp_color_notebook_channel_changed),
+ notebook);
+ g_signal_connect (page, "model-visible-changed",
+ G_CALLBACK (gimp_color_notebook_model_visible_changed),
+ notebook);
+
+ return page;
+}
+
+static void
+gimp_color_notebook_remove_selector (GtkContainer *container,
+ GtkWidget *widget,
+ GimpColorNotebook *notebook)
+{
+ notebook->selectors = g_list_remove (notebook->selectors, widget);
+
+ if (! notebook->selectors)
+ notebook->cur_page = NULL;
+}
+
+
+/**
+ * gimp_color_notebook_set_has_page:
+ * @notebook: A #GimpColorNotebook widget.
+ * @page_type: The #GType of the notebook page to add or remove.
+ * @has_page: Whether the page should be added or removed.
+ *
+ * This function adds and removed pages to / from a #GimpColorNotebook.
+ * The @page_type passed must be a #GimpColorSelector subtype.
+ *
+ * Return value: The new page widget, if @has_page was #TRUE, or #NULL
+ * if @has_page was #FALSE.
+ **/
+GtkWidget *
+gimp_color_notebook_set_has_page (GimpColorNotebook *notebook,
+ GType page_type,
+ gboolean has_page)
+{
+ GList *list;
+
+ g_return_val_if_fail (GIMP_IS_COLOR_NOTEBOOK (notebook), NULL);
+ g_return_val_if_fail (g_type_is_a (page_type, GIMP_TYPE_COLOR_SELECTOR),
+ NULL);
+ g_return_val_if_fail (! g_type_is_a (page_type, GIMP_TYPE_COLOR_NOTEBOOK),
+ NULL);
+
+ for (list = notebook->selectors; list; list = g_list_next (list))
+ {
+ GimpColorSelector *page = list->data;
+
+ if (G_TYPE_FROM_INSTANCE (page) == page_type)
+ {
+ if (has_page)
+ return GTK_WIDGET (page);
+
+ gtk_container_remove (GTK_CONTAINER (notebook->notebook),
+ GTK_WIDGET (page));
+
+ return NULL;
+ }
+ }
+
+ if (! has_page)
+ return NULL;
+
+ return gimp_color_notebook_add_page (notebook, page_type);
+}
diff --git a/libgimpwidgets/gimpcolornotebook.h b/libgimpwidgets/gimpcolornotebook.h
new file mode 100644
index 0000000..37d17e3
--- /dev/null
+++ b/libgimpwidgets/gimpcolornotebook.h
@@ -0,0 +1,78 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcolornotebook.h
+ * Copyright (C) 2002 Michael Natterer <mitch@gimp.org>
+ *
+ * based on color_notebook module
+ * Copyright (C) 1998 Austin Donnelly <austin@greenend.org.uk>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_COLOR_NOTEBOOK_H__
+#define __GIMP_COLOR_NOTEBOOK_H__
+
+#include <libgimpwidgets/gimpcolorselector.h>
+
+G_BEGIN_DECLS
+
+
+#define GIMP_TYPE_COLOR_NOTEBOOK (gimp_color_notebook_get_type ())
+#define GIMP_COLOR_NOTEBOOK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_COLOR_NOTEBOOK, GimpColorNotebook))
+#define GIMP_COLOR_NOTEBOOK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_COLOR_NOTEBOOK, GimpColorNotebookClass))
+#define GIMP_IS_COLOR_NOTEBOOK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_COLOR_NOTEBOOK))
+#define GIMP_IS_COLOR_NOTEBOOK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_COLOR_NOTEBOOK))
+#define GIMP_COLOR_NOTEBOOK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_COLOR_NOTEBOOK, GimpColorNotebookClass))
+
+
+typedef struct _GimpColorNotebookClass GimpColorNotebookClass;
+
+struct _GimpColorNotebook
+{
+ GimpColorSelector parent_instance;
+
+ GtkWidget *notebook;
+
+ GList *selectors;
+ GimpColorSelector *cur_page;
+};
+
+struct _GimpColorNotebookClass
+{
+ GimpColorSelectorClass parent_class;
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_color_notebook_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_color_notebook_set_has_page (GimpColorNotebook *notebook,
+ GType page_type,
+ gboolean has_page);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_COLOR_NOTEBOOK_H__ */
diff --git a/libgimpwidgets/gimpcolorprofilechooserdialog.c b/libgimpwidgets/gimpcolorprofilechooserdialog.c
new file mode 100644
index 0000000..7849e8f
--- /dev/null
+++ b/libgimpwidgets/gimpcolorprofilechooserdialog.c
@@ -0,0 +1,355 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpColorProfileChooserDialog
+ * Copyright (C) 2006-2014 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (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
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#ifdef PLATFORM_OSX
+#include <AppKit/AppKit.h>
+#endif
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpcolor/gimpcolor.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpcolorprofilechooserdialog.h"
+#include "gimpcolorprofileview.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpcolorprofilechooserdialog
+ * @title: GimpColorProfileChooserDialog
+ * @short_description: A file chooser for selecting color profiles.
+ *
+ * A #GtkFileChooser subclass for selecting color profiles.
+ **/
+
+
+struct _GimpColorProfileChooserDialogPrivate
+{
+ GimpColorProfileView *profile_view;
+};
+
+
+static void gimp_color_profile_chooser_dialog_constructed (GObject *object);
+
+static gboolean gimp_color_profile_chooser_dialog_delete_event (GtkWidget *widget,
+ GdkEventAny *event);
+
+static void gimp_color_profile_chooser_dialog_add_shortcut (GimpColorProfileChooserDialog *dialog);
+static void gimp_color_profile_chooser_dialog_update_preview (GimpColorProfileChooserDialog *dialog);
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpColorProfileChooserDialog,
+ gimp_color_profile_chooser_dialog,
+ GTK_TYPE_FILE_CHOOSER_DIALOG)
+
+#define parent_class gimp_color_profile_chooser_dialog_parent_class
+
+
+static void
+gimp_color_profile_chooser_dialog_class_init (GimpColorProfileChooserDialogClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->constructed = gimp_color_profile_chooser_dialog_constructed;
+
+ widget_class->delete_event = gimp_color_profile_chooser_dialog_delete_event;
+}
+
+static void
+gimp_color_profile_chooser_dialog_init (GimpColorProfileChooserDialog *dialog)
+{
+ dialog->priv =
+ gimp_color_profile_chooser_dialog_get_instance_private (dialog);
+}
+
+static void
+gimp_color_profile_chooser_dialog_constructed (GObject *object)
+{
+ GimpColorProfileChooserDialog *dialog;
+ GtkFileFilter *filter;
+ GtkWidget *scrolled_window;
+ GtkWidget *profile_view;
+
+ dialog = GIMP_COLOR_PROFILE_CHOOSER_DIALOG (object);
+
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ gtk_window_set_role (GTK_WINDOW (dialog), "gimp-profile-chooser-dialog");
+
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (filter, _("All files (*.*)"));
+ gtk_file_filter_add_pattern (filter, "*");
+ gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (filter, _("ICC color profile (*.icc, *.icm)"));
+ gtk_file_filter_add_pattern (filter, "*.[Ii][Cc][Cc]");
+ gtk_file_filter_add_pattern (filter, "*.[Ii][Cc][Mm]");
+ gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+ gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+ /* the preview widget */
+
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_widget_set_size_request (scrolled_window, 300, -1);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_SHADOW_IN);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+
+ profile_view = gimp_color_profile_view_new ();
+ gtk_container_add (GTK_CONTAINER (scrolled_window), profile_view);
+ gtk_widget_show (profile_view);
+
+ dialog->priv->profile_view = GIMP_COLOR_PROFILE_VIEW (profile_view);
+
+ gtk_file_chooser_set_preview_widget (GTK_FILE_CHOOSER (dialog),
+ scrolled_window);
+
+ g_signal_connect (dialog, "update-preview",
+ G_CALLBACK (gimp_color_profile_chooser_dialog_update_preview),
+ NULL);
+}
+
+static gboolean
+gimp_color_profile_chooser_dialog_delete_event (GtkWidget *widget,
+ GdkEventAny *event)
+{
+ return TRUE;
+}
+
+GtkWidget *
+gimp_color_profile_chooser_dialog_new (const gchar *title,
+ GtkWindow *parent,
+ GtkFileChooserAction action)
+{
+ GtkWidget *dialog;
+
+ g_return_val_if_fail (title != NULL, NULL);
+ g_return_val_if_fail (parent == NULL || GTK_IS_WINDOW (parent), NULL);
+
+ dialog = g_object_new (GIMP_TYPE_COLOR_PROFILE_CHOOSER_DIALOG,
+ "title", title,
+ "action", action,
+ NULL);
+
+ if (parent)
+ gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
+
+ if (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (dialog)) ==
+ GTK_FILE_CHOOSER_ACTION_SAVE)
+ {
+ gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Save"), GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
+ TRUE);
+ }
+ else
+ {
+ gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Open"), GTK_RESPONSE_ACCEPT,
+ NULL);
+ }
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_ACCEPT,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
+
+ gimp_color_profile_chooser_dialog_add_shortcut (GIMP_COLOR_PROFILE_CHOOSER_DIALOG (dialog));
+
+ return dialog;
+}
+
+/* Add shortcuts for default ICC profile locations */
+static gboolean
+add_shortcut (GimpColorProfileChooserDialog *dialog,
+ const gchar *folder)
+{
+ return (g_file_test (folder, G_FILE_TEST_IS_DIR) &&
+ gtk_file_chooser_add_shortcut_folder (GTK_FILE_CHOOSER (dialog),
+ folder, NULL));
+}
+
+static void
+gimp_color_profile_chooser_dialog_add_shortcut (GimpColorProfileChooserDialog *dialog)
+{
+ gboolean save = (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (dialog)) ==
+ GTK_FILE_CHOOSER_ACTION_SAVE);
+
+#ifdef G_OS_WIN32
+ {
+ const gchar *prefix = g_getenv ("SystemRoot");
+ gchar *folder;
+
+ if (! prefix)
+ prefix = "c:\\windows";
+
+ folder = g_strconcat (prefix, "\\system32\\spool\\drivers\\color", NULL);
+
+ add_shortcut (dialog, folder);
+
+ g_free (folder);
+ }
+#elif defined(PLATFORM_OSX)
+ {
+ NSAutoreleasePool *pool;
+ NSArray *path;
+ NSString *library_dir;
+ gchar *folder;
+ gboolean folder_set = FALSE;
+
+ pool = [[NSAutoreleasePool alloc] init];
+
+ if (save)
+ {
+ path = NSSearchPathForDirectoriesInDomains (NSLibraryDirectory,
+ NSUserDomainMask, YES);
+ library_dir = [path objectAtIndex:0];
+
+ folder = g_build_filename ([library_dir UTF8String],
+ "ColorSync", "Profiles", NULL);
+
+ folder_set = add_shortcut (dialog, folder);
+ g_free (folder);
+ }
+
+ if (! folder_set)
+ {
+ path = NSSearchPathForDirectoriesInDomains (NSLibraryDirectory,
+ NSSystemDomainMask, YES);
+ library_dir = [path objectAtIndex:0];
+
+ folder = g_build_filename ([library_dir UTF8String],
+ "ColorSync", "Profiles", NULL);
+
+ add_shortcut (dialog, folder);
+ g_free (folder);
+ }
+
+ [pool drain];
+ }
+#else
+ {
+ gboolean folder_set = FALSE;
+
+ if (save)
+ {
+ gchar *folder = g_build_filename (g_get_user_data_dir (),
+ "color", "icc", NULL);
+
+ folder_set = add_shortcut (dialog, folder);
+
+ if (! folder_set)
+ {
+ g_free (folder);
+
+ /* Some software, like GNOME color, will save profiles in
+ * $XDG_DATA_HOME/icc/
+ */
+ folder = g_build_filename (g_get_user_data_dir (),
+ "icc", NULL);
+
+ folder_set = add_shortcut (dialog, folder);
+ }
+
+ if (! folder_set)
+ {
+ g_free (folder);
+ folder = g_build_filename (g_get_home_dir (),
+ ".color", "icc", NULL);
+
+ folder_set = add_shortcut (dialog, folder);
+ }
+
+ g_free (folder);
+ }
+
+ if (! folder_set)
+ add_shortcut (dialog, COLOR_PROFILE_DIRECTORY);
+ }
+#endif
+}
+
+static void
+gimp_color_profile_chooser_dialog_update_preview (GimpColorProfileChooserDialog *dialog)
+{
+ GimpColorProfile *profile;
+ GFile *file;
+ GError *error = NULL;
+
+ file = gtk_file_chooser_get_preview_file (GTK_FILE_CHOOSER (dialog));
+
+ if (! file)
+ {
+ gimp_color_profile_view_set_profile (dialog->priv->profile_view, NULL);
+ return;
+ }
+
+ switch (g_file_query_file_type (file, G_FILE_QUERY_INFO_NONE, NULL))
+ {
+ case G_FILE_TYPE_REGULAR:
+ profile = gimp_color_profile_new_from_file (file, &error);
+
+ if (! profile)
+ {
+ gimp_color_profile_view_set_error (dialog->priv->profile_view,
+ error->message);
+ g_clear_error (&error);
+ }
+ else
+ {
+ gimp_color_profile_view_set_profile (dialog->priv->profile_view,
+ profile);
+ g_object_unref (profile);
+ }
+ break;
+
+ case G_FILE_TYPE_DIRECTORY:
+ gimp_color_profile_view_set_error (dialog->priv->profile_view,
+ _("Folder"));
+ break;
+
+ default:
+ gimp_color_profile_view_set_error (dialog->priv->profile_view,
+ _("Not a regular file."));
+ break;
+ }
+
+ g_object_unref (file);
+}
diff --git a/libgimpwidgets/gimpcolorprofilechooserdialog.h b/libgimpwidgets/gimpcolorprofilechooserdialog.h
new file mode 100644
index 0000000..ca67154
--- /dev/null
+++ b/libgimpwidgets/gimpcolorprofilechooserdialog.h
@@ -0,0 +1,67 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpColorProfileChooserDialog
+ * Copyright (C) 2006-2014 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (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
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_COLOR_PROFILE_CHOOSER_DIALOG_H__
+#define __GIMP_COLOR_PROFILE_CHOOSER_DIALOG_H__
+
+G_BEGIN_DECLS
+
+
+#define GIMP_TYPE_COLOR_PROFILE_CHOOSER_DIALOG (gimp_color_profile_chooser_dialog_get_type ())
+#define GIMP_COLOR_PROFILE_CHOOSER_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_COLOR_PROFILE_CHOOSER_DIALOG, GimpColorProfileChooserDialog))
+#define GIMP_COLOR_PROFILE_CHOOSER_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_COLOR_PROFILE_CHOOSER_DIALOG, GimpColorProfileChooserDialogClass))
+#define GIMP_IS_COLOR_PROFILE_CHOOSER_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_COLOR_PROFILE_CHOOSER_DIALOG))
+#define GIMP_IS_COLOR_PROFILE_CHOOSER_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_COLOR_PROFILE_CHOOSER_DIALOG))
+#define GIMP_COLOR_PROFILE_CHOOSER_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_COLOR_PROFILE_CHOOSER_DIALOG, GimpColorProfileChooserDialogClass))
+
+
+typedef struct _GimpColorProfileChooserDialogClass GimpColorProfileChooserDialogClass;
+typedef struct _GimpColorProfileChooserDialogPrivate GimpColorProfileChooserDialogPrivate;
+
+struct _GimpColorProfileChooserDialog
+{
+ GtkFileChooserDialog parent_instance;
+
+ GimpColorProfileChooserDialogPrivate *priv;
+};
+
+struct _GimpColorProfileChooserDialogClass
+{
+ GtkFileChooserDialogClass parent_class;
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_color_profile_chooser_dialog_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_color_profile_chooser_dialog_new (const gchar *title,
+ GtkWindow *parent,
+ GtkFileChooserAction action);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_COLOR_PROFILE_CHOOSER_DIALOG_H__ */
diff --git a/libgimpwidgets/gimpcolorprofilecombobox.c b/libgimpwidgets/gimpcolorprofilecombobox.c
new file mode 100644
index 0000000..3481365
--- /dev/null
+++ b/libgimpwidgets/gimpcolorprofilecombobox.c
@@ -0,0 +1,628 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcolorprofilecombobox.c
+ * Copyright (C) 2007 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpcolor/gimpcolor.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpcolorprofilechooserdialog.h"
+#include "gimpcolorprofilecombobox.h"
+#include "gimpcolorprofilestore.h"
+#include "gimpcolorprofilestore-private.h"
+
+
+/**
+ * SECTION: gimpcolorprofilecombobox
+ * @title: GimpColorProfileComboBox
+ * @short_description: A combo box for selecting color profiles.
+ *
+ * A combo box for selecting color profiles.
+ **/
+
+
+enum
+{
+ PROP_0,
+ PROP_DIALOG,
+ PROP_MODEL
+};
+
+
+typedef struct
+{
+ GtkTreePath *last_path;
+} GimpColorProfileComboBoxPrivate;
+
+#define GIMP_COLOR_PROFILE_COMBO_BOX_GET_PRIVATE(obj) \
+ ((GimpColorProfileComboBoxPrivate *) gimp_color_profile_combo_box_get_instance_private ((GimpColorProfileComboBox *) (obj)))
+
+
+static void gimp_color_profile_combo_box_finalize (GObject *object);
+static void gimp_color_profile_combo_box_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_color_profile_combo_box_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_color_profile_combo_box_changed (GtkComboBox *combo);
+
+static gboolean gimp_color_profile_row_separator_func (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data);
+
+static void gimp_color_profile_combo_dialog_response (GimpColorProfileChooserDialog *dialog,
+ gint response,
+ GimpColorProfileComboBox *combo);
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpColorProfileComboBox,
+ gimp_color_profile_combo_box, GTK_TYPE_COMBO_BOX)
+
+#define parent_class gimp_color_profile_combo_box_parent_class
+
+
+static void
+gimp_color_profile_combo_box_class_init (GimpColorProfileComboBoxClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkComboBoxClass *combo_class = GTK_COMBO_BOX_CLASS (klass);
+
+ object_class->set_property = gimp_color_profile_combo_box_set_property;
+ object_class->get_property = gimp_color_profile_combo_box_get_property;
+ object_class->finalize = gimp_color_profile_combo_box_finalize;
+
+ combo_class->changed = gimp_color_profile_combo_box_changed;
+
+ /**
+ * GimpColorProfileComboBox:dialog:
+ *
+ * #GtkDialog to present when the user selects the
+ * "Select color profile from disk..." item.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class,
+ PROP_DIALOG,
+ g_param_spec_object ("dialog",
+ "Dialog",
+ "The dialog to present when selecting profiles from disk",
+ GTK_TYPE_DIALOG,
+ G_PARAM_CONSTRUCT_ONLY |
+ GIMP_PARAM_READWRITE));
+ /**
+ * GimpColorProfileComboBox:model:
+ *
+ * Overrides the "model" property of the #GtkComboBox class.
+ * #GimpColorProfileComboBox requires the model to be a
+ * #GimpColorProfileStore.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class,
+ PROP_MODEL,
+ g_param_spec_object ("model",
+ "Model",
+ "The profile store used for this combo box",
+ GIMP_TYPE_COLOR_PROFILE_STORE,
+ GIMP_PARAM_READWRITE));
+}
+
+static void
+gimp_color_profile_combo_box_init (GimpColorProfileComboBox *combo_box)
+{
+ GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
+
+ g_object_set (cell,
+ "width-chars", 42,
+ "ellipsize", PANGO_ELLIPSIZE_END,
+ NULL);
+
+
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, TRUE);
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), cell,
+ "text", GIMP_COLOR_PROFILE_STORE_LABEL,
+ NULL);
+
+ gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo_box),
+ gimp_color_profile_row_separator_func,
+ NULL, NULL);
+}
+
+static void
+gimp_color_profile_combo_box_finalize (GObject *object)
+{
+ GimpColorProfileComboBox *combo;
+ GimpColorProfileComboBoxPrivate *priv;
+
+ combo = GIMP_COLOR_PROFILE_COMBO_BOX (object);
+
+ if (combo->dialog)
+ {
+ if (GIMP_IS_COLOR_PROFILE_CHOOSER_DIALOG (combo->dialog))
+ gtk_widget_destroy (combo->dialog);
+
+ g_object_unref (combo->dialog);
+ combo->dialog = NULL;
+ }
+
+ priv = GIMP_COLOR_PROFILE_COMBO_BOX_GET_PRIVATE (combo);
+
+ g_clear_pointer (&priv->last_path, gtk_tree_path_free);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_color_profile_combo_box_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpColorProfileComboBox *combo_box = GIMP_COLOR_PROFILE_COMBO_BOX (object);
+
+ switch (property_id)
+ {
+ case PROP_DIALOG:
+ g_return_if_fail (combo_box->dialog == NULL);
+ combo_box->dialog = g_value_dup_object (value);
+
+ if (GIMP_IS_COLOR_PROFILE_CHOOSER_DIALOG (combo_box->dialog))
+ g_signal_connect (combo_box->dialog, "response",
+ G_CALLBACK (gimp_color_profile_combo_dialog_response),
+ combo_box);
+ break;
+
+ case PROP_MODEL:
+ gtk_combo_box_set_model (GTK_COMBO_BOX (combo_box),
+ g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_color_profile_combo_box_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpColorProfileComboBox *combo_box = GIMP_COLOR_PROFILE_COMBO_BOX (object);
+
+ switch (property_id)
+ {
+ case PROP_DIALOG:
+ g_value_set_object (value, combo_box->dialog);
+ break;
+
+ case PROP_MODEL:
+ g_value_set_object (value,
+ gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box)));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_color_profile_combo_box_changed (GtkComboBox *combo)
+{
+ GimpColorProfileComboBoxPrivate *priv;
+
+ GtkTreeModel *model = gtk_combo_box_get_model (combo);
+ GtkTreeIter iter;
+ gint type;
+
+ if (! gtk_combo_box_get_active_iter (combo, &iter))
+ return;
+
+ gtk_tree_model_get (model, &iter,
+ GIMP_COLOR_PROFILE_STORE_ITEM_TYPE, &type,
+ -1);
+
+ priv = GIMP_COLOR_PROFILE_COMBO_BOX_GET_PRIVATE (combo);
+
+ switch (type)
+ {
+ case GIMP_COLOR_PROFILE_STORE_ITEM_DIALOG:
+ {
+ GtkWidget *dialog = GIMP_COLOR_PROFILE_COMBO_BOX (combo)->dialog;
+ GtkWidget *parent = gtk_widget_get_toplevel (GTK_WIDGET (combo));
+
+ if (GTK_IS_WINDOW (parent))
+ gtk_window_set_transient_for (GTK_WINDOW (dialog),
+ GTK_WINDOW (parent));
+
+ gtk_window_present (GTK_WINDOW (dialog));
+
+ if (priv->last_path &&
+ gtk_tree_model_get_iter (model, &iter, priv->last_path))
+ {
+ gtk_combo_box_set_active_iter (combo, &iter);
+ }
+ }
+ break;
+
+ case GIMP_COLOR_PROFILE_STORE_ITEM_FILE:
+ if (priv->last_path)
+ gtk_tree_path_free (priv->last_path);
+
+ priv->last_path = gtk_tree_model_get_path (model, &iter);
+
+ _gimp_color_profile_store_history_reorder (GIMP_COLOR_PROFILE_STORE (model),
+ &iter);
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+/**
+ * gimp_color_profile_combo_box_new:
+ * @dialog: a #GtkDialog to present when the user selects the
+ * "Select color profile from disk..." item
+ * @history: filename of the profilerc (or %NULL for no history)
+ *
+ * Create a combo-box widget for selecting color profiles. The combo-box
+ * is populated from the file specified as @history. This filename is
+ * typically created using the following code snippet:
+ * <informalexample><programlisting>
+ * gchar *history = gimp_personal_rc_file ("profilerc");
+ * </programlisting></informalexample>
+ *
+ * The recommended @dialog type to use is a #GimpColorProfileChooserDialog.
+ * If a #GimpColorProfileChooserDialog is passed, #GimpColorProfileComboBox
+ * will take complete control over the dialog, which means connecting
+ * a GtkDialog::response() callback by itself, and take care of destroying
+ * the dialog when the combo box is destroyed.
+ *
+ * If another type of @dialog is passed, this has to be implemented
+ * separately.
+ *
+ * See also gimp_color_profile_combo_box_new_with_model().
+ *
+ * Return value: a new #GimpColorProfileComboBox.
+ *
+ * Since: 2.4
+ **/
+GtkWidget *
+gimp_color_profile_combo_box_new (GtkWidget *dialog,
+ const gchar *history)
+{
+ GtkWidget *combo;
+ GtkListStore *store;
+
+ g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
+
+ store = gimp_color_profile_store_new (history);
+ combo = gimp_color_profile_combo_box_new_with_model (dialog,
+ GTK_TREE_MODEL (store));
+ g_object_unref (store);
+
+ return combo;
+}
+
+/**
+ * gimp_color_profile_combo_box_new_with_model:
+ * @dialog: a #GtkDialog to present when the user selects the
+ * "Select color profile from disk..." item
+ * @model: a #GimpColorProfileStore object
+ *
+ * This constructor is useful when you want to create several
+ * combo-boxes for profile selection that all share the same
+ * #GimpColorProfileStore. This is for example done in the
+ * GIMP Preferences dialog.
+ *
+ * See also gimp_color_profile_combo_box_new().
+ *
+ * Return value: a new #GimpColorProfileComboBox.
+ *
+ * Since: 2.4
+ **/
+GtkWidget *
+gimp_color_profile_combo_box_new_with_model (GtkWidget *dialog,
+ GtkTreeModel *model)
+{
+ g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE_STORE (model), NULL);
+
+ return g_object_new (GIMP_TYPE_COLOR_PROFILE_COMBO_BOX,
+ "dialog", dialog,
+ "model", model,
+ NULL);
+}
+
+/**
+ * gimp_color_profile_combo_box_add:
+ * @combo: a #GimpColorProfileComboBox
+ * @filename: filename of the profile to add (or %NULL)
+ * @label: label to use for the profile
+ * (may only be %NULL if @filename is %NULL)
+ *
+ * This function delegates to the underlying
+ * #GimpColorProfileStore. Please refer to the documentation of
+ * gimp_color_profile_store_add_file() for details.
+ *
+ * Deprecated: use gimp_color_profile_combo_box_add_file() instead.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_color_profile_combo_box_add (GimpColorProfileComboBox *combo,
+ const gchar *filename,
+ const gchar *label)
+{
+ GFile *file = NULL;
+
+ g_return_if_fail (GIMP_IS_COLOR_PROFILE_COMBO_BOX (combo));
+ g_return_if_fail (label != NULL || filename == NULL);
+
+ if (filename)
+ file = g_file_new_for_path (filename);
+
+ gimp_color_profile_combo_box_add_file (combo, file, label);
+
+ if (file)
+ g_object_unref (file);
+}
+
+/**
+ * gimp_color_profile_combo_box_add_file:
+ * @combo: a #GimpColorProfileComboBox
+ * @file: file of the profile to add (or %NULL)
+ * @label: label to use for the profile
+ * (may only be %NULL if @file is %NULL)
+ *
+ * This function delegates to the underlying
+ * #GimpColorProfileStore. Please refer to the documentation of
+ * gimp_color_profile_store_add_file() for details.
+ *
+ * Since: 2.10
+ **/
+void
+gimp_color_profile_combo_box_add_file (GimpColorProfileComboBox *combo,
+ GFile *file,
+ const gchar *label)
+{
+ GtkTreeModel *model;
+
+ g_return_if_fail (GIMP_IS_COLOR_PROFILE_COMBO_BOX (combo));
+ g_return_if_fail (label != NULL || file == NULL);
+ g_return_if_fail (file == NULL || G_IS_FILE (file));
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
+
+ gimp_color_profile_store_add_file (GIMP_COLOR_PROFILE_STORE (model),
+ file, label);
+}
+
+/**
+ * gimp_color_profile_combo_box_set_active:
+ * @combo: a #GimpColorProfileComboBox
+ * @filename: filename of the profile to select
+ * @label: label to use when adding a new entry (can be %NULL)
+ *
+ * Selects a color profile from the @combo and makes it the active
+ * item. If the profile is not listed in the @combo, then it is added
+ * with the given @label (or @filename in case that @label is %NULL).
+ *
+ * Deprecated: use gimp_color_profile_combo_box_set_active_file() instead.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_color_profile_combo_box_set_active (GimpColorProfileComboBox *combo,
+ const gchar *filename,
+ const gchar *label)
+{
+ GFile *file = NULL;
+
+ g_return_if_fail (GIMP_IS_COLOR_PROFILE_COMBO_BOX (combo));
+
+ if (filename)
+ file = g_file_new_for_path (filename);
+
+ gimp_color_profile_combo_box_set_active_file (combo, file, label);
+
+ if (file)
+ g_object_unref (file);
+}
+
+/**
+ * gimp_color_profile_combo_box_set_active_file:
+ * @combo: a #GimpColorProfileComboBox
+ * @file: file of the profile to select
+ * @label: label to use when adding a new entry (can be %NULL)
+ *
+ * Selects a color profile from the @combo and makes it the active
+ * item. If the profile is not listed in the @combo, then it is added
+ * with the given @label (or @file in case that @label is %NULL).
+ *
+ * Since: 2.10
+ **/
+void
+gimp_color_profile_combo_box_set_active_file (GimpColorProfileComboBox *combo,
+ GFile *file,
+ const gchar *label)
+{
+ GimpColorProfile *profile = NULL;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ g_return_if_fail (GIMP_IS_COLOR_PROFILE_COMBO_BOX (combo));
+ g_return_if_fail (file == NULL || G_IS_FILE (file));
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
+
+ if (file && ! (label && *label))
+ {
+ GError *error = NULL;
+
+ profile = gimp_color_profile_new_from_file (file, &error);
+
+ if (! profile)
+ {
+ g_message ("%s", error->message);
+ g_clear_error (&error);
+ }
+ else
+ {
+ label = gimp_color_profile_get_label (profile);
+ }
+ }
+
+ if (_gimp_color_profile_store_history_add (GIMP_COLOR_PROFILE_STORE (model),
+ file, label, &iter))
+ {
+ gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter);
+ }
+
+ if (profile)
+ g_object_unref (profile);
+}
+
+/**
+ * gimp_color_profile_combo_box_get_active:
+ * @combo: a #GimpColorProfileComboBox
+ *
+ * Return value: The filename of the currently selected color profile,
+ * This is a newly allocated string and should be released
+ * using g_free() when it is not any longer needed.
+ *
+ * Deprecated: use gimp_color_profile_combo_box_get_active_file() instead.
+ *
+ * Since: 2.4
+ **/
+gchar *
+gimp_color_profile_combo_box_get_active (GimpColorProfileComboBox *combo)
+{
+ GFile *file;
+ gchar *path = NULL;
+
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE_COMBO_BOX (combo), NULL);
+
+ file = gimp_color_profile_combo_box_get_active_file (combo);
+
+ if (file)
+ {
+ path = g_file_get_path (file);
+ g_object_unref (file);
+ }
+
+ return path;
+}
+
+/**
+ * gimp_color_profile_combo_box_get_active_file:
+ * @combo: a #GimpColorProfileComboBox
+ *
+ * Return value: The file of the currently selected color profile,
+ * release using g_object_unref() when it is not any
+ * longer needed.
+ *
+ * Since: 2.10
+ **/
+GFile *
+gimp_color_profile_combo_box_get_active_file (GimpColorProfileComboBox *combo)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE_COMBO_BOX (combo), NULL);
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
+
+ if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter))
+ {
+ GFile *file;
+ gint type;
+
+ gtk_tree_model_get (model, &iter,
+ GIMP_COLOR_PROFILE_STORE_ITEM_TYPE, &type,
+ GIMP_COLOR_PROFILE_STORE_FILE, &file,
+ -1);
+
+ if (type == GIMP_COLOR_PROFILE_STORE_ITEM_FILE)
+ return file;
+
+ if (file)
+ g_object_unref (file);
+ }
+
+ return NULL;
+}
+
+static gboolean
+gimp_color_profile_row_separator_func (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ gint type;
+
+ gtk_tree_model_get (model, iter,
+ GIMP_COLOR_PROFILE_STORE_ITEM_TYPE, &type,
+ -1);
+
+ switch (type)
+ {
+ case GIMP_COLOR_PROFILE_STORE_ITEM_SEPARATOR_TOP:
+ case GIMP_COLOR_PROFILE_STORE_ITEM_SEPARATOR_BOTTOM:
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+
+static void
+gimp_color_profile_combo_dialog_response (GimpColorProfileChooserDialog *dialog,
+ gint response,
+ GimpColorProfileComboBox *combo)
+{
+ if (response == GTK_RESPONSE_ACCEPT)
+ {
+ GFile *file;
+
+ file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
+
+ if (file)
+ {
+ gimp_color_profile_combo_box_set_active_file (combo, file, NULL);
+
+ g_object_unref (file);
+ }
+ }
+
+ gtk_widget_hide (GTK_WIDGET (dialog));
+}
diff --git a/libgimpwidgets/gimpcolorprofilecombobox.h b/libgimpwidgets/gimpcolorprofilecombobox.h
new file mode 100644
index 0000000..e52b48b
--- /dev/null
+++ b/libgimpwidgets/gimpcolorprofilecombobox.h
@@ -0,0 +1,90 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcolorprofilecombobox.h
+ * Copyright (C) 2007 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_COLOR_PROFILE_COMBO_BOX_H__
+#define __GIMP_COLOR_PROFILE_COMBO_BOX_H__
+
+G_BEGIN_DECLS
+
+#define GIMP_TYPE_COLOR_PROFILE_COMBO_BOX (gimp_color_profile_combo_box_get_type ())
+#define GIMP_COLOR_PROFILE_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_COLOR_PROFILE_COMBO_BOX, GimpColorProfileComboBox))
+#define GIMP_COLOR_PROFILE_COMBO_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_COLOR_PROFILE_COMBO_BOX, GimpColorProfileComboBoxClass))
+#define GIMP_IS_COLOR_PROFILE_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_COLOR_PROFILE_COMBO_BOX))
+#define GIMP_IS_COLOR_PROFILE_COMBO_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_COLOR_PROFILE_COMBO_BOX))
+#define GIMP_COLOR_PROFILE_COMBO_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_COLOR_PROFILE_COMBO_BOX, GimpColorProfileComboBoxClass))
+
+
+typedef struct _GimpColorProfileComboBoxClass GimpColorProfileComboBoxClass;
+
+struct _GimpColorProfileComboBox
+{
+ GtkComboBox parent_instance;
+
+ GtkWidget *dialog;
+};
+
+struct _GimpColorProfileComboBoxClass
+{
+ GtkComboBoxClass parent_class;
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_color_profile_combo_box_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_color_profile_combo_box_new (GtkWidget *dialog,
+ const gchar *history);
+GtkWidget * gimp_color_profile_combo_box_new_with_model (GtkWidget *dialog,
+ GtkTreeModel *model);
+
+GIMP_DEPRECATED_FOR (gimp_color_profile_combo_box_add_file)
+void gimp_color_profile_combo_box_add (GimpColorProfileComboBox *combo,
+ const gchar *filename,
+ const gchar *label);
+void gimp_color_profile_combo_box_add_file (GimpColorProfileComboBox *combo,
+ GFile *file,
+ const gchar *label);
+
+GIMP_DEPRECATED_FOR (gimp_color_profile_combo_box_set_active_file)
+void gimp_color_profile_combo_box_set_active (GimpColorProfileComboBox *combo,
+ const gchar *filename,
+ const gchar *label);
+void gimp_color_profile_combo_box_set_active_file (GimpColorProfileComboBox *combo,
+ GFile *file,
+ const gchar *label);
+
+GIMP_DEPRECATED_FOR (gimp_color_profile_combo_box_get_active_file)
+gchar * gimp_color_profile_combo_box_get_active (GimpColorProfileComboBox *combo);
+GFile * gimp_color_profile_combo_box_get_active_file (GimpColorProfileComboBox *combo);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_COLOR_PROFILE_COMBO_BOX_H__ */
diff --git a/libgimpwidgets/gimpcolorprofilestore-private.h b/libgimpwidgets/gimpcolorprofilestore-private.h
new file mode 100644
index 0000000..98de84d
--- /dev/null
+++ b/libgimpwidgets/gimpcolorprofilestore-private.h
@@ -0,0 +1,52 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpprofilestore-private.h
+ * Copyright (C) 2007 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_COLOR_PROFILE_STORE_PRIVATE_H__
+#define __GIMP_COLOR_PROFILE_STORE_PRIVATE_H__
+
+
+typedef enum
+{
+ GIMP_COLOR_PROFILE_STORE_ITEM_FILE,
+ GIMP_COLOR_PROFILE_STORE_ITEM_SEPARATOR_TOP,
+ GIMP_COLOR_PROFILE_STORE_ITEM_SEPARATOR_BOTTOM,
+ GIMP_COLOR_PROFILE_STORE_ITEM_DIALOG
+} GimpColorProfileStoreItemType;
+
+typedef enum
+{
+ GIMP_COLOR_PROFILE_STORE_ITEM_TYPE,
+ GIMP_COLOR_PROFILE_STORE_LABEL,
+ GIMP_COLOR_PROFILE_STORE_FILE,
+ GIMP_COLOR_PROFILE_STORE_INDEX
+} GimpColorProfileStoreColumns;
+
+
+G_GNUC_INTERNAL gboolean _gimp_color_profile_store_history_add (GimpColorProfileStore *store,
+ GFile *file,
+ const gchar *label,
+ GtkTreeIter *iter);
+
+G_GNUC_INTERNAL void _gimp_color_profile_store_history_reorder (GimpColorProfileStore *store,
+ GtkTreeIter *iter);
+
+
+#endif /* __GIMP_COLOR_PROFILE_STORE_PRIVATE_H__ */
diff --git a/libgimpwidgets/gimpcolorprofilestore.c b/libgimpwidgets/gimpcolorprofilestore.c
new file mode 100644
index 0000000..324e4c6
--- /dev/null
+++ b/libgimpwidgets/gimpcolorprofilestore.c
@@ -0,0 +1,794 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpprofilestore.c
+ * Copyright (C) 2004-2008 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpcolorprofilestore.h"
+#include "gimpcolorprofilestore-private.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpcolorprofilestore
+ * @title: GimpColorProfileStore
+ * @short_description: A #GtkListStore subclass that keep color profiles.
+ *
+ * A #GtkListStore subclass that keep color profiles.
+ **/
+
+
+#define HISTORY_SIZE 8
+
+enum
+{
+ PROP_0,
+ PROP_HISTORY
+};
+
+
+static void gimp_color_profile_store_constructed (GObject *object);
+static void gimp_color_profile_store_dispose (GObject *object);
+static void gimp_color_profile_store_finalize (GObject *object);
+static void gimp_color_profile_store_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_color_profile_store_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static gboolean gimp_color_profile_store_history_insert (GimpColorProfileStore *store,
+ GtkTreeIter *iter,
+ GFile *file,
+ const gchar *label,
+ gint index);
+static void gimp_color_profile_store_get_separator (GimpColorProfileStore *store,
+ GtkTreeIter *iter,
+ gboolean top);
+static gboolean gimp_color_profile_store_save (GimpColorProfileStore *store,
+ const gchar *filename,
+ GError **error);
+static gboolean gimp_color_profile_store_load (GimpColorProfileStore *store,
+ const gchar *filename,
+ GError **error);
+
+
+G_DEFINE_TYPE (GimpColorProfileStore,
+ gimp_color_profile_store, GTK_TYPE_LIST_STORE)
+
+#define parent_class gimp_color_profile_store_parent_class
+
+
+static void
+gimp_color_profile_store_class_init (GimpColorProfileStoreClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = gimp_color_profile_store_constructed;
+ object_class->dispose = gimp_color_profile_store_dispose;
+ object_class->finalize = gimp_color_profile_store_finalize;
+ object_class->set_property = gimp_color_profile_store_set_property;
+ object_class->get_property = gimp_color_profile_store_get_property;
+
+ /**
+ * GimpColorProfileStore:history:
+ *
+ * Filename of the color history used to populate the profile store.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class,
+ PROP_HISTORY,
+ g_param_spec_string ("history",
+ "History",
+ "Filename of the color history used to populate the profile store",
+ NULL,
+ G_PARAM_CONSTRUCT_ONLY |
+ GIMP_PARAM_READWRITE));
+}
+
+static void
+gimp_color_profile_store_init (GimpColorProfileStore *store)
+{
+ GType types[] =
+ {
+ G_TYPE_INT, /* GIMP_COLOR_PROFILE_STORE_ITEM_TYPE */
+ G_TYPE_STRING, /* GIMP_COLOR_PROFILE_STORE_LABEL */
+ G_TYPE_FILE, /* GIMP_COLOR_PROFILE_STORE_FILE */
+ G_TYPE_INT /* GIMP_COLOR_PROFILE_STORE_INDEX */
+ };
+
+ gtk_list_store_set_column_types (GTK_LIST_STORE (store),
+ G_N_ELEMENTS (types), types);
+}
+
+static void
+gimp_color_profile_store_constructed (GObject *object)
+{
+ GimpColorProfileStore *store = GIMP_COLOR_PROFILE_STORE (object);
+ GtkTreeIter iter;
+
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ gtk_list_store_append (GTK_LIST_STORE (store), &iter);
+ gtk_list_store_set (GTK_LIST_STORE (store), &iter,
+ GIMP_COLOR_PROFILE_STORE_ITEM_TYPE,
+ GIMP_COLOR_PROFILE_STORE_ITEM_DIALOG,
+ GIMP_COLOR_PROFILE_STORE_LABEL,
+ _("Select color profile from disk..."),
+ -1);
+
+ if (store->history)
+ {
+ gimp_color_profile_store_load (store, store->history, NULL);
+ }
+}
+
+static void
+gimp_color_profile_store_dispose (GObject *object)
+{
+ GimpColorProfileStore *store = GIMP_COLOR_PROFILE_STORE (object);
+
+ if (store->history)
+ {
+ gimp_color_profile_store_save (store, store->history, NULL);
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gimp_color_profile_store_finalize (GObject *object)
+{
+ GimpColorProfileStore *store = GIMP_COLOR_PROFILE_STORE (object);
+
+ g_clear_pointer (&store->history, g_free);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_color_profile_store_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpColorProfileStore *store = GIMP_COLOR_PROFILE_STORE (object);
+
+ switch (property_id)
+ {
+ case PROP_HISTORY:
+ g_return_if_fail (store->history == NULL);
+ store->history = g_value_dup_string (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_color_profile_store_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpColorProfileStore *store = GIMP_COLOR_PROFILE_STORE (object);
+
+ switch (property_id)
+ {
+ case PROP_HISTORY:
+ g_value_set_string (value, store->history);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+
+/**
+ * gimp_color_profile_store_new:
+ * @history: filename of the profilerc (or %NULL for no history)
+ *
+ * Creates a new #GimpColorProfileStore object and populates it with
+ * last used profiles read from the file @history. The updated history
+ * is written back to disk when the store is disposed.
+ *
+ * The filename passed as @history is typically created using the
+ * following code snippet:
+ * <informalexample><programlisting>
+ * gchar *history = gimp_personal_rc_file ("profilerc");
+ * </programlisting></informalexample>
+ *
+ * Return value: a new #GimpColorProfileStore
+ *
+ * Since: 2.4
+ **/
+GtkListStore *
+gimp_color_profile_store_new (const gchar *history)
+{
+ return g_object_new (GIMP_TYPE_COLOR_PROFILE_STORE,
+ "history", history,
+ NULL);
+}
+
+/**
+ * gimp_color_profile_store_add:
+ * @store: a #GimpColorProfileStore
+ * @filename: filename of the profile to add (or %NULL)
+ * @label: label to use for the profile
+ * (may only be %NULL if @filename is %NULL)
+ *
+ * Adds a color profile item to the #GimpColorProfileStore. Items
+ * added with this function will be kept at the top, separated from
+ * the history of last used color profiles.
+ *
+ * This function is often used to add a selectable item for the %NULL
+ * filename. If you pass %NULL for both @filename and @label, the
+ * @label will be set to the string "None" for you (and translated for
+ * the user).
+ *
+ * Deprecated: use gimp_color_profile_store_add_file() instead.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_color_profile_store_add (GimpColorProfileStore *store,
+ const gchar *filename,
+ const gchar *label)
+{
+ GFile *file = NULL;
+
+ g_return_if_fail (GIMP_IS_COLOR_PROFILE_STORE (store));
+ g_return_if_fail (label != NULL || filename == NULL);
+
+ if (filename)
+ file = g_file_new_for_path (filename);
+
+ gimp_color_profile_store_add_file (store, file, label);
+
+ g_object_unref (file);
+}
+
+/**
+ * gimp_color_profile_store_add_file:
+ * @store: a #GimpColorProfileStore
+ * @file: file of the profile to add (or %NULL)
+ * @label: label to use for the profile
+ * (may only be %NULL if @filename is %NULL)
+ *
+ * Adds a color profile item to the #GimpColorProfileStore. Items
+ * added with this function will be kept at the top, separated from
+ * the history of last used color profiles.
+ *
+ * This function is often used to add a selectable item for the %NULL
+ * file. If you pass %NULL for both @file and @label, the @label will
+ * be set to the string "None" for you (and translated for the user).
+ *
+ * Since: 2.10
+ **/
+void
+gimp_color_profile_store_add_file (GimpColorProfileStore *store,
+ GFile *file,
+ const gchar *label)
+{
+ GtkTreeIter separator;
+ GtkTreeIter iter;
+
+ g_return_if_fail (GIMP_IS_COLOR_PROFILE_STORE (store));
+ g_return_if_fail (label != NULL || file == NULL);
+ g_return_if_fail (file == NULL || G_IS_FILE (file));
+
+ if (! file && ! label)
+ label = C_("profile", "None");
+
+ gimp_color_profile_store_get_separator (store, &separator, TRUE);
+
+ gtk_list_store_insert_before (GTK_LIST_STORE (store), &iter, &separator);
+ gtk_list_store_set (GTK_LIST_STORE (store), &iter,
+ GIMP_COLOR_PROFILE_STORE_ITEM_TYPE,
+ GIMP_COLOR_PROFILE_STORE_ITEM_FILE,
+ GIMP_COLOR_PROFILE_STORE_FILE, file,
+ GIMP_COLOR_PROFILE_STORE_LABEL, label,
+ GIMP_COLOR_PROFILE_STORE_INDEX, -1,
+ -1);
+}
+
+/**
+ * _gimp_color_profile_store_history_add:
+ * @store: a #GimpColorProfileStore
+ * @file: file of the profile to add (or %NULL)
+ * @label: label to use for the profile (or %NULL)
+ * @iter: a #GtkTreeIter
+ *
+ * Return value: %TRUE if the iter is valid and pointing to the item
+ *
+ * Since: 2.4
+ **/
+gboolean
+_gimp_color_profile_store_history_add (GimpColorProfileStore *store,
+ GFile *file,
+ const gchar *label,
+ GtkTreeIter *iter)
+{
+ GtkTreeModel *model;
+ gboolean iter_valid;
+ gint max = -1;
+
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE_STORE (store), FALSE);
+ g_return_val_if_fail (iter != NULL, FALSE);
+
+ model = GTK_TREE_MODEL (store);
+
+ for (iter_valid = gtk_tree_model_get_iter_first (model, iter);
+ iter_valid;
+ iter_valid = gtk_tree_model_iter_next (model, iter))
+ {
+ gint type;
+ gint index;
+ GFile *this;
+
+ gtk_tree_model_get (model, iter,
+ GIMP_COLOR_PROFILE_STORE_ITEM_TYPE, &type,
+ GIMP_COLOR_PROFILE_STORE_INDEX, &index,
+ -1);
+
+ if (type != GIMP_COLOR_PROFILE_STORE_ITEM_FILE)
+ continue;
+
+ if (index > max)
+ max = index;
+
+ /* check if we found a filename match */
+ gtk_tree_model_get (model, iter,
+ GIMP_COLOR_PROFILE_STORE_FILE, &this,
+ -1);
+
+ if ((this && file && g_file_equal (this, file)) ||
+ (! this && ! file))
+ {
+ /* update the label */
+ if (label && *label)
+ gtk_list_store_set (GTK_LIST_STORE (store), iter,
+ GIMP_COLOR_PROFILE_STORE_LABEL, label,
+ -1);
+
+ if (this)
+ g_object_unref (this);
+
+ return TRUE;
+ }
+
+ if (this)
+ g_object_unref (this);
+ }
+
+ if (! file)
+ return FALSE;
+
+ if (label && *label)
+ {
+ iter_valid = gimp_color_profile_store_history_insert (store, iter,
+ file, label,
+ ++max);
+ }
+ else
+ {
+ const gchar *utf8 = gimp_file_get_utf8_name (file);
+ gchar *basename = g_path_get_basename (utf8);
+
+ iter_valid = gimp_color_profile_store_history_insert (store, iter,
+ file, basename,
+ ++max);
+ g_free (basename);
+ }
+
+ return iter_valid;
+}
+
+/**
+ * _gimp_color_profile_store_history_reorder
+ * @store: a #GimpColorProfileStore
+ * @iter: a #GtkTreeIter
+ *
+ * Moves the entry pointed to by @iter to the front of the MRU list.
+ *
+ * Since: 2.4
+ **/
+void
+_gimp_color_profile_store_history_reorder (GimpColorProfileStore *store,
+ GtkTreeIter *iter)
+{
+ GtkTreeModel *model;
+ gint index;
+ gboolean iter_valid;
+
+ g_return_if_fail (GIMP_IS_COLOR_PROFILE_STORE (store));
+ g_return_if_fail (iter != NULL);
+
+ model = GTK_TREE_MODEL (store);
+
+ gtk_tree_model_get (model, iter,
+ GIMP_COLOR_PROFILE_STORE_INDEX, &index,
+ -1);
+
+ if (index == 0)
+ return; /* already at the top */
+
+ for (iter_valid = gtk_tree_model_get_iter_first (model, iter);
+ iter_valid;
+ iter_valid = gtk_tree_model_iter_next (model, iter))
+ {
+ gint type;
+ gint this_index;
+
+ gtk_tree_model_get (model, iter,
+ GIMP_COLOR_PROFILE_STORE_ITEM_TYPE, &type,
+ GIMP_COLOR_PROFILE_STORE_INDEX, &this_index,
+ -1);
+
+ if (type == GIMP_COLOR_PROFILE_STORE_ITEM_FILE && this_index > -1)
+ {
+ if (this_index < index)
+ {
+ this_index++;
+ }
+ else if (this_index == index)
+ {
+ this_index = 0;
+ }
+
+ gtk_list_store_set (GTK_LIST_STORE (store), iter,
+ GIMP_COLOR_PROFILE_STORE_INDEX, this_index,
+ -1);
+ }
+ }
+}
+
+static gboolean
+gimp_color_profile_store_history_insert (GimpColorProfileStore *store,
+ GtkTreeIter *iter,
+ GFile *file,
+ const gchar *label,
+ gint index)
+{
+ GtkTreeModel *model = GTK_TREE_MODEL (store);
+ GtkTreeIter sibling;
+ gboolean iter_valid;
+
+ g_return_val_if_fail (G_IS_FILE (file), FALSE);
+ g_return_val_if_fail (label != NULL, FALSE);
+ g_return_val_if_fail (index > -1, FALSE);
+
+ gimp_color_profile_store_get_separator (store, iter, FALSE);
+
+ for (iter_valid = gtk_tree_model_get_iter_first (model, &sibling);
+ iter_valid;
+ iter_valid = gtk_tree_model_iter_next (model, &sibling))
+ {
+ gint type;
+ gint this_index;
+
+ gtk_tree_model_get (model, &sibling,
+ GIMP_COLOR_PROFILE_STORE_ITEM_TYPE, &type,
+ GIMP_COLOR_PROFILE_STORE_INDEX, &this_index,
+ -1);
+
+ if (type == GIMP_COLOR_PROFILE_STORE_ITEM_SEPARATOR_BOTTOM)
+ {
+ gtk_list_store_insert_before (GTK_LIST_STORE (store),
+ iter, &sibling);
+ break;
+ }
+
+ if (type == GIMP_COLOR_PROFILE_STORE_ITEM_FILE && this_index > -1)
+ {
+ gchar *this_label;
+
+ gtk_tree_model_get (model, &sibling,
+ GIMP_COLOR_PROFILE_STORE_LABEL, &this_label,
+ -1);
+
+ if (this_label && g_utf8_collate (label, this_label) < 0)
+ {
+ gtk_list_store_insert_before (GTK_LIST_STORE (store),
+ iter, &sibling);
+ g_free (this_label);
+ break;
+ }
+
+ g_free (this_label);
+ }
+ }
+
+ if (iter_valid)
+ gtk_list_store_set (GTK_LIST_STORE (store), iter,
+ GIMP_COLOR_PROFILE_STORE_ITEM_TYPE,
+ GIMP_COLOR_PROFILE_STORE_ITEM_FILE,
+ GIMP_COLOR_PROFILE_STORE_FILE, file,
+ GIMP_COLOR_PROFILE_STORE_LABEL, label,
+ GIMP_COLOR_PROFILE_STORE_INDEX, index,
+ -1);
+
+ return iter_valid;
+}
+
+static void
+gimp_color_profile_store_create_separator (GimpColorProfileStore *store,
+ GtkTreeIter *iter,
+ gboolean top)
+{
+ if (top)
+ {
+ gtk_list_store_prepend (GTK_LIST_STORE (store), iter);
+ }
+ else
+ {
+ GtkTreeModel *model = GTK_TREE_MODEL (store);
+ GtkTreeIter sibling;
+ gboolean iter_valid;
+
+ for (iter_valid = gtk_tree_model_get_iter_first (model, &sibling);
+ iter_valid;
+ iter_valid = gtk_tree_model_iter_next (model, &sibling))
+ {
+ gint type;
+
+ gtk_tree_model_get (model, &sibling,
+ GIMP_COLOR_PROFILE_STORE_ITEM_TYPE, &type,
+ -1);
+
+ if (type == GIMP_COLOR_PROFILE_STORE_ITEM_DIALOG)
+ break;
+ }
+
+ if (iter_valid)
+ gtk_list_store_insert_before (GTK_LIST_STORE (store), iter, &sibling);
+ }
+
+ gtk_list_store_set (GTK_LIST_STORE (store), iter,
+ GIMP_COLOR_PROFILE_STORE_ITEM_TYPE,
+ top ?
+ GIMP_COLOR_PROFILE_STORE_ITEM_SEPARATOR_TOP :
+ GIMP_COLOR_PROFILE_STORE_ITEM_SEPARATOR_BOTTOM,
+ GIMP_COLOR_PROFILE_STORE_INDEX, -1,
+ -1);
+}
+
+static void
+gimp_color_profile_store_get_separator (GimpColorProfileStore *store,
+ GtkTreeIter *iter,
+ gboolean top)
+{
+ GtkTreeModel *model = GTK_TREE_MODEL (store);
+ gboolean iter_valid;
+ gint needle;
+
+ needle = (top ?
+ GIMP_COLOR_PROFILE_STORE_ITEM_SEPARATOR_TOP :
+ GIMP_COLOR_PROFILE_STORE_ITEM_SEPARATOR_BOTTOM);
+
+ for (iter_valid = gtk_tree_model_get_iter_first (model, iter);
+ iter_valid;
+ iter_valid = gtk_tree_model_iter_next (model, iter))
+ {
+ gint type;
+
+ gtk_tree_model_get (model, iter,
+ GIMP_COLOR_PROFILE_STORE_ITEM_TYPE, &type,
+ -1);
+
+ if (type == needle)
+ return;
+ }
+
+ gimp_color_profile_store_create_separator (store, iter, top);
+}
+
+static GTokenType
+gimp_color_profile_store_load_profile (GimpColorProfileStore *store,
+ GScanner *scanner,
+ gint index)
+{
+ GtkTreeIter iter;
+ gchar *label = NULL;
+ gchar *path = NULL;
+
+ if (gimp_scanner_parse_string (scanner, &label) &&
+ gimp_scanner_parse_string (scanner, &path))
+ {
+ GFile *file = NULL;
+
+ if (g_str_has_prefix (path, "file://"))
+ {
+ file = g_file_new_for_uri (path);
+ }
+ else
+ {
+ file = gimp_file_new_for_config_path (path, NULL);
+ }
+
+ if (file)
+ {
+ if (g_file_query_file_type (file, 0, NULL) == G_FILE_TYPE_REGULAR)
+ {
+ gimp_color_profile_store_history_insert (store, &iter,
+ file, label, index);
+ }
+
+ g_object_unref (file);
+ }
+
+ g_free (label);
+ g_free (path);
+
+ return G_TOKEN_RIGHT_PAREN;
+ }
+
+ g_free (label);
+ g_free (path);
+
+ return G_TOKEN_STRING;
+}
+
+static gboolean
+gimp_color_profile_store_load (GimpColorProfileStore *store,
+ const gchar *filename,
+ GError **error)
+{
+ GScanner *scanner;
+ GTokenType token;
+ gint i = 0;
+
+ scanner = gimp_scanner_new_file (filename, error);
+ if (! scanner)
+ return FALSE;
+
+ g_scanner_scope_add_symbol (scanner, 0, "color-profile", NULL);
+
+ token = G_TOKEN_LEFT_PAREN;
+
+ while (g_scanner_peek_next_token (scanner) == token)
+ {
+ token = g_scanner_get_next_token (scanner);
+
+ switch (token)
+ {
+ case G_TOKEN_LEFT_PAREN:
+ token = G_TOKEN_SYMBOL;
+ break;
+
+ case G_TOKEN_SYMBOL:
+ token = gimp_color_profile_store_load_profile (store, scanner, i++);
+ break;
+
+ case G_TOKEN_RIGHT_PAREN:
+ token = G_TOKEN_LEFT_PAREN;
+ break;
+
+ default: /* do nothing */
+ break;
+ }
+ }
+
+ if (token != G_TOKEN_LEFT_PAREN)
+ {
+ g_scanner_get_next_token (scanner);
+ g_scanner_unexp_token (scanner, token, NULL, NULL, NULL,
+ _("fatal parse error"), TRUE);
+ }
+
+ gimp_scanner_destroy (scanner);
+
+ return TRUE;
+}
+
+static gboolean
+gimp_color_profile_store_save (GimpColorProfileStore *store,
+ const gchar *filename,
+ GError **error)
+{
+ GimpConfigWriter *writer;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gchar *labels[HISTORY_SIZE] = { NULL, };
+ GFile *files[HISTORY_SIZE] = { NULL, };
+ gboolean iter_valid;
+ gint i;
+
+ writer = gimp_config_writer_new_file (filename,
+ TRUE,
+ "GIMP color profile history",
+ error);
+ if (! writer)
+ return FALSE;
+
+ model = GTK_TREE_MODEL (store);
+
+ for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
+ iter_valid;
+ iter_valid = gtk_tree_model_iter_next (model, &iter))
+ {
+ gint type;
+ gint index;
+
+ gtk_tree_model_get (model, &iter,
+ GIMP_COLOR_PROFILE_STORE_ITEM_TYPE, &type,
+ GIMP_COLOR_PROFILE_STORE_INDEX, &index,
+ -1);
+
+ if (type == GIMP_COLOR_PROFILE_STORE_ITEM_FILE &&
+ index >= 0 &&
+ index < HISTORY_SIZE)
+ {
+ if (labels[index] || files[index])
+ g_warning ("%s: double index %d", G_STRFUNC, index);
+
+ gtk_tree_model_get (model, &iter,
+ GIMP_COLOR_PROFILE_STORE_LABEL,
+ &labels[index],
+ GIMP_COLOR_PROFILE_STORE_FILE,
+ &files[index],
+ -1);
+ }
+ }
+
+
+ for (i = 0; i < HISTORY_SIZE; i++)
+ {
+ if (files[i] && labels[i])
+ {
+ gchar *path = gimp_file_get_config_path (files[i], NULL);
+
+ if (path)
+ {
+ gimp_config_writer_open (writer, "color-profile");
+ gimp_config_writer_string (writer, labels[i]);
+ gimp_config_writer_string (writer, path);
+ gimp_config_writer_close (writer);
+
+ g_free (path);
+ }
+ }
+
+ if (files[i])
+ g_object_unref (files[i]);
+
+ g_free (labels[i]);
+ }
+
+ return gimp_config_writer_finish (writer,
+ "end of color profile history", error);
+}
diff --git a/libgimpwidgets/gimpcolorprofilestore.h b/libgimpwidgets/gimpcolorprofilestore.h
new file mode 100644
index 0000000..b1f8108
--- /dev/null
+++ b/libgimpwidgets/gimpcolorprofilestore.h
@@ -0,0 +1,76 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpprofilestore.h
+ * Copyright (C) 2007 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_COLOR_PROFILE_STORE_H__
+#define __GIMP_COLOR_PROFILE_STORE_H__
+
+G_BEGIN_DECLS
+
+
+#define GIMP_TYPE_COLOR_PROFILE_STORE (gimp_color_profile_store_get_type ())
+#define GIMP_COLOR_PROFILE_STORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_COLOR_PROFILE_STORE, GimpColorProfileStore))
+#define GIMP_COLOR_PROFILE_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_COLOR_PROFILE_STORE, GimpColorProfileStoreClass))
+#define GIMP_IS_COLOR_PROFILE_STORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_COLOR_PROFILE_STORE))
+#define GIMP_IS_COLOR_PROFILE_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_COLOR_PROFILE_STORE))
+#define GIMP_COLOR_PROFILE_STORE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_COLOR_PROFILE_STORE, GimpColorProfileStoreClass))
+
+
+typedef struct _GimpColorProfileStoreClass GimpColorProfileStoreClass;
+
+struct _GimpColorProfileStore
+{
+ GtkListStore parent_instance;
+
+ gchar *history;
+};
+
+struct _GimpColorProfileStoreClass
+{
+ GtkListStoreClass parent_class;
+
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_color_profile_store_get_type (void) G_GNUC_CONST;
+
+GtkListStore * gimp_color_profile_store_new (const gchar *history);
+
+GIMP_DEPRECATED_FOR(gimp_color_profile_store_add_file)
+void gimp_color_profile_store_add (GimpColorProfileStore *store,
+ const gchar *filename,
+ const gchar *label);
+
+void gimp_color_profile_store_add_file (GimpColorProfileStore *store,
+ GFile *file,
+ const gchar *label);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_COLOR_PROFILE_STORE_H__ */
diff --git a/libgimpwidgets/gimpcolorprofileview.c b/libgimpwidgets/gimpcolorprofileview.c
new file mode 100644
index 0000000..558392a
--- /dev/null
+++ b/libgimpwidgets/gimpcolorprofileview.c
@@ -0,0 +1,209 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpColorProfileView
+ * Copyright (C) 2014 Michael Natterer <mitch@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (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
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpcolor/gimpcolor.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpcolorprofileview.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpcolorprofileview
+ * @title: GimpColorProfileView
+ * @short_description: A widget for viewing color profile properties
+ *
+ * A widget for viewing the properties of a #GimpColorProfile.
+ **/
+
+
+struct _GimpColorProfileViewPrivate
+{
+ GimpColorProfile *profile;
+};
+
+
+static void gimp_color_profile_view_constructed (GObject *object);
+static void gimp_color_profile_view_finalize (GObject *object);
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpColorProfileView, gimp_color_profile_view,
+ GTK_TYPE_TEXT_VIEW)
+
+#define parent_class gimp_color_profile_view_parent_class
+
+
+static void
+gimp_color_profile_view_class_init (GimpColorProfileViewClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = gimp_color_profile_view_constructed;
+ object_class->finalize = gimp_color_profile_view_finalize;
+}
+
+static void
+gimp_color_profile_view_init (GimpColorProfileView *view)
+{
+ view->priv = gimp_color_profile_view_get_instance_private (view);
+}
+
+static void
+gimp_color_profile_view_constructed (GObject *object)
+{
+ GtkTextBuffer *buffer;
+
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (object));
+
+ gtk_text_buffer_create_tag (buffer, "text",
+ NULL);
+ gtk_text_buffer_create_tag (buffer, "title",
+ "weight", PANGO_WEIGHT_BOLD,
+ "scale", PANGO_SCALE_LARGE,
+ NULL);
+ gtk_text_buffer_create_tag (buffer, "header",
+ "weight", PANGO_WEIGHT_BOLD,
+ NULL);
+ gtk_text_buffer_create_tag (buffer, "error",
+ "style", PANGO_STYLE_OBLIQUE,
+ NULL);
+
+ gtk_text_view_set_editable (GTK_TEXT_VIEW (object), FALSE);
+ gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (object), GTK_WRAP_WORD);
+
+ gtk_text_view_set_pixels_above_lines (GTK_TEXT_VIEW (object), 6);
+ gtk_text_view_set_left_margin (GTK_TEXT_VIEW (object), 6);
+ gtk_text_view_set_right_margin (GTK_TEXT_VIEW (object), 6);
+}
+
+static void
+gimp_color_profile_view_finalize (GObject *object)
+{
+ GimpColorProfileView *view = GIMP_COLOR_PROFILE_VIEW (object);
+
+ g_clear_object (&view->priv->profile);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+GtkWidget *
+gimp_color_profile_view_new (void)
+{
+ return g_object_new (GIMP_TYPE_COLOR_PROFILE_VIEW, NULL);
+}
+
+void
+gimp_color_profile_view_set_profile (GimpColorProfileView *view,
+ GimpColorProfile *profile)
+{
+ GtkTextBuffer *buffer;
+
+ g_return_if_fail (GIMP_IS_COLOR_PROFILE_VIEW (view));
+ g_return_if_fail (profile == NULL || GIMP_IS_COLOR_PROFILE (profile));
+
+ if (profile == view->priv->profile)
+ return;
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+
+ gtk_text_buffer_set_text (buffer, "", 0);
+
+ if (g_set_object (&view->priv->profile, profile) && profile)
+ {
+ GtkTextIter iter;
+ const gchar *text;
+
+ gtk_text_buffer_get_start_iter (buffer, &iter);
+
+ text = gimp_color_profile_get_label (profile);
+ if (text && strlen (text))
+ {
+ gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
+ text, -1,
+ "title", NULL);
+ gtk_text_buffer_insert (buffer, &iter, "\n", 1);
+ }
+
+ text = gimp_color_profile_get_model (profile);
+ if (text && strlen (text))
+ {
+ gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
+ text, -1,
+ "text", NULL);
+ gtk_text_buffer_insert (buffer, &iter, "\n", 1);
+ }
+
+ text = gimp_color_profile_get_manufacturer (profile);
+ if (text && strlen (text))
+ {
+ gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
+ _("Manufacturer: "), -1,
+ "header", NULL);
+ gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
+ text, -1,
+ "text", NULL);
+ gtk_text_buffer_insert (buffer, &iter, "\n", 1);
+ }
+
+ text = gimp_color_profile_get_copyright (profile);
+ if (text && strlen (text))
+ {
+ gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
+ _("Copyright: "), -1,
+ "header", NULL);
+ gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
+ text, -1,
+ "text", NULL);
+ gtk_text_buffer_insert (buffer, &iter, "\n", 1);
+ }
+ }
+}
+
+void
+gimp_color_profile_view_set_error (GimpColorProfileView *view,
+ const gchar *message)
+{
+ GtkTextBuffer *buffer;
+ GtkTextIter iter;
+
+ g_return_if_fail (GIMP_IS_COLOR_PROFILE_VIEW (view));
+ g_return_if_fail (message != NULL);
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+
+ gtk_text_buffer_set_text (buffer, "", 0);
+
+ gtk_text_buffer_get_start_iter (buffer, &iter);
+
+ gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
+ message, -1,
+ "error", NULL);
+}
diff --git a/libgimpwidgets/gimpcolorprofileview.h b/libgimpwidgets/gimpcolorprofileview.h
new file mode 100644
index 0000000..4843a69
--- /dev/null
+++ b/libgimpwidgets/gimpcolorprofileview.h
@@ -0,0 +1,68 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpColorProfileView
+ * Copyright (C) 2014 Michael Natterer <mitch@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (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
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_COLOR_PROFILE_VIEW_H__
+#define __GIMP_COLOR_PROFILE_VIEW_H__
+
+G_BEGIN_DECLS
+
+
+#define GIMP_TYPE_COLOR_PROFILE_VIEW (gimp_color_profile_view_get_type ())
+#define GIMP_COLOR_PROFILE_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_COLOR_PROFILE_VIEW, GimpColorProfileView))
+#define GIMP_COLOR_PROFILE_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_COLOR_PROFILE_VIEW, GimpColorProfileViewClass))
+#define GIMP_IS_COLOR_PROFILE_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_COLOR_PROFILE_VIEW))
+#define GIMP_IS_COLOR_PROFILE_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_COLOR_PROFILE_VIEW))
+#define GIMP_COLOR_PROFILE_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_COLOR_PROFILE_VIEW, GimpColorProfileViewClass))
+
+
+typedef struct _GimpColorProfileViewClass GimpColorProfileViewClass;
+typedef struct _GimpColorProfileViewPrivate GimpColorProfileViewPrivate;
+
+struct _GimpColorProfileView
+{
+ GtkTextView parent_instance;
+
+ GimpColorProfileViewPrivate *priv;
+};
+
+struct _GimpColorProfileViewClass
+{
+ GtkTextViewClass parent_class;
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_color_profile_view_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_color_profile_view_new (void);
+
+void gimp_color_profile_view_set_profile (GimpColorProfileView *view,
+ GimpColorProfile *profile);
+void gimp_color_profile_view_set_error (GimpColorProfileView *view,
+ const gchar *message);
+
+G_END_DECLS
+
+#endif /* __GIMP_COLOR_PROFILE_VIEW_H__ */
diff --git a/libgimpwidgets/gimpcolorscale.c b/libgimpwidgets/gimpcolorscale.c
new file mode 100644
index 0000000..f87ab08
--- /dev/null
+++ b/libgimpwidgets/gimpcolorscale.c
@@ -0,0 +1,1133 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcolorscale.c
+ * Copyright (C) 2002-2010 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpconfig/gimpconfig.h"
+#include "libgimpcolor/gimpcolor.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpcairo-utils.h"
+#include "gimpcolorscale.h"
+#include "gimpwidgetsutils.h"
+
+
+/**
+ * SECTION: gimpcolorscale
+ * @title: GimpColorScale
+ * @short_description: Fancy colored sliders.
+ *
+ * Fancy colored sliders.
+ **/
+
+
+enum
+{
+ PROP_0,
+ PROP_CHANNEL
+};
+
+
+typedef struct _GimpLCH GimpLCH;
+
+struct _GimpLCH
+{
+ gdouble l, c, h, a;
+};
+
+
+typedef struct _GimpColorScalePrivate GimpColorScalePrivate;
+
+struct _GimpColorScalePrivate
+{
+ GimpColorConfig *config;
+ GimpColorTransform *transform;
+ guchar oog_color[3];
+};
+
+#define GET_PRIVATE(obj) \
+ ((GimpColorScalePrivate *) gimp_color_scale_get_instance_private ((GimpColorScale *) (obj)))
+
+
+static void gimp_color_scale_dispose (GObject *object);
+static void gimp_color_scale_finalize (GObject *object);
+static void gimp_color_scale_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_color_scale_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_color_scale_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static void gimp_color_scale_state_changed (GtkWidget *widget,
+ GtkStateType previous_state);
+static gboolean gimp_color_scale_button_press (GtkWidget *widget,
+ GdkEventButton *event);
+static gboolean gimp_color_scale_button_release (GtkWidget *widget,
+ GdkEventButton *event);
+static gboolean gimp_color_scale_scroll (GtkWidget *widget,
+ GdkEventScroll *event);
+static gboolean gimp_color_scale_expose (GtkWidget *widget,
+ GdkEventExpose *event);
+
+static void gimp_color_scale_render (GimpColorScale *scale);
+static void gimp_color_scale_render_alpha (GimpColorScale *scale);
+static void gimp_color_scale_render_stipple (GimpColorScale *scale);
+
+static void gimp_color_scale_create_transform (GimpColorScale *scale);
+static void gimp_color_scale_destroy_transform (GimpColorScale *scale);
+static void gimp_color_scale_notify_config (GimpColorConfig *config,
+ const GParamSpec *pspec,
+ GimpColorScale *scale);
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpColorScale, gimp_color_scale, GTK_TYPE_SCALE)
+
+#define parent_class gimp_color_scale_parent_class
+
+static const Babl *fish_rgb_to_lch = NULL;
+static const Babl *fish_lch_to_rgb = NULL;
+
+
+static void
+gimp_color_scale_class_init (GimpColorScaleClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->dispose = gimp_color_scale_dispose;
+ object_class->finalize = gimp_color_scale_finalize;
+ object_class->get_property = gimp_color_scale_get_property;
+ object_class->set_property = gimp_color_scale_set_property;
+
+ widget_class->size_allocate = gimp_color_scale_size_allocate;
+ widget_class->state_changed = gimp_color_scale_state_changed;
+ widget_class->button_press_event = gimp_color_scale_button_press;
+ widget_class->button_release_event = gimp_color_scale_button_release;
+ widget_class->scroll_event = gimp_color_scale_scroll;
+ widget_class->expose_event = gimp_color_scale_expose;
+
+ /**
+ * GimpColorScale:channel:
+ *
+ * The channel which is edited by the color scale.
+ *
+ * Since: 2.8
+ */
+ g_object_class_install_property (object_class, PROP_CHANNEL,
+ g_param_spec_enum ("channel",
+ "Channel",
+ "The channel which is edited by the color scale",
+ GIMP_TYPE_COLOR_SELECTOR_CHANNEL,
+ GIMP_COLOR_SELECTOR_VALUE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ fish_rgb_to_lch = babl_fish (babl_format ("R'G'B'A double"),
+ babl_format ("CIE LCH(ab) double"));
+ fish_lch_to_rgb = babl_fish (babl_format ("CIE LCH(ab) double"),
+ babl_format ("R'G'B' double"));
+}
+
+static void
+gimp_color_scale_dispose (GObject *object)
+{
+ GimpColorScale *scale = GIMP_COLOR_SCALE (object);
+
+ gimp_color_scale_set_color_config (scale, NULL);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gimp_color_scale_init (GimpColorScale *scale)
+{
+ GtkRange *range = GTK_RANGE (scale);
+
+ gtk_range_set_slider_size_fixed (range, TRUE);
+ gtk_range_set_flippable (GTK_RANGE (scale), TRUE);
+
+ gtk_scale_set_draw_value (GTK_SCALE (scale), FALSE);
+
+ scale->channel = GIMP_COLOR_SELECTOR_VALUE;
+ scale->needs_render = TRUE;
+
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (range),
+ GTK_ORIENTATION_HORIZONTAL);
+
+ gimp_rgba_set (&scale->rgb, 0.0, 0.0, 0.0, 1.0);
+ gimp_rgb_to_hsv (&scale->rgb, &scale->hsv);
+
+ gimp_widget_track_monitor (GTK_WIDGET (scale),
+ G_CALLBACK (gimp_color_scale_destroy_transform),
+ NULL);
+}
+
+static void
+gimp_color_scale_finalize (GObject *object)
+{
+ GimpColorScale *scale = GIMP_COLOR_SCALE (object);
+
+ g_clear_pointer (&scale->buf, g_free);
+ scale->width = 0;
+ scale->height = 0;
+ scale->rowstride = 0;
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_color_scale_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpColorScale *scale = GIMP_COLOR_SCALE (object);
+
+ switch (property_id)
+ {
+ case PROP_CHANNEL:
+ g_value_set_enum (value, scale->channel);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_color_scale_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpColorScale *scale = GIMP_COLOR_SCALE (object);
+
+ switch (property_id)
+ {
+ case PROP_CHANNEL:
+ gimp_color_scale_set_channel (scale, g_value_get_enum (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_color_scale_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GimpColorScale *scale = GIMP_COLOR_SCALE (widget);
+ GtkRange *range = GTK_RANGE (widget);
+ GdkRectangle range_rect;
+ gint focus = 0;
+ gint trough_border;
+ gint scale_width;
+ gint scale_height;
+
+ gtk_widget_style_get (widget,
+ "trough-border", &trough_border,
+ NULL);
+
+ if (gtk_widget_get_can_focus (widget))
+ {
+ gint focus_padding = 0;
+
+ gtk_widget_style_get (widget,
+ "focus-line-width", &focus,
+ "focus-padding", &focus_padding,
+ NULL);
+ focus += focus_padding;
+ }
+
+ gtk_range_set_min_slider_size (range,
+ (MIN (allocation->width,
+ allocation->height) - 2 * focus) / 2);
+
+ if (GTK_WIDGET_CLASS (parent_class)->size_allocate)
+ GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
+
+ gtk_range_get_range_rect (range, &range_rect);
+
+ scale_width = range_rect.width - 2 * (focus + trough_border);
+ scale_height = range_rect.height - 2 * (focus + trough_border);
+
+ switch (gtk_orientable_get_orientation (GTK_ORIENTABLE (range)))
+ {
+ case GTK_ORIENTATION_HORIZONTAL:
+ scale_width -= gtk_range_get_min_slider_size (range) - 1;
+ scale_height -= 2;
+ break;
+
+ case GTK_ORIENTATION_VERTICAL:
+ scale_width -= 2;
+ scale_height -= gtk_range_get_min_slider_size (range) - 1;
+ break;
+ }
+
+ if (scale_width != scale->width || scale_height != scale->height)
+ {
+ scale->width = scale_width;
+ scale->height = scale_height;
+
+ scale->rowstride = scale->width * 4;
+
+ g_free (scale->buf);
+ scale->buf = g_new (guchar, scale->rowstride * scale->height);
+
+ scale->needs_render = TRUE;
+ }
+}
+
+static void
+gimp_color_scale_state_changed (GtkWidget *widget,
+ GtkStateType previous_state)
+{
+ if (gtk_widget_get_state (widget) == GTK_STATE_INSENSITIVE ||
+ previous_state == GTK_STATE_INSENSITIVE)
+ {
+ GIMP_COLOR_SCALE (widget)->needs_render = TRUE;
+ }
+
+ if (GTK_WIDGET_CLASS (parent_class)->state_changed)
+ GTK_WIDGET_CLASS (parent_class)->state_changed (widget, previous_state);
+}
+
+static gboolean
+gimp_color_scale_button_press (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ if (event->button == 1)
+ {
+ GdkEventButton *my_event;
+ gboolean retval;
+
+ my_event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
+ my_event->button = 2;
+
+ retval = GTK_WIDGET_CLASS (parent_class)->button_press_event (widget,
+ my_event);
+
+ gdk_event_free ((GdkEvent *) my_event);
+
+ return retval;
+ }
+
+ return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
+}
+
+static gboolean
+gimp_color_scale_button_release (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ if (event->button == 1)
+ {
+ GdkEventButton *my_event;
+ gboolean retval;
+
+ my_event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
+ my_event->button = 2;
+
+ retval = GTK_WIDGET_CLASS (parent_class)->button_release_event (widget,
+ my_event);
+
+ gdk_event_free ((GdkEvent *) my_event);
+
+ return retval;
+ }
+
+ return GTK_WIDGET_CLASS (parent_class)->button_release_event (widget, event);
+}
+
+static gboolean
+gimp_color_scale_scroll (GtkWidget *widget,
+ GdkEventScroll *event)
+{
+ if (gtk_orientable_get_orientation (GTK_ORIENTABLE (widget)) ==
+ GTK_ORIENTATION_HORIZONTAL)
+ {
+ GdkEventScroll *my_event;
+ gboolean retval;
+
+ my_event = (GdkEventScroll *) gdk_event_copy ((GdkEvent *) event);
+
+ switch (my_event->direction)
+ {
+ case GDK_SCROLL_UP:
+ my_event->direction = GDK_SCROLL_RIGHT;
+ break;
+
+ case GDK_SCROLL_DOWN:
+ my_event->direction = GDK_SCROLL_LEFT;
+ break;
+
+ default:
+ break;
+ }
+
+ retval = GTK_WIDGET_CLASS (parent_class)->scroll_event (widget, my_event);
+
+ gdk_event_free ((GdkEvent *) my_event);
+
+ return retval;
+ }
+
+ return GTK_WIDGET_CLASS (parent_class)->scroll_event (widget, event);
+}
+
+static gboolean
+gimp_color_scale_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ GimpColorScale *scale = GIMP_COLOR_SCALE (widget);
+ GimpColorScalePrivate *priv = GET_PRIVATE (widget);
+ GtkRange *range = GTK_RANGE (widget);
+ GtkStyle *style = gtk_widget_get_style (widget);
+ GdkWindow *window = gtk_widget_get_window (widget);
+ gboolean sensitive = gtk_widget_is_sensitive (widget);
+ GtkAllocation allocation;
+ GdkRectangle range_rect;
+ GdkRectangle area = { 0, };
+ cairo_surface_t *buffer;
+ gint focus = 0;
+ gint trough_border;
+ gint slider_start;
+ gint slider_size;
+ gint x, y;
+ gint w, h;
+ cairo_t *cr;
+
+ if (! scale->buf || ! gtk_widget_is_drawable (widget))
+ return FALSE;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ cr = gdk_cairo_create (window);
+ gdk_cairo_region (cr, event->region);
+ cairo_translate (cr, allocation.x, allocation.y);
+ cairo_clip (cr);
+
+ gtk_widget_style_get (widget,
+ "trough-border", &trough_border,
+ NULL);
+
+ if (gtk_widget_get_can_focus (widget))
+ {
+ gint focus_padding = 0;
+
+ gtk_widget_style_get (widget,
+ "focus-line-width", &focus,
+ "focus-padding", &focus_padding,
+ NULL);
+ focus += focus_padding;
+ }
+
+ gtk_range_get_range_rect (range, &range_rect);
+ gtk_range_get_slider_range (range, &slider_start, NULL);
+
+ x = range_rect.x + focus;
+ y = range_rect.y + focus;
+ w = range_rect.width - 2 * focus;
+ h = range_rect.height - 2 * focus;
+
+ slider_size = gtk_range_get_min_slider_size (range) / 2;
+
+ if (scale->needs_render)
+ {
+ gimp_color_scale_render (scale);
+
+ if (! sensitive)
+ gimp_color_scale_render_stipple (scale);
+
+ scale->needs_render = FALSE;
+ }
+
+ gtk_paint_box (style, window,
+ sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
+ GTK_SHADOW_IN,
+ &event->area, widget, "trough",
+ x + allocation.x,
+ y + allocation.y,
+ w, h);
+
+ if (! priv->transform)
+ gimp_color_scale_create_transform (scale);
+
+ if (priv->transform)
+ {
+ const Babl *format = babl_format ("cairo-RGB24");
+ guchar *buf = g_new (guchar, scale->rowstride * scale->height);
+ guchar *src = scale->buf;
+ guchar *dest = buf;
+ gint i;
+
+ for (i = 0; i < scale->height; i++)
+ {
+ gimp_color_transform_process_pixels (priv->transform,
+ format, src,
+ format, dest,
+ scale->width);
+
+ src += scale->rowstride;
+ dest += scale->rowstride;
+ }
+
+ buffer = cairo_image_surface_create_for_data (buf,
+ CAIRO_FORMAT_RGB24,
+ scale->width,
+ scale->height,
+ scale->rowstride);
+ cairo_surface_set_user_data (buffer, NULL,
+ buf, (cairo_destroy_func_t) g_free);
+ }
+ else
+ {
+ buffer = cairo_image_surface_create_for_data (scale->buf,
+ CAIRO_FORMAT_RGB24,
+ scale->width,
+ scale->height,
+ scale->rowstride);
+ }
+
+ switch (gtk_orientable_get_orientation (GTK_ORIENTABLE (range)))
+ {
+ case GTK_ORIENTATION_HORIZONTAL:
+ cairo_set_source_surface (cr, buffer,
+ x + trough_border + slider_size,
+ y + trough_border + 1);
+ break;
+
+ case GTK_ORIENTATION_VERTICAL:
+ cairo_set_source_surface (cr, buffer,
+ x + trough_border + 1,
+ y + trough_border + slider_size);
+ break;
+ }
+
+ cairo_surface_destroy (buffer);
+ cairo_paint (cr);
+
+ if (gtk_widget_has_focus (widget))
+ gtk_paint_focus (style, window, gtk_widget_get_state (widget),
+ &event->area, widget, "trough",
+ range_rect.x + allocation.x,
+ range_rect.y + allocation.y,
+ range_rect.width,
+ range_rect.height);
+
+ switch (gtk_orientable_get_orientation (GTK_ORIENTABLE (range)))
+ {
+ case GTK_ORIENTATION_HORIZONTAL:
+ area.x = slider_start;
+ area.y = y + trough_border;
+ area.width = 2 * slider_size + 1;
+ area.height = h - 2 * trough_border;
+ break;
+
+ case GTK_ORIENTATION_VERTICAL:
+ area.x = x + trough_border;
+ area.y = slider_start;
+ area.width = w - 2 * trough_border;
+ area.height = 2 * slider_size + 1;
+ break;
+ }
+
+ if (gtk_widget_is_sensitive (widget))
+ gdk_cairo_set_source_color (cr, &style->black);
+ else
+ gdk_cairo_set_source_color (cr, &style->dark[GTK_STATE_INSENSITIVE]);
+
+ switch (gtk_orientable_get_orientation (GTK_ORIENTABLE (range)))
+ {
+ case GTK_ORIENTATION_HORIZONTAL:
+ cairo_move_to (cr, area.x, area.y);
+ cairo_line_to (cr, area.x + area.width, area.y);
+ cairo_line_to (cr,
+ area.x + area.width / 2 + 0.5,
+ area.y + area.width / 2);
+ break;
+
+ case GTK_ORIENTATION_VERTICAL:
+ cairo_move_to (cr, area.x, area.y);
+ cairo_line_to (cr, area.x, area.y + area.height);
+ cairo_line_to (cr,
+ area.x + area.height / 2,
+ area.y + area.height / 2 + 0.5);
+ break;
+ }
+
+ cairo_close_path (cr);
+ cairo_fill (cr);
+
+ if (gtk_widget_is_sensitive (widget))
+ gdk_cairo_set_source_color (cr, &style->white);
+ else
+ gdk_cairo_set_source_color (cr, &style->light[GTK_STATE_INSENSITIVE]);
+
+ switch (gtk_orientable_get_orientation (GTK_ORIENTABLE (range)))
+ {
+ case GTK_ORIENTATION_HORIZONTAL:
+ cairo_move_to (cr, area.x, area.y + area.height);
+ cairo_line_to (cr, area.x + area.width, area.y + area.height);
+ cairo_line_to (cr,
+ area.x + area.width / 2 + 0.5,
+ area.y + area.height - area.width / 2);
+ break;
+
+ case GTK_ORIENTATION_VERTICAL:
+ cairo_move_to (cr, area.x + area.width, area.y);
+ cairo_line_to (cr, area.x + area.width, area.y + area.height);
+ cairo_line_to (cr,
+ area.x + area.width - area.height / 2,
+ area.y + area.height / 2 + 0.5);
+ break;
+ }
+
+ cairo_close_path (cr);
+ cairo_fill (cr);
+
+ cairo_destroy (cr);
+
+ return FALSE;
+}
+
+/**
+ * gimp_color_scale_new:
+ * @orientation: the scale's orientation (horizontal or vertical)
+ * @channel: the scale's color channel
+ *
+ * Creates a new #GimpColorScale widget.
+ *
+ * Return value: a new #GimpColorScale widget
+ **/
+GtkWidget *
+gimp_color_scale_new (GtkOrientation orientation,
+ GimpColorSelectorChannel channel)
+{
+ GimpColorScale *scale = g_object_new (GIMP_TYPE_COLOR_SCALE,
+ "orientation", orientation,
+ "channel", channel,
+ NULL);
+
+ gtk_range_set_flippable (GTK_RANGE (scale),
+ orientation == GTK_ORIENTATION_HORIZONTAL);
+
+ return GTK_WIDGET (scale);
+}
+
+/**
+ * gimp_color_scale_set_channel:
+ * @scale: a #GimpColorScale widget
+ * @channel: the new color channel
+ *
+ * Changes the color channel displayed by the @scale.
+ **/
+void
+gimp_color_scale_set_channel (GimpColorScale *scale,
+ GimpColorSelectorChannel channel)
+{
+ g_return_if_fail (GIMP_IS_COLOR_SCALE (scale));
+
+ if (channel != scale->channel)
+ {
+ scale->channel = channel;
+
+ scale->needs_render = TRUE;
+ gtk_widget_queue_draw (GTK_WIDGET (scale));
+
+ g_object_notify (G_OBJECT (scale), "channel");
+ }
+}
+
+/**
+ * gimp_color_scale_set_color:
+ * @scale: a #GimpColorScale widget
+ * @rgb: the new color as #GimpRGB
+ * @hsv: the new color as #GimpHSV
+ *
+ * Changes the color value of the @scale.
+ **/
+void
+gimp_color_scale_set_color (GimpColorScale *scale,
+ const GimpRGB *rgb,
+ const GimpHSV *hsv)
+{
+ g_return_if_fail (GIMP_IS_COLOR_SCALE (scale));
+ g_return_if_fail (rgb != NULL);
+ g_return_if_fail (hsv != NULL);
+
+ scale->rgb = *rgb;
+ scale->hsv = *hsv;
+
+ scale->needs_render = TRUE;
+ gtk_widget_queue_draw (GTK_WIDGET (scale));
+}
+
+/**
+ * gimp_color_scale_set_color_config:
+ * @scale: a #GimpColorScale widget.
+ * @config: a #GimpColorConfig object.
+ *
+ * Sets the color management configuration to use with this color scale.
+ *
+ * Since: 2.10
+ */
+void
+gimp_color_scale_set_color_config (GimpColorScale *scale,
+ GimpColorConfig *config)
+{
+ GimpColorScalePrivate *priv;
+
+ g_return_if_fail (GIMP_IS_COLOR_SCALE (scale));
+ g_return_if_fail (config == NULL || GIMP_IS_COLOR_CONFIG (config));
+
+ priv = GET_PRIVATE (scale);
+
+ if (config != priv->config)
+ {
+ if (priv->config)
+ {
+ g_signal_handlers_disconnect_by_func (priv->config,
+ gimp_color_scale_notify_config,
+ scale);
+
+ gimp_color_scale_destroy_transform (scale);
+ }
+
+ g_set_object (&priv->config, config);
+
+ if (priv->config)
+ {
+ g_signal_connect (priv->config, "notify",
+ G_CALLBACK (gimp_color_scale_notify_config),
+ scale);
+
+ gimp_color_scale_notify_config (priv->config, NULL, scale);
+ }
+ }
+}
+
+
+/* as in gtkrange.c */
+static gboolean
+should_invert (GtkRange *range)
+{
+ gboolean inverted = gtk_range_get_inverted (range);
+ gboolean flippable = gtk_range_get_flippable (range);
+
+ if (gtk_orientable_get_orientation (GTK_ORIENTABLE (range)) ==
+ GTK_ORIENTATION_HORIZONTAL)
+ {
+ return
+ (inverted && !flippable) ||
+ (inverted && flippable &&
+ gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_LTR) ||
+ (!inverted && flippable &&
+ gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_RTL);
+ }
+ else
+ {
+ return inverted;
+ }
+}
+
+static void
+gimp_color_scale_render (GimpColorScale *scale)
+{
+ GimpColorScalePrivate *priv = GET_PRIVATE (scale);
+ GtkRange *range = GTK_RANGE (scale);
+ GimpRGB rgb;
+ GimpHSV hsv;
+ GimpLCH lch;
+ gint multiplier = 1;
+ guint x, y;
+ gdouble *channel_value = NULL; /* shut up compiler */
+ gboolean from_hsv = FALSE;
+ gboolean from_lch = FALSE;
+ gboolean invert;
+ guchar *buf;
+ guchar *d;
+
+ if ((buf = scale->buf) == NULL)
+ return;
+
+ if (scale->channel == GIMP_COLOR_SELECTOR_ALPHA)
+ {
+ gimp_color_scale_render_alpha (scale);
+ return;
+ }
+
+ rgb = scale->rgb;
+ hsv = scale->hsv;
+ babl_process (fish_rgb_to_lch, &rgb, &lch, 1);
+
+ switch (scale->channel)
+ {
+ case GIMP_COLOR_SELECTOR_HUE: channel_value = &hsv.h; break;
+ case GIMP_COLOR_SELECTOR_SATURATION: channel_value = &hsv.s; break;
+ case GIMP_COLOR_SELECTOR_VALUE: channel_value = &hsv.v; break;
+
+ case GIMP_COLOR_SELECTOR_RED: channel_value = &rgb.r; break;
+ case GIMP_COLOR_SELECTOR_GREEN: channel_value = &rgb.g; break;
+ case GIMP_COLOR_SELECTOR_BLUE: channel_value = &rgb.b; break;
+ case GIMP_COLOR_SELECTOR_ALPHA: channel_value = &rgb.a; break;
+
+ case GIMP_COLOR_SELECTOR_LCH_LIGHTNESS: channel_value = &lch.l; break;
+ case GIMP_COLOR_SELECTOR_LCH_CHROMA: channel_value = &lch.c; break;
+ case GIMP_COLOR_SELECTOR_LCH_HUE: channel_value = &lch.h; break;
+ }
+
+ switch (scale->channel)
+ {
+ case GIMP_COLOR_SELECTOR_HUE:
+ case GIMP_COLOR_SELECTOR_SATURATION:
+ case GIMP_COLOR_SELECTOR_VALUE:
+ from_hsv = TRUE;
+ break;
+
+ case GIMP_COLOR_SELECTOR_LCH_LIGHTNESS:
+ multiplier = 100;
+ from_lch = TRUE;
+ break;
+ case GIMP_COLOR_SELECTOR_LCH_CHROMA:
+ multiplier = 200;
+ from_lch = TRUE;
+ break;
+ case GIMP_COLOR_SELECTOR_LCH_HUE:
+ multiplier = 360;
+ from_lch = TRUE;
+ break;
+
+ default:
+ break;
+ }
+
+ invert = should_invert (range);
+
+ switch (gtk_orientable_get_orientation (GTK_ORIENTABLE (range)))
+ {
+ case GTK_ORIENTATION_HORIZONTAL:
+ for (x = 0, d = buf; x < scale->width; x++, d += 4)
+ {
+ gdouble value = (gdouble) x * multiplier / (gdouble) (scale->width - 1);
+ guchar r, g, b;
+
+ if (invert)
+ value = multiplier - value;
+
+ *channel_value = value;
+
+ if (from_hsv)
+ gimp_hsv_to_rgb (&hsv, &rgb);
+ else if (from_lch)
+ babl_process (fish_lch_to_rgb, &lch, &rgb, 1);
+
+ if (rgb.r < 0.0 || rgb.r > 1.0 ||
+ rgb.g < 0.0 || rgb.g > 1.0 ||
+ rgb.b < 0.0 || rgb.b > 1.0)
+ {
+ r = priv->oog_color[0];
+ g = priv->oog_color[1];
+ b = priv->oog_color[2];
+ }
+ else
+ {
+ gimp_rgb_get_uchar (&rgb, &r, &g, &b);
+ }
+
+ GIMP_CAIRO_RGB24_SET_PIXEL (d, r, g, b);
+ }
+
+ d = buf + scale->rowstride;
+ for (y = 1; y < scale->height; y++)
+ {
+ memcpy (d, buf, scale->rowstride);
+ d += scale->rowstride;
+ }
+ break;
+
+ case GTK_ORIENTATION_VERTICAL:
+ for (y = 0; y < scale->height; y++)
+ {
+ gdouble value = (gdouble) y * multiplier / (gdouble) (scale->height - 1);
+ guchar r, g, b;
+
+ if (invert)
+ value = multiplier - value;
+
+ *channel_value = value;
+
+ if (from_hsv)
+ gimp_hsv_to_rgb (&hsv, &rgb);
+ else if (from_lch)
+ babl_process (fish_lch_to_rgb, &lch, &rgb, 1);
+
+ if (rgb.r < 0.0 || rgb.r > 1.0 ||
+ rgb.g < 0.0 || rgb.g > 1.0 ||
+ rgb.b < 0.0 || rgb.b > 1.0)
+ {
+ r = priv->oog_color[0];
+ g = priv->oog_color[1];
+ b = priv->oog_color[2];
+ }
+ else
+ {
+ gimp_rgb_get_uchar (&rgb, &r, &g, &b);
+ }
+
+ for (x = 0, d = buf; x < scale->width; x++, d += 4)
+ {
+ GIMP_CAIRO_RGB24_SET_PIXEL (d, r, g, b);
+ }
+
+ buf += scale->rowstride;
+ }
+ break;
+ }
+}
+
+static void
+gimp_color_scale_render_alpha (GimpColorScale *scale)
+{
+ GtkRange *range = GTK_RANGE (scale);
+ GimpRGB rgb;
+ gboolean invert;
+ gdouble a;
+ guint x, y;
+ guchar *buf;
+ guchar *d, *l;
+
+ invert = should_invert (range);
+
+ buf = scale->buf;
+ rgb = scale->rgb;
+
+ switch (gtk_orientable_get_orientation (GTK_ORIENTABLE (range)))
+ {
+ case GTK_ORIENTATION_HORIZONTAL:
+ {
+ guchar *light;
+ guchar *dark;
+
+ light = buf;
+ /* this won't work correctly for very thin scales */
+ dark = (scale->height > GIMP_CHECK_SIZE_SM ?
+ buf + GIMP_CHECK_SIZE_SM * scale->rowstride : light);
+
+ for (x = 0, d = light, l = dark; x < scale->width; x++)
+ {
+ if ((x % GIMP_CHECK_SIZE_SM) == 0)
+ {
+ guchar *t;
+
+ t = d;
+ d = l;
+ l = t;
+ }
+
+ a = (gdouble) x / (gdouble) (scale->width - 1);
+
+ if (invert)
+ a = 1.0 - a;
+
+ GIMP_CAIRO_RGB24_SET_PIXEL (l,
+ (GIMP_CHECK_LIGHT +
+ (rgb.r - GIMP_CHECK_LIGHT) * a) * 255.999,
+ (GIMP_CHECK_LIGHT +
+ (rgb.g - GIMP_CHECK_LIGHT) * a) * 255.999,
+ (GIMP_CHECK_LIGHT +
+ (rgb.b - GIMP_CHECK_LIGHT) * a) * 255.999);
+ l += 4;
+
+ GIMP_CAIRO_RGB24_SET_PIXEL (d,
+ (GIMP_CHECK_DARK +
+ (rgb.r - GIMP_CHECK_DARK) * a) * 255.999,
+ (GIMP_CHECK_DARK +
+ (rgb.g - GIMP_CHECK_DARK) * a) * 255.999,
+ (GIMP_CHECK_DARK +
+ (rgb.b - GIMP_CHECK_DARK) * a) * 255.999);
+ d += 4;
+ }
+
+ for (y = 0, d = buf; y < scale->height; y++, d += scale->rowstride)
+ {
+ if (y == 0 || y == GIMP_CHECK_SIZE_SM)
+ continue;
+
+ if ((y / GIMP_CHECK_SIZE_SM) & 1)
+ memcpy (d, dark, scale->rowstride);
+ else
+ memcpy (d, light, scale->rowstride);
+ }
+ }
+ break;
+
+ case GTK_ORIENTATION_VERTICAL:
+ {
+ guchar light[4] = {0xff, 0xff, 0xff, 0xff};
+ guchar dark[4] = {0xff, 0xff, 0xff, 0xff};
+
+ for (y = 0, d = buf; y < scale->height; y++, d += scale->rowstride)
+ {
+ a = (gdouble) y / (gdouble) (scale->height - 1);
+
+ if (invert)
+ a = 1.0 - a;
+
+ GIMP_CAIRO_RGB24_SET_PIXEL (light,
+ (GIMP_CHECK_LIGHT +
+ (rgb.r - GIMP_CHECK_LIGHT) * a) * 255.999,
+ (GIMP_CHECK_LIGHT +
+ (rgb.g - GIMP_CHECK_LIGHT) * a) * 255.999,
+ (GIMP_CHECK_LIGHT +
+ (rgb.b - GIMP_CHECK_LIGHT) * a) * 255.999);
+
+ GIMP_CAIRO_RGB24_SET_PIXEL (dark,
+ (GIMP_CHECK_DARK +
+ (rgb.r - GIMP_CHECK_DARK) * a) * 255.999,
+ (GIMP_CHECK_DARK +
+ (rgb.g - GIMP_CHECK_DARK) * a) * 255.999,
+ (GIMP_CHECK_DARK +
+ (rgb.b - GIMP_CHECK_DARK) * a) * 255.999);
+
+ for (x = 0, l = d; x < scale->width; x++, l += 4)
+ {
+ if (((x / GIMP_CHECK_SIZE_SM) ^ (y / GIMP_CHECK_SIZE_SM)) & 1)
+ {
+ l[0] = light[0];
+ l[1] = light[1];
+ l[2] = light[2];
+ l[3] = light[3];
+ }
+ else
+ {
+ l[0] = dark[0];
+ l[1] = dark[1];
+ l[2] = dark[2];
+ l[3] = dark[3];
+ }
+ }
+ }
+ }
+ break;
+ }
+}
+
+/*
+ * This could be integrated into the render functions which might be
+ * slightly faster. But we trade speed for keeping the code simple.
+ */
+static void
+gimp_color_scale_render_stipple (GimpColorScale *scale)
+{
+ GtkWidget *widget = GTK_WIDGET (scale);
+ GtkStyle *style = gtk_widget_get_style (widget);
+ guchar *buf;
+ guchar insensitive[4] = {0xff, 0xff, 0xff, 0xff};
+ guint x, y;
+
+ if ((buf = scale->buf) == NULL)
+ return;
+
+ GIMP_CAIRO_RGB24_SET_PIXEL (insensitive,
+ style->bg[GTK_STATE_INSENSITIVE].red >> 8,
+ style->bg[GTK_STATE_INSENSITIVE].green >> 8,
+ style->bg[GTK_STATE_INSENSITIVE].blue >> 8);
+
+ for (y = 0; y < scale->height; y++, buf += scale->rowstride)
+ {
+ guchar *d = buf + 4 * (y % 2);
+
+ for (x = 0; x < scale->width - (y % 2); x += 2, d += 8)
+ {
+ d[0] = insensitive[0];
+ d[1] = insensitive[1];
+ d[2] = insensitive[2];
+ d[3] = insensitive[3];
+ }
+ }
+}
+
+static void
+gimp_color_scale_create_transform (GimpColorScale *scale)
+{
+ GimpColorScalePrivate *priv = GET_PRIVATE (scale);
+
+ if (priv->config)
+ {
+ static GimpColorProfile *profile = NULL;
+
+ const Babl *format = babl_format ("cairo-RGB24");
+
+ if (G_UNLIKELY (! profile))
+ profile = gimp_color_profile_new_rgb_srgb ();
+
+ priv->transform = gimp_widget_get_color_transform (GTK_WIDGET (scale),
+ priv->config,
+ profile,
+ format,
+ format);
+ }
+}
+
+static void
+gimp_color_scale_destroy_transform (GimpColorScale *scale)
+{
+ GimpColorScalePrivate *priv = GET_PRIVATE (scale);
+
+ if (priv->transform)
+ {
+ g_object_unref (priv->transform);
+ priv->transform = NULL;
+ }
+
+ gtk_widget_queue_draw (GTK_WIDGET (scale));
+}
+
+static void
+gimp_color_scale_notify_config (GimpColorConfig *config,
+ const GParamSpec *pspec,
+ GimpColorScale *scale)
+{
+ GimpColorScalePrivate *priv = GET_PRIVATE (scale);
+
+ gimp_color_scale_destroy_transform (scale);
+
+ gimp_rgb_get_uchar (&config->out_of_gamut_color,
+ priv->oog_color,
+ priv->oog_color + 1,
+ priv->oog_color + 2);
+ scale->needs_render = TRUE;
+}
diff --git a/libgimpwidgets/gimpcolorscale.h b/libgimpwidgets/gimpcolorscale.h
new file mode 100644
index 0000000..d6aa662
--- /dev/null
+++ b/libgimpwidgets/gimpcolorscale.h
@@ -0,0 +1,88 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcolorscale.h
+ * Copyright (C) 2002 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_COLOR_SCALE_H__
+#define __GIMP_COLOR_SCALE_H__
+
+G_BEGIN_DECLS
+
+
+#define GIMP_TYPE_COLOR_SCALE (gimp_color_scale_get_type ())
+#define GIMP_COLOR_SCALE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_COLOR_SCALE, GimpColorScale))
+#define GIMP_COLOR_SCALE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_COLOR_SCALE, GimpColorScaleClass))
+#define GIMP_IS_COLOR_SCALE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_COLOR_SCALE))
+#define GIMP_IS_COLOR_SCALE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_COLOR_SCALE))
+#define GIMP_COLOR_SCALE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_COLOR_SCALE, GimpColorScaleClass))
+
+
+typedef struct _GimpColorScaleClass GimpColorScaleClass;
+
+struct _GimpColorScale
+{
+ GtkScale parent_instance;
+
+ /*< private >*/
+ GimpColorSelectorChannel channel;
+ GimpRGB rgb;
+ GimpHSV hsv;
+
+ guchar *buf;
+ guint width;
+ guint height;
+ guint rowstride;
+
+ gboolean needs_render;
+};
+
+struct _GimpColorScaleClass
+{
+ GtkScaleClass parent_class;
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_color_scale_get_type (void) G_GNUC_CONST;
+GtkWidget * gimp_color_scale_new (GtkOrientation orientation,
+ GimpColorSelectorChannel channel);
+
+void gimp_color_scale_set_channel (GimpColorScale *scale,
+ GimpColorSelectorChannel channel);
+void gimp_color_scale_set_color (GimpColorScale *scale,
+ const GimpRGB *rgb,
+ const GimpHSV *hsv);
+
+void gimp_color_scale_set_color_config (GimpColorScale *scale,
+ GimpColorConfig *config);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_COLOR_SCALE_H__ */
diff --git a/libgimpwidgets/gimpcolorscales.c b/libgimpwidgets/gimpcolorscales.c
new file mode 100644
index 0000000..0c86013
--- /dev/null
+++ b/libgimpwidgets/gimpcolorscales.c
@@ -0,0 +1,925 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcolorscales.c
+ * Copyright (C) 2002 Michael Natterer <mitch@gimp.org>
+ *
+ * based on color_notebook module
+ * Copyright (C) 1998 Austin Donnelly <austin@greenend.org.uk>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpmath/gimpmath.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpcolorscale.h"
+#include "gimpcolorscales.h"
+#include "gimpwidgets.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpcolorscales
+ * @title: GimpColorScales
+ * @short_description: A #GimpColorSelector implementation.
+ *
+ * The #GimpColorScales widget is an implementation of a
+ * #GimpColorSelector. It shows a group of #GimpColorScale widgets
+ * that allow to adjust the HSV, LCH, and RGB color channels.
+ **/
+
+
+enum
+{
+ PROP_0,
+ PROP_SHOW_RGB_U8,
+ PROP_SHOW_HSV
+};
+
+enum
+{
+ GIMP_COLOR_SELECTOR_RED_U8 = GIMP_COLOR_SELECTOR_LCH_HUE + 1,
+ GIMP_COLOR_SELECTOR_GREEN_U8,
+ GIMP_COLOR_SELECTOR_BLUE_U8,
+ GIMP_COLOR_SELECTOR_ALPHA_U8
+};
+
+
+typedef struct _GimpLCH GimpLCH;
+
+struct _GimpLCH
+{
+ gdouble l, c, h, a;
+};
+
+
+typedef struct _ColorScale ColorScale;
+
+struct _ColorScale
+{
+ GimpColorSelectorChannel channel;
+
+ gdouble default_value;
+ gdouble scale_min_value;
+ gdouble scale_max_value;
+ gdouble scale_inc;
+ gdouble spin_min_value;
+ gdouble spin_max_value;
+};
+
+
+#define GIMP_COLOR_SCALES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_COLOR_SCALES, GimpColorScalesClass))
+#define GIMP_IS_COLOR_SCALES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_COLOR_SCALES))
+#define GIMP_COLOR_SCALES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_COLOR_SCALES, GimpColorScalesClass))
+
+
+typedef struct _GimpColorScalesClass GimpColorScalesClass;
+
+struct _GimpColorScales
+{
+ GimpColorSelector parent_instance;
+
+ gboolean show_rgb_u8;
+ GBinding *show_rgb_u8_binding;
+ GBinding *show_hsv_binding;
+
+ GtkWidget *lch_group;
+ GtkWidget *hsv_group;
+ GtkWidget *rgb_percent_group;
+ GtkWidget *rgb_u8_group;
+ GtkWidget *alpha_percent_group;
+ GtkWidget *alpha_u8_group;
+
+ GtkWidget *dummy_u8_toggle;
+ GtkWidget *toggles[14];
+ GtkAdjustment *adjustments[14];
+ GtkWidget *scales[14];
+};
+
+struct _GimpColorScalesClass
+{
+ GimpColorSelectorClass parent_class;
+};
+
+
+static void gimp_color_scales_dispose (GObject *object);
+static void gimp_color_scales_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_color_scales_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_color_scales_togg_sensitive (GimpColorSelector *selector,
+ gboolean sensitive);
+static void gimp_color_scales_togg_visible (GimpColorSelector *selector,
+ gboolean visible);
+
+static void gimp_color_scales_set_show_alpha (GimpColorSelector *selector,
+ gboolean show_alpha);
+static void gimp_color_scales_set_color (GimpColorSelector *selector,
+ const GimpRGB *rgb,
+ const GimpHSV *hsv);
+static void gimp_color_scales_set_channel (GimpColorSelector *selector,
+ GimpColorSelectorChannel channel);
+static void gimp_color_scales_set_model_visible
+ (GimpColorSelector *selector,
+ GimpColorSelectorModel model,
+ gboolean visible);
+static void gimp_color_scales_set_config (GimpColorSelector *selector,
+ GimpColorConfig *config);
+
+static void gimp_color_scales_update_visible (GimpColorScales *scales);
+static void gimp_color_scales_update_scales (GimpColorScales *scales,
+ gint skip);
+static void gimp_color_scales_toggle_changed (GtkWidget *widget,
+ GimpColorScales *scales);
+static void gimp_color_scales_scale_changed (GtkAdjustment *adjustment,
+ GimpColorScales *scales);
+static void gimp_color_scales_toggle_lch_hsv (GtkToggleButton *toggle,
+ GimpColorScales *scales);
+
+
+G_DEFINE_TYPE (GimpColorScales, gimp_color_scales, GIMP_TYPE_COLOR_SELECTOR)
+
+#define parent_class gimp_color_scales_parent_class
+
+static const Babl *fish_rgb_to_lch = NULL;
+static const Babl *fish_lch_to_rgb = NULL;
+
+static const ColorScale scale_defs[] =
+{
+ { GIMP_COLOR_SELECTOR_HUE, 0, 0, 360, 30, 0, 360 },
+ { GIMP_COLOR_SELECTOR_SATURATION, 0, 0, 100, 10, 0, 500 },
+ { GIMP_COLOR_SELECTOR_VALUE, 0, 0, 100, 10, 0, 500 },
+
+ { GIMP_COLOR_SELECTOR_RED, 0, 0, 100, 10, -500, 500 },
+ { GIMP_COLOR_SELECTOR_GREEN, 0, 0, 100, 10, -500, 500 },
+ { GIMP_COLOR_SELECTOR_BLUE, 0, 0, 100, 10, -500, 500 },
+ { GIMP_COLOR_SELECTOR_ALPHA, 0, 0, 100, 10, 0, 100 },
+
+ { GIMP_COLOR_SELECTOR_LCH_LIGHTNESS, 0, 0, 100, 10, 0, 300 },
+ { GIMP_COLOR_SELECTOR_LCH_CHROMA, 0, 0, 200, 10, 0, 300 },
+ { GIMP_COLOR_SELECTOR_LCH_HUE, 0, 0, 360, 30, 0, 360 },
+
+ { GIMP_COLOR_SELECTOR_RED_U8, 0, 0, 255, 16, -1275, 1275 },
+ { GIMP_COLOR_SELECTOR_GREEN_U8, 0, 0, 255, 16, -1275, 1275 },
+ { GIMP_COLOR_SELECTOR_BLUE_U8, 0, 0, 255, 16, -1275, 1275 },
+ { GIMP_COLOR_SELECTOR_ALPHA_U8, 0, 0, 255, 16, 0, 255 }
+};
+
+
+static void
+gimp_color_scales_class_init (GimpColorScalesClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpColorSelectorClass *selector_class = GIMP_COLOR_SELECTOR_CLASS (klass);
+
+ object_class->dispose = gimp_color_scales_dispose;
+ object_class->get_property = gimp_color_scales_get_property;
+ object_class->set_property = gimp_color_scales_set_property;
+
+ selector_class->name = _("Scales");
+ selector_class->help_id = "gimp-colorselector-scales";
+ selector_class->icon_name = GIMP_ICON_DIALOG_TOOL_OPTIONS;
+ selector_class->set_toggles_visible = gimp_color_scales_togg_visible;
+ selector_class->set_toggles_sensitive = gimp_color_scales_togg_sensitive;
+ selector_class->set_show_alpha = gimp_color_scales_set_show_alpha;
+ selector_class->set_color = gimp_color_scales_set_color;
+ selector_class->set_channel = gimp_color_scales_set_channel;
+ selector_class->set_model_visible = gimp_color_scales_set_model_visible;
+ selector_class->set_config = gimp_color_scales_set_config;
+
+ g_object_class_install_property (object_class, PROP_SHOW_RGB_U8,
+ g_param_spec_boolean ("show-rgb-u8",
+ "Show RGB 0..255",
+ "Show RGB 0..255 scales",
+ FALSE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+ g_object_class_install_property (object_class, PROP_SHOW_HSV,
+ g_param_spec_boolean ("show-hsv",
+ "Show HSV",
+ "Show HSV instead of LCH",
+ FALSE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ fish_rgb_to_lch = babl_fish (babl_format ("R'G'B'A double"),
+ babl_format ("CIE LCH(ab) alpha double"));
+ fish_lch_to_rgb = babl_fish (babl_format ("CIE LCH(ab) alpha double"),
+ babl_format ("R'G'B'A double"));
+}
+
+static GtkWidget *
+create_group (GimpColorScales *scales,
+ GSList **radio_group,
+ GtkSizeGroup *size_group0,
+ GtkSizeGroup *size_group1,
+ GtkSizeGroup *size_group2,
+ GimpColorSelectorChannel first_channel,
+ GimpColorSelectorChannel last_channel)
+{
+ GimpColorSelector *selector = GIMP_COLOR_SELECTOR (scales);
+ GtkWidget *table;
+ GEnumClass *enum_class;
+ gint row;
+ gint i;
+
+ table = gtk_table_new (last_channel - first_channel + 1, 4, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 1);
+ gtk_table_set_col_spacing (GTK_TABLE (table), 0, 0);
+
+ enum_class = g_type_class_ref (GIMP_TYPE_COLOR_SELECTOR_CHANNEL);
+
+ for (i = first_channel, row = 0; i <= last_channel; i++, row++)
+ {
+ GimpEnumDesc *enum_desc;
+ gint enum_value = i;
+ gboolean is_u8 = FALSE;
+
+ if (enum_value >= GIMP_COLOR_SELECTOR_RED_U8 &&
+ enum_value <= GIMP_COLOR_SELECTOR_ALPHA_U8)
+ {
+ enum_value -= 7;
+ is_u8 = TRUE;
+ }
+
+ enum_desc = gimp_enum_get_desc (enum_class, enum_value);
+
+ if (i == GIMP_COLOR_SELECTOR_ALPHA ||
+ i == GIMP_COLOR_SELECTOR_ALPHA_U8)
+ {
+ /* just to allocate the space via the size group */
+ scales->toggles[i] = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ }
+ else
+ {
+ scales->toggles[i] = gtk_radio_button_new (*radio_group);
+ *radio_group =
+ gtk_radio_button_get_group (GTK_RADIO_BUTTON (scales->toggles[i]));
+
+ if (enum_value == gimp_color_selector_get_channel (selector))
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (scales->toggles[i]),
+ TRUE);
+
+ if (is_u8)
+ {
+ /* bind the RGB U8 toggles to the RGB percent toggles */
+ g_object_bind_property (scales->toggles[i - 7], "active",
+ scales->toggles[i], "active",
+ G_BINDING_SYNC_CREATE |
+ G_BINDING_BIDIRECTIONAL);
+ }
+ else
+ {
+ g_signal_connect (scales->toggles[i], "toggled",
+ G_CALLBACK (gimp_color_scales_toggle_changed),
+ scales);
+ }
+ }
+
+ gtk_table_attach (GTK_TABLE (table), scales->toggles[i],
+ 0, 1, row, row + 1,
+ GTK_SHRINK, GTK_EXPAND, 0, 0);
+
+ if (gimp_color_selector_get_toggles_visible (selector))
+ gtk_widget_show (scales->toggles[i]);
+
+ gimp_help_set_help_data (scales->toggles[i],
+ gettext (enum_desc->value_help), NULL);
+
+ gtk_size_group_add_widget (size_group0, scales->toggles[i]);
+
+ scales->adjustments[i] = (GtkAdjustment *)
+ gimp_color_scale_entry_new (GTK_TABLE (table), 1, row,
+ gettext (enum_desc->value_desc),
+ -1, -1,
+ scale_defs[i].default_value,
+ scale_defs[i].scale_min_value,
+ scale_defs[i].scale_max_value,
+ 1.0,
+ scale_defs[i].scale_inc,
+ 1,
+ gettext (enum_desc->value_help),
+ NULL);
+
+ gtk_adjustment_configure (scales->adjustments[i],
+ scale_defs[i].default_value,
+ scale_defs[i].spin_min_value,
+ scale_defs[i].spin_max_value,
+ 1.0,
+ scale_defs[i].scale_inc,
+ 0);
+
+ scales->scales[i] = GIMP_SCALE_ENTRY_SCALE (scales->adjustments[i]);
+ g_object_add_weak_pointer (G_OBJECT (scales->scales[i]),
+ (gpointer) &scales->scales[i]);
+
+ gimp_color_scale_set_channel (GIMP_COLOR_SCALE (scales->scales[i]),
+ enum_value);
+ gtk_size_group_add_widget (size_group1, scales->scales[i]);
+
+ gtk_size_group_add_widget (size_group2,
+ GIMP_SCALE_ENTRY_SPINBUTTON (scales->adjustments[i]));
+
+ g_signal_connect (scales->adjustments[i], "value-changed",
+ G_CALLBACK (gimp_color_scales_scale_changed),
+ scales);
+ }
+
+ g_type_class_unref (enum_class);
+
+ return table;
+}
+
+static void
+gimp_color_scales_init (GimpColorScales *scales)
+{
+ GimpColorSelector *selector = GIMP_COLOR_SELECTOR (scales);
+ GtkSizeGroup *size_group0;
+ GtkSizeGroup *size_group1;
+ GtkSizeGroup *size_group2;
+ GtkWidget *hbox;
+ GtkWidget *radio1;
+ GtkWidget *radio2;
+ GtkWidget *table;
+ GSList *main_group;
+ GSList *u8_group;
+ GSList *radio_group;
+
+ gtk_box_set_spacing (GTK_BOX (scales), 5);
+
+ scales->show_rgb_u8_binding = NULL;
+ scales->show_hsv_binding = NULL;
+
+ /* don't need the toggles for our own operation */
+ selector->toggles_visible = FALSE;
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+ gtk_box_pack_start (GTK_BOX (scales), hbox, 0, 0, FALSE);
+ gtk_widget_show (hbox);
+
+ main_group = NULL;
+ u8_group = NULL;
+
+ scales->dummy_u8_toggle = gtk_radio_button_new (NULL);
+ g_object_ref_sink (scales->dummy_u8_toggle);
+ u8_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (scales->dummy_u8_toggle));
+
+ size_group0 = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+ size_group1 = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+ size_group2 = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+ scales->rgb_percent_group =
+ table = create_group (scales, &main_group,
+ size_group0, size_group1, size_group2,
+ GIMP_COLOR_SELECTOR_RED,
+ GIMP_COLOR_SELECTOR_BLUE);
+ gtk_box_pack_start (GTK_BOX (scales), table, FALSE, FALSE, 0);
+
+ scales->rgb_u8_group =
+ table = create_group (scales, &u8_group,
+ size_group0, size_group1, size_group2,
+ GIMP_COLOR_SELECTOR_RED_U8,
+ GIMP_COLOR_SELECTOR_BLUE_U8);
+ gtk_box_pack_start (GTK_BOX (scales), table, FALSE, FALSE, 0);
+
+ scales->lch_group =
+ table = create_group (scales, &main_group,
+ size_group0, size_group1, size_group2,
+ GIMP_COLOR_SELECTOR_LCH_LIGHTNESS,
+ GIMP_COLOR_SELECTOR_LCH_HUE);
+ gtk_box_pack_start (GTK_BOX (scales), table, FALSE, FALSE, 0);
+
+ scales->hsv_group =
+ table = create_group (scales, &main_group,
+ size_group0, size_group1, size_group2,
+ GIMP_COLOR_SELECTOR_HUE,
+ GIMP_COLOR_SELECTOR_VALUE);
+ gtk_box_pack_start (GTK_BOX (scales), table, FALSE, FALSE, 0);
+
+ scales->alpha_percent_group =
+ table = create_group (scales, &main_group,
+ size_group0, size_group1, size_group2,
+ GIMP_COLOR_SELECTOR_ALPHA,
+ GIMP_COLOR_SELECTOR_ALPHA);
+ gtk_box_pack_start (GTK_BOX (scales), table, FALSE, FALSE, 0);
+
+ scales->alpha_u8_group =
+ table = create_group (scales, &u8_group,
+ size_group0, size_group1, size_group2,
+ GIMP_COLOR_SELECTOR_ALPHA_U8,
+ GIMP_COLOR_SELECTOR_ALPHA_U8);
+ gtk_box_pack_start (GTK_BOX (scales), table, FALSE, FALSE, 0);
+
+ g_object_unref (size_group0);
+ g_object_unref (size_group1);
+ g_object_unref (size_group2);
+
+ gimp_color_scales_update_visible (scales);
+
+ radio_group = NULL;
+
+ radio1 = gtk_radio_button_new_with_label (NULL, _("0..100"));
+ radio_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio1));
+ radio2 = gtk_radio_button_new_with_label (radio_group, _("0..255"));
+
+ gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (radio1), FALSE);
+ gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (radio2), FALSE);
+
+ gtk_box_pack_start (GTK_BOX (hbox), radio1, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), radio2, FALSE, FALSE, 0);
+
+ gtk_widget_show (radio1);
+ gtk_widget_show (radio2);
+
+ if (scales->show_rgb_u8)
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio2), TRUE);
+
+ g_object_bind_property (G_OBJECT (radio2), "active",
+ G_OBJECT (scales), "show-rgb-u8",
+ G_BINDING_SYNC_CREATE |
+ G_BINDING_BIDIRECTIONAL);
+
+ radio_group = NULL;
+
+ radio1 = gtk_radio_button_new_with_label (NULL, _("LCh"));
+ radio_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio1));
+ radio2 = gtk_radio_button_new_with_label (radio_group, _("HSV"));
+
+ gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (radio1), FALSE);
+ gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (radio2), FALSE);
+
+ gtk_box_pack_end (GTK_BOX (hbox), radio2, FALSE, FALSE, 0);
+ gtk_box_pack_end (GTK_BOX (hbox), radio1, FALSE, FALSE, 0);
+
+ gtk_widget_show (radio1);
+ gtk_widget_show (radio2);
+
+ if (gimp_color_selector_get_model_visible (selector,
+ GIMP_COLOR_SELECTOR_MODEL_HSV))
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio2), TRUE);
+
+ g_object_bind_property (G_OBJECT (radio2), "active",
+ G_OBJECT (scales), "show-hsv",
+ G_BINDING_SYNC_CREATE |
+ G_BINDING_BIDIRECTIONAL);
+
+ g_signal_connect (radio1, "toggled",
+ G_CALLBACK (gimp_color_scales_toggle_lch_hsv),
+ scales);
+}
+
+static void
+gimp_color_scales_dispose (GObject *object)
+{
+ GimpColorScales *scales = GIMP_COLOR_SCALES (object);
+
+ g_clear_object (&scales->dummy_u8_toggle);
+
+ g_clear_pointer (&scales->show_rgb_u8_binding, g_binding_unbind);
+ g_clear_pointer (&scales->show_hsv_binding, g_binding_unbind);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gimp_color_scales_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpColorScales *scales = GIMP_COLOR_SCALES (object);
+ gboolean hsv;
+
+ switch (property_id)
+ {
+ case PROP_SHOW_RGB_U8:
+ g_value_set_boolean (value, scales->show_rgb_u8);
+ break;
+ case PROP_SHOW_HSV:
+ hsv = gimp_color_selector_get_model_visible (GIMP_COLOR_SELECTOR (object),
+ GIMP_COLOR_SELECTOR_MODEL_HSV);
+ g_value_set_boolean (value, hsv);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_color_scales_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpColorScales *scales = GIMP_COLOR_SCALES (object);
+ gboolean show_hsv;
+
+ switch (property_id)
+ {
+ case PROP_SHOW_RGB_U8:
+ gimp_color_scales_set_show_rgb_u8 (scales, g_value_get_boolean (value));
+ break;
+ case PROP_SHOW_HSV:
+ show_hsv = g_value_get_boolean (value);
+
+ gimp_color_selector_set_model_visible (GIMP_COLOR_SELECTOR (object),
+ GIMP_COLOR_SELECTOR_MODEL_LCH,
+ ! show_hsv);
+ gimp_color_selector_set_model_visible (GIMP_COLOR_SELECTOR (object),
+ GIMP_COLOR_SELECTOR_MODEL_HSV,
+ show_hsv);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_color_scales_togg_sensitive (GimpColorSelector *selector,
+ gboolean sensitive)
+{
+ GimpColorScales *scales = GIMP_COLOR_SCALES (selector);
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS (scale_defs); i++)
+ if (scales->toggles[i])
+ gtk_widget_set_sensitive (scales->toggles[i], sensitive);
+}
+
+static void
+gimp_color_scales_togg_visible (GimpColorSelector *selector,
+ gboolean visible)
+{
+ GimpColorScales *scales = GIMP_COLOR_SCALES (selector);
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS (scale_defs); i++)
+ if (scales->toggles[i])
+ gtk_widget_set_visible (scales->toggles[i], visible);
+}
+
+static void
+gimp_color_scales_set_show_alpha (GimpColorSelector *selector,
+ gboolean show_alpha)
+{
+ gimp_color_scales_update_visible (GIMP_COLOR_SCALES (selector));
+}
+
+static void
+gimp_color_scales_set_color (GimpColorSelector *selector,
+ const GimpRGB *rgb,
+ const GimpHSV *hsv)
+{
+ GimpColorScales *scales = GIMP_COLOR_SCALES (selector);
+
+ gimp_color_scales_update_scales (scales, -1);
+}
+
+static void
+gimp_color_scales_set_channel (GimpColorSelector *selector,
+ GimpColorSelectorChannel channel)
+{
+ GimpColorScales *scales = GIMP_COLOR_SCALES (selector);
+
+ if (GTK_IS_RADIO_BUTTON (scales->toggles[channel]))
+ {
+ g_signal_handlers_block_by_func (scales->toggles[channel],
+ gimp_color_scales_toggle_changed,
+ scales);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (scales->toggles[channel]),
+ TRUE);
+
+ g_signal_handlers_unblock_by_func (scales->toggles[channel],
+ gimp_color_scales_toggle_changed,
+ scales);
+ }
+}
+
+static void
+gimp_color_scales_set_model_visible (GimpColorSelector *selector,
+ GimpColorSelectorModel model,
+ gboolean visible)
+{
+ gimp_color_scales_update_visible (GIMP_COLOR_SCALES (selector));
+}
+
+static void
+gimp_color_scales_set_config (GimpColorSelector *selector,
+ GimpColorConfig *config)
+{
+ GimpColorScales *scales = GIMP_COLOR_SCALES (selector);
+ gint i;
+
+ g_clear_pointer (&scales->show_rgb_u8_binding, g_binding_unbind);
+ g_clear_pointer (&scales->show_hsv_binding, g_binding_unbind);
+
+ if (config)
+ {
+ scales->show_rgb_u8_binding = g_object_bind_property (config, "show-rgb-u8",
+ scales, "show-rgb-u8",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+ scales->show_hsv_binding = g_object_bind_property (config, "show-hsv",
+ scales, "show-hsv",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+ }
+
+ for (i = 0; i < G_N_ELEMENTS (scale_defs); i++)
+ {
+ if (scales->scales[i])
+ gimp_color_scale_set_color_config (GIMP_COLOR_SCALE (scales->scales[i]),
+ config);
+ }
+}
+
+
+/* public functions */
+
+void
+gimp_color_scales_set_show_rgb_u8 (GimpColorScales *scales,
+ gboolean show_rgb_u8)
+{
+ g_return_if_fail (GIMP_IS_COLOR_SCALES (scales));
+
+ show_rgb_u8 = show_rgb_u8 ? TRUE : FALSE;
+
+ if (show_rgb_u8 != scales->show_rgb_u8)
+ {
+ scales->show_rgb_u8 = show_rgb_u8;
+
+ g_object_notify (G_OBJECT (scales), "show-rgb-u8");
+
+ gimp_color_scales_update_visible (scales);
+ }
+}
+
+gboolean
+gimp_color_scales_get_show_rgb_u8 (GimpColorScales *scales)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_SCALES (scales), FALSE);
+
+ return scales->show_rgb_u8;
+}
+
+
+/* private functions */
+
+static void
+gimp_color_scales_update_visible (GimpColorScales *scales)
+{
+ GimpColorSelector *selector = GIMP_COLOR_SELECTOR (scales);
+ gboolean show_alpha;
+ gboolean rgb_visible;
+ gboolean lch_visible;
+ gboolean hsv_visible;
+
+ show_alpha = gimp_color_selector_get_show_alpha (selector);
+ rgb_visible = gimp_color_selector_get_model_visible (selector,
+ GIMP_COLOR_SELECTOR_MODEL_RGB);
+ lch_visible = gimp_color_selector_get_model_visible (selector,
+ GIMP_COLOR_SELECTOR_MODEL_LCH);
+ hsv_visible = gimp_color_selector_get_model_visible (selector,
+ GIMP_COLOR_SELECTOR_MODEL_HSV);
+
+ gtk_widget_set_visible (scales->rgb_percent_group,
+ rgb_visible && ! scales->show_rgb_u8);
+ gtk_widget_set_visible (scales->rgb_u8_group,
+ rgb_visible && scales->show_rgb_u8);
+
+ gtk_widget_set_visible (scales->lch_group, lch_visible);
+ gtk_widget_set_visible (scales->hsv_group, hsv_visible);
+
+ gtk_widget_set_visible (scales->alpha_percent_group,
+ show_alpha && ! scales->show_rgb_u8);
+ gtk_widget_set_visible (scales->alpha_u8_group,
+ show_alpha && scales->show_rgb_u8);
+}
+
+static void
+gimp_color_scales_update_scales (GimpColorScales *scales,
+ gint skip)
+{
+ GimpColorSelector *selector = GIMP_COLOR_SELECTOR (scales);
+ GimpLCH lch;
+ gdouble values[G_N_ELEMENTS (scale_defs)];
+ gint i;
+
+ babl_process (fish_rgb_to_lch, &selector->rgb, &lch, 1);
+
+ values[GIMP_COLOR_SELECTOR_HUE] = selector->hsv.h * 360.0;
+ values[GIMP_COLOR_SELECTOR_SATURATION] = selector->hsv.s * 100.0;
+ values[GIMP_COLOR_SELECTOR_VALUE] = selector->hsv.v * 100.0;
+
+ values[GIMP_COLOR_SELECTOR_RED] = selector->rgb.r * 100.0;
+ values[GIMP_COLOR_SELECTOR_GREEN] = selector->rgb.g * 100.0;
+ values[GIMP_COLOR_SELECTOR_BLUE] = selector->rgb.b * 100.0;
+ values[GIMP_COLOR_SELECTOR_ALPHA] = selector->rgb.a * 100.0;
+
+ values[GIMP_COLOR_SELECTOR_LCH_LIGHTNESS] = lch.l;
+ values[GIMP_COLOR_SELECTOR_LCH_CHROMA] = lch.c;
+ values[GIMP_COLOR_SELECTOR_LCH_HUE] = lch.h;
+
+ values[GIMP_COLOR_SELECTOR_RED_U8] = selector->rgb.r * 255.0;
+ values[GIMP_COLOR_SELECTOR_GREEN_U8] = selector->rgb.g * 255.0;
+ values[GIMP_COLOR_SELECTOR_BLUE_U8] = selector->rgb.b * 255.0;
+ values[GIMP_COLOR_SELECTOR_ALPHA_U8] = selector->rgb.a * 255.0;
+
+ for (i = 0; i < G_N_ELEMENTS (scale_defs); i++)
+ {
+ if (i != skip)
+ {
+ g_signal_handlers_block_by_func (scales->adjustments[i],
+ gimp_color_scales_scale_changed,
+ scales);
+
+ gtk_adjustment_set_value (scales->adjustments[i], values[i]);
+
+ g_signal_handlers_unblock_by_func (scales->adjustments[i],
+ gimp_color_scales_scale_changed,
+ scales);
+ }
+
+ gimp_color_scale_set_color (GIMP_COLOR_SCALE (scales->scales[i]),
+ &selector->rgb, &selector->hsv);
+ }
+}
+
+static void
+gimp_color_scales_toggle_changed (GtkWidget *widget,
+ GimpColorScales *scales)
+{
+ GimpColorSelector *selector = GIMP_COLOR_SELECTOR (scales);
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ {
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS (scale_defs); i++)
+ {
+ if (widget == scales->toggles[i])
+ {
+ gimp_color_selector_set_channel (selector, i);
+
+ if (i < GIMP_COLOR_SELECTOR_RED ||
+ i > GIMP_COLOR_SELECTOR_BLUE)
+ {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (scales->dummy_u8_toggle),
+ TRUE);
+ }
+
+ break;
+ }
+ }
+ }
+}
+
+static void
+gimp_color_scales_scale_changed (GtkAdjustment *adjustment,
+ GimpColorScales *scales)
+{
+ GimpColorSelector *selector = GIMP_COLOR_SELECTOR (scales);
+ gdouble value = gtk_adjustment_get_value (adjustment);
+ GimpLCH lch;
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS (scale_defs); i++)
+ if (scales->adjustments[i] == adjustment)
+ break;
+
+ switch (i)
+ {
+ case GIMP_COLOR_SELECTOR_HUE:
+ selector->hsv.h = value / 360.0;
+ break;
+
+ case GIMP_COLOR_SELECTOR_SATURATION:
+ selector->hsv.s = value / 100.0;
+ break;
+
+ case GIMP_COLOR_SELECTOR_VALUE:
+ selector->hsv.v = value / 100.0;
+ break;
+
+ case GIMP_COLOR_SELECTOR_RED:
+ selector->rgb.r = value / 100.0;
+ break;
+
+ case GIMP_COLOR_SELECTOR_GREEN:
+ selector->rgb.g = value / 100.0;
+ break;
+
+ case GIMP_COLOR_SELECTOR_BLUE:
+ selector->rgb.b = value / 100.0;
+ break;
+
+ case GIMP_COLOR_SELECTOR_ALPHA:
+ selector->hsv.a = selector->rgb.a = value / 100.0;
+ break;
+
+ case GIMP_COLOR_SELECTOR_LCH_LIGHTNESS:
+ babl_process (fish_rgb_to_lch, &selector->rgb, &lch, 1);
+ lch.l = value;
+ break;
+
+ case GIMP_COLOR_SELECTOR_LCH_CHROMA:
+ babl_process (fish_rgb_to_lch, &selector->rgb, &lch, 1);
+ lch.c = value;
+ break;
+
+ case GIMP_COLOR_SELECTOR_LCH_HUE:
+ babl_process (fish_rgb_to_lch, &selector->rgb, &lch, 1);
+ lch.h = value;
+ break;
+
+ case GIMP_COLOR_SELECTOR_RED_U8:
+ selector->rgb.r = value / 255.0;
+ break;
+
+ case GIMP_COLOR_SELECTOR_GREEN_U8:
+ selector->rgb.g = value / 255.0;
+ break;
+
+ case GIMP_COLOR_SELECTOR_BLUE_U8:
+ selector->rgb.b = value / 255.0;
+ break;
+
+ case GIMP_COLOR_SELECTOR_ALPHA_U8:
+ selector->hsv.a = selector->rgb.a = value / 255.0;
+ break;
+ }
+
+ if ((i >= GIMP_COLOR_SELECTOR_HUE) &&
+ (i <= GIMP_COLOR_SELECTOR_VALUE))
+ {
+ gimp_hsv_to_rgb (&selector->hsv, &selector->rgb);
+ }
+ else if ((i >= GIMP_COLOR_SELECTOR_LCH_LIGHTNESS) &&
+ (i <= GIMP_COLOR_SELECTOR_LCH_HUE))
+ {
+ babl_process (fish_lch_to_rgb, &lch, &selector->rgb, 1);
+ gimp_rgb_to_hsv (&selector->rgb, &selector->hsv);
+ }
+ else if ((i >= GIMP_COLOR_SELECTOR_RED) &&
+ (i <= GIMP_COLOR_SELECTOR_BLUE))
+ {
+ gimp_rgb_to_hsv (&selector->rgb, &selector->hsv);
+ }
+ else if ((i >= GIMP_COLOR_SELECTOR_RED_U8) &&
+ (i <= GIMP_COLOR_SELECTOR_BLUE_U8))
+ {
+ gimp_rgb_to_hsv (&selector->rgb, &selector->hsv);
+ }
+
+ gimp_color_scales_update_scales (scales, i);
+
+ gimp_color_selector_color_changed (selector);
+}
+
+static void
+gimp_color_scales_toggle_lch_hsv (GtkToggleButton *toggle,
+ GimpColorScales *scales)
+{
+ GimpColorSelector *selector = GIMP_COLOR_SELECTOR (scales);
+ gboolean show_hsv = ! gtk_toggle_button_get_active (toggle);
+
+ gimp_color_selector_set_model_visible (selector,
+ GIMP_COLOR_SELECTOR_MODEL_LCH,
+ ! show_hsv);
+ gimp_color_selector_set_model_visible (selector,
+ GIMP_COLOR_SELECTOR_MODEL_HSV,
+ show_hsv);
+ g_object_set (scales, "show-hsv", show_hsv, NULL);
+}
diff --git a/libgimpwidgets/gimpcolorscales.h b/libgimpwidgets/gimpcolorscales.h
new file mode 100644
index 0000000..9bfc90c
--- /dev/null
+++ b/libgimpwidgets/gimpcolorscales.h
@@ -0,0 +1,49 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcolorscales.h
+ * Copyright (C) 2002 Michael Natterer <mitch@gimp.org>
+ *
+ * based on color_notebook module
+ * Copyright (C) 1998 Austin Donnelly <austin@greenend.org.uk>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_COLOR_SCALES_H__
+#define __GIMP_COLOR_SCALES_H__
+
+G_BEGIN_DECLS
+
+
+#define GIMP_TYPE_COLOR_SCALES (gimp_color_scales_get_type ())
+#define GIMP_COLOR_SCALES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_COLOR_SCALES, GimpColorScales))
+#define GIMP_IS_COLOR_SCALES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_COLOR_SCALES))
+
+
+GType gimp_color_scales_get_type (void) G_GNUC_CONST;
+
+void gimp_color_scales_set_show_rgb_u8 (GimpColorScales *scales,
+ gboolean show_rgb_u8);
+gboolean gimp_color_scales_get_show_rgb_u8 (GimpColorScales *scales);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_COLOR_SCALES_H__ */
diff --git a/libgimpwidgets/gimpcolorselect.c b/libgimpwidgets/gimpcolorselect.c
new file mode 100644
index 0000000..373917d
--- /dev/null
+++ b/libgimpwidgets/gimpcolorselect.c
@@ -0,0 +1,2006 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcolorselect.c
+ * Copyright (C) 2002 Michael Natterer <mitch@gimp.org>
+ *
+ * based on color_notebook module
+ * Copyright (C) 1998 Austin Donnelly <austin@greenend.org.uk>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpconfig/gimpconfig.h"
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpmath/gimpmath.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpcolorselector.h"
+#include "gimpcolorselect.h"
+#include "gimphelpui.h"
+#include "gimpicons.h"
+#include "gimpwidgetsutils.h"
+#include "gimp3migration.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpcolorselect
+ * @title: GimpColorSelect
+ * @short_description: A #GimpColorSelector implementation.
+ *
+ * The #GimpColorSelect widget is an implementation of a
+ * #GimpColorSelector. It shows a square area that supports
+ * interactively changing two color channels and a smaller area to
+ * change the third channel. You can select which channel should be
+ * the third by calling gimp_color_selector_set_channel(). The widget
+ * will then change the other two channels accordingly.
+ **/
+
+
+#define COLOR_AREA_EVENT_MASK (GDK_EXPOSURE_MASK | \
+ GDK_BUTTON_PRESS_MASK | \
+ GDK_BUTTON_RELEASE_MASK | \
+ GDK_BUTTON_MOTION_MASK | \
+ GDK_ENTER_NOTIFY_MASK)
+
+
+typedef enum
+{
+ COLOR_SELECT_HUE = 0,
+ COLOR_SELECT_SATURATION,
+ COLOR_SELECT_VALUE,
+
+ COLOR_SELECT_RED,
+ COLOR_SELECT_GREEN,
+ COLOR_SELECT_BLUE,
+ COLOR_SELECT_ALPHA,
+
+ COLOR_SELECT_LCH_LIGHTNESS,
+ COLOR_SELECT_LCH_CHROMA,
+ COLOR_SELECT_LCH_HUE,
+
+ COLOR_SELECT_HUE_SATURATION,
+ COLOR_SELECT_HUE_VALUE,
+ COLOR_SELECT_SATURATION_VALUE,
+
+ COLOR_SELECT_RED_GREEN,
+ COLOR_SELECT_RED_BLUE,
+ COLOR_SELECT_GREEN_BLUE,
+
+ COLOR_SELECT_LCH_HUE_CHROMA,
+ COLOR_SELECT_LCH_HUE_LIGHTNESS,
+ COLOR_SELECT_LCH_CHROMA_LIGHTNESS
+} ColorSelectFillType;
+
+typedef enum
+{
+ UPDATE_VALUES = 1 << 0,
+ UPDATE_POS = 1 << 1,
+ UPDATE_XY_COLOR = 1 << 2,
+ UPDATE_Z_COLOR = 1 << 3,
+ UPDATE_CALLER = 1 << 6
+} ColorSelectUpdateType;
+
+typedef enum
+{
+ DRAG_NONE,
+ DRAG_XY,
+ DRAG_Z
+} ColorSelectDragMode;
+
+
+typedef struct _GimpLCH GimpLCH;
+
+struct _GimpLCH
+{
+ gdouble l, c, h, a;
+};
+
+
+#define GIMP_COLOR_SELECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_COLOR_SELECT, GimpColorSelectClass))
+#define GIMP_IS_COLOR_SELECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_COLOR_SELECT))
+#define GIMP_COLOR_SELECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_COLOR_SELECT, GimpColorSelectClass))
+
+
+typedef struct _GimpColorSelectClass GimpColorSelectClass;
+
+struct _GimpColorSelect
+{
+ GimpColorSelector parent_instance;
+
+ GtkWidget *toggle_box[3];
+
+ GtkWidget *xy_color;
+ ColorSelectFillType xy_color_fill;
+ guchar *xy_buf;
+ gint xy_width;
+ gint xy_height;
+ gint xy_rowstride;
+ gboolean xy_needs_render;
+
+ GtkWidget *z_color;
+ ColorSelectFillType z_color_fill;
+ guchar *z_buf;
+ gint z_width;
+ gint z_height;
+ gint z_rowstride;
+ gboolean z_needs_render;
+
+ gdouble pos[3];
+
+ ColorSelectDragMode drag_mode;
+
+ GimpColorConfig *config;
+ GimpColorTransform *transform;
+ guchar oog_color[3];
+};
+
+struct _GimpColorSelectClass
+{
+ GimpColorSelectorClass parent_class;
+};
+
+
+typedef struct _ColorSelectFill ColorSelectFill;
+
+typedef void (* ColorSelectRenderFunc) (ColorSelectFill *color_select_fill);
+
+struct _ColorSelectFill
+{
+ guchar *buffer;
+ gint y;
+ gint width;
+ gint height;
+ GimpRGB rgb;
+ GimpHSV hsv;
+ GimpLCH lch;
+ guchar oog_color[3];
+
+ ColorSelectRenderFunc render_line;
+};
+
+
+static void gimp_color_select_finalize (GObject *object);
+
+static void gimp_color_select_togg_visible (GimpColorSelector *selector,
+ gboolean visible);
+static void gimp_color_select_togg_sensitive (GimpColorSelector *selector,
+ gboolean sensitive);
+static void gimp_color_select_set_color (GimpColorSelector *selector,
+ const GimpRGB *rgb,
+ const GimpHSV *hsv);
+static void gimp_color_select_set_channel (GimpColorSelector *selector,
+ GimpColorSelectorChannel channel);
+static void gimp_color_select_set_model_visible
+ (GimpColorSelector *selector,
+ GimpColorSelectorModel model,
+ gboolean visible);
+static void gimp_color_select_set_config (GimpColorSelector *selector,
+ GimpColorConfig *config);
+
+static void gimp_color_select_channel_toggled (GtkWidget *widget,
+ GimpColorSelect *select);
+
+static void gimp_color_select_update (GimpColorSelect *select,
+ ColorSelectUpdateType type);
+static void gimp_color_select_update_values (GimpColorSelect *select);
+static void gimp_color_select_update_pos (GimpColorSelect *select);
+
+#if 0
+static void gimp_color_select_drop_color (GtkWidget *widget,
+ gint x,
+ gint y,
+ const GimpRGB *color,
+ gpointer data);
+#endif
+
+static void gimp_color_select_xy_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation,
+ GimpColorSelect *select);
+static gboolean gimp_color_select_xy_expose (GtkWidget *widget,
+ GdkEventExpose *eevent,
+ GimpColorSelect *select);
+static gboolean gimp_color_select_xy_events (GtkWidget *widget,
+ GdkEvent *event,
+ GimpColorSelect *select);
+static void gimp_color_select_z_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation,
+ GimpColorSelect *select);
+static gboolean gimp_color_select_z_expose (GtkWidget *widget,
+ GdkEventExpose *eevent,
+ GimpColorSelect *select);
+static gboolean gimp_color_select_z_events (GtkWidget *widget,
+ GdkEvent *event,
+ GimpColorSelect *select);
+
+static void gimp_color_select_render (GtkWidget *widget,
+ guchar *buf,
+ gint width,
+ gint height,
+ gint rowstride,
+ ColorSelectFillType fill_type,
+ const GimpHSV *hsv,
+ const GimpRGB *rgb,
+ const guchar *oog_color);
+
+static void color_select_render_red (ColorSelectFill *csf);
+static void color_select_render_green (ColorSelectFill *csf);
+static void color_select_render_blue (ColorSelectFill *csf);
+
+static void color_select_render_hue (ColorSelectFill *csf);
+static void color_select_render_saturation (ColorSelectFill *csf);
+static void color_select_render_value (ColorSelectFill *csf);
+
+static void color_select_render_lch_lightness (ColorSelectFill *csf);
+static void color_select_render_lch_chroma (ColorSelectFill *csf);
+static void color_select_render_lch_hue (ColorSelectFill *csf);
+
+static void color_select_render_red_green (ColorSelectFill *csf);
+static void color_select_render_red_blue (ColorSelectFill *csf);
+static void color_select_render_green_blue (ColorSelectFill *csf);
+
+static void color_select_render_hue_saturation (ColorSelectFill *csf);
+static void color_select_render_hue_value (ColorSelectFill *csf);
+static void color_select_render_saturation_value (ColorSelectFill *csf);
+
+static void color_select_render_lch_chroma_lightness (ColorSelectFill *csf);
+static void color_select_render_lch_hue_lightness (ColorSelectFill *csf);
+static void color_select_render_lch_hue_chroma (ColorSelectFill *csf);
+
+static void gimp_color_select_create_transform (GimpColorSelect *select);
+static void gimp_color_select_destroy_transform (GimpColorSelect *select);
+static void gimp_color_select_notify_config (GimpColorConfig *config,
+ const GParamSpec *pspec,
+ GimpColorSelect *select);
+
+
+G_DEFINE_TYPE (GimpColorSelect, gimp_color_select, GIMP_TYPE_COLOR_SELECTOR)
+
+#define parent_class gimp_color_select_parent_class
+
+static const ColorSelectRenderFunc render_funcs[] =
+{
+ color_select_render_hue,
+ color_select_render_saturation,
+ color_select_render_value,
+
+ color_select_render_red,
+ color_select_render_green,
+ color_select_render_blue,
+ NULL, /* alpha */
+
+ color_select_render_lch_lightness,
+ color_select_render_lch_chroma,
+ color_select_render_lch_hue,
+
+ color_select_render_hue_saturation,
+ color_select_render_hue_value,
+ color_select_render_saturation_value,
+
+ color_select_render_red_green,
+ color_select_render_red_blue,
+ color_select_render_green_blue,
+
+ color_select_render_lch_hue_chroma,
+ color_select_render_lch_hue_lightness,
+ color_select_render_lch_chroma_lightness
+};
+
+static const Babl *fish_rgb_to_lch = NULL;
+static const Babl *fish_lch_to_rgb = NULL;
+static const Babl *fish_lch_to_rgb_u8 = NULL;
+
+
+static void
+gimp_color_select_class_init (GimpColorSelectClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpColorSelectorClass *selector_class = GIMP_COLOR_SELECTOR_CLASS (klass);
+
+ object_class->finalize = gimp_color_select_finalize;
+
+ selector_class->name = "GIMP";
+ selector_class->help_id = "gimp-colorselector-gimp";
+ selector_class->icon_name = GIMP_ICON_WILBER;
+ selector_class->set_toggles_visible = gimp_color_select_togg_visible;
+ selector_class->set_toggles_sensitive = gimp_color_select_togg_sensitive;
+ selector_class->set_color = gimp_color_select_set_color;
+ selector_class->set_channel = gimp_color_select_set_channel;
+ selector_class->set_model_visible = gimp_color_select_set_model_visible;
+ selector_class->set_config = gimp_color_select_set_config;
+
+ fish_rgb_to_lch = babl_fish (babl_format ("R'G'B'A double"),
+ babl_format ("CIE LCH(ab) double"));
+ fish_lch_to_rgb = babl_fish (babl_format ("CIE LCH(ab) double"),
+ babl_format ("R'G'B' double"));
+ fish_lch_to_rgb_u8 = babl_fish (babl_format ("CIE LCH(ab) double"),
+ babl_format ("R'G'B' u8"));
+}
+
+static void
+gimp_color_select_init (GimpColorSelect *select)
+{
+ GimpColorSelector *selector = GIMP_COLOR_SELECTOR (select);
+ GtkWidget *hbox;
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GEnumClass *model_class;
+ GEnumClass *channel_class;
+ GimpEnumDesc *enum_desc;
+ GimpColorSelectorModel model;
+ GSList *group = NULL;
+
+ /* Default values. */
+ select->z_color_fill = COLOR_SELECT_HUE;
+ select->xy_color_fill = COLOR_SELECT_SATURATION_VALUE;
+ select->drag_mode = DRAG_NONE;
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+ gtk_box_pack_start (GTK_BOX (select), hbox, TRUE, TRUE, 0);
+ gtk_widget_show (hbox);
+
+ /* The x/y component preview */
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ select->xy_color = gtk_event_box_new ();
+ gtk_event_box_set_visible_window (GTK_EVENT_BOX (select->xy_color), FALSE);
+ g_object_add_weak_pointer (G_OBJECT (select->xy_color),
+ (gpointer) &select->xy_color);
+ gtk_widget_set_size_request (select->xy_color,
+ GIMP_COLOR_SELECTOR_SIZE,
+ GIMP_COLOR_SELECTOR_SIZE);
+ gtk_widget_set_events (select->xy_color, COLOR_AREA_EVENT_MASK);
+ gtk_container_add (GTK_CONTAINER (frame), select->xy_color);
+ gtk_widget_show (select->xy_color);
+
+ g_signal_connect (select->xy_color, "size-allocate",
+ G_CALLBACK (gimp_color_select_xy_size_allocate),
+ select);
+ g_signal_connect_after (select->xy_color, "expose-event",
+ G_CALLBACK (gimp_color_select_xy_expose),
+ select);
+ g_signal_connect (select->xy_color, "event",
+ G_CALLBACK (gimp_color_select_xy_events),
+ select);
+
+#if 0
+ gimp_dnd_color_dest_add (select->xy_color, gimp_color_select_drop_color,
+ select);
+#endif
+
+ /* The z component preview */
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ select->z_color = gtk_event_box_new ();
+ gtk_event_box_set_visible_window (GTK_EVENT_BOX (select->z_color), FALSE);
+ g_object_add_weak_pointer (G_OBJECT (select->z_color),
+ (gpointer) &select->z_color);
+ gtk_widget_set_size_request (select->z_color,
+ GIMP_COLOR_SELECTOR_BAR_SIZE, -1);
+ gtk_widget_set_events (select->z_color, COLOR_AREA_EVENT_MASK);
+ gtk_container_add (GTK_CONTAINER (frame), select->z_color);
+ gtk_widget_show (select->z_color);
+
+ g_signal_connect (select->z_color, "size-allocate",
+ G_CALLBACK (gimp_color_select_z_size_allocate),
+ select);
+ g_signal_connect_after (select->z_color, "expose-event",
+ G_CALLBACK (gimp_color_select_z_expose),
+ select);
+ g_signal_connect (select->z_color, "event",
+ G_CALLBACK (gimp_color_select_z_events),
+ select);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ model_class = g_type_class_ref (GIMP_TYPE_COLOR_SELECTOR_MODEL);
+ channel_class = g_type_class_ref (GIMP_TYPE_COLOR_SELECTOR_CHANNEL);
+
+ for (model = GIMP_COLOR_SELECTOR_MODEL_RGB;
+ model <= GIMP_COLOR_SELECTOR_MODEL_HSV;
+ model++)
+ {
+ enum_desc = gimp_enum_get_desc (model_class, model);
+
+ select->toggle_box[model] = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_box_pack_start (GTK_BOX (vbox), select->toggle_box[model],
+ FALSE, FALSE, 0);
+
+ if (gimp_color_selector_get_model_visible (selector, model))
+ gtk_widget_show (select->toggle_box[model]);
+
+ /* channel toggles */
+ {
+ GimpColorSelectorChannel channel = GIMP_COLOR_SELECTOR_RED;
+ GimpColorSelectorChannel end_channel;
+
+ switch (model)
+ {
+ case GIMP_COLOR_SELECTOR_MODEL_RGB:
+ channel = GIMP_COLOR_SELECTOR_RED;
+ break;
+ case GIMP_COLOR_SELECTOR_MODEL_LCH:
+ channel = GIMP_COLOR_SELECTOR_LCH_LIGHTNESS;
+ break;
+ case GIMP_COLOR_SELECTOR_MODEL_HSV:
+ channel = GIMP_COLOR_SELECTOR_HUE;
+ break;
+ default:
+ /* Should not happen. */
+ g_return_if_reached ();
+ break;
+ }
+
+ end_channel = channel + 3;
+
+ for (; channel < end_channel; channel++)
+ {
+ GtkWidget *button;
+
+ enum_desc = gimp_enum_get_desc (channel_class, channel);
+
+ button = gtk_radio_button_new_with_mnemonic (group,
+ gettext (enum_desc->value_desc));
+ group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
+ gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (button), FALSE);
+ gtk_box_pack_start (GTK_BOX (select->toggle_box[model]), button,
+ TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ g_object_set_data (G_OBJECT (button), "channel",
+ GINT_TO_POINTER (channel));
+
+ if (channel == gimp_color_selector_get_channel (selector))
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
+
+ gimp_help_set_help_data (button, gettext (enum_desc->value_help),
+ NULL);
+
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (gimp_color_select_channel_toggled),
+ select);
+ }
+ }
+ }
+
+ g_type_class_unref (model_class);
+ g_type_class_unref (channel_class);
+}
+
+static void
+gimp_color_select_finalize (GObject *object)
+{
+ GimpColorSelect *select = GIMP_COLOR_SELECT (object);
+
+ g_clear_pointer (&select->xy_buf, g_free);
+ select->xy_width = 0;
+ select->xy_height = 0;
+ select->xy_rowstride = 0;
+
+ g_clear_pointer (&select->z_buf, g_free);
+ select->z_width = 0;
+ select->z_height = 0;
+ select->z_rowstride = 0;
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_color_select_togg_visible (GimpColorSelector *selector,
+ gboolean visible)
+{
+ GimpColorSelect *select = GIMP_COLOR_SELECT (selector);
+ gint i;
+
+ for (i = 0; i < 3; i++)
+ {
+ gtk_widget_set_visible (select->toggle_box[i], visible);
+ }
+}
+
+static void
+gimp_color_select_togg_sensitive (GimpColorSelector *selector,
+ gboolean sensitive)
+{
+ GimpColorSelect *select = GIMP_COLOR_SELECT (selector);
+ gint i;
+
+ for (i = 0; i < 3; i++)
+ {
+ gtk_widget_set_sensitive (select->toggle_box[i], sensitive);
+ }
+}
+
+static void
+gimp_color_select_set_color (GimpColorSelector *selector,
+ const GimpRGB *rgb,
+ const GimpHSV *hsv)
+{
+ GimpColorSelect *select = GIMP_COLOR_SELECT (selector);
+
+ gimp_color_select_update (select,
+ UPDATE_POS | UPDATE_XY_COLOR | UPDATE_Z_COLOR);
+}
+
+static void
+gimp_color_select_set_channel (GimpColorSelector *selector,
+ GimpColorSelectorChannel channel)
+{
+ GimpColorSelect *select = GIMP_COLOR_SELECT (selector);
+
+ switch ((ColorSelectFillType) channel)
+ {
+ case COLOR_SELECT_HUE:
+ select->z_color_fill = COLOR_SELECT_HUE;
+ select->xy_color_fill = COLOR_SELECT_SATURATION_VALUE;
+ break;
+
+ case COLOR_SELECT_SATURATION:
+ select->z_color_fill = COLOR_SELECT_SATURATION;
+ select->xy_color_fill = COLOR_SELECT_HUE_VALUE;
+ break;
+
+ case COLOR_SELECT_VALUE:
+ select->z_color_fill = COLOR_SELECT_VALUE;
+ select->xy_color_fill = COLOR_SELECT_HUE_SATURATION;
+ break;
+
+ case COLOR_SELECT_RED:
+ select->z_color_fill = COLOR_SELECT_RED;
+ select->xy_color_fill = COLOR_SELECT_GREEN_BLUE;
+ break;
+
+ case COLOR_SELECT_GREEN:
+ select->z_color_fill = COLOR_SELECT_GREEN;
+ select->xy_color_fill = COLOR_SELECT_RED_BLUE;
+ break;
+
+ case COLOR_SELECT_BLUE:
+ select->z_color_fill = COLOR_SELECT_BLUE;
+ select->xy_color_fill = COLOR_SELECT_RED_GREEN;
+ break;
+
+ case COLOR_SELECT_LCH_LIGHTNESS:
+ select->z_color_fill = COLOR_SELECT_LCH_LIGHTNESS;
+ select->xy_color_fill = COLOR_SELECT_LCH_HUE_CHROMA;
+ break;
+
+ case COLOR_SELECT_LCH_CHROMA:
+ select->z_color_fill = COLOR_SELECT_LCH_CHROMA;
+ select->xy_color_fill = COLOR_SELECT_LCH_HUE_LIGHTNESS;
+ break;
+
+ case COLOR_SELECT_LCH_HUE:
+ select->z_color_fill = COLOR_SELECT_LCH_HUE;
+ select->xy_color_fill = COLOR_SELECT_LCH_CHROMA_LIGHTNESS;
+ break;
+
+ default:
+ break;
+ }
+
+ gimp_color_select_update (select,
+ UPDATE_POS | UPDATE_Z_COLOR | UPDATE_XY_COLOR);
+}
+
+static void
+gimp_color_select_set_model_visible (GimpColorSelector *selector,
+ GimpColorSelectorModel model,
+ gboolean visible)
+{
+ GimpColorSelect *select = GIMP_COLOR_SELECT (selector);
+
+ gtk_widget_set_visible (select->toggle_box[model], visible);
+}
+
+static void
+gimp_color_select_set_config (GimpColorSelector *selector,
+ GimpColorConfig *config)
+{
+ GimpColorSelect *select = GIMP_COLOR_SELECT (selector);
+
+ if (config != select->config)
+ {
+ if (select->config)
+ {
+ g_signal_handlers_disconnect_by_func (select->config,
+ gimp_color_select_notify_config,
+ select);
+
+ gimp_color_select_destroy_transform (select);
+ }
+
+ g_set_object (&select->config, config);
+
+ if (select->config)
+ {
+ g_signal_connect (select->config, "notify",
+ G_CALLBACK (gimp_color_select_notify_config),
+ select);
+
+ gimp_color_select_notify_config (select->config, NULL, select);
+ }
+ }
+}
+
+static void
+gimp_color_select_channel_toggled (GtkWidget *widget,
+ GimpColorSelect *select)
+{
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ {
+ GimpColorSelector *selector = GIMP_COLOR_SELECTOR (select);
+ GimpColorSelectorChannel channel;
+
+ channel = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
+ "channel"));
+
+ gimp_color_selector_set_channel (selector, channel);
+ }
+}
+
+static void
+gimp_color_select_update (GimpColorSelect *select,
+ ColorSelectUpdateType update)
+{
+ if (update & UPDATE_POS)
+ gimp_color_select_update_pos (select);
+
+ if (update & UPDATE_VALUES)
+ gimp_color_select_update_values (select);
+
+ if (update & UPDATE_XY_COLOR)
+ {
+ select->xy_needs_render = TRUE;
+ gtk_widget_queue_draw (select->xy_color);
+ }
+
+ if (update & UPDATE_Z_COLOR)
+ {
+ select->z_needs_render = TRUE;
+ gtk_widget_queue_draw (select->z_color);
+ }
+
+ if (update & UPDATE_CALLER)
+ gimp_color_selector_color_changed (GIMP_COLOR_SELECTOR (select));
+}
+
+static void
+gimp_color_select_update_values (GimpColorSelect *select)
+{
+ GimpColorSelector *selector = GIMP_COLOR_SELECTOR (select);
+ GimpLCH lch;
+
+ switch (select->z_color_fill)
+ {
+ case COLOR_SELECT_RED:
+ selector->rgb.g = select->pos[0];
+ selector->rgb.b = select->pos[1];
+ selector->rgb.r = select->pos[2];
+ break;
+ case COLOR_SELECT_GREEN:
+ selector->rgb.r = select->pos[0];
+ selector->rgb.b = select->pos[1];
+ selector->rgb.g = select->pos[2];
+ break;
+ case COLOR_SELECT_BLUE:
+ selector->rgb.r = select->pos[0];
+ selector->rgb.g = select->pos[1];
+ selector->rgb.b = select->pos[2];
+ break;
+
+ case COLOR_SELECT_HUE:
+ selector->hsv.s = select->pos[0];
+ selector->hsv.v = select->pos[1];
+ selector->hsv.h = select->pos[2];
+ break;
+ case COLOR_SELECT_SATURATION:
+ selector->hsv.h = select->pos[0];
+ selector->hsv.v = select->pos[1];
+ selector->hsv.s = select->pos[2];
+ break;
+ case COLOR_SELECT_VALUE:
+ selector->hsv.h = select->pos[0];
+ selector->hsv.s = select->pos[1];
+ selector->hsv.v = select->pos[2];
+ break;
+
+ case COLOR_SELECT_LCH_LIGHTNESS:
+ lch.h = select->pos[0] * 360;
+ lch.c = select->pos[1] * 200;
+ lch.l = select->pos[2] * 100;
+ break;
+ case COLOR_SELECT_LCH_CHROMA:
+ lch.h = select->pos[0] * 360;
+ lch.l = select->pos[1] * 100;
+ lch.c = select->pos[2] * 200;
+ break;
+ case COLOR_SELECT_LCH_HUE:
+ lch.c = select->pos[0] * 200;
+ lch.l = select->pos[1] * 100;
+ lch.h = select->pos[2] * 360;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (select->z_color_fill)
+ {
+ case COLOR_SELECT_RED:
+ case COLOR_SELECT_GREEN:
+ case COLOR_SELECT_BLUE:
+ gimp_rgb_to_hsv (&selector->rgb, &selector->hsv);
+ break;
+
+ case COLOR_SELECT_HUE:
+ case COLOR_SELECT_SATURATION:
+ case COLOR_SELECT_VALUE:
+ gimp_hsv_to_rgb (&selector->hsv, &selector->rgb);
+ break;
+
+ case COLOR_SELECT_LCH_LIGHTNESS:
+ case COLOR_SELECT_LCH_CHROMA:
+ case COLOR_SELECT_LCH_HUE:
+ babl_process (fish_lch_to_rgb, &lch, &selector->rgb, 1);
+ gimp_rgb_to_hsv (&selector->rgb, &selector->hsv);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void
+gimp_color_select_update_pos (GimpColorSelect *select)
+{
+ GimpColorSelector *selector = GIMP_COLOR_SELECTOR (select);
+ GimpLCH lch;
+
+ babl_process (fish_rgb_to_lch, &selector->rgb, &lch, 1);
+
+ switch (select->z_color_fill)
+ {
+ case COLOR_SELECT_RED:
+ select->pos[0] = CLAMP (selector->rgb.g, 0.0, 1.0);
+ select->pos[1] = CLAMP (selector->rgb.b, 0.0, 1.0);
+ select->pos[2] = CLAMP (selector->rgb.r, 0.0, 1.0);
+ break;
+ case COLOR_SELECT_GREEN:
+ select->pos[0] = CLAMP (selector->rgb.r, 0.0, 1.0);
+ select->pos[1] = CLAMP (selector->rgb.b, 0.0, 1.0);
+ select->pos[2] = CLAMP (selector->rgb.g, 0.0, 1.0);
+ break;
+ case COLOR_SELECT_BLUE:
+ select->pos[0] = CLAMP (selector->rgb.r, 0.0, 1.0);
+ select->pos[1] = CLAMP (selector->rgb.g, 0.0, 1.0);
+ select->pos[2] = CLAMP (selector->rgb.b, 0.0, 1.0);
+ break;
+
+ case COLOR_SELECT_HUE:
+ select->pos[0] = CLAMP (selector->hsv.s, 0.0, 1.0);
+ select->pos[1] = CLAMP (selector->hsv.v, 0.0, 1.0);
+ select->pos[2] = CLAMP (selector->hsv.h, 0.0, 1.0);
+ break;
+ case COLOR_SELECT_SATURATION:
+ select->pos[0] = CLAMP (selector->hsv.h, 0.0, 1.0);
+ select->pos[1] = CLAMP (selector->hsv.v, 0.0, 1.0);
+ select->pos[2] = CLAMP (selector->hsv.s, 0.0, 1.0);
+ break;
+ case COLOR_SELECT_VALUE:
+ select->pos[0] = CLAMP (selector->hsv.h, 0.0, 1.0);
+ select->pos[1] = CLAMP (selector->hsv.s, 0.0, 1.0);
+ select->pos[2] = CLAMP (selector->hsv.v, 0.0, 1.0);
+ break;
+
+ case COLOR_SELECT_LCH_LIGHTNESS:
+ select->pos[0] = CLAMP (lch.h / 360, 0.0, 1.0);
+ select->pos[1] = CLAMP (lch.c / 200, 0.0, 1.0);
+ select->pos[2] = CLAMP (lch.l / 100, 0.0, 1.0);
+ break;
+ case COLOR_SELECT_LCH_CHROMA:
+ select->pos[0] = CLAMP (lch.h / 360, 0.0, 1.0);
+ select->pos[1] = CLAMP (lch.l / 100, 0.0, 1.0);
+ select->pos[2] = CLAMP (lch.c / 200, 0.0, 1.0);
+ break;
+ case COLOR_SELECT_LCH_HUE:
+ select->pos[0] = CLAMP (lch.c / 200, 0.0, 1.0);
+ select->pos[1] = CLAMP (lch.l / 100, 0.0, 1.0);
+ select->pos[2] = CLAMP (lch.h / 360, 0.0, 1.0);
+ break;
+
+ default:
+ break;
+ }
+}
+
+#if 0
+static void
+gimp_color_select_drop_color (GtkWidget *widget,
+ gint x,
+ gint y,
+ const GimpRGB *color,
+ gpointer data)
+{
+ GimpColorSelect *select = GIMP_COLOR_SELECT (data);
+
+ select->rgb = *color;
+
+ gimp_color_select_update_hsv_values (select);
+ gimp_color_select_update_lch_values (select);
+
+ gimp_color_select_update (select,
+ UPDATE_POS | UPDATE_XY_COLOR | UPDATE_Z_COLOR |
+ UPDATE_CALLER);
+}
+#endif
+
+static void
+gimp_color_select_xy_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation,
+ GimpColorSelect *select)
+{
+ if (allocation->width != select->xy_width ||
+ allocation->height != select->xy_height)
+ {
+ select->xy_width = allocation->width;
+ select->xy_height = allocation->height;
+
+ select->xy_rowstride = (select->xy_width * 3 + 3) & ~3;
+
+ g_free (select->xy_buf);
+ select->xy_buf = g_new (guchar, select->xy_rowstride * select->xy_height);
+
+ select->xy_needs_render = TRUE;
+ }
+
+ gimp_color_select_update (select, UPDATE_XY_COLOR);
+}
+
+static gboolean
+gimp_color_select_xy_expose (GtkWidget *widget,
+ GdkEventExpose *event,
+ GimpColorSelect *select)
+{
+ GtkAllocation allocation;
+ cairo_t *cr;
+ GdkPixbuf *pixbuf;
+ gint x, y;
+
+ if (! select->xy_buf)
+ return FALSE;
+
+ if (select->xy_needs_render)
+ {
+ GimpColorSelector *selector = GIMP_COLOR_SELECTOR (select);
+
+ gimp_color_select_render (select->xy_color,
+ select->xy_buf,
+ select->xy_width,
+ select->xy_height,
+ select->xy_rowstride,
+ select->xy_color_fill,
+ &selector->hsv,
+ &selector->rgb,
+ select->oog_color);
+ select->xy_needs_render = FALSE;
+ }
+
+ if (! select->transform)
+ gimp_color_select_create_transform (select);
+
+ if (select->transform)
+ {
+ const Babl *format = babl_format ("R'G'B' u8");
+ guchar *buf = g_new (guchar,
+ select->xy_rowstride * select->xy_height);
+ guchar *src = select->xy_buf;
+ guchar *dest = buf;
+ gint i;
+
+ for (i = 0; i < select->xy_height; i++)
+ {
+ gimp_color_transform_process_pixels (select->transform,
+ format, src,
+ format, dest,
+ select->xy_width);
+
+ src += select->xy_rowstride;
+ dest += select->xy_rowstride;
+ }
+
+ pixbuf = gdk_pixbuf_new_from_data (buf,
+ GDK_COLORSPACE_RGB,
+ FALSE,
+ 8,
+ select->xy_width,
+ select->xy_height,
+ select->xy_rowstride,
+ (GdkPixbufDestroyNotify) g_free, NULL);
+ }
+ else
+ {
+ pixbuf = gdk_pixbuf_new_from_data (select->xy_buf,
+ GDK_COLORSPACE_RGB,
+ FALSE,
+ 8,
+ select->xy_width,
+ select->xy_height,
+ select->xy_rowstride,
+ NULL, NULL);
+ }
+
+ gtk_widget_get_allocation (select->xy_color, &allocation);
+
+ cr = gdk_cairo_create (gtk_widget_get_window (widget));
+ gdk_cairo_region (cr, event->region);
+ cairo_clip (cr);
+
+ cairo_translate (cr, allocation.x, allocation.y);
+
+ gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
+ g_object_unref (pixbuf);
+ cairo_paint (cr);
+
+ x = (allocation.width - 1) * select->pos[0];
+ y = (allocation.height - 1) - (allocation.height - 1) * select->pos[1];
+
+ cairo_move_to (cr, 0, y + 0.5);
+ cairo_line_to (cr, allocation.width, y + 0.5);
+
+ cairo_move_to (cr, x + 0.5, 0);
+ cairo_line_to (cr, x + 0.5, allocation.height);
+
+ cairo_set_line_width (cr, 3.0);
+ cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.6);
+ cairo_stroke_preserve (cr);
+
+ cairo_set_line_width (cr, 1.0);
+ cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.8);
+ cairo_stroke (cr);
+
+ cairo_destroy (cr);
+
+ return TRUE;
+}
+
+static gboolean
+gimp_color_select_xy_events (GtkWidget *widget,
+ GdkEvent *event,
+ GimpColorSelect *select)
+{
+ GtkAllocation allocation;
+ gdouble x, y;
+
+ switch (event->type)
+ {
+ case GDK_BUTTON_PRESS:
+ {
+ GdkEventButton *bevent = (GdkEventButton *) event;
+
+ if (select->drag_mode != DRAG_NONE || bevent->button != 1)
+ return FALSE;
+
+ x = bevent->x;
+ y = bevent->y;
+
+ gtk_grab_add (widget);
+ select->drag_mode = DRAG_XY;
+ }
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ {
+ GdkEventButton *bevent = (GdkEventButton *) event;
+
+ if (select->drag_mode != DRAG_XY || bevent->button != 1)
+ return FALSE;
+
+ x = bevent->x;
+ y = bevent->y;
+
+ gtk_grab_remove (widget);
+ select->drag_mode = DRAG_NONE;
+ }
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ {
+ GdkEventMotion *mevent = (GdkEventMotion *) event;
+
+ if (select->drag_mode != DRAG_XY)
+ return FALSE;
+
+ x = mevent->x;
+ y = mevent->y;
+ }
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ gtk_widget_get_allocation (select->xy_color, &allocation);
+
+ if (allocation.width > 1 && allocation.height > 1)
+ {
+ select->pos[0] = x / (allocation.width - 1);
+ select->pos[1] = 1.0 - y / (allocation.height - 1);
+ }
+
+ select->pos[0] = CLAMP (select->pos[0], 0.0, 1.0);
+ select->pos[1] = CLAMP (select->pos[1], 0.0, 1.0);
+
+ gtk_widget_queue_draw (select->xy_color);
+
+ gimp_color_select_update (select, UPDATE_VALUES | UPDATE_CALLER);
+
+ /* Ask for more motion events in case the event was a hint */
+ gdk_event_request_motions ((GdkEventMotion *) event);
+
+ return TRUE;
+}
+
+static void
+gimp_color_select_z_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation,
+ GimpColorSelect *select)
+{
+ if (allocation->width != select->z_width ||
+ allocation->height != select->z_height)
+ {
+ select->z_width = allocation->width;
+ select->z_height = allocation->height;
+
+ select->z_rowstride = (select->z_width * 3 + 3) & ~3;
+
+ g_free (select->z_buf);
+ select->z_buf = g_new (guchar, select->z_rowstride * select->z_height);
+
+ select->z_needs_render = TRUE;
+ }
+
+ gimp_color_select_update (select, UPDATE_Z_COLOR);
+}
+
+static gboolean
+gimp_color_select_z_expose (GtkWidget *widget,
+ GdkEventExpose *event,
+ GimpColorSelect *select)
+{
+ GtkAllocation allocation;
+ cairo_t *cr;
+ GdkPixbuf *pixbuf;
+ gint y;
+
+ if (! select->z_buf)
+ return FALSE;
+
+ if (select->z_needs_render)
+ {
+ GimpColorSelector *selector = GIMP_COLOR_SELECTOR (select);
+
+ gimp_color_select_render (select->z_color,
+ select->z_buf,
+ select->z_width,
+ select->z_height,
+ select->z_rowstride,
+ select->z_color_fill,
+ &selector->hsv,
+ &selector->rgb,
+ select->oog_color);
+ select->z_needs_render = FALSE;
+ }
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ cr = gdk_cairo_create (gtk_widget_get_window (widget));
+ gdk_cairo_region (cr, event->region);
+ cairo_clip (cr);
+
+ if (! select->transform)
+ gimp_color_select_create_transform (select);
+
+ if (select->transform)
+ {
+ const Babl *format = babl_format ("R'G'B' u8");
+ guchar *buf = g_new (guchar,
+ select->z_rowstride * select->z_height);
+ guchar *src = select->z_buf;
+ guchar *dest = buf;
+ gint i;
+
+ for (i = 0; i < select->z_height; i++)
+ {
+ gimp_color_transform_process_pixels (select->transform,
+ format, src,
+ format, dest,
+ select->z_width);
+
+ src += select->z_rowstride;
+ dest += select->z_rowstride;
+ }
+
+ pixbuf = gdk_pixbuf_new_from_data (buf,
+ GDK_COLORSPACE_RGB,
+ FALSE,
+ 8,
+ select->z_width,
+ select->z_height,
+ select->z_rowstride,
+ (GdkPixbufDestroyNotify) g_free, NULL);
+ }
+ else
+ {
+ pixbuf = gdk_pixbuf_new_from_data (select->z_buf,
+ GDK_COLORSPACE_RGB,
+ FALSE,
+ 8,
+ select->z_width,
+ select->z_height,
+ select->z_rowstride,
+ NULL, NULL);
+ }
+
+ cairo_translate (cr, allocation.x, allocation.y);
+
+ gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
+ g_object_unref (pixbuf);
+ cairo_paint (cr);
+
+ y = (allocation.height - 1) - (allocation.height - 1) * select->pos[2];
+
+ cairo_move_to (cr, 0, y + 0.5);
+ cairo_line_to (cr, allocation.width, y + 0.5);
+
+ cairo_set_line_width (cr, 3.0);
+ cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.6);
+ cairo_stroke_preserve (cr);
+
+ cairo_set_line_width (cr, 1.0);
+ cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.8);
+ cairo_stroke (cr);
+
+ cairo_destroy (cr);
+
+ return TRUE;
+}
+
+static gboolean
+gimp_color_select_z_events (GtkWidget *widget,
+ GdkEvent *event,
+ GimpColorSelect *select)
+{
+ GtkAllocation allocation;
+ gdouble z;
+
+ switch (event->type)
+ {
+ case GDK_BUTTON_PRESS:
+ {
+ GdkEventButton *bevent = (GdkEventButton *) event;
+
+ if (select->drag_mode != DRAG_NONE || bevent->button != 1)
+ return FALSE;
+
+ z = bevent->y;
+
+ gtk_grab_add (widget);
+ select->drag_mode = DRAG_Z;
+ }
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ {
+ GdkEventButton *bevent = (GdkEventButton *) event;
+
+ if (select->drag_mode != DRAG_Z || bevent->button != 1)
+ return FALSE;
+
+ z = bevent->y;
+
+ gtk_grab_remove (widget);
+ select->drag_mode = DRAG_NONE;
+ }
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ {
+ GdkEventMotion *mevent = (GdkEventMotion *) event;
+
+ if (select->drag_mode != DRAG_Z)
+ return FALSE;
+
+ z = mevent->y;
+ }
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ gtk_widget_get_allocation (select->z_color, &allocation);
+
+ if (allocation.height > 1)
+ select->pos[2] = 1.0 - z / (allocation.height - 1);
+
+ select->pos[2] = CLAMP (select->pos[2], 0.0, 1.0);
+
+ gtk_widget_queue_draw (select->z_color);
+
+ gimp_color_select_update (select,
+ UPDATE_VALUES | UPDATE_XY_COLOR | UPDATE_CALLER);
+
+ /* Ask for more motion events in case the event was a hint */
+ gdk_event_request_motions ((GdkEventMotion *) event);
+
+ return TRUE;
+}
+
+static void
+gimp_color_select_render (GtkWidget *preview,
+ guchar *buf,
+ gint width,
+ gint height,
+ gint rowstride,
+ ColorSelectFillType fill_type,
+ const GimpHSV *hsv,
+ const GimpRGB *rgb,
+ const guchar *oog_color)
+{
+ ColorSelectFill csf;
+
+ csf.width = width;
+ csf.height = height;
+ csf.hsv = *hsv;
+ csf.rgb = *rgb;
+ csf.render_line = render_funcs[fill_type];
+
+ csf.oog_color[0] = oog_color[0];
+ csf.oog_color[1] = oog_color[1];
+ csf.oog_color[2] = oog_color[2];
+
+ babl_process (fish_rgb_to_lch, rgb, &csf.lch, 1);
+
+ for (csf.y = 0; csf.y < csf.height; csf.y++)
+ {
+ csf.buffer = buf;
+
+ csf.render_line (&csf);
+
+ buf += rowstride;
+ }
+}
+
+static void
+color_select_render_red (ColorSelectFill *csf)
+{
+ guchar *p = csf->buffer;
+ gint i, r;
+
+ r = (csf->height - csf->y + 1) * 255 / csf->height;
+ r = CLAMP (r, 0, 255);
+
+ for (i = 0; i < csf->width; i++)
+ {
+ *p++ = r;
+ *p++ = 0;
+ *p++ = 0;
+ }
+}
+
+static void
+color_select_render_green (ColorSelectFill *csf)
+{
+ guchar *p = csf->buffer;
+ gint i, g;
+
+ g = (csf->height - csf->y + 1) * 255 / csf->height;
+ g = CLAMP (g, 0, 255);
+
+ for (i = 0; i < csf->width; i++)
+ {
+ *p++ = 0;
+ *p++ = g;
+ *p++ = 0;
+ }
+}
+
+static void
+color_select_render_blue (ColorSelectFill *csf)
+{
+ guchar *p = csf->buffer;
+ gint i, b;
+
+ b = (csf->height - csf->y + 1) * 255 / csf->height;
+ b = CLAMP (b, 0, 255);
+
+ for (i = 0; i < csf->width; i++)
+ {
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = b;
+ }
+}
+
+static void
+color_select_render_hue (ColorSelectFill *csf)
+{
+ guchar *p = csf->buffer;
+ gfloat h, f;
+ gint r, g, b;
+ gint i;
+
+ h = csf->y * 360.0 / csf->height;
+ h = CLAMP (360 - h, 0, 360);
+
+ h /= 60;
+ f = (h - (int) h) * 255;
+
+ r = g = b = 0;
+
+ switch ((int) h)
+ {
+ case 0:
+ r = 255;
+ g = f;
+ b = 0;
+ break;
+ case 1:
+ r = 255 - f;
+ g = 255;
+ b = 0;
+ break;
+ case 2:
+ r = 0;
+ g = 255;
+ b = f;
+ break;
+ case 3:
+ r = 0;
+ g = 255 - f;
+ b = 255;
+ break;
+ case 4:
+ r = f;
+ g = 0;
+ b = 255;
+ break;
+ case 5:
+ r = 255;
+ g = 0;
+ b = 255 - f;
+ break;
+ }
+
+ for (i = 0; i < csf->width; i++)
+ {
+ *p++ = r;
+ *p++ = g;
+ *p++ = b;
+ }
+}
+
+static void
+color_select_render_saturation (ColorSelectFill *csf)
+{
+ guchar *p = csf->buffer;
+ gint s;
+ gint i;
+
+ s = csf->y * 255 / csf->height;
+ s = CLAMP (s, 0, 255);
+
+ s = 255 - s;
+
+ for (i = 0; i < csf->width; i++)
+ {
+ *p++ = s;
+ *p++ = s;
+ *p++ = s;
+ }
+}
+
+static void
+color_select_render_value (ColorSelectFill *csf)
+{
+ guchar *p = csf->buffer;
+ gint v;
+ gint i;
+
+ v = csf->y * 255 / csf->height;
+ v = CLAMP (v, 0, 255);
+
+ v = 255 - v;
+
+ for (i = 0; i < csf->width; i++)
+ {
+ *p++ = v;
+ *p++ = v;
+ *p++ = v;
+ }
+}
+
+static void
+color_select_render_lch_lightness (ColorSelectFill *csf)
+{
+ guchar *p = csf->buffer;
+ GimpLCH lch = { 0.0, 0.0, 0.0, 1.0 };
+ guchar rgb[3];
+ gint i;
+
+ lch.l = (csf->height - 1 - csf->y) * 100.0 / csf->height;
+ babl_process (fish_lch_to_rgb_u8, &lch, &rgb, 1);
+
+ for (i = 0; i < csf->width; i++)
+ {
+ *p++ = rgb[0];
+ *p++ = rgb[1];
+ *p++ = rgb[2];
+ }
+}
+
+static void
+color_select_render_lch_chroma (ColorSelectFill *csf)
+{
+ guchar *p = csf->buffer;
+ GimpLCH lch = { 80.0, 0.0, 0.0, 1.0 };
+ guchar rgb[3];
+ gint i;
+
+ lch.c = (csf->height - 1 - csf->y) * 200.0 / csf->height ;
+ babl_process (fish_lch_to_rgb_u8, &lch, &rgb, 1);
+
+ for (i = 0; i < csf->width; i++)
+ {
+ *p++ = rgb[0];
+ *p++ = rgb[1];
+ *p++ = rgb[2];
+ }
+}
+
+static void
+color_select_render_lch_hue (ColorSelectFill *csf)
+{
+ guchar *p = csf->buffer;
+ GimpLCH lch = { 80.0, 200.0, 0.0, 1.0 };
+ guchar rgb[3];
+ gint i;
+
+ lch.h = (csf->height - 1 - csf->y) * 360.0 / csf->height;
+ babl_process (fish_lch_to_rgb_u8, &lch, &rgb, 1);
+
+ for (i = 0; i < csf->width; i++)
+ {
+ *p++ = rgb[0];
+ *p++ = rgb[1];
+ *p++ = rgb[2];
+ }
+}
+
+static void
+color_select_render_red_green (ColorSelectFill *csf)
+{
+ guchar *p = csf->buffer;
+ gfloat r = 0;
+ gfloat g = 0;
+ gfloat b = 0;
+ gfloat dr = 0;
+ gint i;
+
+ b = csf->rgb.b * 255.0;
+
+ if (b < 0.0 || b > 255.0)
+ {
+ r = csf->oog_color[0];
+ g = csf->oog_color[1];
+ b = csf->oog_color[2];
+ }
+ else
+ {
+ g = (csf->height - csf->y + 1) * 255.0 / csf->height;
+
+ dr = 255.0 / csf->width;
+ }
+
+ for (i = 0; i < csf->width; i++)
+ {
+ *p++ = r;
+ *p++ = g;
+ *p++ = b;
+
+ r += dr;
+ }
+}
+
+static void
+color_select_render_red_blue (ColorSelectFill *csf)
+{
+ guchar *p = csf->buffer;
+ gfloat r = 0;
+ gfloat g = 0;
+ gfloat b = 0;
+ gfloat dr = 0;
+ gint i;
+
+ g = csf->rgb.g * 255.0;
+
+ if (g < 0.0 || g > 255.0)
+ {
+ r = csf->oog_color[0];
+ g = csf->oog_color[1];
+ b = csf->oog_color[2];
+ }
+ else
+ {
+ b = (csf->height - csf->y + 1) * 255.0 / csf->height;
+
+ dr = 255.0 / csf->width;
+ }
+
+ for (i = 0; i < csf->width; i++)
+ {
+ *p++ = r;
+ *p++ = g;
+ *p++ = b;
+
+ r += dr;
+ }
+}
+
+static void
+color_select_render_green_blue (ColorSelectFill *csf)
+{
+ guchar *p = csf->buffer;
+ gfloat r = 0;
+ gfloat g = 0;
+ gfloat b = 0;
+ gfloat dg = 0;
+ gint i;
+
+ r = csf->rgb.r * 255.0;
+
+ if (r < 0.0 || r > 255.0)
+ {
+ r = csf->oog_color[0];
+ g = csf->oog_color[1];
+ b = csf->oog_color[2];
+ }
+ else
+ {
+ b = (csf->height - csf->y + 1) * 255.0 / csf->height;
+
+ dg = 255.0 / csf->width;
+ }
+
+ for (i = 0; i < csf->width; i++)
+ {
+ *p++ = r;
+ *p++ = g;
+ *p++ = b;
+
+ g += dg;
+ }
+}
+
+static void
+color_select_render_hue_saturation (ColorSelectFill *csf)
+{
+ guchar *p = csf->buffer;
+ gfloat h, dh, s, v;
+ gint f;
+ gint i;
+
+ v = csf->hsv.v;
+
+ s = (gfloat) csf->y / csf->height;
+ s = CLAMP (s, 0.0, 1.0);
+ s = 1.0 - s;
+
+ h = 0;
+ dh = 360.0 / csf->width;
+
+ for (i = 0; i < csf->width; i++)
+ {
+ gfloat r, g, b;
+
+ f = ((h / 60) - (int) (h / 60)) * 255;
+
+ switch ((int) (h / 60))
+ {
+ default:
+ case 0:
+ r = v * 255;
+ g = v * (255 - (s * (255 - f)));
+ b = v * 255 * (1 - s);
+ break;
+ case 1:
+ r = v * (255 - s * f);
+ g = v * 255;
+ b = v * 255 * (1 - s);
+ break;
+ case 2:
+ r = v * 255 * (1 - s);
+ g = v *255;
+ b = v * (255 - (s * (255 - f)));
+ break;
+ case 3:
+ r = v * 255 * (1 - s);
+ g = v * (255 - s * f);
+ b = v * 255;
+ break;
+ case 4:
+ r = v * (255 - (s * (255 - f)));
+ g = v * (255 * (1 - s));
+ b = v * 255;
+ break;
+ case 5:
+ r = v * 255;
+ g = v * 255 * (1 - s);
+ b = v * (255 - s * f);
+ break;
+ }
+
+ if (r < 0.0 || r > 255.0 ||
+ g < 0.0 || g > 255.0 ||
+ b < 0.0 || b > 255.0)
+ {
+ *p++ = csf->oog_color[0];
+ *p++ = csf->oog_color[1];
+ *p++ = csf->oog_color[2];
+ }
+ else
+ {
+ *p++ = r;
+ *p++ = g;
+ *p++ = b;
+ }
+
+ h += dh;
+ }
+}
+
+static void
+color_select_render_hue_value (ColorSelectFill *csf)
+{
+ guchar *p = csf->buffer;
+ gfloat h, dh, s, v;
+ gint f;
+ gint i;
+
+ s = csf->hsv.s;
+
+ v = (gfloat) csf->y / csf->height;
+ v = CLAMP (v, 0.0, 1.0);
+ v = 1.0 - v;
+
+ h = 0;
+ dh = 360.0 / csf->width;
+
+ for (i = 0; i < csf->width; i++)
+ {
+ gfloat r, g, b;
+
+ f = ((h / 60) - (int) (h / 60)) * 255;
+
+ switch ((int) (h / 60))
+ {
+ default:
+ case 0:
+ r = v * 255;
+ g = v * (255 - (s * (255 - f)));
+ b = v * 255 * (1 - s);
+ break;
+ case 1:
+ r = v * (255 - s * f);
+ g = v * 255;
+ b = v * 255 * (1 - s);
+ break;
+ case 2:
+ r = v * 255 * (1 - s);
+ g = v *255;
+ b = v * (255 - (s * (255 - f)));
+ break;
+ case 3:
+ r = v * 255 * (1 - s);
+ g = v * (255 - s * f);
+ b = v * 255;
+ break;
+ case 4:
+ r = v * (255 - (s * (255 - f)));
+ g = v * (255 * (1 - s));
+ b = v * 255;
+ break;
+ case 5:
+ r = v * 255;
+ g = v * 255 * (1 - s);
+ b = v * (255 - s * f);
+ break;
+ }
+
+ if (r < 0.0 || r > 255.0 ||
+ g < 0.0 || g > 255.0 ||
+ b < 0.0 || b > 255.0)
+ {
+ *p++ = csf->oog_color[0];
+ *p++ = csf->oog_color[1];
+ *p++ = csf->oog_color[2];
+ }
+ else
+ {
+ *p++ = r;
+ *p++ = g;
+ *p++ = b;
+ }
+
+ h += dh;
+ }
+}
+
+static void
+color_select_render_saturation_value (ColorSelectFill *csf)
+{
+ guchar *p = csf->buffer;
+ gfloat h, s, ds, v;
+ gint f;
+ gint i;
+
+ h = (gfloat) csf->hsv.h * 360.0;
+ if (h >= 360)
+ h -= 360;
+ h /= 60;
+ f = (h - (gint) h) * 255;
+
+ v = (gfloat) csf->y / csf->height;
+ v = CLAMP (v, 0.0, 1.0);
+ v = 1.0 - v;
+
+ s = 0;
+ ds = 1.0 / csf->width;
+
+ switch ((gint) h)
+ {
+ case 0:
+ for (i = 0; i < csf->width; i++)
+ {
+ *p++ = v * 255;
+ *p++ = v * (255 - (s * (255 - f)));
+ *p++ = v * 255 * (1 - s);
+
+ s += ds;
+ }
+ break;
+ case 1:
+ for (i = 0; i < csf->width; i++)
+ {
+ *p++ = v * (255 - s * f);
+ *p++ = v * 255;
+ *p++ = v * 255 * (1 - s);
+
+ s += ds;
+ }
+ break;
+ case 2:
+ for (i = 0; i < csf->width; i++)
+ {
+ *p++ = v * 255 * (1 - s);
+ *p++ = v *255;
+ *p++ = v * (255 - (s * (255 - f)));
+
+ s += ds;
+ }
+ break;
+ case 3:
+ for (i = 0; i < csf->width; i++)
+ {
+ *p++ = v * 255 * (1 - s);
+ *p++ = v * (255 - s * f);
+ *p++ = v * 255;
+
+ s += ds;
+ }
+ break;
+ case 4:
+ for (i = 0; i < csf->width; i++)
+ {
+ *p++ = v * (255 - (s * (255 - f)));
+ *p++ = v * (255 * (1 - s));
+ *p++ = v * 255;
+
+ s += ds;
+ }
+ break;
+ case 5:
+ for (i = 0; i < csf->width; i++)
+ {
+ *p++ = v * 255;
+ *p++ = v * 255 * (1 - s);
+ *p++ = v * (255 - s * f);
+
+ s += ds;
+ }
+ break;
+ }
+}
+
+static void
+color_select_render_lch_chroma_lightness (ColorSelectFill *csf)
+{
+ guchar *p = csf->buffer;
+ GimpLCH lch;
+ gint i;
+
+ lch.l = (csf->height - 1 - csf->y) * 100.0 / csf->height;
+ lch.h = csf->lch.h;
+
+ for (i = 0; i < csf->width; i++)
+ {
+ GimpRGB rgb;
+
+ lch.c = i * 200.0 / csf->width;
+
+ babl_process (fish_lch_to_rgb, &lch, &rgb, 1);
+
+ if (rgb.r < 0.0 || rgb.r > 1.0 ||
+ rgb.g < 0.0 || rgb.g > 1.0 ||
+ rgb.b < 0.0 || rgb.b > 1.0)
+ {
+ p[0] = csf->oog_color[0];
+ p[1] = csf->oog_color[1];
+ p[2] = csf->oog_color[2];
+ }
+ else
+ {
+ gimp_rgb_get_uchar (&rgb, p, p + 1, p + 2);
+ }
+
+ p += 3;
+ }
+}
+
+static void
+color_select_render_lch_hue_lightness (ColorSelectFill *csf)
+{
+ guchar *p = csf->buffer;
+ GimpLCH lch;
+ gint i;
+
+ lch.l = (csf->height - 1 - csf->y) * 100.0 / csf->height;
+ lch.c = csf->lch.c;
+
+ for (i = 0; i < csf->width; i++)
+ {
+ GimpRGB rgb;
+
+ lch.h = i * 360.0 / csf->width;
+
+ babl_process (fish_lch_to_rgb, &lch, &rgb, 1);
+
+ if (rgb.r < 0.0 || rgb.r > 1.0 ||
+ rgb.g < 0.0 || rgb.g > 1.0 ||
+ rgb.b < 0.0 || rgb.b > 1.0)
+ {
+ p[0] = csf->oog_color[0];
+ p[1] = csf->oog_color[1];
+ p[2] = csf->oog_color[2];
+ }
+ else
+ {
+ gimp_rgb_get_uchar (&rgb, p, p + 1, p + 2);
+ }
+
+ p += 3;
+ }
+}
+
+static void
+color_select_render_lch_hue_chroma (ColorSelectFill *csf)
+{
+ guchar *p = csf->buffer;
+ GimpLCH lch;
+ gint i;
+
+ lch.l = csf->lch.l;
+ lch.c = (csf->height - 1 - csf->y) * 200.0 / csf->height;
+
+ for (i = 0; i < csf->width; i++)
+ {
+ GimpRGB rgb;
+
+ lch.h = i * 360.0 / csf->width;
+
+ babl_process (fish_lch_to_rgb, &lch, &rgb, 1);
+
+ if (rgb.r < 0.0 || rgb.r > 1.0 ||
+ rgb.g < 0.0 || rgb.g > 1.0 ||
+ rgb.b < 0.0 || rgb.b > 1.0)
+ {
+ p[0] = csf->oog_color[0];
+ p[1] = csf->oog_color[1];
+ p[2] = csf->oog_color[2];
+ }
+ else
+ {
+ gimp_rgb_get_uchar (&rgb, p, p + 1, p + 2);
+ }
+
+ p += 3;
+ }
+}
+
+static void
+gimp_color_select_create_transform (GimpColorSelect *select)
+{
+ if (select->config)
+ {
+ static GimpColorProfile *profile = NULL;
+
+ const Babl *format = babl_format ("cairo-RGB24");
+
+ if (G_UNLIKELY (! profile))
+ profile = gimp_color_profile_new_rgb_srgb ();
+
+ select->transform = gimp_widget_get_color_transform (GTK_WIDGET (select),
+ select->config,
+ profile,
+ format,
+ format);
+ }
+}
+
+static void
+gimp_color_select_destroy_transform (GimpColorSelect *select)
+{
+ if (select->transform)
+ {
+ g_object_unref (select->transform);
+ select->transform = NULL;
+ }
+
+ gtk_widget_queue_draw (select->xy_color);
+ gtk_widget_queue_draw (select->z_color);
+}
+
+static void
+gimp_color_select_notify_config (GimpColorConfig *config,
+ const GParamSpec *pspec,
+ GimpColorSelect *select)
+{
+ gimp_color_select_destroy_transform (select);
+
+ gimp_rgb_get_uchar (&config->out_of_gamut_color,
+ select->oog_color,
+ select->oog_color + 1,
+ select->oog_color + 2);
+ select->xy_needs_render = TRUE;
+ select->z_needs_render = TRUE;
+}
diff --git a/libgimpwidgets/gimpcolorselect.h b/libgimpwidgets/gimpcolorselect.h
new file mode 100644
index 0000000..a73d37c
--- /dev/null
+++ b/libgimpwidgets/gimpcolorselect.h
@@ -0,0 +1,41 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcolorselect.h
+ * Copyright (C) 2002 Michael Natterer <mitch@gimp.org>
+ *
+ * based on color_notebook module
+ * Copyright (C) 1998 Austin Donnelly <austin@greenend.org.uk>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_COLOR_SELECT_H__
+#define __GIMP_COLOR_SELECT_H__
+
+G_BEGIN_DECLS
+
+
+#define GIMP_TYPE_COLOR_SELECT (gimp_color_select_get_type ())
+#define GIMP_COLOR_SELECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_COLOR_SELECT, GimpColorSelect))
+#define GIMP_IS_COLOR_SELECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_COLOR_SELECT))
+
+
+GType gimp_color_select_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif /* __GIMP_COLOR_SELECT_H__ */
diff --git a/libgimpwidgets/gimpcolorselection.c b/libgimpwidgets/gimpcolorselection.c
new file mode 100644
index 0000000..a7a9838
--- /dev/null
+++ b/libgimpwidgets/gimpcolorselection.c
@@ -0,0 +1,697 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcolorselection.c
+ * Copyright (C) 2003 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpcolorarea.h"
+#include "gimpcolornotebook.h"
+#include "gimpcolorscales.h"
+#include "gimpcolorselect.h"
+#include "gimpcolorselection.h"
+#include "gimphelpui.h"
+#include "gimpicons.h"
+#include "gimpwidgets.h"
+#include "gimpwidgets-private.h"
+
+#include "gimpwidgetsmarshal.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpcolorselection
+ * @title: GimpColorSelection
+ * @short_description: Widget for doing a color selection.
+ *
+ * Widget for doing a color selection.
+ **/
+
+
+#define COLOR_AREA_SIZE 20
+
+
+typedef enum
+{
+ UPDATE_NOTEBOOK = 1 << 0,
+ UPDATE_SCALES = 1 << 1,
+ UPDATE_ENTRY = 1 << 2,
+ UPDATE_COLOR = 1 << 3
+} UpdateType;
+
+#define UPDATE_ALL (UPDATE_NOTEBOOK | \
+ UPDATE_SCALES | \
+ UPDATE_ENTRY | \
+ UPDATE_COLOR)
+
+enum
+{
+ COLOR_CHANGED,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_CONFIG
+};
+
+
+static void gimp_color_selection_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_color_selection_switch_page (GtkWidget *widget,
+ gpointer page,
+ guint page_num,
+ GimpColorSelection *selection);
+static void gimp_color_selection_notebook_changed (GimpColorSelector *selector,
+ const GimpRGB *rgb,
+ const GimpHSV *hsv,
+ GimpColorSelection *selection);
+static void gimp_color_selection_scales_changed (GimpColorSelector *selector,
+ const GimpRGB *rgb,
+ const GimpHSV *hsv,
+ GimpColorSelection *selection);
+static void gimp_color_selection_color_picked (GtkWidget *widget,
+ const GimpRGB *rgb,
+ GimpColorSelection *selection);
+static void gimp_color_selection_entry_changed (GimpColorHexEntry *entry,
+ GimpColorSelection *selection);
+static void gimp_color_selection_channel_changed (GimpColorSelector *selector,
+ GimpColorSelectorChannel channel,
+ GimpColorSelection *selection);
+static void gimp_color_selection_new_color_changed (GtkWidget *widget,
+ GimpColorSelection *selection);
+
+static void gimp_color_selection_update (GimpColorSelection *selection,
+ UpdateType update);
+
+
+G_DEFINE_TYPE (GimpColorSelection, gimp_color_selection, GTK_TYPE_BOX)
+
+#define parent_class gimp_color_selection_parent_class
+
+static guint selection_signals[LAST_SIGNAL] = { 0, };
+
+
+static void
+gimp_color_selection_class_init (GimpColorSelectionClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->set_property = gimp_color_selection_set_property;
+
+ klass->color_changed = NULL;
+
+ g_object_class_install_property (object_class, PROP_CONFIG,
+ g_param_spec_object ("config",
+ "Config",
+ "The color config used by this color selection",
+ GIMP_TYPE_COLOR_CONFIG,
+ G_PARAM_WRITABLE));
+
+ selection_signals[COLOR_CHANGED] =
+ g_signal_new ("color-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpColorSelectionClass, color_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+}
+
+static void
+gimp_color_selection_init (GimpColorSelection *selection)
+{
+ GtkWidget *main_hbox;
+ GtkWidget *hbox;
+ GtkWidget *vbox;
+ GtkWidget *frame;
+ GtkWidget *label;
+ GtkWidget *entry;
+ GtkWidget *button;
+ GtkSizeGroup *new_group;
+ GtkSizeGroup *old_group;
+
+ selection->show_alpha = TRUE;
+
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (selection),
+ GTK_ORIENTATION_VERTICAL);
+
+ gimp_rgba_set (&selection->rgb, 0.0, 0.0, 0.0, 1.0);
+ gimp_rgb_to_hsv (&selection->rgb, &selection->hsv);
+
+ selection->channel = GIMP_COLOR_SELECTOR_RED;
+
+ main_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (selection), main_hbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_hbox);
+
+ /* The left vbox with the notebook */
+ selection->left_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (main_hbox), selection->left_vbox,
+ TRUE, TRUE, 0);
+ gtk_widget_show (selection->left_vbox);
+
+ if (_gimp_ensure_modules_func)
+ {
+ g_type_class_ref (GIMP_TYPE_COLOR_SELECT);
+ _gimp_ensure_modules_func ();
+ }
+
+ selection->notebook = gimp_color_selector_new (GIMP_TYPE_COLOR_NOTEBOOK,
+ &selection->rgb,
+ &selection->hsv,
+ selection->channel);
+
+ if (_gimp_ensure_modules_func)
+ g_type_class_unref (g_type_class_peek (GIMP_TYPE_COLOR_SELECT));
+
+ gimp_color_selector_set_toggles_visible
+ (GIMP_COLOR_SELECTOR (selection->notebook), FALSE);
+ gtk_box_pack_start (GTK_BOX (selection->left_vbox), selection->notebook,
+ TRUE, TRUE, 0);
+ gtk_widget_show (selection->notebook);
+
+ g_signal_connect (selection->notebook, "color-changed",
+ G_CALLBACK (gimp_color_selection_notebook_changed),
+ selection);
+ g_signal_connect (GIMP_COLOR_NOTEBOOK (selection->notebook)->notebook,
+ "switch-page",
+ G_CALLBACK (gimp_color_selection_switch_page),
+ selection);
+
+ /* The hbox for the color_areas */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_end (GTK_BOX (selection->left_vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ /* The labels */
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ label = gtk_label_new (_("Current:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 1.0);
+ gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
+ gtk_widget_show (label);
+
+ new_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
+ gtk_size_group_add_widget (new_group, label);
+ g_object_unref (new_group);
+
+ label = gtk_label_new (_("Old:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 1.0);
+ gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
+ gtk_widget_show (label);
+
+ old_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
+ gtk_size_group_add_widget (old_group, label);
+ g_object_unref (old_group);
+
+ /* The color areas */
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ selection->new_color = gimp_color_area_new (&selection->rgb,
+ selection->show_alpha ?
+ GIMP_COLOR_AREA_SMALL_CHECKS :
+ GIMP_COLOR_AREA_FLAT,
+ GDK_BUTTON1_MASK |
+ GDK_BUTTON2_MASK);
+ gtk_size_group_add_widget (new_group, selection->new_color);
+ gtk_box_pack_start (GTK_BOX (vbox), selection->new_color, FALSE, FALSE, 0);
+ gtk_widget_show (selection->new_color);
+
+ g_signal_connect (selection->new_color, "color-changed",
+ G_CALLBACK (gimp_color_selection_new_color_changed),
+ selection);
+
+ selection->old_color = gimp_color_area_new (&selection->rgb,
+ selection->show_alpha ?
+ GIMP_COLOR_AREA_SMALL_CHECKS :
+ GIMP_COLOR_AREA_FLAT,
+ GDK_BUTTON1_MASK |
+ GDK_BUTTON2_MASK);
+ gtk_drag_dest_unset (selection->old_color);
+ gtk_size_group_add_widget (old_group, selection->old_color);
+ gtk_box_pack_start (GTK_BOX (vbox), selection->old_color, FALSE, FALSE, 0);
+ gtk_widget_show (selection->old_color);
+
+ /* The right vbox with color scales */
+ selection->right_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (main_hbox), selection->right_vbox,
+ TRUE, TRUE, 0);
+ gtk_widget_show (selection->right_vbox);
+
+ selection->scales = gimp_color_selector_new (GIMP_TYPE_COLOR_SCALES,
+ &selection->rgb,
+ &selection->hsv,
+ selection->channel);
+ gimp_color_selector_set_toggles_visible
+ (GIMP_COLOR_SELECTOR (selection->scales), TRUE);
+ gimp_color_selector_set_show_alpha (GIMP_COLOR_SELECTOR (selection->scales),
+ selection->show_alpha);
+ gtk_box_pack_start (GTK_BOX (selection->right_vbox), selection->scales,
+ TRUE, TRUE, 0);
+ gtk_widget_show (selection->scales);
+
+ g_signal_connect (selection->scales, "channel-changed",
+ G_CALLBACK (gimp_color_selection_channel_changed),
+ selection);
+ g_signal_connect (selection->scales, "color-changed",
+ G_CALLBACK (gimp_color_selection_scales_changed),
+ selection);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (selection->right_vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ /* The color picker */
+ button = gimp_pick_button_new ();
+ gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "color-picked",
+ G_CALLBACK (gimp_color_selection_color_picked),
+ selection);
+
+ /* The hex triplet entry */
+ entry = gimp_color_hex_entry_new ();
+ gtk_box_pack_end (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
+ gtk_widget_show (entry);
+
+ label = gtk_label_new_with_mnemonic (_("HTML _notation:"));
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
+ gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ g_object_set_data (G_OBJECT (selection), "color-hex-entry", entry);
+
+ g_signal_connect (entry, "color-changed",
+ G_CALLBACK (gimp_color_selection_entry_changed),
+ selection);
+}
+
+static void
+gimp_color_selection_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpColorSelection *selection = GIMP_COLOR_SELECTION (object);
+
+ switch (property_id)
+ {
+ case PROP_CONFIG:
+ gimp_color_selection_set_config (selection, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+
+/**
+ * gimp_color_selection_new:
+ *
+ * Creates a new #GimpColorSelection widget.
+ *
+ * Return value: The new #GimpColorSelection widget.
+ **/
+GtkWidget *
+gimp_color_selection_new (void)
+{
+ return g_object_new (GIMP_TYPE_COLOR_SELECTION, NULL);
+}
+
+/**
+ * gimp_color_selection_set_show_alpha:
+ * @selection: A #GimpColorSelection widget.
+ * @show_alpha: The new @show_alpha setting.
+ *
+ * Sets the @show_alpha property of the @selection widget.
+ **/
+void
+gimp_color_selection_set_show_alpha (GimpColorSelection *selection,
+ gboolean show_alpha)
+{
+ g_return_if_fail (GIMP_IS_COLOR_SELECTION (selection));
+
+ if (show_alpha != selection->show_alpha)
+ {
+ selection->show_alpha = show_alpha ? TRUE : FALSE;
+
+ gimp_color_selector_set_show_alpha
+ (GIMP_COLOR_SELECTOR (selection->notebook), selection->show_alpha);
+ gimp_color_selector_set_show_alpha
+ (GIMP_COLOR_SELECTOR (selection->scales), selection->show_alpha);
+
+ gimp_color_area_set_type (GIMP_COLOR_AREA (selection->new_color),
+ selection->show_alpha ?
+ GIMP_COLOR_AREA_SMALL_CHECKS :
+ GIMP_COLOR_AREA_FLAT);
+ gimp_color_area_set_type (GIMP_COLOR_AREA (selection->old_color),
+ selection->show_alpha ?
+ GIMP_COLOR_AREA_SMALL_CHECKS :
+ GIMP_COLOR_AREA_FLAT);
+ }
+}
+
+/**
+ * gimp_color_selection_get_show_alpha:
+ * @selection: A #GimpColorSelection widget.
+ *
+ * Returns the @selection's @show_alpha property.
+ *
+ * Return value: #TRUE if the #GimpColorSelection has alpha controls.
+ **/
+gboolean
+gimp_color_selection_get_show_alpha (GimpColorSelection *selection)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_SELECTION (selection), FALSE);
+
+ return selection->show_alpha;
+}
+
+/**
+ * gimp_color_selection_set_color:
+ * @selection: A #GimpColorSelection widget.
+ * @color: The @color to set as current color.
+ *
+ * Sets the #GimpColorSelection's current color to the new @color.
+ **/
+void
+gimp_color_selection_set_color (GimpColorSelection *selection,
+ const GimpRGB *color)
+{
+ g_return_if_fail (GIMP_IS_COLOR_SELECTION (selection));
+ g_return_if_fail (color != NULL);
+
+ selection->rgb = *color;
+ gimp_rgb_to_hsv (&selection->rgb, &selection->hsv);
+
+ gimp_color_selection_update (selection, UPDATE_ALL);
+
+ gimp_color_selection_color_changed (selection);
+}
+
+/**
+ * gimp_color_selection_get_color:
+ * @selection: A #GimpColorSelection widget.
+ * @color: Return location for the @selection's current @color.
+ *
+ * This function returns the #GimpColorSelection's current color.
+ **/
+void
+gimp_color_selection_get_color (GimpColorSelection *selection,
+ GimpRGB *color)
+{
+ g_return_if_fail (GIMP_IS_COLOR_SELECTION (selection));
+ g_return_if_fail (color != NULL);
+
+ *color = selection->rgb;
+}
+
+/**
+ * gimp_color_selection_set_old_color:
+ * @selection: A #GimpColorSelection widget.
+ * @color: The @color to set as old color.
+ *
+ * Sets the #GimpColorSelection's old color.
+ **/
+void
+gimp_color_selection_set_old_color (GimpColorSelection *selection,
+ const GimpRGB *color)
+{
+ g_return_if_fail (GIMP_IS_COLOR_SELECTION (selection));
+ g_return_if_fail (color != NULL);
+
+ gimp_color_area_set_color (GIMP_COLOR_AREA (selection->old_color), color);
+}
+
+/**
+ * gimp_color_selection_get_old_color:
+ * @selection: A #GimpColorSelection widget.
+ * @color: Return location for the @selection's old @color.
+ *
+ * This function returns the #GimpColorSelection's old color.
+ **/
+void
+gimp_color_selection_get_old_color (GimpColorSelection *selection,
+ GimpRGB *color)
+{
+ g_return_if_fail (GIMP_IS_COLOR_SELECTION (selection));
+ g_return_if_fail (color != NULL);
+
+ gimp_color_area_get_color (GIMP_COLOR_AREA (selection->old_color), color);
+}
+
+/**
+ * gimp_color_selection_reset:
+ * @selection: A #GimpColorSelection widget.
+ *
+ * Sets the #GimpColorSelection's current color to its old color.
+ **/
+void
+gimp_color_selection_reset (GimpColorSelection *selection)
+{
+ GimpRGB color;
+
+ g_return_if_fail (GIMP_IS_COLOR_SELECTION (selection));
+
+ gimp_color_area_get_color (GIMP_COLOR_AREA (selection->old_color), &color);
+ gimp_color_selection_set_color (selection, &color);
+}
+
+/**
+ * gimp_color_selection_color_changed:
+ * @selection: A #GimpColorSelection widget.
+ *
+ * Emits the "color-changed" signal.
+ **/
+void
+gimp_color_selection_color_changed (GimpColorSelection *selection)
+{
+ g_return_if_fail (GIMP_IS_COLOR_SELECTION (selection));
+
+ g_signal_emit (selection, selection_signals[COLOR_CHANGED], 0);
+}
+
+/**
+ * gimp_color_selection_set_config:
+ * @selection: A #GimpColorSelection widget.
+ * @config: A #GimpColorConfig object.
+ *
+ * Sets the color management configuration to use with this color selection.
+ *
+ * Since: 2.4
+ */
+void
+gimp_color_selection_set_config (GimpColorSelection *selection,
+ GimpColorConfig *config)
+{
+ g_return_if_fail (GIMP_IS_COLOR_SELECTION (selection));
+ g_return_if_fail (config == NULL || GIMP_IS_COLOR_CONFIG (config));
+
+ gimp_color_selector_set_config (GIMP_COLOR_SELECTOR (selection->notebook),
+ config);
+ gimp_color_selector_set_config (GIMP_COLOR_SELECTOR (selection->scales),
+ config);
+ gimp_color_area_set_color_config (GIMP_COLOR_AREA (selection->old_color),
+ config);
+ gimp_color_area_set_color_config (GIMP_COLOR_AREA (selection->new_color),
+ config);
+}
+
+/* private functions */
+
+static void
+gimp_color_selection_switch_page (GtkWidget *widget,
+ gpointer page,
+ guint page_num,
+ GimpColorSelection *selection)
+{
+ GimpColorNotebook *notebook = GIMP_COLOR_NOTEBOOK (selection->notebook);
+ gboolean sensitive;
+
+ sensitive =
+ (GIMP_COLOR_SELECTOR_GET_CLASS (notebook->cur_page)->set_channel != NULL);
+
+ gimp_color_selector_set_toggles_sensitive
+ (GIMP_COLOR_SELECTOR (selection->scales), sensitive);
+}
+
+static void
+gimp_color_selection_notebook_changed (GimpColorSelector *selector,
+ const GimpRGB *rgb,
+ const GimpHSV *hsv,
+ GimpColorSelection *selection)
+{
+ selection->hsv = *hsv;
+ selection->rgb = *rgb;
+
+ gimp_color_selection_update (selection,
+ UPDATE_SCALES | UPDATE_ENTRY | UPDATE_COLOR);
+ gimp_color_selection_color_changed (selection);
+}
+
+static void
+gimp_color_selection_scales_changed (GimpColorSelector *selector,
+ const GimpRGB *rgb,
+ const GimpHSV *hsv,
+ GimpColorSelection *selection)
+{
+ selection->rgb = *rgb;
+ selection->hsv = *hsv;
+
+ gimp_color_selection_update (selection,
+ UPDATE_ENTRY | UPDATE_NOTEBOOK | UPDATE_COLOR);
+ gimp_color_selection_color_changed (selection);
+}
+
+static void
+gimp_color_selection_color_picked (GtkWidget *widget,
+ const GimpRGB *rgb,
+ GimpColorSelection *selection)
+{
+ gimp_color_selection_set_color (selection, rgb);
+}
+
+static void
+gimp_color_selection_entry_changed (GimpColorHexEntry *entry,
+ GimpColorSelection *selection)
+{
+ gimp_color_hex_entry_get_color (entry, &selection->rgb);
+
+ gimp_rgb_to_hsv (&selection->rgb, &selection->hsv);
+
+ gimp_color_selection_update (selection,
+ UPDATE_NOTEBOOK | UPDATE_SCALES | UPDATE_COLOR);
+ gimp_color_selection_color_changed (selection);
+}
+
+static void
+gimp_color_selection_channel_changed (GimpColorSelector *selector,
+ GimpColorSelectorChannel channel,
+ GimpColorSelection *selection)
+{
+ selection->channel = channel;
+
+ gimp_color_selector_set_channel (GIMP_COLOR_SELECTOR (selection->notebook),
+ selection->channel);
+}
+
+static void
+gimp_color_selection_new_color_changed (GtkWidget *widget,
+ GimpColorSelection *selection)
+{
+ gimp_color_area_get_color (GIMP_COLOR_AREA (widget), &selection->rgb);
+ gimp_rgb_to_hsv (&selection->rgb, &selection->hsv);
+
+ gimp_color_selection_update (selection,
+ UPDATE_NOTEBOOK | UPDATE_SCALES | UPDATE_ENTRY);
+ gimp_color_selection_color_changed (selection);
+}
+
+static void
+gimp_color_selection_update (GimpColorSelection *selection,
+ UpdateType update)
+{
+ if (update & UPDATE_NOTEBOOK)
+ {
+ g_signal_handlers_block_by_func (selection->notebook,
+ gimp_color_selection_notebook_changed,
+ selection);
+
+ gimp_color_selector_set_color (GIMP_COLOR_SELECTOR (selection->notebook),
+ &selection->rgb,
+ &selection->hsv);
+
+ g_signal_handlers_unblock_by_func (selection->notebook,
+ gimp_color_selection_notebook_changed,
+ selection);
+ }
+
+ if (update & UPDATE_SCALES)
+ {
+ g_signal_handlers_block_by_func (selection->scales,
+ gimp_color_selection_scales_changed,
+ selection);
+
+ gimp_color_selector_set_color (GIMP_COLOR_SELECTOR (selection->scales),
+ &selection->rgb,
+ &selection->hsv);
+
+ g_signal_handlers_unblock_by_func (selection->scales,
+ gimp_color_selection_scales_changed,
+ selection);
+ }
+
+ if (update & UPDATE_ENTRY)
+ {
+ GimpColorHexEntry *entry;
+
+ entry = g_object_get_data (G_OBJECT (selection), "color-hex-entry");
+
+ g_signal_handlers_block_by_func (entry,
+ gimp_color_selection_entry_changed,
+ selection);
+
+ gimp_color_hex_entry_set_color (entry, &selection->rgb);
+
+ g_signal_handlers_unblock_by_func (entry,
+ gimp_color_selection_entry_changed,
+ selection);
+ }
+
+ if (update & UPDATE_COLOR)
+ {
+ g_signal_handlers_block_by_func (selection->new_color,
+ gimp_color_selection_new_color_changed,
+ selection);
+
+ gimp_color_area_set_color (GIMP_COLOR_AREA (selection->new_color),
+ &selection->rgb);
+
+ g_signal_handlers_unblock_by_func (selection->new_color,
+ gimp_color_selection_new_color_changed,
+ selection);
+ }
+}
diff --git a/libgimpwidgets/gimpcolorselection.h b/libgimpwidgets/gimpcolorselection.h
new file mode 100644
index 0000000..16bf915
--- /dev/null
+++ b/libgimpwidgets/gimpcolorselection.h
@@ -0,0 +1,106 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcolorselection.h
+ * Copyright (C) 2003 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_COLOR_SELECTION_H__
+#define __GIMP_COLOR_SELECTION_H__
+
+G_BEGIN_DECLS
+
+/* For information look at the html documentation */
+
+
+#define GIMP_TYPE_COLOR_SELECTION (gimp_color_selection_get_type ())
+#define GIMP_COLOR_SELECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_COLOR_SELECTION, GimpColorSelection))
+#define GIMP_COLOR_SELECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_COLOR_SELECTION, GimpColorSelectionClass))
+#define GIMP_IS_COLOR_SELECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_COLOR_SELECTION))
+#define GIMP_IS_COLOR_SELECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_COLOR_SELECTION))
+#define GIMP_COLOR_SELECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_COLOR_SELECTION, GimpColorSelectionClass))
+
+
+typedef struct _GimpColorSelectionClass GimpColorSelectionClass;
+
+struct _GimpColorSelection
+{
+ GtkBox parent_instance;
+
+ gboolean show_alpha;
+
+ GimpHSV hsv;
+ GimpRGB rgb;
+ GimpColorSelectorChannel channel;
+
+ GtkWidget *left_vbox;
+ GtkWidget *right_vbox;
+
+ GtkWidget *notebook;
+ GtkWidget *scales;
+
+ GtkWidget *new_color;
+ GtkWidget *old_color;
+};
+
+struct _GimpColorSelectionClass
+{
+ GtkBoxClass parent_class;
+
+ void (* color_changed) (GimpColorSelection *selection);
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_color_selection_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_color_selection_new (void);
+
+void gimp_color_selection_set_show_alpha (GimpColorSelection *selection,
+ gboolean show_alpha);
+gboolean gimp_color_selection_get_show_alpha (GimpColorSelection *selection);
+
+void gimp_color_selection_set_color (GimpColorSelection *selection,
+ const GimpRGB *color);
+void gimp_color_selection_get_color (GimpColorSelection *selection,
+ GimpRGB *color);
+
+void gimp_color_selection_set_old_color (GimpColorSelection *selection,
+ const GimpRGB *color);
+void gimp_color_selection_get_old_color (GimpColorSelection *selection,
+ GimpRGB *color);
+
+void gimp_color_selection_reset (GimpColorSelection *selection);
+
+void gimp_color_selection_color_changed (GimpColorSelection *selection);
+
+void gimp_color_selection_set_config (GimpColorSelection *selection,
+ GimpColorConfig *config);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_COLOR_SELECTION_H__ */
diff --git a/libgimpwidgets/gimpcolorselector.c b/libgimpwidgets/gimpcolorselector.c
new file mode 100644
index 0000000..72023ac
--- /dev/null
+++ b/libgimpwidgets/gimpcolorselector.c
@@ -0,0 +1,638 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcolorselector.c
+ * Copyright (C) 2002 Michael Natterer <mitch@gimp.org>
+ *
+ * based on:
+ * Colour selector module
+ * Copyright (C) 1999 Austin Donnelly <austin@greenend.org.uk>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpcolorselector.h"
+#include "gimpicons.h"
+#include "gimpwidgetsmarshal.h"
+
+
+/**
+ * SECTION: gimpcolorselector
+ * @title: GimpColorSelector
+ * @short_description: Pluggable GIMP color selector modules.
+ * @see_also: #GModule, #GTypeModule, #GimpModule
+ *
+ * Functions and definitions for creating pluggable GIMP color
+ * selector modules.
+ **/
+
+
+enum
+{
+ COLOR_CHANGED,
+ CHANNEL_CHANGED,
+ MODEL_VISIBLE_CHANGED,
+ LAST_SIGNAL
+};
+
+
+typedef struct _GimpColorSelectorPrivate GimpColorSelectorPrivate;
+
+struct _GimpColorSelectorPrivate
+{
+ gboolean model_visible[3];
+};
+
+#define GET_PRIVATE(obj) \
+ ((GimpColorSelectorPrivate *) gimp_color_selector_get_instance_private ((GimpColorSelector *) (obj)))
+
+
+static void gimp_color_selector_dispose (GObject *object);
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpColorSelector, gimp_color_selector,
+ GTK_TYPE_BOX)
+
+#define parent_class gimp_color_selector_parent_class
+
+static guint selector_signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+gimp_color_selector_class_init (GimpColorSelectorClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = gimp_color_selector_dispose;
+
+ selector_signals[COLOR_CHANGED] =
+ g_signal_new ("color-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpColorSelectorClass, color_changed),
+ NULL, NULL,
+ _gimp_widgets_marshal_VOID__POINTER_POINTER,
+ G_TYPE_NONE, 2,
+ G_TYPE_POINTER,
+ G_TYPE_POINTER);
+
+ selector_signals[CHANNEL_CHANGED] =
+ g_signal_new ("channel-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpColorSelectorClass, channel_changed),
+ NULL, NULL,
+ _gimp_widgets_marshal_VOID__ENUM,
+ G_TYPE_NONE, 1,
+ GIMP_TYPE_COLOR_SELECTOR_CHANNEL);
+
+ selector_signals[MODEL_VISIBLE_CHANGED] =
+ g_signal_new ("model-visible-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpColorSelectorClass, model_visible_changed),
+ NULL, NULL,
+ _gimp_widgets_marshal_VOID__ENUM_BOOLEAN,
+ G_TYPE_NONE, 2,
+ GIMP_TYPE_COLOR_SELECTOR_MODEL,
+ G_TYPE_BOOLEAN);
+
+ klass->name = "Unnamed";
+ klass->help_id = NULL;
+ klass->icon_name = GIMP_ICON_PALETTE;
+
+ klass->set_toggles_visible = NULL;
+ klass->set_toggles_sensitive = NULL;
+ klass->set_show_alpha = NULL;
+ klass->set_color = NULL;
+ klass->set_channel = NULL;
+ klass->set_model_visible = NULL;
+ klass->color_changed = NULL;
+ klass->channel_changed = NULL;
+ klass->model_visible_changed = NULL;
+ klass->set_config = NULL;
+}
+
+static void
+gimp_color_selector_init (GimpColorSelector *selector)
+{
+ GimpColorSelectorPrivate *priv = GET_PRIVATE (selector);
+
+ selector->toggles_visible = TRUE;
+ selector->toggles_sensitive = TRUE;
+ selector->show_alpha = TRUE;
+
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (selector),
+ GTK_ORIENTATION_VERTICAL);
+
+ gimp_rgba_set (&selector->rgb, 0.0, 0.0, 0.0, 1.0);
+ gimp_rgb_to_hsv (&selector->rgb, &selector->hsv);
+
+ selector->channel = GIMP_COLOR_SELECTOR_RED;
+
+ priv->model_visible[GIMP_COLOR_SELECTOR_MODEL_RGB] = TRUE;
+ priv->model_visible[GIMP_COLOR_SELECTOR_MODEL_LCH] = TRUE;
+ priv->model_visible[GIMP_COLOR_SELECTOR_MODEL_HSV] = FALSE;
+}
+
+static void
+gimp_color_selector_dispose (GObject *object)
+{
+ gimp_color_selector_set_config (GIMP_COLOR_SELECTOR (object), NULL);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+
+/* public functions */
+
+/**
+ * gimp_color_selector_new:
+ * @selector_type: The #GType of the selector to create.
+ * @rgb: The initial color to be edited.
+ * @hsv: The same color in HSV.
+ * @channel: The selector's initial channel.
+ *
+ * Creates a new #GimpColorSelector widget of type @selector_type.
+ *
+ * Note that this is mostly internal API to be used by other widgets.
+ *
+ * Please use gimp_color_selection_new() for the "GIMP-typical" color
+ * selection widget. Also see gimp_color_button_new().
+ *
+ * Retunn value: the new #GimpColorSelector widget.
+ **/
+GtkWidget *
+gimp_color_selector_new (GType selector_type,
+ const GimpRGB *rgb,
+ const GimpHSV *hsv,
+ GimpColorSelectorChannel channel)
+{
+ GimpColorSelector *selector;
+
+ g_return_val_if_fail (g_type_is_a (selector_type, GIMP_TYPE_COLOR_SELECTOR),
+ NULL);
+ g_return_val_if_fail (rgb != NULL, NULL);
+ g_return_val_if_fail (hsv != NULL, NULL);
+
+ selector = g_object_new (selector_type, NULL);
+
+ gimp_color_selector_set_color (selector, rgb, hsv);
+ gimp_color_selector_set_channel (selector, channel);
+
+ return GTK_WIDGET (selector);
+}
+
+/**
+ * gimp_color_selector_set_toggles_visible:
+ * @selector: A #GimpColorSelector widget.
+ * @visible: The new @visible setting.
+ *
+ * Sets the @visible property of the @selector's toggles.
+ *
+ * This function has no effect if this @selector instance has no
+ * toggles to switch channels.
+ **/
+void
+gimp_color_selector_set_toggles_visible (GimpColorSelector *selector,
+ gboolean visible)
+{
+ g_return_if_fail (GIMP_IS_COLOR_SELECTOR (selector));
+
+ if (selector->toggles_visible != visible)
+ {
+ GimpColorSelectorClass *selector_class;
+
+ selector->toggles_visible = visible ? TRUE : FALSE;
+
+ selector_class = GIMP_COLOR_SELECTOR_GET_CLASS (selector);
+
+ if (selector_class->set_toggles_visible)
+ selector_class->set_toggles_visible (selector, visible);
+ }
+}
+
+/**
+ * gimp_color_selector_get_toggles_visible:
+ * @selector: A #GimpColorSelector widget.
+ *
+ * Returns the @visible property of the @selector's toggles.
+ *
+ * Return value: #TRUE if the #GimpColorSelector's toggles are visible.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_color_selector_get_toggles_visible (GimpColorSelector *selector)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_SELECTOR (selector), FALSE);
+
+ return selector->toggles_visible;
+}
+
+/**
+ * gimp_color_selector_set_toggles_sensitive:
+ * @selector: A #GimpColorSelector widget.
+ * @sensitive: The new @sensitive setting.
+ *
+ * Sets the @sensitive property of the @selector's toggles.
+ *
+ * This function has no effect if this @selector instance has no
+ * toggles to switch channels.
+ **/
+void
+gimp_color_selector_set_toggles_sensitive (GimpColorSelector *selector,
+ gboolean sensitive)
+{
+ g_return_if_fail (GIMP_IS_COLOR_SELECTOR (selector));
+
+ if (selector->toggles_sensitive != sensitive)
+ {
+ GimpColorSelectorClass *selector_class;
+
+ selector->toggles_sensitive = sensitive ? TRUE : FALSE;
+
+ selector_class = GIMP_COLOR_SELECTOR_GET_CLASS (selector);
+
+ if (selector_class->set_toggles_sensitive)
+ selector_class->set_toggles_sensitive (selector, sensitive);
+ }
+}
+
+/**
+ * gimp_color_selector_get_toggles_sensitive:
+ * @selector: A #GimpColorSelector widget.
+ *
+ * Returns the @sensitive property of the @selector's toggles.
+ *
+ * Return value: #TRUE if the #GimpColorSelector's toggles are sensitive.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_color_selector_get_toggles_sensitive (GimpColorSelector *selector)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_SELECTOR (selector), FALSE);
+
+ return selector->toggles_sensitive;
+}
+
+/**
+ * gimp_color_selector_set_show_alpha:
+ * @selector: A #GimpColorSelector widget.
+ * @show_alpha: The new @show_alpha setting.
+ *
+ * Sets the @show_alpha property of the @selector widget.
+ **/
+void
+gimp_color_selector_set_show_alpha (GimpColorSelector *selector,
+ gboolean show_alpha)
+{
+ g_return_if_fail (GIMP_IS_COLOR_SELECTOR (selector));
+
+ if (show_alpha != selector->show_alpha)
+ {
+ GimpColorSelectorClass *selector_class;
+
+ selector->show_alpha = show_alpha ? TRUE : FALSE;
+
+ selector_class = GIMP_COLOR_SELECTOR_GET_CLASS (selector);
+
+ if (selector_class->set_show_alpha)
+ selector_class->set_show_alpha (selector, show_alpha);
+ }
+}
+
+/**
+ * gimp_color_selector_get_show_alpha:
+ * @selector: A #GimpColorSelector widget.
+ *
+ * Returns the @selector's @show_alpha property.
+ *
+ * Return value: #TRUE if the #GimpColorSelector has alpha controls.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_color_selector_get_show_alpha (GimpColorSelector *selector)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_SELECTOR (selector), FALSE);
+
+ return selector->show_alpha;
+}
+
+/**
+ * gimp_color_selector_set_color:
+ * @selector: A #GimpColorSelector widget.
+ * @rgb: The new color.
+ * @hsv: The same color in HSV.
+ *
+ * Sets the color shown in the @selector widget.
+ **/
+void
+gimp_color_selector_set_color (GimpColorSelector *selector,
+ const GimpRGB *rgb,
+ const GimpHSV *hsv)
+{
+ GimpColorSelectorClass *selector_class;
+
+ g_return_if_fail (GIMP_IS_COLOR_SELECTOR (selector));
+ g_return_if_fail (rgb != NULL);
+ g_return_if_fail (hsv != NULL);
+
+ selector->rgb = *rgb;
+ selector->hsv = *hsv;
+
+ selector_class = GIMP_COLOR_SELECTOR_GET_CLASS (selector);
+
+ if (selector_class->set_color)
+ selector_class->set_color (selector, rgb, hsv);
+
+ gimp_color_selector_color_changed (selector);
+}
+
+/**
+ * gimp_color_selector_get_color:
+ * @selector: A #GimpColorSelector widget.
+ * @rgb: Return location for the color.
+ * @hsv: Return location for the same same color in HSV.
+ *
+ * Retrieves the color shown in the @selector widget.
+ *
+ * Since: 2.10
+ **/
+void
+gimp_color_selector_get_color (GimpColorSelector *selector,
+ GimpRGB *rgb,
+ GimpHSV *hsv)
+{
+ g_return_if_fail (GIMP_IS_COLOR_SELECTOR (selector));
+ g_return_if_fail (rgb != NULL);
+ g_return_if_fail (hsv != NULL);
+
+ *rgb = selector->rgb;
+ *hsv = selector->hsv;
+}
+
+/**
+ * gimp_color_selector_set_channel:
+ * @selector: A #GimpColorSelector widget.
+ * @channel: The new @channel setting.
+ *
+ * Sets the @channel property of the @selector widget.
+ *
+ * Changes between displayed channels if this @selector instance has
+ * the ability to show different channels.
+ * This will also update the color model if needed.
+ **/
+void
+gimp_color_selector_set_channel (GimpColorSelector *selector,
+ GimpColorSelectorChannel channel)
+{
+ g_return_if_fail (GIMP_IS_COLOR_SELECTOR (selector));
+
+ if (channel != selector->channel)
+ {
+ GimpColorSelectorClass *selector_class;
+ GimpColorSelectorModel model = -1;
+
+ selector->channel = channel;
+
+ switch (channel)
+ {
+ case GIMP_COLOR_SELECTOR_RED:
+ case GIMP_COLOR_SELECTOR_GREEN:
+ case GIMP_COLOR_SELECTOR_BLUE:
+ model = GIMP_COLOR_SELECTOR_MODEL_RGB;
+ break;
+
+ case GIMP_COLOR_SELECTOR_HUE:
+ case GIMP_COLOR_SELECTOR_SATURATION:
+ case GIMP_COLOR_SELECTOR_VALUE:
+ model = GIMP_COLOR_SELECTOR_MODEL_HSV;
+ break;
+
+ case GIMP_COLOR_SELECTOR_LCH_LIGHTNESS:
+ case GIMP_COLOR_SELECTOR_LCH_CHROMA:
+ case GIMP_COLOR_SELECTOR_LCH_HUE:
+ model = GIMP_COLOR_SELECTOR_MODEL_LCH;
+ break;
+
+ case GIMP_COLOR_SELECTOR_ALPHA:
+ /* Alpha channel does not change the color model. */
+ break;
+
+ default:
+ /* Should not happen. */
+ g_return_if_reached ();
+ break;
+ }
+
+ selector_class = GIMP_COLOR_SELECTOR_GET_CLASS (selector);
+
+ if (selector_class->set_channel)
+ selector_class->set_channel (selector, channel);
+
+ gimp_color_selector_channel_changed (selector);
+
+ if (model != -1)
+ {
+ /* make visibility of LCH and HSV mutuallky exclusive */
+ if (model == GIMP_COLOR_SELECTOR_MODEL_HSV)
+ {
+ gimp_color_selector_set_model_visible (selector,
+ GIMP_COLOR_SELECTOR_MODEL_LCH,
+ FALSE);
+ }
+ else if (model == GIMP_COLOR_SELECTOR_MODEL_LCH)
+ {
+ gimp_color_selector_set_model_visible (selector,
+ GIMP_COLOR_SELECTOR_MODEL_HSV,
+ FALSE);
+ }
+
+ gimp_color_selector_set_model_visible (selector, model, TRUE);
+ }
+ }
+}
+
+/**
+ * gimp_color_selector_get_channel:
+ * @selector: A #GimpColorSelector widget.
+ *
+ * Returns the @selector's current channel.
+ *
+ * Return value: The #GimpColorSelectorChannel currently shown by the
+ * @selector.
+ *
+ * Since: 2.10
+ **/
+GimpColorSelectorChannel
+gimp_color_selector_get_channel (GimpColorSelector *selector)
+{
+ g_return_val_if_fail (GIMP_IS_COLOR_SELECTOR (selector),
+ GIMP_COLOR_SELECTOR_RED);
+
+ return selector->channel;
+}
+
+/**
+ * gimp_color_selector_set_model_visible:
+ * @selector: A #GimpColorSelector widget.
+ * @model: The affected #GimpColorSelectorModel.
+ * @visible: The new visible setting.
+ *
+ * Sets the @model visible/invisible on the @selector widget.
+ *
+ * Toggles visibility of displayed models if this @selector instance
+ * has the ability to show different color models.
+ *
+ * Since: 2.10
+ **/
+void
+gimp_color_selector_set_model_visible (GimpColorSelector *selector,
+ GimpColorSelectorModel model,
+ gboolean visible)
+{
+ GimpColorSelectorPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_COLOR_SELECTOR (selector));
+
+ priv = GET_PRIVATE (selector);
+
+ visible = visible ? TRUE : FALSE;
+
+ if (visible != priv->model_visible[model])
+ {
+ GimpColorSelectorClass *selector_class;
+
+ priv->model_visible[model] = visible;
+
+ selector_class = GIMP_COLOR_SELECTOR_GET_CLASS (selector);
+
+ if (selector_class->set_model_visible)
+ selector_class->set_model_visible (selector, model, visible);
+
+ gimp_color_selector_model_visible_changed (selector, model);
+ }
+}
+
+/**
+ * gimp_color_selector_get_model_visible:
+ * @selector: A #GimpColorSelector widget.
+ * @model: The #GimpColorSelectorModel.
+ *
+ * Return value: whether @model is visible in @selector.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_color_selector_get_model_visible (GimpColorSelector *selector,
+ GimpColorSelectorModel model)
+{
+ GimpColorSelectorPrivate *priv;
+
+ g_return_val_if_fail (GIMP_IS_COLOR_SELECTOR (selector), FALSE);
+
+ priv = GET_PRIVATE (selector);
+
+ return priv->model_visible[model];
+}
+
+/**
+ * gimp_color_selector_color_changed:
+ * @selector: A #GimpColorSelector widget.
+ *
+ * Emits the "color-changed" signal.
+ **/
+void
+gimp_color_selector_color_changed (GimpColorSelector *selector)
+{
+ g_return_if_fail (GIMP_IS_COLOR_SELECTOR (selector));
+
+ g_signal_emit (selector, selector_signals[COLOR_CHANGED], 0,
+ &selector->rgb, &selector->hsv);
+}
+
+/**
+ * gimp_color_selector_channel_changed:
+ * @selector: A #GimpColorSelector widget.
+ *
+ * Emits the "channel-changed" signal.
+ **/
+void
+gimp_color_selector_channel_changed (GimpColorSelector *selector)
+{
+ g_return_if_fail (GIMP_IS_COLOR_SELECTOR (selector));
+
+ g_signal_emit (selector, selector_signals[CHANNEL_CHANGED], 0,
+ selector->channel);
+}
+
+/**
+ * gimp_color_selector_model_visible_changed:
+ * @selector: A #GimpColorSelector widget.
+ *
+ * Emits the "model-visible-changed" signal.
+ *
+ * Since: 2.10
+ **/
+void
+gimp_color_selector_model_visible_changed (GimpColorSelector *selector,
+ GimpColorSelectorModel model)
+{
+ GimpColorSelectorPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_COLOR_SELECTOR (selector));
+
+ priv = GET_PRIVATE (selector);
+
+ g_signal_emit (selector, selector_signals[MODEL_VISIBLE_CHANGED], 0,
+ model, priv->model_visible[model]);
+}
+
+/**
+ * gimp_color_selector_set_config:
+ * @selector: a #GimpColorSelector widget.
+ * @config: a #GimpColorConfig object.
+ *
+ * Sets the color management configuration to use with this color selector.
+ *
+ * Since: 2.4
+ */
+void
+gimp_color_selector_set_config (GimpColorSelector *selector,
+ GimpColorConfig *config)
+{
+ GimpColorSelectorClass *selector_class;
+
+ g_return_if_fail (GIMP_IS_COLOR_SELECTOR (selector));
+ g_return_if_fail (config == NULL || GIMP_IS_COLOR_CONFIG (config));
+
+ selector_class = GIMP_COLOR_SELECTOR_GET_CLASS (selector);
+
+ if (selector_class->set_config)
+ selector_class->set_config (selector, config);
+}
diff --git a/libgimpwidgets/gimpcolorselector.h b/libgimpwidgets/gimpcolorselector.h
new file mode 100644
index 0000000..d6c6660
--- /dev/null
+++ b/libgimpwidgets/gimpcolorselector.h
@@ -0,0 +1,177 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcolorselector.h
+ * Copyright (C) 2002 Michael Natterer <mitch@gimp.org>
+ *
+ * based on:
+ * Colour selector module
+ * Copyright (C) 1999 Austin Donnelly <austin@greenend.org.uk>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_COLOR_SELECTOR_H__
+#define __GIMP_COLOR_SELECTOR_H__
+
+G_BEGIN_DECLS
+
+/* For information look at the html documentation */
+
+
+/**
+ * GIMP_COLOR_SELECTOR_SIZE:
+ *
+ * The suggested size for a color area in a #GimpColorSelector
+ * implementation.
+ **/
+#define GIMP_COLOR_SELECTOR_SIZE 150
+
+/**
+ * GIMP_COLOR_SELECTOR_BAR_SIZE:
+ *
+ * The suggested width for a color bar in a #GimpColorSelector
+ * implementation.
+ **/
+#define GIMP_COLOR_SELECTOR_BAR_SIZE 15
+
+
+#define GIMP_TYPE_COLOR_SELECTOR (gimp_color_selector_get_type ())
+#define GIMP_COLOR_SELECTOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_COLOR_SELECTOR, GimpColorSelector))
+#define GIMP_COLOR_SELECTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_COLOR_SELECTOR, GimpColorSelectorClass))
+#define GIMP_IS_COLOR_SELECTOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_COLOR_SELECTOR))
+#define GIMP_IS_COLOR_SELECTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_COLOR_SELECTOR))
+#define GIMP_COLOR_SELECTOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_COLOR_SELECTOR, GimpColorSelectorClass))
+
+
+typedef struct _GimpColorSelectorClass GimpColorSelectorClass;
+
+struct _GimpColorSelector
+{
+ GtkBox parent_instance;
+
+ gboolean toggles_visible;
+ gboolean toggles_sensitive;
+ gboolean show_alpha;
+
+ GimpRGB rgb;
+ GimpHSV hsv;
+
+ GimpColorSelectorChannel channel;
+};
+
+struct _GimpColorSelectorClass
+{
+ GtkBoxClass parent_class;
+
+ const gchar *name;
+ const gchar *help_id;
+#ifdef GIMP_DISABLE_DEPRECATED
+ gpointer deprecated_stock_id;
+#else
+ const gchar *stock_id;
+#endif
+
+ /* virtual functions */
+ void (* set_toggles_visible) (GimpColorSelector *selector,
+ gboolean visible);
+ void (* set_toggles_sensitive) (GimpColorSelector *selector,
+ gboolean sensitive);
+ void (* set_show_alpha) (GimpColorSelector *selector,
+ gboolean show_alpha);
+ void (* set_color) (GimpColorSelector *selector,
+ const GimpRGB *rgb,
+ const GimpHSV *hsv);
+ void (* set_channel) (GimpColorSelector *selector,
+ GimpColorSelectorChannel channel);
+
+ /* signals */
+ void (* color_changed) (GimpColorSelector *selector,
+ const GimpRGB *rgb,
+ const GimpHSV *hsv);
+ void (* channel_changed) (GimpColorSelector *selector,
+ GimpColorSelectorChannel channel);
+
+ /* another virtual function */
+ void (* set_config) (GimpColorSelector *selector,
+ GimpColorConfig *config);
+
+ /* icon name */
+ const gchar *icon_name;
+
+ /* another virtual function */
+ void (* set_model_visible) (GimpColorSelector *selector,
+ GimpColorSelectorModel model,
+ gboolean visible);
+
+ /* another signal */
+ void (* model_visible_changed) (GimpColorSelector *selector,
+ GimpColorSelectorModel model,
+ gboolean visible);
+};
+
+
+GType gimp_color_selector_get_type (void) G_GNUC_CONST;
+GtkWidget * gimp_color_selector_new (GType selector_type,
+ const GimpRGB *rgb,
+ const GimpHSV *hsv,
+ GimpColorSelectorChannel channel);
+
+void gimp_color_selector_set_toggles_visible (GimpColorSelector *selector,
+ gboolean visible);
+gboolean gimp_color_selector_get_toggles_visible (GimpColorSelector *selector);
+
+void gimp_color_selector_set_toggles_sensitive (GimpColorSelector *selector,
+ gboolean sensitive);
+gboolean gimp_color_selector_get_toggles_sensitive (GimpColorSelector *selector);
+
+void gimp_color_selector_set_show_alpha (GimpColorSelector *selector,
+ gboolean show_alpha);
+gboolean gimp_color_selector_get_show_alpha (GimpColorSelector *selector);
+
+void gimp_color_selector_set_color (GimpColorSelector *selector,
+ const GimpRGB *rgb,
+ const GimpHSV *hsv);
+void gimp_color_selector_get_color (GimpColorSelector *selector,
+ GimpRGB *rgb,
+ GimpHSV *hsv);
+
+void gimp_color_selector_set_channel (GimpColorSelector *selector,
+ GimpColorSelectorChannel channel);
+GimpColorSelectorChannel
+ gimp_color_selector_get_channel (GimpColorSelector *selector);
+
+void gimp_color_selector_set_model_visible (GimpColorSelector *selector,
+ GimpColorSelectorModel model,
+ gboolean visible);
+gboolean gimp_color_selector_get_model_visible (GimpColorSelector *selector,
+ GimpColorSelectorModel model);
+
+void gimp_color_selector_color_changed (GimpColorSelector *selector);
+void gimp_color_selector_channel_changed (GimpColorSelector *selector);
+void gimp_color_selector_model_visible_changed (GimpColorSelector *selector,
+ GimpColorSelectorModel model);
+
+void gimp_color_selector_set_config (GimpColorSelector *selector,
+ GimpColorConfig *config);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_COLOR_SELECTOR_H__ */
diff --git a/libgimpwidgets/gimpcontroller.c b/libgimpwidgets/gimpcontroller.c
new file mode 100644
index 0000000..72199df
--- /dev/null
+++ b/libgimpwidgets/gimpcontroller.c
@@ -0,0 +1,263 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcontroller.c
+ * Copyright (C) 2004 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpwidgetsmarshal.h"
+
+#define GIMP_ENABLE_CONTROLLER_UNDER_CONSTRUCTION
+#include "gimpcontroller.h"
+#include "gimpicons.h"
+
+
+/**
+ * SECTION: gimpcontroller
+ * @title: GimpController
+ * @short_description: Pluggable GIMP input controller modules.
+ *
+ * An abstract interface for implementing arbitrary input controllers.
+ **/
+
+
+enum
+{
+ PROP_0,
+ PROP_NAME,
+ PROP_STATE
+};
+
+enum
+{
+ EVENT,
+ LAST_SIGNAL
+};
+
+
+static void gimp_controller_finalize (GObject *object);
+static void gimp_controller_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_controller_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+
+G_DEFINE_TYPE_WITH_CODE (GimpController, gimp_controller, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, NULL))
+
+#define parent_class gimp_controller_parent_class
+
+static guint controller_signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+gimp_controller_class_init (GimpControllerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gimp_controller_finalize;
+ object_class->set_property = gimp_controller_set_property;
+ object_class->get_property = gimp_controller_get_property;
+
+ klass->name = "Unnamed";
+ klass->help_domain = NULL;
+ klass->help_id = NULL;
+ klass->icon_name = GIMP_ICON_CONTROLLER;
+
+ klass->get_n_events = NULL;
+ klass->get_event_name = NULL;
+ klass->event = NULL;
+
+ g_object_class_install_property (object_class, PROP_NAME,
+ g_param_spec_string ("name",
+ "Name",
+ "The controller's name",
+ "Unnamed Controller",
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_STATE,
+ g_param_spec_string ("state",
+ "State",
+ "The controller's state, as human-readable string",
+ "Unknown",
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ controller_signals[EVENT] =
+ g_signal_new ("event",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GimpControllerClass, event),
+ g_signal_accumulator_true_handled, NULL,
+ _gimp_widgets_marshal_BOOLEAN__POINTER,
+ G_TYPE_BOOLEAN, 1,
+ G_TYPE_POINTER);
+}
+
+static void
+gimp_controller_init (GimpController *controller)
+{
+}
+
+static void
+gimp_controller_finalize (GObject *object)
+{
+ GimpController *controller = GIMP_CONTROLLER (object);
+
+ g_clear_pointer (&controller->name, g_free);
+ g_clear_pointer (&controller->state, g_free);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_controller_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpController *controller = GIMP_CONTROLLER (object);
+
+ switch (property_id)
+ {
+ case PROP_NAME:
+ if (controller->name)
+ g_free (controller->name);
+ controller->name = g_value_dup_string (value);
+ break;
+ case PROP_STATE:
+ if (controller->state)
+ g_free (controller->state);
+ controller->state = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_controller_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpController *controller = GIMP_CONTROLLER (object);
+
+ switch (property_id)
+ {
+ case PROP_NAME:
+ g_value_set_string (value, controller->name);
+ break;
+ case PROP_STATE:
+ g_value_set_string (value, controller->state);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+GimpController *
+gimp_controller_new (GType controller_type)
+{
+ GimpController *controller;
+
+ g_return_val_if_fail (g_type_is_a (controller_type, GIMP_TYPE_CONTROLLER),
+ NULL);
+
+ controller = g_object_new (controller_type, NULL);
+
+ return controller;
+}
+
+gint
+gimp_controller_get_n_events (GimpController *controller)
+{
+ g_return_val_if_fail (GIMP_IS_CONTROLLER (controller), 0);
+
+ if (GIMP_CONTROLLER_GET_CLASS (controller)->get_n_events)
+ return GIMP_CONTROLLER_GET_CLASS (controller)->get_n_events (controller);
+
+ return 0;
+}
+
+const gchar *
+gimp_controller_get_event_name (GimpController *controller,
+ gint event_id)
+{
+ const gchar *name = NULL;
+
+ g_return_val_if_fail (GIMP_IS_CONTROLLER (controller), NULL);
+
+ if (GIMP_CONTROLLER_GET_CLASS (controller)->get_event_name)
+ name = GIMP_CONTROLLER_GET_CLASS (controller)->get_event_name (controller,
+ event_id);
+
+ if (! name)
+ name = "<invalid event id>";
+
+ return name;
+}
+
+const gchar *
+gimp_controller_get_event_blurb (GimpController *controller,
+ gint event_id)
+{
+ const gchar *blurb = NULL;
+
+ g_return_val_if_fail (GIMP_IS_CONTROLLER (controller), NULL);
+
+ if (GIMP_CONTROLLER_GET_CLASS (controller)->get_event_blurb)
+ blurb = GIMP_CONTROLLER_GET_CLASS (controller)->get_event_blurb (controller,
+ event_id);
+
+ if (! blurb)
+ blurb = "<invalid event id>";
+
+ return blurb;
+}
+
+gboolean
+gimp_controller_event (GimpController *controller,
+ const GimpControllerEvent *event)
+{
+ gboolean retval = FALSE;
+
+ g_return_val_if_fail (GIMP_IS_CONTROLLER (controller), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ g_signal_emit (controller, controller_signals[EVENT], 0,
+ event, &retval);
+
+ return retval;
+}
diff --git a/libgimpwidgets/gimpcontroller.h b/libgimpwidgets/gimpcontroller.h
new file mode 100644
index 0000000..b258084
--- /dev/null
+++ b/libgimpwidgets/gimpcontroller.h
@@ -0,0 +1,183 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpcontroller.h
+ * Copyright (C) 2004 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef GIMP_ENABLE_CONTROLLER_UNDER_CONSTRUCTION
+#error GimpController is unstable API under construction
+#endif
+
+#ifndef __GIMP_CONTROLLER_H__
+#define __GIMP_CONTROLLER_H__
+
+G_BEGIN_DECLS
+
+/* For information look at the html documentation */
+
+
+/**
+ * GimpControllerEventType:
+ * @GIMP_CONTROLLER_EVENT_TRIGGER: the event is a simple trigger
+ * @GIMP_CONTROLLER_EVENT_VALUE: the event carries a double value
+ *
+ * Event types for #GimpController.
+ **/
+typedef enum
+{
+ GIMP_CONTROLLER_EVENT_TRIGGER,
+ GIMP_CONTROLLER_EVENT_VALUE
+} GimpControllerEventType;
+
+
+typedef struct _GimpControllerEventAny GimpControllerEventAny;
+typedef struct _GimpControllerEventTrigger GimpControllerEventTrigger;
+typedef struct _GimpControllerEventValue GimpControllerEventValue;
+typedef union _GimpControllerEvent GimpControllerEvent;
+
+/**
+ * GimpControllerEventAny:
+ * @type: The event's #GimpControllerEventType
+ * @source: The event's source #GimpController
+ * @event_id: The event's ID
+ *
+ * Generic controller event. Every event has these three members at the
+ * beginning of its struct
+ **/
+struct _GimpControllerEventAny
+{
+ GimpControllerEventType type;
+ GimpController *source;
+ gint event_id;
+};
+
+/**
+ * GimpControllerEventTrigger:
+ * @type: The event's #GimpControllerEventType
+ * @source: The event's source #GimpController
+ * @event_id: The event's ID
+ *
+ * Trigger controller event.
+ **/
+struct _GimpControllerEventTrigger
+{
+ GimpControllerEventType type;
+ GimpController *source;
+ gint event_id;
+};
+
+/**
+ * GimpControllerEventValue:
+ * @type: The event's #GimpControllerEventType
+ * @source: The event's source #GimpController
+ * @event_id: The event's ID
+ * @value: The event's value
+ *
+ * Value controller event.
+ **/
+struct _GimpControllerEventValue
+{
+ GimpControllerEventType type;
+ GimpController *source;
+ gint event_id;
+ GValue value;
+};
+
+/**
+ * GimpControllerEvent:
+ * @type: The event type
+ * @any: GimpControllerEventAny
+ * @trigger: GimpControllerEventTrigger
+ * @value: GimpControllerEventValue
+ *
+ * A union to hjold all event event types
+ **/
+union _GimpControllerEvent
+{
+ GimpControllerEventType type;
+ GimpControllerEventAny any;
+ GimpControllerEventTrigger trigger;
+ GimpControllerEventValue value;
+};
+
+
+#define GIMP_TYPE_CONTROLLER (gimp_controller_get_type ())
+#define GIMP_CONTROLLER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CONTROLLER, GimpController))
+#define GIMP_CONTROLLER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CONTROLLER, GimpControllerClass))
+#define GIMP_IS_CONTROLLER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CONTROLLER))
+#define GIMP_IS_CONTROLLER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CONTROLLER))
+#define GIMP_CONTROLLER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CONTROLLER, GimpControllerClass))
+
+
+typedef struct _GimpControllerClass GimpControllerClass;
+
+struct _GimpController
+{
+ GObject parent_instance;
+
+ gchar *name;
+ gchar *state;
+};
+
+struct _GimpControllerClass
+{
+ GObjectClass parent_class;
+
+ const gchar *name;
+ const gchar *help_domain;
+ const gchar *help_id;
+
+ /* virtual functions */
+ gint (* get_n_events) (GimpController *controller);
+ const gchar * (* get_event_name) (GimpController *controller,
+ gint event_id);
+ const gchar * (* get_event_blurb) (GimpController *controller,
+ gint event_id);
+
+ /* signals */
+ gboolean (* event) (GimpController *controller,
+ const GimpControllerEvent *event);
+
+ const gchar *icon_name;
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_controller_get_type (void) G_GNUC_CONST;
+GimpController * gimp_controller_new (GType controller_type);
+
+gint gimp_controller_get_n_events (GimpController *controller);
+const gchar * gimp_controller_get_event_name (GimpController *controller,
+ gint event_id);
+const gchar * gimp_controller_get_event_blurb (GimpController *controller,
+ gint event_id);
+
+
+/* protected */
+
+gboolean gimp_controller_event (GimpController *controller,
+ const GimpControllerEvent *event);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_CONTROLLER_H__ */
diff --git a/libgimpwidgets/gimpdialog.c b/libgimpwidgets/gimpdialog.c
new file mode 100644
index 0000000..be92c40
--- /dev/null
+++ b/libgimpwidgets/gimpdialog.c
@@ -0,0 +1,689 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpdialog.c
+ * Copyright (C) 2000-2003 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpdialog.h"
+#include "gimphelpui.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpdialog
+ * @title: GimpDialog
+ * @short_description: Constructors for #GtkDialog's and action_areas as
+ * well as other dialog-related stuff.
+ *
+ * Constructors for #GtkDialog's and action_areas as well as other
+ * dialog-related stuff.
+ **/
+
+
+enum
+{
+ PROP_0,
+ PROP_HELP_FUNC,
+ PROP_HELP_ID,
+ PROP_PARENT
+};
+
+
+typedef struct _GimpDialogPrivate GimpDialogPrivate;
+
+struct _GimpDialogPrivate
+{
+ GimpHelpFunc help_func;
+ gchar *help_id;
+ GtkWidget *help_button;
+};
+
+#define GET_PRIVATE(dialog) ((GimpDialogPrivate *) gimp_dialog_get_instance_private ((GimpDialog *) (dialog)))
+
+
+static void gimp_dialog_constructed (GObject *object);
+static void gimp_dialog_dispose (GObject *object);
+static void gimp_dialog_finalize (GObject *object);
+static void gimp_dialog_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_dialog_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_dialog_hide (GtkWidget *widget);
+static gboolean gimp_dialog_delete_event (GtkWidget *widget,
+ GdkEventAny *event);
+
+static void gimp_dialog_close (GtkDialog *dialog);
+
+static void gimp_dialog_help (GObject *dialog);
+static void gimp_dialog_response (GtkDialog *dialog,
+ gint response_id);
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpDialog, gimp_dialog, GTK_TYPE_DIALOG)
+
+#define parent_class gimp_dialog_parent_class
+
+static gboolean show_help_button = TRUE;
+
+
+static void
+gimp_dialog_class_init (GimpDialogClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GtkDialogClass *dialog_class = GTK_DIALOG_CLASS (klass);
+
+ object_class->constructed = gimp_dialog_constructed;
+ object_class->dispose = gimp_dialog_dispose;
+ object_class->finalize = gimp_dialog_finalize;
+ object_class->set_property = gimp_dialog_set_property;
+ object_class->get_property = gimp_dialog_get_property;
+
+ widget_class->hide = gimp_dialog_hide;
+ widget_class->delete_event = gimp_dialog_delete_event;
+
+ dialog_class->close = gimp_dialog_close;
+
+ /**
+ * GimpDialog:help-func:
+ *
+ * Since: 2.2
+ **/
+ g_object_class_install_property (object_class, PROP_HELP_FUNC,
+ g_param_spec_pointer ("help-func",
+ "Help Func",
+ "The help function to call when F1 is hit",
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * GimpDialog:help-id:
+ *
+ * Since: 2.2
+ **/
+ g_object_class_install_property (object_class, PROP_HELP_ID,
+ g_param_spec_string ("help-id",
+ "Help ID",
+ "The help ID to pass to help-func",
+ NULL,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ /**
+ * GimpDialog:parent:
+ *
+ * Since: 2.8
+ **/
+ g_object_class_install_property (object_class, PROP_PARENT,
+ g_param_spec_object ("parent",
+ "Parent",
+ "The dialog's parent widget",
+ GTK_TYPE_WIDGET,
+ GIMP_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+gimp_dialog_init (GimpDialog *dialog)
+{
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (gimp_dialog_response),
+ NULL);
+}
+
+static void
+gimp_dialog_constructed (GObject *object)
+{
+ GimpDialogPrivate *private = GET_PRIVATE (object);
+
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ if (private->help_func)
+ gimp_help_connect (GTK_WIDGET (object),
+ private->help_func, private->help_id,
+ object);
+
+ if (show_help_button && private->help_func && private->help_id)
+ {
+ GtkDialog *dialog = GTK_DIALOG (object);
+ GtkWidget *action_area = gtk_dialog_get_action_area (dialog);
+
+ private->help_button = gtk_button_new_with_mnemonic (_("_Help"));
+
+ gtk_box_pack_end (GTK_BOX (action_area), private->help_button,
+ FALSE, TRUE, 0);
+ gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (action_area),
+ private->help_button, TRUE);
+ gtk_widget_show (private->help_button);
+
+ g_signal_connect_object (private->help_button, "clicked",
+ G_CALLBACK (gimp_dialog_help),
+ dialog, G_CONNECT_SWAPPED);
+ }
+}
+
+static void
+gimp_dialog_dispose (GObject *object)
+{
+ GdkDisplay *display = NULL;
+
+ if (g_main_depth () == 0)
+ {
+ display = gtk_widget_get_display (GTK_WIDGET (object));
+ g_object_ref (display);
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+
+ if (display)
+ {
+ gdk_display_flush (display);
+ g_object_unref (display);
+ }
+}
+
+static void
+gimp_dialog_finalize (GObject *object)
+{
+ GimpDialogPrivate *private = GET_PRIVATE (object);
+
+ g_clear_pointer (&private->help_id, g_free);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_dialog_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpDialogPrivate *private = GET_PRIVATE (object);
+
+ switch (property_id)
+ {
+ case PROP_HELP_FUNC:
+ private->help_func = g_value_get_pointer (value);
+ break;
+
+ case PROP_HELP_ID:
+ g_free (private->help_id);
+ private->help_id = g_value_dup_string (value);
+ gimp_help_set_help_data (GTK_WIDGET (object), NULL, private->help_id);
+ break;
+
+ case PROP_PARENT:
+ {
+ GtkWidget *parent = g_value_get_object (value);
+
+ if (parent)
+ {
+ if (GTK_IS_WINDOW (parent))
+ {
+ gtk_window_set_transient_for (GTK_WINDOW (object),
+ GTK_WINDOW (parent));
+ }
+ else
+ {
+ gtk_window_set_screen (GTK_WINDOW (object),
+ gtk_widget_get_screen (parent));
+ gtk_window_set_position (GTK_WINDOW (object),
+ GTK_WIN_POS_MOUSE);
+ }
+ }
+ }
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_dialog_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpDialogPrivate *private = GET_PRIVATE (object);
+
+ switch (property_id)
+ {
+ case PROP_HELP_FUNC:
+ g_value_set_pointer (value, private->help_func);
+ break;
+
+ case PROP_HELP_ID:
+ g_value_set_string (value, private->help_id);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_dialog_hide (GtkWidget *widget)
+{
+ /* set focus to NULL so focus_out callbacks are invoked synchronously */
+ gtk_window_set_focus (GTK_WINDOW (widget), NULL);
+
+ GTK_WIDGET_CLASS (parent_class)->hide (widget);
+}
+
+static gboolean
+gimp_dialog_delete_event (GtkWidget *widget,
+ GdkEventAny *event)
+{
+ return TRUE;
+}
+
+static void
+gimp_dialog_close (GtkDialog *dialog)
+{
+ /* Synthesize delete_event to close dialog. */
+
+ GtkWidget *widget = GTK_WIDGET (dialog);
+
+ if (gtk_widget_get_window (widget))
+ {
+ GdkEvent *event = gdk_event_new (GDK_DELETE);
+
+ event->any.window = g_object_ref (gtk_widget_get_window (widget));
+ event->any.send_event = TRUE;
+
+ gtk_main_do_event (event);
+ gdk_event_free (event);
+ }
+}
+
+static void
+gimp_dialog_help (GObject *dialog)
+{
+ GimpDialogPrivate *private = GET_PRIVATE (dialog);
+
+ if (private->help_func)
+ private->help_func (private->help_id, dialog);
+}
+
+static void
+gimp_dialog_response (GtkDialog *dialog,
+ gint response_id)
+{
+ GtkWidget *action_area;
+ GList *children;
+ GList *list;
+
+ action_area = gtk_dialog_get_action_area (dialog);
+
+ children = gtk_container_get_children (GTK_CONTAINER (action_area));
+
+ for (list = children; list; list = g_list_next (list))
+ {
+ GtkWidget *widget = list->data;
+
+ if (gtk_dialog_get_response_for_widget (dialog, widget) == response_id)
+ {
+ if (! GTK_IS_BUTTON (widget) ||
+ gtk_button_get_focus_on_click (GTK_BUTTON (widget)))
+ {
+ gtk_widget_grab_focus (widget);
+ }
+
+ break;
+ }
+ }
+
+ g_list_free (children);
+}
+
+
+/**
+ * gimp_dialog_new:
+ * @title: The dialog's title which will be set with
+ * gtk_window_set_title().
+ * @role: The dialog's @role which will be set with
+ * gtk_window_set_role().
+ * @parent: The @parent widget of this dialog.
+ * @flags: The @flags (see the #GtkDialog documentation).
+ * @help_func: The function which will be called if the user presses "F1".
+ * @help_id: The help_id which will be passed to @help_func.
+ * @...: A %NULL-terminated @va_list destribing the
+ * action_area buttons.
+ *
+ * Creates a new @GimpDialog widget.
+ *
+ * This function simply packs the action_area arguments passed in "..."
+ * into a @va_list variable and passes everything to gimp_dialog_new_valist().
+ *
+ * For a description of the format of the @va_list describing the
+ * action_area buttons see gtk_dialog_new_with_buttons().
+ *
+ * Returns: A #GimpDialog.
+ **/
+GtkWidget *
+gimp_dialog_new (const gchar *title,
+ const gchar *role,
+ GtkWidget *parent,
+ GtkDialogFlags flags,
+ GimpHelpFunc help_func,
+ const gchar *help_id,
+ ...)
+{
+ GtkWidget *dialog;
+ va_list args;
+
+ g_return_val_if_fail (parent == NULL || GTK_IS_WIDGET (parent), NULL);
+ g_return_val_if_fail (title != NULL, NULL);
+ g_return_val_if_fail (role != NULL, NULL);
+
+ va_start (args, help_id);
+
+ dialog = gimp_dialog_new_valist (title, role,
+ parent, flags,
+ help_func, help_id,
+ args);
+
+ va_end (args);
+
+ return dialog;
+}
+
+/**
+ * gimp_dialog_new_valist:
+ * @title: The dialog's title which will be set with
+ * gtk_window_set_title().
+ * @role: The dialog's @role which will be set with
+ * gtk_window_set_role().
+ * @parent: The @parent widget of this dialog or %NULL.
+ * @flags: The @flags (see the #GtkDialog documentation).
+ * @help_func: The function which will be called if the user presses "F1".
+ * @help_id: The help_id which will be passed to @help_func.
+ * @args: A @va_list destribing the action_area buttons.
+ *
+ * Creates a new @GimpDialog widget. If a GtkWindow is specified as
+ * @parent then the dialog will be made transient for this window.
+ *
+ * For a description of the format of the @va_list describing the
+ * action_area buttons see gtk_dialog_new_with_buttons().
+ *
+ * Returns: A #GimpDialog.
+ **/
+GtkWidget *
+gimp_dialog_new_valist (const gchar *title,
+ const gchar *role,
+ GtkWidget *parent,
+ GtkDialogFlags flags,
+ GimpHelpFunc help_func,
+ const gchar *help_id,
+ va_list args)
+{
+ GtkWidget *dialog;
+
+ g_return_val_if_fail (title != NULL, NULL);
+ g_return_val_if_fail (role != NULL, NULL);
+ g_return_val_if_fail (parent == NULL || GTK_IS_WIDGET (parent), NULL);
+
+ dialog = g_object_new (GIMP_TYPE_DIALOG,
+ "title", title,
+ "role", role,
+ "modal", (flags & GTK_DIALOG_MODAL),
+ "help-func", help_func,
+ "help-id", help_id,
+ "parent", parent,
+ NULL);
+
+ if (parent)
+ {
+ if (flags & GTK_DIALOG_DESTROY_WITH_PARENT)
+ g_signal_connect_object (parent, "destroy",
+ G_CALLBACK (gimp_dialog_close),
+ dialog, G_CONNECT_SWAPPED);
+ }
+
+ gimp_dialog_add_buttons_valist (GIMP_DIALOG (dialog), args);
+
+ return dialog;
+}
+
+/**
+ * gimp_dialog_add_button:
+ * @dialog: The @dialog to add a button to.
+ * @button_text: text of button, or stock ID.
+ * @response_id: response ID for the button.
+ *
+ * This function is essentially the same as gtk_dialog_add_button()
+ * except it ensures there is only one help button and automatically
+ * sets the RESPONSE_OK widget as the default response.
+ *
+ * Return value: the button widget that was added.
+ **/
+GtkWidget *
+gimp_dialog_add_button (GimpDialog *dialog,
+ const gchar *button_text,
+ gint response_id)
+{
+ GtkWidget *button;
+
+ /* hide the automatically added help button if another one is added */
+ if (response_id == GTK_RESPONSE_HELP)
+ {
+ GimpDialogPrivate *private = GET_PRIVATE (dialog);
+
+ if (private->help_button)
+ gtk_widget_hide (private->help_button);
+ }
+
+ button = gtk_dialog_add_button (GTK_DIALOG (dialog), button_text,
+ response_id);
+
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK);
+ }
+
+ return button;
+}
+
+/**
+ * gimp_dialog_add_buttons:
+ * @dialog: The @dialog to add buttons to.
+ * @...: button_text-response_id pairs.
+ *
+ * This function is essentially the same as gtk_dialog_add_buttons()
+ * except it calls gimp_dialog_add_button() instead of gtk_dialog_add_button()
+ **/
+void
+gimp_dialog_add_buttons (GimpDialog *dialog,
+ ...)
+{
+ va_list args;
+
+ va_start (args, dialog);
+
+ gimp_dialog_add_buttons_valist (dialog, args);
+
+ va_end (args);
+}
+
+/**
+ * gimp_dialog_add_buttons_valist:
+ * @dialog: The @dialog to add buttons to.
+ * @args: The buttons as va_list.
+ *
+ * This function is essentially the same as gimp_dialog_add_buttons()
+ * except it takes a va_list instead of '...'
+ **/
+void
+gimp_dialog_add_buttons_valist (GimpDialog *dialog,
+ va_list args)
+{
+ const gchar *button_text;
+ gint response_id;
+
+ g_return_if_fail (GIMP_IS_DIALOG (dialog));
+
+ while ((button_text = va_arg (args, const gchar *)))
+ {
+ response_id = va_arg (args, gint);
+
+ gimp_dialog_add_button (dialog, button_text, response_id);
+ }
+}
+
+
+typedef struct
+{
+ GtkDialog *dialog;
+ gint response_id;
+ GMainLoop *loop;
+ gboolean destroyed;
+} RunInfo;
+
+static void
+run_shutdown_loop (RunInfo *ri)
+{
+ if (g_main_loop_is_running (ri->loop))
+ g_main_loop_quit (ri->loop);
+}
+
+static void
+run_unmap_handler (GtkDialog *dialog,
+ RunInfo *ri)
+{
+ run_shutdown_loop (ri);
+}
+
+static void
+run_response_handler (GtkDialog *dialog,
+ gint response_id,
+ RunInfo *ri)
+{
+ ri->response_id = response_id;
+
+ run_shutdown_loop (ri);
+}
+
+static gint
+run_delete_handler (GtkDialog *dialog,
+ GdkEventAny *event,
+ RunInfo *ri)
+{
+ run_shutdown_loop (ri);
+
+ return TRUE; /* Do not destroy */
+}
+
+static void
+run_destroy_handler (GtkDialog *dialog,
+ RunInfo *ri)
+{
+ /* shutdown_loop will be called by run_unmap_handler */
+
+ ri->destroyed = TRUE;
+}
+
+/**
+ * gimp_dialog_run:
+ * @dialog: a #GimpDialog
+ *
+ * This function does exactly the same as gtk_dialog_run() except it
+ * does not make the dialog modal while the #GMainLoop is running.
+ *
+ * Return value: response ID
+ **/
+gint
+gimp_dialog_run (GimpDialog *dialog)
+{
+ RunInfo ri = { NULL, GTK_RESPONSE_NONE, NULL };
+ gulong response_handler;
+ gulong unmap_handler;
+ gulong destroy_handler;
+ gulong delete_handler;
+
+ g_return_val_if_fail (GIMP_IS_DIALOG (dialog), -1);
+
+ g_object_ref (dialog);
+
+ gtk_window_present (GTK_WINDOW (dialog));
+
+ response_handler = g_signal_connect (dialog, "response",
+ G_CALLBACK (run_response_handler),
+ &ri);
+ unmap_handler = g_signal_connect (dialog, "unmap",
+ G_CALLBACK (run_unmap_handler),
+ &ri);
+ delete_handler = g_signal_connect (dialog, "delete-event",
+ G_CALLBACK (run_delete_handler),
+ &ri);
+ destroy_handler = g_signal_connect (dialog, "destroy",
+ G_CALLBACK (run_destroy_handler),
+ &ri);
+
+ ri.loop = g_main_loop_new (NULL, FALSE);
+
+ GDK_THREADS_LEAVE ();
+ g_main_loop_run (ri.loop);
+ GDK_THREADS_ENTER ();
+
+ g_main_loop_unref (ri.loop);
+
+ ri.loop = NULL;
+ ri.destroyed = FALSE;
+
+ if (!ri.destroyed)
+ {
+ g_signal_handler_disconnect (dialog, response_handler);
+ g_signal_handler_disconnect (dialog, unmap_handler);
+ g_signal_handler_disconnect (dialog, delete_handler);
+ g_signal_handler_disconnect (dialog, destroy_handler);
+ }
+
+ g_object_unref (dialog);
+
+ return ri.response_id;
+}
+
+/**
+ * gimp_dialogs_show_help_button:
+ * @show: whether a help button should be added when creating a GimpDialog
+ *
+ * This function is for internal use only.
+ *
+ * Since: 2.2
+ **/
+void
+gimp_dialogs_show_help_button (gboolean show)
+{
+ show_help_button = show ? TRUE : FALSE;
+}
diff --git a/libgimpwidgets/gimpdialog.h b/libgimpwidgets/gimpdialog.h
new file mode 100644
index 0000000..ce321e5
--- /dev/null
+++ b/libgimpwidgets/gimpdialog.h
@@ -0,0 +1,95 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpdialog.h
+ * Copyright (C) 2000-2003 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_DIALOG_H__
+#define __GIMP_DIALOG_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+#define GIMP_TYPE_DIALOG (gimp_dialog_get_type ())
+#define GIMP_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_DIALOG, GimpDialog))
+#define GIMP_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_DIALOG, GimpDialogClass))
+#define GIMP_IS_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_DIALOG))
+#define GIMP_IS_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_DIALOG))
+#define GIMP_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_DIALOG, GimpDialogClass))
+
+
+typedef struct _GimpDialogClass GimpDialogClass;
+
+struct _GimpDialog
+{
+ GtkDialog parent_instance;
+};
+
+struct _GimpDialogClass
+{
+ GtkDialogClass parent_class;
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_dialog_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_dialog_new (const gchar *title,
+ const gchar *role,
+ GtkWidget *parent,
+ GtkDialogFlags flags,
+ GimpHelpFunc help_func,
+ const gchar *help_id,
+ ...) G_GNUC_NULL_TERMINATED;
+
+GtkWidget * gimp_dialog_new_valist (const gchar *title,
+ const gchar *role,
+ GtkWidget *parent,
+ GtkDialogFlags flags,
+ GimpHelpFunc help_func,
+ const gchar *help_id,
+ va_list args);
+
+GtkWidget * gimp_dialog_add_button (GimpDialog *dialog,
+ const gchar *button_text,
+ gint response_id);
+void gimp_dialog_add_buttons (GimpDialog *dialog,
+ ...) G_GNUC_NULL_TERMINATED;
+void gimp_dialog_add_buttons_valist (GimpDialog *dialog,
+ va_list args);
+
+gint gimp_dialog_run (GimpDialog *dialog);
+
+/* for internal use only! */
+void gimp_dialogs_show_help_button (gboolean show);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_DIALOG_H__ */
diff --git a/libgimpwidgets/gimpeevl.c b/libgimpwidgets/gimpeevl.c
new file mode 100644
index 0000000..55bf323
--- /dev/null
+++ b/libgimpwidgets/gimpeevl.c
@@ -0,0 +1,666 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpeevl.c
+ * Copyright (C) 2008 Fredrik Alstromer <roe@excu.se>
+ * Copyright (C) 2008 Martin Nordholts <martinn@svn.gnome.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* Introducing eevl eva, the evaluator. A straightforward recursive
+ * descent parser, no fuss, no new dependencies. The lexer is hand
+ * coded, tedious, not extremely fast but works. It evaluates the
+ * expression as it goes along, and does not create a parse tree or
+ * anything, and will not optimize anything. It uses doubles for
+ * precision, with the given use case, that's enough to combat any
+ * rounding errors (as opposed to optimizing the evaluation).
+ *
+ * It relies on external unit resolving through a callback and does
+ * elementary dimensionality constraint check (e.g. "2 mm + 3 px * 4
+ * in" is an error, as L + L^2 is a mismatch). It uses setjmp/longjmp
+ * for try/catch like pattern on error, it uses g_strtod() for numeric
+ * conversions and it's non-destructive in terms of the parameters, and
+ * it's reentrant.
+ *
+ * EBNF:
+ *
+ * expression ::= term { ('+' | '-') term }* |
+ * <empty string> ;
+ *
+ * term ::= ratio { ( '*' | '/' ) ratio }* ;
+ *
+ * ratio ::= signed factor { ':' signed factor }* ;
+ *
+ * signed factor ::= ( '+' | '-' )? factor ;
+ *
+ * factor ::= quantity ( '^' signed factor )? ;
+ *
+ * quantity ::= number unit? | '(' expression ')' ;
+ *
+ * number ::= ? what g_strtod() consumes ? ;
+ *
+ * unit ::= simple unit ( '^' signed factor )? ;
+ *
+ * simple unit ::= ? what not g_strtod() consumes and not whitespace ? ;
+ *
+ * The code should match the EBNF rather closely (except for the
+ * non-terminal unit factor, which is inlined into factor) for
+ * maintainability reasons.
+ *
+ * It will allow 1++1 and 1+-1 (resulting in 2 and 0, respectively),
+ * but I figured one might want that, and I don't think it's going to
+ * throw anyone off.
+ */
+
+#include "config.h"
+
+#include <setjmp.h>
+#include <string.h>
+
+#include <glib-object.h>
+
+#include "libgimpmath/gimpmath.h"
+
+#include "gimpeevl.h"
+#include "gimpwidgets-error.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+typedef enum
+{
+ GIMP_EEVL_TOKEN_NUM = 30000,
+ GIMP_EEVL_TOKEN_IDENTIFIER = 30001,
+
+ GIMP_EEVL_TOKEN_ANY = 40000,
+
+ GIMP_EEVL_TOKEN_END = 50000
+} GimpEevlTokenType;
+
+
+typedef struct
+{
+ GimpEevlTokenType type;
+
+ union
+ {
+ gdouble fl;
+
+ struct
+ {
+ const gchar *c;
+ gint size;
+ };
+
+ } value;
+
+} GimpEevlToken;
+
+typedef struct
+{
+ const gchar *string;
+ GimpEevlOptions options;
+
+ GimpEevlToken current_token;
+ const gchar *start_of_current_token;
+
+ jmp_buf catcher;
+ const gchar *error_message;
+
+} GimpEevl;
+
+
+static void gimp_eevl_init (GimpEevl *eva,
+ const gchar *string,
+ const GimpEevlOptions *options);
+static GimpEevlQuantity gimp_eevl_complete (GimpEevl *eva);
+static GimpEevlQuantity gimp_eevl_expression (GimpEevl *eva);
+static GimpEevlQuantity gimp_eevl_term (GimpEevl *eva);
+static GimpEevlQuantity gimp_eevl_ratio (GimpEevl *eva);
+static GimpEevlQuantity gimp_eevl_signed_factor (GimpEevl *eva);
+static GimpEevlQuantity gimp_eevl_factor (GimpEevl *eva);
+static GimpEevlQuantity gimp_eevl_quantity (GimpEevl *eva);
+static gboolean gimp_eevl_accept (GimpEevl *eva,
+ GimpEevlTokenType token_type,
+ GimpEevlToken *consumed_token);
+static void gimp_eevl_lex (GimpEevl *eva);
+static void gimp_eevl_lex_accept_count (GimpEevl *eva,
+ gint count,
+ GimpEevlTokenType token_type);
+static void gimp_eevl_lex_accept_to (GimpEevl *eva,
+ gchar *to,
+ GimpEevlTokenType token_type);
+static void gimp_eevl_move_past_whitespace (GimpEevl *eva);
+static gboolean gimp_eevl_unit_identifier_start (gunichar c);
+static gboolean gimp_eevl_unit_identifier_continue (gunichar c);
+static gint gimp_eevl_unit_identifier_size (const gchar *s,
+ gint start);
+static void gimp_eevl_expect (GimpEevl *eva,
+ GimpEevlTokenType token_type,
+ GimpEevlToken *value);
+static void gimp_eevl_error (GimpEevl *eva,
+ gchar *msg);
+
+
+/**
+ * gimp_eevl_evaluate:
+ * @string: The NULL-terminated string to be evaluated.
+ * @options: Evaluations options.
+ * @result: Result of evaluation.
+ * @error_pos: Will point to the position within the string,
+ * before which the parse / evaluation error
+ * occurred. Will be set to null of no error occurred.
+ * @error_message: Will point to a static string with a semi-descriptive
+ * error message if parsing / evaluation failed.
+ *
+ * Evaluates the given arithmetic expression, along with an optional dimension
+ * analysis, and basic unit conversions.
+ *
+ * All units conversions factors are relative to some implicit
+ * base-unit (which in GIMP is inches). This is also the unit of the
+ * returned value.
+ *
+ * Returns: A #GimpEevlQuantity with a value given in the base unit along with
+ * the order of the dimension (i.e. if the base unit is inches, a dimension
+ * order of two means in^2).
+ **/
+gboolean
+gimp_eevl_evaluate (const gchar *string,
+ const GimpEevlOptions *options,
+ GimpEevlQuantity *result,
+ const gchar **error_pos,
+ GError **error)
+{
+ GimpEevl eva;
+
+ g_return_val_if_fail (g_utf8_validate (string, -1, NULL), FALSE);
+ g_return_val_if_fail (options != NULL, FALSE);
+ g_return_val_if_fail (options->unit_resolver_proc != NULL, FALSE);
+ g_return_val_if_fail (result != NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ gimp_eevl_init (&eva,
+ string,
+ options);
+
+ if (!setjmp (eva.catcher)) /* try... */
+ {
+ *result = gimp_eevl_complete (&eva);
+
+ return TRUE;
+ }
+ else /* catch.. */
+ {
+ if (error_pos)
+ *error_pos = eva.start_of_current_token;
+
+ g_set_error_literal (error,
+ GIMP_WIDGETS_ERROR,
+ GIMP_WIDGETS_PARSE_ERROR,
+ eva.error_message);
+
+ return FALSE;
+ }
+}
+
+static void
+gimp_eevl_init (GimpEevl *eva,
+ const gchar *string,
+ const GimpEevlOptions *options)
+{
+ eva->string = string;
+ eva->options = *options;
+
+ eva->current_token.type = GIMP_EEVL_TOKEN_END;
+
+ eva->error_message = NULL;
+
+ /* Preload symbol... */
+ gimp_eevl_lex (eva);
+}
+
+static GimpEevlQuantity
+gimp_eevl_complete (GimpEevl *eva)
+{
+ GimpEevlQuantity result = {0, 0};
+ GimpEevlQuantity default_unit_factor;
+ gdouble default_unit_offset;
+
+ /* Empty expression evaluates to 0 */
+ if (gimp_eevl_accept (eva, GIMP_EEVL_TOKEN_END, NULL))
+ return result;
+
+ result = gimp_eevl_expression (eva);
+
+ /* There should be nothing left to parse by now */
+ gimp_eevl_expect (eva, GIMP_EEVL_TOKEN_END, 0);
+
+ eva->options.unit_resolver_proc (NULL,
+ &default_unit_factor,
+ &default_unit_offset,
+ eva->options.data);
+
+ /* Entire expression is dimensionless, apply default unit if
+ * applicable
+ */
+ if (result.dimension == 0 && default_unit_factor.dimension != 0)
+ {
+ result.value /= default_unit_factor.value;
+ result.value += default_unit_offset;
+ result.dimension = default_unit_factor.dimension;
+ }
+ return result;
+}
+
+static GimpEevlQuantity
+gimp_eevl_expression (GimpEevl *eva)
+{
+ gboolean subtract;
+ GimpEevlQuantity evaluated_terms;
+
+ evaluated_terms = gimp_eevl_term (eva);
+
+ /* continue evaluating terms, chained with + or -. */
+ for (subtract = FALSE;
+ gimp_eevl_accept (eva, '+', NULL) ||
+ (subtract = gimp_eevl_accept (eva, '-', NULL));
+ subtract = FALSE)
+ {
+ GimpEevlQuantity new_term = gimp_eevl_term (eva);
+
+ /* If dimensions mismatch, attempt default unit assignment */
+ if (new_term.dimension != evaluated_terms.dimension)
+ {
+ GimpEevlQuantity default_unit_factor;
+ gdouble default_unit_offset;
+
+ eva->options.unit_resolver_proc (NULL,
+ &default_unit_factor,
+ &default_unit_offset,
+ eva->options.data);
+
+ if (new_term.dimension == 0 &&
+ evaluated_terms.dimension == default_unit_factor.dimension)
+ {
+ new_term.value /= default_unit_factor.value;
+ new_term.value += default_unit_offset;
+ new_term.dimension = default_unit_factor.dimension;
+ }
+ else if (evaluated_terms.dimension == 0 &&
+ new_term.dimension == default_unit_factor.dimension)
+ {
+ evaluated_terms.value /= default_unit_factor.value;
+ evaluated_terms.value += default_unit_offset;
+ evaluated_terms.dimension = default_unit_factor.dimension;
+ }
+ else
+ {
+ gimp_eevl_error (eva, "Dimension mismatch during addition");
+ }
+ }
+
+ evaluated_terms.value += (subtract ? -new_term.value : new_term.value);
+ }
+
+ return evaluated_terms;
+}
+
+static GimpEevlQuantity
+gimp_eevl_term (GimpEevl *eva)
+{
+ gboolean division;
+ GimpEevlQuantity evaluated_ratios;
+
+ evaluated_ratios = gimp_eevl_ratio (eva);
+
+ for (division = FALSE;
+ gimp_eevl_accept (eva, '*', NULL) ||
+ (division = gimp_eevl_accept (eva, '/', NULL));
+ division = FALSE)
+ {
+ GimpEevlQuantity new_ratio = gimp_eevl_ratio (eva);
+
+ if (division)
+ {
+ evaluated_ratios.value /= new_ratio.value;
+ evaluated_ratios.dimension -= new_ratio.dimension;
+ }
+ else
+ {
+ evaluated_ratios.value *= new_ratio.value;
+ evaluated_ratios.dimension += new_ratio.dimension;
+ }
+ }
+
+ return evaluated_ratios;
+}
+
+static GimpEevlQuantity
+gimp_eevl_ratio (GimpEevl *eva)
+{
+ GimpEevlQuantity evaluated_signed_factors;
+
+ if (! eva->options.ratio_expressions)
+ return gimp_eevl_signed_factor (eva);
+
+ evaluated_signed_factors = gimp_eevl_signed_factor (eva);
+
+ while (gimp_eevl_accept (eva, ':', NULL))
+ {
+ GimpEevlQuantity new_signed_factor = gimp_eevl_signed_factor (eva);
+
+ if (eva->options.ratio_invert)
+ {
+ GimpEevlQuantity temp;
+
+ temp = evaluated_signed_factors;
+ evaluated_signed_factors = new_signed_factor;
+ new_signed_factor = temp;
+ }
+
+ evaluated_signed_factors.value *= eva->options.ratio_quantity.value /
+ new_signed_factor.value;
+ evaluated_signed_factors.dimension += eva->options.ratio_quantity.dimension -
+ new_signed_factor.dimension;
+ }
+
+ return evaluated_signed_factors;
+}
+
+static GimpEevlQuantity
+gimp_eevl_signed_factor (GimpEevl *eva)
+{
+ GimpEevlQuantity result;
+ gboolean negate = FALSE;
+
+ if (! gimp_eevl_accept (eva, '+', NULL))
+ negate = gimp_eevl_accept (eva, '-', NULL);
+
+ result = gimp_eevl_factor (eva);
+
+ if (negate) result.value = -result.value;
+
+ return result;
+}
+
+static GimpEevlQuantity
+gimp_eevl_factor (GimpEevl *eva)
+{
+ GimpEevlQuantity evaluated_factor;
+
+ evaluated_factor = gimp_eevl_quantity (eva);
+
+ if (gimp_eevl_accept (eva, '^', NULL))
+ {
+ GimpEevlQuantity evaluated_exponent;
+
+ evaluated_exponent = gimp_eevl_signed_factor (eva);
+
+ if (evaluated_exponent.dimension != 0)
+ gimp_eevl_error (eva, "Exponent is not a dimensionless quantity");
+
+ evaluated_factor.value = pow (evaluated_factor.value,
+ evaluated_exponent.value);
+ evaluated_factor.dimension *= evaluated_exponent.value;
+ }
+
+ return evaluated_factor;
+}
+
+static GimpEevlQuantity
+gimp_eevl_quantity (GimpEevl *eva)
+{
+ GimpEevlQuantity evaluated_quantity = { 0, 0 };
+ GimpEevlToken consumed_token;
+
+ if (gimp_eevl_accept (eva,
+ GIMP_EEVL_TOKEN_NUM,
+ &consumed_token))
+ {
+ evaluated_quantity.value = consumed_token.value.fl;
+ }
+ else if (gimp_eevl_accept (eva, '(', NULL))
+ {
+ evaluated_quantity = gimp_eevl_expression (eva);
+ gimp_eevl_expect (eva, ')', 0);
+ }
+ else
+ {
+ gimp_eevl_error (eva, "Expected number or '('");
+ }
+
+ if (eva->current_token.type == GIMP_EEVL_TOKEN_IDENTIFIER)
+ {
+ gchar *identifier;
+ GimpEevlQuantity factor;
+ gdouble offset;
+
+ gimp_eevl_accept (eva,
+ GIMP_EEVL_TOKEN_ANY,
+ &consumed_token);
+
+ identifier = g_newa (gchar, consumed_token.value.size + 1);
+
+ strncpy (identifier, consumed_token.value.c, consumed_token.value.size);
+ identifier[consumed_token.value.size] = '\0';
+
+ if (eva->options.unit_resolver_proc (identifier,
+ &factor,
+ &offset,
+ eva->options.data))
+ {
+ if (gimp_eevl_accept (eva, '^', NULL))
+ {
+ GimpEevlQuantity evaluated_exponent;
+
+ evaluated_exponent = gimp_eevl_signed_factor (eva);
+
+ if (evaluated_exponent.dimension != 0)
+ {
+ gimp_eevl_error (eva,
+ "Exponent is not a dimensionless quantity");
+ }
+
+ if (offset != 0.0)
+ {
+ gimp_eevl_error (eva,
+ "Invalid unit exponent");
+ }
+
+ factor.value = pow (factor.value, evaluated_exponent.value);
+ factor.dimension *= evaluated_exponent.value;
+ }
+
+ evaluated_quantity.value /= factor.value;
+ evaluated_quantity.value += offset;
+ evaluated_quantity.dimension += factor.dimension;
+ }
+ else
+ {
+ gimp_eevl_error (eva, "Unit was not resolved");
+ }
+ }
+
+ return evaluated_quantity;
+}
+
+static gboolean
+gimp_eevl_accept (GimpEevl *eva,
+ GimpEevlTokenType token_type,
+ GimpEevlToken *consumed_token)
+{
+ gboolean existed = FALSE;
+
+ if (token_type == eva->current_token.type ||
+ token_type == GIMP_EEVL_TOKEN_ANY)
+ {
+ existed = TRUE;
+
+ if (consumed_token)
+ *consumed_token = eva->current_token;
+
+ /* Parse next token */
+ gimp_eevl_lex (eva);
+ }
+
+ return existed;
+}
+
+static void
+gimp_eevl_lex (GimpEevl *eva)
+{
+ const gchar *s;
+
+ gimp_eevl_move_past_whitespace (eva);
+ s = eva->string;
+ eva->start_of_current_token = s;
+
+ if (! s || s[0] == '\0')
+ {
+ /* We're all done */
+ eva->current_token.type = GIMP_EEVL_TOKEN_END;
+ }
+ else if (s[0] == '+' || s[0] == '-')
+ {
+ /* Snatch these before the g_strtod() does, otherwise they might
+ * be used in a numeric conversion.
+ */
+ gimp_eevl_lex_accept_count (eva, 1, s[0]);
+ }
+ else
+ {
+ /* Attempt to parse a numeric value */
+ gchar *endptr = NULL;
+ gdouble value = g_strtod (s, &endptr);
+
+ if (endptr && endptr != s)
+ {
+ /* A numeric could be parsed, use it */
+ eva->current_token.value.fl = value;
+
+ gimp_eevl_lex_accept_to (eva, endptr, GIMP_EEVL_TOKEN_NUM);
+ }
+ else if (gimp_eevl_unit_identifier_start (s[0]))
+ {
+ /* Unit identifier */
+ eva->current_token.value.c = s;
+ eva->current_token.value.size = gimp_eevl_unit_identifier_size (s, 0);
+
+ gimp_eevl_lex_accept_count (eva,
+ eva->current_token.value.size,
+ GIMP_EEVL_TOKEN_IDENTIFIER);
+ }
+ else
+ {
+ /* Everything else is a single character token */
+ gimp_eevl_lex_accept_count (eva, 1, s[0]);
+ }
+ }
+}
+
+static void
+gimp_eevl_lex_accept_count (GimpEevl *eva,
+ gint count,
+ GimpEevlTokenType token_type)
+{
+ eva->current_token.type = token_type;
+ eva->string += count;
+}
+
+static void
+gimp_eevl_lex_accept_to (GimpEevl *eva,
+ gchar *to,
+ GimpEevlTokenType token_type)
+{
+ eva->current_token.type = token_type;
+ eva->string = to;
+}
+
+static void
+gimp_eevl_move_past_whitespace (GimpEevl *eva)
+{
+ if (! eva->string)
+ return;
+
+ while (g_ascii_isspace (*eva->string))
+ eva->string++;
+}
+
+static gboolean
+gimp_eevl_unit_identifier_start (gunichar c)
+{
+ return (g_unichar_isalpha (c) ||
+ c == (gunichar) '%' ||
+ c == (gunichar) '\'');
+}
+
+static gboolean
+gimp_eevl_unit_identifier_continue (gunichar c)
+{
+ return (gimp_eevl_unit_identifier_start (c) ||
+ g_unichar_isdigit (c));
+}
+
+/**
+ * gimp_eevl_unit_identifier_size:
+ * @s:
+ * @start:
+ *
+ * Returns: Size of identifier in bytes (not including NULL
+ * terminator).
+ **/
+static gint
+gimp_eevl_unit_identifier_size (const gchar *string,
+ gint start_offset)
+{
+ const gchar *start = g_utf8_offset_to_pointer (string, start_offset);
+ const gchar *s = start;
+ gunichar c = g_utf8_get_char (s);
+ gint length = 0;
+
+ if (gimp_eevl_unit_identifier_start (c))
+ {
+ s = g_utf8_next_char (s);
+ c = g_utf8_get_char (s);
+ length++;
+
+ while (gimp_eevl_unit_identifier_continue (c))
+ {
+ s = g_utf8_next_char (s);
+ c = g_utf8_get_char (s);
+ length++;
+ }
+ }
+
+ return g_utf8_offset_to_pointer (start, length) - start;
+}
+
+static void
+gimp_eevl_expect (GimpEevl *eva,
+ GimpEevlTokenType token_type,
+ GimpEevlToken *value)
+{
+ if (! gimp_eevl_accept (eva, token_type, value))
+ gimp_eevl_error (eva, "Unexpected token");
+}
+
+static void
+gimp_eevl_error (GimpEevl *eva,
+ gchar *msg)
+{
+ eva->error_message = msg;
+ longjmp (eva->catcher, 1);
+}
diff --git a/libgimpwidgets/gimpeevl.h b/libgimpwidgets/gimpeevl.h
new file mode 100644
index 0000000..bbcb69b
--- /dev/null
+++ b/libgimpwidgets/gimpeevl.h
@@ -0,0 +1,99 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpeevl.h
+ * Copyright (C) 2008-2009 Fredrik Alstromer <roe@excu.se>
+ * Copyright (C) 2008-2009 Martin Nordholts <martinn@svn.gnome.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_EEVL_H__
+#define __GIMP_EEVL_H__
+
+G_BEGIN_DECLS
+
+
+/**
+ * GimpEevlQuantity:
+ * @value: In reference units.
+ * @dimension: in has a dimension of 1, in^2 has a dimension of 2 etc
+ */
+typedef struct
+{
+ gdouble value;
+ gint dimension;
+} GimpEevlQuantity;
+
+
+/**
+ * GimpEevlUnitResolverProc:
+ * @identifier: Identifier of unit to resolve or %NULL if default unit
+ * should be resolved.
+ * @factor: Units per reference unit. For example, in GIMP the
+ * reference unit is inches so resolving "mm" should
+ * return 25.4 since there are 25.4 millimeters per inch.
+ * @offset: Offset to apply after scaling the value according to @factor.
+ * @data: Data given to gimp_eevl_evaluate().
+ *
+ * Returns: If the unit was successfully resolved or not.
+ *
+ */
+typedef gboolean (* GimpEevlUnitResolverProc) (const gchar *identifier,
+ GimpEevlQuantity *factor,
+ gdouble *offset,
+ gpointer data);
+
+
+/**
+ * GimpEevlOptions:
+ * @unit_resolver_proc: Unit resolver callback.
+ * @data: Data passed to unit resolver.
+ * @ratio_expressions: Allow ratio expressions
+ * @ratio_invert: Invert ratios
+ * @ratio_quantity: Quantity to multiply ratios by
+ */
+typedef struct
+{
+ GimpEevlUnitResolverProc unit_resolver_proc;
+ gpointer data;
+
+ gboolean ratio_expressions;
+ gboolean ratio_invert;
+ GimpEevlQuantity ratio_quantity;
+} GimpEevlOptions;
+
+#define GIMP_EEVL_OPTIONS_INIT \
+ ((const GimpEevlOptions) \
+ { \
+ .unit_resolver_proc = NULL, \
+ .data = NULL, \
+ \
+ .ratio_expressions = FALSE, \
+ .ratio_invert = FALSE, \
+ .ratio_quantity = {0.0, 0} \
+ })
+
+
+gboolean gimp_eevl_evaluate (const gchar *string,
+ const GimpEevlOptions *options,
+ GimpEevlQuantity *result,
+ const gchar **error_pos,
+ GError **error);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_EEVL_H__ */
diff --git a/libgimpwidgets/gimpenumcombobox.c b/libgimpwidgets/gimpenumcombobox.c
new file mode 100644
index 0000000..0715109
--- /dev/null
+++ b/libgimpwidgets/gimpenumcombobox.c
@@ -0,0 +1,229 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpenumcombobox.c
+ * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpenumcombobox.h"
+#include "gimpenumstore.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpenumcombobox
+ * @title: GimpEnumComboBox
+ * @short_description: A #GimpIntComboBox subclass for selecting an enum value.
+ *
+ * A #GtkComboBox subclass for selecting an enum value.
+ **/
+
+
+enum
+{
+ PROP_0,
+ PROP_MODEL
+};
+
+
+static void gimp_enum_combo_box_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_enum_combo_box_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+
+G_DEFINE_TYPE (GimpEnumComboBox, gimp_enum_combo_box,
+ GIMP_TYPE_INT_COMBO_BOX)
+
+#define parent_class gimp_enum_combo_box_parent_class
+
+
+static void
+gimp_enum_combo_box_class_init (GimpEnumComboBoxClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->set_property = gimp_enum_combo_box_set_property;
+ object_class->get_property = gimp_enum_combo_box_get_property;
+
+ /* override the "model" property of GtkComboBox */
+ g_object_class_install_property (object_class,
+ PROP_MODEL,
+ g_param_spec_object ("model",
+ "Model",
+ "The enum store used by this combo box",
+ GIMP_TYPE_ENUM_STORE,
+ GIMP_PARAM_READWRITE));
+}
+
+static void
+gimp_enum_combo_box_init (GimpEnumComboBox *combo_box)
+{
+}
+
+static void
+gimp_enum_combo_box_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkComboBox *combo_box = GTK_COMBO_BOX (object);
+
+ switch (prop_id)
+ {
+ case PROP_MODEL:
+ gtk_combo_box_set_model (combo_box, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_enum_combo_box_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkComboBox *combo_box = GTK_COMBO_BOX (object);
+
+ switch (prop_id)
+ {
+ case PROP_MODEL:
+ g_value_set_object (value, gtk_combo_box_get_model (combo_box));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+/**
+ * gimp_enum_combo_box_new:
+ * @enum_type: the #GType of an enum.
+ *
+ * Creates a #GtkComboBox readily filled with all enum values from a
+ * given @enum_type. The enum needs to be registered to the type
+ * system. It should also have %GimpEnumDesc descriptions registered
+ * that contain translatable value names. This is the case for the
+ * enums used in the GIMP PDB functions.
+ *
+ * This is just a convenience function. If you need more control over
+ * the enum values that appear in the combo_box, you can create your
+ * own #GimpEnumStore and use gimp_enum_combo_box_new_with_model().
+ *
+ * Return value: a new #GimpEnumComboBox.
+ *
+ * Since: 2.4
+ **/
+GtkWidget *
+gimp_enum_combo_box_new (GType enum_type)
+{
+ GtkListStore *store;
+ GtkWidget *combo_box;
+
+ g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), NULL);
+
+ store = gimp_enum_store_new (enum_type);
+
+ combo_box = g_object_new (GIMP_TYPE_ENUM_COMBO_BOX,
+ "model", store,
+ NULL);
+
+ g_object_unref (store);
+
+ return combo_box;
+}
+
+/**
+ * gimp_enum_combo_box_new_with_model
+ * @enum_store: a #GimpEnumStore to use as the model
+ *
+ * Creates a #GtkComboBox for the given @enum_store.
+ *
+ * Return value: a new #GimpEnumComboBox.
+ *
+ * Since: 2.4
+ **/
+GtkWidget *
+gimp_enum_combo_box_new_with_model (GimpEnumStore *enum_store)
+{
+ g_return_val_if_fail (GIMP_IS_ENUM_STORE (enum_store), NULL);
+
+ return g_object_new (GIMP_TYPE_ENUM_COMBO_BOX,
+ "model", enum_store,
+ NULL);
+}
+
+/**
+ * gimp_enum_combo_box_set_stock_prefix:
+ * @combo_box: a #GimpEnumComboBox
+ * @stock_prefix: a prefix to create icon stock ID from enum values
+ *
+ * Attempts to create stock icons for all items in the @combo_box. See
+ * gimp_enum_store_set_stock_prefix() to find out what to use as
+ * @stock_prefix.
+ *
+ * Since: 2.4
+ *
+ * Deprecated: GIMP 2.10
+ **/
+void
+gimp_enum_combo_box_set_stock_prefix (GimpEnumComboBox *combo_box,
+ const gchar *stock_prefix)
+{
+ gimp_enum_combo_box_set_icon_prefix (combo_box, stock_prefix);
+}
+
+/**
+ * gimp_enum_combo_box_set_icon_prefix:
+ * @combo_box: a #GimpEnumComboBox
+ * @icon_prefix: a prefix to create icon names from enum values
+ *
+ * Attempts to create icons for all items in the @combo_box. See
+ * gimp_enum_store_set_icon_prefix() to find out what to use as
+ * @icon_prefix.
+ *
+ * Since: 2.10
+ **/
+void
+gimp_enum_combo_box_set_icon_prefix (GimpEnumComboBox *combo_box,
+ const gchar *icon_prefix)
+{
+ GtkTreeModel *model;
+
+ g_return_if_fail (GIMP_IS_ENUM_COMBO_BOX (combo_box));
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));
+
+ gimp_enum_store_set_icon_prefix (GIMP_ENUM_STORE (model), icon_prefix);
+}
diff --git a/libgimpwidgets/gimpenumcombobox.h b/libgimpwidgets/gimpenumcombobox.h
new file mode 100644
index 0000000..6d8a7c1
--- /dev/null
+++ b/libgimpwidgets/gimpenumcombobox.h
@@ -0,0 +1,74 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpenumcombobox.h
+ * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_ENUM_COMBO_BOX_H__
+#define __GIMP_ENUM_COMBO_BOX_H__
+
+#include <libgimpwidgets/gimpintcombobox.h>
+
+G_BEGIN_DECLS
+
+#define GIMP_TYPE_ENUM_COMBO_BOX (gimp_enum_combo_box_get_type ())
+#define GIMP_ENUM_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_ENUM_COMBO_BOX, GimpEnumComboBox))
+#define GIMP_ENUM_COMBO_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_ENUM_COMBO_BOX, GimpEnumComboBoxClass))
+#define GIMP_IS_ENUM_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_ENUM_COMBO_BOX))
+#define GIMP_IS_ENUM_COMBO_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_ENUM_COMBO_BOX))
+#define GIMP_ENUM_COMBO_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_ENUM_COMBO_BOX, GimpEnumComboBoxClass))
+
+
+typedef struct _GimpEnumComboBoxClass GimpEnumComboBoxClass;
+
+struct _GimpEnumComboBox
+{
+ GimpIntComboBox parent_instance;
+};
+
+struct _GimpEnumComboBoxClass
+{
+ GimpIntComboBoxClass parent_class;
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_enum_combo_box_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_enum_combo_box_new (GType enum_type);
+GtkWidget * gimp_enum_combo_box_new_with_model (GimpEnumStore *enum_store);
+
+GIMP_DEPRECATED_FOR(gimp_enum_combo_box_set_icon_prefix)
+void gimp_enum_combo_box_set_stock_prefix (GimpEnumComboBox *combo_box,
+ const gchar *stock_prefix);
+
+void gimp_enum_combo_box_set_icon_prefix (GimpEnumComboBox *combo_box,
+ const gchar *icon_prefix);
+
+G_END_DECLS
+
+#endif /* __GIMP_ENUM_COMBO_BOX_H__ */
diff --git a/libgimpwidgets/gimpenumlabel.c b/libgimpwidgets/gimpenumlabel.c
new file mode 100644
index 0000000..ad114ad
--- /dev/null
+++ b/libgimpwidgets/gimpenumlabel.c
@@ -0,0 +1,218 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpenumlabel.c
+ * Copyright (C) 2005 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpenumlabel.h"
+
+
+/**
+ * SECTION: gimpenumlabel
+ * @title: GimpEnumLabel
+ * @short_description: A #GtkLabel subclass that displays an enum value.
+ *
+ * A #GtkLabel subclass that displays an enum value.
+ **/
+
+
+enum
+{
+ PROP_0,
+ PROP_ENUM_TYPE,
+ PROP_ENUM_VALUE
+};
+
+
+static void gimp_enum_label_finalize (GObject *object);
+static void gimp_enum_label_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_enum_label_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+
+G_DEFINE_TYPE (GimpEnumLabel, gimp_enum_label, GTK_TYPE_LABEL)
+
+#define parent_class gimp_enum_label_parent_class
+
+
+static void
+gimp_enum_label_class_init (GimpEnumLabelClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gimp_enum_label_finalize;
+ object_class->get_property = gimp_enum_label_get_property;
+ object_class->set_property = gimp_enum_label_set_property;
+
+ /**
+ * GimpEnumLabel:enum-type:
+ *
+ * The #GType of the enum.
+ *
+ * Since: 2.8
+ **/
+ g_object_class_install_property (object_class, PROP_ENUM_TYPE,
+ g_param_spec_gtype ("enum-type",
+ "Enum Type",
+ "The type of the displayed enum",
+ G_TYPE_NONE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * GimpEnumLabel:enum-value:
+ *
+ * The value to display.
+ *
+ * Since: 2.8
+ **/
+ g_object_class_install_property (object_class, PROP_ENUM_VALUE,
+ g_param_spec_int ("enum-value",
+ "Enum Value",
+ "The enum value to display",
+ G_MININT, G_MAXINT, 0,
+ GIMP_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT));
+}
+
+static void
+gimp_enum_label_init (GimpEnumLabel *enum_label)
+{
+}
+
+static void
+gimp_enum_label_finalize (GObject *object)
+{
+ GimpEnumLabel *enum_label = GIMP_ENUM_LABEL (object);
+
+ g_clear_pointer (&enum_label->enum_class, g_type_class_unref);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_enum_label_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpEnumLabel *label = GIMP_ENUM_LABEL (object);
+
+ switch (property_id)
+ {
+ case PROP_ENUM_TYPE:
+ if (label->enum_class)
+ g_value_set_gtype (value, G_TYPE_FROM_CLASS (label->enum_class));
+ else
+ g_value_set_gtype (value, G_TYPE_NONE);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_enum_label_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpEnumLabel *label = GIMP_ENUM_LABEL (object);
+
+ switch (property_id)
+ {
+ case PROP_ENUM_TYPE:
+ label->enum_class = g_type_class_ref (g_value_get_gtype (value));
+ break;
+
+ case PROP_ENUM_VALUE:
+ gimp_enum_label_set_value (label, g_value_get_int (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+/**
+ * gimp_enum_label_new:
+ * @enum_type: the #GType of an enum
+ * @value: an enum value
+ *
+ * Return value: a new #GimpEnumLabel.
+ *
+ * Since: 2.4
+ **/
+GtkWidget *
+gimp_enum_label_new (GType enum_type,
+ gint value)
+{
+ g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), NULL);
+
+ return g_object_new (GIMP_TYPE_ENUM_LABEL,
+ "enum-type", enum_type,
+ "enum-value", value,
+ NULL);
+}
+
+/**
+ * gimp_enum_label_set_value
+ * @label: a #GimpEnumLabel
+ * @value: an enum value
+ *
+ * Since: 2.4
+ **/
+void
+gimp_enum_label_set_value (GimpEnumLabel *label,
+ gint value)
+{
+ const gchar *nick;
+ const gchar *desc;
+
+ g_return_if_fail (GIMP_IS_ENUM_LABEL (label));
+
+ if (! gimp_enum_get_value (G_TYPE_FROM_CLASS (label->enum_class), value,
+ NULL, &nick, &desc, NULL))
+ {
+ g_warning ("%s: %d is not valid for enum of type '%s'",
+ G_STRLOC, value,
+ g_type_name (G_TYPE_FROM_CLASS (label->enum_class)));
+ return;
+ }
+
+ if (! desc)
+ desc = nick;
+
+ gtk_label_set_text (GTK_LABEL (label), desc);
+}
diff --git a/libgimpwidgets/gimpenumlabel.h b/libgimpwidgets/gimpenumlabel.h
new file mode 100644
index 0000000..8737eb3
--- /dev/null
+++ b/libgimpwidgets/gimpenumlabel.h
@@ -0,0 +1,66 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpenumlabel.h
+ * Copyright (C) 2005 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_ENUM__LABEL_H__
+#define __GIMP_ENUM__LABEL_H__
+
+G_BEGIN_DECLS
+
+
+#define GIMP_TYPE_ENUM_LABEL (gimp_enum_label_get_type ())
+#define GIMP_ENUM_LABEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_ENUM_LABEL, GimpEnumLabel))
+#define GIMP_ENUM_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_ENUM_LABEL, GimpEnumLabelClass))
+#define GIMP_IS_ENUM_LABEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_ENUM_LABEL))
+#define GIMP_IS_ENUM_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_ENUM_LABEL))
+#define GIMP_ENUM_LABEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_ENUM_LABEL, GimpEnumLabelClass))
+
+
+typedef struct _GimpEnumLabelClass GimpEnumLabelClass;
+
+struct _GimpEnumLabel
+{
+ GtkLabel parent_instance;
+
+ /*< private >*/
+ GEnumClass *enum_class;
+};
+
+struct _GimpEnumLabelClass
+{
+ GtkLabelClass parent_class;
+};
+
+
+GType gimp_enum_label_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_enum_label_new (GType enum_type,
+ gint value);
+void gimp_enum_label_set_value (GimpEnumLabel *label,
+ gint value);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_ENUM_LABEL_H__ */
diff --git a/libgimpwidgets/gimpenumstore.c b/libgimpwidgets/gimpenumstore.c
new file mode 100644
index 0000000..c062119
--- /dev/null
+++ b/libgimpwidgets/gimpenumstore.c
@@ -0,0 +1,397 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpenumstore.c
+ * Copyright (C) 2004-2007 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpenumstore.h"
+
+
+/**
+ * SECTION: gimpenumstore
+ * @title: GimpEnumStore
+ * @short_description: A #GimpIntStore subclass that keeps enum values.
+ *
+ * A #GimpIntStore subclass that keeps enum values.
+ **/
+
+
+enum
+{
+ PROP_0,
+ PROP_ENUM_TYPE
+};
+
+
+static void gimp_enum_store_finalize (GObject *object);
+static void gimp_enum_store_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_enum_store_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_enum_store_add_value (GtkListStore *store,
+ GEnumValue *value);
+
+
+G_DEFINE_TYPE (GimpEnumStore, gimp_enum_store, GIMP_TYPE_INT_STORE)
+
+#define parent_class gimp_enum_store_parent_class
+
+
+static void
+gimp_enum_store_class_init (GimpEnumStoreClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gimp_enum_store_finalize;
+ object_class->set_property = gimp_enum_store_set_property;
+ object_class->get_property = gimp_enum_store_get_property;
+
+ /**
+ * GimpEnumStore:enum-type:
+ *
+ * Sets the #GType of the enum to be used in the store.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class,
+ PROP_ENUM_TYPE,
+ g_param_spec_gtype ("enum-type",
+ "Enum Type",
+ "The type of the enum",
+ G_TYPE_ENUM,
+ G_PARAM_CONSTRUCT_ONLY |
+ GIMP_PARAM_READWRITE));
+}
+
+static void
+gimp_enum_store_init (GimpEnumStore *store)
+{
+}
+
+static void
+gimp_enum_store_finalize (GObject *object)
+{
+ GimpEnumStore *store = GIMP_ENUM_STORE (object);
+
+ if (store->enum_class)
+ g_type_class_unref (store->enum_class);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_enum_store_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpEnumStore *store = GIMP_ENUM_STORE (object);
+
+ switch (property_id)
+ {
+ case PROP_ENUM_TYPE:
+ g_return_if_fail (store->enum_class == NULL);
+ store->enum_class = g_type_class_ref (g_value_get_gtype (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_enum_store_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpEnumStore *store = GIMP_ENUM_STORE (object);
+
+ switch (property_id)
+ {
+ case PROP_ENUM_TYPE:
+ g_value_set_gtype (value, (store->enum_class ?
+ G_TYPE_FROM_CLASS (store->enum_class) :
+ G_TYPE_NONE));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_enum_store_add_value (GtkListStore *store,
+ GEnumValue *value)
+{
+ GtkTreeIter iter = { 0, };
+ const gchar *desc;
+ const gchar *abbrev;
+ gchar *stripped;
+
+ desc = gimp_enum_value_get_desc (GIMP_ENUM_STORE (store)->enum_class, value);
+ abbrev = gimp_enum_value_get_abbrev (GIMP_ENUM_STORE (store)->enum_class, value);
+
+ /* no mnemonics in combo boxes */
+ stripped = gimp_strip_uline (desc);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ GIMP_INT_STORE_VALUE, value->value,
+ GIMP_INT_STORE_LABEL, stripped,
+ GIMP_INT_STORE_ABBREV, abbrev,
+ -1);
+
+ g_free (stripped);
+}
+
+
+/**
+ * gimp_enum_store_new:
+ * @enum_type: the #GType of an enum.
+ *
+ * Creates a new #GimpEnumStore, derived from #GtkListStore and fills
+ * it with enum values. The enum needs to be registered to the type
+ * system and should have translatable value names.
+ *
+ * Return value: a new #GimpEnumStore.
+ *
+ * Since: 2.4
+ **/
+GtkListStore *
+gimp_enum_store_new (GType enum_type)
+{
+ GtkListStore *store;
+ GEnumClass *enum_class;
+
+ g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), NULL);
+
+ enum_class = g_type_class_ref (enum_type);
+
+ store = gimp_enum_store_new_with_range (enum_type,
+ enum_class->minimum,
+ enum_class->maximum);
+
+ g_type_class_unref (enum_class);
+
+ return store;
+}
+
+/**
+ * gimp_enum_store_new_with_range:
+ * @enum_type: the #GType of an enum.
+ * @minimum: the minimum value to include
+ * @maximum: the maximum value to include
+ *
+ * Creates a new #GimpEnumStore like gimp_enum_store_new() but allows
+ * to limit the enum values to a certain range. Values smaller than
+ * @minimum or larger than @maximum are not added to the store.
+ *
+ * Return value: a new #GimpEnumStore.
+ *
+ * Since: 2.4
+ **/
+GtkListStore *
+gimp_enum_store_new_with_range (GType enum_type,
+ gint minimum,
+ gint maximum)
+{
+ GtkListStore *store;
+ GEnumValue *value;
+
+ g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), NULL);
+
+ store = g_object_new (GIMP_TYPE_ENUM_STORE,
+ "enum-type", enum_type,
+ NULL);
+
+ for (value = GIMP_ENUM_STORE (store)->enum_class->values;
+ value->value_name;
+ value++)
+ {
+ if (value->value < minimum || value->value > maximum)
+ continue;
+
+ gimp_enum_store_add_value (store, value);
+ }
+
+ return store;
+}
+
+/**
+ * gimp_enum_store_new_with_values
+ * @enum_type: the #GType of an enum.
+ * @n_values: the number of enum values to include
+ * @...: a list of enum values (exactly @n_values)
+ *
+ * Creates a new #GimpEnumStore like gimp_enum_store_new() but allows
+ * to explicitly list the enum values that should be added to the
+ * store.
+ *
+ * Return value: a new #GimpEnumStore.
+ *
+ * Since: 2.4
+ **/
+GtkListStore *
+gimp_enum_store_new_with_values (GType enum_type,
+ gint n_values,
+ ...)
+{
+ GtkListStore *store;
+ va_list args;
+
+ va_start (args, n_values);
+
+ store = gimp_enum_store_new_with_values_valist (enum_type, n_values, args);
+
+ va_end (args);
+
+ return store;
+}
+
+/**
+ * gimp_enum_store_new_with_values_valist:
+ * @enum_type: the #GType of an enum.
+ * @n_values: the number of enum values to include
+ * @args: a va_list of enum values (exactly @n_values)
+ *
+ * See gimp_enum_store_new_with_values().
+ *
+ * Return value: a new #GimpEnumStore.
+ *
+ * Since: 2.4
+ **/
+GtkListStore *
+gimp_enum_store_new_with_values_valist (GType enum_type,
+ gint n_values,
+ va_list args)
+{
+ GtkListStore *store;
+ GEnumValue *value;
+ gint i;
+
+ g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), NULL);
+ g_return_val_if_fail (n_values > 1, NULL);
+
+ store = g_object_new (GIMP_TYPE_ENUM_STORE,
+ "enum-type", enum_type,
+ NULL);
+
+ for (i = 0; i < n_values; i++)
+ {
+ value = g_enum_get_value (GIMP_ENUM_STORE (store)->enum_class,
+ va_arg (args, gint));
+
+ if (value)
+ gimp_enum_store_add_value (store, value);
+ }
+
+ return store;
+}
+
+/**
+ * gimp_enum_store_set_stock_prefix:
+ * @store: a #GimpEnumStore
+ * @stock_prefix: a prefix to create icon stock ID from enum values
+ *
+ * Creates a stock ID for each enum value in the @store by appending
+ * the value's nick to the given @stock_prefix, separated by a hyphen.
+ *
+ * See also: gimp_enum_combo_box_set_stock_prefix().
+ *
+ * Since: 2.4
+ *
+ * Deprecated: GIMP 2.10
+ **/
+void
+gimp_enum_store_set_stock_prefix (GimpEnumStore *store,
+ const gchar *stock_prefix)
+{
+ gimp_enum_store_set_icon_prefix (store, stock_prefix);
+}
+
+/**
+ * gimp_enum_store_set_icon_prefix:
+ * @store: a #GimpEnumStore
+ * @icon_prefix: a prefix to create icon names from enum values
+ *
+ * Creates an icon name for each enum value in the @store by appending
+ * the value's nick to the given @icon_prefix, separated by a hyphen.
+ *
+ * See also: gimp_enum_combo_box_set_icon_prefix().
+ *
+ * Since: 2.10
+ **/
+void
+gimp_enum_store_set_icon_prefix (GimpEnumStore *store,
+ const gchar *icon_prefix)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gboolean iter_valid;
+
+ g_return_if_fail (GIMP_IS_ENUM_STORE (store));
+
+ model = GTK_TREE_MODEL (store);
+
+ for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
+ iter_valid;
+ iter_valid = gtk_tree_model_iter_next (model, &iter))
+ {
+ gchar *icon_name = NULL;
+
+ if (icon_prefix)
+ {
+ GEnumValue *enum_value;
+ gint value;
+
+ gtk_tree_model_get (model, &iter,
+ GIMP_INT_STORE_VALUE, &value,
+ -1);
+
+ enum_value = g_enum_get_value (store->enum_class, value);
+
+ if (enum_value)
+ {
+ icon_name = g_strconcat (icon_prefix, "-",
+ enum_value->value_nick,
+ NULL);
+ }
+ }
+
+ gtk_list_store_set (GTK_LIST_STORE (store), &iter,
+ GIMP_INT_STORE_ICON_NAME, icon_name,
+ -1);
+
+ if (icon_name)
+ g_free (icon_name);
+ }
+}
diff --git a/libgimpwidgets/gimpenumstore.h b/libgimpwidgets/gimpenumstore.h
new file mode 100644
index 0000000..738eab0
--- /dev/null
+++ b/libgimpwidgets/gimpenumstore.h
@@ -0,0 +1,85 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpenumstore.h
+ * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_ENUM_STORE_H__
+#define __GIMP_ENUM_STORE_H__
+
+#include <libgimpwidgets/gimpintstore.h>
+
+
+G_BEGIN_DECLS
+
+#define GIMP_TYPE_ENUM_STORE (gimp_enum_store_get_type ())
+#define GIMP_ENUM_STORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_ENUM_STORE, GimpEnumStore))
+#define GIMP_ENUM_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_ENUM_STORE, GimpEnumStoreClass))
+#define GIMP_IS_ENUM_STORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_ENUM_STORE))
+#define GIMP_IS_ENUM_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_ENUM_STORE))
+#define GIMP_ENUM_STORE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_ENUM_STORE, GimpEnumStoreClass))
+
+
+typedef struct _GimpEnumStoreClass GimpEnumStoreClass;
+
+struct _GimpEnumStore
+{
+ GimpIntStore parent_instance;
+
+ GEnumClass *enum_class;
+};
+
+struct _GimpEnumStoreClass
+{
+ GimpIntStoreClass parent_class;
+
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_enum_store_get_type (void) G_GNUC_CONST;
+
+GtkListStore * gimp_enum_store_new (GType enum_type);
+GtkListStore * gimp_enum_store_new_with_range (GType enum_type,
+ gint minimum,
+ gint maximum);
+GtkListStore * gimp_enum_store_new_with_values (GType enum_type,
+ gint n_values,
+ ...);
+GtkListStore * gimp_enum_store_new_with_values_valist (GType enum_type,
+ gint n_values,
+ va_list args);
+
+GIMP_DEPRECATED_FOR(gimp_enum_store_set_icon_prefix)
+void gimp_enum_store_set_stock_prefix (GimpEnumStore *store,
+ const gchar *stock_prefix);
+
+void gimp_enum_store_set_icon_prefix (GimpEnumStore *store,
+ const gchar *icon_prefix);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_ENUM_STORE_H__ */
diff --git a/libgimpwidgets/gimpenumwidgets.c b/libgimpwidgets/gimpenumwidgets.c
new file mode 100644
index 0000000..1f30d1e
--- /dev/null
+++ b/libgimpwidgets/gimpenumwidgets.c
@@ -0,0 +1,536 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpenumwidgets.c
+ * Copyright (C) 2002-2004 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpenumwidgets.h"
+#include "gimpframe.h"
+#include "gimphelpui.h"
+#include "gimp3migration.h"
+
+
+/**
+ * SECTION: gimpenumwidgets
+ * @title: GimpEnumWidgets
+ * @short_description: A set of utility functions to create widgets
+ * based on enums.
+ *
+ * A set of utility functions to create widgets based on enums.
+ **/
+
+
+/**
+ * gimp_enum_radio_box_new:
+ * @enum_type: the #GType of an enum.
+ * @callback: a callback to connect to the "toggled" signal of each
+ * #GtkRadioButton that is created.
+ * @callback_data: data to pass to the @callback.
+ * @first_button: returns the first button in the created group.
+ *
+ * Creates a new group of #GtkRadioButtons representing the enum
+ * values. A group of radiobuttons is a good way to represent enums
+ * with up to three or four values. Often it is better to use a
+ * #GimpEnumComboBox instead.
+ *
+ * Return value: a new #GtkVBox holding a group of #GtkRadioButtons.
+ *
+ * Since: 2.4
+ **/
+GtkWidget *
+gimp_enum_radio_box_new (GType enum_type,
+ GCallback callback,
+ gpointer callback_data,
+ GtkWidget **first_button)
+{
+ GEnumClass *enum_class;
+ GtkWidget *vbox;
+
+ g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), NULL);
+
+ enum_class = g_type_class_ref (enum_type);
+
+ vbox = gimp_enum_radio_box_new_with_range (enum_type,
+ enum_class->minimum,
+ enum_class->maximum,
+ callback, callback_data,
+ first_button);
+
+ g_type_class_unref (enum_class);
+
+ return vbox;
+}
+
+/**
+ * gimp_enum_radio_box_new_with_range:
+ * @minimum: the minimum enum value
+ * @maximum: the maximum enum value
+ * @enum_type: the #GType of an enum.
+ * @callback: a callback to connect to the "toggled" signal of each
+ * #GtkRadioButton that is created.
+ * @callback_data: data to pass to the @callback.
+ * @first_button: returns the first button in the created group.
+ *
+ * Just like gimp_enum_radio_box_new(), this function creates a group
+ * of radio buttons, but additionally it supports limiting the range
+ * of available enum values.
+ *
+ * Return value: a new #GtkVBox holding a group of #GtkRadioButtons.
+ *
+ * Since: 2.4
+ **/
+GtkWidget *
+gimp_enum_radio_box_new_with_range (GType enum_type,
+ gint minimum,
+ gint maximum,
+ GCallback callback,
+ gpointer callback_data,
+ GtkWidget **first_button)
+{
+ GtkWidget *vbox;
+ GtkWidget *button;
+ GEnumClass *enum_class;
+ GEnumValue *value;
+ GSList *group = NULL;
+
+ g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), NULL);
+
+ enum_class = g_type_class_ref (enum_type);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 1);
+ g_object_weak_ref (G_OBJECT (vbox),
+ (GWeakNotify) g_type_class_unref, enum_class);
+
+ if (first_button)
+ *first_button = NULL;
+
+ for (value = enum_class->values; value->value_name; value++)
+ {
+ const gchar *desc;
+
+ if (value->value < minimum || value->value > maximum)
+ continue;
+
+ desc = gimp_enum_value_get_desc (enum_class, value);
+
+ button = gtk_radio_button_new_with_mnemonic (group, desc);
+
+ if (first_button && *first_button == NULL)
+ *first_button = button;
+
+ group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_object_set_data (G_OBJECT (button), "gimp-item-data",
+ GINT_TO_POINTER (value->value));
+
+ if (callback)
+ g_signal_connect (button, "toggled",
+ callback,
+ callback_data);
+ }
+
+ return vbox;
+}
+
+/**
+ * gimp_enum_radio_frame_new:
+ * @enum_type: the #GType of an enum.
+ * @label_widget: a widget to use as label for the frame that will
+ * hold the radio box.
+ * @callback: a callback to connect to the "toggled" signal of each
+ * #GtkRadioButton that is created.
+ * @callback_data: data to pass to the @callback.
+ * @first_button: returns the first button in the created group.
+ *
+ * Calls gimp_enum_radio_box_new() and puts the resulting vbox into a
+ * #GtkFrame.
+ *
+ * Return value: a new #GtkFrame holding a group of #GtkRadioButtons.
+ *
+ * Since: 2.4
+ **/
+GtkWidget *
+gimp_enum_radio_frame_new (GType enum_type,
+ GtkWidget *label_widget,
+ GCallback callback,
+ gpointer callback_data,
+ GtkWidget **first_button)
+{
+ GtkWidget *frame;
+ GtkWidget *radio_box;
+
+ g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), NULL);
+ g_return_val_if_fail (label_widget == NULL || GTK_IS_WIDGET (label_widget),
+ NULL);
+
+ frame = gimp_frame_new (NULL);
+
+ if (label_widget)
+ {
+ gtk_frame_set_label_widget (GTK_FRAME (frame), label_widget);
+ gtk_widget_show (label_widget);
+ }
+
+ radio_box = gimp_enum_radio_box_new (enum_type,
+ callback, callback_data,
+ first_button);
+ gtk_container_add (GTK_CONTAINER (frame), radio_box);
+ gtk_widget_show (radio_box);
+
+ return frame;
+}
+
+/**
+ * gimp_enum_radio_frame_new_with_range:
+ * @enum_type: the #GType of an enum.
+ * @minimum: the minimum enum value
+ * @maximum: the maximum enum value
+ * @label_widget: a widget to put into the frame that will hold the radio box.
+ * @callback: a callback to connect to the "toggled" signal of each
+ * #GtkRadioButton that is created.
+ * @callback_data: data to pass to the @callback.
+ * @first_button: returns the first button in the created group.
+ *
+ * Calls gimp_enum_radio_box_new_with_range() and puts the resulting
+ * vbox into a #GtkFrame.
+ *
+ * Return value: a new #GtkFrame holding a group of #GtkRadioButtons.
+ *
+ * Since: 2.4
+ **/
+GtkWidget *
+gimp_enum_radio_frame_new_with_range (GType enum_type,
+ gint minimum,
+ gint maximum,
+ GtkWidget *label_widget,
+ GCallback callback,
+ gpointer callback_data,
+ GtkWidget **first_button)
+{
+ GtkWidget *frame;
+ GtkWidget *radio_box;
+
+ g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), NULL);
+ g_return_val_if_fail (label_widget == NULL || GTK_IS_WIDGET (label_widget),
+ NULL);
+
+ frame = gimp_frame_new (NULL);
+
+ if (label_widget)
+ {
+ gtk_frame_set_label_widget (GTK_FRAME (frame), label_widget);
+ gtk_widget_show (label_widget);
+ }
+
+ radio_box = gimp_enum_radio_box_new_with_range (enum_type,
+ minimum,
+ maximum,
+ callback, callback_data,
+ first_button);
+ gtk_container_add (GTK_CONTAINER (frame), radio_box);
+ gtk_widget_show (radio_box);
+
+ return frame;
+}
+
+
+/**
+ * gimp_enum_stock_box_new:
+ * @enum_type: the #GType of an enum.
+ * @stock_prefix: the prefix of the group of stock ids to use.
+ * @icon_size: the icon size for the stock icons
+ * @callback: a callback to connect to the "toggled" signal of each
+ * #GtkRadioButton that is created.
+ * @callback_data: data to pass to the @callback.
+ * @first_button: returns the first button in the created group.
+ *
+ * Creates a horizontal box of radio buttons with stock icons. The
+ * stock_id for each icon is created by appending the enum_value's
+ * nick to the given @stock_prefix.
+ *
+ * Return value: a new #GtkHBox holding a group of #GtkRadioButtons.
+ *
+ * Since: 2.4
+ *
+ * Deprecated: GIMP 2.10
+ **/
+GtkWidget *
+gimp_enum_stock_box_new (GType enum_type,
+ const gchar *stock_prefix,
+ GtkIconSize icon_size,
+ GCallback callback,
+ gpointer callback_data,
+ GtkWidget **first_button)
+{
+ return gimp_enum_icon_box_new (enum_type, stock_prefix, icon_size,
+ callback, callback_data,
+ first_button);
+}
+
+/**
+ * gimp_enum_stock_box_new_with_range:
+ * @enum_type: the #GType of an enum.
+ * @minimum: the minumim enum value
+ * @maximum: the maximum enum value
+ * @stock_prefix: the prefix of the group of stock ids to use.
+ * @icon_size: the icon size for the stock icons
+ * @callback: a callback to connect to the "toggled" signal of each
+ * #GtkRadioButton that is created.
+ * @callback_data: data to pass to the @callback.
+ * @first_button: returns the first button in the created group.
+ *
+ * Just like gimp_enum_stock_box_new(), this function creates a group
+ * of radio buttons, but additionally it supports limiting the range
+ * of available enum values.
+ *
+ * Return value: a new #GtkHBox holding a group of #GtkRadioButtons.
+ *
+ * Since: 2.4
+ *
+ * Deprecated: GIMP 2.10
+ **/
+GtkWidget *
+gimp_enum_stock_box_new_with_range (GType enum_type,
+ gint minimum,
+ gint maximum,
+ const gchar *stock_prefix,
+ GtkIconSize icon_size,
+ GCallback callback,
+ gpointer callback_data,
+ GtkWidget **first_button)
+{
+ return gimp_enum_icon_box_new_with_range (enum_type, minimum, maximum,
+ stock_prefix, icon_size,
+ callback, callback_data,
+ first_button);
+}
+
+/**
+ * gimp_enum_stock_box_set_child_padding:
+ * @stock_box: a stock box widget
+ * @xpad: horizontal padding
+ * @ypad: vertical padding
+ *
+ * Sets the padding of all buttons in a box created by
+ * gimp_enum_stock_box_new().
+ *
+ * Since: 2.4
+ *
+ * Deprecated: GIMP 2.10
+ **/
+void
+gimp_enum_stock_box_set_child_padding (GtkWidget *stock_box,
+ gint xpad,
+ gint ypad)
+{
+ gimp_enum_icon_box_set_child_padding (stock_box, xpad, ypad);
+}
+
+/**
+ * gimp_enum_icon_box_new:
+ * @enum_type: the #GType of an enum.
+ * @icon_prefix: the prefix of the group of icon names to use.
+ * @icon_size: the icon size for the icons
+ * @callback: a callback to connect to the "toggled" signal of each
+ * #GtkRadioButton that is created.
+ * @callback_data: data to pass to the @callback.
+ * @first_button: returns the first button in the created group.
+ *
+ * Creates a horizontal box of radio buttons with named icons. The
+ * icon name for each icon is created by appending the enum_value's
+ * nick to the given @icon_prefix.
+ *
+ * Return value: a new #GtkHBox holding a group of #GtkRadioButtons.
+ *
+ * Since: 2.10
+ **/
+GtkWidget *
+gimp_enum_icon_box_new (GType enum_type,
+ const gchar *icon_prefix,
+ GtkIconSize icon_size,
+ GCallback callback,
+ gpointer callback_data,
+ GtkWidget **first_button)
+{
+ GEnumClass *enum_class;
+ GtkWidget *box;
+
+ g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), NULL);
+
+ enum_class = g_type_class_ref (enum_type);
+
+ box = gimp_enum_icon_box_new_with_range (enum_type,
+ enum_class->minimum,
+ enum_class->maximum,
+ icon_prefix, icon_size,
+ callback, callback_data,
+ first_button);
+
+ g_type_class_unref (enum_class);
+
+ return box;
+}
+
+/**
+ * gimp_enum_icon_box_new_with_range:
+ * @enum_type: the #GType of an enum.
+ * @minimum: the minumim enum value
+ * @maximum: the maximum enum value
+ * @icon_prefix: the prefix of the group of icon names to use.
+ * @icon_size: the icon size for the icons
+ * @callback: a callback to connect to the "toggled" signal of each
+ * #GtkRadioButton that is created.
+ * @callback_data: data to pass to the @callback.
+ * @first_button: returns the first button in the created group.
+ *
+ * Just like gimp_enum_icon_box_new(), this function creates a group
+ * of radio buttons, but additionally it supports limiting the range
+ * of available enum values.
+ *
+ * Return value: a new #GtkHBox holding a group of #GtkRadioButtons.
+ *
+ * Since: 2.10
+ **/
+GtkWidget *
+gimp_enum_icon_box_new_with_range (GType enum_type,
+ gint minimum,
+ gint maximum,
+ const gchar *icon_prefix,
+ GtkIconSize icon_size,
+ GCallback callback,
+ gpointer callback_data,
+ GtkWidget **first_button)
+{
+ GtkWidget *hbox;
+ GtkWidget *button;
+ GtkWidget *image;
+ GEnumClass *enum_class;
+ GEnumValue *value;
+ gchar *icon_name;
+ GSList *group = NULL;
+
+ g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), NULL);
+ g_return_val_if_fail (icon_prefix != NULL, NULL);
+
+ enum_class = g_type_class_ref (enum_type);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ g_object_weak_ref (G_OBJECT (hbox),
+ (GWeakNotify) g_type_class_unref, enum_class);
+
+ if (first_button)
+ *first_button = NULL;
+
+ for (value = enum_class->values; value->value_name; value++)
+ {
+ if (value->value < minimum || value->value > maximum)
+ continue;
+
+ button = gtk_radio_button_new (group);
+
+ gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+ gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (button), FALSE);
+
+ if (first_button && *first_button == NULL)
+ *first_button = button;
+
+ icon_name = g_strconcat (icon_prefix, "-", value->value_nick, NULL);
+
+ image = gtk_image_new_from_icon_name (icon_name, icon_size);
+
+ g_free (icon_name);
+
+ if (image)
+ {
+ gtk_container_add (GTK_CONTAINER (button), image);
+ gtk_widget_show (image);
+ }
+
+ gimp_help_set_help_data (button,
+ gimp_enum_value_get_desc (enum_class, value),
+ NULL);
+
+ group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_object_set_data (G_OBJECT (button), "gimp-item-data",
+ GINT_TO_POINTER (value->value));
+
+ if (callback)
+ g_signal_connect (button, "toggled",
+ callback,
+ callback_data);
+ }
+
+ return hbox;
+}
+
+/**
+ * gimp_enum_icon_box_set_child_padding:
+ * @icon_box: an icon box widget
+ * @xpad: horizontal padding
+ * @ypad: vertical padding
+ *
+ * Sets the padding of all buttons in a box created by
+ * gimp_enum_icon_box_new().
+ *
+ * Since: 2.10
+ **/
+void
+gimp_enum_icon_box_set_child_padding (GtkWidget *icon_box,
+ gint xpad,
+ gint ypad)
+{
+ GList *children;
+ GList *list;
+
+ g_return_if_fail (GTK_IS_CONTAINER (icon_box));
+
+ children = gtk_container_get_children (GTK_CONTAINER (icon_box));
+
+ for (list = children; list; list = g_list_next (list))
+ {
+ GtkWidget *child = gtk_bin_get_child (GTK_BIN (list->data));
+
+ if (GTK_IS_MISC (child))
+ {
+ GtkMisc *misc = GTK_MISC (child);
+ gint misc_xpad;
+ gint misc_ypad;
+
+ gtk_misc_get_padding (misc, &misc_xpad, &misc_ypad);
+
+ gtk_misc_set_padding (misc,
+ xpad < 0 ? misc_xpad : xpad,
+ ypad < 0 ? misc_ypad : ypad);
+ }
+ }
+
+ g_list_free (children);
+}
diff --git a/libgimpwidgets/gimpenumwidgets.h b/libgimpwidgets/gimpenumwidgets.h
new file mode 100644
index 0000000..847b94a
--- /dev/null
+++ b/libgimpwidgets/gimpenumwidgets.h
@@ -0,0 +1,97 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpenumwidgets.h
+ * Copyright (C) 2002-2004 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_ENUM_WIDGETS_H__
+#define __GIMP_ENUM_WIDGETS_H__
+
+G_BEGIN_DECLS
+
+
+GtkWidget * gimp_enum_radio_box_new (GType enum_type,
+ GCallback callback,
+ gpointer callback_data,
+ GtkWidget **first_button);
+GtkWidget * gimp_enum_radio_box_new_with_range (GType enum_type,
+ gint minimum,
+ gint maximum,
+ GCallback callback,
+ gpointer callback_data,
+ GtkWidget **first_button);
+
+GtkWidget * gimp_enum_radio_frame_new (GType enum_type,
+ GtkWidget *label_widget,
+ GCallback callback,
+ gpointer callback_data,
+ GtkWidget **first_button);
+GtkWidget * gimp_enum_radio_frame_new_with_range (GType enum_type,
+ gint minimum,
+ gint maximum,
+ GtkWidget *label_widget,
+ GCallback callback,
+ gpointer callback_data,
+ GtkWidget **first_button);
+
+GIMP_DEPRECATED_FOR(gimp_enum_icon_box_new)
+GtkWidget * gimp_enum_stock_box_new (GType enum_type,
+ const gchar *stock_prefix,
+ GtkIconSize icon_size,
+ GCallback callback,
+ gpointer callback_data,
+ GtkWidget **first_button);
+GIMP_DEPRECATED_FOR(gimp_enum_icon_box_new_with_range)
+GtkWidget * gimp_enum_stock_box_new_with_range (GType enum_type,
+ gint minimum,
+ gint maximum,
+ const gchar *stock_prefix,
+ GtkIconSize icon_size,
+ GCallback callback,
+ gpointer callback_data,
+ GtkWidget **first_button);
+GIMP_DEPRECATED_FOR(gimp_enum_icon_box_set_child_padding)
+void gimp_enum_stock_box_set_child_padding (GtkWidget *stock_box,
+ gint xpad,
+ gint ypad);
+
+GtkWidget * gimp_enum_icon_box_new (GType enum_type,
+ const gchar *icon_prefix,
+ GtkIconSize icon_size,
+ GCallback callback,
+ gpointer callback_data,
+ GtkWidget **first_button);
+GtkWidget * gimp_enum_icon_box_new_with_range (GType enum_type,
+ gint minimum,
+ gint maximum,
+ const gchar *icon_prefix,
+ GtkIconSize icon_size,
+ GCallback callback,
+ gpointer callback_data,
+ GtkWidget **first_button);
+void gimp_enum_icon_box_set_child_padding (GtkWidget *icon_box,
+ gint xpad,
+ gint ypad);
+
+G_END_DECLS
+
+#endif /* __GIMP_ENUM_WIDGETS_H__ */
diff --git a/libgimpwidgets/gimpfileentry.c b/libgimpwidgets/gimpfileentry.c
new file mode 100644
index 0000000..b3cc59c
--- /dev/null
+++ b/libgimpwidgets/gimpfileentry.c
@@ -0,0 +1,499 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpfileentry.c
+ * Copyright (C) 1999-2004 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "gimpwidgetstypes.h"
+
+#undef GIMP_DISABLE_DEPRECATED
+#include "gimpfileentry.h"
+
+#include "gimphelpui.h"
+#include "gimpicons.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpfileentry
+ * @title: GimpFileEntry
+ * @short_description: Widget for entering a filename.
+ * @see_also: #GimpPathEditor
+ *
+ * This widget is used to enter filenames or directories.
+ *
+ * There is a #GtkEntry for entering the filename manually and a "..."
+ * button which will pop up a #GtkFileChooserDialog.
+ *
+ * You can restrict the #GimpFileEntry to directories. In this
+ * case the filename listbox of the #GtkFileChooser dialog will be
+ * set to directory mode.
+ *
+ * If you specify @check_valid as #TRUE in gimp_file_entry_new() the
+ * entered filename will be checked for validity and a pixmap will be
+ * shown which indicates if the file exists or not.
+ *
+ * Whenever the user changes the filename, the "filename_changed"
+ * signal will be emitted.
+ **/
+
+
+enum
+{
+ FILENAME_CHANGED,
+ LAST_SIGNAL
+};
+
+
+static void gimp_file_entry_dispose (GObject *object);
+
+static void gimp_file_entry_entry_changed (GtkWidget *widget,
+ GtkWidget *button);
+static void gimp_file_entry_entry_activate (GtkWidget *widget,
+ GimpFileEntry *entry);
+static gint gimp_file_entry_entry_focus_out (GtkWidget *widget,
+ GdkEvent *event,
+ GimpFileEntry *entry);
+static void gimp_file_entry_file_manager_clicked (GtkWidget *widget,
+ GimpFileEntry *entry);
+static void gimp_file_entry_browse_clicked (GtkWidget *widget,
+ GimpFileEntry *entry);
+static void gimp_file_entry_check_filename (GimpFileEntry *entry);
+
+
+G_DEFINE_TYPE (GimpFileEntry, gimp_file_entry, GTK_TYPE_BOX)
+
+#define parent_class gimp_file_entry_parent_class
+
+static guint gimp_file_entry_signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+gimp_file_entry_class_init (GimpFileEntryClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ /**
+ * GimpFileEntry::filename-changed:
+ *
+ * This signal is emitted whenever the user changes the filename.
+ **/
+ gimp_file_entry_signals[FILENAME_CHANGED] =
+ g_signal_new ("filename-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpFileEntryClass, filename_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ object_class->dispose = gimp_file_entry_dispose;
+
+ klass->filename_changed = NULL;
+}
+
+static void
+gimp_file_entry_init (GimpFileEntry *entry)
+{
+ GtkWidget *image;
+ GtkWidget *button;
+
+ entry->title = NULL;
+ entry->file_dialog = NULL;
+ entry->check_valid = FALSE;
+ entry->file_exists = NULL;
+
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (entry),
+ GTK_ORIENTATION_HORIZONTAL);
+
+ gtk_box_set_spacing (GTK_BOX (entry), 4);
+ gtk_box_set_homogeneous (GTK_BOX (entry), FALSE);
+
+ button = gtk_button_new ();
+ gtk_box_pack_end (GTK_BOX (entry), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ gtk_widget_set_sensitive (button, FALSE);
+
+ image = gtk_image_new_from_icon_name (GIMP_ICON_FILE_MANAGER,
+ GTK_ICON_SIZE_BUTTON);
+ gtk_container_add (GTK_CONTAINER (button), image);
+ gtk_widget_show (image);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (gimp_file_entry_file_manager_clicked),
+ entry);
+
+ gimp_help_set_help_data (button,
+ _("Show file location in the file manager"),
+ NULL);
+
+ entry->browse_button = gtk_button_new ();
+ gtk_box_pack_end (GTK_BOX (entry), entry->browse_button, FALSE, FALSE, 0);
+ gtk_widget_show (entry->browse_button);
+
+ image = gtk_image_new_from_icon_name (GIMP_ICON_DOCUMENT_OPEN,
+ GTK_ICON_SIZE_BUTTON);
+ gtk_container_add (GTK_CONTAINER (entry->browse_button), image);
+ gtk_widget_show (image);
+
+ g_signal_connect (entry->browse_button, "clicked",
+ G_CALLBACK (gimp_file_entry_browse_clicked),
+ entry);
+
+ entry->entry = gtk_entry_new ();
+ gtk_box_pack_end (GTK_BOX (entry), entry->entry, TRUE, TRUE, 0);
+ gtk_widget_show (entry->entry);
+
+ g_signal_connect (entry->entry, "changed",
+ G_CALLBACK (gimp_file_entry_entry_changed),
+ button);
+ g_signal_connect (entry->entry, "activate",
+ G_CALLBACK (gimp_file_entry_entry_activate),
+ entry);
+ g_signal_connect (entry->entry, "focus-out-event",
+ G_CALLBACK (gimp_file_entry_entry_focus_out),
+ entry);
+}
+
+static void
+gimp_file_entry_dispose (GObject *object)
+{
+ GimpFileEntry *entry = GIMP_FILE_ENTRY (object);
+
+ g_clear_pointer (&entry->file_dialog, gtk_widget_destroy);
+
+ g_clear_pointer (&entry->title, g_free);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+/**
+ * gimp_file_entry_new:
+ * @title: The title of the #GimpFileEntry dialog.
+ * @filename: The initial filename.
+ * @dir_only: %TRUE if the file entry should accept directories only.
+ * @check_valid: %TRUE if the widget should check if the entered file
+ * really exists.
+ *
+ * You should use #GtkFileChooserButton instead.
+ *
+ * Returns: A pointer to the new #GimpFileEntry widget.
+ **/
+GtkWidget *
+gimp_file_entry_new (const gchar *title,
+ const gchar *filename,
+ gboolean dir_only,
+ gboolean check_valid)
+{
+ GimpFileEntry *entry;
+
+ entry = g_object_new (GIMP_TYPE_FILE_ENTRY, NULL);
+
+ entry->title = g_strdup (title);
+ entry->dir_only = dir_only;
+ entry->check_valid = check_valid;
+
+ gimp_help_set_help_data (entry->browse_button,
+ entry->dir_only ?
+ _("Open a file selector to browse your folders") :
+ _("Open a file selector to browse your files"),
+ NULL);
+
+ if (check_valid)
+ {
+ entry->file_exists = gtk_image_new_from_icon_name ("gtk-no",
+ GTK_ICON_SIZE_BUTTON);
+ gtk_box_pack_start (GTK_BOX (entry), entry->file_exists, FALSE, FALSE, 0);
+ gtk_widget_show (entry->file_exists);
+
+ gimp_help_set_help_data (entry->file_exists,
+ entry->dir_only ?
+ _("Indicates whether or not the folder exists") :
+ _("Indicates whether or not the file exists"),
+ NULL);
+ }
+
+ gimp_file_entry_set_filename (entry, filename);
+
+ return GTK_WIDGET (entry);
+}
+
+/**
+ * gimp_file_entry_get_filename:
+ * @entry: The file entry you want to know the filename from.
+ *
+ * Note that you have to g_free() the returned string.
+ *
+ * Returns: The file or directory the user has entered.
+ **/
+gchar *
+gimp_file_entry_get_filename (GimpFileEntry *entry)
+{
+ gchar *utf8;
+ gchar *filename;
+
+ g_return_val_if_fail (GIMP_IS_FILE_ENTRY (entry), NULL);
+
+ utf8 = gtk_editable_get_chars (GTK_EDITABLE (entry->entry), 0, -1);
+
+ filename = g_filename_from_utf8 (utf8, -1, NULL, NULL, NULL);
+
+ g_free (utf8);
+
+ return filename;
+}
+
+/**
+ * gimp_file_entry_set_filename:
+ * @entry: The file entry you want to set the filename for.
+ * @filename: The new filename.
+ *
+ * If you specified @check_valid as %TRUE in gimp_file_entry_new()
+ * the #GimpFileEntry will immediately check the validity of the file
+ * name.
+ **/
+void
+gimp_file_entry_set_filename (GimpFileEntry *entry,
+ const gchar *filename)
+{
+ gchar *utf8;
+
+ g_return_if_fail (GIMP_IS_FILE_ENTRY (entry));
+
+ if (filename)
+ utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
+ else
+ utf8 = g_strdup ("");
+
+ gtk_entry_set_text (GTK_ENTRY (entry->entry), utf8);
+ g_free (utf8);
+
+ /* update everything
+ */
+ gimp_file_entry_entry_activate (entry->entry, entry);
+}
+
+static void
+gimp_file_entry_entry_changed (GtkWidget *widget,
+ GtkWidget *button)
+{
+ const gchar *text = gtk_entry_get_text (GTK_ENTRY (widget));
+
+ if (text && strlen (text))
+ gtk_widget_set_sensitive (button, TRUE);
+ else
+ gtk_widget_set_sensitive (button, FALSE);
+}
+
+static void
+gimp_file_entry_entry_activate (GtkWidget *widget,
+ GimpFileEntry *entry)
+{
+ gchar *utf8;
+ gchar *filename;
+ gint len;
+
+ /* filenames still need more sanity checking
+ * (erase double G_DIR_SEPARATORS, ...)
+ */
+ utf8 = gtk_editable_get_chars (GTK_EDITABLE (widget), 0, -1);
+ utf8 = g_strstrip (utf8);
+
+ while (((len = strlen (utf8)) > 1) &&
+ (utf8[len - 1] == G_DIR_SEPARATOR))
+ utf8[len - 1] = '\0';
+
+ filename = g_filename_from_utf8 (utf8, -1, NULL, NULL, NULL);
+
+ g_signal_handlers_block_by_func (entry->entry,
+ gimp_file_entry_entry_activate,
+ entry);
+ gtk_entry_set_text (GTK_ENTRY (entry->entry), utf8);
+ g_signal_handlers_unblock_by_func (entry->entry,
+ gimp_file_entry_entry_activate,
+ entry);
+
+ if (entry->file_dialog)
+ gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (entry->file_dialog),
+ filename);
+
+ g_free (filename);
+ g_free (utf8);
+
+ gimp_file_entry_check_filename (entry);
+
+ gtk_editable_set_position (GTK_EDITABLE (entry->entry), -1);
+
+ g_signal_emit (entry, gimp_file_entry_signals[FILENAME_CHANGED], 0);
+}
+
+static gboolean
+gimp_file_entry_entry_focus_out (GtkWidget *widget,
+ GdkEvent *event,
+ GimpFileEntry *entry)
+{
+ gimp_file_entry_entry_activate (widget, entry);
+
+ return FALSE;
+}
+
+/* local callback of gimp_file_entry_browse_clicked() */
+static void
+gimp_file_entry_chooser_response (GtkWidget *dialog,
+ gint response_id,
+ GimpFileEntry *entry)
+{
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ gchar *filename;
+
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+ gimp_file_entry_set_filename (entry, filename);
+ g_free (filename);
+ }
+
+ gtk_widget_hide (dialog);
+}
+
+static void
+gimp_file_entry_file_manager_clicked (GtkWidget *widget,
+ GimpFileEntry *entry)
+{
+ gchar *utf8;
+ GFile *file;
+ GError *error = NULL;
+
+ utf8 = gtk_editable_get_chars (GTK_EDITABLE (entry->entry), 0, -1);
+ file = g_file_parse_name (utf8);
+ g_free (utf8);
+
+ if (! gimp_file_show_in_file_manager (file, &error))
+ {
+ g_message (_("Can't show file in file manager: %s"),
+ error->message);
+ g_clear_error (&error);
+ }
+
+ g_object_unref (file);
+}
+
+static void
+gimp_file_entry_browse_clicked (GtkWidget *widget,
+ GimpFileEntry *entry)
+{
+ GtkFileChooser *chooser;
+ gchar *utf8;
+ gchar *filename;
+
+ utf8 = gtk_editable_get_chars (GTK_EDITABLE (entry->entry), 0, -1);
+ filename = g_filename_from_utf8 (utf8, -1, NULL, NULL, NULL);
+ g_free (utf8);
+
+ if (! entry->file_dialog)
+ {
+ const gchar *title = entry->title;
+
+ if (! title)
+ {
+ if (entry->dir_only)
+ title = _("Select Folder");
+ else
+ title = _("Select File");
+ }
+
+ entry->file_dialog =
+ gtk_file_chooser_dialog_new (title, NULL,
+ entry->dir_only ?
+ GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER :
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (entry->file_dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ chooser = GTK_FILE_CHOOSER (entry->file_dialog);
+
+ gtk_window_set_position (GTK_WINDOW (chooser), GTK_WIN_POS_MOUSE);
+ gtk_window_set_role (GTK_WINDOW (chooser),
+ "gimp-file-entry-file-dialog");
+
+ g_signal_connect (chooser, "response",
+ G_CALLBACK (gimp_file_entry_chooser_response),
+ entry);
+ g_signal_connect (chooser, "delete-event",
+ G_CALLBACK (gtk_true),
+ NULL);
+
+ g_signal_connect_swapped (entry, "unmap",
+ G_CALLBACK (gtk_widget_hide),
+ chooser);
+ }
+ else
+ {
+ chooser = GTK_FILE_CHOOSER (entry->file_dialog);
+ }
+
+ gtk_file_chooser_set_filename (chooser, filename);
+
+ g_free (filename);
+
+ gtk_window_set_screen (GTK_WINDOW (chooser), gtk_widget_get_screen (widget));
+ gtk_window_present (GTK_WINDOW (chooser));
+}
+
+static void
+gimp_file_entry_check_filename (GimpFileEntry *entry)
+{
+ gchar *utf8;
+ gchar *filename;
+ gboolean exists;
+
+ if (! entry->check_valid || ! entry->file_exists)
+ return;
+
+ utf8 = gtk_editable_get_chars (GTK_EDITABLE (entry->entry), 0, -1);
+ filename = g_filename_from_utf8 (utf8, -1, NULL, NULL, NULL);
+ g_free (utf8);
+
+ if (entry->dir_only)
+ exists = g_file_test (filename, G_FILE_TEST_IS_DIR);
+ else
+ exists = g_file_test (filename, G_FILE_TEST_IS_REGULAR);
+
+ g_free (filename);
+
+ gtk_image_set_from_icon_name (GTK_IMAGE (entry->file_exists),
+ exists ? "gtk-yes" : "gtk-no",
+ GTK_ICON_SIZE_BUTTON);
+}
diff --git a/libgimpwidgets/gimpfileentry.h b/libgimpwidgets/gimpfileentry.h
new file mode 100644
index 0000000..c408960
--- /dev/null
+++ b/libgimpwidgets/gimpfileentry.h
@@ -0,0 +1,91 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpfileentry.h
+ * Copyright (C) 1999-2004 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef GIMP_DISABLE_DEPRECATED
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_FILE_ENTRY_H__
+#define __GIMP_FILE_ENTRY_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+#define GIMP_TYPE_FILE_ENTRY (gimp_file_entry_get_type ())
+#define GIMP_FILE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_FILE_ENTRY, GimpFileEntry))
+#define GIMP_FILE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_FILE_ENTRY, GimpFileEntryClass))
+#define GIMP_IS_FILE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GIMP_TYPE_FILE_ENTRY))
+#define GIMP_IS_FILE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_FILE_ENTRY))
+#define GIMP_FILE_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_FILE_ENTRY, GimpFileEntryClass))
+
+
+typedef struct _GimpFileEntryClass GimpFileEntryClass;
+
+struct _GimpFileEntry
+{
+ GtkBox parent_instance;
+
+ GtkWidget *file_exists;
+ GtkWidget *entry;
+ GtkWidget *browse_button;
+
+ GtkWidget *file_dialog;
+
+ gchar *title;
+ gboolean dir_only;
+ gboolean check_valid;
+};
+
+struct _GimpFileEntryClass
+{
+ GtkBoxClass parent_class;
+
+ void (* filename_changed) (GimpFileEntry *entry);
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_file_entry_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_file_entry_new (const gchar *title,
+ const gchar *filename,
+ gboolean dir_only,
+ gboolean check_valid);
+
+gchar * gimp_file_entry_get_filename (GimpFileEntry *entry);
+void gimp_file_entry_set_filename (GimpFileEntry *entry,
+ const gchar *filename);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_FILE_ENTRY_H__ */
+
+#endif /* GIMP_DISABLE_DEPRECATED */
diff --git a/libgimpwidgets/gimpframe.c b/libgimpwidgets/gimpframe.c
new file mode 100644
index 0000000..97fb487
--- /dev/null
+++ b/libgimpwidgets/gimpframe.c
@@ -0,0 +1,372 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpframe.c
+ * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "gimpwidgetstypes.h"
+
+#include "gimp3migration.h"
+#include "gimpframe.h"
+
+
+/**
+ * SECTION: gimpframe
+ * @title: GimpFrame
+ * @short_description: A widget providing a HIG-compliant subclass
+ * of #GtkFrame.
+ *
+ * A widget providing a HIG-compliant subclass of #GtkFrame.
+ **/
+
+
+#define DEFAULT_LABEL_SPACING 6
+#define DEFAULT_LABEL_BOLD TRUE
+
+#define GIMP_FRAME_INDENT_KEY "gimp-frame-indent"
+#define GIMP_FRAME_IN_EXPANDER_KEY "gimp-frame-in-expander"
+
+
+static void gimp_frame_size_request (GtkWidget *widget,
+ GtkRequisition *requisition);
+static void gimp_frame_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static void gimp_frame_style_set (GtkWidget *widget,
+ GtkStyle *previous);
+static gboolean gimp_frame_expose_event (GtkWidget *widget,
+ GdkEventExpose *event);
+static void gimp_frame_child_allocate (GtkFrame *frame,
+ GtkAllocation *allocation);
+static void gimp_frame_label_widget_notify (GtkFrame *frame);
+static gint gimp_frame_get_indent (GtkWidget *widget);
+static gint gimp_frame_get_label_spacing (GtkFrame *frame);
+
+
+G_DEFINE_TYPE (GimpFrame, gimp_frame, GTK_TYPE_FRAME)
+
+#define parent_class gimp_frame_parent_class
+
+
+static void
+gimp_frame_class_init (GimpFrameClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ widget_class->size_request = gimp_frame_size_request;
+ widget_class->size_allocate = gimp_frame_size_allocate;
+ widget_class->style_set = gimp_frame_style_set;
+ widget_class->expose_event = gimp_frame_expose_event;
+
+ gtk_widget_class_install_style_property (widget_class,
+ g_param_spec_boolean ("label-bold",
+ "Label Bold",
+ "Whether the frame's label should be bold",
+ DEFAULT_LABEL_BOLD,
+ G_PARAM_READABLE));
+ gtk_widget_class_install_style_property (widget_class,
+ g_param_spec_int ("label-spacing",
+ "Label Spacing",
+ "The spacing between the label and the frame content",
+ 0,
+ G_MAXINT,
+ DEFAULT_LABEL_SPACING,
+ G_PARAM_READABLE));
+}
+
+
+static void
+gimp_frame_init (GimpFrame *frame)
+{
+ g_signal_connect (frame, "notify::label-widget",
+ G_CALLBACK (gimp_frame_label_widget_notify),
+ NULL);
+}
+
+static void
+gimp_frame_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ GtkFrame *frame = GTK_FRAME (widget);
+ GtkWidget *label_widget = gtk_frame_get_label_widget (frame);
+ GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
+ GtkRequisition child_requisition;
+ gint border_width;
+
+ if (label_widget && gtk_widget_get_visible (label_widget))
+ {
+ gtk_widget_size_request (label_widget, requisition);
+ }
+ else
+ {
+ requisition->width = 0;
+ requisition->height = 0;
+ }
+
+ requisition->height += gimp_frame_get_label_spacing (frame);
+
+ if (child && gtk_widget_get_visible (child))
+ {
+ gint indent = gimp_frame_get_indent (widget);
+
+ gtk_widget_size_request (child, &child_requisition);
+
+ requisition->width = MAX (requisition->width,
+ child_requisition.width + indent);
+ requisition->height += child_requisition.height;
+ }
+
+ border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
+
+ requisition->width += 2 * border_width;
+ requisition->height += 2 * border_width;
+}
+
+static void
+gimp_frame_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GtkFrame *frame = GTK_FRAME (widget);
+ GtkWidget *label_widget = gtk_frame_get_label_widget (frame);
+ GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
+ GtkAllocation child_allocation;
+
+ /* must not chain up here */
+ gtk_widget_set_allocation (widget, allocation);
+
+ gimp_frame_child_allocate (frame, &child_allocation);
+
+ if (child && gtk_widget_get_visible (child))
+ gtk_widget_size_allocate (child, &child_allocation);
+
+ if (label_widget && gtk_widget_get_visible (label_widget))
+ {
+ GtkAllocation label_allocation;
+ GtkRequisition label_requisition;
+ gint border_width;
+
+ border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
+
+ gtk_widget_get_child_requisition (label_widget, &label_requisition);
+
+ label_allocation.x = allocation->x + border_width;
+ label_allocation.y = allocation->y + border_width;
+ label_allocation.width = MAX (label_requisition.width,
+ allocation->width - 2 * border_width);
+ label_allocation.height = label_requisition.height;
+
+ gtk_widget_size_allocate (label_widget, &label_allocation);
+ }
+}
+
+static void
+gimp_frame_child_allocate (GtkFrame *frame,
+ GtkAllocation *child_allocation)
+{
+ GtkWidget *widget = GTK_WIDGET (frame);
+ GtkWidget *label_widget = gtk_frame_get_label_widget (frame);
+ GtkAllocation allocation;
+ gint border_width;
+ gint spacing = 0;
+ gint indent = gimp_frame_get_indent (widget);
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ border_width = gtk_container_get_border_width (GTK_CONTAINER (frame));
+
+ if (label_widget && gtk_widget_get_visible (label_widget))
+ {
+ GtkRequisition child_requisition;
+
+ gtk_widget_get_child_requisition (label_widget, &child_requisition);
+ spacing += child_requisition.height;
+ }
+
+ spacing += gimp_frame_get_label_spacing (frame);
+
+ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
+ child_allocation->x = border_width + indent;
+ else
+ child_allocation->x = border_width;
+
+ child_allocation->y = border_width + spacing;
+ child_allocation->width = MAX (1,
+ allocation.width - 2 * border_width - indent);
+ child_allocation->height = MAX (1,
+ allocation.height -
+ child_allocation->y - border_width);
+
+ child_allocation->x += allocation.x;
+ child_allocation->y += allocation.y;
+}
+
+static void
+gimp_frame_style_set (GtkWidget *widget,
+ GtkStyle *previous)
+{
+ /* font changes invalidate the indentation */
+ g_object_set_data (G_OBJECT (widget), GIMP_FRAME_INDENT_KEY, NULL);
+
+ /* for "label_bold" */
+ gimp_frame_label_widget_notify (GTK_FRAME (widget));
+}
+
+static gboolean
+gimp_frame_expose_event (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ if (gtk_widget_is_drawable (widget))
+ {
+ GtkWidgetClass *widget_class = g_type_class_peek_parent (parent_class);
+
+ return widget_class->expose_event (widget, event);
+ }
+
+ return FALSE;
+}
+
+static void
+gimp_frame_label_widget_notify (GtkFrame *frame)
+{
+ GtkWidget *label_widget = gtk_frame_get_label_widget (frame);
+
+ if (label_widget)
+ {
+ GtkLabel *label = NULL;
+
+ if (GTK_IS_LABEL (label_widget))
+ {
+ gfloat xalign, yalign;
+
+ label = GTK_LABEL (label_widget);
+
+ gtk_frame_get_label_align (frame, &xalign, &yalign);
+ gtk_label_set_xalign (GTK_LABEL (label), xalign);
+ gtk_label_set_yalign (GTK_LABEL (label), yalign);
+ }
+ else if (GTK_IS_BIN (label_widget))
+ {
+ GtkWidget *child = gtk_bin_get_child (GTK_BIN (label_widget));
+
+ if (GTK_IS_LABEL (child))
+ label = GTK_LABEL (child);
+ }
+
+ if (label)
+ {
+ PangoAttrList *attrs = pango_attr_list_new ();
+ PangoAttribute *attr;
+ gboolean bold;
+
+ gtk_widget_style_get (GTK_WIDGET (frame), "label_bold", &bold, NULL);
+
+ attr = pango_attr_weight_new (bold ?
+ PANGO_WEIGHT_BOLD :
+ PANGO_WEIGHT_NORMAL);
+ attr->start_index = 0;
+ attr->end_index = -1;
+ pango_attr_list_insert (attrs, attr);
+
+ gtk_label_set_attributes (label, attrs);
+
+ pango_attr_list_unref (attrs);
+ }
+ }
+}
+
+static gint
+gimp_frame_get_indent (GtkWidget *widget)
+{
+ gint width = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
+ GIMP_FRAME_INDENT_KEY));
+
+ if (! width)
+ {
+ PangoLayout *layout;
+
+ /* the HIG suggests to use four spaces so do just that */
+ layout = gtk_widget_create_pango_layout (widget, " ");
+ pango_layout_get_pixel_size (layout, &width, NULL);
+ g_object_unref (layout);
+
+ g_object_set_data (G_OBJECT (widget),
+ GIMP_FRAME_INDENT_KEY, GINT_TO_POINTER (width));
+ }
+
+ return width;
+}
+
+static gint
+gimp_frame_get_label_spacing (GtkFrame *frame)
+{
+ GtkWidget *label_widget = gtk_frame_get_label_widget (frame);
+ gint spacing = 0;
+
+ if ((label_widget && gtk_widget_get_visible (label_widget)) ||
+ (g_object_get_data (G_OBJECT (frame), GIMP_FRAME_IN_EXPANDER_KEY)))
+ {
+ gtk_widget_style_get (GTK_WIDGET (frame),
+ "label_spacing", &spacing,
+ NULL);
+ }
+
+ return spacing;
+}
+
+/**
+ * gimp_frame_new:
+ * @label: text to set as the frame's title label (or %NULL for no title)
+ *
+ * Creates a #GimpFrame widget. A #GimpFrame is a HIG-compliant
+ * variant of #GtkFrame. It doesn't render a frame at all but
+ * otherwise behaves like a frame. The frame's title is rendered in
+ * bold and the frame content is indented four spaces as suggested by
+ * the GNOME HIG (see https://developer.gnome.org/hig/stable/).
+ *
+ * Return value: a new #GimpFrame widget
+ *
+ * Since: 2.2
+ **/
+GtkWidget *
+gimp_frame_new (const gchar *label)
+{
+ GtkWidget *frame;
+ gboolean expander = FALSE;
+
+ /* somewhat hackish, should perhaps be an object property of GimpFrame */
+ if (label && strcmp (label, "<expander>") == 0)
+ {
+ expander = TRUE;
+ label = NULL;
+ }
+
+ frame = g_object_new (GIMP_TYPE_FRAME,
+ "label", label,
+ NULL);
+
+ if (expander)
+ g_object_set_data (G_OBJECT (frame),
+ GIMP_FRAME_IN_EXPANDER_KEY, (gpointer) TRUE);
+
+ return frame;
+}
diff --git a/libgimpwidgets/gimpframe.h b/libgimpwidgets/gimpframe.h
new file mode 100644
index 0000000..48d84ae
--- /dev/null
+++ b/libgimpwidgets/gimpframe.h
@@ -0,0 +1,67 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpframe.h
+ * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_FRAME_H__
+#define __GIMP_FRAME_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+#define GIMP_TYPE_FRAME (gimp_frame_get_type ())
+#define GIMP_FRAME(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_FRAME, GimpFrame))
+#define GIMP_FRAME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_FRAME, GimpFrameClass))
+#define GIMP_IS_FRAME(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_FRAME))
+#define GIMP_IS_FRAME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_FRAME))
+#define GIMP_FRAME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_FRAME, GimpFrameClass))
+
+
+typedef struct _GimpFrameClass GimpFrameClass;
+
+struct _GimpFrame
+{
+ GtkFrame parent_instance;
+};
+
+struct _GimpFrameClass
+{
+ GtkFrameClass parent_class;
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_frame_get_type (void) G_GNUC_CONST;
+GtkWidget * gimp_frame_new (const gchar *label);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_FRAME_H__ */
diff --git a/libgimpwidgets/gimphelpui.c b/libgimpwidgets/gimphelpui.c
new file mode 100644
index 0000000..8c57f9a
--- /dev/null
+++ b/libgimpwidgets/gimphelpui.c
@@ -0,0 +1,563 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimphelpui.c
+ * Copyright (C) 2000-2003 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "gimpwidgets.h"
+#include "gimpwidgets-private.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimphelpui
+ * @title: GimpHelpUI
+ * @short_description: Functions for setting tooltip and help identifier
+ * used by the GIMP help system.
+ *
+ * Functions for setting tooltip and help identifier used by the GIMP
+ * help system.
+ **/
+
+
+typedef enum
+{
+ GIMP_WIDGET_HELP_TOOLTIP = GTK_WIDGET_HELP_TOOLTIP,
+ GIMP_WIDGET_HELP_WHATS_THIS = GTK_WIDGET_HELP_WHATS_THIS,
+ GIMP_WIDGET_HELP_TYPE_HELP = 0xff
+} GimpWidgetHelpType;
+
+
+/* local variables */
+
+static gboolean tooltips_enabled = TRUE;
+static gboolean tooltips_enable_called = FALSE;
+
+
+/* local function prototypes */
+
+static const gchar * gimp_help_get_help_data (GtkWidget *widget,
+ GtkWidget **help_widget,
+ gpointer *ret_data);
+static gboolean gimp_help_callback (GtkWidget *widget,
+ GimpWidgetHelpType help_type,
+ GimpHelpFunc help_func);
+
+static void gimp_help_menu_item_set_tooltip (GtkWidget *widget,
+ const gchar *tooltip,
+ const gchar *help_id);
+static gboolean gimp_help_menu_item_query_tooltip (GtkWidget *widget,
+ gint x,
+ gint y,
+ gboolean keyboard_mode,
+ GtkTooltip *tooltip);
+static gboolean gimp_context_help_idle_start (gpointer widget);
+static gboolean gimp_context_help_button_press (GtkWidget *widget,
+ GdkEventButton *bevent,
+ gpointer data);
+static gboolean gimp_context_help_key_press (GtkWidget *widget,
+ GdkEventKey *kevent,
+ gpointer data);
+static gboolean gimp_context_help_idle_show_help (gpointer data);
+
+
+/* public functions */
+
+/**
+ * gimp_help_enable_tooltips:
+ *
+ * Enable tooltips to be shown in the GIMP user interface.
+ *
+ * As a plug-in author, you don't need to care about this as this
+ * function is called for you from gimp_ui_init(). This ensures that
+ * the user setting from the GIMP preferences dialog is respected in
+ * all plug-in dialogs.
+ **/
+void
+gimp_help_enable_tooltips (void)
+{
+ if (! tooltips_enable_called)
+ {
+ tooltips_enable_called = TRUE;
+ tooltips_enabled = TRUE;
+ }
+}
+
+/**
+ * gimp_help_disable_tooltips:
+ *
+ * Disable tooltips to be shown in the GIMP user interface.
+ *
+ * As a plug-in author, you don't need to care about this as this
+ * function is called for you from gimp_ui_init(). This ensures that
+ * the user setting from the GIMP preferences dialog is respected in
+ * all plug-in dialogs.
+ **/
+void
+gimp_help_disable_tooltips (void)
+{
+ if (! tooltips_enable_called)
+ {
+ tooltips_enable_called = TRUE;
+ tooltips_enabled = FALSE;
+ }
+}
+
+/**
+ * gimp_standard_help_func:
+ * @help_id: A unique help identifier.
+ * @help_data: The @help_data passed to gimp_help_connect().
+ *
+ * This is the standard GIMP help function which does nothing but calling
+ * gimp_help(). It is the right function to use in almost all cases.
+ **/
+void
+gimp_standard_help_func (const gchar *help_id,
+ gpointer help_data)
+{
+ if (! _gimp_standard_help_func)
+ {
+ g_warning ("%s: you must call gimp_widgets_init() before using "
+ "the help system", G_STRFUNC);
+ return;
+ }
+
+ (* _gimp_standard_help_func) (help_id, help_data);
+}
+
+/**
+ * gimp_help_connect:
+ * @widget: The widget you want to connect the help accelerator for. Will
+ * be a #GtkWindow in most cases.
+ * @help_func: The function which will be called if the user presses "F1".
+ * @help_id: The @help_id which will be passed to @help_func.
+ * @help_data: The @help_data pointer which will be passed to @help_func.
+ *
+ * Note that this function is automatically called by all libgimp dialog
+ * constructors. You only have to call it for windows/dialogs you created
+ * "manually".
+ **/
+void
+gimp_help_connect (GtkWidget *widget,
+ GimpHelpFunc help_func,
+ const gchar *help_id,
+ gpointer help_data)
+{
+ static gboolean initialized = FALSE;
+
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (help_func != NULL);
+
+ /* set up the help signals
+ */
+ if (! initialized)
+ {
+ GtkBindingSet *binding_set;
+
+ binding_set =
+ gtk_binding_set_by_class (g_type_class_peek (GTK_TYPE_WIDGET));
+
+ gtk_binding_entry_add_signal (binding_set, GDK_KEY_F1, 0,
+ "show-help", 1,
+ GTK_TYPE_WIDGET_HELP_TYPE,
+ GIMP_WIDGET_HELP_TYPE_HELP);
+ gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_F1, 0,
+ "show-help", 1,
+ GTK_TYPE_WIDGET_HELP_TYPE,
+ GIMP_WIDGET_HELP_TYPE_HELP);
+
+ initialized = TRUE;
+ }
+
+ gimp_help_set_help_data (widget, NULL, help_id);
+
+ g_object_set_data (G_OBJECT (widget), "gimp-help-data", help_data);
+
+ g_signal_connect (widget, "show-help",
+ G_CALLBACK (gimp_help_callback),
+ help_func);
+
+ gtk_widget_add_events (widget, GDK_BUTTON_PRESS_MASK);
+}
+
+/**
+ * gimp_help_set_help_data:
+ * @widget: The #GtkWidget you want to set a @tooltip and/or @help_id for.
+ * @tooltip: The text for this widget's tooltip (or %NULL).
+ * @help_id: The @help_id for the #GtkTipsQuery tooltips inspector.
+ *
+ * The reason why we don't use gtk_widget_set_tooltip_text() is that
+ * elements in the GIMP user interface should, if possible, also have
+ * a @help_id set for context-sensitive help.
+ *
+ * This function can be called with #NULL for @tooltip. Use this feature
+ * if you want to set a help link for a widget which shouldn't have
+ * a visible tooltip.
+ **/
+void
+gimp_help_set_help_data (GtkWidget *widget,
+ const gchar *tooltip,
+ const gchar *help_id)
+{
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ if (tooltips_enabled)
+ {
+ gtk_widget_set_tooltip_text (widget, tooltip);
+
+ if (GTK_IS_MENU_ITEM (widget))
+ gimp_help_menu_item_set_tooltip (widget, tooltip, help_id);
+ }
+
+ g_object_set_qdata (G_OBJECT (widget), GIMP_HELP_ID, (gpointer) help_id);
+}
+
+/**
+ * gimp_help_set_help_data_with_markup:
+ * @widget: The #GtkWidget you want to set a @tooltip and/or @help_id for.
+ * @tooltip: The markup for this widget's tooltip (or %NULL).
+ * @help_id: The @help_id for the #GtkTipsQuery tooltips inspector.
+ *
+ * Just like gimp_help_set_help_data(), but supports to pass text
+ * which is marked up with <link linkend="PangoMarkupFormat">Pango
+ * text markup language</link>.
+ *
+ * Since: 2.6
+ **/
+void
+gimp_help_set_help_data_with_markup (GtkWidget *widget,
+ const gchar *tooltip,
+ const gchar *help_id)
+{
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ if (tooltips_enabled)
+ {
+ gtk_widget_set_tooltip_markup (widget, tooltip);
+
+ if (GTK_IS_MENU_ITEM (widget))
+ gimp_help_menu_item_set_tooltip (widget, tooltip, help_id);
+ }
+
+ g_object_set_qdata (G_OBJECT (widget), GIMP_HELP_ID, (gpointer) help_id);
+}
+
+/**
+ * gimp_context_help:
+ * @widget: Any #GtkWidget on the screen.
+ *
+ * This function invokes the context help inspector.
+ *
+ * The mouse cursor will turn turn into a question mark and the user can
+ * click on any widget of the application which started the inspector.
+ *
+ * If the widget the user clicked on has a @help_id string attached
+ * (see gimp_help_set_help_data()), the corresponding help page will
+ * be displayed. Otherwise the help system will ascend the widget hierarchy
+ * until it finds an attached @help_id string (which should be the
+ * case at least for every window/dialog).
+ **/
+void
+gimp_context_help (GtkWidget *widget)
+{
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ gimp_help_callback (widget, GIMP_WIDGET_HELP_WHATS_THIS, NULL);
+}
+
+/**
+ * gimp_help_id_quark:
+ *
+ * This function returns the #GQuark which should be used as key when
+ * attaching help IDs to widgets and objects.
+ *
+ * Return value: The #GQuark.
+ *
+ * Since: 2.2
+ **/
+GQuark
+gimp_help_id_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (! quark)
+ quark = g_quark_from_static_string ("gimp-help-id");
+
+ return quark;
+}
+
+
+/* private functions */
+
+static const gchar *
+gimp_help_get_help_data (GtkWidget *widget,
+ GtkWidget **help_widget,
+ gpointer *ret_data)
+{
+ const gchar *help_id = NULL;
+ gpointer help_data = NULL;
+
+ for (; widget; widget = gtk_widget_get_parent (widget))
+ {
+ help_id = g_object_get_qdata (G_OBJECT (widget), GIMP_HELP_ID);
+ help_data = g_object_get_data (G_OBJECT (widget), "gimp-help-data");
+
+ if (help_id)
+ {
+ if (help_widget)
+ *help_widget = widget;
+
+ if (ret_data)
+ *ret_data = help_data;
+
+ return help_id;
+ }
+ }
+
+ if (help_widget)
+ *help_widget = NULL;
+
+ if (ret_data)
+ *ret_data = NULL;
+
+ return NULL;
+}
+
+static gboolean
+gimp_help_callback (GtkWidget *widget,
+ GimpWidgetHelpType help_type,
+ GimpHelpFunc help_func)
+{
+ switch (help_type)
+ {
+ case GIMP_WIDGET_HELP_TYPE_HELP:
+ if (help_func)
+ {
+ help_func (g_object_get_qdata (G_OBJECT (widget), GIMP_HELP_ID),
+ g_object_get_data (G_OBJECT (widget), "gimp-help-data"));
+ }
+ return TRUE;
+
+ case GIMP_WIDGET_HELP_WHATS_THIS:
+ g_idle_add (gimp_context_help_idle_start, widget);
+ return TRUE;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+gimp_help_menu_item_set_tooltip (GtkWidget *widget,
+ const gchar *tooltip,
+ const gchar *help_id)
+{
+ g_return_if_fail (GTK_IS_MENU_ITEM (widget));
+
+ if (tooltip && help_id)
+ {
+ g_object_set (widget, "has-tooltip", TRUE, NULL);
+
+ g_signal_connect (widget, "query-tooltip",
+ G_CALLBACK (gimp_help_menu_item_query_tooltip),
+ NULL);
+ }
+ else if (! tooltip)
+ {
+ g_object_set (widget, "has-tooltip", FALSE, NULL);
+
+ g_signal_handlers_disconnect_by_func (widget,
+ gimp_help_menu_item_query_tooltip,
+ NULL);
+ }
+}
+
+static gboolean
+gimp_help_menu_item_query_tooltip (GtkWidget *widget,
+ gint x,
+ gint y,
+ gboolean keyboard_mode,
+ GtkTooltip *tooltip)
+{
+ GtkWidget *vbox;
+ GtkWidget *label;
+ gchar *text;
+ gboolean use_markup = TRUE;
+
+ text = gtk_widget_get_tooltip_markup (widget);
+
+ if (! text)
+ {
+ text = gtk_widget_get_tooltip_text (widget);
+ use_markup = FALSE;
+ }
+
+ if (! text)
+ return FALSE;
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+
+ label = gtk_label_new (text);
+ gtk_label_set_use_markup (GTK_LABEL (label), use_markup);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
+ gtk_widget_show (label);
+
+ g_free (text);
+
+ label = gtk_label_new (_("Press F1 for more help"));
+ gimp_label_set_attributes (GTK_LABEL (label),
+ PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
+ PANGO_ATTR_SCALE, PANGO_SCALE_SMALL,
+ -1);
+ gtk_label_set_xalign (GTK_LABEL (label), 1.0);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ gtk_tooltip_set_custom (tooltip, vbox);
+
+ return TRUE;
+}
+
+
+/* Do all the actual context help calls in idle functions and check for
+ * some widget holding a grab before starting the query because strange
+ * things happen if (1) the help browser pops up while the query has
+ * grabbed the pointer or (2) the query grabs the pointer while some
+ * other part of GIMP has grabbed it (e.g. a tool, eek)
+ */
+
+static gboolean
+gimp_context_help_idle_start (gpointer widget)
+{
+ if (! gtk_grab_get_current ())
+ {
+ GtkWidget *invisible;
+ GdkCursor *cursor;
+ GdkGrabStatus status;
+
+ invisible = gtk_invisible_new_for_screen (gtk_widget_get_screen (widget));
+ gtk_widget_show (invisible);
+
+ cursor = gdk_cursor_new_for_display (gtk_widget_get_display (invisible),
+ GDK_QUESTION_ARROW);
+
+ status = gdk_pointer_grab (gtk_widget_get_window (invisible), TRUE,
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_ENTER_NOTIFY_MASK |
+ GDK_LEAVE_NOTIFY_MASK,
+ NULL, cursor,
+ GDK_CURRENT_TIME);
+
+ gdk_cursor_unref (cursor);
+
+ if (status != GDK_GRAB_SUCCESS)
+ {
+ gtk_widget_destroy (invisible);
+ return FALSE;
+ }
+
+ if (gdk_keyboard_grab (gtk_widget_get_window (invisible), TRUE,
+ GDK_CURRENT_TIME) != GDK_GRAB_SUCCESS)
+ {
+ gdk_display_pointer_ungrab (gtk_widget_get_display (invisible),
+ GDK_CURRENT_TIME);
+ gtk_widget_destroy (invisible);
+ return FALSE;
+ }
+
+ gtk_grab_add (invisible);
+
+ g_signal_connect (invisible, "button-press-event",
+ G_CALLBACK (gimp_context_help_button_press),
+ NULL);
+ g_signal_connect (invisible, "key-press-event",
+ G_CALLBACK (gimp_context_help_key_press),
+ NULL);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gimp_context_help_button_press (GtkWidget *widget,
+ GdkEventButton *bevent,
+ gpointer data)
+{
+ GtkWidget *event_widget = gtk_get_event_widget ((GdkEvent *) bevent);
+
+ if (event_widget && bevent->button == 1 && bevent->type == GDK_BUTTON_PRESS)
+ {
+ GdkDisplay *display = gtk_widget_get_display (widget);
+
+ gtk_grab_remove (widget);
+ gdk_display_keyboard_ungrab (display, bevent->time);
+ gdk_display_pointer_ungrab (display, bevent->time);
+ gtk_widget_destroy (widget);
+
+ if (event_widget != widget)
+ g_idle_add (gimp_context_help_idle_show_help, event_widget);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gimp_context_help_key_press (GtkWidget *widget,
+ GdkEventKey *kevent,
+ gpointer data)
+{
+ if (kevent->keyval == GDK_KEY_Escape)
+ {
+ GdkDisplay *display = gtk_widget_get_display (widget);
+
+ gtk_grab_remove (widget);
+ gdk_display_keyboard_ungrab (display, kevent->time);
+ gdk_display_pointer_ungrab (display, kevent->time);
+ gtk_widget_destroy (widget);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gimp_context_help_idle_show_help (gpointer data)
+{
+ GtkWidget *help_widget;
+ const gchar *help_id = NULL;
+ gpointer help_data = NULL;
+
+ help_id = gimp_help_get_help_data (GTK_WIDGET (data), &help_widget,
+ &help_data);
+
+ if (help_id)
+ gimp_standard_help_func (help_id, help_data);
+
+ return FALSE;
+}
diff --git a/libgimpwidgets/gimphelpui.h b/libgimpwidgets/gimphelpui.h
new file mode 100644
index 0000000..0445d64
--- /dev/null
+++ b/libgimpwidgets/gimphelpui.h
@@ -0,0 +1,76 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimphelpui.h
+ * Copyright (C) 2000-2003 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_HELP_UI_H__
+#define __GIMP_HELP_UI_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+void gimp_help_enable_tooltips (void);
+void gimp_help_disable_tooltips (void);
+
+/* the standard gimp help function
+ */
+void gimp_standard_help_func (const gchar *help_id,
+ gpointer help_data);
+
+/* connect the help callback of a window */
+void gimp_help_connect (GtkWidget *widget,
+ GimpHelpFunc help_func,
+ const gchar *help_id,
+ gpointer help_data);
+
+/* set help data for non-window widgets */
+void gimp_help_set_help_data (GtkWidget *widget,
+ const gchar *tooltip,
+ const gchar *help_id);
+
+/* set help data with markup for non-window widgets */
+void gimp_help_set_help_data_with_markup (GtkWidget *widget,
+ const gchar *tooltip,
+ const gchar *help_id);
+
+/* activate the context help inspector */
+void gimp_context_help (GtkWidget *widget);
+
+
+/**
+ * GIMP_HELP_ID:
+ *
+ * The #GQuark used to attach GIMP help IDs to widgets.
+ *
+ * Since: 2.2
+ **/
+#define GIMP_HELP_ID (gimp_help_id_quark ())
+
+GQuark gimp_help_id_quark (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif /* __GIMP_HELP_UI_H__ */
diff --git a/libgimpwidgets/gimphintbox.c b/libgimpwidgets/gimphintbox.c
new file mode 100644
index 0000000..f1e2291
--- /dev/null
+++ b/libgimpwidgets/gimphintbox.c
@@ -0,0 +1,246 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimphintbox.c
+ * Copyright (C) 2006 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "gimpwidgets.h"
+
+
+/**
+ * SECTION: gimphintbox
+ * @title: GimpHintBox
+ * @short_description: Displays a wilber icon and a text.
+ *
+ * Displays a wilber icon and a text.
+ **/
+
+
+enum
+{
+ PROP_0,
+ PROP_ICON_NAME,
+ PROP_STOCK_ID,
+ PROP_HINT
+};
+
+struct _GimpHintBoxPrivate
+{
+ gchar *icon_name;
+ gchar *stock_id;
+ gchar *hint;
+};
+
+
+static void gimp_hint_box_constructed (GObject *object);
+static void gimp_hint_box_finalize (GObject *object);
+static void gimp_hint_box_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_hint_box_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpHintBox, gimp_hint_box, GTK_TYPE_BOX)
+
+#define parent_class gimp_hint_box_parent_class
+
+
+static void
+gimp_hint_box_class_init (GimpHintBoxClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = gimp_hint_box_constructed;
+ object_class->finalize = gimp_hint_box_finalize;
+ object_class->set_property = gimp_hint_box_set_property;
+ object_class->get_property = gimp_hint_box_get_property;
+
+ g_object_class_install_property (object_class, PROP_ICON_NAME,
+ g_param_spec_string ("icon-name",
+ "Icon Name",
+ "The icon to show next to the hint",
+ GIMP_ICON_DIALOG_INFORMATION,
+ G_PARAM_CONSTRUCT_ONLY |
+ GIMP_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_STOCK_ID,
+ g_param_spec_string ("stock-id",
+ "Stock ID",
+ "Deprecated: use icon-name instead",
+ GIMP_ICON_DIALOG_INFORMATION,
+ G_PARAM_CONSTRUCT_ONLY |
+ GIMP_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_HINT,
+ g_param_spec_string ("hint",
+ "Hint",
+ "The hint to display",
+ NULL,
+ G_PARAM_CONSTRUCT_ONLY |
+ GIMP_PARAM_READWRITE));
+}
+
+static void
+gimp_hint_box_init (GimpHintBox *box)
+{
+ box->priv = gimp_hint_box_get_instance_private (box);
+
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (box),
+ GTK_ORIENTATION_HORIZONTAL);
+}
+
+static void
+gimp_hint_box_constructed (GObject *object)
+{
+ GimpHintBox *box = GIMP_HINT_BOX (object);
+ GtkWidget *image = NULL;
+ GtkWidget *label;
+
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ gtk_box_set_spacing (GTK_BOX (box), 12);
+
+ if (box->priv->icon_name)
+ {
+ image = gtk_image_new_from_icon_name (box->priv->icon_name,
+ GTK_ICON_SIZE_DIALOG);
+ }
+ else if (box->priv->stock_id)
+ {
+ image = gtk_image_new_from_stock (box->priv->stock_id,
+ GTK_ICON_SIZE_DIALOG);
+ }
+
+ if (image)
+ {
+ gtk_box_pack_start (GTK_BOX (box), image, FALSE, FALSE, 0);
+ gtk_widget_show (image);
+ }
+
+ label = g_object_new (GTK_TYPE_LABEL,
+ "label", box->priv->hint,
+ "wrap", TRUE,
+ "justify", GTK_JUSTIFY_LEFT,
+ "xalign", 0.0,
+ "yalign", 0.5,
+ NULL);
+
+ gimp_label_set_attributes (GTK_LABEL (label),
+ PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
+ -1);
+ gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+}
+
+static void
+gimp_hint_box_finalize (GObject *object)
+{
+ GimpHintBox *box = GIMP_HINT_BOX (object);
+
+ g_clear_pointer (&box->priv->icon_name, g_free);
+ g_clear_pointer (&box->priv->stock_id, g_free);
+ g_clear_pointer (&box->priv->hint, g_free);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_hint_box_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpHintBox *box = GIMP_HINT_BOX (object);
+
+ switch (property_id)
+ {
+ case PROP_ICON_NAME:
+ box->priv->icon_name = g_value_dup_string (value);
+ break;
+
+ case PROP_STOCK_ID:
+ box->priv->stock_id = g_value_dup_string (value);
+ break;
+
+ case PROP_HINT:
+ box->priv->hint = g_value_dup_string (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_hint_box_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpHintBox *box = GIMP_HINT_BOX (object);
+
+ switch (property_id)
+ {
+ case PROP_ICON_NAME:
+ g_value_set_string (value, box->priv->icon_name);
+ break;
+
+ case PROP_STOCK_ID:
+ g_value_set_string (value, box->priv->stock_id);
+ break;
+
+ case PROP_HINT:
+ g_value_set_string (value, box->priv->hint);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+/**
+ * gimp_hint_box_new:
+ * @hint: text to display as a user hint
+ *
+ * Creates a new widget that shows a text label showing @hint,
+ * decorated with a GIMP_ICON_DIALOG_INFORMATION wilber icon.
+ *
+ * Return value: a new widget
+ *
+ * Since GIMP 2.4
+ **/
+GtkWidget *
+gimp_hint_box_new (const gchar *hint)
+{
+ g_return_val_if_fail (hint != NULL, NULL);
+
+ return g_object_new (GIMP_TYPE_HINT_BOX,
+ "hint", hint,
+ NULL);
+}
diff --git a/libgimpwidgets/gimphintbox.h b/libgimpwidgets/gimphintbox.h
new file mode 100644
index 0000000..c5c050e
--- /dev/null
+++ b/libgimpwidgets/gimphintbox.h
@@ -0,0 +1,75 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimphintbox.h
+ * Copyright (C) 2006 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_HINT_BOX_H__
+#define __GIMP_HINT_BOX_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+#define GIMP_TYPE_HINT_BOX (gimp_hint_box_get_type ())
+#define GIMP_HINT_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_HINT_BOX, GimpHintBox))
+#define GIMP_HINT_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_HINT_BOX, GimpHintBoxClass))
+#define GIMP_IS_HINT_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_HINT_BOX))
+#define GIMP_IS_HINT_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_HINT_BOX))
+#define GIMP_HINT_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_HINT_BOX, GimpHintBoxClass))
+
+
+typedef struct _GimpHintBoxPrivate GimpHintBoxPrivate;
+typedef struct _GimpHintBoxClass GimpHintBoxClass;
+
+struct _GimpHintBox
+{
+ GtkBox parent_instance;
+
+ GimpHintBoxPrivate *priv;
+};
+
+struct _GimpHintBoxClass
+{
+ GtkBoxClass parent_class;
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+ void (* _gimp_reserved5) (void);
+ void (* _gimp_reserved6) (void);
+ void (* _gimp_reserved7) (void);
+ void (* _gimp_reserved8) (void);
+};
+
+
+GType gimp_hint_box_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_hint_box_new (const gchar *hint);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_HINT_BOX_H__ */
diff --git a/libgimpwidgets/gimpicons.c b/libgimpwidgets/gimpicons.c
new file mode 100644
index 0000000..66921cf
--- /dev/null
+++ b/libgimpwidgets/gimpicons.c
@@ -0,0 +1,651 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpicons.c
+ * Copyright (C) 2001-2015 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#undef GIMP_DISABLE_DEPRECATED
+#include "gimpicons.h"
+
+#include "icons/Color/gimp-icon-pixbufs.c"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpicons
+ * @title: GimpIcons
+ * @short_description: Prebuilt common menu/toolbar items and
+ * corresponding icons
+ *
+ * GIMP registers a set of menu/toolbar items and corresponding icons
+ * in addition to the standard GTK+ stock items. These can be used
+ * just like GTK+ stock items. GIMP also overrides a few of the GTK+
+ * icons (namely the ones in dialog size).
+ *
+ * Stock icons may have a RTL variant which gets used for
+ * right-to-left locales.
+ **/
+
+
+#define LIBGIMP_DOMAIN GETTEXT_PACKAGE "-libgimp"
+#define GIMP_TOILET_PAPER "gimp-toilet-paper"
+#define GIMP_DEFAULT_ICON_THEME "Symbolic"
+
+
+static GtkIconFactory *gimp_stock_factory = NULL;
+
+
+static const GtkStockItem gimp_stock_items[] =
+{
+ { GIMP_STOCK_ANCHOR, N_("Anchor"), 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_CENTER, N_("C_enter"), 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_DUPLICATE, N_("_Duplicate"), 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_LINKED, N_("Linked"), 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_PASTE_AS_NEW, N_("Paste as New"), 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_PASTE_INTO, N_("Paste Into"), 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_RESET, N_("_Reset"), 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_VISIBLE, N_("Visible"), 0, 0, LIBGIMP_DOMAIN },
+
+ { GIMP_STOCK_GRADIENT_LINEAR, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_GRADIENT_BILINEAR, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_GRADIENT_RADIAL, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_GRADIENT_SQUARE, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_GRADIENT_CONICAL_SYMMETRIC, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_GRADIENT_CONICAL_ASYMMETRIC, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_GRADIENT_SHAPEBURST_ANGULAR, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_GRADIENT_SHAPEBURST_SPHERICAL, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_GRADIENT_SHAPEBURST_DIMPLED, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_GRADIENT_SPIRAL_CLOCKWISE, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_GRADIENT_SPIRAL_ANTICLOCKWISE, NULL, 0, 0, LIBGIMP_DOMAIN },
+
+ { GIMP_STOCK_GRAVITY_EAST, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_GRAVITY_NORTH, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_GRAVITY_NORTH_EAST, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_GRAVITY_NORTH_WEST, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_GRAVITY_SOUTH, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_GRAVITY_SOUTH_EAST, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_GRAVITY_SOUTH_WEST, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_GRAVITY_WEST, NULL, 0, 0, LIBGIMP_DOMAIN },
+
+ { GIMP_STOCK_HCENTER, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_VCENTER, NULL, 0, 0, LIBGIMP_DOMAIN },
+
+ { GIMP_STOCK_HCHAIN, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_HCHAIN_BROKEN, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_VCHAIN, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_VCHAIN_BROKEN, NULL, 0, 0, LIBGIMP_DOMAIN },
+
+ { GIMP_STOCK_SELECTION, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_SELECTION_REPLACE, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_SELECTION_ADD, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_SELECTION_SUBTRACT, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_SELECTION_INTERSECT, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_SELECTION_STROKE, N_("_Stroke"), 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_SELECTION_TO_CHANNEL, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_SELECTION_TO_PATH, NULL, 0, 0, LIBGIMP_DOMAIN },
+
+ { GIMP_STOCK_PATH_STROKE, N_("_Stroke"), 0, 0, LIBGIMP_DOMAIN },
+
+ { GIMP_STOCK_CURVE_FREE, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_CURVE_SMOOTH, NULL, 0, 0, LIBGIMP_DOMAIN },
+
+ { GIMP_STOCK_COLOR_PICKER_BLACK, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_COLOR_PICKER_GRAY, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_COLOR_PICKER_WHITE, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_COLOR_TRIANGLE, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_COLOR_PICK_FROM_SCREEN, NULL, 0, 0, LIBGIMP_DOMAIN },
+
+ { GIMP_STOCK_CHAR_PICKER, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_LETTER_SPACING, N_("L_etter Spacing"), 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_LINE_SPACING, N_("L_ine Spacing"), 0, 0, LIBGIMP_DOMAIN },
+
+ { GIMP_STOCK_TEXT_DIR_LTR, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TEXT_DIR_RTL, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_PATTERN, NULL, 0, 0, LIBGIMP_DOMAIN },
+
+ { GIMP_STOCK_CONVERT_RGB, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_CONVERT_GRAYSCALE, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_CONVERT_INDEXED, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_INVERT, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_MERGE_DOWN, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_LAYER_TO_IMAGESIZE, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_PLUGIN, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_RESHOW_FILTER, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_ROTATE_90, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_ROTATE_180, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_ROTATE_270, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_RESIZE, N_("Re_size"), 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_SCALE, N_("_Scale"), 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_FLIP_HORIZONTAL, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_FLIP_VERTICAL, NULL, 0, 0, LIBGIMP_DOMAIN },
+
+ { GIMP_STOCK_IMAGES, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_LAYERS, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_CHANNELS, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_PATHS, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOLS, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_OPTIONS, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_DEVICE_STATUS, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_INPUT_DEVICE, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_CURSOR, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_SAMPLE_POINT, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_DYNAMICS, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_PRESET, NULL, 0, 0, LIBGIMP_DOMAIN },
+
+ { GIMP_STOCK_IMAGE, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_LAYER, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TEXT_LAYER, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_FLOATING_SELECTION, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_CHANNEL, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_CHANNEL_RED, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_CHANNEL_GREEN, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_CHANNEL_BLUE, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_CHANNEL_GRAY, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_CHANNEL_INDEXED, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_CHANNEL_ALPHA, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_LAYER_MASK, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_PATH, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TEMPLATE, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_COLORMAP, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_HISTOGRAM, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_HISTOGRAM_LINEAR, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_HISTOGRAM_LOGARITHMIC, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_UNDO_HISTORY, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TRANSPARENCY, NULL, 0, 0, LIBGIMP_DOMAIN },
+
+ { GIMP_STOCK_SELECTION_ALL, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_SELECTION_NONE, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_SELECTION_GROW, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_SELECTION_SHRINK, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_SELECTION_BORDER, NULL, 0, 0, LIBGIMP_DOMAIN },
+
+ { GIMP_STOCK_NAVIGATION, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_QUICK_MASK_OFF, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_QUICK_MASK_ON, NULL, 0, 0, LIBGIMP_DOMAIN },
+
+ { GIMP_STOCK_CONTROLLER, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_CONTROLLER_KEYBOARD, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_CONTROLLER_LINUX_INPUT, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_CONTROLLER_MIDI, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_CONTROLLER_WHEEL, NULL, 0, 0, LIBGIMP_DOMAIN },
+
+ { GIMP_STOCK_DISPLAY_FILTER, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_DISPLAY_FILTER_COLORBLIND, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_DISPLAY_FILTER_CONTRAST, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_DISPLAY_FILTER_GAMMA, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_DISPLAY_FILTER_LCMS, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_DISPLAY_FILTER_PROOF, NULL, 0, 0, LIBGIMP_DOMAIN },
+
+ { GIMP_STOCK_LIST, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_GRID, NULL, 0, 0, LIBGIMP_DOMAIN },
+
+ { GIMP_STOCK_PORTRAIT, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_LANDSCAPE, NULL, 0, 0, LIBGIMP_DOMAIN },
+
+ { GIMP_TOILET_PAPER, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_GEGL, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_WEB, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_VIDEO, NULL, 0, 0, LIBGIMP_DOMAIN },
+
+ { GIMP_STOCK_SHAPE_CIRCLE, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_SHAPE_DIAMOND, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_SHAPE_SQUARE, NULL, 0, 0, LIBGIMP_DOMAIN },
+
+ { GIMP_STOCK_CAP_BUTT, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_CAP_ROUND, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_CAP_SQUARE, NULL, 0, 0, LIBGIMP_DOMAIN },
+
+ { GIMP_STOCK_JOIN_MITER, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_JOIN_ROUND, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_JOIN_BEVEL, NULL, 0, 0, LIBGIMP_DOMAIN },
+
+ { GIMP_STOCK_ERROR, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_INFO, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_QUESTION, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_USER_MANUAL, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_WARNING, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_WILBER, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_WILBER_EEK, NULL, 0, 0, LIBGIMP_DOMAIN },
+
+ { GIMP_STOCK_FRAME, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TEXTURE, NULL, 0, 0, LIBGIMP_DOMAIN },
+
+ { GIMP_STOCK_TOOL_AIRBRUSH, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_ALIGN, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_BLEND, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_BLUR, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_BRIGHTNESS_CONTRAST, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_BUCKET_FILL, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_BY_COLOR_SELECT, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_CAGE, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_CLONE, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_COLOR_BALANCE, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_COLOR_PICKER, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_COLORIZE, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_CROP, N_("Cr_op"), 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_CURVES, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_DESATURATE, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_DODGE, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_ELLIPSE_SELECT, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_ERASER, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_FLIP, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_FREE_SELECT, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_FOREGROUND_SELECT, N_("_Select"), 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_FUZZY_SELECT, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_HUE_SATURATION, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_HEAL, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_INK, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_ISCISSORS, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_LEVELS, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_MEASURE, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_MOVE, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_PAINTBRUSH, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_PATH, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_PENCIL, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_PERSPECTIVE, N_("_Transform"), 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_PERSPECTIVE_CLONE, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_POSTERIZE, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_RECT_SELECT, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_ROTATE, N_("_Rotate"), 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_SCALE, N_("_Scale"), 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_SHEAR, N_("_Shear"), 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_SMUDGE, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_TEXT, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_THRESHOLD, NULL, 0, 0, LIBGIMP_DOMAIN },
+ { GIMP_STOCK_TOOL_ZOOM, NULL, 0, 0, LIBGIMP_DOMAIN }
+};
+
+static const GtkStockItem gimp_compat_stock_items[] =
+{
+ { "gimp-indexed-palette", NULL, 0, 0, LIBGIMP_DOMAIN },
+ { "gimp-qmask-off", NULL, 0, 0, LIBGIMP_DOMAIN },
+ { "gimp-qmask-on", NULL, 0, 0, LIBGIMP_DOMAIN }
+};
+
+
+static void
+register_stock_icon (GtkIconFactory *factory,
+ const gchar *stock_id,
+ const gchar *icon_name)
+{
+ GtkIconSet *set = gtk_icon_set_new ();
+ GtkIconSource *source = gtk_icon_source_new ();
+
+ gtk_icon_source_set_direction_wildcarded (source, TRUE);
+ gtk_icon_source_set_size_wildcarded (source, TRUE);
+ gtk_icon_source_set_state_wildcarded (source, TRUE);
+ gtk_icon_source_set_icon_name (source, icon_name);
+
+ gtk_icon_set_add_source (set, source);
+ gtk_icon_source_free (source);
+
+ gtk_icon_factory_add (factory, stock_id, set);
+ gtk_icon_set_unref (set);
+}
+
+static void
+register_bidi_stock_icon (GtkIconFactory *factory,
+ const gchar *stock_id,
+ const gchar *icon_name_ltr,
+ const gchar *icon_name_rtl)
+{
+ GtkIconSet *set = gtk_icon_set_new ();
+ GtkIconSource *source = gtk_icon_source_new ();
+
+ gtk_icon_source_set_direction (source, GTK_TEXT_DIR_LTR);
+ gtk_icon_source_set_direction_wildcarded (source, FALSE);
+ gtk_icon_source_set_size_wildcarded (source, TRUE);
+ gtk_icon_source_set_state_wildcarded (source, TRUE);
+ gtk_icon_source_set_icon_name (source, icon_name_ltr);
+
+ gtk_icon_set_add_source (set, source);
+ gtk_icon_source_free (source);
+
+ source = gtk_icon_source_new ();
+
+ gtk_icon_source_set_direction (source, GTK_TEXT_DIR_RTL);
+ gtk_icon_source_set_direction_wildcarded (source, FALSE);
+ gtk_icon_source_set_size_wildcarded (source, TRUE);
+ gtk_icon_source_set_state_wildcarded (source, TRUE);
+ gtk_icon_source_set_icon_name (source, icon_name_rtl);
+
+ gtk_icon_set_add_source (set, source);
+ gtk_icon_source_free (source);
+
+ gtk_icon_factory_add (factory, stock_id, set);
+ gtk_icon_set_unref (set);
+}
+
+
+static GFile *icon_theme_path = NULL;
+static GFile *default_search_path = NULL;
+
+
+static void
+gimp_icons_change_icon_theme (GFile *new_search_path)
+{
+ GFile *old_search_path = g_file_get_parent (icon_theme_path);
+
+ if (! default_search_path)
+ default_search_path = gimp_data_directory_file ("icons", NULL);
+
+ if (! g_file_equal (new_search_path, old_search_path))
+ {
+ GtkIconTheme *icon_theme = gtk_icon_theme_get_default ();
+
+ if (g_file_equal (old_search_path, default_search_path))
+ {
+ /* if the old icon theme is in the default search path,
+ * simply prepend the new theme's path
+ */
+ gchar *path_str = g_file_get_path (new_search_path);
+
+ gtk_icon_theme_prepend_search_path (icon_theme, path_str);
+ g_free (path_str);
+ }
+ else
+ {
+ /* if the old theme is not in the default search path,
+ * we need to deal with the search path's first element
+ */
+ gchar **paths;
+ gint n_paths;
+
+ gtk_icon_theme_get_search_path (icon_theme, &paths, &n_paths);
+
+ if (g_file_equal (new_search_path, default_search_path))
+ {
+ /* when switching to a theme in the default path, remove
+ * the first search path element, the default search path
+ * is still in the search path
+ */
+ gtk_icon_theme_set_search_path (icon_theme,
+ (const gchar **) paths + 1,
+ n_paths - 1);
+ }
+ else
+ {
+ /* when switching between two non-default search paths, replace
+ * the first element of the search path with the new
+ * theme's path
+ */
+ g_free (paths[0]);
+ paths[0] = g_file_get_path (new_search_path);
+
+ gtk_icon_theme_set_search_path (icon_theme,
+ (const gchar **) paths, n_paths);
+ }
+
+ g_strfreev (paths);
+ }
+ }
+
+ g_object_unref (old_search_path);
+}
+
+static void
+gimp_icons_notify_system_icon_theme (GObject *settings,
+ GParamSpec *param,
+ gpointer unused)
+{
+ GdkScreen *screen = gdk_screen_get_default ();
+ GValue value = G_VALUE_INIT;
+
+ g_value_init (&value, G_TYPE_STRING);
+
+ if (gdk_screen_get_setting (screen, "gtk-icon-theme-name", &value))
+ {
+ const gchar *new_system_icon_theme = g_value_get_string (&value);
+ gchar *cur_system_icon_theme = NULL;
+
+ g_object_get (settings,
+ "gtk-fallback-icon-theme", &cur_system_icon_theme,
+ NULL);
+ if (g_strcmp0 (cur_system_icon_theme, new_system_icon_theme))
+ {
+ g_object_set (settings,
+ "gtk-fallback-icon-theme", new_system_icon_theme,
+ NULL);
+
+ g_object_notify (settings, "gtk-icon-theme-name");
+ }
+
+ g_free (cur_system_icon_theme);
+ }
+
+ g_value_unset (&value);
+}
+
+static gboolean
+gimp_icons_sanity_check (GFile *path,
+ const gchar *theme_name)
+{
+ gboolean exists = FALSE;
+ GFile *child = g_file_get_child (path, theme_name);
+
+ if (g_file_query_exists (child, NULL))
+ {
+ GFile *index = g_file_get_child (child, "index.theme");
+
+ if (g_file_query_exists (index, NULL))
+ exists = TRUE;
+ else
+ g_printerr ("%s: Icon theme path has no '%s/index.theme': %s\n",
+ G_STRFUNC, theme_name, gimp_file_get_utf8_name (path));
+
+ g_object_unref (index);
+ }
+ else
+ g_printerr ("%s: Icon theme path has no '%s' subdirectory: %s\n",
+ G_STRFUNC, theme_name, gimp_file_get_utf8_name (path));
+
+ g_object_unref (child);
+
+ return exists;
+}
+
+void
+gimp_icons_set_icon_theme (GFile *path)
+{
+ gchar *icon_theme_name;
+ GFile *search_path;
+
+ g_return_if_fail (path == NULL || G_IS_FILE (path));
+
+ if (path)
+ path = g_object_ref (path);
+ else
+ path = gimp_data_directory_file ("icons", GIMP_DEFAULT_ICON_THEME, NULL);
+
+ search_path = g_file_get_parent (path);
+ icon_theme_name = g_file_get_basename (path);
+
+ if (gimp_icons_sanity_check (search_path, "hicolor") &&
+ gimp_icons_sanity_check (search_path, icon_theme_name))
+ {
+ if (icon_theme_path)
+ {
+ /* this is an icon theme change */
+ gimp_icons_change_icon_theme (search_path);
+
+ if (! g_file_equal (icon_theme_path, path))
+ {
+ g_object_unref (icon_theme_path);
+ icon_theme_path = g_object_ref (path);
+ }
+
+ g_object_set (gtk_settings_get_for_screen (gdk_screen_get_default ()),
+ "gtk-icon-theme-name", icon_theme_name,
+ NULL);
+ }
+ else
+ {
+ /* this is the first call upon initialization */
+ icon_theme_path = g_object_ref (path);
+ }
+ }
+
+ g_free (icon_theme_name);
+ g_object_unref (search_path);
+ g_object_unref (path);
+}
+
+/**
+ * gimp_stock_init:
+ *
+ * Initializes the GIMP stock icon factory.
+ *
+ * You don't need to call this function as gimp_ui_init() already does
+ * this for you.
+ *
+ * Deprecated: 2.10: Use gimp_icons_init() instead.
+ */
+void
+gimp_stock_init (void)
+{
+ gimp_icons_init ();
+}
+
+/**
+ * gimp_icons_init:
+ *
+ * Initializes the GIMP stock icon factory.
+ *
+ * You don't need to call this function as gimp_ui_init() already does
+ * this for you.
+ */
+void
+gimp_icons_init (void)
+{
+ static gboolean initialized = FALSE;
+
+ GtkSettings *settings;
+ GdkPixbuf *pixbuf;
+ GError *error = NULL;
+ gchar *icons_dir;
+ gchar *system_icon_theme;
+ gchar *gimp_icon_theme;
+ gint i;
+
+ if (initialized)
+ return;
+
+ gimp_stock_factory = gtk_icon_factory_new ();
+
+ for (i = 0; i < G_N_ELEMENTS (gimp_stock_items); i++)
+ {
+ register_stock_icon (gimp_stock_factory,
+ gimp_stock_items[i].stock_id,
+ gimp_stock_items[i].stock_id);
+ }
+
+ register_bidi_stock_icon (gimp_stock_factory,
+ GIMP_STOCK_MENU_LEFT,
+ GIMP_STOCK_MENU_LEFT, GIMP_STOCK_MENU_RIGHT);
+ register_bidi_stock_icon (gimp_stock_factory,
+ GIMP_STOCK_MENU_RIGHT,
+ GIMP_STOCK_MENU_RIGHT, GIMP_STOCK_MENU_LEFT);
+
+ register_stock_icon (gimp_stock_factory,
+ "gimp-indexed-palette", GIMP_STOCK_COLORMAP);
+ register_stock_icon (gimp_stock_factory,
+ "gimp-qmask-off", GIMP_STOCK_QUICK_MASK_OFF);
+ register_stock_icon (gimp_stock_factory,
+ "gimp-qmask-on", GIMP_STOCK_QUICK_MASK_ON);
+ register_stock_icon (gimp_stock_factory,
+ "gimp-tool-blend", GIMP_STOCK_TOOL_BLEND);
+
+ gtk_icon_factory_add_default (gimp_stock_factory);
+
+ gtk_stock_add_static (gimp_stock_items,
+ G_N_ELEMENTS (gimp_stock_items));
+ gtk_stock_add_static (gimp_compat_stock_items,
+ G_N_ELEMENTS (gimp_compat_stock_items));
+
+ /* always prepend the default icon theme, it's never removed from
+ * the path again and acts as fallback for missing icons in other
+ * themes.
+ */
+ if (! default_search_path)
+ default_search_path = gimp_data_directory_file ("icons",
+ NULL);
+
+ icons_dir = g_file_get_path (default_search_path);
+ gtk_icon_theme_prepend_search_path (gtk_icon_theme_get_default (),
+ icons_dir);
+ g_free (icons_dir);
+
+ /* if an icon theme was chosen before init(), change to it */
+ if (icon_theme_path)
+ {
+ GFile *search_path = g_file_get_parent (icon_theme_path);
+
+ if (!g_file_equal (search_path, default_search_path))
+ {
+ gchar *icon_dir = g_file_get_path (search_path);
+
+ gtk_icon_theme_prepend_search_path (gtk_icon_theme_get_default (),
+ icon_dir);
+ g_free (icon_dir);
+ }
+ g_object_unref (search_path);
+
+ gimp_icon_theme = g_file_get_basename (icon_theme_path);
+ }
+ else
+ {
+ gimp_icon_theme = g_strdup (GIMP_DEFAULT_ICON_THEME);
+ }
+
+ settings = gtk_settings_get_for_screen (gdk_screen_get_default ());
+
+ g_object_get (settings, "gtk-icon-theme-name", &system_icon_theme, NULL);
+
+ g_object_set (settings,
+ "gtk-fallback-icon-theme", system_icon_theme,
+ "gtk-icon-theme-name", gimp_icon_theme,
+ NULL);
+
+ g_free (gimp_icon_theme);
+ g_free (system_icon_theme);
+
+ g_signal_connect (settings, "notify::gtk-icon-theme-name",
+ G_CALLBACK (gimp_icons_notify_system_icon_theme), NULL);
+ pixbuf = gdk_pixbuf_new_from_resource ("/org/gimp/icons/64/gimp-wilber-eek.png",
+ &error);
+
+ if (pixbuf)
+ {
+ gtk_icon_theme_add_builtin_icon (GIMP_STOCK_WILBER_EEK, 64, pixbuf);
+ g_object_unref (pixbuf);
+ }
+ else
+ {
+ g_critical ("Failed to create icon image: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ initialized = TRUE;
+}
diff --git a/libgimpwidgets/gimpicons.h b/libgimpwidgets/gimpicons.h
new file mode 100644
index 0000000..c297213
--- /dev/null
+++ b/libgimpwidgets/gimpicons.h
@@ -0,0 +1,680 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpicons.h
+ * Copyright (C) 2001-2015 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_ICONS_H__
+#define __GIMP_ICONS_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+/* random actions that don't fit in any category */
+
+#define GIMP_ICON_ATTACH "gimp-attach"
+#define GIMP_ICON_DETACH "gimp-detach"
+#define GIMP_ICON_INVERT "gimp-invert"
+#define GIMP_ICON_RECORD "media-record"
+#define GIMP_ICON_RESET "gimp-reset"
+#define GIMP_ICON_SHRED "gimp-shred"
+
+
+/* random states/things that don't fit in any category */
+
+#define GIMP_ICON_BUSINESS_CARD "gimp-business-card"
+#define GIMP_ICON_CHAR_PICKER "gimp-char-picker"
+#define GIMP_ICON_CURSOR "gimp-cursor"
+#define GIMP_ICON_DISPLAY "gimp-display"
+#define GIMP_ICON_GEGL "gimp-gegl"
+#define GIMP_ICON_LINKED "gimp-linked"
+#define GIMP_ICON_MARKER "gimp-marker"
+#define GIMP_ICON_SMARTPHONE "gimp-smartphone"
+#define GIMP_ICON_TRANSPARENCY "gimp-transparency"
+#define GIMP_ICON_VIDEO "gimp-video"
+#define GIMP_ICON_VISIBLE "gimp-visible"
+#define GIMP_ICON_WEB "gimp-web"
+
+
+/* random objects/entities that don't fit in any category */
+
+#define GIMP_ICON_BRUSH GIMP_ICON_TOOL_PAINTBRUSH
+#define GIMP_ICON_BUFFER GIMP_ICON_EDIT_PASTE
+#define GIMP_ICON_COLORMAP "gimp-colormap"
+#define GIMP_ICON_DYNAMICS "gimp-dynamics"
+#define GIMP_ICON_FILE_MANAGER "gimp-file-manager"
+#define GIMP_ICON_FONT "gtk-select-font"
+#define GIMP_ICON_GRADIENT GIMP_ICON_TOOL_GRADIENT
+#define GIMP_ICON_GRID "gimp-grid"
+#define GIMP_ICON_INPUT_DEVICE "gimp-input-device"
+#define GIMP_ICON_MYPAINT_BRUSH GIMP_ICON_TOOL_MYPAINT_BRUSH
+#define GIMP_ICON_PALETTE "gtk-select-color"
+#define GIMP_ICON_PATTERN "gimp-pattern"
+#define GIMP_ICON_PLUGIN "gimp-plugin"
+#define GIMP_ICON_SAMPLE_POINT "gimp-sample-point"
+#define GIMP_ICON_SYMMETRY "gimp-symmetry"
+#define GIMP_ICON_TEMPLATE "gimp-template"
+#define GIMP_ICON_TOOL_PRESET "gimp-tool-preset"
+
+
+/* not really icons */
+
+#define GIMP_ICON_FRAME "gimp-frame"
+#define GIMP_ICON_TEXTURE "gimp-texture"
+
+
+/* icons that follow, or at least try to follow the FDO naming and
+ * category conventions; and groups of icons with a common prefix;
+ * all sorted alphabetically
+ *
+ * see also:
+ * https://specifications.freedesktop.org/icon-naming-spec/latest/ar01s04.html
+ *
+ * some icons are marked with "use FDO", these shall be renamed in 3.0
+ * because we duplicated FDO standard icon names
+ */
+
+#define GIMP_ICON_APPLICATION_EXIT "application-exit"
+
+#define GIMP_ICON_ASPECT_PORTRAIT "gimp-portrait"
+#define GIMP_ICON_ASPECT_LANDSCAPE "gimp-landscape"
+
+#define GIMP_ICON_CAP_BUTT "gimp-cap-butt"
+#define GIMP_ICON_CAP_ROUND "gimp-cap-round"
+#define GIMP_ICON_CAP_SQUARE "gimp-cap-square"
+
+#define GIMP_ICON_CENTER "gimp-center"
+#define GIMP_ICON_CENTER_HORIZONTAL "gimp-hcenter"
+#define GIMP_ICON_CENTER_VERTICAL "gimp-vcenter"
+
+#define GIMP_ICON_CHAIN_HORIZONTAL "gimp-hchain"
+#define GIMP_ICON_CHAIN_HORIZONTAL_BROKEN "gimp-hchain-broken"
+#define GIMP_ICON_CHAIN_VERTICAL "gimp-vchain"
+#define GIMP_ICON_CHAIN_VERTICAL_BROKEN "gimp-vchain-broken"
+
+#define GIMP_ICON_CHANNEL "gimp-channel"
+#define GIMP_ICON_CHANNEL_ALPHA "gimp-channel-alpha"
+#define GIMP_ICON_CHANNEL_BLUE "gimp-channel-blue"
+#define GIMP_ICON_CHANNEL_GRAY "gimp-channel-gray"
+#define GIMP_ICON_CHANNEL_GREEN "gimp-channel-green"
+#define GIMP_ICON_CHANNEL_INDEXED "gimp-channel-indexed"
+#define GIMP_ICON_CHANNEL_RED "gimp-channel-red"
+
+#define GIMP_ICON_CLOSE "gimp-close"
+#define GIMP_ICON_CLOSE_ALL "gimp-close-all"
+
+#define GIMP_ICON_COLOR_PICKER_BLACK "gimp-color-picker-black"
+#define GIMP_ICON_COLOR_PICKER_GRAY "gimp-color-picker-gray"
+#define GIMP_ICON_COLOR_PICKER_WHITE "gimp-color-picker-white"
+#define GIMP_ICON_COLOR_PICK_FROM_SCREEN "gimp-color-pick-from-screen"
+
+#define GIMP_ICON_COLOR_SELECTOR_CMYK "gimp-color-cmyk"
+#define GIMP_ICON_COLOR_SELECTOR_TRIANGLE "gimp-color-triangle"
+#define GIMP_ICON_COLOR_SELECTOR_WATER "gimp-color-water"
+
+#define GIMP_ICON_COLOR_SPACE_LINEAR "gimp-color-space-linear"
+#define GIMP_ICON_COLOR_SPACE_NON_LINEAR "gimp-color-space-non-linear"
+#define GIMP_ICON_COLOR_SPACE_PERCEPTUAL "gimp-color-space-perceptual"
+
+#define GIMP_ICON_COLORS_DEFAULT "gimp-default-colors"
+#define GIMP_ICON_COLORS_SWAP "gimp-swap-colors"
+
+#define GIMP_ICON_CONTROLLER "gimp-controller"
+#define GIMP_ICON_CONTROLLER_KEYBOARD "gimp-controller-keyboard"
+#define GIMP_ICON_CONTROLLER_LINUX_INPUT "gimp-controller-linux-input"
+#define GIMP_ICON_CONTROLLER_MIDI "gimp-controller-midi"
+#define GIMP_ICON_CONTROLLER_MOUSE GIMP_ICON_CURSOR
+#define GIMP_ICON_CONTROLLER_WHEEL "gimp-controller-wheel"
+
+#define GIMP_ICON_CONVERT_RGB "gimp-convert-rgb"
+#define GIMP_ICON_CONVERT_GRAYSCALE "gimp-convert-grayscale"
+#define GIMP_ICON_CONVERT_INDEXED "gimp-convert-indexed"
+#define GIMP_ICON_CONVERT_PRECISION GIMP_ICON_CONVERT_RGB
+
+#define GIMP_ICON_CURVE_FREE "gimp-curve-free"
+#define GIMP_ICON_CURVE_SMOOTH "gimp-curve-smooth"
+
+#define GIMP_ICON_DIALOG_CHANNELS "gimp-channels"
+#define GIMP_ICON_DIALOG_DASHBOARD "gimp-dashboard"
+#define GIMP_ICON_DIALOG_DEVICE_STATUS "gimp-device-status"
+#define GIMP_ICON_DIALOG_ERROR "gimp-error" /* use FDO */
+#define GIMP_ICON_DIALOG_IMAGES "gimp-images"
+#define GIMP_ICON_DIALOG_INFORMATION "gimp-info" /* use FDO */
+#define GIMP_ICON_DIALOG_LAYERS "gimp-layers"
+#define GIMP_ICON_DIALOG_NAVIGATION "gimp-navigation"
+#define GIMP_ICON_DIALOG_PATHS "gimp-paths"
+#define GIMP_ICON_DIALOG_QUESTION "gimp-question" /* use FDO */
+#define GIMP_ICON_DIALOG_RESHOW_FILTER "gimp-reshow-filter"
+#define GIMP_ICON_DIALOG_TOOLS "gimp-tools"
+#define GIMP_ICON_DIALOG_TOOL_OPTIONS "gimp-tool-options"
+#define GIMP_ICON_DIALOG_UNDO_HISTORY "gimp-undo-history"
+#define GIMP_ICON_DIALOG_WARNING "gimp-warning" /* use FDO */
+
+#define GIMP_ICON_DISPLAY_FILTER "gimp-display-filter"
+#define GIMP_ICON_DISPLAY_FILTER_CLIP_WARNING "gimp-display-filter-clip-warning"
+#define GIMP_ICON_DISPLAY_FILTER_COLORBLIND "gimp-display-filter-colorblind"
+#define GIMP_ICON_DISPLAY_FILTER_CONTRAST "gimp-display-filter-contrast"
+#define GIMP_ICON_DISPLAY_FILTER_GAMMA "gimp-display-filter-gamma"
+#define GIMP_ICON_DISPLAY_FILTER_LCMS "gimp-display-filter-lcms"
+#define GIMP_ICON_DISPLAY_FILTER_PROOF "gimp-display-filter-proof"
+
+#define GIMP_ICON_DOCUMENT_NEW "document-new"
+#define GIMP_ICON_DOCUMENT_OPEN "document-open"
+#define GIMP_ICON_DOCUMENT_OPEN_RECENT "document-open-recent"
+#define GIMP_ICON_DOCUMENT_PAGE_SETUP "document-page-setup"
+#define GIMP_ICON_DOCUMENT_PRINT "document-print"
+#define GIMP_ICON_DOCUMENT_PRINT_RESOLUTION "document-print"
+#define GIMP_ICON_DOCUMENT_PROPERTIES "document-properties"
+#define GIMP_ICON_DOCUMENT_REVERT "document-revert"
+#define GIMP_ICON_DOCUMENT_SAVE "document-save"
+#define GIMP_ICON_DOCUMENT_SAVE_AS "document-save-as"
+
+#define GIMP_ICON_EDIT "gtk-edit"
+#define GIMP_ICON_EDIT_CLEAR "edit-clear"
+#define GIMP_ICON_EDIT_COPY "edit-copy"
+#define GIMP_ICON_EDIT_CUT "edit-cut"
+#define GIMP_ICON_EDIT_DELETE "edit-delete"
+#define GIMP_ICON_EDIT_FIND "edit-find"
+#define GIMP_ICON_EDIT_PASTE "edit-paste"
+#define GIMP_ICON_EDIT_PASTE_AS_NEW "gimp-paste-as-new"
+#define GIMP_ICON_EDIT_PASTE_INTO "gimp-paste-into"
+#define GIMP_ICON_EDIT_REDO "edit-redo"
+#define GIMP_ICON_EDIT_UNDO "edit-undo"
+
+#define GIMP_ICON_FILL_HORIZONTAL "gimp-hfill"
+#define GIMP_ICON_FILL_VERTICAL "gimp-vfill"
+
+#define GIMP_ICON_FOLDER_NEW "folder-new"
+
+#define GIMP_ICON_FORMAT_INDENT_MORE "format-indent-more"
+#define GIMP_ICON_FORMAT_INDENT_LESS "format-indent-less"
+#define GIMP_ICON_FORMAT_JUSTIFY_CENTER "format-justify-center"
+#define GIMP_ICON_FORMAT_JUSTIFY_FILL "format-justify-fill"
+#define GIMP_ICON_FORMAT_JUSTIFY_LEFT "format-justify-left"
+#define GIMP_ICON_FORMAT_JUSTIFY_RIGHT "format-justify-right"
+#define GIMP_ICON_FORMAT_TEXT_BOLD "format-text-bold"
+#define GIMP_ICON_FORMAT_TEXT_ITALIC "format-text-italic"
+#define GIMP_ICON_FORMAT_TEXT_STRIKETHROUGH "format-text-strikethrough"
+#define GIMP_ICON_FORMAT_TEXT_UNDERLINE "format-text-underline"
+#define GIMP_ICON_FORMAT_TEXT_DIRECTION_LTR "gimp-text-dir-ltr" /* use FDO */
+#define GIMP_ICON_FORMAT_TEXT_DIRECTION_RTL "gimp-text-dir-rtl" /* use FDO */
+#define GIMP_ICON_FORMAT_TEXT_DIRECTION_TTB_RTL "gimp-text-dir-ttb-rtl" /* use FDO */
+#define GIMP_ICON_FORMAT_TEXT_DIRECTION_TTB_RTL_UPRIGHT "gimp-text-dir-ttb-rtl-upright" /* use FDO */
+#define GIMP_ICON_FORMAT_TEXT_DIRECTION_TTB_LTR "gimp-text-dir-ttb-ltr" /* use FDO */
+#define GIMP_ICON_FORMAT_TEXT_DIRECTION_TTB_LTR_UPRIGHT "gimp-text-dir-ttb-ltr-upright" /* use FDO */
+#define GIMP_ICON_FORMAT_TEXT_SPACING_LETTER "gimp-letter-spacing"
+#define GIMP_ICON_FORMAT_TEXT_SPACING_LINE "gimp-line-spacing"
+
+#define GIMP_ICON_GRADIENT_LINEAR "gimp-gradient-linear"
+#define GIMP_ICON_GRADIENT_BILINEAR "gimp-gradient-bilinear"
+#define GIMP_ICON_GRADIENT_RADIAL "gimp-gradient-radial"
+#define GIMP_ICON_GRADIENT_SQUARE "gimp-gradient-square"
+#define GIMP_ICON_GRADIENT_CONICAL_SYMMETRIC "gimp-gradient-conical-symmetric"
+#define GIMP_ICON_GRADIENT_CONICAL_ASYMMETRIC "gimp-gradient-conical-asymmetric"
+#define GIMP_ICON_GRADIENT_SHAPEBURST_ANGULAR "gimp-gradient-shapeburst-angular"
+#define GIMP_ICON_GRADIENT_SHAPEBURST_SPHERICAL "gimp-gradient-shapeburst-spherical"
+#define GIMP_ICON_GRADIENT_SHAPEBURST_DIMPLED "gimp-gradient-shapeburst-dimpled"
+#define GIMP_ICON_GRADIENT_SPIRAL_CLOCKWISE "gimp-gradient-spiral-clockwise"
+#define GIMP_ICON_GRADIENT_SPIRAL_ANTICLOCKWISE "gimp-gradient-spiral-anticlockwise"
+
+#define GIMP_ICON_GRAVITY_EAST "gimp-gravity-east"
+#define GIMP_ICON_GRAVITY_NORTH "gimp-gravity-north"
+#define GIMP_ICON_GRAVITY_NORTH_EAST "gimp-gravity-north-east"
+#define GIMP_ICON_GRAVITY_NORTH_WEST "gimp-gravity-north-west"
+#define GIMP_ICON_GRAVITY_SOUTH "gimp-gravity-south"
+#define GIMP_ICON_GRAVITY_SOUTH_EAST "gimp-gravity-south-east"
+#define GIMP_ICON_GRAVITY_SOUTH_WEST "gimp-gravity-south-west"
+#define GIMP_ICON_GRAVITY_WEST "gimp-gravity-west"
+
+#define GIMP_ICON_GO_BOTTOM "go-bottom"
+#define GIMP_ICON_GO_DOWN "go-down"
+#define GIMP_ICON_GO_FIRST "go-first"
+#define GIMP_ICON_GO_HOME "go-home"
+#define GIMP_ICON_GO_LAST "go-last"
+#define GIMP_ICON_GO_TOP "go-top"
+#define GIMP_ICON_GO_UP "go-up"
+#define GIMP_ICON_GO_PREVIOUS "go-previous"
+#define GIMP_ICON_GO_NEXT "go-next"
+
+#define GIMP_ICON_HELP "help"
+#define GIMP_ICON_HELP_ABOUT "help-about"
+#define GIMP_ICON_HELP_USER_MANUAL "gimp-user-manual"
+
+#define GIMP_ICON_HISTOGRAM "gimp-histogram"
+#define GIMP_ICON_HISTOGRAM_LINEAR "gimp-histogram-linear"
+#define GIMP_ICON_HISTOGRAM_LOGARITHMIC "gimp-histogram-logarithmic"
+
+#define GIMP_ICON_IMAGE "gimp-image"
+#define GIMP_ICON_IMAGE_OPEN "gimp-image-open"
+#define GIMP_ICON_IMAGE_RELOAD "gimp-image-reload"
+
+#define GIMP_ICON_JOIN_MITER "gimp-join-miter"
+#define GIMP_ICON_JOIN_ROUND "gimp-join-round"
+#define GIMP_ICON_JOIN_BEVEL "gimp-join-bevel"
+
+#define GIMP_ICON_LAYER "gimp-layer"
+#define GIMP_ICON_LAYER_ANCHOR "gimp-anchor"
+#define GIMP_ICON_LAYER_FLOATING_SELECTION "gimp-floating-selection"
+#define GIMP_ICON_LAYER_MASK "gimp-layer-mask"
+#define GIMP_ICON_LAYER_MERGE_DOWN "gimp-merge-down"
+#define GIMP_ICON_LAYER_TEXT_LAYER "gimp-text-layer"
+#define GIMP_ICON_LAYER_TO_IMAGESIZE "gimp-layer-to-imagesize"
+
+#define GIMP_ICON_LIST "gimp-list"
+#define GIMP_ICON_LIST_ADD "list-add"
+#define GIMP_ICON_LIST_REMOVE "list-remove"
+
+#define GIMP_ICON_MENU_LEFT "gimp-menu-left"
+#define GIMP_ICON_MENU_RIGHT "gimp-menu-right"
+
+#define GIMP_ICON_OBJECT_DUPLICATE "gimp-duplicate"
+#define GIMP_ICON_OBJECT_FLIP_HORIZONTAL "gimp-flip-horizontal" /* use FDO */
+#define GIMP_ICON_OBJECT_FLIP_VERTICAL "gimp-flip-vertical" /* use FDO */
+#define GIMP_ICON_OBJECT_RESIZE "gimp-resize"
+#define GIMP_ICON_OBJECT_ROTATE_180 "gimp-rotate-180"
+#define GIMP_ICON_OBJECT_ROTATE_270 "gimp-rotate-270" /* use FDO */
+#define GIMP_ICON_OBJECT_ROTATE_90 "gimp-rotate-90" /* use FDO */
+#define GIMP_ICON_OBJECT_SCALE "gimp-scale"
+
+#define GIMP_ICON_PATH "gimp-path"
+#define GIMP_ICON_PATH_STROKE "gimp-path-stroke"
+
+#define GIMP_ICON_PIVOT_CENTER "gimp-pivot-center"
+#define GIMP_ICON_PIVOT_EAST "gimp-pivot-east"
+#define GIMP_ICON_PIVOT_NORTH "gimp-pivot-north"
+#define GIMP_ICON_PIVOT_NORTH_EAST "gimp-pivot-north-east"
+#define GIMP_ICON_PIVOT_NORTH_WEST "gimp-pivot-north-west"
+#define GIMP_ICON_PIVOT_SOUTH "gimp-pivot-south"
+#define GIMP_ICON_PIVOT_SOUTH_EAST "gimp-pivot-south-east"
+#define GIMP_ICON_PIVOT_SOUTH_WEST "gimp-pivot-south-west"
+#define GIMP_ICON_PIVOT_WEST "gimp-pivot-west"
+
+#define GIMP_ICON_PREFERENCES_SYSTEM "preferences-system"
+
+#define GIMP_ICON_PROCESS_STOP "process-stop"
+
+#define GIMP_ICON_QUICK_MASK_OFF "gimp-quick-mask-off"
+#define GIMP_ICON_QUICK_MASK_ON "gimp-quick-mask-on"
+
+#define GIMP_ICON_SELECTION "gimp-selection"
+#define GIMP_ICON_SELECTION_ADD "gimp-selection-add"
+#define GIMP_ICON_SELECTION_ALL "gimp-selection-all"
+#define GIMP_ICON_SELECTION_BORDER "gimp-selection-border"
+#define GIMP_ICON_SELECTION_GROW "gimp-selection-grow"
+#define GIMP_ICON_SELECTION_INTERSECT "gimp-selection-intersect"
+#define GIMP_ICON_SELECTION_NONE "gimp-selection-none"
+#define GIMP_ICON_SELECTION_REPLACE "gimp-selection-replace"
+#define GIMP_ICON_SELECTION_SHRINK "gimp-selection-shrink"
+#define GIMP_ICON_SELECTION_STROKE "gimp-selection-stroke"
+#define GIMP_ICON_SELECTION_SUBTRACT "gimp-selection-subtract"
+#define GIMP_ICON_SELECTION_TO_CHANNEL "gimp-selection-to-channel"
+#define GIMP_ICON_SELECTION_TO_PATH "gimp-selection-to-path"
+
+#define GIMP_ICON_SHAPE_CIRCLE "gimp-shape-circle"
+#define GIMP_ICON_SHAPE_DIAMOND "gimp-shape-diamond"
+#define GIMP_ICON_SHAPE_SQUARE "gimp-shape-square"
+
+#define GIMP_ICON_SYSTEM_RUN "system-run"
+
+#define GIMP_ICON_TOOL_AIRBRUSH "gimp-tool-airbrush"
+#define GIMP_ICON_TOOL_ALIGN "gimp-tool-align"
+#define GIMP_ICON_TOOL_BLUR "gimp-tool-blur"
+#define GIMP_ICON_TOOL_BRIGHTNESS_CONTRAST "gimp-tool-brightness-contrast"
+#define GIMP_ICON_TOOL_BUCKET_FILL "gimp-tool-bucket-fill"
+#define GIMP_ICON_TOOL_BY_COLOR_SELECT "gimp-tool-by-color-select"
+#define GIMP_ICON_TOOL_CAGE "gimp-tool-cage"
+#define GIMP_ICON_TOOL_CLONE "gimp-tool-clone"
+#define GIMP_ICON_TOOL_COLORIZE "gimp-tool-colorize"
+#define GIMP_ICON_TOOL_COLOR_BALANCE "gimp-tool-color-balance"
+#define GIMP_ICON_TOOL_COLOR_PICKER "gimp-tool-color-picker"
+#define GIMP_ICON_TOOL_COLOR_TEMPERATURE "gimp-tool-color-temperature"
+#define GIMP_ICON_TOOL_CROP "gimp-tool-crop"
+#define GIMP_ICON_TOOL_CURVES "gimp-tool-curves"
+#define GIMP_ICON_TOOL_DESATURATE "gimp-tool-desaturate"
+#define GIMP_ICON_TOOL_DODGE "gimp-tool-dodge"
+#define GIMP_ICON_TOOL_ELLIPSE_SELECT "gimp-tool-ellipse-select"
+#define GIMP_ICON_TOOL_ERASER "gimp-tool-eraser"
+#define GIMP_ICON_TOOL_EXPOSURE "gimp-tool-exposure"
+#define GIMP_ICON_TOOL_FLIP "gimp-tool-flip"
+#define GIMP_ICON_TOOL_FOREGROUND_SELECT "gimp-tool-foreground-select"
+#define GIMP_ICON_TOOL_FREE_SELECT "gimp-tool-free-select"
+#define GIMP_ICON_TOOL_FUZZY_SELECT "gimp-tool-fuzzy-select"
+#define GIMP_ICON_TOOL_GRADIENT "gimp-tool-gradient"
+#define GIMP_ICON_TOOL_HANDLE_TRANSFORM "gimp-tool-handle-transform"
+#define GIMP_ICON_TOOL_HEAL "gimp-tool-heal"
+#define GIMP_ICON_TOOL_HUE_SATURATION "gimp-tool-hue-saturation"
+#define GIMP_ICON_TOOL_INK "gimp-tool-ink"
+#define GIMP_ICON_TOOL_ISCISSORS "gimp-tool-iscissors"
+#define GIMP_ICON_TOOL_LEVELS "gimp-tool-levels"
+#define GIMP_ICON_TOOL_MEASURE "gimp-tool-measure"
+#define GIMP_ICON_TOOL_MOVE "gimp-tool-move"
+#define GIMP_ICON_TOOL_MYPAINT_BRUSH "gimp-tool-mypaint-brush"
+#define GIMP_ICON_TOOL_N_POINT_DEFORMATION "gimp-tool-n-point-deformation"
+#define GIMP_ICON_TOOL_OFFSET "gimp-tool-offset"
+#define GIMP_ICON_TOOL_PAINTBRUSH "gimp-tool-paintbrush"
+#define GIMP_ICON_TOOL_PATH "gimp-tool-path"
+#define GIMP_ICON_TOOL_PENCIL "gimp-tool-pencil"
+#define GIMP_ICON_TOOL_PERSPECTIVE "gimp-tool-perspective"
+#define GIMP_ICON_TOOL_PERSPECTIVE_CLONE "gimp-tool-perspective-clone"
+#define GIMP_ICON_TOOL_POSTERIZE "gimp-tool-posterize"
+#define GIMP_ICON_TOOL_RECT_SELECT "gimp-tool-rect-select"
+#define GIMP_ICON_TOOL_ROTATE "gimp-tool-rotate"
+#define GIMP_ICON_TOOL_SCALE "gimp-tool-scale"
+#define GIMP_ICON_TOOL_SEAMLESS_CLONE "gimp-tool-seamless-clone"
+#define GIMP_ICON_TOOL_SHADOWS_HIGHLIGHTS "gimp-tool-shadows-highlights"
+#define GIMP_ICON_TOOL_SHEAR "gimp-tool-shear"
+#define GIMP_ICON_TOOL_SMUDGE "gimp-tool-smudge"
+#define GIMP_ICON_TOOL_TEXT "gimp-tool-text"
+#define GIMP_ICON_TOOL_THRESHOLD "gimp-tool-threshold"
+#define GIMP_ICON_TOOL_TRANSFORM_3D "gimp-tool-transform-3d"
+#define GIMP_ICON_TOOL_UNIFIED_TRANSFORM "gimp-tool-unified-transform"
+#define GIMP_ICON_TOOL_WARP "gimp-tool-warp"
+#define GIMP_ICON_TOOL_ZOOM "gimp-tool-zoom"
+
+#define GIMP_ICON_TRANSFORM_3D_CAMERA "gimp-transform-3d-camera"
+#define GIMP_ICON_TRANSFORM_3D_MOVE "gimp-transform-3d-move"
+#define GIMP_ICON_TRANSFORM_3D_ROTATE "gimp-transform-3d-rotate"
+
+#define GIMP_ICON_VIEW_REFRESH "view-refresh"
+#define GIMP_ICON_VIEW_FULLSCREEN "view-fullscreen"
+
+#define GIMP_ICON_WILBER "gimp-wilber"
+#define GIMP_ICON_WILBER_EEK "gimp-wilber-eek"
+
+#define GIMP_ICON_WINDOW_CLOSE "window-close"
+#define GIMP_ICON_WINDOW_MOVE_TO_SCREEN "gimp-move-to-screen"
+#define GIMP_ICON_WINDOW_NEW "window-new"
+
+#define GIMP_ICON_ZOOM_IN "zoom-in"
+#define GIMP_ICON_ZOOM_ORIGINAL "zoom-original"
+#define GIMP_ICON_ZOOM_OUT "zoom-out"
+#define GIMP_ICON_ZOOM_FIT_BEST "zoom-fit-best"
+#define GIMP_ICON_ZOOM_FOLLOW_WINDOW "gimp-zoom-follow-window"
+
+
+#ifndef GIMP_DISABLE_DEPRECATED
+
+/* in button size: */
+
+#define GIMP_STOCK_ANCHOR "gimp-anchor"
+#define GIMP_STOCK_CENTER "gimp-center"
+#define GIMP_STOCK_DUPLICATE "gimp-duplicate"
+#define GIMP_STOCK_LINKED "gimp-linked"
+#define GIMP_STOCK_PASTE_AS_NEW "gimp-paste-as-new"
+#define GIMP_STOCK_PASTE_INTO "gimp-paste-into"
+#define GIMP_STOCK_RESET "gimp-reset"
+#define GIMP_STOCK_VISIBLE "gimp-visible"
+
+#define GIMP_STOCK_GRADIENT_LINEAR "gimp-gradient-linear"
+#define GIMP_STOCK_GRADIENT_BILINEAR "gimp-gradient-bilinear"
+#define GIMP_STOCK_GRADIENT_RADIAL "gimp-gradient-radial"
+#define GIMP_STOCK_GRADIENT_SQUARE "gimp-gradient-square"
+#define GIMP_STOCK_GRADIENT_CONICAL_SYMMETRIC "gimp-gradient-conical-symmetric"
+#define GIMP_STOCK_GRADIENT_CONICAL_ASYMMETRIC "gimp-gradient-conical-asymmetric"
+#define GIMP_STOCK_GRADIENT_SHAPEBURST_ANGULAR "gimp-gradient-shapeburst-angular"
+#define GIMP_STOCK_GRADIENT_SHAPEBURST_SPHERICAL "gimp-gradient-shapeburst-spherical"
+#define GIMP_STOCK_GRADIENT_SHAPEBURST_DIMPLED "gimp-gradient-shapeburst-dimpled"
+#define GIMP_STOCK_GRADIENT_SPIRAL_CLOCKWISE "gimp-gradient-spiral-clockwise"
+#define GIMP_STOCK_GRADIENT_SPIRAL_ANTICLOCKWISE "gimp-gradient-spiral-anticlockwise"
+
+#define GIMP_STOCK_GRAVITY_EAST "gimp-gravity-east"
+#define GIMP_STOCK_GRAVITY_NORTH "gimp-gravity-north"
+#define GIMP_STOCK_GRAVITY_NORTH_EAST "gimp-gravity-north-east"
+#define GIMP_STOCK_GRAVITY_NORTH_WEST "gimp-gravity-north-west"
+#define GIMP_STOCK_GRAVITY_SOUTH "gimp-gravity-south"
+#define GIMP_STOCK_GRAVITY_SOUTH_EAST "gimp-gravity-south-east"
+#define GIMP_STOCK_GRAVITY_SOUTH_WEST "gimp-gravity-south-west"
+#define GIMP_STOCK_GRAVITY_WEST "gimp-gravity-west"
+
+#define GIMP_STOCK_HCENTER "gimp-hcenter"
+#define GIMP_STOCK_VCENTER "gimp-vcenter"
+
+#define GIMP_STOCK_HCHAIN "gimp-hchain"
+#define GIMP_STOCK_HCHAIN_BROKEN "gimp-hchain-broken"
+#define GIMP_STOCK_VCHAIN "gimp-vchain"
+#define GIMP_STOCK_VCHAIN_BROKEN "gimp-vchain-broken"
+
+#define GIMP_STOCK_SELECTION "gimp-selection"
+#define GIMP_STOCK_SELECTION_REPLACE "gimp-selection-replace"
+#define GIMP_STOCK_SELECTION_ADD "gimp-selection-add"
+#define GIMP_STOCK_SELECTION_SUBTRACT "gimp-selection-subtract"
+#define GIMP_STOCK_SELECTION_INTERSECT "gimp-selection-intersect"
+#define GIMP_STOCK_SELECTION_STROKE "gimp-selection-stroke"
+#define GIMP_STOCK_SELECTION_TO_CHANNEL "gimp-selection-to-channel"
+#define GIMP_STOCK_SELECTION_TO_PATH "gimp-selection-to-path"
+
+#define GIMP_STOCK_PATH_STROKE "gimp-path-stroke"
+
+#define GIMP_STOCK_CURVE_FREE "gimp-curve-free"
+#define GIMP_STOCK_CURVE_SMOOTH "gimp-curve-smooth"
+
+#define GIMP_STOCK_COLOR_PICKER_BLACK "gimp-color-picker-black"
+#define GIMP_STOCK_COLOR_PICKER_GRAY "gimp-color-picker-gray"
+#define GIMP_STOCK_COLOR_PICKER_WHITE "gimp-color-picker-white"
+#define GIMP_STOCK_COLOR_TRIANGLE "gimp-color-triangle"
+#define GIMP_STOCK_COLOR_PICK_FROM_SCREEN "gimp-color-pick-from-screen"
+
+#define GIMP_STOCK_CHAR_PICKER "gimp-char-picker"
+#define GIMP_STOCK_LETTER_SPACING "gimp-letter-spacing"
+#define GIMP_STOCK_LINE_SPACING "gimp-line-spacing"
+#define GIMP_STOCK_PATTERN "gimp-pattern"
+
+#define GIMP_STOCK_TEXT_DIR_LTR "gimp-text-dir-ltr"
+#define GIMP_STOCK_TEXT_DIR_RTL "gimp-text-dir-rtl"
+
+#define GIMP_STOCK_TOOL_AIRBRUSH "gimp-tool-airbrush"
+#define GIMP_STOCK_TOOL_ALIGN "gimp-tool-align"
+#define GIMP_STOCK_TOOL_BLEND "gimp-tool-gradient"
+#define GIMP_STOCK_TOOL_BLUR "gimp-tool-blur"
+#define GIMP_STOCK_TOOL_BRIGHTNESS_CONTRAST "gimp-tool-brightness-contrast"
+#define GIMP_STOCK_TOOL_BUCKET_FILL "gimp-tool-bucket-fill"
+#define GIMP_STOCK_TOOL_BY_COLOR_SELECT "gimp-tool-by-color-select"
+#define GIMP_STOCK_TOOL_CAGE "gimp-tool-cage"
+#define GIMP_STOCK_TOOL_CLONE "gimp-tool-clone"
+#define GIMP_STOCK_TOOL_COLOR_BALANCE "gimp-tool-color-balance"
+#define GIMP_STOCK_TOOL_COLOR_PICKER "gimp-tool-color-picker"
+#define GIMP_STOCK_TOOL_COLORIZE "gimp-tool-colorize"
+#define GIMP_STOCK_TOOL_CROP "gimp-tool-crop"
+#define GIMP_STOCK_TOOL_CURVES "gimp-tool-curves"
+#define GIMP_STOCK_TOOL_DESATURATE "gimp-tool-desaturate"
+#define GIMP_STOCK_TOOL_DODGE "gimp-tool-dodge"
+#define GIMP_STOCK_TOOL_ELLIPSE_SELECT "gimp-tool-ellipse-select"
+#define GIMP_STOCK_TOOL_ERASER "gimp-tool-eraser"
+#define GIMP_STOCK_TOOL_FLIP "gimp-tool-flip"
+#define GIMP_STOCK_TOOL_FREE_SELECT "gimp-tool-free-select"
+#define GIMP_STOCK_TOOL_FOREGROUND_SELECT "gimp-tool-foreground-select"
+#define GIMP_STOCK_TOOL_FUZZY_SELECT "gimp-tool-fuzzy-select"
+#define GIMP_STOCK_TOOL_HEAL "gimp-tool-heal"
+#define GIMP_STOCK_TOOL_HUE_SATURATION "gimp-tool-hue-saturation"
+#define GIMP_STOCK_TOOL_INK "gimp-tool-ink"
+#define GIMP_STOCK_TOOL_ISCISSORS "gimp-tool-iscissors"
+#define GIMP_STOCK_TOOL_LEVELS "gimp-tool-levels"
+#define GIMP_STOCK_TOOL_MEASURE "gimp-tool-measure"
+#define GIMP_STOCK_TOOL_MOVE "gimp-tool-move"
+#define GIMP_STOCK_TOOL_PAINTBRUSH "gimp-tool-paintbrush"
+#define GIMP_STOCK_TOOL_PATH "gimp-tool-path"
+#define GIMP_STOCK_TOOL_PENCIL "gimp-tool-pencil"
+#define GIMP_STOCK_TOOL_PERSPECTIVE "gimp-tool-perspective"
+#define GIMP_STOCK_TOOL_PERSPECTIVE_CLONE "gimp-tool-perspective-clone"
+#define GIMP_STOCK_TOOL_POSTERIZE "gimp-tool-posterize"
+#define GIMP_STOCK_TOOL_RECT_SELECT "gimp-tool-rect-select"
+#define GIMP_STOCK_TOOL_ROTATE "gimp-tool-rotate"
+#define GIMP_STOCK_TOOL_SCALE "gimp-tool-scale"
+#define GIMP_STOCK_TOOL_SHEAR "gimp-tool-shear"
+#define GIMP_STOCK_TOOL_SMUDGE "gimp-tool-smudge"
+#define GIMP_STOCK_TOOL_TEXT "gimp-tool-text"
+#define GIMP_STOCK_TOOL_THRESHOLD "gimp-tool-threshold"
+#define GIMP_STOCK_TOOL_ZOOM "gimp-tool-zoom"
+
+/* in menu size: */
+
+#define GIMP_STOCK_CONVERT_RGB "gimp-convert-rgb"
+#define GIMP_STOCK_CONVERT_GRAYSCALE "gimp-convert-grayscale"
+#define GIMP_STOCK_CONVERT_INDEXED "gimp-convert-indexed"
+#define GIMP_STOCK_INVERT "gimp-invert"
+#define GIMP_STOCK_MERGE_DOWN "gimp-merge-down"
+#define GIMP_STOCK_LAYER_TO_IMAGESIZE "gimp-layer-to-imagesize"
+#define GIMP_STOCK_PLUGIN "gimp-plugin"
+#define GIMP_STOCK_UNDO_HISTORY "gimp-undo-history"
+#define GIMP_STOCK_RESHOW_FILTER "gimp-reshow-filter"
+#define GIMP_STOCK_ROTATE_90 "gimp-rotate-90"
+#define GIMP_STOCK_ROTATE_180 "gimp-rotate-180"
+#define GIMP_STOCK_ROTATE_270 "gimp-rotate-270"
+#define GIMP_STOCK_RESIZE "gimp-resize"
+#define GIMP_STOCK_SCALE "gimp-scale"
+#define GIMP_STOCK_FLIP_HORIZONTAL "gimp-flip-horizontal"
+#define GIMP_STOCK_FLIP_VERTICAL "gimp-flip-vertical"
+
+#define GIMP_STOCK_IMAGE "gimp-image"
+#define GIMP_STOCK_LAYER "gimp-layer"
+#define GIMP_STOCK_TEXT_LAYER "gimp-text-layer"
+#define GIMP_STOCK_FLOATING_SELECTION "gimp-floating-selection"
+#define GIMP_STOCK_CHANNEL "gimp-channel"
+#define GIMP_STOCK_CHANNEL_RED "gimp-channel-red"
+#define GIMP_STOCK_CHANNEL_GREEN "gimp-channel-green"
+#define GIMP_STOCK_CHANNEL_BLUE "gimp-channel-blue"
+#define GIMP_STOCK_CHANNEL_GRAY "gimp-channel-gray"
+#define GIMP_STOCK_CHANNEL_INDEXED "gimp-channel-indexed"
+#define GIMP_STOCK_CHANNEL_ALPHA "gimp-channel-alpha"
+#define GIMP_STOCK_LAYER_MASK "gimp-layer-mask"
+#define GIMP_STOCK_PATH "gimp-path"
+#define GIMP_STOCK_TEMPLATE "gimp-template"
+#define GIMP_STOCK_TRANSPARENCY "gimp-transparency"
+#define GIMP_STOCK_COLORMAP "gimp-colormap"
+
+#define GIMP_STOCK_INDEXED_PALETTE "gimp-colormap"
+
+#define GIMP_STOCK_IMAGES "gimp-images"
+#define GIMP_STOCK_LAYERS "gimp-layers"
+#define GIMP_STOCK_CHANNELS "gimp-channels"
+#define GIMP_STOCK_PATHS "gimp-paths"
+
+#define GIMP_STOCK_SELECTION_ALL "gimp-selection-all"
+#define GIMP_STOCK_SELECTION_NONE "gimp-selection-none"
+#define GIMP_STOCK_SELECTION_GROW "gimp-selection-grow"
+#define GIMP_STOCK_SELECTION_SHRINK "gimp-selection-shrink"
+#define GIMP_STOCK_SELECTION_BORDER "gimp-selection-border"
+
+#define GIMP_STOCK_NAVIGATION "gimp-navigation"
+#define GIMP_STOCK_QUICK_MASK_OFF "gimp-quick-mask-off"
+#define GIMP_STOCK_QUICK_MASK_ON "gimp-quick-mask-on"
+
+#define GIMP_STOCK_QMASK_OFF "gimp-quick-mask-off"
+#define GIMP_STOCK_QMASK_ON "gimp-quick-mask-on"
+
+#define GIMP_STOCK_HISTOGRAM "gimp-histogram"
+#define GIMP_STOCK_HISTOGRAM_LINEAR "gimp-histogram-linear"
+#define GIMP_STOCK_HISTOGRAM_LOGARITHMIC "gimp-histogram-logarithmic"
+
+#define GIMP_STOCK_CLOSE "gimp-close"
+#define GIMP_STOCK_MENU_LEFT "gimp-menu-left"
+#define GIMP_STOCK_MENU_RIGHT "gimp-menu-right"
+#define GIMP_STOCK_MOVE_TO_SCREEN "gimp-move-to-screen"
+#define GIMP_STOCK_DEFAULT_COLORS "gimp-default-colors"
+#define GIMP_STOCK_SWAP_COLORS "gimp-swap-colors"
+#define GIMP_STOCK_ZOOM_FOLLOW_WINDOW "gimp-zoom-follow-window"
+
+#define GIMP_STOCK_TOOLS "gimp-tools"
+#define GIMP_STOCK_TOOL_OPTIONS "gimp-tool-options"
+#define GIMP_STOCK_DEVICE_STATUS "gimp-device-status"
+#define GIMP_STOCK_INPUT_DEVICE "gimp-input-device"
+#define GIMP_STOCK_CURSOR "gimp-cursor"
+#define GIMP_STOCK_SAMPLE_POINT "gimp-sample-point"
+#define GIMP_STOCK_DYNAMICS "gimp-dynamics"
+#define GIMP_STOCK_TOOL_PRESET "gimp-tool-preset"
+
+#define GIMP_STOCK_CONTROLLER "gimp-controller"
+#define GIMP_STOCK_CONTROLLER_KEYBOARD "gimp-controller-keyboard"
+#define GIMP_STOCK_CONTROLLER_LINUX_INPUT "gimp-controller-linux-input"
+#define GIMP_STOCK_CONTROLLER_MIDI "gimp-controller-midi"
+#define GIMP_STOCK_CONTROLLER_WHEEL "gimp-controller-wheel"
+
+#define GIMP_STOCK_DISPLAY_FILTER "gimp-display-filter"
+#define GIMP_STOCK_DISPLAY_FILTER_COLORBLIND "gimp-display-filter-colorblind"
+#define GIMP_STOCK_DISPLAY_FILTER_CONTRAST "gimp-display-filter-contrast"
+#define GIMP_STOCK_DISPLAY_FILTER_GAMMA "gimp-display-filter-gamma"
+#define GIMP_STOCK_DISPLAY_FILTER_LCMS "gimp-display-filter-lcms"
+#define GIMP_STOCK_DISPLAY_FILTER_PROOF "gimp-display-filter-proof"
+
+#define GIMP_STOCK_LIST "gimp-list"
+#define GIMP_STOCK_GRID "gimp-grid"
+
+#define GIMP_STOCK_PORTRAIT "gimp-portrait"
+#define GIMP_STOCK_LANDSCAPE "gimp-landscape"
+
+#define GIMP_STOCK_GEGL "gimp-gegl"
+#define GIMP_STOCK_VIDEO "gimp-video"
+#define GIMP_STOCK_WEB "gimp-web"
+
+#define GIMP_STOCK_SHAPE_CIRCLE "gimp-shape-circle"
+#define GIMP_STOCK_SHAPE_DIAMOND "gimp-shape-diamond"
+#define GIMP_STOCK_SHAPE_SQUARE "gimp-shape-square"
+
+#define GIMP_STOCK_CAP_BUTT "gimp-cap-butt"
+#define GIMP_STOCK_CAP_ROUND "gimp-cap-round"
+#define GIMP_STOCK_CAP_SQUARE "gimp-cap-square"
+
+#define GIMP_STOCK_JOIN_MITER "gimp-join-miter"
+#define GIMP_STOCK_JOIN_ROUND "gimp-join-round"
+#define GIMP_STOCK_JOIN_BEVEL "gimp-join-bevel"
+
+/* in dialog size: */
+
+#define GIMP_STOCK_ERROR "gimp-error"
+#define GIMP_STOCK_INFO "gimp-info"
+#define GIMP_STOCK_QUESTION "gimp-question"
+#define GIMP_STOCK_WARNING "gimp-warning"
+#define GIMP_STOCK_WILBER "gimp-wilber"
+#define GIMP_STOCK_WILBER_EEK "gimp-wilber-eek"
+#define GIMP_STOCK_FRAME "gimp-frame"
+#define GIMP_STOCK_TEXTURE "gimp-texture"
+#define GIMP_STOCK_USER_MANUAL "gimp-user-manual"
+
+/* missing icons: */
+
+#define GIMP_STOCK_BRUSH GIMP_STOCK_TOOL_PAINTBRUSH
+#define GIMP_STOCK_BUFFER "edit-paste"
+#define GIMP_STOCK_DETACH GTK_STOCK_CONVERT
+#define GIMP_STOCK_FONT GTK_STOCK_SELECT_FONT
+#define GIMP_STOCK_GRADIENT GIMP_STOCK_TOOL_BLEND
+#define GIMP_STOCK_PALETTE GTK_STOCK_SELECT_COLOR
+#define GIMP_STOCK_CONTROLLER_MOUSE GIMP_STOCK_CURSOR
+#define GIMP_STOCK_PRINT_RESOLUTION "document-print"
+
+#define GIMP_STOCK_EDIT "gtk-edit"
+
+#endif /* GIMP_DISABLE_DEPRECATED */
+
+
+GIMP_DEPRECATED_FOR(gimp_icons_init)
+void gimp_stock_init (void);
+
+void gimp_icons_init (void);
+
+void gimp_icons_set_icon_theme (GFile *path);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_ICONS_H__ */
diff --git a/libgimpwidgets/gimpintcombobox.c b/libgimpwidgets/gimpintcombobox.c
new file mode 100644
index 0000000..6cd8909
--- /dev/null
+++ b/libgimpwidgets/gimpintcombobox.c
@@ -0,0 +1,978 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpintcombobox.c
+ * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <libintl.h>
+
+#include <gtk/gtk.h>
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpintcombobox.h"
+#include "gimpintstore.h"
+
+
+/**
+ * SECTION: gimpintcombobox
+ * @title: GimpIntComboBox
+ * @short_description: A widget providing a popup menu of integer
+ * values (e.g. enums).
+ *
+ * A widget providing a popup menu of integer values (e.g. enums).
+ **/
+
+
+enum
+{
+ PROP_0,
+ PROP_ELLIPSIZE,
+ PROP_LABEL,
+ PROP_LAYOUT
+};
+
+
+typedef struct
+{
+ GtkCellRenderer *pixbuf_renderer;
+ GtkCellRenderer *text_renderer;
+
+ GtkCellRenderer *menu_pixbuf_renderer;
+ GtkCellRenderer *menu_text_renderer;
+
+ PangoEllipsizeMode ellipsize;
+ gchar *label;
+ GtkCellRenderer *label_renderer;
+ GimpIntComboBoxLayout layout;
+
+ GimpIntSensitivityFunc sensitivity_func;
+ gpointer sensitivity_data;
+ GDestroyNotify sensitivity_destroy;
+} GimpIntComboBoxPrivate;
+
+#define GIMP_INT_COMBO_BOX_GET_PRIVATE(obj) \
+ ((GimpIntComboBoxPrivate *) ((GimpIntComboBox *) (obj))->priv)
+
+
+static void gimp_int_combo_box_finalize (GObject *object);
+static void gimp_int_combo_box_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_int_combo_box_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_int_combo_box_create_cells (GimpIntComboBox *combo_box);
+static void gimp_int_combo_box_data_func (GtkCellLayout *layout,
+ GtkCellRenderer *cell,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data);
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpIntComboBox, gimp_int_combo_box,
+ GTK_TYPE_COMBO_BOX)
+
+#define parent_class gimp_int_combo_box_parent_class
+
+
+static void
+gimp_int_combo_box_class_init (GimpIntComboBoxClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gimp_int_combo_box_finalize;
+ object_class->set_property = gimp_int_combo_box_set_property;
+ object_class->get_property = gimp_int_combo_box_get_property;
+
+ /**
+ * GimpIntComboBox:ellipsize:
+ *
+ * Specifies the preferred place to ellipsize text in the combo-box,
+ * if the cell renderer does not have enough room to display the
+ * entire string.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class, PROP_ELLIPSIZE,
+ g_param_spec_enum ("ellipsize",
+ "Ellipsize",
+ "Ellipsize mode for the used text cell renderer",
+ PANGO_TYPE_ELLIPSIZE_MODE,
+ PANGO_ELLIPSIZE_NONE,
+ GIMP_PARAM_READWRITE));
+
+ /**
+ * GimpIntComboBox:label:
+ *
+ * Sets a label on the combo-box, see gimp_int_combo_box_set_label().
+ *
+ * Since: 2.10
+ */
+ g_object_class_install_property (object_class, PROP_LABEL,
+ g_param_spec_string ("label",
+ "Label",
+ "An optional label to be displayed",
+ NULL,
+ GIMP_PARAM_READWRITE));
+
+ /**
+ * GimpIntComboBox:layout:
+ *
+ * Specifies the combo box layout.
+ *
+ * Since: 2.10
+ */
+ g_object_class_install_property (object_class, PROP_LAYOUT,
+ g_param_spec_enum ("layout",
+ "Layout",
+ "Combo box layout",
+ GIMP_TYPE_INT_COMBO_BOX_LAYOUT,
+ GIMP_INT_COMBO_BOX_LAYOUT_ABBREVIATED,
+ GIMP_PARAM_READWRITE));
+}
+
+static void
+gimp_int_combo_box_init (GimpIntComboBox *combo_box)
+{
+ GimpIntComboBoxPrivate *priv;
+ GtkListStore *store;
+
+ combo_box->priv = priv = gimp_int_combo_box_get_instance_private (combo_box);
+
+ store = gimp_int_store_new ();
+ gtk_combo_box_set_model (GTK_COMBO_BOX (combo_box), GTK_TREE_MODEL (store));
+ g_object_unref (store);
+
+ priv->layout = GIMP_INT_COMBO_BOX_LAYOUT_ABBREVIATED;
+
+ gimp_int_combo_box_create_cells (GIMP_INT_COMBO_BOX (combo_box));
+}
+
+static void
+gimp_int_combo_box_finalize (GObject *object)
+{
+ GimpIntComboBoxPrivate *priv = GIMP_INT_COMBO_BOX_GET_PRIVATE (object);
+
+ g_clear_pointer (&priv->label, g_free);
+
+ if (priv->sensitivity_destroy)
+ {
+ GDestroyNotify d = priv->sensitivity_destroy;
+
+ priv->sensitivity_destroy = NULL;
+ d (priv->sensitivity_data);
+ }
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_int_combo_box_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpIntComboBoxPrivate *priv = GIMP_INT_COMBO_BOX_GET_PRIVATE (object);
+
+ switch (property_id)
+ {
+ case PROP_ELLIPSIZE:
+ priv->ellipsize = g_value_get_enum (value);
+ if (priv->text_renderer)
+ {
+ g_object_set_property (G_OBJECT (priv->text_renderer),
+ pspec->name, value);
+ }
+ break;
+ case PROP_LABEL:
+ gimp_int_combo_box_set_label (GIMP_INT_COMBO_BOX (object),
+ g_value_get_string (value));
+ break;
+ case PROP_LAYOUT:
+ gimp_int_combo_box_set_layout (GIMP_INT_COMBO_BOX (object),
+ g_value_get_enum (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_int_combo_box_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpIntComboBoxPrivate *priv = GIMP_INT_COMBO_BOX_GET_PRIVATE (object);
+
+ switch (property_id)
+ {
+ case PROP_ELLIPSIZE:
+ g_value_set_enum (value, priv->ellipsize);
+ break;
+ case PROP_LABEL:
+ g_value_set_string (value, priv->label);
+ break;
+ case PROP_LAYOUT:
+ g_value_set_enum (value, priv->layout);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+
+/**
+ * gimp_int_combo_box_new:
+ * @first_label: the label of the first item
+ * @first_value: the value of the first item
+ * @...: a %NULL terminated list of more label, value pairs
+ *
+ * Creates a GtkComboBox that has integer values associated with each
+ * item. The items to fill the combo box with are specified as a %NULL
+ * terminated list of label/value pairs.
+ *
+ * If you need to construct an empty #GimpIntComboBox, it's best to use
+ * g_object_new (GIMP_TYPE_INT_COMBO_BOX, NULL).
+ *
+ * Return value: a new #GimpIntComboBox.
+ *
+ * Since: 2.2
+ **/
+GtkWidget *
+gimp_int_combo_box_new (const gchar *first_label,
+ gint first_value,
+ ...)
+{
+ GtkWidget *combo_box;
+ va_list args;
+
+ va_start (args, first_value);
+
+ combo_box = gimp_int_combo_box_new_valist (first_label, first_value, args);
+
+ va_end (args);
+
+ return combo_box;
+}
+
+/**
+ * gimp_int_combo_box_new_valist:
+ * @first_label: the label of the first item
+ * @first_value: the value of the first item
+ * @values: a va_list with more values
+ *
+ * A variant of gimp_int_combo_box_new() that takes a va_list of
+ * label/value pairs. Probably only useful for language bindings.
+ *
+ * Return value: a new #GimpIntComboBox.
+ *
+ * Since: 2.2
+ **/
+GtkWidget *
+gimp_int_combo_box_new_valist (const gchar *first_label,
+ gint first_value,
+ va_list values)
+{
+ GtkWidget *combo_box;
+ GtkListStore *store;
+ const gchar *label;
+ gint value;
+
+ combo_box = g_object_new (GIMP_TYPE_INT_COMBO_BOX, NULL);
+
+ store = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box)));
+
+ for (label = first_label, value = first_value;
+ label;
+ label = va_arg (values, const gchar *), value = va_arg (values, gint))
+ {
+ GtkTreeIter iter = { 0, };
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ GIMP_INT_STORE_VALUE, value,
+ GIMP_INT_STORE_LABEL, label,
+ -1);
+ }
+
+ return combo_box;
+}
+
+/**
+ * gimp_int_combo_box_new_array:
+ * @n_values: the number of values
+ * @labels: an array of labels (array length must be @n_values)
+ *
+ * A variant of gimp_int_combo_box_new() that takes an array of labels.
+ * The array indices are used as values.
+ *
+ * Return value: a new #GimpIntComboBox.
+ *
+ * Since: 2.2
+ **/
+GtkWidget *
+gimp_int_combo_box_new_array (gint n_values,
+ const gchar *labels[])
+{
+ GtkWidget *combo_box;
+ GtkListStore *store;
+ gint i;
+
+ g_return_val_if_fail (n_values >= 0, NULL);
+ g_return_val_if_fail (labels != NULL || n_values == 0, NULL);
+
+ combo_box = g_object_new (GIMP_TYPE_INT_COMBO_BOX, NULL);
+
+ store = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box)));
+
+ for (i = 0; i < n_values; i++)
+ {
+ GtkTreeIter iter;
+
+ if (labels[i])
+ {
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ GIMP_INT_STORE_VALUE, i,
+ GIMP_INT_STORE_LABEL, gettext (labels[i]),
+ -1);
+ }
+ }
+
+ return combo_box;
+}
+
+/**
+ * gimp_int_combo_box_prepend:
+ * @combo_box: a #GimpIntComboBox
+ * @...: pairs of column number and value, terminated with -1
+ *
+ * This function provides a convenient way to prepend items to a
+ * #GimpIntComboBox. It prepends a row to the @combo_box's list store
+ * and calls gtk_list_store_set() for you.
+ *
+ * The column number must be taken from the enum #GimpIntStoreColumns.
+ *
+ * Since: 2.2
+ **/
+void
+gimp_int_combo_box_prepend (GimpIntComboBox *combo_box,
+ ...)
+{
+ GtkListStore *store;
+ GtkTreeIter iter;
+ va_list args;
+
+ g_return_if_fail (GIMP_IS_INT_COMBO_BOX (combo_box));
+
+ store = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box)));
+
+ va_start (args, combo_box);
+
+ gtk_list_store_prepend (store, &iter);
+ gtk_list_store_set_valist (store, &iter, args);
+
+ va_end (args);
+}
+
+/**
+ * gimp_int_combo_box_append:
+ * @combo_box: a #GimpIntComboBox
+ * @...: pairs of column number and value, terminated with -1
+ *
+ * This function provides a convenient way to append items to a
+ * #GimpIntComboBox. It appends a row to the @combo_box's list store
+ * and calls gtk_list_store_set() for you.
+ *
+ * The column number must be taken from the enum #GimpIntStoreColumns.
+ *
+ * Since: 2.2
+ **/
+void
+gimp_int_combo_box_append (GimpIntComboBox *combo_box,
+ ...)
+{
+ GtkListStore *store;
+ GtkTreeIter iter;
+ va_list args;
+
+ g_return_if_fail (GIMP_IS_INT_COMBO_BOX (combo_box));
+
+ store = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box)));
+
+ va_start (args, combo_box);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set_valist (store, &iter, args);
+
+ va_end (args);
+}
+
+/**
+ * gimp_int_combo_box_set_active:
+ * @combo_box: a #GimpIntComboBox
+ * @value: an integer value
+ *
+ * Looks up the item that belongs to the given @value and makes it the
+ * selected item in the @combo_box.
+ *
+ * Return value: %TRUE on success or %FALSE if there was no item for
+ * this value.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_int_combo_box_set_active (GimpIntComboBox *combo_box,
+ gint value)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ g_return_val_if_fail (GIMP_IS_INT_COMBO_BOX (combo_box), FALSE);
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));
+
+ if (gimp_int_store_lookup_by_value (model, value, &iter))
+ {
+ gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_box), &iter);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * gimp_int_combo_box_get_active:
+ * @combo_box: a #GimpIntComboBox
+ * @value: return location for the integer value
+ *
+ * Retrieves the value of the selected (active) item in the @combo_box.
+ *
+ * Return value: %TRUE if @value has been set or %FALSE if no item was
+ * active.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_int_combo_box_get_active (GimpIntComboBox *combo_box,
+ gint *value)
+{
+ GtkTreeIter iter;
+
+ g_return_val_if_fail (GIMP_IS_INT_COMBO_BOX (combo_box), FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo_box), &iter))
+ {
+ gtk_tree_model_get (gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box)),
+ &iter,
+ GIMP_INT_STORE_VALUE, value,
+ -1);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * gimp_int_combo_box_set_active_by_user_data:
+ * @combo_box: a #GimpIntComboBox
+ * @user_data: an integer value
+ *
+ * Looks up the item that has the given @user_data and makes it the
+ * selected item in the @combo_box.
+ *
+ * Return value: %TRUE on success or %FALSE if there was no item for
+ * this user-data.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_int_combo_box_set_active_by_user_data (GimpIntComboBox *combo_box,
+ gpointer user_data)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ g_return_val_if_fail (GIMP_IS_INT_COMBO_BOX (combo_box), FALSE);
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));
+
+ if (gimp_int_store_lookup_by_user_data (model, user_data, &iter))
+ {
+ gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_box), &iter);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * gimp_int_combo_box_get_active_user_data:
+ * @combo_box: a #GimpIntComboBox
+ * @user_data: return location for the gpointer value
+ *
+ * Retrieves the user-data of the selected (active) item in the @combo_box.
+ *
+ * Return value: %TRUE if @user_data has been set or %FALSE if no item was
+ * active.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_int_combo_box_get_active_user_data (GimpIntComboBox *combo_box,
+ gpointer *user_data)
+{
+ GtkTreeIter iter;
+
+ g_return_val_if_fail (GIMP_IS_INT_COMBO_BOX (combo_box), FALSE);
+ g_return_val_if_fail (user_data != NULL, FALSE);
+
+ if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo_box), &iter))
+ {
+ gtk_tree_model_get (gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box)),
+ &iter,
+ GIMP_INT_STORE_USER_DATA, user_data,
+ -1);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * gimp_int_combo_box_connect:
+ * @combo_box: a #GimpIntComboBox
+ * @value: the value to set
+ * @callback: a callback to connect to the @combo_box's "changed" signal
+ * @data: a pointer passed as data to g_signal_connect()
+ *
+ * A convenience function that sets the initial @value of a
+ * #GimpIntComboBox and connects @callback to the "changed"
+ * signal.
+ *
+ * This function also calls the @callback once after setting the
+ * initial @value. This is often convenient when working with combo
+ * boxes that select a default active item, like for example
+ * gimp_drawable_combo_box_new(). If you pass an invalid initial
+ * @value, the @callback will be called with the default item active.
+ *
+ * Return value: the signal handler ID as returned by g_signal_connect()
+ *
+ * Since: 2.2
+ **/
+gulong
+gimp_int_combo_box_connect (GimpIntComboBox *combo_box,
+ gint value,
+ GCallback callback,
+ gpointer data)
+{
+ gulong handler = 0;
+
+ g_return_val_if_fail (GIMP_IS_INT_COMBO_BOX (combo_box), 0);
+
+ if (callback)
+ handler = g_signal_connect (combo_box, "changed", callback, data);
+
+ if (! gimp_int_combo_box_set_active (combo_box, value))
+ g_signal_emit_by_name (combo_box, "changed", NULL);
+
+ return handler;
+}
+
+/**
+ * gimp_int_combo_box_set_label:
+ * @combo_box: a #GimpIntComboBox
+ * @label: a string to be shown as label
+ *
+ * Sets a caption on the @combo_box that will be displayed
+ * left-aligned inside the box. When a label is set, the remaining
+ * contents of the box will be right-aligned. This is useful for
+ * places where screen estate is rare, like in tool options.
+ *
+ * Since: 2.10
+ **/
+void
+gimp_int_combo_box_set_label (GimpIntComboBox *combo_box,
+ const gchar *label)
+{
+ GimpIntComboBoxPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_INT_COMBO_BOX (combo_box));
+
+ priv = GIMP_INT_COMBO_BOX_GET_PRIVATE (combo_box);
+
+ if (label == priv->label)
+ return;
+
+ g_free (priv->label);
+
+ priv->label = g_strdup (label);
+
+ gimp_int_combo_box_create_cells (combo_box);
+
+ g_object_notify (G_OBJECT (combo_box), "label");
+}
+
+/**
+ * gimp_int_combo_box_get_label:
+ * @combo_box: a #GimpIntComboBox
+ *
+ * Returns the label previously set with gimp_int_combo_box_set_label(),
+ * or %NULL,
+ *
+ * Return value: the @combo_box' label.
+ *
+ * Since: 2.10
+ **/
+const gchar *
+gimp_int_combo_box_get_label (GimpIntComboBox *combo_box)
+{
+ g_return_val_if_fail (GIMP_IS_INT_COMBO_BOX (combo_box), NULL);
+
+ return GIMP_INT_COMBO_BOX_GET_PRIVATE (combo_box)->label;
+}
+
+/**
+ * gimp_int_combo_box_set_layout:
+ * @combo_box: a #GimpIntComboBox
+ * @layout: the combo box layout
+ *
+ * Sets the layout of @combo_box to @layout.
+ *
+ * Since: 2.10
+ **/
+void
+gimp_int_combo_box_set_layout (GimpIntComboBox *combo_box,
+ GimpIntComboBoxLayout layout)
+{
+ GimpIntComboBoxPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_INT_COMBO_BOX (combo_box));
+
+ priv = GIMP_INT_COMBO_BOX_GET_PRIVATE (combo_box);
+
+ if (layout == priv->layout)
+ return;
+
+ priv->layout = layout;
+
+ gimp_int_combo_box_create_cells (combo_box);
+
+ g_object_notify (G_OBJECT (combo_box), "layout");
+}
+
+/**
+ * gimp_int_combo_box_get_layout:
+ * @combo_box: a #GimpIntComboBox
+ *
+ * Returns the layout of @combo_box
+ *
+ * Return value: the @combo_box's layout.
+ *
+ * Since: 2.10
+ **/
+GimpIntComboBoxLayout
+gimp_int_combo_box_get_layout (GimpIntComboBox *combo_box)
+{
+ g_return_val_if_fail (GIMP_IS_INT_COMBO_BOX (combo_box),
+ GIMP_INT_COMBO_BOX_LAYOUT_ABBREVIATED);
+
+ return GIMP_INT_COMBO_BOX_GET_PRIVATE (combo_box)->layout;
+}
+
+/**
+ * gimp_int_combo_box_set_sensitivity:
+ * @combo_box: a #GimpIntComboBox
+ * @func: a function that returns a boolean value, or %NULL to unset
+ * @data: data to pass to @func
+ * @destroy: destroy notification for @data
+ *
+ * Sets a function that is used to decide about the sensitivity of
+ * rows in the @combo_box. Use this if you want to set certain rows
+ * insensitive.
+ *
+ * Calling gtk_widget_queue_draw() on the @combo_box will cause the
+ * sensitivity to be updated.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_int_combo_box_set_sensitivity (GimpIntComboBox *combo_box,
+ GimpIntSensitivityFunc func,
+ gpointer data,
+ GDestroyNotify destroy)
+{
+ GimpIntComboBoxPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_INT_COMBO_BOX (combo_box));
+
+ priv = GIMP_INT_COMBO_BOX_GET_PRIVATE (combo_box);
+
+ if (priv->sensitivity_destroy)
+ {
+ GDestroyNotify d = priv->sensitivity_destroy;
+
+ priv->sensitivity_destroy = NULL;
+ d (priv->sensitivity_data);
+ }
+
+ priv->sensitivity_func = func;
+ priv->sensitivity_data = data;
+ priv->sensitivity_destroy = destroy;
+
+ gimp_int_combo_box_create_cells (combo_box);
+}
+
+
+/* private functions */
+
+static void
+queue_resize_cell_view (GtkContainer *container)
+{
+ GList *children = gtk_container_get_children (container);
+ GList *list;
+
+ for (list = children; list; list = g_list_next (list))
+ {
+ if (GTK_IS_CELL_VIEW (list->data))
+ {
+ gtk_widget_queue_resize (list->data);
+ break;
+ }
+ else if (GTK_IS_CONTAINER (list->data))
+ {
+ queue_resize_cell_view (list->data);
+ }
+ }
+
+ g_list_free (children);
+}
+
+static void
+gimp_int_combo_box_create_cells (GimpIntComboBox *combo_box)
+{
+ GimpIntComboBoxPrivate *priv = GIMP_INT_COMBO_BOX_GET_PRIVATE (combo_box);
+ GtkCellLayout *layout;
+
+ /* menu layout */
+
+ layout = GTK_CELL_LAYOUT (combo_box);
+
+ gtk_cell_layout_clear (layout);
+
+ priv->menu_pixbuf_renderer = gtk_cell_renderer_pixbuf_new ();
+ g_object_set (priv->menu_pixbuf_renderer,
+ "xpad", 2,
+ NULL);
+
+ priv->menu_text_renderer = gtk_cell_renderer_text_new ();
+
+ gtk_cell_layout_pack_start (layout,
+ priv->menu_pixbuf_renderer, FALSE);
+ gtk_cell_layout_pack_start (layout,
+ priv->menu_text_renderer, TRUE);
+
+ gtk_cell_layout_set_attributes (layout,
+ priv->menu_pixbuf_renderer,
+ "icon-name", GIMP_INT_STORE_ICON_NAME,
+ "pixbuf", GIMP_INT_STORE_PIXBUF,
+ NULL);
+ gtk_cell_layout_set_attributes (layout,
+ priv->menu_text_renderer,
+ "text", GIMP_INT_STORE_LABEL,
+ NULL);
+
+ if (priv->sensitivity_func)
+ {
+ gtk_cell_layout_set_cell_data_func (layout,
+ priv->menu_pixbuf_renderer,
+ gimp_int_combo_box_data_func,
+ priv, NULL);
+
+ gtk_cell_layout_set_cell_data_func (layout,
+ priv->menu_text_renderer,
+ gimp_int_combo_box_data_func,
+ priv, NULL);
+ }
+
+ /* combo box layout */
+
+ layout = GTK_CELL_LAYOUT (gtk_bin_get_child (GTK_BIN (combo_box)));
+
+ gtk_cell_layout_clear (layout);
+
+ if (priv->layout != GIMP_INT_COMBO_BOX_LAYOUT_ICON_ONLY)
+ {
+ priv->text_renderer = gtk_cell_renderer_text_new ();
+ g_object_set (priv->text_renderer,
+ "ellipsize", priv->ellipsize,
+ NULL);
+ }
+ else
+ {
+ priv->text_renderer = NULL;
+ }
+
+ priv->pixbuf_renderer = gtk_cell_renderer_pixbuf_new ();
+
+ if (priv->text_renderer)
+ {
+ g_object_set (priv->pixbuf_renderer,
+ "xpad", 2,
+ NULL);
+ }
+
+ if (priv->label)
+ {
+ priv->label_renderer = gtk_cell_renderer_text_new ();
+ g_object_set (priv->label_renderer,
+ "text", priv->label,
+ NULL);
+
+ gtk_cell_layout_pack_start (layout,
+ priv->label_renderer, FALSE);
+
+ gtk_cell_layout_pack_end (layout,
+ priv->pixbuf_renderer, FALSE);
+
+ if (priv->text_renderer)
+ {
+ gtk_cell_layout_pack_end (layout,
+ priv->text_renderer, TRUE);
+
+ g_object_set (priv->text_renderer,
+ "xalign", 1.0,
+ NULL);
+ }
+ }
+ else
+ {
+ gtk_cell_layout_pack_start (layout,
+ priv->pixbuf_renderer, FALSE);
+
+ if (priv->text_renderer)
+ {
+ gtk_cell_layout_pack_start (layout,
+ priv->text_renderer, TRUE);
+ }
+ }
+
+ gtk_cell_layout_set_attributes (layout,
+ priv->pixbuf_renderer,
+ "icon-name", GIMP_INT_STORE_ICON_NAME,
+ NULL);
+
+ if (priv->text_renderer)
+ {
+ gtk_cell_layout_set_attributes (layout,
+ priv->text_renderer,
+ "text", GIMP_INT_STORE_LABEL,
+ NULL);
+ }
+
+ if (priv->layout == GIMP_INT_COMBO_BOX_LAYOUT_ABBREVIATED ||
+ priv->sensitivity_func)
+ {
+ gtk_cell_layout_set_cell_data_func (layout,
+ priv->pixbuf_renderer,
+ gimp_int_combo_box_data_func,
+ priv, NULL);
+
+ if (priv->text_renderer)
+ {
+ gtk_cell_layout_set_cell_data_func (layout,
+ priv->text_renderer,
+ gimp_int_combo_box_data_func,
+ priv, NULL);
+ }
+ }
+
+ /* HACK: GtkCellView doesn't invalidate itself when stuff is
+ * added/removed, work around this bug until GTK+ 2.24.19
+ */
+ if (gtk_check_version (2, 24, 19))
+ {
+ GList *attached_menus;
+
+ queue_resize_cell_view (GTK_CONTAINER (combo_box));
+
+ /* HACK HACK HACK OMG */
+ attached_menus = g_object_get_data (G_OBJECT (combo_box),
+ "gtk-attached-menus");
+
+ for (; attached_menus; attached_menus = g_list_next (attached_menus))
+ queue_resize_cell_view (attached_menus->data);
+ }
+}
+
+static void
+gimp_int_combo_box_data_func (GtkCellLayout *layout,
+ GtkCellRenderer *cell,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ GimpIntComboBoxPrivate *priv = data;
+
+ if (priv->layout == GIMP_INT_COMBO_BOX_LAYOUT_ABBREVIATED &&
+ cell == priv->text_renderer)
+ {
+ gchar *abbrev;
+
+ gtk_tree_model_get (model, iter,
+ GIMP_INT_STORE_ABBREV, &abbrev,
+ -1);
+
+ if (abbrev)
+ {
+ g_object_set (cell,
+ "text", abbrev,
+ NULL);
+
+ g_free (abbrev);
+ }
+ }
+
+ if (priv->sensitivity_func)
+ {
+ gint value;
+ gboolean sensitive;
+
+ gtk_tree_model_get (model, iter,
+ GIMP_INT_STORE_VALUE, &value,
+ -1);
+
+ sensitive = priv->sensitivity_func (value, priv->sensitivity_data);
+
+ g_object_set (cell,
+ "sensitive", sensitive,
+ NULL);
+ }
+}
diff --git a/libgimpwidgets/gimpintcombobox.h b/libgimpwidgets/gimpintcombobox.h
new file mode 100644
index 0000000..ca0e607
--- /dev/null
+++ b/libgimpwidgets/gimpintcombobox.h
@@ -0,0 +1,117 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpintcombobox.h
+ * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_INT_COMBO_BOX_H__
+#define __GIMP_INT_COMBO_BOX_H__
+
+G_BEGIN_DECLS
+
+
+#define GIMP_TYPE_INT_COMBO_BOX (gimp_int_combo_box_get_type ())
+#define GIMP_INT_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_INT_COMBO_BOX, GimpIntComboBox))
+#define GIMP_INT_COMBO_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_INT_COMBO_BOX, GimpIntComboBoxClass))
+#define GIMP_IS_INT_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_INT_COMBO_BOX))
+#define GIMP_IS_INT_COMBO_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_INT_COMBO_BOX))
+#define GIMP_INT_COMBO_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_INT_COMBO_BOX, GimpIntComboBoxClass))
+
+
+typedef struct _GimpIntComboBoxClass GimpIntComboBoxClass;
+
+struct _GimpIntComboBox
+{
+ GtkComboBox parent_instance;
+
+ /*< private >*/
+ gpointer priv;
+
+ /* Padding for future expansion (should have gone to the class) */
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+struct _GimpIntComboBoxClass
+{
+ GtkComboBoxClass parent_class;
+};
+
+
+typedef gboolean (* GimpIntSensitivityFunc) (gint value,
+ gpointer data);
+
+
+
+GType gimp_int_combo_box_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_int_combo_box_new (const gchar *first_label,
+ gint first_value,
+ ...) G_GNUC_NULL_TERMINATED;
+GtkWidget * gimp_int_combo_box_new_valist (const gchar *first_label,
+ gint first_value,
+ va_list values);
+
+GtkWidget * gimp_int_combo_box_new_array (gint n_values,
+ const gchar *labels[]);
+
+void gimp_int_combo_box_prepend (GimpIntComboBox *combo_box,
+ ...);
+void gimp_int_combo_box_append (GimpIntComboBox *combo_box,
+ ...);
+
+gboolean gimp_int_combo_box_set_active (GimpIntComboBox *combo_box,
+ gint value);
+gboolean gimp_int_combo_box_get_active (GimpIntComboBox *combo_box,
+ gint *value);
+
+gboolean
+ gimp_int_combo_box_set_active_by_user_data (GimpIntComboBox *combo_box,
+ gpointer user_data);
+gboolean
+ gimp_int_combo_box_get_active_user_data (GimpIntComboBox *combo_box,
+ gpointer *user_data);
+
+gulong gimp_int_combo_box_connect (GimpIntComboBox *combo_box,
+ gint value,
+ GCallback callback,
+ gpointer data);
+
+void gimp_int_combo_box_set_label (GimpIntComboBox *combo_box,
+ const gchar *label);
+const gchar * gimp_int_combo_box_get_label (GimpIntComboBox *combo_box);
+
+void gimp_int_combo_box_set_layout (GimpIntComboBox *combo_box,
+ GimpIntComboBoxLayout layout);
+GimpIntComboBoxLayout
+ gimp_int_combo_box_get_layout (GimpIntComboBox *combo_box);
+
+void gimp_int_combo_box_set_sensitivity (GimpIntComboBox *combo_box,
+ GimpIntSensitivityFunc func,
+ gpointer data,
+ GDestroyNotify destroy);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_INT_COMBO_BOX_H__ */
diff --git a/libgimpwidgets/gimpintstore.c b/libgimpwidgets/gimpintstore.c
new file mode 100644
index 0000000..54c6cf9
--- /dev/null
+++ b/libgimpwidgets/gimpintstore.c
@@ -0,0 +1,351 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpintstore.c
+ * Copyright (C) 2004-2007 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpintstore.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpintstore
+ * @title: GimpIntStore
+ * @short_description: A model for integer based name-value pairs
+ * (e.g. enums)
+ *
+ * A model for integer based name-value pairs (e.g. enums)
+ **/
+
+
+enum
+{
+ PROP_0,
+ PROP_USER_DATA_TYPE
+};
+
+typedef struct
+{
+ GType user_data_type;
+} GimpIntStorePrivate;
+
+
+static void gimp_int_store_tree_model_init (GtkTreeModelIface *iface);
+
+static void gimp_int_store_constructed (GObject *object);
+static void gimp_int_store_finalize (GObject *object);
+static void gimp_int_store_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_int_store_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_int_store_row_inserted (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter);
+static void gimp_int_store_row_deleted (GtkTreeModel *model,
+ GtkTreePath *path);
+static void gimp_int_store_add_empty (GimpIntStore *store);
+
+
+G_DEFINE_TYPE_WITH_CODE (GimpIntStore, gimp_int_store, GTK_TYPE_LIST_STORE,
+ G_ADD_PRIVATE (GimpIntStore)
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
+ gimp_int_store_tree_model_init))
+
+#define GIMP_INT_STORE_GET_PRIVATE(obj) \
+ ((GimpIntStorePrivate *) gimp_int_store_get_instance_private ((GimpIntStore *) (obj)))
+
+#define parent_class gimp_int_store_parent_class
+
+static GtkTreeModelIface *parent_iface = NULL;
+
+
+static void
+gimp_int_store_class_init (GimpIntStoreClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = gimp_int_store_constructed;
+ object_class->finalize = gimp_int_store_finalize;
+ object_class->set_property = gimp_int_store_set_property;
+ object_class->get_property = gimp_int_store_get_property;
+
+ /**
+ * GimpIntStore:user-data-type:
+ *
+ * Sets the #GType for the GIMP_INT_STORE_USER_DATA column.
+ *
+ * You need to set this property when constructing the store if you want
+ * to use the GIMP_INT_STORE_USER_DATA column and want to have the store
+ * handle ref-counting of your user data.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class,
+ PROP_USER_DATA_TYPE,
+ g_param_spec_gtype ("user-data-type",
+ "User Data Type",
+ "The GType of the user_data column",
+ G_TYPE_NONE,
+ G_PARAM_CONSTRUCT_ONLY |
+ GIMP_PARAM_READWRITE));
+}
+
+static void
+gimp_int_store_tree_model_init (GtkTreeModelIface *iface)
+{
+ parent_iface = g_type_interface_peek_parent (iface);
+
+ iface->row_inserted = gimp_int_store_row_inserted;
+ iface->row_deleted = gimp_int_store_row_deleted;
+}
+
+static void
+gimp_int_store_init (GimpIntStore *store)
+{
+ store->empty_iter = NULL;
+}
+
+static void
+gimp_int_store_constructed (GObject *object)
+{
+ GimpIntStore *store = GIMP_INT_STORE (object);
+ GimpIntStorePrivate *priv = GIMP_INT_STORE_GET_PRIVATE (store);
+ GType types[GIMP_INT_STORE_NUM_COLUMNS];
+
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ types[GIMP_INT_STORE_VALUE] = G_TYPE_INT;
+ types[GIMP_INT_STORE_LABEL] = G_TYPE_STRING;
+ types[GIMP_INT_STORE_ICON_NAME] = G_TYPE_STRING;
+ types[GIMP_INT_STORE_PIXBUF] = GDK_TYPE_PIXBUF;
+ types[GIMP_INT_STORE_USER_DATA] = (priv->user_data_type != G_TYPE_NONE ?
+ priv->user_data_type : G_TYPE_POINTER);
+ types[GIMP_INT_STORE_ABBREV] = G_TYPE_STRING;
+
+ gtk_list_store_set_column_types (GTK_LIST_STORE (store),
+ GIMP_INT_STORE_NUM_COLUMNS, types);
+
+ gimp_int_store_add_empty (store);
+}
+
+static void
+gimp_int_store_finalize (GObject *object)
+{
+ GimpIntStore *store = GIMP_INT_STORE (object);
+
+ if (store->empty_iter)
+ {
+ gtk_tree_iter_free (store->empty_iter);
+ store->empty_iter = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_int_store_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpIntStorePrivate *priv = GIMP_INT_STORE_GET_PRIVATE (object);
+
+ switch (property_id)
+ {
+ case PROP_USER_DATA_TYPE:
+ priv->user_data_type = g_value_get_gtype (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_int_store_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpIntStorePrivate *priv = GIMP_INT_STORE_GET_PRIVATE (object);
+
+ switch (property_id)
+ {
+ case PROP_USER_DATA_TYPE:
+ g_value_set_gtype (value, priv->user_data_type);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_int_store_row_inserted (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter)
+{
+ GimpIntStore *store = GIMP_INT_STORE (model);
+
+ if (parent_iface->row_inserted)
+ parent_iface->row_inserted (model, path, iter);
+
+ if (store->empty_iter &&
+ memcmp (iter, store->empty_iter, sizeof (GtkTreeIter)))
+ {
+ gtk_list_store_remove (GTK_LIST_STORE (store), store->empty_iter);
+ gtk_tree_iter_free (store->empty_iter);
+ store->empty_iter = NULL;
+ }
+}
+
+static void
+gimp_int_store_row_deleted (GtkTreeModel *model,
+ GtkTreePath *path)
+{
+ if (parent_iface->row_deleted)
+ parent_iface->row_deleted (model, path);
+}
+
+static void
+gimp_int_store_add_empty (GimpIntStore *store)
+{
+ GtkTreeIter iter = { 0, };
+
+ g_return_if_fail (store->empty_iter == NULL);
+
+ gtk_list_store_prepend (GTK_LIST_STORE (store), &iter);
+ gtk_list_store_set (GTK_LIST_STORE (store), &iter,
+ GIMP_INT_STORE_VALUE, -1,
+ /* This string appears in an empty menu as in
+ * "nothing selected and nothing to select"
+ */
+ GIMP_INT_STORE_LABEL, (_("(Empty)")),
+ -1);
+
+ store->empty_iter = gtk_tree_iter_copy (&iter);
+}
+
+/**
+ * gimp_int_store_new:
+ *
+ * Creates a #GtkListStore with a number of useful columns.
+ * #GimpIntStore is especially useful if the items you want to store
+ * are identified using an integer value.
+ *
+ * Return value: a new #GimpIntStore.
+ *
+ * Since: 2.2
+ **/
+GtkListStore *
+gimp_int_store_new (void)
+{
+ return g_object_new (GIMP_TYPE_INT_STORE, NULL);
+}
+
+/**
+ * gimp_int_store_lookup_by_value:
+ * @model: a #GimpIntStore
+ * @value: an integer value to lookup in the @model
+ * @iter: return location for the iter of the given @value
+ *
+ * Iterate over the @model looking for @value.
+ *
+ * Return value: %TRUE if the value has been located and @iter is
+ * valid, %FALSE otherwise.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_int_store_lookup_by_value (GtkTreeModel *model,
+ gint value,
+ GtkTreeIter *iter)
+{
+ gboolean iter_valid;
+
+ g_return_val_if_fail (GTK_IS_TREE_MODEL (model), FALSE);
+ g_return_val_if_fail (iter != NULL, FALSE);
+
+ for (iter_valid = gtk_tree_model_get_iter_first (model, iter);
+ iter_valid;
+ iter_valid = gtk_tree_model_iter_next (model, iter))
+ {
+ gint this;
+
+ gtk_tree_model_get (model, iter,
+ GIMP_INT_STORE_VALUE, &this,
+ -1);
+ if (this == value)
+ break;
+ }
+
+ return iter_valid;
+}
+
+/**
+ * gimp_int_store_lookup_by_user_data:
+ * @model: a #GimpIntStore
+ * @user_data: a gpointer "user-data" to lookup in the @model
+ * @iter: return location for the iter of the given @user_data
+ *
+ * Iterate over the @model looking for @user_data.
+ *
+ * Return value: %TRUE if the user-data has been located and @iter is
+ * valid, %FALSE otherwise.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gimp_int_store_lookup_by_user_data (GtkTreeModel *model,
+ gpointer user_data,
+ GtkTreeIter *iter)
+{
+ gboolean iter_valid = FALSE;
+
+ g_return_val_if_fail (GTK_IS_TREE_MODEL (model), FALSE);
+ g_return_val_if_fail (iter != NULL, FALSE);
+
+ for (iter_valid = gtk_tree_model_get_iter_first (model, iter);
+ iter_valid;
+ iter_valid = gtk_tree_model_iter_next (model, iter))
+ {
+ gpointer this;
+
+ gtk_tree_model_get (model, iter,
+ GIMP_INT_STORE_USER_DATA, &this,
+ -1);
+ if (this == user_data)
+ break;
+ }
+
+ return (gboolean) iter_valid;
+}
diff --git a/libgimpwidgets/gimpintstore.h b/libgimpwidgets/gimpintstore.h
new file mode 100644
index 0000000..c3d62ef
--- /dev/null
+++ b/libgimpwidgets/gimpintstore.h
@@ -0,0 +1,104 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpintstore.c
+ * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_INT_STORE_H__
+#define __GIMP_INT_STORE_H__
+
+G_BEGIN_DECLS
+
+
+/**
+ * GimpIntStoreColumns:
+ * @GIMP_INT_STORE_VALUE: the integer value
+ * @GIMP_INT_STORE_LABEL: a human-readable label
+ * @GIMP_INT_STORE_ICON_NAME: an icon name
+ * @GIMP_INT_STORE_PIXBUF: a #GdkPixbuf
+ * @GIMP_INT_STORE_USER_DATA: arbitrary user data
+ * @GIMP_INT_STORE_ABBREV: an abbreviated label
+ * @GIMP_INT_STORE_NUM_COLUMNS: the number of columns
+ * @GIMP_INT_STORE_STOCK_ID: compat alias for @GIMP_INT_STORE_ICON_NAME
+ *
+ * The column types of #GimpIntStore.
+ **/
+typedef enum
+{
+ GIMP_INT_STORE_VALUE,
+ GIMP_INT_STORE_LABEL,
+ GIMP_INT_STORE_ICON_NAME,
+ GIMP_INT_STORE_PIXBUF,
+ GIMP_INT_STORE_USER_DATA,
+ GIMP_INT_STORE_ABBREV,
+ GIMP_INT_STORE_NUM_COLUMNS,
+
+ /* deprecated */
+ GIMP_INT_STORE_STOCK_ID = GIMP_INT_STORE_ICON_NAME
+} GimpIntStoreColumns;
+
+
+#define GIMP_TYPE_INT_STORE (gimp_int_store_get_type ())
+#define GIMP_INT_STORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_INT_STORE, GimpIntStore))
+#define GIMP_INT_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_INT_STORE, GimpIntStoreClass))
+#define GIMP_IS_INT_STORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_INT_STORE))
+#define GIMP_IS_INT_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_INT_STORE))
+#define GIMP_INT_STORE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_INT_STORE, GimpIntStoreClass))
+
+
+typedef struct _GimpIntStoreClass GimpIntStoreClass;
+
+struct _GimpIntStore
+{
+ GtkListStore parent_instance;
+
+ /*< private >*/
+ GtkTreeIter *empty_iter;
+};
+
+struct _GimpIntStoreClass
+{
+ GtkListStoreClass parent_class;
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_int_store_get_type (void) G_GNUC_CONST;
+
+GtkListStore * gimp_int_store_new (void);
+
+gboolean gimp_int_store_lookup_by_value (GtkTreeModel *model,
+ gint value,
+ GtkTreeIter *iter);
+gboolean gimp_int_store_lookup_by_user_data (GtkTreeModel *model,
+ gpointer user_data,
+ GtkTreeIter *iter);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_INT_STORE_H__ */
diff --git a/libgimpwidgets/gimpmemsizeentry.c b/libgimpwidgets/gimpmemsizeentry.c
new file mode 100644
index 0000000..1d925bb
--- /dev/null
+++ b/libgimpwidgets/gimpmemsizeentry.c
@@ -0,0 +1,320 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpmemsizeentry.c
+ * Copyright (C) 2000-2003 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpmemsizeentry.h"
+#include "gimpspinbutton.h"
+#include "gimpwidgets.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpmemsizeentry
+ * @title: GimpMemSizeEntry
+ * @short_description: A composite widget to enter a memory size.
+ *
+ * Similar to a #GimpSizeEntry but instead of lengths, this widget is
+ * used to let the user enter memory sizes. A combo box allows one to
+ * switch between Kilobytes, Megabytes and Gigabytes. Used in the GIMP
+ * preferences dialog.
+ **/
+
+
+enum
+{
+ VALUE_CHANGED,
+ LAST_SIGNAL
+};
+
+
+static void gimp_memsize_entry_finalize (GObject *object);
+
+static void gimp_memsize_entry_adj_callback (GtkAdjustment *adj,
+ GimpMemsizeEntry *entry);
+static void gimp_memsize_entry_unit_callback (GtkWidget *widget,
+ GimpMemsizeEntry *entry);
+
+static guint64 gimp_memsize_entry_get_rounded_value (GimpMemsizeEntry *entry,
+ guint64 value);
+
+G_DEFINE_TYPE (GimpMemsizeEntry, gimp_memsize_entry, GTK_TYPE_BOX)
+
+#define parent_class gimp_memsize_entry_parent_class
+
+static guint gimp_memsize_entry_signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+gimp_memsize_entry_class_init (GimpMemsizeEntryClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gimp_memsize_entry_finalize;
+
+ klass->value_changed = NULL;
+
+ gimp_memsize_entry_signals[VALUE_CHANGED] =
+ g_signal_new ("value-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpMemsizeEntryClass, value_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static void
+gimp_memsize_entry_init (GimpMemsizeEntry *entry)
+{
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (entry),
+ GTK_ORIENTATION_HORIZONTAL);
+
+ gtk_box_set_spacing (GTK_BOX (entry), 4);
+
+ entry->value = 0;
+ entry->lower = 0;
+ entry->upper = 0;
+ entry->shift = 0;
+ entry->adjustment = NULL;
+ entry->menu = NULL;
+}
+
+static void
+gimp_memsize_entry_finalize (GObject *object)
+{
+ GimpMemsizeEntry *entry = (GimpMemsizeEntry *) object;
+
+ g_clear_object (&entry->adjustment);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_memsize_entry_adj_callback (GtkAdjustment *adj,
+ GimpMemsizeEntry *entry)
+{
+ guint64 size = gtk_adjustment_get_value (adj);
+
+ if (gimp_memsize_entry_get_rounded_value (entry, entry->value) != size)
+ /* Do not allow losing accuracy if the converted/displayed value
+ * stays the same.
+ */
+ entry->value = size << entry->shift;
+
+ g_signal_emit (entry, gimp_memsize_entry_signals[VALUE_CHANGED], 0);
+}
+
+static void
+gimp_memsize_entry_unit_callback (GtkWidget *widget,
+ GimpMemsizeEntry *entry)
+{
+ guint shift;
+
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), (gint *) &shift);
+
+#if _MSC_VER < 1300
+# define CAST (gint64)
+#else
+# define CAST
+#endif
+
+ if (shift != entry->shift)
+ {
+ entry->shift = shift;
+
+ gtk_adjustment_configure (entry->adjustment,
+ gimp_memsize_entry_get_rounded_value (entry, entry->value),
+ CAST entry->lower >> shift,
+ CAST entry->upper >> shift,
+ gtk_adjustment_get_step_increment (entry->adjustment),
+ gtk_adjustment_get_page_increment (entry->adjustment),
+ gtk_adjustment_get_page_size (entry->adjustment));
+ }
+
+#undef CAST
+}
+
+/**
+ * gimp_memsize_entry_get_rounded_value:
+ * @entry: #GimpMemsizeEntry whose set unit is used.
+ * @value: value to convert to @entry unit, and rounded.
+ *
+ * Returns: the proper integer value to be displayed for current unit.
+ * This value has been appropriately rounded to the nearest
+ * integer, away from zero.
+ */
+static guint64
+gimp_memsize_entry_get_rounded_value (GimpMemsizeEntry *entry,
+ guint64 value)
+{
+ guint64 converted;
+
+#if _MSC_VER < 1300
+# define CAST (gint64)
+#else
+# define CAST
+#endif
+
+ converted = (CAST value >> entry->shift) +
+ ((CAST entry->value >> (entry->shift - 1)) & 1);
+
+#undef CAST
+
+ return converted;
+}
+
+
+/**
+ * gimp_memsize_entry_new:
+ * @value: the initial value (in Bytes)
+ * @lower: the lower limit for the value (in Bytes)
+ * @upper: the upper limit for the value (in Bytes)
+ *
+ * Creates a new #GimpMemsizeEntry which is a #GtkHBox with a #GtkSpinButton
+ * and a #GtkOptionMenu all setup to allow the user to enter memory sizes.
+ *
+ * Returns: Pointer to the new #GimpMemsizeEntry.
+ **/
+GtkWidget *
+gimp_memsize_entry_new (guint64 value,
+ guint64 lower,
+ guint64 upper)
+{
+ GimpMemsizeEntry *entry;
+ GtkAdjustment *adj;
+ guint shift;
+
+#if _MSC_VER < 1300
+# define CAST (gint64)
+#else
+# define CAST
+#endif
+
+ g_return_val_if_fail (value >= lower && value <= upper, NULL);
+
+ entry = g_object_new (GIMP_TYPE_MEMSIZE_ENTRY, NULL);
+
+ for (shift = 30; shift > 10; shift -= 10)
+ {
+ if (value > (G_GUINT64_CONSTANT (1) << shift) &&
+ value % (G_GUINT64_CONSTANT (1) << shift) == 0)
+ break;
+ }
+
+ entry->value = value;
+ entry->lower = lower;
+ entry->upper = upper;
+ entry->shift = shift;
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (gimp_memsize_entry_get_rounded_value (entry,
+ entry->value),
+ CAST (lower >> shift),
+ CAST (upper >> shift),
+ 1, 8, 0);
+
+ entry->spinbutton = gimp_spin_button_new (adj, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (entry->spinbutton), TRUE);
+
+#undef CAST
+
+ entry->adjustment = GTK_ADJUSTMENT (adj);
+ g_object_ref_sink (entry->adjustment);
+
+ gtk_entry_set_width_chars (GTK_ENTRY (entry->spinbutton), 7);
+ gtk_box_pack_start (GTK_BOX (entry), entry->spinbutton, FALSE, FALSE, 0);
+ gtk_widget_show (entry->spinbutton);
+
+ g_signal_connect (entry->adjustment, "value-changed",
+ G_CALLBACK (gimp_memsize_entry_adj_callback),
+ entry);
+
+ entry->menu = gimp_int_combo_box_new (_("Kibibyte"), 10,
+ _("Mebibyte"), 20,
+ _("Gibibyte"), 30,
+ NULL);
+
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (entry->menu), shift);
+
+ g_signal_connect (entry->menu, "changed",
+ G_CALLBACK (gimp_memsize_entry_unit_callback),
+ entry);
+
+ gtk_box_pack_start (GTK_BOX (entry), entry->menu, FALSE, FALSE, 0);
+ gtk_widget_show (entry->menu);
+
+ return GTK_WIDGET (entry);
+}
+
+/**
+ * gimp_memsize_entry_set_value:
+ * @entry: a #GimpMemsizeEntry
+ * @value: the new value (in Bytes)
+ *
+ * Sets the @entry's value. Please note that the #GimpMemsizeEntry rounds
+ * the value to full Kilobytes.
+ **/
+void
+gimp_memsize_entry_set_value (GimpMemsizeEntry *entry,
+ guint64 value)
+{
+ guint shift;
+
+ g_return_if_fail (GIMP_IS_MEMSIZE_ENTRY (entry));
+ g_return_if_fail (value >= entry->lower && value <= entry->upper);
+
+ for (shift = 30; shift > 10; shift -= 10)
+ {
+ if (value > (G_GUINT64_CONSTANT (1) << shift) &&
+ value % (G_GUINT64_CONSTANT (1) << shift) == 0)
+ break;
+ }
+
+ if (shift != entry->shift)
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (entry->menu), shift);
+
+ gtk_adjustment_set_value (entry->adjustment,
+ (gdouble) gimp_memsize_entry_get_rounded_value (entry, value));
+
+#undef CASE
+}
+
+/**
+ * gimp_memsize_entry_get_value:
+ * @entry: a #GimpMemsizeEntry
+ *
+ * Retrieves the current value from a #GimpMemsizeEntry.
+ *
+ * Returns: the current value of @entry (in Bytes).
+ **/
+guint64
+gimp_memsize_entry_get_value (GimpMemsizeEntry *entry)
+{
+ g_return_val_if_fail (GIMP_IS_MEMSIZE_ENTRY (entry), 0);
+
+ return entry->value;
+}
diff --git a/libgimpwidgets/gimpmemsizeentry.h b/libgimpwidgets/gimpmemsizeentry.h
new file mode 100644
index 0000000..61af7bf
--- /dev/null
+++ b/libgimpwidgets/gimpmemsizeentry.h
@@ -0,0 +1,84 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpmemsizeentry.h
+ * Copyright (C) 2000-2003 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_MEMSIZE_ENTRY_H__
+#define __GIMP_MEMSIZE_ENTRY_H__
+
+G_BEGIN_DECLS
+
+
+#define GIMP_TYPE_MEMSIZE_ENTRY (gimp_memsize_entry_get_type ())
+#define GIMP_MEMSIZE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_MEMSIZE_ENTRY, GimpMemsizeEntry))
+#define GIMP_MEMSIZE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_MEMSIZE_ENTRY, GimpMemsizeEntryClass))
+#define GIMP_IS_MEMSIZE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_MEMSIZE_ENTRY))
+#define GIMP_IS_MEMSIZE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_MEMSIZE_ENTRY))
+#define GIMP_MEMSIZE_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_MEMSIZE_ENTRY, GimpMemsizeEntryClass))
+
+
+typedef struct _GimpMemsizeEntryClass GimpMemsizeEntryClass;
+
+struct _GimpMemsizeEntry
+{
+ GtkBox parent_instance;
+
+ /*< private >*/
+ guint64 value;
+ guint64 lower;
+ guint64 upper;
+
+ guint shift;
+
+ GtkAdjustment *adjustment;
+ GtkWidget *spinbutton;
+ GtkWidget *menu;
+};
+
+struct _GimpMemsizeEntryClass
+{
+ GtkBoxClass parent_class;
+
+ void (* value_changed) (GimpMemsizeEntry *entry);
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_memsize_entry_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_memsize_entry_new (guint64 value,
+ guint64 lower,
+ guint64 upper);
+void gimp_memsize_entry_set_value (GimpMemsizeEntry *entry,
+ guint64 value);
+guint64 gimp_memsize_entry_get_value (GimpMemsizeEntry *entry);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_MEMSIZE_ENTRY_H__ */
diff --git a/libgimpwidgets/gimpnumberpairentry.c b/libgimpwidgets/gimpnumberpairentry.c
new file mode 100644
index 0000000..c41fda7
--- /dev/null
+++ b/libgimpwidgets/gimpnumberpairentry.c
@@ -0,0 +1,1301 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpnumberpairentry.c
+ * Copyright (C) 2006 Simon Budig <simon@gimp.org>
+ * Copyright (C) 2007 Sven Neumann <sven@gimp.org>
+ * Copyright (C) 2007 Martin Nordholts <martin@svn.gnome.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "libgimpmath/gimpmath.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpicons.h"
+#include "gimpnumberpairentry.h"
+
+
+/**
+ * SECTION: gimpnumberpairentry
+ * @title: GimpNumberPairEntry
+ * @short_description: A #GtkEntry subclass to enter ratios.
+ *
+ * A #GtkEntry subclass to enter ratios.
+ **/
+
+
+#define EPSILON 0.000001
+
+
+enum
+{
+ NUMBERS_CHANGED,
+ RATIO_CHANGED,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_LEFT_NUMBER,
+ PROP_RIGHT_NUMBER,
+ PROP_DEFAULT_LEFT_NUMBER,
+ PROP_DEFAULT_RIGHT_NUMBER,
+ PROP_USER_OVERRIDE,
+ PROP_SEPARATORS,
+ PROP_DEFAULT_TEXT,
+ PROP_ALLOW_SIMPLIFICATION,
+ PROP_MIN_VALID_VALUE,
+ PROP_MAX_VALID_VALUE,
+ PROP_RATIO,
+ PROP_ASPECT
+};
+
+typedef enum
+{
+ PARSE_VALID,
+ PARSE_CLEAR,
+ PARSE_INVALID
+} ParseResult;
+
+typedef struct
+{
+ /* The current number pair displayed in the widget. */
+ gdouble left_number;
+ gdouble right_number;
+
+ /* What number pair that should be displayed when not in
+ user_override mode. */
+ gdouble default_left_number;
+ gdouble default_right_number;
+
+ /* Whether or not the current value in the entry has been explicitly
+ * set by the user.
+ */
+ gboolean user_override;
+
+ /* Is the font style currently set to ITALIC or NORMAL ? */
+ gboolean font_italic;
+
+ /* What separators that are valid when parsing input, e.g. when the
+ * widget is used for aspect ratio, valid separators are typically
+ * ':' and '/'.
+ */
+ gunichar *separators;
+ glong num_separators;
+
+ /* A string to be shown in the entry when in automatic mode */
+ gchar *default_text;
+
+ /* Whether or to not to divide the numbers with the greatest common
+ * divisor when input ends in '='.
+ */
+ gboolean allow_simplification;
+
+ /* What range of values considered valid. */
+ gdouble min_valid_value;
+ gdouble max_valid_value;
+} GimpNumberPairEntryPrivate;
+
+#define GIMP_NUMBER_PAIR_ENTRY_GET_PRIVATE(obj) \
+ ((GimpNumberPairEntryPrivate *) ((GimpNumberPairEntry *) (obj))->priv)
+
+
+static void gimp_number_pair_entry_finalize (GObject *entry);
+
+static gboolean gimp_number_pair_entry_valid_separator (GimpNumberPairEntry *entry,
+ gunichar candidate);
+static void gimp_number_pair_entry_ratio_to_fraction (gdouble ratio,
+ gdouble *numerator,
+ gdouble *denominator);
+
+static void gimp_number_pair_entry_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_number_pair_entry_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_number_pair_entry_changed (GimpNumberPairEntry *entry);
+static void gimp_number_pair_entry_icon_press (GimpNumberPairEntry *entry);
+static gboolean gimp_number_pair_entry_events (GtkWidget *widget,
+ GdkEvent *event);
+
+static void gimp_number_pair_entry_update_text (GimpNumberPairEntry *entry);
+
+static ParseResult gimp_number_pair_entry_parse_text (GimpNumberPairEntry *entry,
+ const gchar *text,
+ gdouble *left_value,
+ gdouble *right_value);
+static gboolean gimp_number_pair_entry_numbers_in_range (GimpNumberPairEntry *entry,
+ gdouble left_number,
+ gdouble right_number);
+
+static gchar * gimp_number_pair_entry_strdup_number_pair_string
+ (GimpNumberPairEntry *entry,
+ gdouble left_number,
+ gdouble right_number);
+
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpNumberPairEntry, gimp_number_pair_entry,
+ GTK_TYPE_ENTRY)
+
+
+#define parent_class gimp_number_pair_entry_parent_class
+
+/* What the user shall end the input with when simplification is desired. */
+#define SIMPLIFICATION_CHAR ((gunichar) '=')
+
+#define DEFAULT_SEPARATOR ((gunichar) ',')
+
+
+static guint entry_signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+gimp_number_pair_entry_class_init (GimpNumberPairEntryClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ entry_signals[NUMBERS_CHANGED] =
+ g_signal_new ("numbers-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpNumberPairEntryClass, numbers_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ entry_signals[RATIO_CHANGED] =
+ g_signal_new ("ratio-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpNumberPairEntryClass, ratio_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ object_class->set_property = gimp_number_pair_entry_set_property;
+ object_class->get_property = gimp_number_pair_entry_get_property;
+ object_class->finalize = gimp_number_pair_entry_finalize;
+
+ klass->numbers_changed = NULL;
+ klass->ratio_changed = NULL;
+
+ g_object_class_install_property (object_class, PROP_LEFT_NUMBER,
+ g_param_spec_double ("left-number",
+ "Left number",
+ "The left number",
+ G_MINDOUBLE, G_MAXDOUBLE,
+ 100.0,
+ GIMP_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_RIGHT_NUMBER,
+ g_param_spec_double ("right-number",
+ "Right number",
+ "The right number",
+ G_MINDOUBLE, G_MAXDOUBLE,
+ 100.0,
+ GIMP_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_DEFAULT_LEFT_NUMBER,
+ g_param_spec_double ("default-left-number",
+ "Default left number",
+ "The default left number",
+ G_MINDOUBLE, G_MAXDOUBLE,
+ 100.0,
+ GIMP_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_DEFAULT_RIGHT_NUMBER,
+ g_param_spec_double ("default-right-number",
+ "Default right number",
+ "The default right number",
+ G_MINDOUBLE, G_MAXDOUBLE,
+ 100.0,
+ GIMP_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_USER_OVERRIDE,
+ g_param_spec_boolean ("user-override",
+ "User override",
+ "Whether the widget is in 'user override' mode",
+ FALSE,
+ GIMP_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_SEPARATORS,
+ g_param_spec_string ("separators",
+ "Separators",
+ "A string of valid separators",
+ NULL,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (object_class, PROP_DEFAULT_TEXT,
+ g_param_spec_string ("default-text",
+ "Default text",
+ "String to show when in automatic mode",
+ NULL,
+ GIMP_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_ALLOW_SIMPLIFICATION,
+ g_param_spec_boolean ("allow-simplification",
+ "Allow simplification",
+ "Whether to allow simplification",
+ FALSE,
+ GIMP_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_MIN_VALID_VALUE,
+ g_param_spec_double ("min-valid-value",
+ "Min valid value",
+ "Minimum value valid when parsing input",
+ G_MINDOUBLE, G_MAXDOUBLE,
+ G_MINDOUBLE,
+ GIMP_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_MAX_VALID_VALUE,
+ g_param_spec_double ("max-valid-value",
+ "Max valid value",
+ "Maximum value valid when parsing input",
+ G_MINDOUBLE, G_MAXDOUBLE,
+ G_MAXDOUBLE,
+ GIMP_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_RATIO,
+ g_param_spec_double ("ratio",
+ "Ratio",
+ "The value as ratio",
+ G_MINDOUBLE, G_MAXDOUBLE,
+ 1.0,
+ GIMP_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_ASPECT,
+ g_param_spec_enum ("aspect",
+ "Aspect",
+ "The value as aspect",
+ GIMP_TYPE_ASPECT_TYPE,
+ GIMP_ASPECT_SQUARE,
+ GIMP_PARAM_READWRITE));
+}
+
+static void
+gimp_number_pair_entry_init (GimpNumberPairEntry *entry)
+{
+ GimpNumberPairEntryPrivate *priv;
+
+ entry->priv = gimp_number_pair_entry_get_instance_private (entry);
+
+ priv = GIMP_NUMBER_PAIR_ENTRY_GET_PRIVATE (entry);
+
+ priv->left_number = 1.0;
+ priv->right_number = 1.0;
+ priv->default_left_number = 1.0;
+ priv->default_right_number = 1.0;
+ priv->user_override = FALSE;
+ priv->font_italic = FALSE;
+ priv->separators = NULL;
+ priv->default_text = NULL;
+ priv->num_separators = 0;
+ priv->allow_simplification = FALSE;
+ priv->min_valid_value = G_MINDOUBLE;
+ priv->max_valid_value = G_MAXDOUBLE;
+
+ g_signal_connect (entry, "changed",
+ G_CALLBACK (gimp_number_pair_entry_changed),
+ NULL);
+ g_signal_connect (entry, "icon-press",
+ G_CALLBACK (gimp_number_pair_entry_icon_press),
+ NULL);
+ g_signal_connect (entry, "focus-out-event",
+ G_CALLBACK (gimp_number_pair_entry_events),
+ NULL);
+ g_signal_connect (entry, "key-press-event",
+ G_CALLBACK (gimp_number_pair_entry_events),
+ NULL);
+
+ gtk_widget_set_direction (GTK_WIDGET (entry), GTK_TEXT_DIR_LTR);
+
+ gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry),
+ GTK_ENTRY_ICON_SECONDARY,
+ GIMP_ICON_EDIT_CLEAR);
+}
+
+static void
+gimp_number_pair_entry_finalize (GObject *object)
+{
+ GimpNumberPairEntryPrivate *priv;
+
+ priv = GIMP_NUMBER_PAIR_ENTRY_GET_PRIVATE (object);
+
+ g_clear_pointer (&priv->separators, g_free);
+ priv->num_separators = 0;
+
+ g_clear_pointer (&priv->default_text, g_free);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+/**
+ * gimp_number_pair_entry_new:
+ * @separators: The allowed separators.
+ * @allow_simplification: Whether to do simplification on the entered term.
+ * @min_valid_value: The minimum allowed result value.
+ * @max_valid_value: The maximum allowed result value.
+ *
+ * Creates a new #GimpNumberPairEntry widget, which is a GtkEntry that
+ * accepts two numbers separated by a separator. Typical input example
+ * with a 'x' separator: "377x233".
+ *
+ * The widget supports simplification of the entered ratio when the
+ * input ends in '=', if "allow-simplification" is TRUE.
+ *
+ * The "separators" property contains a string of characters valid as
+ * separators when parsing input. The first separator is used when
+ * displaying the current values.
+ *
+ * It is possible to specify what range of values that shall be
+ * considered as valid when parsing user input, by changing
+ * "min-valid-value" and "max-valid-value".
+ *
+ * The first separator of @separators is used to display the current
+ * value.
+ *
+ * Return value: The new #GimpNumberPairEntry widget.
+ *
+ * Since: 2.4
+ **/
+GtkWidget *
+gimp_number_pair_entry_new (const gchar *separators,
+ gboolean allow_simplification,
+ gdouble min_valid_value,
+ gdouble max_valid_value)
+{
+ return g_object_new (GIMP_TYPE_NUMBER_PAIR_ENTRY,
+ "separators", separators,
+ "allow-simplification", allow_simplification,
+ "min-valid-value", min_valid_value,
+ "max-valid-value", max_valid_value,
+ NULL);
+}
+
+static void
+gimp_number_pair_entry_ratio_to_fraction (gdouble ratio,
+ gdouble *numerator,
+ gdouble *denominator)
+{
+ gdouble remainder, next_cf;
+ gint p0, p1, p2;
+ gint q0, q1, q2;
+
+ /* calculate the continued fraction to approximate the desired ratio */
+
+ p0 = 1;
+ q0 = 0;
+ p1 = floor (ratio);
+ q1 = 1;
+
+ remainder = ratio - p1;
+
+ while (fabs (remainder) >= 0.0001 &&
+ fabs (((gdouble) p1 / q1) - ratio) > 0.0001)
+ {
+ remainder = 1.0 / remainder;
+
+ next_cf = floor (remainder);
+
+ p2 = next_cf * p1 + p0;
+ q2 = next_cf * q1 + q0;
+
+ /* remember the last two fractions */
+ p0 = p1;
+ q0 = q1;
+ p1 = p2;
+ q1 = q2;
+
+ remainder = remainder - next_cf;
+ }
+
+ /* only use the calculated fraction if it is "reasonable" */
+ if (p1 < 1000 && q1 < 1000)
+ {
+ *numerator = p1;
+ *denominator = q1;
+ }
+ else
+ {
+ *numerator = ratio;
+ *denominator = 1.0;
+ }
+}
+
+/**
+ * gimp_number_pair_entry_set_ratio:
+ * @entry: A #GimpNumberPairEntry widget.
+ * @ratio: Ratio to set in the widget.
+ *
+ * Sets the numbers of the #GimpNumberPairEntry to have the desired
+ * ratio. If the new ratio is different than the previous ratio, the
+ * "ratio-changed" signal is emitted.
+ *
+ * An attempt is made to convert the decimal number into a fraction
+ * with left_number and right_number < 1000.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_number_pair_entry_set_ratio (GimpNumberPairEntry *entry,
+ gdouble ratio)
+{
+ gdouble numerator;
+ gdouble denominator;
+
+ g_return_if_fail (GIMP_IS_NUMBER_PAIR_ENTRY (entry));
+
+ gimp_number_pair_entry_ratio_to_fraction (ratio, &numerator, &denominator);
+
+ gimp_number_pair_entry_set_values (entry, numerator, denominator);
+}
+
+/**
+ * gimp_number_pair_entry_get_ratio:
+ * @entry: A #GimpNumberPairEntry widget.
+ *
+ * Retrieves the ratio of the numbers displayed by a #GimpNumberPairEntry.
+ *
+ * Returns: The ratio value.
+ *
+ * Since: 2.4
+ **/
+gdouble
+gimp_number_pair_entry_get_ratio (GimpNumberPairEntry *entry)
+{
+ GimpNumberPairEntryPrivate *priv;
+
+ g_return_val_if_fail (GIMP_IS_NUMBER_PAIR_ENTRY (entry), 1.0);
+
+ priv = GIMP_NUMBER_PAIR_ENTRY_GET_PRIVATE (entry);
+
+ return priv->left_number / priv->right_number;
+}
+
+/**
+ * gimp_number_pair_entry_set_values:
+ * @entry: A #GimpNumberPairEntry widget.
+ * @left: Left number in the entry.
+ * @right: Right number in the entry.
+ *
+ * Forces setting the numbers displayed by a #GimpNumberPairEntry,
+ * ignoring if the user has set his/her own value. The state of
+ * user-override will not be changed.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_number_pair_entry_set_values (GimpNumberPairEntry *entry,
+ gdouble left,
+ gdouble right)
+{
+ GimpNumberPairEntryPrivate *priv;
+ GimpAspectType old_aspect;
+ gdouble old_ratio;
+ gdouble old_left_number;
+ gdouble old_right_number;
+ gboolean numbers_changed = FALSE;
+ gboolean ratio_changed = FALSE;
+
+ g_return_if_fail (GIMP_IS_NUMBER_PAIR_ENTRY (entry));
+
+ priv = GIMP_NUMBER_PAIR_ENTRY_GET_PRIVATE (entry);
+
+ /* Store current values */
+
+ old_left_number = priv->left_number;
+ old_right_number = priv->right_number;
+ old_ratio = gimp_number_pair_entry_get_ratio (entry);
+ old_aspect = gimp_number_pair_entry_get_aspect (entry);
+
+
+ /* Freeze notification */
+
+ g_object_freeze_notify (G_OBJECT (entry));
+
+
+ /* Set the new numbers and update the entry */
+
+ priv->left_number = left;
+ priv->right_number = right;
+
+ g_object_notify (G_OBJECT (entry), "left-number");
+ g_object_notify (G_OBJECT (entry), "right-number");
+
+ gimp_number_pair_entry_update_text (entry);
+
+
+ /* Find out what has changed */
+
+ if (fabs (old_ratio - gimp_number_pair_entry_get_ratio (entry)) > EPSILON)
+ {
+ g_object_notify (G_OBJECT (entry), "ratio");
+
+ ratio_changed = TRUE;
+
+ if (old_aspect != gimp_number_pair_entry_get_aspect (entry))
+ g_object_notify (G_OBJECT (entry), "aspect");
+ }
+
+ if (old_left_number != priv->left_number ||
+ old_right_number != priv->right_number)
+ {
+ numbers_changed = TRUE;
+ }
+
+
+ /* Thaw */
+
+ g_object_thaw_notify (G_OBJECT (entry));
+
+
+ /* Emit relevant signals */
+
+ if (numbers_changed)
+ g_signal_emit (entry, entry_signals[NUMBERS_CHANGED], 0);
+
+ if (ratio_changed)
+ g_signal_emit (entry, entry_signals[RATIO_CHANGED], 0);
+}
+
+/**
+ * gimp_number_pair_entry_get_values:
+ * @entry: A #GimpNumberPairEntry widget.
+ * @left: Pointer of where to store the left number (may be %NULL).
+ * @right: Pointer of to store the right number (may be %NULL).
+ *
+ * Gets the numbers displayed by a #GimpNumberPairEntry.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_number_pair_entry_get_values (GimpNumberPairEntry *entry,
+ gdouble *left,
+ gdouble *right)
+{
+ GimpNumberPairEntryPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_NUMBER_PAIR_ENTRY (entry));
+
+ priv = GIMP_NUMBER_PAIR_ENTRY_GET_PRIVATE (entry);
+
+ if (left != NULL)
+ *left = priv->left_number;
+
+ if (right != NULL)
+ *right = priv->right_number;
+}
+
+/**
+ * gimp_number_pair_entry_set_default_text:
+ * @entry: A #GimpNumberPairEntry widget.
+ * @string: Default string.
+ *
+ * Causes the entry to show a given string when in automatic mode,
+ * instead of the default numbers. The only thing this does is making
+ * the #GimpNumberPairEntry showing this string, the internal state
+ * and API calls are not affected.
+ *
+ * Set the default string to %NULL to display default values as
+ * normal.
+ *
+ * Since: 2.4
+ */
+void
+gimp_number_pair_entry_set_default_text (GimpNumberPairEntry *entry,
+ const gchar *string)
+{
+ GimpNumberPairEntryPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_NUMBER_PAIR_ENTRY (entry));
+
+ priv = GIMP_NUMBER_PAIR_ENTRY_GET_PRIVATE (entry);
+
+ g_free (priv->default_text);
+ priv->default_text = g_strdup (string);
+
+ gimp_number_pair_entry_update_text (entry);
+
+ g_object_notify (G_OBJECT (entry), "default-text");
+}
+
+/**
+ * gimp_number_pair_entry_get_default_text:
+ * @entry: A #GimpNumberPairEntry widget.
+ *
+ * Returns: the string manually set to be shown, or %NULL if values are
+ * shown in a normal fashion.
+ *
+ * Since: 2.4
+ */
+const gchar *
+gimp_number_pair_entry_get_default_text (GimpNumberPairEntry *entry)
+{
+ GimpNumberPairEntryPrivate *priv;
+
+ g_return_val_if_fail (GIMP_IS_NUMBER_PAIR_ENTRY (entry), NULL);
+
+ priv = GIMP_NUMBER_PAIR_ENTRY_GET_PRIVATE (entry);
+
+ return priv->default_text;
+}
+
+/**
+ * gimp_number_pair_entry_set_aspect:
+ * @entry: A #GimpNumberPairEntry widget.
+ * @aspect: The new aspect.
+ *
+ * Sets the aspect of the ratio by swapping the left_number and
+ * right_number if necessary (or setting them to 1.0 in case that
+ * @aspect is %GIMP_ASPECT_SQUARE).
+ *
+ * Since: 2.4
+ **/
+void
+gimp_number_pair_entry_set_aspect (GimpNumberPairEntry *entry,
+ GimpAspectType aspect)
+{
+ GimpNumberPairEntryPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_NUMBER_PAIR_ENTRY (entry));
+
+ if (gimp_number_pair_entry_get_aspect (entry) == aspect)
+ return;
+
+ priv = GIMP_NUMBER_PAIR_ENTRY_GET_PRIVATE (entry);
+
+ switch (aspect)
+ {
+ case GIMP_ASPECT_SQUARE:
+ gimp_number_pair_entry_set_values (entry,
+ priv->left_number,
+ priv->left_number);
+ break;
+
+ case GIMP_ASPECT_LANDSCAPE:
+ case GIMP_ASPECT_PORTRAIT:
+ gimp_number_pair_entry_set_values (entry,
+ priv->right_number,
+ priv->left_number);
+ break;
+ }
+}
+
+/**
+ * gimp_number_pair_entry_get_aspect:
+ * @entry: A #GimpNumberPairEntry widget.
+ *
+ * Gets the aspect of the ratio displayed by a #GimpNumberPairEntry.
+ *
+ * Returns: The entry's current aspect.
+ *
+ * Since: 2.4
+ **/
+GimpAspectType
+gimp_number_pair_entry_get_aspect (GimpNumberPairEntry *entry)
+{
+ GimpNumberPairEntryPrivate *priv;
+
+ g_return_val_if_fail (GIMP_IS_NUMBER_PAIR_ENTRY (entry), GIMP_ASPECT_SQUARE);
+
+ priv = GIMP_NUMBER_PAIR_ENTRY_GET_PRIVATE (entry);
+
+ if (priv->left_number > priv->right_number)
+ {
+ return GIMP_ASPECT_LANDSCAPE;
+ }
+ else if (priv->left_number < priv->right_number)
+ {
+ return GIMP_ASPECT_PORTRAIT;
+ }
+ else
+ {
+ return GIMP_ASPECT_SQUARE;
+ }
+}
+
+static void
+gimp_number_pair_entry_modify_font (GimpNumberPairEntry *entry,
+ gboolean italic)
+{
+ GimpNumberPairEntryPrivate *priv = GIMP_NUMBER_PAIR_ENTRY_GET_PRIVATE (entry);
+ GtkRcStyle *rc_style;
+
+ if (priv->font_italic == italic)
+ return;
+
+ rc_style = gtk_widget_get_modifier_style (GTK_WIDGET (entry));
+
+ if (! rc_style->font_desc)
+ {
+ PangoContext *context;
+ PangoFontDescription *font_desc;
+
+ context = gtk_widget_get_pango_context (GTK_WIDGET (entry));
+ font_desc = pango_context_get_font_description (context);
+
+ rc_style->font_desc = pango_font_description_copy (font_desc);
+ }
+
+ pango_font_description_set_style (rc_style->font_desc,
+ italic ?
+ PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL);
+
+ gtk_widget_modify_style (GTK_WIDGET (entry), rc_style);
+
+ gtk_entry_set_icon_sensitive (GTK_ENTRY (entry),
+ GTK_ENTRY_ICON_SECONDARY,
+ ! italic);
+
+ priv->font_italic = italic;
+}
+
+
+/**
+ * gimp_number_pair_entry_set_user_override:
+ * @entry: A #GimpNumberPairEntry widget.
+ * @user_override: %TRUE sets the entry in user overridden mode,
+ * %FALSE disables.
+ *
+ * When the entry is not in user overridden mode, the values will
+ * change when the default values are changed. When in user overridden
+ * mode, setting default values will not affect the active values.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_number_pair_entry_set_user_override (GimpNumberPairEntry *entry,
+ gboolean user_override)
+{
+ GimpNumberPairEntryPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_NUMBER_PAIR_ENTRY (entry));
+
+ priv = GIMP_NUMBER_PAIR_ENTRY_GET_PRIVATE (entry);
+
+ priv->user_override = user_override;
+
+ if (! user_override)
+ {
+ gimp_number_pair_entry_set_default_values (entry,
+ priv->default_left_number,
+ priv->default_right_number);
+ }
+
+ gimp_number_pair_entry_modify_font (entry, ! user_override);
+
+ g_object_notify (G_OBJECT (entry), "user-override");
+}
+
+/**
+ * gimp_number_pair_entry_get_user_override:
+ * @entry: A #GimpNumberPairEntry widget.
+ *
+ * Returns: Whether or not the the widget is in user overridden mode.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_number_pair_entry_get_user_override (GimpNumberPairEntry *entry)
+{
+ GimpNumberPairEntryPrivate *priv;
+
+ g_return_val_if_fail (GIMP_IS_NUMBER_PAIR_ENTRY (entry), FALSE);
+
+ priv = GIMP_NUMBER_PAIR_ENTRY_GET_PRIVATE (entry);
+
+ return priv->user_override;
+}
+
+static void
+gimp_number_pair_entry_changed (GimpNumberPairEntry *entry)
+{
+ gimp_number_pair_entry_modify_font (entry, FALSE);
+}
+
+static void
+gimp_number_pair_entry_icon_press (GimpNumberPairEntry *entry)
+{
+ gimp_number_pair_entry_set_user_override (entry, FALSE);
+
+ gtk_editable_set_position (GTK_EDITABLE (entry), -1);
+}
+
+static gboolean
+gimp_number_pair_entry_events (GtkWidget *widget,
+ GdkEvent *event)
+{
+ GimpNumberPairEntry *entry;
+ GimpNumberPairEntryPrivate *priv;
+ gboolean force_user_override;
+
+ entry = GIMP_NUMBER_PAIR_ENTRY (widget);
+ priv = GIMP_NUMBER_PAIR_ENTRY_GET_PRIVATE (entry);
+ force_user_override = FALSE;
+
+ switch (event->type)
+ {
+ case GDK_KEY_PRESS:
+ {
+ GdkEventKey *kevent = (GdkEventKey *) event;
+
+ if (kevent->keyval != GDK_KEY_Return &&
+ kevent->keyval != GDK_KEY_KP_Enter &&
+ kevent->keyval != GDK_KEY_ISO_Enter)
+ break;
+
+ /* If parsing was done due to widgets focus being lost, we only change
+ * to user-override mode if the values differ from the default ones. If
+ * Return was pressed however, we always switch to user-override mode.
+ */
+ force_user_override = TRUE;
+ }
+ /* Fall through */
+
+ case GDK_FOCUS_CHANGE:
+ {
+ const gchar *text;
+ ParseResult parse_result;
+ gdouble left_value;
+ gdouble right_value;
+
+ text = gtk_entry_get_text (GTK_ENTRY (entry));
+
+ parse_result = gimp_number_pair_entry_parse_text (entry,
+ text,
+ &left_value,
+ &right_value);
+ switch (parse_result)
+ {
+ case PARSE_VALID:
+ {
+ if (priv->left_number != left_value ||
+ priv->right_number != right_value ||
+ force_user_override)
+ {
+ gimp_number_pair_entry_set_values (entry,
+ left_value,
+ right_value);
+
+ gimp_number_pair_entry_set_user_override (entry, TRUE);
+ }
+ }
+ break;
+
+ case PARSE_CLEAR:
+ gimp_number_pair_entry_set_user_override (entry, FALSE);
+ break;
+
+ default:
+ break;
+ }
+
+ /* Make sure the entry text is up to date */
+
+ gimp_number_pair_entry_update_text (entry);
+
+ gtk_editable_set_position (GTK_EDITABLE (entry), -1);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+/**
+ * gimp_number_pair_entry_strdup_number_pair_string:
+ * @entry:
+ * @left_number:
+ * @right_number:
+ *
+ * Returns: allocated data, must be g_free:d.
+ **/
+static gchar *
+gimp_number_pair_entry_strdup_number_pair_string (GimpNumberPairEntry *entry,
+ gdouble left_number,
+ gdouble right_number)
+{
+ GimpNumberPairEntryPrivate *priv = GIMP_NUMBER_PAIR_ENTRY_GET_PRIVATE (entry);
+ gchar sep[8];
+ gint len;
+
+ if (priv->num_separators > 0)
+ len = g_unichar_to_utf8 (priv->separators[0], sep);
+ else
+ len = g_unichar_to_utf8 (DEFAULT_SEPARATOR, sep);
+
+ sep[len] = '\0';
+
+ return g_strdup_printf ("%g%s%g", left_number, sep, right_number);
+}
+
+static void
+gimp_number_pair_entry_update_text (GimpNumberPairEntry *entry)
+{
+ GimpNumberPairEntryPrivate *priv = GIMP_NUMBER_PAIR_ENTRY_GET_PRIVATE (entry);
+ gchar *buffer;
+
+ if (! priv->user_override &&
+ priv->default_text != NULL)
+ {
+ /* Instead of the numbers, show the string explicitly set by a
+ * client to show when in automatic mode.
+ */
+ buffer = g_strdup (priv->default_text);
+ }
+ else
+ {
+ buffer = gimp_number_pair_entry_strdup_number_pair_string (entry,
+ priv->left_number,
+ priv->right_number);
+ }
+
+ g_signal_handlers_block_by_func (entry,
+ gimp_number_pair_entry_changed, NULL);
+
+ gtk_entry_set_text (GTK_ENTRY (entry), buffer);
+ g_free (buffer);
+
+ g_signal_handlers_unblock_by_func (entry,
+ gimp_number_pair_entry_changed, NULL);
+
+ gimp_number_pair_entry_modify_font (entry, ! priv->user_override);
+}
+
+static gboolean
+gimp_number_pair_entry_valid_separator (GimpNumberPairEntry *entry,
+ gunichar candidate)
+{
+ GimpNumberPairEntryPrivate *priv = GIMP_NUMBER_PAIR_ENTRY_GET_PRIVATE (entry);
+
+ if (priv->num_separators > 0)
+ {
+ gint i;
+
+ for (i = 0; i < priv->num_separators; i++)
+ if (priv->separators[i] == candidate)
+ return TRUE;
+ }
+ else if (candidate == DEFAULT_SEPARATOR)
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static ParseResult
+gimp_number_pair_entry_parse_text (GimpNumberPairEntry *entry,
+ const gchar *text,
+ gdouble *left_value,
+ gdouble *right_value)
+{
+ GimpNumberPairEntryPrivate *priv = GIMP_NUMBER_PAIR_ENTRY_GET_PRIVATE (entry);
+
+ gdouble new_left_number;
+ gdouble new_right_number;
+ gboolean simplify = FALSE;
+ gchar *end;
+
+ /* skip over whitespace */
+ while (g_unichar_isspace (g_utf8_get_char (text)))
+ text = g_utf8_next_char (text);
+
+ /* check if clear */
+ if (! *text)
+ return PARSE_CLEAR;
+
+ /* try to parse a number */
+ new_left_number = strtod (text, &end);
+
+ if (end == text)
+ return PARSE_INVALID;
+ else
+ text = end;
+
+ /* skip over whitespace */
+ while (g_unichar_isspace (g_utf8_get_char (text)))
+ text = g_utf8_next_char (text);
+
+ /* check for a valid separator */
+ if (! gimp_number_pair_entry_valid_separator (entry, g_utf8_get_char (text)))
+ return PARSE_INVALID;
+ else
+ text = g_utf8_next_char (text);
+
+ /* try to parse another number */
+ new_right_number = strtod (text, &end);
+
+ if (end == text)
+ return PARSE_INVALID;
+ else
+ text = end;
+
+ /* skip over whitespace */
+ while (g_unichar_isspace (g_utf8_get_char (text)))
+ text = g_utf8_next_char (text);
+
+ /* check for the simplification char */
+ if (g_utf8_get_char (text) == SIMPLIFICATION_CHAR)
+ {
+ simplify = priv->allow_simplification;
+ text = g_utf8_next_char (text);
+ }
+
+ /* skip over whitespace */
+ while (g_unichar_isspace (g_utf8_get_char (text)))
+ text = g_utf8_next_char (text);
+
+ /* check for trailing garbage */
+ if (*text)
+ return PARSE_INVALID;
+
+ if (! gimp_number_pair_entry_numbers_in_range (entry,
+ new_left_number,
+ new_right_number))
+ return PARSE_INVALID;
+
+ if (simplify && new_right_number != 0.0)
+ {
+ gimp_number_pair_entry_ratio_to_fraction (new_left_number /
+ new_right_number,
+ left_value,
+ right_value);
+ }
+ else
+ {
+ *left_value = new_left_number;
+ *right_value = new_right_number;
+ }
+
+ return PARSE_VALID;
+}
+
+static void
+gimp_number_pair_entry_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpNumberPairEntry *entry = GIMP_NUMBER_PAIR_ENTRY (object);
+ GimpNumberPairEntryPrivate *priv;
+
+ priv = GIMP_NUMBER_PAIR_ENTRY_GET_PRIVATE (entry);
+
+ switch (property_id)
+ {
+ case PROP_LEFT_NUMBER:
+ gimp_number_pair_entry_set_values (entry,
+ g_value_get_double (value),
+ priv->right_number);
+ break;
+ case PROP_RIGHT_NUMBER:
+ gimp_number_pair_entry_set_values (entry,
+ priv->left_number,
+ g_value_get_double (value));
+ break;
+ case PROP_DEFAULT_LEFT_NUMBER:
+ gimp_number_pair_entry_set_default_values (entry,
+ g_value_get_double (value),
+ priv->default_right_number);
+ break;
+ case PROP_DEFAULT_RIGHT_NUMBER:
+ gimp_number_pair_entry_set_default_values (entry,
+ priv->default_left_number,
+ g_value_get_double (value));
+ break;
+ case PROP_USER_OVERRIDE:
+ gimp_number_pair_entry_set_user_override (entry,
+ g_value_get_boolean (value));
+ break;
+ case PROP_SEPARATORS:
+ g_free (priv->separators);
+ priv->num_separators = 0;
+ if (g_value_get_string (value))
+ priv->separators = g_utf8_to_ucs4 (g_value_get_string (value), -1,
+ NULL, &priv->num_separators, NULL);
+ else
+ priv->separators = NULL;
+ break;
+ case PROP_DEFAULT_TEXT:
+ gimp_number_pair_entry_set_default_text (entry,
+ g_value_get_string (value));
+ break;
+ case PROP_ALLOW_SIMPLIFICATION:
+ priv->allow_simplification = g_value_get_boolean (value);
+ break;
+ case PROP_MIN_VALID_VALUE:
+ priv->min_valid_value = g_value_get_double (value);
+ break;
+ case PROP_MAX_VALID_VALUE:
+ priv->max_valid_value = g_value_get_double (value);
+ break;
+ case PROP_RATIO:
+ gimp_number_pair_entry_set_ratio (entry, g_value_get_double (value));
+ break;
+ case PROP_ASPECT:
+ gimp_number_pair_entry_set_aspect (entry, g_value_get_enum (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_number_pair_entry_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpNumberPairEntry *entry = GIMP_NUMBER_PAIR_ENTRY (object);
+ GimpNumberPairEntryPrivate *priv;
+
+ priv = GIMP_NUMBER_PAIR_ENTRY_GET_PRIVATE (entry);
+
+ switch (property_id)
+ {
+ case PROP_LEFT_NUMBER:
+ g_value_set_double (value, priv->left_number);
+ break;
+ case PROP_RIGHT_NUMBER:
+ g_value_set_double (value, priv->right_number);
+ break;
+ case PROP_DEFAULT_LEFT_NUMBER:
+ g_value_set_double (value, priv->default_left_number);
+ break;
+ case PROP_DEFAULT_RIGHT_NUMBER:
+ g_value_set_double (value, priv->default_right_number);
+ break;
+ case PROP_USER_OVERRIDE:
+ g_value_set_boolean (value, priv->user_override);
+ break;
+ case PROP_SEPARATORS:
+ g_value_take_string (value,
+ g_ucs4_to_utf8 (priv->separators,
+ priv->num_separators,
+ NULL, NULL, NULL));
+ break;
+ case PROP_ALLOW_SIMPLIFICATION:
+ g_value_set_boolean (value, priv->allow_simplification);
+ break;
+ case PROP_DEFAULT_TEXT:
+ g_value_set_string (value, priv->default_text);
+ break;
+ case PROP_MIN_VALID_VALUE:
+ g_value_set_double (value, priv->min_valid_value);
+ break;
+ case PROP_MAX_VALID_VALUE:
+ g_value_set_double (value, priv->max_valid_value);
+ break;
+ case PROP_RATIO:
+ g_value_set_double (value, gimp_number_pair_entry_get_ratio (entry));
+ break;
+ case PROP_ASPECT:
+ g_value_set_enum (value, gimp_number_pair_entry_get_aspect (entry));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+/**
+ * gimp_number_pair_entry_set_default_values:
+ * @entry: A #GimpNumberPairEntry widget.
+ * @left: Default left value in the entry.
+ * @right: Default right value in the entry.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_number_pair_entry_set_default_values (GimpNumberPairEntry *entry,
+ gdouble left,
+ gdouble right)
+{
+ GimpNumberPairEntryPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_NUMBER_PAIR_ENTRY (entry));
+
+ priv = GIMP_NUMBER_PAIR_ENTRY_GET_PRIVATE (entry);
+
+ priv->default_left_number = left;
+ priv->default_right_number = right;
+
+ if (! priv->user_override)
+ {
+ gimp_number_pair_entry_set_values (entry,
+ priv->default_left_number,
+ priv->default_right_number);
+ }
+}
+
+/**
+ * gimp_number_pair_entry_get_default_values:
+ * @entry: A #GimpNumberPairEntry widget.
+ * @left: Pointer of where to put left value.
+ * @right: Pointer of where to put right value.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_number_pair_entry_get_default_values (GimpNumberPairEntry *entry,
+ gdouble *left,
+ gdouble *right)
+{
+ GimpNumberPairEntryPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_NUMBER_PAIR_ENTRY (entry));
+
+ priv = GIMP_NUMBER_PAIR_ENTRY_GET_PRIVATE (entry);
+
+ if (left != NULL)
+ *left = priv->default_left_number;
+
+ if (right != NULL)
+ *right = priv->default_right_number;
+}
+
+static gboolean
+gimp_number_pair_entry_numbers_in_range (GimpNumberPairEntry *entry,
+ gdouble left_number,
+ gdouble right_number)
+{
+ GimpNumberPairEntryPrivate *priv = GIMP_NUMBER_PAIR_ENTRY_GET_PRIVATE (entry);
+
+ return (left_number >= priv->min_valid_value &&
+ left_number <= priv->max_valid_value &&
+ right_number >= priv->min_valid_value &&
+ right_number <= priv->max_valid_value);
+}
diff --git a/libgimpwidgets/gimpnumberpairentry.h b/libgimpwidgets/gimpnumberpairentry.h
new file mode 100644
index 0000000..fb46c0b
--- /dev/null
+++ b/libgimpwidgets/gimpnumberpairentry.h
@@ -0,0 +1,104 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpratioentry.h
+ * Copyright (C) 2006 Simon Budig <simon@gimp.org>
+ * Copyright (C) 2007 Sven Neumann <sven@gimp.org>
+ * Copyright (C) 2007 Martin Nordholts <martin@svn.gnome.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_NUMBER_PAIR_ENTRY_H__
+#define __GIMP_NUMBER_PAIR_ENTRY_H__
+
+G_BEGIN_DECLS
+
+
+#define GIMP_TYPE_NUMBER_PAIR_ENTRY (gimp_number_pair_entry_get_type ())
+#define GIMP_NUMBER_PAIR_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_NUMBER_PAIR_ENTRY, GimpNumberPairEntry))
+#define GIMP_NUMBER_PAIR_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_NUMBER_PAIR_ENTRY, GimpNumberPairEntryClass))
+#define GIMP_IS_NUMBER_PAIR_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_NUMBER_PAIR_ENTRY))
+#define GIMP_IS_NUMBER_PAIR_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_NUMBER_PAIR_ENTRY))
+#define GIMP_NUMBER_PAIR_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_NUMBER_PAIR_AREA, GimpNumberPairEntryClass))
+
+
+typedef struct _GimpNumberPairEntryClass GimpNumberPairEntryClass;
+
+
+struct _GimpNumberPairEntry
+{
+ GtkEntry parent_instance;
+
+ gpointer priv;
+};
+
+struct _GimpNumberPairEntryClass
+{
+ GtkEntryClass parent_class;
+
+ void (* numbers_changed) (GimpNumberPairEntry *entry);
+ void (* ratio_changed) (GimpNumberPairEntry *entry);
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_number_pair_entry_get_type (void) G_GNUC_CONST;
+GtkWidget * gimp_number_pair_entry_new (const gchar *separators,
+ gboolean allow_simplification,
+ gdouble min_valid_value,
+ gdouble max_valid_value);
+void gimp_number_pair_entry_set_default_values (GimpNumberPairEntry *entry,
+ gdouble left,
+ gdouble right);
+void gimp_number_pair_entry_get_default_values (GimpNumberPairEntry *entry,
+ gdouble *left,
+ gdouble *right);
+void gimp_number_pair_entry_set_values (GimpNumberPairEntry *entry,
+ gdouble left,
+ gdouble right);
+void gimp_number_pair_entry_get_values (GimpNumberPairEntry *entry,
+ gdouble *left,
+ gdouble *right);
+
+void gimp_number_pair_entry_set_default_text (GimpNumberPairEntry *entry,
+ const gchar *string);
+const gchar * gimp_number_pair_entry_get_default_text (GimpNumberPairEntry *entry);
+
+void gimp_number_pair_entry_set_ratio (GimpNumberPairEntry *entry,
+ gdouble ratio);
+gdouble gimp_number_pair_entry_get_ratio (GimpNumberPairEntry *entry);
+
+void gimp_number_pair_entry_set_aspect (GimpNumberPairEntry *entry,
+ GimpAspectType aspect);
+GimpAspectType gimp_number_pair_entry_get_aspect (GimpNumberPairEntry *entry);
+
+void gimp_number_pair_entry_set_user_override (GimpNumberPairEntry *entry,
+ gboolean user_override);
+gboolean gimp_number_pair_entry_get_user_override (GimpNumberPairEntry *entry);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_NUMBER_PAIR_ENTRY_H__ */
diff --git a/libgimpwidgets/gimpoffsetarea.c b/libgimpwidgets/gimpoffsetarea.c
new file mode 100644
index 0000000..3cf9521
--- /dev/null
+++ b/libgimpwidgets/gimpoffsetarea.c
@@ -0,0 +1,506 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpoffsetarea.c
+ * Copyright (C) 2001 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpwidgetsmarshal.h"
+#include "gimpoffsetarea.h"
+
+
+/**
+ * SECTION: gimpoffsetarea
+ * @title: GimpOffsetArea
+ * @short_description: Widget to control image offsets.
+ *
+ * Widget to control image offsets.
+ **/
+
+
+#define DRAWING_AREA_SIZE 200
+
+
+enum
+{
+ OFFSETS_CHANGED,
+ LAST_SIGNAL
+};
+
+
+static void gimp_offset_area_resize (GimpOffsetArea *area);
+
+static void gimp_offset_area_realize (GtkWidget *widget);
+static void gimp_offset_area_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static gboolean gimp_offset_area_event (GtkWidget *widget,
+ GdkEvent *event);
+static gboolean gimp_offset_area_expose_event (GtkWidget *widget,
+ GdkEventExpose *eevent);
+
+
+G_DEFINE_TYPE (GimpOffsetArea, gimp_offset_area, GTK_TYPE_DRAWING_AREA)
+
+#define parent_class gimp_offset_area_parent_class
+
+static guint gimp_offset_area_signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+gimp_offset_area_class_init (GimpOffsetAreaClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ gimp_offset_area_signals[OFFSETS_CHANGED] =
+ g_signal_new ("offsets-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpOffsetAreaClass, offsets_changed),
+ NULL, NULL,
+ _gimp_widgets_marshal_VOID__INT_INT,
+ G_TYPE_NONE, 2,
+ G_TYPE_INT,
+ G_TYPE_INT);
+
+ widget_class->size_allocate = gimp_offset_area_size_allocate;
+ widget_class->realize = gimp_offset_area_realize;
+ widget_class->event = gimp_offset_area_event;
+ widget_class->expose_event = gimp_offset_area_expose_event;
+}
+
+static void
+gimp_offset_area_init (GimpOffsetArea *area)
+{
+ area->orig_width = 0;
+ area->orig_height = 0;
+ area->width = 0;
+ area->height = 0;
+ area->offset_x = 0;
+ area->offset_y = 0;
+ area->display_ratio_x = 1.0;
+ area->display_ratio_y = 1.0;
+
+ gtk_widget_add_events (GTK_WIDGET (area),
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_BUTTON1_MOTION_MASK);
+}
+
+/**
+ * gimp_offset_area_new:
+ * @orig_width: the original width
+ * @orig_height: the original height
+ *
+ * Creates a new #GimpOffsetArea widget. A #GimpOffsetArea can be used
+ * when resizing an image or a drawable to allow the user to interactively
+ * specify the new offsets.
+ *
+ * Return value: the new #GimpOffsetArea widget.
+ **/
+GtkWidget *
+gimp_offset_area_new (gint orig_width,
+ gint orig_height)
+{
+ GimpOffsetArea *area;
+
+ g_return_val_if_fail (orig_width > 0, NULL);
+ g_return_val_if_fail (orig_height > 0, NULL);
+
+ area = g_object_new (GIMP_TYPE_OFFSET_AREA, NULL);
+
+ area->orig_width = area->width = orig_width;
+ area->orig_height = area->height = orig_height;
+
+ gimp_offset_area_resize (area);
+
+ return GTK_WIDGET (area);
+}
+
+/**
+ * gimp_offset_area_set_pixbuf:
+ * @offset_area: a #GimpOffsetArea.
+ * @pixbuf: a #GdkPixbuf.
+ *
+ * Sets the pixbuf which represents the original image/drawable which
+ * is being offset.
+ *
+ * Since: 2.2
+ **/
+void
+gimp_offset_area_set_pixbuf (GimpOffsetArea *area,
+ GdkPixbuf *pixbuf)
+{
+ g_return_if_fail (GIMP_IS_OFFSET_AREA (area));
+ g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
+
+ g_object_set_data_full (G_OBJECT (area), "pixbuf",
+ gdk_pixbuf_copy (pixbuf),
+ (GDestroyNotify) g_object_unref);
+
+ gtk_widget_queue_draw (GTK_WIDGET (area));
+}
+
+/**
+ * gimp_offset_area_set_size:
+ * @offset_area: a #GimpOffsetArea.
+ * @width: the new width
+ * @height: the new height
+ *
+ * Sets the size of the image/drawable displayed by the #GimpOffsetArea.
+ * If the offsets change as a result of this change, the "offsets-changed"
+ * signal is emitted.
+ **/
+void
+gimp_offset_area_set_size (GimpOffsetArea *area,
+ gint width,
+ gint height)
+{
+ g_return_if_fail (GIMP_IS_OFFSET_AREA (area));
+ g_return_if_fail (width > 0 && height > 0);
+
+ if (area->width != width || area->height != height)
+ {
+ gint offset_x;
+ gint offset_y;
+
+ area->width = width;
+ area->height = height;
+
+ if (area->orig_width <= area->width)
+ offset_x = CLAMP (area->offset_x, 0, area->width - area->orig_width);
+ else
+ offset_x = CLAMP (area->offset_x, area->width - area->orig_width, 0);
+
+ if (area->orig_height <= area->height)
+ offset_y = CLAMP (area->offset_y, 0, area->height - area->orig_height);
+ else
+ offset_y = CLAMP (area->offset_y, area->height - area->orig_height, 0);
+
+ if (offset_x != area->offset_x || offset_y != area->offset_y)
+ {
+ area->offset_x = offset_x;
+ area->offset_y = offset_y;
+
+ g_signal_emit (area,
+ gimp_offset_area_signals[OFFSETS_CHANGED], 0,
+ offset_x, offset_y);
+ }
+
+ gimp_offset_area_resize (area);
+ }
+}
+
+/**
+ * gimp_offset_area_set_offsets:
+ * @offset_area: a #GimpOffsetArea.
+ * @offset_x: the X offset
+ * @offset_y: the Y offset
+ *
+ * Sets the offsets of the image/drawable displayed by the #GimpOffsetArea.
+ * It does not emit the "offsets-changed" signal.
+ **/
+void
+gimp_offset_area_set_offsets (GimpOffsetArea *area,
+ gint offset_x,
+ gint offset_y)
+{
+ g_return_if_fail (GIMP_IS_OFFSET_AREA (area));
+
+ if (area->offset_x != offset_x || area->offset_y != offset_y)
+ {
+ if (area->orig_width <= area->width)
+ area->offset_x = CLAMP (offset_x, 0, area->width - area->orig_width);
+ else
+ area->offset_x = CLAMP (offset_x, area->width - area->orig_width, 0);
+
+ if (area->orig_height <= area->height)
+ area->offset_y = CLAMP (offset_y, 0, area->height - area->orig_height);
+ else
+ area->offset_y = CLAMP (offset_y, area->height - area->orig_height, 0);
+
+ gtk_widget_queue_draw (GTK_WIDGET (area));
+ }
+}
+
+static void
+gimp_offset_area_resize (GimpOffsetArea *area)
+{
+ gint width;
+ gint height;
+ gdouble ratio;
+
+ if (area->orig_width == 0 || area->orig_height == 0)
+ return;
+
+ if (area->orig_width <= area->width)
+ width = area->width;
+ else
+ width = area->orig_width * 2 - area->width;
+
+ if (area->orig_height <= area->height)
+ height = area->height;
+ else
+ height = area->orig_height * 2 - area->height;
+
+ ratio = (gdouble) DRAWING_AREA_SIZE / (gdouble) MAX (width, height);
+
+ width = ratio * (gdouble) width;
+ height = ratio * (gdouble) height;
+
+ gtk_widget_set_size_request (GTK_WIDGET (area), width, height);
+ gtk_widget_queue_resize (GTK_WIDGET (area));
+}
+
+static void
+gimp_offset_area_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GimpOffsetArea *area = GIMP_OFFSET_AREA (widget);
+ GdkPixbuf *pixbuf;
+
+ GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
+
+ area->display_ratio_x = ((gdouble) allocation->width /
+ ((area->orig_width <= area->width) ?
+ area->width :
+ area->orig_width * 2 - area->width));
+
+ area->display_ratio_y = ((gdouble) allocation->height /
+ ((area->orig_height <= area->height) ?
+ area->height :
+ area->orig_height * 2 - area->height));
+
+ pixbuf = g_object_get_data (G_OBJECT (area), "pixbuf");
+
+ if (pixbuf)
+ {
+ GdkPixbuf *copy;
+ gint pixbuf_width;
+ gint pixbuf_height;
+
+ pixbuf_width = area->display_ratio_x * area->orig_width;
+ pixbuf_width = MAX (pixbuf_width, 1);
+
+ pixbuf_height = area->display_ratio_y * area->orig_height;
+ pixbuf_height = MAX (pixbuf_height, 1);
+
+ copy = g_object_get_data (G_OBJECT (area), "pixbuf-copy");
+
+ if (copy &&
+ (pixbuf_width != gdk_pixbuf_get_width (copy) ||
+ pixbuf_height != gdk_pixbuf_get_height (copy)))
+ {
+ copy = NULL;
+ }
+
+ if (! copy)
+ {
+ copy = gdk_pixbuf_scale_simple (pixbuf, pixbuf_width, pixbuf_height,
+ GDK_INTERP_NEAREST);
+
+ g_object_set_data_full (G_OBJECT (area), "pixbuf-copy",
+ copy, (GDestroyNotify) g_object_unref);
+ }
+ }
+}
+
+static void
+gimp_offset_area_realize (GtkWidget *widget)
+{
+ GdkCursor *cursor;
+
+ GTK_WIDGET_CLASS (parent_class)->realize (widget);
+
+ cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
+ GDK_FLEUR);
+ gdk_window_set_cursor (gtk_widget_get_window (widget), cursor);
+ gdk_cursor_unref (cursor);
+}
+
+static gboolean
+gimp_offset_area_event (GtkWidget *widget,
+ GdkEvent *event)
+{
+ static gint orig_offset_x = 0;
+ static gint orig_offset_y = 0;
+ static gint start_x = 0;
+ static gint start_y = 0;
+
+ GimpOffsetArea *area = GIMP_OFFSET_AREA (widget);
+ gint offset_x;
+ gint offset_y;
+
+ if (area->orig_width == 0 || area->orig_height == 0)
+ return FALSE;
+
+ switch (event->type)
+ {
+ case GDK_BUTTON_PRESS:
+ if (event->button.button == 1)
+ {
+ gtk_grab_add (widget);
+
+ orig_offset_x = area->offset_x;
+ orig_offset_y = area->offset_y;
+ start_x = event->button.x;
+ start_y = event->button.y;
+ }
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ offset_x = (orig_offset_x +
+ (event->motion.x - start_x) / area->display_ratio_x);
+ offset_y = (orig_offset_y +
+ (event->motion.y - start_y) / area->display_ratio_y);
+
+ if (area->offset_x != offset_x || area->offset_y != offset_y)
+ {
+ gimp_offset_area_set_offsets (area, offset_x, offset_y);
+
+ g_signal_emit (area,
+ gimp_offset_area_signals[OFFSETS_CHANGED], 0,
+ area->offset_x, area->offset_y);
+ }
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ if (event->button.button == 1)
+ {
+ gtk_grab_remove (widget);
+
+ start_x = start_y = 0;
+ }
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gimp_offset_area_expose_event (GtkWidget *widget,
+ GdkEventExpose *eevent)
+{
+ GimpOffsetArea *area = GIMP_OFFSET_AREA (widget);
+ GtkStyle *style = gtk_widget_get_style (widget);
+ GdkWindow *window = gtk_widget_get_window (widget);
+ cairo_t *cr;
+ GtkAllocation allocation;
+ GdkPixbuf *pixbuf;
+ gint w, h;
+ gint x, y;
+
+ cr = gdk_cairo_create (eevent->window);
+ gdk_cairo_region (cr, eevent->region);
+ cairo_clip (cr);
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ x = (area->display_ratio_x *
+ ((area->orig_width <= area->width) ?
+ area->offset_x :
+ area->offset_x + area->orig_width - area->width));
+
+ y = (area->display_ratio_y *
+ ((area->orig_height <= area->height) ?
+ area->offset_y :
+ area->offset_y + area->orig_height - area->height));
+
+ w = area->display_ratio_x * area->orig_width;
+ w = MAX (w, 1);
+
+ h = area->display_ratio_y * area->orig_height;
+ h = MAX (h, 1);
+
+ pixbuf = g_object_get_data (G_OBJECT (widget), "pixbuf-copy");
+
+ if (pixbuf)
+ {
+ gdk_cairo_set_source_pixbuf (cr, pixbuf, x, y);
+ cairo_paint (cr);
+
+ cairo_rectangle (cr, x + 0.5, y + 0.5, w - 1, h - 1);
+ cairo_set_line_width (cr, 1.0);
+ gdk_cairo_set_source_color (cr, &style->black);
+ cairo_stroke (cr);
+ }
+ else
+ {
+ gtk_paint_shadow (style, window, GTK_STATE_NORMAL,
+ GTK_SHADOW_OUT,
+ NULL, widget, NULL,
+ x, y, w, h);
+ }
+
+ if (area->orig_width > area->width || area->orig_height > area->height)
+ {
+ gint line_width;
+
+ if (area->orig_width > area->width)
+ {
+ x = area->display_ratio_x * (area->orig_width - area->width);
+ w = area->display_ratio_x * area->width;
+ }
+ else
+ {
+ x = -1;
+ w = allocation.width + 2;
+ }
+
+ if (area->orig_height > area->height)
+ {
+ y = area->display_ratio_y * (area->orig_height - area->height);
+ h = area->display_ratio_y * area->height;
+ }
+ else
+ {
+ y = -1;
+ h = allocation.height + 2;
+ }
+
+ w = MAX (w, 1);
+ h = MAX (h, 1);
+
+ line_width = MIN (3, MIN (w, h));
+
+ cairo_rectangle (cr,
+ x + line_width / 2.0,
+ y + line_width / 2.0,
+ MAX (w - line_width, 1),
+ MAX (h - line_width, 1));
+
+ cairo_set_line_width (cr, line_width);
+ cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.6);
+ cairo_stroke_preserve (cr);
+
+ cairo_set_line_width (cr, 1.0);
+ cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.8);
+ cairo_stroke (cr);
+ }
+
+ cairo_destroy (cr);
+
+ return FALSE;
+}
diff --git a/libgimpwidgets/gimpoffsetarea.h b/libgimpwidgets/gimpoffsetarea.h
new file mode 100644
index 0000000..5c5705a
--- /dev/null
+++ b/libgimpwidgets/gimpoffsetarea.h
@@ -0,0 +1,91 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpoffsetarea.h
+ * Copyright (C) 2001 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_OFFSET_AREA_H__
+#define __GIMP_OFFSET_AREA_H__
+
+G_BEGIN_DECLS
+
+
+/* For information look into the C source or the html documentation */
+
+#define GIMP_TYPE_OFFSET_AREA (gimp_offset_area_get_type ())
+#define GIMP_OFFSET_AREA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OFFSET_AREA, GimpOffsetArea))
+#define GIMP_OFFSET_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OFFSET_AREA, GimpOffsetAreaClass))
+#define GIMP_IS_OFFSET_AREA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OFFSET_AREA))
+#define GIMP_IS_OFFSET_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OFFSET_AREA))
+#define GIMP_OFFSET_AREA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OFFSET_AREA, GimpOffsetAreaClass))
+
+
+typedef struct _GimpOffsetAreaClass GimpOffsetAreaClass;
+
+struct _GimpOffsetArea
+{
+ GtkDrawingArea parent_instance;
+
+ gint orig_width;
+ gint orig_height;
+ gint width;
+ gint height;
+ gint offset_x;
+ gint offset_y;
+ gdouble display_ratio_x;
+ gdouble display_ratio_y;
+};
+
+struct _GimpOffsetAreaClass
+{
+ GtkDrawingAreaClass parent_class;
+
+ void (* offsets_changed) (GimpOffsetArea *offset_area,
+ gint offset_x,
+ gint offset_y);
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_offset_area_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_offset_area_new (gint orig_width,
+ gint orig_height);
+void gimp_offset_area_set_pixbuf (GimpOffsetArea *offset_area,
+ GdkPixbuf *pixbuf);
+
+void gimp_offset_area_set_size (GimpOffsetArea *offset_area,
+ gint width,
+ gint height);
+void gimp_offset_area_set_offsets (GimpOffsetArea *offset_area,
+ gint offset_x,
+ gint offset_y);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_OFFSET_AREA_H__ */
diff --git a/libgimpwidgets/gimpoldwidgets.c b/libgimpwidgets/gimpoldwidgets.c
new file mode 100644
index 0000000..0b7bcb3
--- /dev/null
+++ b/libgimpwidgets/gimpoldwidgets.c
@@ -0,0 +1,650 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpoldwidgets.c
+ * Copyright (C) 2000 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+/* FIXME: #undef GTK_DISABLE_DEPRECATED */
+#undef GTK_DISABLE_DEPRECATED
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "gimpwidgetstypes.h"
+
+#undef GIMP_DISABLE_DEPRECATED
+#include "gimpoldwidgets.h"
+#include "gimppixmap.h"
+#include "gimpunitmenu.h"
+#include "gimp3migration.h"
+
+
+/**
+ * SECTION: gimpoldwidgets
+ * @title: GimpOldWidgets
+ * @short_description: Old API that is still available but declared
+ * as deprecated.
+ * @see_also: #GimpIntComboBox
+ *
+ * These functions are not defined if you #define GIMP_DISABLE_DEPRECATED.
+ **/
+
+
+/*
+ * Widget Constructors
+ */
+
+/**
+ * gimp_option_menu_new:
+ * @menu_only: %TRUE if the function should return a #GtkMenu only.
+ * @...: A %NULL-terminated @va_list describing the menu items.
+ *
+ * Convenience function to create a #GtkOptionMenu or a #GtkMenu.
+ *
+ * Returns: A #GtkOptionMenu or a #GtkMenu (depending on @menu_only).
+ **/
+GtkWidget *
+gimp_option_menu_new (gboolean menu_only,
+
+ /* specify menu items as va_list:
+ * const gchar *label,
+ * GCallback callback,
+ * gpointer callback_data,
+ * gpointer item_data,
+ * GtkWidget **widget_ptr,
+ * gboolean active
+ */
+
+ ...)
+{
+ GtkWidget *menu;
+ GtkWidget *menuitem;
+
+ /* menu item variables */
+ const gchar *label;
+ GCallback callback;
+ gpointer callback_data;
+ gpointer item_data;
+ GtkWidget **widget_ptr;
+ gboolean active;
+
+ va_list args;
+ gint i;
+ gint initial_index;
+
+ menu = gtk_menu_new ();
+
+ /* create the menu items */
+ initial_index = 0;
+
+ va_start (args, menu_only);
+ label = va_arg (args, const gchar *);
+
+ for (i = 0; label; i++)
+ {
+ callback = va_arg (args, GCallback);
+ callback_data = va_arg (args, gpointer);
+ item_data = va_arg (args, gpointer);
+ widget_ptr = va_arg (args, GtkWidget **);
+ active = va_arg (args, gboolean);
+
+ if (strcmp (label, "---"))
+ {
+ menuitem = gtk_menu_item_new_with_label (label);
+
+ g_signal_connect (menuitem, "activate",
+ callback,
+ callback_data);
+
+ if (item_data)
+ {
+ g_object_set_data (G_OBJECT (menuitem), "gimp-item-data",
+ item_data);
+
+ /* backward compat */
+ g_object_set_data (G_OBJECT (menuitem), "user_data", item_data);
+ }
+ }
+ else
+ {
+ menuitem = gtk_menu_item_new ();
+
+ gtk_widget_set_sensitive (menuitem, FALSE);
+ }
+
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+
+ if (widget_ptr)
+ *widget_ptr = menuitem;
+
+ gtk_widget_show (menuitem);
+
+ /* remember the initial menu item */
+ if (active)
+ initial_index = i;
+
+ label = va_arg (args, const gchar *);
+ }
+ va_end (args);
+
+ if (! menu_only)
+ {
+ GtkWidget *optionmenu;
+
+ optionmenu = gtk_option_menu_new ();
+ gtk_option_menu_set_menu (GTK_OPTION_MENU (optionmenu), menu);
+
+ /* select the initial menu item */
+ gtk_option_menu_set_history (GTK_OPTION_MENU (optionmenu), initial_index);
+
+ return optionmenu;
+ }
+
+ return menu;
+}
+
+/**
+ * gimp_option_menu_new2:
+ * @menu_only: %TRUE if the function should return a #GtkMenu only.
+ * @menu_item_callback: The callback each menu item's "activate" signal will
+ * be connected with.
+ * @menu_item_callback_data:
+ * The data which will be passed to g_signal_connect().
+ * @initial: The @item_data of the initially selected menu item.
+ * @...: A %NULL-terminated @va_list describing the menu items.
+ *
+ * Convenience function to create a #GtkOptionMenu or a #GtkMenu.
+ *
+ * Returns: A #GtkOptionMenu or a #GtkMenu (depending on @menu_only).
+ **/
+GtkWidget *
+gimp_option_menu_new2 (gboolean menu_only,
+ GCallback menu_item_callback,
+ gpointer callback_data,
+ gpointer initial, /* item_data */
+
+ /* specify menu items as va_list:
+ * const gchar *label,
+ * gpointer item_data,
+ * GtkWidget **widget_ptr,
+ */
+
+ ...)
+{
+ GtkWidget *menu;
+ GtkWidget *menuitem;
+
+ /* menu item variables */
+ const gchar *label;
+ gpointer item_data;
+ GtkWidget **widget_ptr;
+
+ va_list args;
+ gint i;
+ gint initial_index;
+
+ menu = gtk_menu_new ();
+
+ /* create the menu items */
+ initial_index = 0;
+
+ va_start (args, initial);
+ label = va_arg (args, const gchar *);
+
+ for (i = 0; label; i++)
+ {
+ item_data = va_arg (args, gpointer);
+ widget_ptr = va_arg (args, GtkWidget **);
+
+ if (strcmp (label, "---"))
+ {
+ menuitem = gtk_menu_item_new_with_label (label);
+
+ g_signal_connect (menuitem, "activate",
+ menu_item_callback,
+ callback_data);
+
+ if (item_data)
+ {
+ g_object_set_data (G_OBJECT (menuitem), "gimp-item-data",
+ item_data);
+
+ /* backward compat */
+ g_object_set_data (G_OBJECT (menuitem), "user_data", item_data);
+ }
+ }
+ else
+ {
+ menuitem = gtk_menu_item_new ();
+
+ gtk_widget_set_sensitive (menuitem, FALSE);
+ }
+
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+
+ if (widget_ptr)
+ *widget_ptr = menuitem;
+
+ gtk_widget_show (menuitem);
+
+ /* remember the initial menu item */
+ if (item_data == initial)
+ initial_index = i;
+
+ label = va_arg (args, const gchar *);
+ }
+ va_end (args);
+
+ if (! menu_only)
+ {
+ GtkWidget *optionmenu;
+
+ optionmenu = gtk_option_menu_new ();
+ gtk_option_menu_set_menu (GTK_OPTION_MENU (optionmenu), menu);
+
+ /* select the initial menu item */
+ gtk_option_menu_set_history (GTK_OPTION_MENU (optionmenu), initial_index);
+
+ return optionmenu;
+ }
+
+ return menu;
+}
+
+/**
+ * gimp_int_option_menu_new:
+ * @menu_only: %TRUE if the function should return a #GtkMenu only.
+ * @menu_item_callback: The callback each menu item's "activate" signal will
+ * be connected with.
+ * @menu_item_callback_data:
+ * The data which will be passed to g_signal_connect().
+ * @initial: The @item_data of the initially selected menu item.
+ * @...: A %NULL-terminated @va_list describing the menu items.
+ *
+ * Convenience function to create a #GtkOptionMenu or a #GtkMenu. This
+ * function does the same thing as the deprecated function
+ * gimp_option_menu_new2(), but it takes integers as @item_data
+ * instead of pointers, since that is a very common case (mapping an
+ * enum to a menu).
+ *
+ * Returns: A #GtkOptionMenu or a #GtkMenu (depending on @menu_only).
+ **/
+GtkWidget *
+gimp_int_option_menu_new (gboolean menu_only,
+ GCallback menu_item_callback,
+ gpointer callback_data,
+ gint initial, /* item_data */
+
+ /* specify menu items as va_list:
+ * const gchar *label,
+ * gint item_data,
+ * GtkWidget **widget_ptr,
+ */
+
+ ...)
+{
+ GtkWidget *menu;
+ GtkWidget *menuitem;
+
+ /* menu item variables */
+ const gchar *label;
+ gint item_data;
+ gpointer item_ptr;
+ GtkWidget **widget_ptr;
+
+ va_list args;
+ gint i;
+ gint initial_index;
+
+ menu = gtk_menu_new ();
+
+ /* create the menu items */
+ initial_index = 0;
+
+ va_start (args, initial);
+ label = va_arg (args, const gchar *);
+
+ for (i = 0; label; i++)
+ {
+ item_data = va_arg (args, gint);
+ widget_ptr = va_arg (args, GtkWidget **);
+
+ item_ptr = GINT_TO_POINTER (item_data);
+
+ if (strcmp (label, "---"))
+ {
+ menuitem = gtk_menu_item_new_with_label (label);
+
+ g_signal_connect (menuitem, "activate",
+ menu_item_callback,
+ callback_data);
+
+ if (item_data)
+ {
+ g_object_set_data (G_OBJECT (menuitem), "gimp-item-data",
+ item_ptr);
+
+ /* backward compat */
+ g_object_set_data (G_OBJECT (menuitem), "user_data", item_ptr);
+ }
+ }
+ else
+ {
+ menuitem = gtk_menu_item_new ();
+
+ gtk_widget_set_sensitive (menuitem, FALSE);
+ }
+
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+
+ if (widget_ptr)
+ *widget_ptr = menuitem;
+
+ gtk_widget_show (menuitem);
+
+ /* remember the initial menu item */
+ if (item_data == initial)
+ initial_index = i;
+
+ label = va_arg (args, const gchar *);
+ }
+ va_end (args);
+
+ if (! menu_only)
+ {
+ GtkWidget *optionmenu;
+
+ optionmenu = gtk_option_menu_new ();
+ gtk_option_menu_set_menu (GTK_OPTION_MENU (optionmenu), menu);
+
+ /* select the initial menu item */
+ gtk_option_menu_set_history (GTK_OPTION_MENU (optionmenu), initial_index);
+
+ return optionmenu;
+ }
+
+ return menu;
+}
+
+/**
+ * gimp_option_menu_set_history:
+ * @option_menu: A #GtkOptionMenu as returned by gimp_option_menu_new() or
+ * gimp_option_menu_new2().
+ * @item_data: The @item_data of the menu item you want to select.
+ *
+ * Iterates over all entries in a #GtkOptionMenu and selects the one
+ * with the matching @item_data. Probably only makes sense to use with
+ * a #GtkOptionMenu that was created using gimp_option_menu_new() or
+ * gimp_option_menu_new2().
+ **/
+void
+gimp_option_menu_set_history (GtkOptionMenu *option_menu,
+ gpointer item_data)
+{
+ GList *children;
+ GList *list;
+ gint history = 0;
+
+ g_return_if_fail (GTK_IS_OPTION_MENU (option_menu));
+
+ children = gtk_container_get_children (GTK_CONTAINER (option_menu->menu));
+
+ for (list = children; list; list = g_list_next (list))
+ {
+ GtkWidget *menu_item = GTK_WIDGET (list->data);
+
+ if (GTK_IS_LABEL (gtk_bin_get_child (GTK_BIN (menu_item))) &&
+ g_object_get_data (G_OBJECT (menu_item),
+ "gimp-item-data") == item_data)
+ {
+ break;
+ }
+
+ history++;
+ }
+
+ if (list)
+ gtk_option_menu_set_history (option_menu, history);
+
+ g_list_free (children);
+}
+
+/**
+ * gimp_int_option_menu_set_history:
+ * @option_menu: A #GtkOptionMenu as returned by gimp_int_option_menu_new().
+ * @item_data: The @item_data of the menu item you want to select.
+ *
+ * Iterates over all entries in a #GtkOptionMenu and selects the one with the
+ * matching @item_data. Probably only makes sense to use with a #GtkOptionMenu
+ * that was created using gimp_int_option_menu_new(). This function does the
+ * same thing as gimp_option_menu_set_history(), but takes integers as
+ * @item_data instead of pointers.
+ **/
+void
+gimp_int_option_menu_set_history (GtkOptionMenu *option_menu,
+ gint item_data)
+{
+ g_return_if_fail (GTK_IS_OPTION_MENU (option_menu));
+
+ gimp_option_menu_set_history (option_menu, GINT_TO_POINTER (item_data));
+}
+
+/**
+ * gimp_option_menu_set_sensitive:
+ * @option_menu: a #GtkOptionMenu as returned by gimp_option_menu_new() or
+ * gimp_option_menu_new2().
+ * @callback: a function called for each item in the menu to determine the
+ * the sensitivity state.
+ * @callback_data: data to pass to the @callback function.
+ *
+ * Calls the given @callback for each item in the menu and passes it the
+ * item_data and the @callback_data. The menu item's sensitivity is set
+ * according to the return value of this function.
+ **/
+void
+gimp_option_menu_set_sensitive (GtkOptionMenu *option_menu,
+ GimpOptionMenuSensitivityCallback callback,
+ gpointer callback_data)
+{
+ GList *children;
+ GList *list;
+
+ g_return_if_fail (GTK_IS_OPTION_MENU (option_menu));
+ g_return_if_fail (callback != NULL);
+
+ children = gtk_container_get_children (GTK_CONTAINER (option_menu->menu));
+
+ for (list = children; list; list = g_list_next (list))
+ {
+ GtkWidget *menu_item = GTK_WIDGET (list->data);
+
+ if (GTK_IS_LABEL (gtk_bin_get_child (GTK_BIN (menu_item))))
+ {
+ gpointer item_data;
+ gboolean sensitive;
+
+ item_data = g_object_get_data (G_OBJECT (menu_item),
+ "gimp-item-data");
+ sensitive = callback (item_data, callback_data);
+ gtk_widget_set_sensitive (menu_item, sensitive);
+ }
+ }
+
+ g_list_free (children);
+}
+
+/**
+ * gimp_int_option_menu_set_sensitive:
+ * @option_menu: a #GtkOptionMenu as returned by gimp_option_menu_new() or
+ * gimp_option_menu_new2().
+ * @callback: a function called for each item in the menu to determine the
+ * the sensitivity state.
+ * @callback_data: data to pass to the @callback function.
+ *
+ * Calls the given @callback for each item in the menu and passes it the
+ * item_data and the @callback_data. The menu item's sensitivity is set
+ * according to the return value of this function. This function does the
+ * same thing as gimp_option_menu_set_sensitive(), but takes integers as
+ * @item_data instead of pointers.
+ **/
+void
+gimp_int_option_menu_set_sensitive (GtkOptionMenu *option_menu,
+ GimpIntOptionMenuSensitivityCallback callback,
+ gpointer callback_data)
+{
+ GList *children;
+ GList *list;
+
+ g_return_if_fail (GTK_IS_OPTION_MENU (option_menu));
+ g_return_if_fail (callback != NULL);
+
+ children = gtk_container_get_children (GTK_CONTAINER (option_menu->menu));
+
+ for (list = children; list; list = g_list_next (list))
+ {
+ GtkWidget *menu_item = GTK_WIDGET (list->data);
+
+ if (GTK_IS_LABEL (gtk_bin_get_child (GTK_BIN (menu_item))))
+ {
+ gint item_data;
+ gboolean sensitive;
+
+ item_data = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menu_item),
+ "gimp-item-data"));
+ sensitive = callback (item_data, callback_data);
+ gtk_widget_set_sensitive (menu_item, sensitive);
+ }
+ }
+
+ g_list_free (children);
+}
+
+
+/**
+ * gimp_menu_item_update:
+ * @widget: A #GtkMenuItem.
+ * @data: A pointer to a #gint variable which will store the value of
+ * GPOINTER_TO_INT (g_object_get_data (@widget, "gimp-item-data")).
+ **/
+void
+gimp_menu_item_update (GtkWidget *widget,
+ gpointer data)
+{
+ gint *item_val = (gint *) data;
+
+ *item_val = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
+ "gimp-item-data"));
+}
+
+
+/**
+ * gimp_pixmap_button_new:
+ * @xpm_data: The XPM data which will be passed to gimp_pixmap_new().
+ * @text: An optional text which will appear right of the pixmap.
+ *
+ * Convenience function that creates a #GtkButton with a #GimpPixmap
+ * and an optional #GtkLabel.
+ *
+ * Returns: The new #GtkButton.
+ **/
+GtkWidget *
+gimp_pixmap_button_new (gchar **xpm_data,
+ const gchar *text)
+{
+ GtkWidget *button;
+ GtkWidget *pixmap;
+
+ button = gtk_button_new ();
+ pixmap = gimp_pixmap_new (xpm_data);
+
+ if (text)
+ {
+ GtkWidget *abox;
+ GtkWidget *hbox;
+ GtkWidget *label;
+
+ abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
+ gtk_container_add (GTK_CONTAINER (button), abox);
+ gtk_widget_show (abox);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_container_add (GTK_CONTAINER (abox), hbox);
+ gtk_widget_show (hbox);
+
+ gtk_box_pack_start (GTK_BOX (hbox), pixmap, FALSE, FALSE, 4);
+ gtk_widget_show (pixmap);
+
+ label = gtk_label_new_with_mnemonic (text);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), button);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 4);
+ gtk_widget_show (label);
+ }
+ else
+ {
+ gtk_container_add (GTK_CONTAINER (button), pixmap);
+ gtk_widget_show (pixmap);
+ }
+
+
+ return button;
+}
+
+
+/**
+ * gimp_unit_menu_update:
+ * @widget: A #GimpUnitMenu.
+ * @data: A pointer to a #GimpUnit variable which will store the unit menu's
+ * value.
+ *
+ * This callback can set the number of decimal digits of an arbitrary number
+ * of #GtkSpinButton's. To use this functionality, attach the spinbuttons
+ * as list of data pointers attached with g_object_set_data() with the
+ * "set_digits" key.
+ *
+ * See gimp_toggle_button_sensitive_update() for a description of how
+ * to set up the list.
+ *
+ * Deprecated: use #GimpUnitComboBox instead.
+ **/
+void
+gimp_unit_menu_update (GtkWidget *widget,
+ gpointer data)
+{
+ GimpUnit *val = (GimpUnit *) data;
+ GtkWidget *spinbutton;
+ gint digits;
+
+ *val = gimp_unit_menu_get_unit (GIMP_UNIT_MENU (widget));
+
+ digits = ((*val == GIMP_UNIT_PIXEL) ? 0 :
+ ((*val == GIMP_UNIT_PERCENT) ? 2 :
+ (MIN (6, MAX (3, gimp_unit_get_digits (*val))))));
+
+ digits += gimp_unit_menu_get_pixel_digits (GIMP_UNIT_MENU (widget));
+
+ spinbutton = g_object_get_data (G_OBJECT (widget), "set_digits");
+ while (spinbutton)
+ {
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (spinbutton), digits);
+ spinbutton = g_object_get_data (G_OBJECT (spinbutton), "set_digits");
+ }
+}
diff --git a/libgimpwidgets/gimpoldwidgets.h b/libgimpwidgets/gimpoldwidgets.h
new file mode 100644
index 0000000..3c7a525
--- /dev/null
+++ b/libgimpwidgets/gimpoldwidgets.h
@@ -0,0 +1,122 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpoldwidgets.h
+ * Copyright (C) 2000 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* These functions are deprecated and should not be used in newly
+ * written code.
+ */
+
+#ifndef GIMP_DISABLE_DEPRECATED
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_OLD_WIDGETS_H__
+#define __GIMP_OLD_WIDGETS_H__
+
+G_BEGIN_DECLS
+
+
+/*
+ * Widget Constructors
+ */
+
+GtkWidget * gimp_int_option_menu_new (gboolean menu_only,
+ GCallback menu_item_callback,
+ gpointer menu_item_callback_data,
+ gint initial, /* item_data */
+
+ /* specify menu items as va_list:
+ * gchar *label,
+ * gint item_data,
+ * GtkWidget **widget_ptr,
+ */
+
+ ...) G_GNUC_NULL_TERMINATED;
+
+void gimp_int_option_menu_set_history (GtkOptionMenu *option_menu,
+ gint item_data);
+
+typedef gboolean (* GimpIntOptionMenuSensitivityCallback) (gint item_data,
+ gpointer callback_data);
+
+void gimp_int_option_menu_set_sensitive (GtkOptionMenu *option_menu,
+ GimpIntOptionMenuSensitivityCallback callback,
+ gpointer callback_data);
+
+
+GtkWidget * gimp_option_menu_new (gboolean menu_only,
+
+ /* specify menu items as va_list:
+ * gchar *label,
+ * GCallback callback,
+ * gpointer callback_data,
+ * gpointer item_data,
+ * GtkWidget **widget_ptr,
+ * gboolean active
+ */
+
+ ...) G_GNUC_NULL_TERMINATED;
+GtkWidget * gimp_option_menu_new2 (gboolean menu_only,
+ GCallback menu_item_callback,
+ gpointer menu_item_callback_data,
+ gpointer initial, /* item_data */
+
+ /* specify menu items as va_list:
+ * gchar *label,
+ * gpointer item_data,
+ * GtkWidget **widget_ptr,
+ */
+ ...) G_GNUC_NULL_TERMINATED;
+void gimp_option_menu_set_history (GtkOptionMenu *option_menu,
+ gpointer item_data);
+
+
+typedef gboolean (* GimpOptionMenuSensitivityCallback) (gpointer item_data,
+ gpointer callback_data);
+
+void gimp_option_menu_set_sensitive (GtkOptionMenu *option_menu,
+ GimpOptionMenuSensitivityCallback callback,
+ gpointer callback_data);
+
+
+void gimp_menu_item_update (GtkWidget *widget,
+ gpointer data);
+
+GtkWidget * gimp_pixmap_button_new (gchar **xpm_data,
+ const gchar *text);
+
+
+/*
+ * Standard Callbacks
+ */
+
+void gimp_toggle_button_sensitive_update (GtkToggleButton *toggle_button);
+
+void gimp_unit_menu_update (GtkWidget *widget,
+ gpointer data);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_OLD_WIDGETS_H__ */
+
+#endif /* GIMP_DISABLE_DEPRECATED */
diff --git a/libgimpwidgets/gimppageselector.c b/libgimpwidgets/gimppageselector.c
new file mode 100644
index 0000000..c882781
--- /dev/null
+++ b/libgimpwidgets/gimppageselector.c
@@ -0,0 +1,1327 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppageselector.c
+ * Copyright (C) 2005 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpicons.h"
+#include "gimppageselector.h"
+#include "gimppropwidgets.h"
+#include "gimpwidgets.h"
+#include "gimp3migration.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimppageselector
+ * @title: GimpPageSelector
+ * @short_description: A widget to select pages from multi-page things.
+ *
+ * Use this for example for specifying what pages to import from
+ * a PDF or PS document.
+ **/
+
+
+enum
+{
+ SELECTION_CHANGED,
+ ACTIVATE,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_N_PAGES,
+ PROP_TARGET
+};
+
+enum
+{
+ COLUMN_PAGE_NO,
+ COLUMN_THUMBNAIL,
+ COLUMN_LABEL,
+ COLUMN_LABEL_SET
+};
+
+
+typedef struct
+{
+ gint n_pages;
+ GimpPageSelectorTarget target;
+
+ GtkListStore *store;
+ GtkWidget *view;
+
+ GtkWidget *count_label;
+ GtkWidget *range_entry;
+
+ GdkPixbuf *default_thumbnail;
+} GimpPageSelectorPrivate;
+
+#define GIMP_PAGE_SELECTOR_GET_PRIVATE(obj) \
+ ((GimpPageSelectorPrivate *) ((GimpPageSelector *) (obj))->priv)
+
+
+static void gimp_page_selector_finalize (GObject *object);
+static void gimp_page_selector_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_page_selector_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_page_selector_selection_changed (GtkIconView *icon_view,
+ GimpPageSelector *selector);
+static void gimp_page_selector_item_activated (GtkIconView *icon_view,
+ GtkTreePath *path,
+ GimpPageSelector *selector);
+static gboolean gimp_page_selector_range_focus_out (GtkEntry *entry,
+ GdkEventFocus *fevent,
+ GimpPageSelector *selector);
+static void gimp_page_selector_range_activate (GtkEntry *entry,
+ GimpPageSelector *selector);
+static gint gimp_page_selector_int_compare (gconstpointer a,
+ gconstpointer b);
+static void gimp_page_selector_print_range (GString *string,
+ gint start,
+ gint end);
+
+static GdkPixbuf * gimp_page_selector_add_frame (GtkWidget *widget,
+ GdkPixbuf *pixbuf);
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpPageSelector, gimp_page_selector, GTK_TYPE_BOX)
+
+#define parent_class gimp_page_selector_parent_class
+
+static guint selector_signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+gimp_page_selector_class_init (GimpPageSelectorClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = gimp_page_selector_finalize;
+ object_class->get_property = gimp_page_selector_get_property;
+ object_class->set_property = gimp_page_selector_set_property;
+
+ klass->selection_changed = NULL;
+ klass->activate = NULL;
+
+ /**
+ * GimpPageSelector::selection-changed:
+ * @widget: the object which received the signal.
+ *
+ * This signal is emitted whenever the set of selected pages changes.
+ *
+ * Since: 2.4
+ **/
+ selector_signals[SELECTION_CHANGED] =
+ g_signal_new ("selection-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpPageSelectorClass, selection_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ /**
+ * GimpPageSelector::activate:
+ * @widget: the object which received the signal.
+ *
+ * The "activate" signal on GimpPageSelector is an action signal. It
+ * is emitted when a user double-clicks an item in the page selection.
+ *
+ * Since: 2.4
+ */
+ selector_signals[ACTIVATE] =
+ g_signal_new ("activate",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (GimpPageSelectorClass, activate),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ widget_class->activate_signal = selector_signals[ACTIVATE];
+
+ /**
+ * GimpPageSelector:n-pages:
+ *
+ * The number of pages of the document to open.
+ *
+ * Since: 2.4
+ **/
+ g_object_class_install_property (object_class, PROP_N_PAGES,
+ g_param_spec_int ("n-pages",
+ "N Pages",
+ "The number of pages to open",
+ 0, G_MAXINT, 0,
+ GIMP_PARAM_READWRITE));
+
+ /**
+ * GimpPageSelector:target:
+ *
+ * The target to open the document to.
+ *
+ * Since: 2.4
+ **/
+ g_object_class_install_property (object_class, PROP_TARGET,
+ g_param_spec_enum ("target",
+ "Target",
+ "the target to open to",
+ GIMP_TYPE_PAGE_SELECTOR_TARGET,
+ GIMP_PAGE_SELECTOR_TARGET_LAYERS,
+ GIMP_PARAM_READWRITE));
+}
+
+static void
+gimp_page_selector_init (GimpPageSelector *selector)
+{
+ GimpPageSelectorPrivate *priv;
+ GtkWidget *vbox;
+ GtkWidget *sw;
+ GtkWidget *hbox;
+ GtkWidget *hbbox;
+ GtkWidget *button;
+ GtkWidget *label;
+ GtkWidget *combo;
+
+ selector->priv = gimp_page_selector_get_instance_private (selector);
+
+ priv = GIMP_PAGE_SELECTOR_GET_PRIVATE (selector);
+
+ priv->n_pages = 0;
+ priv->target = GIMP_PAGE_SELECTOR_TARGET_LAYERS;
+
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (selector),
+ GTK_ORIENTATION_VERTICAL);
+
+ gtk_box_set_spacing (GTK_BOX (selector), 12);
+
+ /* Pages */
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_box_pack_start (GTK_BOX (selector), vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ sw = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+ GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
+ gtk_widget_show (sw);
+
+ priv->store = gtk_list_store_new (4,
+ G_TYPE_INT,
+ GDK_TYPE_PIXBUF,
+ G_TYPE_STRING,
+ G_TYPE_BOOLEAN);
+
+ priv->view = gtk_icon_view_new_with_model (GTK_TREE_MODEL (priv->store));
+ gtk_icon_view_set_text_column (GTK_ICON_VIEW (priv->view),
+ COLUMN_LABEL);
+ gtk_icon_view_set_pixbuf_column (GTK_ICON_VIEW (priv->view),
+ COLUMN_THUMBNAIL);
+ gtk_icon_view_set_selection_mode (GTK_ICON_VIEW (priv->view),
+ GTK_SELECTION_MULTIPLE);
+ gtk_container_add (GTK_CONTAINER (sw), priv->view);
+ gtk_widget_show (priv->view);
+
+ g_signal_connect (priv->view, "selection-changed",
+ G_CALLBACK (gimp_page_selector_selection_changed),
+ selector);
+ g_signal_connect (priv->view, "item-activated",
+ G_CALLBACK (gimp_page_selector_item_activated),
+ selector);
+
+ /* Count label */
+
+ priv->count_label = gtk_label_new (_("Nothing selected"));
+ gtk_label_set_xalign (GTK_LABEL (priv->count_label), 0.0);
+ gimp_label_set_attributes (GTK_LABEL (priv->count_label),
+ PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
+ -1);
+ gtk_box_pack_start (GTK_BOX (vbox), priv->count_label, FALSE, FALSE, 0);
+ gtk_widget_show (priv->count_label);
+
+ /* Select all button & range entry */
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (selector), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ hbbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
+ gtk_box_pack_start (GTK_BOX (hbox), hbbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbbox);
+
+ button = gtk_button_new_with_mnemonic (_("Select _All"));
+ gtk_box_pack_start (GTK_BOX (hbbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (gimp_page_selector_select_all),
+ selector);
+
+ priv->range_entry = gtk_entry_new ();
+ gtk_widget_set_size_request (priv->range_entry, 80, -1);
+ gtk_box_pack_end (GTK_BOX (hbox), priv->range_entry, TRUE, TRUE, 0);
+ gtk_widget_show (priv->range_entry);
+
+ g_signal_connect (priv->range_entry, "focus-out-event",
+ G_CALLBACK (gimp_page_selector_range_focus_out),
+ selector);
+ g_signal_connect (priv->range_entry, "activate",
+ G_CALLBACK (gimp_page_selector_range_activate),
+ selector);
+
+ label = gtk_label_new_with_mnemonic (_("Select _range:"));
+ gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), priv->range_entry);
+
+ /* Target combo */
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (selector), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ label = gtk_label_new_with_mnemonic (_("Open _pages as"));
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ combo = gimp_prop_enum_combo_box_new (G_OBJECT (selector), "target", -1, -1);
+ gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0);
+ gtk_widget_show (combo);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);
+
+ priv->default_thumbnail =
+ gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
+ "text-x-generic", 32, 0, NULL);
+}
+
+static void
+gimp_page_selector_finalize (GObject *object)
+{
+ GimpPageSelectorPrivate *priv = GIMP_PAGE_SELECTOR_GET_PRIVATE (object);
+
+ g_clear_object (&priv->default_thumbnail);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_page_selector_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpPageSelectorPrivate *priv = GIMP_PAGE_SELECTOR_GET_PRIVATE (object);
+
+ switch (property_id)
+ {
+ case PROP_N_PAGES:
+ g_value_set_int (value, priv->n_pages);
+ break;
+ case PROP_TARGET:
+ g_value_set_enum (value, priv->target);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_page_selector_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpPageSelector *selector = GIMP_PAGE_SELECTOR (object);
+ GimpPageSelectorPrivate *priv = GIMP_PAGE_SELECTOR_GET_PRIVATE (object);
+
+ switch (property_id)
+ {
+ case PROP_N_PAGES:
+ gimp_page_selector_set_n_pages (selector, g_value_get_int (value));
+ break;
+ case PROP_TARGET:
+ priv->target = g_value_get_enum (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+
+/* public functions */
+
+/**
+ * gimp_page_selector_new:
+ *
+ * Creates a new #GimpPageSelector widget.
+ *
+ * Returns: Pointer to the new #GimpPageSelector widget.
+ *
+ * Since: 2.4
+ **/
+GtkWidget *
+gimp_page_selector_new (void)
+{
+ return g_object_new (GIMP_TYPE_PAGE_SELECTOR, NULL);
+}
+
+/**
+ * gimp_page_selector_set_n_pages:
+ * @selector: Pointer to a #GimpPageSelector.
+ * @n_pages: The number of pages.
+ *
+ * Sets the number of pages in the document to open.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_page_selector_set_n_pages (GimpPageSelector *selector,
+ gint n_pages)
+{
+ GimpPageSelectorPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_PAGE_SELECTOR (selector));
+ g_return_if_fail (n_pages >= 0);
+
+ priv = GIMP_PAGE_SELECTOR_GET_PRIVATE (selector);
+
+ if (n_pages != priv->n_pages)
+ {
+ GtkTreeIter iter;
+ gint i;
+
+ if (n_pages < priv->n_pages)
+ {
+ for (i = n_pages; i < priv->n_pages; i++)
+ {
+ gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (priv->store),
+ &iter, NULL, n_pages);
+ gtk_list_store_remove (priv->store, &iter);
+ }
+ }
+ else
+ {
+ for (i = priv->n_pages; i < n_pages; i++)
+ {
+ gchar *text;
+
+ text = g_strdup_printf (_("Page %d"), i + 1);
+
+ gtk_list_store_append (priv->store, &iter);
+ gtk_list_store_set (priv->store, &iter,
+ COLUMN_PAGE_NO, i,
+ COLUMN_THUMBNAIL, priv->default_thumbnail,
+ COLUMN_LABEL, text,
+ COLUMN_LABEL_SET, FALSE,
+ -1);
+
+ g_free (text);
+ }
+ }
+
+ priv->n_pages = n_pages;
+
+ g_object_notify (G_OBJECT (selector), "n-pages");
+ }
+}
+
+/**
+ * gimp_page_selector_get_n_pages:
+ * @selector: Pointer to a #GimpPageSelector.
+ *
+ * Returns: the number of pages in the document to open.
+ *
+ * Since: 2.4
+ **/
+gint
+gimp_page_selector_get_n_pages (GimpPageSelector *selector)
+{
+ GimpPageSelectorPrivate *priv;
+
+ g_return_val_if_fail (GIMP_IS_PAGE_SELECTOR (selector), 0);
+
+ priv = GIMP_PAGE_SELECTOR_GET_PRIVATE (selector);
+
+ return priv->n_pages;
+}
+
+/**
+ * gimp_page_selector_set_target:
+ * @selector: Pointer to a #GimpPageSelector.
+ * @target: How to open the selected pages.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_page_selector_set_target (GimpPageSelector *selector,
+ GimpPageSelectorTarget target)
+{
+ GimpPageSelectorPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_PAGE_SELECTOR (selector));
+ g_return_if_fail (target <= GIMP_PAGE_SELECTOR_TARGET_IMAGES);
+
+ priv = GIMP_PAGE_SELECTOR_GET_PRIVATE (selector);
+
+ if (target != priv->target)
+ {
+ priv->target = target;
+
+ g_object_notify (G_OBJECT (selector), "target");
+ }
+}
+
+/**
+ * gimp_page_selector_get_target:
+ * @selector: Pointer to a #GimpPageSelector.
+ *
+ * Returns: How the selected pages should be opened.
+ *
+ * Since: 2.4
+ **/
+GimpPageSelectorTarget
+gimp_page_selector_get_target (GimpPageSelector *selector)
+{
+ GimpPageSelectorPrivate *priv;
+
+ g_return_val_if_fail (GIMP_IS_PAGE_SELECTOR (selector),
+ GIMP_PAGE_SELECTOR_TARGET_LAYERS);
+
+ priv = GIMP_PAGE_SELECTOR_GET_PRIVATE (selector);
+
+ return priv->target;
+}
+
+/**
+ * gimp_page_selector_set_page_thumbnail:
+ * @selector: Pointer to a #GimpPageSelector.
+ * @page_no: The number of the page to set the thumbnail for.
+ * @thumbnail: The thumbnail pixbuf.
+ *
+ * Sets the thumbnail for given @page_no. A default "page" icon will
+ * be used if no page thumbnail is set.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_page_selector_set_page_thumbnail (GimpPageSelector *selector,
+ gint page_no,
+ GdkPixbuf *thumbnail)
+{
+ GimpPageSelectorPrivate *priv;
+ GtkTreeIter iter;
+
+ g_return_if_fail (GIMP_IS_PAGE_SELECTOR (selector));
+ g_return_if_fail (thumbnail == NULL || GDK_IS_PIXBUF (thumbnail));
+
+ priv = GIMP_PAGE_SELECTOR_GET_PRIVATE (selector);
+
+ g_return_if_fail (page_no >= 0 && page_no < priv->n_pages);
+
+ gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (priv->store),
+ &iter, NULL, page_no);
+
+ if (! thumbnail)
+ {
+ thumbnail = g_object_ref (priv->default_thumbnail);
+ }
+ else
+ {
+ thumbnail = gimp_page_selector_add_frame (GTK_WIDGET (selector),
+ thumbnail);
+ }
+
+ gtk_list_store_set (priv->store, &iter,
+ COLUMN_THUMBNAIL, thumbnail,
+ -1);
+ g_clear_object (&thumbnail);
+}
+
+/**
+ * gimp_page_selector_get_page_thumbnail:
+ * @selector: Pointer to a #GimpPageSelector.
+ * @page_no: The number of the page to get the thumbnail for.
+ *
+ * Returns: The page's thumbnail, or %NULL if none is set. The returned
+ * pixbuf is owned by #GimpPageSelector and must not be
+ * unref'ed when no longer needed.
+ *
+ * Since: 2.4
+ **/
+GdkPixbuf *
+gimp_page_selector_get_page_thumbnail (GimpPageSelector *selector,
+ gint page_no)
+{
+ GimpPageSelectorPrivate *priv;
+ GdkPixbuf *thumbnail;
+ GtkTreeIter iter;
+
+ g_return_val_if_fail (GIMP_IS_PAGE_SELECTOR (selector), NULL);
+
+ priv = GIMP_PAGE_SELECTOR_GET_PRIVATE (selector);
+
+ g_return_val_if_fail (page_no >= 0 && page_no < priv->n_pages, NULL);
+
+ gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (priv->store),
+ &iter, NULL, page_no);
+ gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter,
+ COLUMN_THUMBNAIL, &thumbnail,
+ -1);
+
+ if (thumbnail)
+ g_object_unref (thumbnail);
+
+ if (thumbnail == priv->default_thumbnail)
+ return NULL;
+
+ return thumbnail;
+}
+
+/**
+ * gimp_page_selector_set_page_label:
+ * @selector: Pointer to a #GimpPageSelector.
+ * @page_no: The number of the page to set the label for.
+ * @label: The label.
+ *
+ * Sets the label of the specified page.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_page_selector_set_page_label (GimpPageSelector *selector,
+ gint page_no,
+ const gchar *label)
+{
+ GimpPageSelectorPrivate *priv;
+ GtkTreeIter iter;
+ gchar *tmp;
+
+ g_return_if_fail (GIMP_IS_PAGE_SELECTOR (selector));
+
+ priv = GIMP_PAGE_SELECTOR_GET_PRIVATE (selector);
+
+ g_return_if_fail (page_no >= 0 && page_no < priv->n_pages);
+
+ if (! label)
+ tmp = g_strdup_printf (_("Page %d"), page_no + 1);
+ else
+ tmp = (gchar *) label;
+
+ gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (priv->store),
+ &iter, NULL, page_no);
+ gtk_list_store_set (priv->store, &iter,
+ COLUMN_LABEL, tmp,
+ COLUMN_LABEL_SET, label != NULL,
+ -1);
+
+ if (! label)
+ g_free (tmp);
+}
+
+/**
+ * gimp_page_selector_get_page_label:
+ * @selector: Pointer to a #GimpPageSelector.
+ * @page_no: The number of the page to get the thumbnail for.
+ *
+ * Returns: The page's label, or %NULL if none is set. This is a newly
+ * allocated string that should be g_free()'d when no longer
+ * needed.
+ *
+ * Since: 2.4
+ **/
+gchar *
+gimp_page_selector_get_page_label (GimpPageSelector *selector,
+ gint page_no)
+{
+ GimpPageSelectorPrivate *priv;
+ GtkTreeIter iter;
+ gchar *label;
+ gboolean label_set;
+
+ g_return_val_if_fail (GIMP_IS_PAGE_SELECTOR (selector), NULL);
+
+ priv = GIMP_PAGE_SELECTOR_GET_PRIVATE (selector);
+
+ g_return_val_if_fail (page_no >= 0 && page_no < priv->n_pages, NULL);
+
+ gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (priv->store),
+ &iter, NULL, page_no);
+ gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter,
+ COLUMN_LABEL, &label,
+ COLUMN_LABEL_SET, &label_set,
+ -1);
+
+ if (! label_set)
+ {
+ g_free (label);
+ label = NULL;
+ }
+
+ return label;
+}
+
+/**
+ * gimp_page_selector_select_all:
+ * @selector: Pointer to a #GimpPageSelector.
+ *
+ * Selects all pages.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_page_selector_select_all (GimpPageSelector *selector)
+{
+ GimpPageSelectorPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_PAGE_SELECTOR (selector));
+
+ priv = GIMP_PAGE_SELECTOR_GET_PRIVATE (selector);
+
+ gtk_icon_view_select_all (GTK_ICON_VIEW (priv->view));
+}
+
+/**
+ * gimp_page_selector_unselect_all:
+ * @selector: Pointer to a #GimpPageSelector.
+ *
+ * Unselects all pages.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_page_selector_unselect_all (GimpPageSelector *selector)
+{
+ GimpPageSelectorPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_PAGE_SELECTOR (selector));
+
+ priv = GIMP_PAGE_SELECTOR_GET_PRIVATE (selector);
+
+ gtk_icon_view_unselect_all (GTK_ICON_VIEW (priv->view));
+}
+
+/**
+ * gimp_page_selector_select_page:
+ * @selector: Pointer to a #GimpPageSelector.
+ * @page_no: The number of the page to select.
+ *
+ * Adds a page to the selection.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_page_selector_select_page (GimpPageSelector *selector,
+ gint page_no)
+{
+ GimpPageSelectorPrivate *priv;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+
+ g_return_if_fail (GIMP_IS_PAGE_SELECTOR (selector));
+
+ priv = GIMP_PAGE_SELECTOR_GET_PRIVATE (selector);
+
+ g_return_if_fail (page_no >= 0 && page_no < priv->n_pages);
+
+ gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (priv->store),
+ &iter, NULL, page_no);
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->store), &iter);
+
+ gtk_icon_view_select_path (GTK_ICON_VIEW (priv->view), path);
+
+ gtk_tree_path_free (path);
+}
+
+/**
+ * gimp_page_selector_unselect_page:
+ * @selector: Pointer to a #GimpPageSelector.
+ * @page_no: The number of the page to unselect.
+ *
+ * Removes a page from the selection.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_page_selector_unselect_page (GimpPageSelector *selector,
+ gint page_no)
+{
+ GimpPageSelectorPrivate *priv;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+
+ g_return_if_fail (GIMP_IS_PAGE_SELECTOR (selector));
+
+ priv = GIMP_PAGE_SELECTOR_GET_PRIVATE (selector);
+
+ g_return_if_fail (page_no >= 0 && page_no < priv->n_pages);
+
+ gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (priv->store),
+ &iter, NULL, page_no);
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->store), &iter);
+
+ gtk_icon_view_unselect_path (GTK_ICON_VIEW (priv->view), path);
+
+ gtk_tree_path_free (path);
+}
+
+/**
+ * gimp_page_selector_page_is_selected:
+ * @selector: Pointer to a #GimpPageSelector.
+ * @page_no: The number of the page to check.
+ *
+ * Returns: %TRUE if the page is selected, %FALSE otherwise.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_page_selector_page_is_selected (GimpPageSelector *selector,
+ gint page_no)
+{
+ GimpPageSelectorPrivate *priv;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ gboolean selected;
+
+ g_return_val_if_fail (GIMP_IS_PAGE_SELECTOR (selector), FALSE);
+
+ priv = GIMP_PAGE_SELECTOR_GET_PRIVATE (selector);
+
+ g_return_val_if_fail (page_no >= 0 && page_no < priv->n_pages, FALSE);
+
+ gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (priv->store),
+ &iter, NULL, page_no);
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->store), &iter);
+
+ selected = gtk_icon_view_path_is_selected (GTK_ICON_VIEW (priv->view),
+ path);
+
+ gtk_tree_path_free (path);
+
+ return selected;
+}
+
+/**
+ * gimp_page_selector_get_selected_pages:
+ * @selector: Pointer to a #GimpPageSelector.
+ * @n_selected_pages: Returns the number of selected pages.
+ *
+ * Returns: A sorted array of page numbers of selected pages. Use g_free() if
+ * you don't need the array any longer.
+ *
+ * Since: 2.4
+ **/
+gint *
+gimp_page_selector_get_selected_pages (GimpPageSelector *selector,
+ gint *n_selected_pages)
+{
+ GimpPageSelectorPrivate *priv;
+ GList *selected;
+ GList *list;
+ gint *array;
+ gint i;
+
+ g_return_val_if_fail (GIMP_IS_PAGE_SELECTOR (selector), NULL);
+ g_return_val_if_fail (n_selected_pages != NULL, NULL);
+
+ priv = GIMP_PAGE_SELECTOR_GET_PRIVATE (selector);
+
+ selected = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (priv->view));
+
+ *n_selected_pages = g_list_length (selected);
+ array = g_new0 (gint, *n_selected_pages);
+
+ for (list = selected, i = 0; list; list = g_list_next (list), i++)
+ {
+ gint *indices = gtk_tree_path_get_indices (list->data);
+
+ array[i] = indices[0];
+ }
+
+ qsort (array, *n_selected_pages, sizeof (gint),
+ gimp_page_selector_int_compare);
+
+ g_list_free_full (selected, (GDestroyNotify) gtk_tree_path_free);
+
+ return array;
+}
+
+/**
+ * gimp_page_selector_select_range:
+ * @selector: Pointer to a #GimpPageSelector.
+ * @range: A string representing the set of selected pages.
+ *
+ * Selects the pages described by @range. The range string is a
+ * user-editable list of pages and ranges, e.g. "1,3,5-7,9-12,14".
+ * Note that the page numbering in the range string starts with 1,
+ * not 0.
+ *
+ * Invalid pages and ranges will be silently ignored, duplicate and
+ * overlapping pages and ranges will be merged.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_page_selector_select_range (GimpPageSelector *selector,
+ const gchar *range)
+{
+ GimpPageSelectorPrivate *priv;
+ gchar **ranges;
+
+ g_return_if_fail (GIMP_IS_PAGE_SELECTOR (selector));
+
+ priv = GIMP_PAGE_SELECTOR_GET_PRIVATE (selector);
+
+ if (! range)
+ range = "";
+
+ g_signal_handlers_block_by_func (priv->view,
+ gimp_page_selector_selection_changed,
+ selector);
+
+ gimp_page_selector_unselect_all (selector);
+
+ ranges = g_strsplit (range, ",", -1);
+
+ if (ranges)
+ {
+ gint i;
+
+ for (i = 0; ranges[i] != NULL; i++)
+ {
+ gchar *range = g_strstrip (ranges[i]);
+ gchar *dash;
+
+ dash = strchr (range, '-');
+
+ if (dash)
+ {
+ gchar *from;
+ gchar *to;
+ gint page_from = -1;
+ gint page_to = -1;
+
+ *dash = '\0';
+
+ from = g_strstrip (range);
+ to = g_strstrip (dash + 1);
+
+ if (sscanf (from, "%i", &page_from) != 1 && strlen (from) == 0)
+ page_from = 1;
+
+ if (sscanf (to, "%i", &page_to) != 1 && strlen (to) == 0)
+ page_to = priv->n_pages;
+
+ if (page_from > 0 &&
+ page_to > 0 &&
+ page_from <= page_to &&
+ page_from <= priv->n_pages)
+ {
+ gint page_no;
+
+ page_from = MAX (page_from, 1) - 1;
+ page_to = MIN (page_to, priv->n_pages) - 1;
+
+ for (page_no = page_from; page_no <= page_to; page_no++)
+ gimp_page_selector_select_page (selector, page_no);
+ }
+ }
+ else
+ {
+ gint page_no;
+
+ if (sscanf (range, "%i", &page_no) == 1 &&
+ page_no >= 1 &&
+ page_no <= priv->n_pages)
+ {
+ gimp_page_selector_select_page (selector, page_no - 1);
+ }
+ }
+ }
+
+ g_strfreev (ranges);
+ }
+
+ g_signal_handlers_unblock_by_func (priv->view,
+ gimp_page_selector_selection_changed,
+ selector);
+
+ gimp_page_selector_selection_changed (GTK_ICON_VIEW (priv->view), selector);
+}
+
+/**
+ * gimp_page_selector_get_selected_range:
+ * @selector: Pointer to a #GimpPageSelector.
+ *
+ * Returns: A newly allocated string representing the set of selected
+ * pages. See gimp_page_selector_select_range() for the
+ * format of the string.
+ *
+ * Since: 2.4
+ **/
+gchar *
+gimp_page_selector_get_selected_range (GimpPageSelector *selector)
+{
+ gint *pages;
+ gint n_pages;
+ GString *string;
+
+ g_return_val_if_fail (GIMP_IS_PAGE_SELECTOR (selector), NULL);
+
+ string = g_string_new ("");
+
+ pages = gimp_page_selector_get_selected_pages (selector, &n_pages);
+
+ if (pages)
+ {
+ gint range_start, range_end;
+ gint last_printed;
+ gint i;
+
+ range_start = pages[0];
+ range_end = pages[0];
+ last_printed = -1;
+
+ for (i = 1; i < n_pages; i++)
+ {
+ if (pages[i] > range_end + 1)
+ {
+ gimp_page_selector_print_range (string,
+ range_start, range_end);
+
+ last_printed = range_end;
+ range_start = pages[i];
+ }
+
+ range_end = pages[i];
+ }
+
+ if (range_end != last_printed)
+ gimp_page_selector_print_range (string, range_start, range_end);
+
+ g_free (pages);
+ }
+
+ return g_string_free (string, FALSE);
+}
+
+
+/* private functions */
+
+static void
+gimp_page_selector_selection_changed (GtkIconView *icon_view,
+ GimpPageSelector *selector)
+{
+ GimpPageSelectorPrivate *priv = GIMP_PAGE_SELECTOR_GET_PRIVATE (selector);
+ GList *selected;
+ gint n_selected;
+ gchar *range;
+
+ selected = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (priv->view));
+ n_selected = g_list_length (selected);
+ g_list_free_full (selected, (GDestroyNotify) gtk_tree_path_free);
+
+ if (n_selected == 0)
+ {
+ gtk_label_set_text (GTK_LABEL (priv->count_label),
+ _("Nothing selected"));
+ }
+ else if (n_selected == 1)
+ {
+ gtk_label_set_text (GTK_LABEL (priv->count_label),
+ _("One page selected"));
+ }
+ else
+ {
+ gchar *text;
+
+ if (n_selected == priv->n_pages)
+ text = g_strdup_printf (ngettext ("%d page selected",
+ "All %d pages selected", n_selected),
+ n_selected);
+ else
+ text = g_strdup_printf (ngettext ("%d page selected",
+ "%d pages selected",
+ n_selected),
+ n_selected);
+
+ gtk_label_set_text (GTK_LABEL (priv->count_label), text);
+ g_free (text);
+ }
+
+ range = gimp_page_selector_get_selected_range (selector);
+ gtk_entry_set_text (GTK_ENTRY (priv->range_entry), range);
+ g_free (range);
+
+ gtk_editable_set_position (GTK_EDITABLE (priv->range_entry), -1);
+
+ g_signal_emit (selector, selector_signals[SELECTION_CHANGED], 0);
+}
+
+static void
+gimp_page_selector_item_activated (GtkIconView *icon_view,
+ GtkTreePath *path,
+ GimpPageSelector *selector)
+{
+ g_signal_emit (selector, selector_signals[ACTIVATE], 0);
+}
+
+static gboolean
+gimp_page_selector_range_focus_out (GtkEntry *entry,
+ GdkEventFocus *fevent,
+ GimpPageSelector *selector)
+{
+ gimp_page_selector_range_activate (entry, selector);
+
+ return FALSE;
+}
+
+static void
+gimp_page_selector_range_activate (GtkEntry *entry,
+ GimpPageSelector *selector)
+{
+ gimp_page_selector_select_range (selector, gtk_entry_get_text (entry));
+}
+
+static gint
+gimp_page_selector_int_compare (gconstpointer a,
+ gconstpointer b)
+{
+ return *(gint*)a - *(gint*)b;
+}
+
+static void
+gimp_page_selector_print_range (GString *string,
+ gint start,
+ gint end)
+{
+ if (string->len != 0)
+ g_string_append_c (string, ',');
+
+ if (start == end)
+ g_string_append_printf (string, "%d", start + 1);
+ else
+ g_string_append_printf (string, "%d-%d", start + 1, end + 1);
+}
+
+static void
+draw_frame_row (GdkPixbuf *frame_image,
+ gint target_width,
+ gint source_width,
+ gint source_v_position,
+ gint dest_v_position,
+ GdkPixbuf *result_pixbuf,
+ gint left_offset,
+ gint height)
+{
+ gint remaining_width = target_width;
+ gint h_offset = 0;
+
+ while (remaining_width > 0)
+ {
+ gint slab_width = (remaining_width > source_width ?
+ source_width : remaining_width);
+
+ gdk_pixbuf_copy_area (frame_image,
+ left_offset, source_v_position,
+ slab_width, height,
+ result_pixbuf,
+ left_offset + h_offset, dest_v_position);
+
+ remaining_width -= slab_width;
+ h_offset += slab_width;
+ }
+}
+
+/* utility to draw the middle section of the frame in a loop */
+static void
+draw_frame_column (GdkPixbuf *frame_image,
+ gint target_height,
+ gint source_height,
+ gint source_h_position,
+ gint dest_h_position,
+ GdkPixbuf *result_pixbuf,
+ gint top_offset, int width)
+{
+ gint remaining_height = target_height;
+ gint v_offset = 0;
+
+ while (remaining_height > 0)
+ {
+ gint slab_height = (remaining_height > source_height ?
+ source_height : remaining_height);
+
+ gdk_pixbuf_copy_area (frame_image,
+ source_h_position, top_offset,
+ width, slab_height,
+ result_pixbuf,
+ dest_h_position, top_offset + v_offset);
+
+ remaining_height -= slab_height;
+ v_offset += slab_height;
+ }
+}
+
+static GdkPixbuf *
+stretch_frame_image (GdkPixbuf *frame_image,
+ gint left_offset,
+ gint top_offset,
+ gint right_offset,
+ gint bottom_offset,
+ gint dest_width,
+ gint dest_height)
+{
+ GdkPixbuf *pixbuf;
+ gint frame_width, frame_height;
+ gint target_width, target_frame_width;
+ gint target_height, target_frame_height;
+
+ frame_width = gdk_pixbuf_get_width (frame_image);
+ frame_height = gdk_pixbuf_get_height (frame_image );
+
+ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
+ dest_width, dest_height);
+ gdk_pixbuf_fill (pixbuf, 0);
+
+ target_width = dest_width - left_offset - right_offset;
+ target_height = dest_height - top_offset - bottom_offset;
+ target_frame_width = frame_width - left_offset - right_offset;
+ target_frame_height = frame_height - top_offset - bottom_offset;
+
+ left_offset += MIN (target_width / 4, target_frame_width / 4);
+ right_offset += MIN (target_width / 4, target_frame_width / 4);
+ top_offset += MIN (target_height / 4, target_frame_height / 4);
+ bottom_offset += MIN (target_height / 4, target_frame_height / 4);
+
+ target_width = dest_width - left_offset - right_offset;
+ target_height = dest_height - top_offset - bottom_offset;
+ target_frame_width = frame_width - left_offset - right_offset;
+ target_frame_height = frame_height - top_offset - bottom_offset;
+
+ /* draw the left top corner and top row */
+ gdk_pixbuf_copy_area (frame_image,
+ 0, 0, left_offset, top_offset,
+ pixbuf, 0, 0);
+ draw_frame_row (frame_image, target_width, target_frame_width,
+ 0, 0,
+ pixbuf,
+ left_offset, top_offset);
+
+ /* draw the right top corner and left column */
+ gdk_pixbuf_copy_area (frame_image,
+ frame_width - right_offset, 0,
+ right_offset, top_offset,
+
+ pixbuf,
+ dest_width - right_offset, 0);
+ draw_frame_column (frame_image, target_height, target_frame_height, 0, 0,
+ pixbuf, top_offset, left_offset);
+
+ /* draw the bottom right corner and bottom row */
+ gdk_pixbuf_copy_area (frame_image,
+ frame_width - right_offset, frame_height - bottom_offset,
+ right_offset, bottom_offset,
+ pixbuf,
+ dest_width - right_offset, dest_height - bottom_offset);
+ draw_frame_row (frame_image, target_width, target_frame_width,
+ frame_height - bottom_offset, dest_height - bottom_offset,
+ pixbuf, left_offset, bottom_offset);
+
+ /* draw the bottom left corner and the right column */
+ gdk_pixbuf_copy_area (frame_image,
+ 0, frame_height - bottom_offset,
+ left_offset, bottom_offset,
+ pixbuf,
+ 0, dest_height - bottom_offset);
+ draw_frame_column (frame_image, target_height, target_frame_height,
+ frame_width - right_offset, dest_width - right_offset,
+ pixbuf, top_offset, right_offset);
+
+ return pixbuf;
+}
+
+#define FRAME_LEFT 2
+#define FRAME_TOP 2
+#define FRAME_RIGHT 4
+#define FRAME_BOTTOM 4
+
+static GdkPixbuf *
+gimp_page_selector_add_frame (GtkWidget *widget,
+ GdkPixbuf *pixbuf)
+{
+ GdkPixbuf *frame;
+ gint width, height;
+
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+
+ frame = g_object_get_data (G_OBJECT (widget), "frame");
+
+ if (! frame)
+ {
+ GError *error = NULL;
+
+ frame = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
+ GIMP_ICON_FRAME, 64, 0, &error);
+ if (error)
+ {
+ g_printerr ("%s: %s\n", G_STRFUNC, error->message);
+ g_error_free (error);
+ }
+ g_return_val_if_fail (frame, NULL);
+ g_object_set_data_full (G_OBJECT (widget), "frame", frame,
+ (GDestroyNotify) g_object_unref);
+ }
+
+ frame = stretch_frame_image (frame,
+ FRAME_LEFT,
+ FRAME_TOP,
+ FRAME_RIGHT,
+ FRAME_BOTTOM,
+ width + FRAME_LEFT + FRAME_RIGHT,
+ height + FRAME_TOP + FRAME_BOTTOM);
+
+ gdk_pixbuf_copy_area (pixbuf, 0, 0, width, height,
+ frame, FRAME_LEFT, FRAME_TOP);
+
+ return frame;
+}
diff --git a/libgimpwidgets/gimppageselector.h b/libgimpwidgets/gimppageselector.h
new file mode 100644
index 0000000..1ac6de0
--- /dev/null
+++ b/libgimpwidgets/gimppageselector.h
@@ -0,0 +1,105 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppageselector.h
+ * Copyright (C) 2005 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PAGE_SELECTOR_H__
+#define __GIMP_PAGE_SELECTOR_H__
+
+G_BEGIN_DECLS
+
+#define GIMP_TYPE_PAGE_SELECTOR (gimp_page_selector_get_type ())
+#define GIMP_PAGE_SELECTOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_PAGE_SELECTOR, GimpPageSelector))
+#define GIMP_PAGE_SELECTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_PAGE_SELECTOR, GimpPageSelectorClass))
+#define GIMP_IS_PAGE_SELECTOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_PAGE_SELECTOR))
+#define GIMP_IS_PAGE_SELECTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_PAGE_SELECTOR))
+#define GIMP_PAGE_SELECTOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_PAGE_SELECTOR, GimpPageSelectorClass))
+
+
+typedef struct _GimpPageSelectorClass GimpPageSelectorClass;
+
+struct _GimpPageSelector
+{
+ GtkBox parent_instance;
+
+ gpointer priv;
+};
+
+struct _GimpPageSelectorClass
+{
+ GtkBoxClass parent_class;
+
+ void (* selection_changed) (GimpPageSelector *selector);
+ void (* activate) (GimpPageSelector *selector);
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_page_selector_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_page_selector_new (void);
+
+void gimp_page_selector_set_n_pages (GimpPageSelector *selector,
+ gint n_pages);
+gint gimp_page_selector_get_n_pages (GimpPageSelector *selector);
+
+void gimp_page_selector_set_target (GimpPageSelector *selector,
+ GimpPageSelectorTarget target);
+GimpPageSelectorTarget
+ gimp_page_selector_get_target (GimpPageSelector *selector);
+
+void gimp_page_selector_set_page_thumbnail (GimpPageSelector *selector,
+ gint page_no,
+ GdkPixbuf *thumbnail);
+GdkPixbuf * gimp_page_selector_get_page_thumbnail (GimpPageSelector *selector,
+ gint page_no);
+
+void gimp_page_selector_set_page_label (GimpPageSelector *selector,
+ gint page_no,
+ const gchar *label);
+gchar * gimp_page_selector_get_page_label (GimpPageSelector *selector,
+ gint page_no);
+
+void gimp_page_selector_select_all (GimpPageSelector *selector);
+void gimp_page_selector_unselect_all (GimpPageSelector *selector);
+void gimp_page_selector_select_page (GimpPageSelector *selector,
+ gint page_no);
+void gimp_page_selector_unselect_page (GimpPageSelector *selector,
+ gint page_no);
+gboolean gimp_page_selector_page_is_selected (GimpPageSelector *selector,
+ gint page_no);
+gint * gimp_page_selector_get_selected_pages (GimpPageSelector *selector,
+ gint *n_selected_pages);
+
+void gimp_page_selector_select_range (GimpPageSelector *selector,
+ const gchar *range);
+gchar * gimp_page_selector_get_selected_range (GimpPageSelector *selector);
+
+G_END_DECLS
+
+#endif /* __GIMP_PAGE_SELECTOR_H__ */
diff --git a/libgimpwidgets/gimppatheditor.c b/libgimpwidgets/gimppatheditor.c
new file mode 100644
index 0000000..4abc31d
--- /dev/null
+++ b/libgimpwidgets/gimppatheditor.c
@@ -0,0 +1,876 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppatheditor.c
+ * Copyright (C) 1999-2004 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "gimpwidgetstypes.h"
+
+#undef GIMP_DISABLE_DEPRECATED
+#include "gimpfileentry.h"
+
+#include "gimphelpui.h"
+#include "gimpicons.h"
+#include "gimppatheditor.h"
+#include "gimp3migration.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimppatheditor
+ * @title: GimpPathEditor
+ * @short_description: Widget for editing a file search path.
+ * @see_also: #GimpFileEntry, #G_SEARCHPATH_SEPARATOR
+ *
+ * This widget is used to edit file search paths.
+ *
+ * It shows a list of all directories which are in the search
+ * path. You can click a directory to select it. The widget provides a
+ * #GimpFileEntry to change the currently selected directory.
+ *
+ * There are buttons to add or delete directories as well as "up" and
+ * "down" buttons to change the order in which the directories will be
+ * searched.
+ *
+ * Whenever the user adds, deletes, changes or reorders a directory of
+ * the search path, the "path_changed" signal will be emitted.
+ **/
+
+
+enum
+{
+ PATH_CHANGED,
+ WRITABLE_CHANGED,
+ LAST_SIGNAL
+};
+
+enum
+{
+ COLUMN_UTF8,
+ COLUMN_DIRECTORY,
+ COLUMN_WRITABLE,
+ NUM_COLUMNS
+};
+
+
+static void gimp_path_editor_new_clicked (GtkWidget *widget,
+ GimpPathEditor *editor);
+static void gimp_path_editor_move_clicked (GtkWidget *widget,
+ GimpPathEditor *editor);
+static void gimp_path_editor_delete_clicked (GtkWidget *widget,
+ GimpPathEditor *editor);
+static void gimp_path_editor_file_entry_changed (GtkWidget *widget,
+ GimpPathEditor *editor);
+static void gimp_path_editor_selection_changed (GtkTreeSelection *sel,
+ GimpPathEditor *editor);
+static void gimp_path_editor_writable_toggled (GtkCellRendererToggle *toggle,
+ gchar *path_str,
+ GimpPathEditor *editor);
+
+
+G_DEFINE_TYPE (GimpPathEditor, gimp_path_editor, GTK_TYPE_BOX)
+
+#define parent_class gimp_path_editor_parent_class
+
+static guint gimp_path_editor_signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+gimp_path_editor_class_init (GimpPathEditorClass *klass)
+{
+ /**
+ * GimpPathEditor::path-changed:
+ *
+ * This signal is emitted whenever the user adds, deletes, modifies
+ * or reorders an element of the search path.
+ **/
+ gimp_path_editor_signals[PATH_CHANGED] =
+ g_signal_new ("path-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpPathEditorClass, path_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ /**
+ * GimpPathEditor::writable-changed:
+ *
+ * This signal is emitted whenever the "writable" column of a directory
+ * is changed, either by the user clicking on it or by calling
+ * gimp_path_editor_set_dir_writable().
+ **/
+ gimp_path_editor_signals[WRITABLE_CHANGED] =
+ g_signal_new ("writable-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpPathEditorClass, writable_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ klass->path_changed = NULL;
+ klass->writable_changed = NULL;
+}
+
+static void
+gimp_path_editor_init (GimpPathEditor *editor)
+{
+ GtkWidget *button_box;
+ GtkWidget *button;
+ GtkWidget *image;
+ GtkWidget *scrolled_window;
+ GtkWidget *tv;
+ GtkTreeViewColumn *col;
+ GtkCellRenderer *renderer;
+
+ editor->file_entry = NULL;
+ editor->sel_path = NULL;
+ editor->num_items = 0;
+
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (editor),
+ GTK_ORIENTATION_VERTICAL);
+
+ editor->upper_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
+ gtk_box_pack_start (GTK_BOX (editor), editor->upper_hbox, FALSE, TRUE, 0);
+ gtk_widget_show (editor->upper_hbox);
+
+ button_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_set_homogeneous (GTK_BOX (button_box), TRUE);
+ gtk_box_pack_start (GTK_BOX (editor->upper_hbox), button_box, FALSE, TRUE, 0);
+ gtk_widget_show (button_box);
+
+ editor->new_button = button = gtk_button_new ();
+ gtk_box_pack_start (GTK_BOX (button_box), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ image = gtk_image_new_from_icon_name (GIMP_ICON_DOCUMENT_NEW,
+ GTK_ICON_SIZE_BUTTON);
+ gtk_container_add (GTK_CONTAINER (button), image);
+ gtk_widget_show (image);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (gimp_path_editor_new_clicked),
+ editor);
+
+ gimp_help_set_help_data (editor->new_button,
+ _("Add a new folder"),
+ NULL);
+
+ editor->up_button = button = gtk_button_new ();
+ gtk_widget_set_sensitive (button, FALSE);
+ gtk_box_pack_start (GTK_BOX (button_box), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ image = gtk_image_new_from_icon_name (GIMP_ICON_GO_UP,
+ GTK_ICON_SIZE_BUTTON);
+ gtk_container_add (GTK_CONTAINER (button), image);
+ gtk_widget_show (image);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (gimp_path_editor_move_clicked),
+ editor);
+
+ gimp_help_set_help_data (editor->up_button,
+ _("Move the selected folder up"),
+ NULL);
+
+ editor->down_button = button = gtk_button_new ();
+ gtk_widget_set_sensitive (button, FALSE);
+ gtk_box_pack_start (GTK_BOX (button_box), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ image = gtk_image_new_from_icon_name (GIMP_ICON_GO_DOWN,
+ GTK_ICON_SIZE_BUTTON);
+ gtk_container_add (GTK_CONTAINER (button), image);
+ gtk_widget_show (image);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (gimp_path_editor_move_clicked),
+ editor);
+
+ gimp_help_set_help_data (editor->down_button,
+ _("Move the selected folder down"),
+ NULL);
+
+ editor->delete_button = button = gtk_button_new ();
+ gtk_widget_set_sensitive (button, FALSE);
+ gtk_box_pack_start (GTK_BOX (button_box), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ image = gtk_image_new_from_icon_name (GIMP_ICON_EDIT_DELETE,
+ GTK_ICON_SIZE_BUTTON);
+ gtk_container_add (GTK_CONTAINER (button), image);
+ gtk_widget_show (image);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (gimp_path_editor_delete_clicked),
+ editor);
+
+ gimp_help_set_help_data (editor->delete_button,
+ _("Remove the selected folder from the list"),
+ NULL);
+
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_SHADOW_IN);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_ALWAYS);
+ gtk_box_pack_start (GTK_BOX (editor), scrolled_window, TRUE, TRUE, 2);
+ gtk_widget_show (scrolled_window);
+
+ editor->dir_list = gtk_list_store_new (NUM_COLUMNS,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_BOOLEAN);
+ tv = gtk_tree_view_new_with_model (GTK_TREE_MODEL (editor->dir_list));
+ g_object_unref (editor->dir_list);
+
+ renderer = gtk_cell_renderer_toggle_new ();
+
+ g_signal_connect (renderer, "toggled",
+ G_CALLBACK (gimp_path_editor_writable_toggled),
+ editor);
+
+ editor->writable_column = col = gtk_tree_view_column_new ();
+ gtk_tree_view_column_set_title (col, _("Writable"));
+ gtk_tree_view_column_pack_start (col, renderer, FALSE);
+ gtk_tree_view_column_add_attribute (col, renderer, "active", COLUMN_WRITABLE);
+
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tv), col);
+
+ gtk_tree_view_column_set_visible (col, FALSE);
+
+ gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tv),
+ -1, _("Folder"),
+ gtk_cell_renderer_text_new (),
+ "text", COLUMN_UTF8,
+ NULL);
+
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tv), TRUE);
+
+ gtk_container_add (GTK_CONTAINER (scrolled_window), tv);
+ gtk_widget_show (tv);
+
+ editor->sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (tv));
+ g_signal_connect (editor->sel, "changed",
+ G_CALLBACK (gimp_path_editor_selection_changed),
+ editor);
+}
+
+/**
+ * gimp_path_editor_new:
+ * @title: The title of the #GtkFileChooser dialog which can be popped up.
+ * @path: The initial search path.
+ *
+ * Creates a new #GimpPathEditor widget.
+ *
+ * The elements of the initial search path must be separated with the
+ * #G_SEARCHPATH_SEPARATOR character.
+ *
+ * Returns: A pointer to the new #GimpPathEditor widget.
+ **/
+GtkWidget *
+gimp_path_editor_new (const gchar *title,
+ const gchar *path)
+{
+ GimpPathEditor *editor;
+
+ g_return_val_if_fail (title != NULL, NULL);
+
+ editor = g_object_new (GIMP_TYPE_PATH_EDITOR, NULL);
+
+ editor->file_entry = gimp_file_entry_new (title, "", TRUE, TRUE);
+ gtk_widget_set_sensitive (editor->file_entry, FALSE);
+ gtk_box_pack_start (GTK_BOX (editor->upper_hbox), editor->file_entry,
+ TRUE, TRUE, 0);
+ gtk_widget_show (editor->file_entry);
+
+ g_signal_connect (editor->file_entry, "filename-changed",
+ G_CALLBACK (gimp_path_editor_file_entry_changed),
+ editor);
+
+ if (path)
+ gimp_path_editor_set_path (editor, path);
+
+ return GTK_WIDGET (editor);
+}
+
+/**
+ * gimp_path_editor_get_path:
+ * @editor: The path editor you want to get the search path from.
+ *
+ * The elements of the returned search path string are separated with the
+ * #G_SEARCHPATH_SEPARATOR character.
+ *
+ * Note that you have to g_free() the returned string.
+ *
+ * Returns: The search path the user has selected in the path editor.
+ **/
+gchar *
+gimp_path_editor_get_path (GimpPathEditor *editor)
+{
+ GtkTreeModel *model;
+ GString *path;
+ GtkTreeIter iter;
+ gboolean iter_valid;
+
+ g_return_val_if_fail (GIMP_IS_PATH_EDITOR (editor), g_strdup (""));
+
+ model = GTK_TREE_MODEL (editor->dir_list);
+
+ path = g_string_new ("");
+
+ for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
+ iter_valid;
+ iter_valid = gtk_tree_model_iter_next (model, &iter))
+ {
+ gchar *dir;
+
+ gtk_tree_model_get (model, &iter,
+ COLUMN_DIRECTORY, &dir,
+ -1);
+
+ if (path->len > 0)
+ g_string_append_c (path, G_SEARCHPATH_SEPARATOR);
+
+ g_string_append (path, dir);
+
+ g_free (dir);
+ }
+
+ return g_string_free (path, FALSE);
+}
+
+/**
+ * gimp_path_editor_set_path:
+ * @editor: The path editor you want to set the search path from.
+ * @path: The new path to set.
+ *
+ * The elements of the initial search path must be separated with the
+ * #G_SEARCHPATH_SEPARATOR character.
+ **/
+void
+gimp_path_editor_set_path (GimpPathEditor *editor,
+ const gchar *path)
+{
+ gchar *old_path;
+ GList *path_list;
+ GList *list;
+
+ g_return_if_fail (GIMP_IS_PATH_EDITOR (editor));
+
+ old_path = gimp_path_editor_get_path (editor);
+
+ if (old_path && path && strcmp (old_path, path) == 0)
+ {
+ g_free (old_path);
+ return;
+ }
+
+ g_free (old_path);
+
+ path_list = gimp_path_parse (path, 256, FALSE, NULL);
+
+ gtk_list_store_clear (editor->dir_list);
+
+ for (list = path_list; list; list = g_list_next (list))
+ {
+ gchar *directory = list->data;
+ gchar *utf8;
+ GtkTreeIter iter;
+
+ utf8 = g_filename_to_utf8 (directory, -1, NULL, NULL, NULL);
+
+ gtk_list_store_append (editor->dir_list, &iter);
+ gtk_list_store_set (editor->dir_list, &iter,
+ COLUMN_UTF8, utf8,
+ COLUMN_DIRECTORY, directory,
+ COLUMN_WRITABLE, FALSE,
+ -1);
+
+ g_free (utf8);
+
+ editor->num_items++;
+ }
+
+ gimp_path_free (path_list);
+
+ g_signal_emit (editor, gimp_path_editor_signals[PATH_CHANGED], 0);
+}
+
+gchar *
+gimp_path_editor_get_writable_path (GimpPathEditor *editor)
+{
+ GtkTreeModel *model;
+ GString *path;
+ GtkTreeIter iter;
+ gboolean iter_valid;
+
+ g_return_val_if_fail (GIMP_IS_PATH_EDITOR (editor), g_strdup (""));
+
+ model = GTK_TREE_MODEL (editor->dir_list);
+
+ path = g_string_new ("");
+
+ for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
+ iter_valid;
+ iter_valid = gtk_tree_model_iter_next (model, &iter))
+ {
+ gchar *dir;
+ gboolean dir_writable;
+
+ gtk_tree_model_get (model, &iter,
+ COLUMN_DIRECTORY, &dir,
+ COLUMN_WRITABLE, &dir_writable,
+ -1);
+
+ if (dir_writable)
+ {
+ if (path->len > 0)
+ g_string_append_c (path, G_SEARCHPATH_SEPARATOR);
+
+ g_string_append (path, dir);
+ }
+
+ g_free (dir);
+ }
+
+ return g_string_free (path, FALSE);
+}
+
+void
+gimp_path_editor_set_writable_path (GimpPathEditor *editor,
+ const gchar *path)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gboolean iter_valid;
+ GList *path_list;
+ gboolean writable_changed = FALSE;
+
+ g_return_if_fail (GIMP_IS_PATH_EDITOR (editor));
+
+ gtk_tree_view_column_set_visible (editor->writable_column, TRUE);
+
+ path_list = gimp_path_parse (path, 256, FALSE, NULL);
+
+ model = GTK_TREE_MODEL (editor->dir_list);
+
+ for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
+ iter_valid;
+ iter_valid = gtk_tree_model_iter_next (model, &iter))
+ {
+ gchar *dir;
+ gboolean dir_writable;
+ gboolean new_writable = FALSE;
+
+ gtk_tree_model_get (model, &iter,
+ COLUMN_DIRECTORY, &dir,
+ COLUMN_WRITABLE, &dir_writable,
+ -1);
+
+ if (g_list_find_custom (path_list, dir, (GCompareFunc) strcmp))
+ new_writable = TRUE;
+
+ g_free (dir);
+
+ if (dir_writable != new_writable)
+ {
+ gtk_list_store_set (editor->dir_list, &iter,
+ COLUMN_WRITABLE, new_writable,
+ -1);
+
+ writable_changed = TRUE;
+ }
+ }
+
+ gimp_path_free (path_list);
+
+ if (writable_changed)
+ g_signal_emit (editor, gimp_path_editor_signals[WRITABLE_CHANGED], 0);
+}
+
+gboolean
+gimp_path_editor_get_dir_writable (GimpPathEditor *editor,
+ const gchar *directory)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gboolean iter_valid;
+
+ g_return_val_if_fail (GIMP_IS_PATH_EDITOR (editor), FALSE);
+ g_return_val_if_fail (directory != NULL, FALSE);
+
+ model = GTK_TREE_MODEL (editor->dir_list);
+
+ for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
+ iter_valid;
+ iter_valid = gtk_tree_model_iter_next (model, &iter))
+ {
+ gchar *dir;
+ gboolean dir_writable;
+
+ gtk_tree_model_get (model, &iter,
+ COLUMN_DIRECTORY, &dir,
+ COLUMN_WRITABLE, &dir_writable,
+ -1);
+
+ if (! strcmp (dir, directory))
+ {
+ g_free (dir);
+
+ return dir_writable;
+ }
+
+ g_free (dir);
+ }
+
+ return FALSE;
+}
+
+void
+gimp_path_editor_set_dir_writable (GimpPathEditor *editor,
+ const gchar *directory,
+ gboolean writable)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gboolean iter_valid;
+
+ g_return_if_fail (GIMP_IS_PATH_EDITOR (editor));
+ g_return_if_fail (directory != NULL);
+
+ model = GTK_TREE_MODEL (editor->dir_list);
+
+ for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
+ iter_valid;
+ iter_valid = gtk_tree_model_iter_next (model, &iter))
+ {
+ gchar *dir;
+ gboolean dir_writable;
+
+ gtk_tree_model_get (model, &iter,
+ COLUMN_DIRECTORY, &dir,
+ COLUMN_WRITABLE, &dir_writable,
+ -1);
+
+ if (! strcmp (dir, directory) && dir_writable != writable)
+ {
+ gtk_list_store_set (editor->dir_list, &iter,
+ COLUMN_WRITABLE, writable ? TRUE : FALSE,
+ -1);
+
+ g_signal_emit (editor, gimp_path_editor_signals[WRITABLE_CHANGED], 0);
+
+ g_free (dir);
+ break;
+ }
+
+ g_free (dir);
+ }
+}
+
+
+/* private functions */
+
+static void
+gimp_path_editor_new_clicked (GtkWidget *widget,
+ GimpPathEditor *editor)
+{
+ if (editor->sel_path)
+ {
+ g_signal_handlers_block_by_func (editor->sel,
+ gimp_path_editor_selection_changed,
+ editor);
+
+ gtk_tree_selection_unselect_path (editor->sel, editor->sel_path);
+
+ g_signal_handlers_unblock_by_func (editor->sel,
+ gimp_path_editor_selection_changed,
+ editor);
+
+ gtk_tree_path_free (editor->sel_path);
+ editor->sel_path = NULL;
+ }
+
+ gtk_widget_set_sensitive (editor->delete_button, FALSE);
+ gtk_widget_set_sensitive (editor->up_button, FALSE);
+ gtk_widget_set_sensitive (editor->down_button, FALSE);
+ gtk_widget_set_sensitive (editor->file_entry, TRUE);
+
+ gtk_editable_set_position
+ (GTK_EDITABLE (GIMP_FILE_ENTRY (editor->file_entry)->entry), -1);
+ gtk_widget_grab_focus
+ (GTK_WIDGET (GIMP_FILE_ENTRY (editor->file_entry)->entry));
+}
+
+static void
+gimp_path_editor_move_clicked (GtkWidget *widget,
+ GimpPathEditor *editor)
+{
+ GtkTreePath *path;
+ GtkTreeModel *model;
+ GtkTreeIter iter1, iter2;
+ gchar *utf81, *utf82;
+ gchar *dir1, *dir2;
+ gboolean writable1, writable2;
+
+ if (editor->sel_path == NULL)
+ return;
+
+ path = gtk_tree_path_copy (editor->sel_path);
+
+ if (widget == editor->up_button)
+ gtk_tree_path_prev (path);
+ else
+ gtk_tree_path_next (path);
+
+ model = GTK_TREE_MODEL (editor->dir_list);
+
+ gtk_tree_model_get_iter (model, &iter1, editor->sel_path);
+ gtk_tree_model_get_iter (model, &iter2, path);
+
+ gtk_tree_model_get (model, &iter1,
+ COLUMN_UTF8, &utf81,
+ COLUMN_DIRECTORY, &dir1,
+ COLUMN_WRITABLE, &writable1,
+ -1);
+ gtk_tree_model_get (model, &iter2,
+ COLUMN_UTF8, &utf82,
+ COLUMN_DIRECTORY, &dir2,
+ COLUMN_WRITABLE, &writable2,
+ -1);
+
+ gtk_list_store_set (editor->dir_list, &iter1,
+ COLUMN_UTF8, utf82,
+ COLUMN_DIRECTORY, dir2,
+ COLUMN_WRITABLE, writable2,
+ -1);
+ gtk_list_store_set (editor->dir_list, &iter2,
+ COLUMN_UTF8, utf81,
+ COLUMN_DIRECTORY, dir1,
+ COLUMN_WRITABLE, writable1,
+ -1);
+
+ g_free (utf81);
+ g_free (utf82);
+
+ g_free (dir2);
+ g_free (dir1);
+
+ gtk_tree_selection_select_iter (editor->sel, &iter2);
+
+ g_signal_emit (editor, gimp_path_editor_signals[PATH_CHANGED], 0);
+}
+
+static void
+gimp_path_editor_delete_clicked (GtkWidget *widget,
+ GimpPathEditor *editor)
+{
+ GtkTreeIter iter;
+ gboolean dir_writable;
+
+ if (editor->sel_path == NULL)
+ return;
+
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (editor->dir_list), &iter,
+ editor->sel_path);
+
+ gtk_tree_model_get (GTK_TREE_MODEL (editor->dir_list), &iter,
+ COLUMN_WRITABLE, &dir_writable,
+ -1);
+
+ gtk_list_store_remove (editor->dir_list, &iter);
+
+ editor->num_items--;
+
+ if (editor->num_items == 0)
+ {
+ gtk_tree_path_free (editor->sel_path);
+ editor->sel_path = NULL;
+
+ g_signal_handlers_block_by_func (editor->file_entry,
+ gimp_path_editor_file_entry_changed,
+ editor);
+
+ gimp_file_entry_set_filename (GIMP_FILE_ENTRY (editor->file_entry), "");
+
+ g_signal_handlers_unblock_by_func (editor->file_entry,
+ gimp_path_editor_file_entry_changed,
+ editor);
+
+ gtk_widget_set_sensitive (editor->delete_button, FALSE);
+ gtk_widget_set_sensitive (editor->up_button, FALSE);
+ gtk_widget_set_sensitive (editor->down_button, FALSE);
+ gtk_widget_set_sensitive (editor->file_entry, FALSE);
+ }
+ else
+ {
+ gint *indices;
+
+ indices = gtk_tree_path_get_indices (editor->sel_path);
+ if ((indices[0] == editor->num_items) && (indices[0] > 0))
+ gtk_tree_path_prev (editor->sel_path);
+
+ gtk_tree_selection_select_path (editor->sel, editor->sel_path);
+ }
+
+ g_signal_emit (editor, gimp_path_editor_signals[PATH_CHANGED], 0);
+
+ if (dir_writable)
+ g_signal_emit (editor, gimp_path_editor_signals[WRITABLE_CHANGED], 0);
+}
+
+static void
+gimp_path_editor_file_entry_changed (GtkWidget *widget,
+ GimpPathEditor *editor)
+{
+ gchar *dir;
+ gchar *utf8;
+ GtkTreeIter iter;
+
+ dir = gimp_file_entry_get_filename (GIMP_FILE_ENTRY (widget));
+ if (strcmp (dir, "") == 0)
+ {
+ g_free (dir);
+ return;
+ }
+
+ utf8 = g_filename_display_name (dir);
+
+ if (editor->sel_path == NULL)
+ {
+ gtk_list_store_append (editor->dir_list, &iter);
+ gtk_list_store_set (editor->dir_list, &iter,
+ COLUMN_UTF8, utf8,
+ COLUMN_DIRECTORY, dir,
+ COLUMN_WRITABLE, FALSE,
+ -1);
+ editor->num_items++;
+
+ gtk_tree_selection_select_iter (editor->sel, &iter);
+ }
+ else
+ {
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (editor->dir_list), &iter,
+ editor->sel_path);
+ gtk_list_store_set (editor->dir_list, &iter,
+ COLUMN_UTF8, utf8,
+ COLUMN_DIRECTORY, dir,
+ -1);
+ }
+
+ g_free (dir);
+ g_free (utf8);
+
+ g_signal_emit (editor, gimp_path_editor_signals[PATH_CHANGED], 0);
+}
+
+static void
+gimp_path_editor_selection_changed (GtkTreeSelection *sel,
+ GimpPathEditor *editor)
+{
+ GtkTreeIter iter;
+ gchar *directory;
+ gint *indices;
+
+ if (gtk_tree_selection_get_selected (sel, NULL, &iter))
+ {
+ gtk_tree_model_get (GTK_TREE_MODEL (editor->dir_list), &iter,
+ COLUMN_DIRECTORY, &directory,
+ -1);
+
+ g_signal_handlers_block_by_func (editor->file_entry,
+ gimp_path_editor_file_entry_changed,
+ editor);
+
+ gimp_file_entry_set_filename (GIMP_FILE_ENTRY (editor->file_entry),
+ directory);
+
+ g_signal_handlers_unblock_by_func (editor->file_entry,
+ gimp_path_editor_file_entry_changed,
+ editor);
+
+ g_free (directory);
+
+ if (editor->sel_path)
+ gtk_tree_path_free (editor->sel_path);
+
+ editor->sel_path =
+ gtk_tree_model_get_path (GTK_TREE_MODEL (editor->dir_list), &iter);
+
+ indices = gtk_tree_path_get_indices (editor->sel_path);
+
+ gtk_widget_set_sensitive (editor->delete_button, TRUE);
+ gtk_widget_set_sensitive (editor->up_button, (indices[0] > 0));
+ gtk_widget_set_sensitive (editor->down_button,
+ (indices[0] < (editor->num_items - 1)));
+ gtk_widget_set_sensitive (editor->file_entry, TRUE);
+ }
+ else
+ {
+ g_signal_handlers_block_by_func (sel,
+ gimp_path_editor_selection_changed,
+ editor);
+
+ gtk_tree_selection_select_path (editor->sel, editor->sel_path);
+
+ g_signal_handlers_unblock_by_func (sel,
+ gimp_path_editor_selection_changed,
+ editor);
+ }
+}
+
+static void
+gimp_path_editor_writable_toggled (GtkCellRendererToggle *toggle,
+ gchar *path_str,
+ GimpPathEditor *editor)
+{
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ path = gtk_tree_path_new_from_string (path_str);
+
+ if (gtk_tree_model_get_iter (GTK_TREE_MODEL (editor->dir_list), &iter, path))
+ {
+ gboolean dir_writable;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (editor->dir_list), &iter,
+ COLUMN_WRITABLE, &dir_writable,
+ -1);
+
+ gtk_list_store_set (editor->dir_list, &iter,
+ COLUMN_WRITABLE, ! dir_writable,
+ -1);
+
+ g_signal_emit (editor, gimp_path_editor_signals[WRITABLE_CHANGED], 0);
+ }
+
+ gtk_tree_path_free (path);
+}
diff --git a/libgimpwidgets/gimppatheditor.h b/libgimpwidgets/gimppatheditor.h
new file mode 100644
index 0000000..2686501
--- /dev/null
+++ b/libgimpwidgets/gimppatheditor.h
@@ -0,0 +1,105 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppatheditor.h
+ * Copyright (C) 1999-2004 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PATH_EDITOR_H__
+#define __GIMP_PATH_EDITOR_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+#define GIMP_TYPE_PATH_EDITOR (gimp_path_editor_get_type ())
+#define GIMP_PATH_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_PATH_EDITOR, GimpPathEditor))
+#define GIMP_PATH_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_PATH_EDITOR, GimpPathEditorClass))
+#define GIMP_IS_PATH_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GIMP_TYPE_PATH_EDITOR))
+#define GIMP_IS_PATH_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_PATH_EDITOR))
+#define GIMP_PATH_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_PATH_EDITOR, GimpPathEditorClass))
+
+
+typedef struct _GimpPathEditorClass GimpPathEditorClass;
+
+struct _GimpPathEditor
+{
+ GtkBox parent_instance;
+
+ GtkWidget *upper_hbox;
+
+ GtkWidget *new_button;
+ GtkWidget *up_button;
+ GtkWidget *down_button;
+ GtkWidget *delete_button;
+
+ GtkWidget *file_entry;
+
+ GtkListStore *dir_list;
+
+ GtkTreeSelection *sel;
+ GtkTreePath *sel_path;
+
+ GtkTreeViewColumn *writable_column;
+
+ gint num_items;
+};
+
+struct _GimpPathEditorClass
+{
+ GtkBoxClass parent_class;
+
+ void (* path_changed) (GimpPathEditor *editor);
+ void (* writable_changed) (GimpPathEditor *editor);
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+/* For information look into the C source or the html documentation */
+
+GType gimp_path_editor_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_path_editor_new (const gchar *title,
+ const gchar *path);
+
+gchar * gimp_path_editor_get_path (GimpPathEditor *editor);
+void gimp_path_editor_set_path (GimpPathEditor *editor,
+ const gchar *path);
+
+gchar * gimp_path_editor_get_writable_path (GimpPathEditor *editor);
+void gimp_path_editor_set_writable_path (GimpPathEditor *editor,
+ const gchar *path);
+
+gboolean gimp_path_editor_get_dir_writable (GimpPathEditor *editor,
+ const gchar *directory);
+void gimp_path_editor_set_dir_writable (GimpPathEditor *editor,
+ const gchar *directory,
+ gboolean writable);
+
+G_END_DECLS
+
+#endif /* __GIMP_PATH_EDITOR_H__ */
diff --git a/libgimpwidgets/gimppickbutton-default.c b/libgimpwidgets/gimppickbutton-default.c
new file mode 100644
index 0000000..3d55f32
--- /dev/null
+++ b/libgimpwidgets/gimppickbutton-default.c
@@ -0,0 +1,368 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppickbutton.c
+ * Copyright (C) 2002 Michael Natterer <mitch@gimp.org>
+ *
+ * based on gtk+/gtk/gtkcolorsel.c
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#ifdef G_OS_WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+
+#include "libgimpcolor/gimpcolor.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpcairo-utils.h"
+#include "gimphelpui.h"
+#include "gimpicons.h"
+#include "gimppickbutton.h"
+#include "gimppickbutton-default.h"
+#include "gimpwidgetsutils.h"
+
+#include "cursors/gimp-color-picker-cursors.c"
+
+#include "libgimp/libgimp-intl.h"
+
+
+static gboolean gimp_pick_button_mouse_press (GtkWidget *invisible,
+ GdkEventButton *event,
+ GimpPickButton *button);
+static gboolean gimp_pick_button_key_press (GtkWidget *invisible,
+ GdkEventKey *event,
+ GimpPickButton *button);
+static gboolean gimp_pick_button_mouse_motion (GtkWidget *invisible,
+ GdkEventMotion *event,
+ GimpPickButton *button);
+static gboolean gimp_pick_button_mouse_release (GtkWidget *invisible,
+ GdkEventButton *event,
+ GimpPickButton *button);
+static void gimp_pick_button_shutdown (GimpPickButton *button);
+static void gimp_pick_button_pick (GdkScreen *screen,
+ gint x_root,
+ gint y_root,
+ GimpPickButton *button);
+
+
+static GdkCursor *
+make_cursor (GdkDisplay *display)
+{
+ GdkPixbuf *pixbuf;
+ GError *error = NULL;
+
+ pixbuf = gdk_pixbuf_new_from_resource ("/org/gimp/color-picker-cursors/cursor-color-picker.png",
+ &error);
+
+ if (pixbuf)
+ {
+ GdkCursor *cursor = gdk_cursor_new_from_pixbuf (display, pixbuf, 1, 30);
+
+ g_object_unref (pixbuf);
+
+ return cursor;
+ }
+ else
+ {
+ g_critical ("Failed to create cursor image: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ return NULL;
+}
+
+static gboolean
+gimp_pick_button_mouse_press (GtkWidget *invisible,
+ GdkEventButton *event,
+ GimpPickButton *button)
+{
+ if (event->type == GDK_BUTTON_PRESS && event->button == 1)
+ {
+ g_signal_connect (invisible, "motion-notify-event",
+ G_CALLBACK (gimp_pick_button_mouse_motion),
+ button);
+ g_signal_connect (invisible, "button-release-event",
+ G_CALLBACK (gimp_pick_button_mouse_release),
+ button);
+
+ g_signal_handlers_disconnect_by_func (invisible,
+ gimp_pick_button_mouse_press,
+ button);
+ g_signal_handlers_disconnect_by_func (invisible,
+ gimp_pick_button_key_press,
+ button);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gimp_pick_button_key_press (GtkWidget *invisible,
+ GdkEventKey *event,
+ GimpPickButton *button)
+{
+ if (event->keyval == GDK_KEY_Escape)
+ {
+ gimp_pick_button_shutdown (button);
+
+ g_signal_handlers_disconnect_by_func (invisible,
+ gimp_pick_button_mouse_press,
+ button);
+ g_signal_handlers_disconnect_by_func (invisible,
+ gimp_pick_button_key_press,
+ button);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gimp_pick_button_mouse_motion (GtkWidget *invisible,
+ GdkEventMotion *event,
+ GimpPickButton *button)
+{
+ gint x_root;
+ gint y_root;
+
+ gdk_window_get_origin (event->window, &x_root, &y_root);
+ x_root += event->x;
+ y_root += event->y;
+
+ gimp_pick_button_pick (gdk_event_get_screen ((GdkEvent *) event),
+ x_root, y_root, button);
+
+ return TRUE;
+}
+
+static gboolean
+gimp_pick_button_mouse_release (GtkWidget *invisible,
+ GdkEventButton *event,
+ GimpPickButton *button)
+{
+ gint x_root;
+ gint y_root;
+
+ if (event->button != 1)
+ return FALSE;
+
+ gdk_window_get_origin (event->window, &x_root, &y_root);
+ x_root += event->x;
+ y_root += event->y;
+
+ gimp_pick_button_pick (gdk_event_get_screen ((GdkEvent *) event),
+ x_root, y_root, button);
+
+ gimp_pick_button_shutdown (button);
+
+ g_signal_handlers_disconnect_by_func (invisible,
+ gimp_pick_button_mouse_motion,
+ button);
+ g_signal_handlers_disconnect_by_func (invisible,
+ gimp_pick_button_mouse_release,
+ button);
+
+ return TRUE;
+}
+
+static void
+gimp_pick_button_shutdown (GimpPickButton *button)
+{
+ GdkDisplay *display = gtk_widget_get_display (button->grab_widget);
+ guint32 timestamp = gtk_get_current_event_time ();
+
+ gdk_display_keyboard_ungrab (display, timestamp);
+ gdk_display_pointer_ungrab (display, timestamp);
+
+ gtk_grab_remove (button->grab_widget);
+}
+
+static void
+gimp_pick_button_pick (GdkScreen *screen,
+ gint x_root,
+ gint y_root,
+ GimpPickButton *button)
+{
+ GimpRGB rgb;
+ GimpColorProfile *monitor_profile;
+ gint monitor;
+
+#ifdef G_OS_WIN32
+
+ HDC hdc;
+ RECT rect;
+ COLORREF win32_color;
+
+ /* For MS Windows, use native GDI functions to get the pixel, as
+ * cairo does not handle the case where you have multiple monitors
+ * with a monitor on the left or above the primary monitor. That
+ * scenario create a cairo primary surface with negative extent,
+ * which is not handled properly (bug 740634).
+ */
+
+ hdc = GetDC (HWND_DESKTOP);
+ GetClipBox (hdc, &rect);
+ win32_color = GetPixel (hdc, x_root + rect.left, y_root + rect.top);
+ ReleaseDC (HWND_DESKTOP, hdc);
+
+ gimp_rgba_set_uchar (&rgb,
+ GetRValue (win32_color),
+ GetGValue (win32_color),
+ GetBValue (win32_color),
+ 255);
+
+#else
+
+ GdkWindow *window;
+ gint x_window;
+ gint y_window;
+ cairo_surface_t *image;
+ cairo_t *cr;
+ guchar *data;
+ guchar color[3];
+
+ /* we try to pick from the local window under the cursor, and fall back to
+ * picking from the root window if this fails (i.e., if the cursor is not
+ * under a local window). on wayland, picking from the root window is not
+ * supported, so this at least allows us to pick from local windows. see
+ * bug #780375.
+ */
+ window = gdk_display_get_window_at_pointer (gdk_screen_get_display (screen),
+ &x_window, &y_window);
+
+ if (! window)
+ {
+ window = gdk_screen_get_root_window (screen);
+ x_window = x_root;
+ y_window = y_root;
+ }
+
+ image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 1, 1);
+
+ cr = cairo_create (image);
+
+ gdk_cairo_set_source_window (cr, window, -x_window, -y_window);
+ cairo_paint (cr);
+
+ cairo_destroy (cr);
+
+ data = cairo_image_surface_get_data (image);
+ GIMP_CAIRO_RGB24_GET_PIXEL (data, color[0], color[1], color[2]);
+
+ cairo_surface_destroy (image);
+
+ gimp_rgba_set_uchar (&rgb, color[0], color[1], color[2], 255);
+
+#endif
+
+ monitor = gdk_screen_get_monitor_at_point (screen, x_root, y_root);
+ monitor_profile = gimp_screen_get_color_profile (screen, monitor);
+
+ if (monitor_profile)
+ {
+ GimpColorProfile *srgb_profile;
+ GimpColorTransform *transform;
+ const Babl *format;
+ GimpColorTransformFlags flags = 0;
+
+ format = babl_format ("R'G'B'A double");
+
+ flags |= GIMP_COLOR_TRANSFORM_FLAGS_NOOPTIMIZE;
+ flags |= GIMP_COLOR_TRANSFORM_FLAGS_BLACK_POINT_COMPENSATION;
+
+ srgb_profile = gimp_color_profile_new_rgb_srgb ();
+ transform = gimp_color_transform_new (monitor_profile, format,
+ srgb_profile, format,
+ GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL,
+ flags);
+ g_object_unref (srgb_profile);
+
+ if (transform)
+ {
+ gimp_color_transform_process_pixels (transform,
+ format, &rgb,
+ format, &rgb,
+ 1);
+ gimp_rgb_clamp (&rgb);
+
+ g_object_unref (transform);
+ }
+ }
+
+ g_signal_emit_by_name (button, "color-picked", &rgb);
+}
+
+/* entry point to this file, called from gimppickbutton.c */
+void
+_gimp_pick_button_default_pick (GimpPickButton *button)
+{
+ GtkWidget *widget;
+ guint32 timestamp;
+
+ if (! button->cursor)
+ button->cursor = make_cursor (gtk_widget_get_display (GTK_WIDGET (button)));
+
+ if (! button->grab_widget)
+ {
+ button->grab_widget = gtk_invisible_new ();
+
+ gtk_widget_add_events (button->grab_widget,
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_POINTER_MOTION_MASK);
+
+ gtk_widget_show (button->grab_widget);
+ }
+
+ widget = button->grab_widget;
+ timestamp = gtk_get_current_event_time ();
+
+ if (gdk_keyboard_grab (gtk_widget_get_window (widget), FALSE,
+ timestamp) != GDK_GRAB_SUCCESS)
+ {
+ g_warning ("Failed to grab keyboard to do eyedropper");
+ return;
+ }
+
+ if (gdk_pointer_grab (gtk_widget_get_window (widget), FALSE,
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_POINTER_MOTION_MASK,
+ NULL,
+ button->cursor,
+ timestamp) != GDK_GRAB_SUCCESS)
+ {
+ gdk_display_keyboard_ungrab (gtk_widget_get_display (widget), timestamp);
+ g_warning ("Failed to grab pointer to do eyedropper");
+ return;
+ }
+
+ gtk_grab_add (widget);
+
+ g_signal_connect (widget, "button-press-event",
+ G_CALLBACK (gimp_pick_button_mouse_press),
+ button);
+ g_signal_connect (widget, "key-press-event",
+ G_CALLBACK (gimp_pick_button_key_press),
+ button);
+}
diff --git a/libgimpwidgets/gimppickbutton-default.h b/libgimpwidgets/gimppickbutton-default.h
new file mode 100644
index 0000000..8b252b8
--- /dev/null
+++ b/libgimpwidgets/gimppickbutton-default.h
@@ -0,0 +1,25 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppickbutton-default.h
+ * Copyright (C) 2017 Jehan <jehan@gimp.org>
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* Private header file which is not meant to be exported. */
+#ifndef __GIMP_PICK_BUTTON_DEFAULT_H__
+#define __GIMP_PICK_BUTTON_DEFAULT_H__
+
+void _gimp_pick_button_default_pick (GimpPickButton *button);
+
+#endif /* __GIMP_PICK_BUTTON_DEFAULT_H__ */
+
+
diff --git a/libgimpwidgets/gimppickbutton-kwin.c b/libgimpwidgets/gimppickbutton-kwin.c
new file mode 100644
index 0000000..ec09bfc
--- /dev/null
+++ b/libgimpwidgets/gimppickbutton-kwin.c
@@ -0,0 +1,112 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppickbutton-kwin.c
+ * Copyright (C) 2017 Jehan <jehan@gimp.org>
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpcolor/gimpcolor.h"
+
+#include "gimpwidgetstypes.h"
+#include "gimppickbutton.h"
+#include "gimppickbutton-default.h"
+#include "gimppickbutton-kwin.h"
+
+#include "libgimp/libgimp-intl.h"
+
+gboolean
+_gimp_pick_button_kwin_available (void)
+{
+ GDBusProxy *proxy = NULL;
+ gboolean available = FALSE;
+
+ proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ NULL,
+ "org.kde.KWin",
+ "/ColorPicker",
+ "org.kde.kwin.ColorPicker",
+ NULL, NULL);
+
+ if (proxy)
+ {
+ GError *error = NULL;
+
+ g_dbus_proxy_call_sync (proxy, "org.freedesktop.DBus.Peer.Ping",
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL, &error);
+ if (! error)
+ available = TRUE;
+
+ g_clear_error (&error);
+ g_object_unref (proxy);
+ }
+
+ return available;
+}
+
+/* entry point to this file, called from gimppickbutton.c */
+void
+_gimp_pick_button_kwin_pick (GimpPickButton *button)
+{
+ GDBusProxy *proxy = NULL;
+ GError *error = NULL;
+ GVariant *retval;
+
+ proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ NULL,
+ "org.kde.KWin",
+ "/ColorPicker",
+ "org.kde.kwin.ColorPicker",
+ NULL, NULL);
+ g_return_if_fail (proxy);
+
+ retval = g_dbus_proxy_call_sync (proxy, "pick", NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL, &error);
+ if (retval)
+ {
+ GimpRGB rgb;
+ guint32 color;
+
+ g_variant_get (retval, "((u))", &color);
+ g_variant_unref (retval);
+ /* Returned value is ARGB stored in uint32. */
+ gimp_rgba_set_uchar (&rgb,
+ (color >> 16 ) & 0xff, /* Red */
+ (color >> 8) & 0xff, /* Green */
+ color & 0xff, /* Blue: least significant byte. */
+ (color >> 24) & 0xff); /* Alpha: most significant byte. */
+ g_signal_emit_by_name (button, "color-picked", &rgb);
+ }
+ else
+ {
+ /* I had failure of KDE's color picking API. So let's just
+ * fallback to the default color picking when this happens. This
+ * will at least work on X11.
+ * See: https://bugs.kde.org/show_bug.cgi?id=387720
+ */
+ if (error)
+ g_warning ("KWin backend for color picking failed with error: %s",
+ error->message);
+ _gimp_pick_button_default_pick (GIMP_PICK_BUTTON (button));
+ }
+ g_clear_error (&error);
+ g_object_unref (proxy);
+}
diff --git a/libgimpwidgets/gimppickbutton-kwin.h b/libgimpwidgets/gimppickbutton-kwin.h
new file mode 100644
index 0000000..4f596af
--- /dev/null
+++ b/libgimpwidgets/gimppickbutton-kwin.h
@@ -0,0 +1,25 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppickbutton-kwin.h
+ * Copyright (C) 2017 Jehan <jehan@gimp.org>
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* Private header file which is not meant to be exported. */
+#ifndef __GIMP_PICK_BUTTON_KWIN_H__
+#define __GIMP_PICK_BUTTON_KWIN_H__
+
+gboolean _gimp_pick_button_kwin_available (void);
+void _gimp_pick_button_kwin_pick (GimpPickButton *button);
+
+#endif /* __GIMP_PICK_BUTTON_KWIN_H__ */
+
diff --git a/libgimpwidgets/gimppickbutton-quartz.c b/libgimpwidgets/gimppickbutton-quartz.c
new file mode 100644
index 0000000..5d581d3
--- /dev/null
+++ b/libgimpwidgets/gimppickbutton-quartz.c
@@ -0,0 +1,485 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppickbutton-quartz.c
+ * Copyright (C) 2015 Kristian Rietveld <kris@loopnest.org>
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "libgimpcolor/gimpcolor.h"
+
+#include "gimpwidgetstypes.h"
+#include "gimppickbutton.h"
+#include "gimppickbutton-quartz.h"
+
+#include "cursors/gimp-color-picker-cursors.c"
+
+#ifdef GDK_WINDOWING_QUARTZ
+#import <AppKit/AppKit.h>
+#include <Carbon/Carbon.h> /* For virtual key codes ... */
+#include <ApplicationServices/ApplicationServices.h>
+#endif
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED < 1070
+#if MAC_OS_X_VERSION_MAX_ALLOWED < 1060
+@protocol NSWindowDelegate
+@end
+#endif
+
+@interface NSWindow (GIMPExt)
+- (NSRect) convertRectToScreen: (NSRect)aRect;
+@end
+@implementation NSWindow (GIMPExt)
+- (NSRect) convertRectToScreen: (NSRect)aRect
+{
+ NSRect result = aRect;
+ NSPoint origin = result.origin;
+ result.origin = [self convertBaseToScreen:origin];
+ return result;
+}
+@end
+#endif
+
+
+@interface GimpPickWindowController : NSObject
+{
+ GimpPickButton *button;
+ NSMutableArray *windows;
+}
+
+@property (nonatomic, assign) BOOL firstBecameKey;
+@property (readonly, retain) NSCursor *cursor;
+
+- (id)initWithButton:(GimpPickButton *)_button;
+- (void)updateKeyWindow;
+- (void)shutdown;
+@end
+
+@interface GimpPickView : NSView
+{
+ GimpPickButton *button;
+ GimpPickWindowController *controller;
+}
+
+@property (readonly,assign) NSTrackingArea *area;
+
+- (id)initWithButton:(GimpPickButton *)_button controller:(GimpPickWindowController *)controller;
+@end
+
+@implementation GimpPickView
+
+@synthesize area;
+
+- (id)initWithButton:(GimpPickButton *)_button controller:(GimpPickWindowController *)_controller
+{
+ self = [super init];
+
+ if (self)
+ {
+ button = _button;
+ controller = _controller;
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [self removeTrackingArea:self.area];
+
+ [super dealloc];
+}
+
+- (void)viewDidMoveToWindow
+{
+ NSTrackingAreaOptions options;
+
+ [super viewDidMoveToWindow];
+
+ if ([self window] == nil)
+ return;
+
+ options = NSTrackingMouseEnteredAndExited |
+ NSTrackingMouseMoved |
+ NSTrackingActiveAlways;
+
+ /* Add assume inside if mouse pointer is above this window */
+ if (NSPointInRect ([NSEvent mouseLocation], self.window.frame))
+ options |= NSTrackingAssumeInside;
+
+ area = [[NSTrackingArea alloc] initWithRect:self.bounds
+ options:options
+ owner:self
+ userInfo:nil];
+ [self addTrackingArea:self.area];
+}
+
+- (void)mouseEntered:(NSEvent *)event
+{
+ /* We handle the mouse cursor manually, see also the comment in
+ * [GimpPickWindow windowDidBecomeMain below].
+ */
+ if (controller.cursor)
+ [controller.cursor push];
+}
+
+- (void)mouseExited:(NSEvent *)event
+{
+ if (controller.cursor)
+ [controller.cursor pop];
+
+ [controller updateKeyWindow];
+}
+
+- (void)mouseMoved:(NSEvent *)event
+{
+ [self pickColor:event];
+}
+
+- (void)mouseUp:(NSEvent *)event
+{
+ [self pickColor:event];
+
+ [controller shutdown];
+}
+
+- (void)rightMouseUp:(NSEvent *)event
+{
+ [self mouseUp:event];
+}
+
+- (void)otherMouseUp:(NSEvent *)event
+{
+ [self mouseUp:event];
+}
+
+- (void)keyDown:(NSEvent *)event
+{
+ if (event.keyCode == kVK_Escape)
+ [controller shutdown];
+}
+
+- (void)pickColor:(NSEvent *)event
+{
+ CGImageRef root_image_ref;
+ CFDataRef pixel_data;
+ const guchar *data;
+ GimpRGB rgb;
+ NSPoint point;
+ GimpColorProfile *profile = NULL;
+ CGColorSpaceRef color_space = NULL;
+
+ /* The event gives us a point in Cocoa window coordinates. The function
+ * CGWindowListCreateImage expects a rectangle in screen coordinates
+ * with the origin in the upper left (contrary to Cocoa). The origin is
+ * on the screen showing the menu bar (this is the screen at index 0 in the
+ * screens array). So, after converting the rectangle to Cocoa screen
+ * coordinates, we use the height of this particular screen to translate
+ * to the coordinate space expected by CGWindowListCreateImage.
+ */
+ point = event.locationInWindow;
+ NSRect rect = NSMakeRect (point.x, point.y,
+ 1, 1);
+ rect = [self.window convertRectToScreen:rect];
+ rect.origin.y = [[[NSScreen screens] objectAtIndex:0] frame].size.height - rect.origin.y;
+
+ root_image_ref = CGWindowListCreateImage (rect,
+ kCGWindowListOptionOnScreenOnly,
+ kCGNullWindowID,
+ kCGWindowImageDefault);
+ pixel_data = CGDataProviderCopyData (CGImageGetDataProvider (root_image_ref));
+ data = CFDataGetBytePtr (pixel_data);
+
+ color_space = CGImageGetColorSpace (root_image_ref);
+ if (color_space)
+ {
+ CFDataRef icc_data = NULL;
+
+ icc_data = CGColorSpaceCopyICCProfile (color_space);
+ if (icc_data)
+ {
+ UInt8 *buffer = g_malloc (CFDataGetLength (icc_data));
+
+ CFDataGetBytes (icc_data, CFRangeMake (0, CFDataGetLength (icc_data)),
+ buffer);
+
+ profile = gimp_color_profile_new_from_icc_profile (buffer,
+ CFDataGetLength (icc_data),
+ NULL);
+ g_free (buffer);
+ CFRelease (icc_data);
+ }
+ }
+
+ gimp_rgba_set_uchar (&rgb, data[2], data[1], data[0], 255);
+ if (profile)
+ {
+ GimpColorProfile *srgb_profile;
+ GimpColorTransform *transform;
+ const Babl *format;
+ GimpColorTransformFlags flags = 0;
+
+ format = babl_format ("R'G'B'A double");
+
+ flags |= GIMP_COLOR_TRANSFORM_FLAGS_NOOPTIMIZE;
+ flags |= GIMP_COLOR_TRANSFORM_FLAGS_BLACK_POINT_COMPENSATION;
+
+ srgb_profile = gimp_color_profile_new_rgb_srgb ();
+ transform = gimp_color_transform_new (profile, format,
+ srgb_profile, format,
+ GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL,
+ flags);
+
+ if (transform)
+ {
+ gimp_color_transform_process_pixels (transform,
+ format, &rgb,
+ format, &rgb,
+ 1);
+ gimp_rgb_clamp (&rgb);
+
+ g_object_unref (transform);
+ }
+ g_object_unref (srgb_profile);
+ g_object_unref (profile);
+ }
+
+ CGImageRelease (root_image_ref);
+ CFRelease (pixel_data);
+
+ g_signal_emit_by_name (button, "color-picked", &rgb);
+}
+@end
+
+
+@interface GimpPickWindow : NSWindow <NSWindowDelegate>
+{
+ GimpPickWindowController *controller;
+}
+
+- (id)initWithButton:(GimpPickButton *)button forScreen:(NSScreen *)screen withController:(GimpPickWindowController *)_controller;
+@end
+
+@implementation GimpPickWindow
+- (id)initWithButton:(GimpPickButton *)button forScreen:(NSScreen *)screen withController:(GimpPickWindowController *)_controller
+{
+ self = [super initWithContentRect:screen.frame
+ styleMask:NSBorderlessWindowMask
+ backing:NSBackingStoreBuffered
+ defer:NO];
+
+ if (self)
+ {
+ GimpPickView *view;
+
+ controller = _controller;
+
+ [self setDelegate:self];
+
+ [self setAlphaValue:0.0];
+#if 0
+ /* Useful for debugging purposes */
+ [self setBackgroundColor:[NSColor redColor]];
+ [self setAlphaValue:0.2];
+#endif
+ [self setIgnoresMouseEvents:NO];
+ [self setAcceptsMouseMovedEvents:YES];
+ [self setHasShadow:NO];
+ [self setOpaque:NO];
+
+ /* Set the highest level, so on top of everything */
+ [self setLevel:NSScreenSaverWindowLevel];
+
+ view = [[GimpPickView alloc] initWithButton:button controller:controller];
+ [self setContentView:view];
+ [self makeFirstResponder:view];
+ [view release];
+
+ [self disableCursorRects];
+ }
+
+ return self;
+}
+
+/* Borderless windows cannot become key/main by default, so we force it
+ * to make it so. We need this to receive events.
+ */
+- (BOOL)canBecomeKeyWindow
+{
+ return YES;
+}
+
+- (BOOL)canBecomeMainWindow
+{
+ return YES;
+}
+
+- (void)windowDidBecomeKey:(NSNotification *)aNotification
+{
+ /* We cannot use the usual Cocoa method for handling cursor updates,
+ * since the GDK Quartz backend is interfering. Additionally, because
+ * one of the screen-spanning windows pops up under the mouse pointer this
+ * view will not receive a MouseEntered event. So, we synthesize such
+ * an event here and the view can set the mouse pointer in response to
+ * this. So, this event only has to be synthesized once and only for
+ * the window that pops up under the mouse cursor. Synthesizing multiple
+ * times messes up the mouse cursor stack.
+ *
+ * We cannot set the mouse pointer at this moment, because the GDK window
+ * will still receive an MouseExited event in which turn it will modify
+ * the cursor. So, with this synthesized event we also ensure we set
+ * the mouse cursor *after* the GDK window has manipulated the cursor.
+ */
+ NSEvent *event;
+
+ if (!controller.firstBecameKey ||
+ !NSPointInRect ([NSEvent mouseLocation], self.frame))
+ return;
+
+ controller.firstBecameKey = NO;
+
+ event = [NSEvent enterExitEventWithType:NSMouseEntered
+ location:[self mouseLocationOutsideOfEventStream]
+ modifierFlags:0
+ timestamp:[[NSApp currentEvent] timestamp]
+ windowNumber:self.windowNumber
+ context:nil
+ eventNumber:0
+ trackingNumber:(NSInteger)[[self contentView] area]
+ userData:nil];
+
+ [NSApp postEvent:event atStart:NO];
+}
+@end
+
+
+/* To properly handle multi-monitor setups we need to create a
+ * GimpPickWindow for each monitor (NSScreen). This is necessary because
+ * a window on Mac OS X (tested on 10.9) cannot span more than one
+ * monitor, so any approach that attempts to create one large window
+ * spanning all monitors cannot work. So, we have to create multiple
+ * windows in case of multi-monitor setups and these different windows
+ * are managed by GimpPickWindowController.
+ */
+@implementation GimpPickWindowController
+
+@synthesize firstBecameKey;
+@synthesize cursor;
+
+- (id)initWithButton:(GimpPickButton *)_button;
+{
+ self = [super init];
+
+ if (self)
+ {
+ firstBecameKey = YES;
+ button = _button;
+ cursor = [GimpPickWindowController makePickCursor];
+
+ windows = [[NSMutableArray alloc] init];
+
+ for (NSScreen *screen in [NSScreen screens])
+ {
+ GimpPickWindow *window;
+
+ window = [[GimpPickWindow alloc] initWithButton:button
+ forScreen:screen
+ withController:self];
+
+ [window orderFrontRegardless];
+ [window makeMainWindow];
+
+ [windows addObject:window];
+ }
+
+ [self updateKeyWindow];
+ }
+
+ return self;
+}
+
+- (void)updateKeyWindow
+{
+ for (GimpPickWindow *window in windows)
+ {
+ if (NSPointInRect ([NSEvent mouseLocation], window.frame))
+ [window makeKeyWindow];
+ }
+}
+
+- (void)shutdown
+{
+ GtkWidget *window;
+
+ for (GimpPickWindow *window in windows)
+ [window close];
+
+ [windows release];
+
+ if (cursor)
+ [cursor release];
+
+ /* Give focus back to the window containing the pick button */
+ window = gtk_widget_get_toplevel (GTK_WIDGET (button));
+ gtk_window_present_with_time (GTK_WINDOW (window), GDK_CURRENT_TIME);
+
+ [self release];
+}
+
++ (NSCursor *)makePickCursor
+{
+ GBytes *bytes = NULL;
+ GError *error = NULL;
+
+ bytes = g_resources_lookup_data ("/org/gimp/color-picker-cursors-raw/cursor-color-picker.png",
+ G_RESOURCE_LOOKUP_FLAGS_NONE, &error);
+
+ if (bytes)
+ {
+ NSData *data = [NSData dataWithBytes:g_bytes_get_data (bytes, NULL)
+ length:g_bytes_get_size (bytes)];
+ NSImage *image = [[NSImage alloc] initWithData:data];
+ NSCursor *cursor = [[NSCursor alloc] initWithImage:image hotSpot:NSMakePoint(1, 30)];
+
+ [image release];
+ g_bytes_unref (bytes);
+
+ return [cursor retain];
+ }
+ else
+ {
+ g_critical ("Failed to create cursor image: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ return NULL;
+}
+@end
+
+/* entry point to this file, called from gimppickbutton.c */
+void
+_gimp_pick_button_quartz_pick (GimpPickButton *button)
+{
+ GimpPickWindowController *controller;
+ NSAutoreleasePool *pool;
+
+ pool = [[NSAutoreleasePool alloc] init];
+
+ controller = [[GimpPickWindowController alloc] initWithButton:button];
+
+ [pool release];
+}
diff --git a/libgimpwidgets/gimppickbutton-quartz.h b/libgimpwidgets/gimppickbutton-quartz.h
new file mode 100644
index 0000000..53f1ce4
--- /dev/null
+++ b/libgimpwidgets/gimppickbutton-quartz.h
@@ -0,0 +1,25 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppickbutton-quartz.h
+ * Copyright (C) 2017 Jehan <jehan@gimp.org>
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* Private header file which is not meant to be exported. */
+#ifndef __GIMP_PICK_BUTTON_QUARTZ_H__
+#define __GIMP_PICK_BUTTON_QUARTZ_H__
+
+void _gimp_pick_button_quartz_pick (GimpPickButton *button);
+
+#endif /* __GIMP_PICK_BUTTON_QUARTZ_H__ */
+
+
diff --git a/libgimpwidgets/gimppickbutton-win32.c b/libgimpwidgets/gimppickbutton-win32.c
new file mode 100644
index 0000000..d5ca8b7
--- /dev/null
+++ b/libgimpwidgets/gimppickbutton-win32.c
@@ -0,0 +1,1135 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppickbutton-win32.c
+ * Copyright (C) 2022 Luca Bacci <luca.bacci@outlook.com>
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpcolor/gimpcolor.h"
+
+#include "gimpwidgetstypes.h"
+#include "gimppickbutton.h"
+#include "gimppickbutton-win32.h"
+
+#include <sdkddkver.h>
+#include <windows.h>
+#include <windowsx.h>
+
+#include <stdint.h>
+
+/*
+ * NOTES:
+ *
+ * This implementation is based on gtk/gtkcolorpickerwin32.c from GTK.
+ *
+ * We install a low-level mouse hook so that color picking continues
+ * even when switching active windows.
+ *
+ * Beyond that, we also create keep-above, input-only HWNDs and place
+ * them on the screen to cover each monitor. This is done to show our
+ * custom cursor and to avoid giving input to other windows: that way
+ * the desktop appears "frozen" while picking the color.
+ *
+ * Finally, we also set up a low-level keyboard hook to dismiss color-
+ * picking mode whenever the user presses <ESC>.
+ *
+ * Note that low-level hooks for mouse and keyboard do not use any DLL
+ * injection and are thus non-invasive.
+ *
+ * For GTK4: consider using an appropriate GDK surface for input-only
+ * windows. This'd enable us to also get Wintab input when CXO_SYSTEM
+ * is not set.
+ */
+
+typedef struct
+{
+ HMONITOR handle;
+ wchar_t *device;
+ HDC hdc;
+ POINT screen_origin;
+ int logical_width;
+ int logical_height;
+ int physical_width;
+ int physical_height;
+} MonitorData;
+
+GList *pickers;
+HHOOK mouse_hook;
+HHOOK keyboard_hook;
+
+ATOM notif_window_class;
+HWND notif_window_handle;
+GArray *monitors;
+
+ATOM input_window_class;
+GArray *input_window_handles;
+
+/* Utils */
+static HMODULE this_module (void);
+static MonitorData * monitors_find_for_logical_point (POINT logical);
+static MonitorData * monitors_find_for_physical_point (POINT physical);
+static POINT logical_to_physical (MonitorData *data,
+ POINT logical);
+static void destroy_window (gpointer ptr);
+
+/* Mouse cursor */
+static HCURSOR create_cursor_from_rgba32_pixbuf (GdkPixbuf *pixbuf,
+ POINT hotspot);
+static HCURSOR create_cursor (void);
+
+/* Low-level mouse hook */
+static LRESULT CALLBACK mouse_procedure (int nCode,
+ WPARAM wParam,
+ LPARAM lParam);
+static gboolean ensure_mouse_hook (void);
+static void remove_mouse_hook (void);
+
+/* Low-level keyboard hook */
+static LRESULT CALLBACK keyboard_procedure (int nCode,
+ WPARAM wParam,
+ LPARAM lParam);
+static gboolean ensure_keyboard_hook (void);
+static void remove_keyboard_hook (void);
+
+/* Input-only window */
+static LRESULT CALLBACK input_window_procedure (HWND hwnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam);
+static gboolean ensure_input_window_class (void);
+static void remove_input_window_class (void);
+static HWND create_input_window (POINT origin,
+ int width,
+ int height);
+
+/* Hidden notification window */
+static LRESULT CALLBACK notif_window_procedure (HWND hwnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam);
+static gboolean ensure_notif_window_class (void);
+static void remove_notif_window_class (void);
+static gboolean ensure_notif_window (void);
+static void remove_notif_window (void);
+
+/* Monitor enumeration and discovery */
+static void monitor_data_free (gpointer ptr);
+static BOOL CALLBACK enum_monitor_callback (HMONITOR hMonitor,
+ HDC hDC,
+ RECT *pRect,
+ LPARAM lParam);
+static GArray* enumerate_monitors (void);
+
+/* GimpPickButtonWin32 */
+static void ensure_input_windows (void);
+static void remove_input_windows (void);
+static void ensure_monitors (void);
+static void remove_monitors (void);
+static void ensure_screen_data (void);
+static void remove_screen_data (void);
+static void screen_changed (void);
+static void ensure_screen_tracking (void);
+static void remove_screen_tracking (void);
+static GimpRGB pick_color_with_gdi (POINT physical_point);
+static void user_picked (MonitorData *monitor,
+ POINT physical_point);
+void _gimp_pick_button_win32_pick (GimpPickButton *button);
+static void stop_picking (void);
+
+/* {{{ Utils */
+
+/* Gets a handle to the module containing this code.
+ * Works regardless if we're building a shared or
+ * static library */
+
+static HMODULE
+this_module (void)
+{
+ extern IMAGE_DOS_HEADER __ImageBase;
+ return (HMODULE) &__ImageBase;
+}
+
+static MonitorData*
+monitors_find_for_logical_point (POINT logical)
+{
+ HMONITOR monitor_handle;
+ guint i;
+
+ monitor_handle = MonitorFromPoint (logical, MONITOR_DEFAULTTONULL);
+ if (!monitor_handle)
+ return NULL;
+
+ ensure_monitors ();
+
+ for (i = 0; i < monitors->len; i++)
+ {
+ MonitorData *data = &g_array_index (monitors, MonitorData, i);
+
+ if (data->handle == monitor_handle)
+ return data;
+ }
+
+ return NULL;
+}
+
+static MonitorData*
+monitors_find_for_physical_point (POINT physical)
+{
+ guint i;
+
+ ensure_monitors ();
+
+ for (i = 0; i < monitors->len; i++)
+ {
+ MonitorData *data = &g_array_index (monitors, MonitorData, i);
+ RECT physical_rect;
+
+ physical_rect.left = data->screen_origin.x;
+ physical_rect.top = data->screen_origin.y;
+ physical_rect.right = physical_rect.left + data->physical_width;
+ physical_rect.bottom = physical_rect.top + data->physical_height;
+
+ /* TODO: tolerance */
+ if (PtInRect (&physical_rect, physical))
+ return data;
+ }
+
+ return NULL;
+}
+
+static POINT
+logical_to_physical (MonitorData *data,
+ POINT logical)
+{
+ POINT physical = logical;
+
+ if (data &&
+ (data->logical_width != data->physical_width) &&
+ data->logical_width > 0 && data->physical_width > 0)
+ {
+ double dpi_scale = (double) data->physical_width /
+ (double) data->logical_width;
+
+ physical.x = logical.x * dpi_scale;
+ physical.y = logical.y * dpi_scale;
+ }
+
+ return physical;
+}
+
+static void
+destroy_window (gpointer ptr)
+{
+ HWND *hwnd = (HWND*) ptr;
+ DestroyWindow (*hwnd);
+}
+
+/* }}}
+ * {{{ Mouse cursor */
+
+static HCURSOR
+create_cursor_from_rgba32_pixbuf (GdkPixbuf *pixbuf,
+ POINT hotspot)
+{
+ struct {
+ BITMAPV5HEADER header;
+ RGBQUAD colors[2];
+ } info;
+ HBITMAP bitmap = NULL;
+ uint8_t *bitmap_data = NULL;
+ HBITMAP mask = NULL;
+ uint8_t *mask_data = NULL;
+ unsigned int mask_stride;
+ HDC hdc = NULL;
+ int width;
+ int height;
+ int size;
+ int stride;
+ const uint8_t *pixbuf_data = NULL;
+ int i_offset;
+ int j_offset;
+ int i;
+ int j;
+ ICONINFO icon_info;
+ HICON icon = NULL;
+
+ if (gdk_pixbuf_get_n_channels (pixbuf) != 4)
+ goto cleanup;
+
+ hdc = GetDC (NULL);
+ if (!hdc)
+ goto cleanup;
+
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+ stride = gdk_pixbuf_get_rowstride (pixbuf);
+ size = MAX (width, height);
+ pixbuf_data = gdk_pixbuf_read_pixels (pixbuf);
+
+ memset (&info, 0, sizeof (info));
+ info.header.bV5Size = sizeof (info.header);
+ info.header.bV5Width = size;
+ info.header.bV5Height = size;
+ /* Since Windows XP the OS supports mouse cursors as 32bpp
+ * bitmaps with alpha channel that are laid out as BGRA32
+ * (assuming little-endian) */
+ info.header.bV5Planes = 1;
+ info.header.bV5BitCount = 32;
+ info.header.bV5Compression = BI_BITFIELDS;
+ info.header.bV5BlueMask = 0x000000FF;
+ info.header.bV5GreenMask = 0x0000FF00;
+ info.header.bV5RedMask = 0x00FF0000;
+ info.header.bV5AlphaMask = 0xFF000000;
+
+ bitmap = CreateDIBSection (hdc, (BITMAPINFO*) &info, DIB_RGB_COLORS,
+ (void**) &bitmap_data, NULL, 0);
+ if (!bitmap || !bitmap_data)
+ {
+ g_warning ("CreateDIBSection failed with error code %u",
+ (unsigned) GetLastError ());
+ goto cleanup;
+ }
+
+ /* We also need to provide a proper mask HBITMAP, otherwise
+ * CreateIconIndirect() fails with ERROR_INVALID_PARAMETER.
+ * This mask bitmap is a bitfield indicating whether the */
+ memset (&info, 0, sizeof (info));
+ info.header.bV5Size = sizeof (info.header);
+ info.header.bV5Width = size;
+ info.header.bV5Height = size;
+ info.header.bV5Planes = 1;
+ info.header.bV5BitCount = 1;
+ info.header.bV5Compression = BI_RGB;
+ info.colors[0] = (RGBQUAD){0x00, 0x00, 0x00};
+ info.colors[1] = (RGBQUAD){0xFF, 0xFF, 0xFF};
+
+ mask = CreateDIBSection (hdc, (BITMAPINFO*) &info, DIB_RGB_COLORS,
+ (void**) &mask_data, NULL, 0);
+ if (!mask || !mask_data)
+ {
+ g_warning ("CreateDIBSection failed with error code %u",
+ (unsigned) GetLastError ());
+ goto cleanup;
+ }
+
+ /* MSDN says mask rows are aligned to LONG boundaries */
+ mask_stride = (((size + 31) & ~31) >> 3);
+
+ if (width > height)
+ {
+ i_offset = 0;
+ j_offset = (width - height) / 2;
+ }
+ else
+ {
+ i_offset = (height - width) / 2;
+ j_offset = 0;
+ }
+
+ for (j = 0; j < height; j++) /* loop over all the bitmap rows */
+ {
+ uint8_t *bitmap_row = bitmap_data + 4 * ((j + j_offset) * size + i_offset);
+ uint8_t *mask_byte = mask_data + (j + j_offset) * mask_stride + i_offset / 8;
+ unsigned int mask_bit = (0x80 >> (i_offset % 8));
+ const uint8_t *pixbuf_row = pixbuf_data + (height - j - 1) * stride;
+
+ for (i = 0; i < width; i++) /* loop over the current bitmap row */
+ {
+ uint8_t *bitmap_pixel = bitmap_row + 4 * i;
+ const uint8_t *pixbuf_pixel = pixbuf_row + 4 * i;
+
+ /* Assign to destination pixel from source pixel
+ * by also swapping channels as appropriate */
+ bitmap_pixel[0] = pixbuf_pixel[2];
+ bitmap_pixel[1] = pixbuf_pixel[1];
+ bitmap_pixel[2] = pixbuf_pixel[0];
+ bitmap_pixel[3] = pixbuf_pixel[3];
+
+ if (pixbuf_pixel[3] == 0)
+ mask_byte[0] |= mask_bit; /* turn ON bit */
+ else
+ mask_byte[0] &= ~mask_bit; /* turn OFF bit */
+
+ mask_bit >>= 1;
+ if (mask_bit == 0)
+ {
+ mask_bit = 0x80;
+ mask_byte++;
+ }
+ }
+ }
+
+ memset (&icon_info, 0, sizeof (icon_info));
+ icon_info.fIcon = FALSE;
+ icon_info.xHotspot = hotspot.x;
+ icon_info.yHotspot = hotspot.y;
+ icon_info.hbmMask = mask;
+ icon_info.hbmColor = bitmap;
+
+ icon = CreateIconIndirect (&icon_info);
+ if (!icon)
+ {
+ g_warning ("CreateIconIndirect failed with error code %u",
+ (unsigned) GetLastError ());
+ goto cleanup;
+ }
+
+cleanup:
+ if (mask)
+ DeleteObject (mask);
+
+ if (bitmap)
+ DeleteObject (bitmap);
+
+ if (hdc)
+ ReleaseDC (NULL, hdc);
+
+ return (HCURSOR) icon;
+}
+
+static HCURSOR
+create_cursor (void)
+{
+ GdkPixbuf *pixbuf = NULL;
+ GError *error = NULL;
+ HCURSOR cursor = NULL;
+
+ pixbuf = gdk_pixbuf_new_from_resource ("/org/gimp/color-picker-cursors/cursor-color-picker.png",
+ &error);
+ if (!pixbuf)
+ {
+ g_critical ("gdk_pixbuf_new_from_resource failed: %s",
+ error ? error->message : "unknown error");
+ goto cleanup;
+ }
+ g_clear_error (&error);
+
+ cursor = create_cursor_from_rgba32_pixbuf (pixbuf, (POINT){ 1, 30 });
+
+cleanup:
+ g_clear_error (&error);
+ g_clear_object (&pixbuf);
+
+ return cursor;
+}
+
+/* }}}
+ * {{{ Low-level mouse hook */
+
+/* This mouse procedure can detect clicks made on any
+ * application window. Countrary to mouse capture, this
+ * method continues to work even after switching active
+ * windows. */
+
+static LRESULT CALLBACK
+mouse_procedure (int nCode,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ if (nCode == HC_ACTION)
+ {
+ MSLLHOOKSTRUCT *info = (MSLLHOOKSTRUCT*) lParam;
+
+ switch (wParam)
+ {
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_XBUTTONDOWN:
+ {
+ POINT physical;
+ MonitorData *data;
+
+ if (pickers == NULL)
+ break;
+
+ /* A low-level mouse hook always receives points in
+ * per-monitor DPI-aware screen coordinates, regardless of
+ * the DPI awareness setting of the application. */
+ physical = info->pt;
+
+ data = monitors_find_for_physical_point (physical);
+ if (!data)
+ g_message ("Captured point (%ld, %ld) doesn't belong to any monitor",
+ (long) physical.x, (long) physical.y);
+
+ user_picked (data, physical);
+
+ /* It's safe to remove a hook from within its callback.
+ * Anyway this can even be called from an idle callback,
+ * as the hook does nothing if there are no pickers.
+ * (In that case also the ensure functions have to be
+ * scheduled in an idle callback) */
+ stop_picking ();
+
+ return (wParam == WM_XBUTTONDOWN) ? TRUE : 0;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return CallNextHookEx (NULL, nCode, wParam, lParam);
+}
+
+static gboolean
+ensure_mouse_hook (void)
+{
+ if (!mouse_hook)
+ {
+ mouse_hook = SetWindowsHookEx (WH_MOUSE_LL, mouse_procedure, this_module (), 0);
+ if (!mouse_hook)
+ {
+ g_warning ("SetWindowsHookEx failed with error code %u",
+ (unsigned) GetLastError ());
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+remove_mouse_hook (void)
+{
+ if (mouse_hook)
+ {
+ if (!UnhookWindowsHookEx (mouse_hook))
+ {
+ g_warning ("UnhookWindowsHookEx failed with error code %u",
+ (unsigned) GetLastError ());
+ return;
+ }
+
+ mouse_hook = NULL;
+ }
+}
+
+/* }}}
+ * {{{ Low-level keyboard hook */
+
+/* This is used to stop picking anytime the user presses ESC */
+
+static LRESULT CALLBACK
+keyboard_procedure (int nCode,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ if (nCode == HC_ACTION)
+ {
+ KBDLLHOOKSTRUCT *info = (KBDLLHOOKSTRUCT*) lParam;
+
+ switch (wParam)
+ {
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ if (info->vkCode == VK_ESCAPE)
+ {
+ stop_picking ();
+ return 1;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return CallNextHookEx(NULL, nCode, wParam, lParam);
+}
+
+static gboolean
+ensure_keyboard_hook (void)
+{
+ if (!keyboard_hook)
+ {
+ keyboard_hook = SetWindowsHookEx (WH_KEYBOARD_LL, keyboard_procedure, this_module (), 0);
+ if (!keyboard_hook)
+ {
+ g_warning ("SetWindowsHookEx failed with error code %u",
+ (unsigned) GetLastError ());
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+remove_keyboard_hook (void)
+{
+ if (keyboard_hook)
+ {
+ if (!UnhookWindowsHookEx (keyboard_hook))
+ {
+ g_warning ("UnhookWindowsHookEx failed with error code %u",
+ (unsigned) GetLastError ());
+ return;
+ }
+
+ keyboard_hook = NULL;
+ }
+}
+
+/* }}}
+ * {{{ Input-only windows */
+
+/* Those input-only windows are placed to cover each monitor on
+ * the screen and serve two purposes: display our custom mouse
+ * cursor and freeze the desktop by avoiding interaction of the
+ * mouse with other applications */
+
+static LRESULT CALLBACK
+input_window_procedure (HWND hwnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_NCCREATE:
+ /* The shell automatically hides the taskbar when a window
+ * covers the entire area of a monitor (fullscreened). In
+ * order to avoid that, we can set a special property on
+ * the window
+ * See the docs for ITaskbarList2::MarkFullscreenWindow()
+ * on MSDN for more informations */
+
+ if (!SetPropW (hwnd, L"NonRudeHWND", (HANDLE) TRUE))
+ g_warning_once ("SetPropW failed with error code %u",
+ (unsigned) GetLastError ());
+ break;
+
+ case WM_NCDESTROY:
+ /* We have to remove window properties manually before the
+ * window gets destroyed */
+
+ if (!RemovePropW (hwnd, L"NonRudeHWND"))
+ g_warning_once ("SetPropW failed with error code %u",
+ (unsigned) GetLastError ());
+ break;
+
+ /* Avoid any drawing at all. Here we block processing of the
+ * default window procedure, although we set the NULL_BRUSH
+ * as the window class's background brush, so even the default
+ * window procedure wouldn't draw anything at all */
+
+ case WM_ERASEBKGND:
+ return 1;
+ case WM_PAINT:
+ {
+ #if 1
+ PAINTSTRUCT ps;
+ BeginPaint(hwnd, &ps);
+ EndPaint(hwnd, &ps);
+ #else
+ UINT flags = RDW_VALIDATE |
+ RDW_NOFRAME |
+ RDW_NOERASE;
+ RedrawWindow (hwnd, NULL, NULL, flags);
+ #endif
+ }
+ return 0;
+ #if 0
+ case WM_SYNCPAINT:
+ return 0;
+ #endif
+
+ case WM_MOUSEACTIVATE:
+ /* This can be useful in case we want to avoid
+ * using a mouse hook */
+ return MA_NOACTIVATE;
+
+ /* Anytime we detect a mouse click, pick the color. Note that
+ * this is rarely used as the mouse hook would process the
+ * clicks before that */
+
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_XBUTTONDOWN:
+ {
+ POINT logical = (POINT){GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam)};
+ MonitorData *data = monitors_find_for_logical_point (logical);
+ POINT physical = logical_to_physical (data, logical);
+
+ if (!data)
+ g_message ("Captured point (%ld, %ld) doesn't belong to any monitor",
+ (long) logical.x, (long) logical.y);
+
+ user_picked (data, physical);
+
+ stop_picking ();
+ }
+
+ return (uMsg == WM_XBUTTONDOWN) ? TRUE : 0;
+ default:
+ break;
+ }
+
+ return DefWindowProcW (hwnd, uMsg, wParam, lParam);
+}
+
+static gboolean
+ensure_input_window_class (void)
+{
+ if (!input_window_class)
+ {
+ WNDCLASSEXW wndclassex;
+ HCURSOR cursor = create_cursor ();
+
+ memset (&wndclassex, 0, sizeof (wndclassex));
+ wndclassex.cbSize = sizeof (wndclassex);
+ wndclassex.hInstance = this_module ();
+ wndclassex.lpszClassName = L"GimpPickButtonInputWindowClass";
+ wndclassex.lpfnWndProc = input_window_procedure;
+ wndclassex.hbrBackground = GetStockObject (NULL_BRUSH);
+ wndclassex.hCursor = cursor ?
+ cursor :
+ LoadImageW (NULL,
+ (LPCWSTR)(guintptr)IDC_CROSS,
+ IMAGE_CURSOR, 0, 0,
+ LR_DEFAULTSIZE | LR_SHARED);
+
+ input_window_class = RegisterClassExW (&wndclassex);
+ if (input_window_class == 0)
+ {
+ g_warning ("RegisterClassExW failed with error code %u",
+ (unsigned) GetLastError ());
+
+ if (cursor)
+ DestroyCursor (cursor);
+
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+remove_input_window_class (void)
+{
+ if (input_window_class)
+ {
+ LPCWSTR name = (LPCWSTR)(guintptr)input_window_class;
+ UnregisterClassW (name, this_module ());
+ input_window_class = 0;
+ }
+}
+
+/* create_input_window expects logical screen coordinates */
+
+static HWND
+create_input_window (POINT origin,
+ int width,
+ int height)
+{
+ DWORD stylex = WS_EX_NOACTIVATE | WS_EX_TOPMOST;
+ LPCWSTR wclass;
+ LPCWSTR title = L"Gimp Input Window";
+ DWORD style = WS_POPUP;
+ HWND hwnd;
+
+ if (!ensure_input_window_class ())
+ return FALSE;
+
+ /* This must go after the ensure_input_window_class */
+ wclass = (LPCWSTR)(guintptr)input_window_class;
+
+ hwnd = CreateWindowExW (stylex, wclass, title, style,
+ origin.x, origin.y, width, height,
+ NULL, NULL, this_module (), NULL);
+ if (hwnd == NULL)
+ {
+ g_warning_once ("CreateWindowExW failed with error code %u",
+ (unsigned) GetLastError ());
+ return FALSE;
+ }
+
+ ShowWindow (hwnd, SW_SHOWNOACTIVATE);
+
+ return hwnd;
+}
+
+/* }}}
+ * {{{ Hidden notification window */
+
+/* We setup a hidden window to listen for WM_DISPLAYCAHNGE
+ * messages and reposition the input-only windows on the
+ * screen */
+
+static LRESULT CALLBACK
+notif_window_procedure (HWND hwnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_DISPLAYCHANGE:
+ screen_changed ();
+ break;
+ default:
+ break;
+ }
+
+ return DefWindowProcW (hwnd, uMsg, wParam, lParam);
+}
+
+static gboolean
+ensure_notif_window_class (void)
+{
+ if (!notif_window_class)
+ {
+ WNDCLASSEXW wndclassex;
+
+ memset (&wndclassex, 0, sizeof (wndclassex));
+ wndclassex.cbSize = sizeof (wndclassex);
+ wndclassex.hInstance = this_module ();
+ wndclassex.lpszClassName = L"GimpPickButtonNotifWindowClass";
+ wndclassex.lpfnWndProc = notif_window_procedure;
+
+ notif_window_class = RegisterClassExW (&wndclassex);
+ if (notif_window_class == 0)
+ {
+ g_warning ("RegisterClassExW failed with error code %u",
+ (unsigned) GetLastError ());
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+remove_notif_window_class (void)
+{
+ if (notif_window_class)
+ {
+ LPCWSTR name = (LPCWSTR)(guintptr)notif_window_class;
+ UnregisterClassW (name, this_module ());
+ notif_window_class = 0;
+ }
+}
+
+static gboolean
+ensure_notif_window (void)
+{
+ if (!ensure_notif_window_class ())
+ return FALSE;
+
+ if (!notif_window_handle)
+ {
+ DWORD stylex = 0;
+ LPCWSTR wclass = (LPCWSTR)(guintptr)notif_window_class;
+ LPCWSTR title = L"Gimp Notifications Window";
+ DWORD style = WS_POPUP;
+
+ notif_window_handle = CreateWindowExW (stylex, wclass,
+ title, style,
+ 0, 0, 1, 1,
+ NULL, NULL,
+ this_module (),
+ NULL);
+ if (notif_window_handle == NULL)
+ {
+ g_warning_once ("CreateWindowExW failed with error code %u",
+ (unsigned) GetLastError ());
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+remove_notif_window (void)
+{
+ if (notif_window_handle)
+ {
+ DestroyWindow (notif_window_handle);
+ notif_window_handle = NULL;
+ }
+
+ remove_notif_window_class ();
+}
+
+/* }}}
+ * {{{ Monitor enumeration and discovery */
+
+static void
+monitor_data_free (gpointer ptr)
+{
+ MonitorData *data = (MonitorData*) ptr;
+
+ if (data->device)
+ free (data->device);
+}
+
+static BOOL CALLBACK
+enum_monitor_callback (HMONITOR hMonitor,
+ HDC hDC,
+ RECT *pRect,
+ LPARAM lParam)
+{
+ GArray *result = (GArray*) lParam;
+ MonitorData data;
+ MONITORINFOEXW info;
+ DEVMODEW devmode;
+
+ const BOOL CALLBACK_CONTINUE = TRUE;
+ const BOOL CALLBACK_STOP = FALSE;
+
+ if (!pRect)
+ return CALLBACK_CONTINUE;
+
+ if (IsRectEmpty (pRect))
+ return CALLBACK_CONTINUE;
+
+ memset (&data, 0, sizeof (data));
+ data.handle = hMonitor;
+ data.hdc = hDC;
+ data.screen_origin.x = pRect->left;
+ data.screen_origin.y = pRect->top;
+ data.logical_width = pRect->right - pRect->left;
+ data.logical_height = pRect->bottom - pRect->top;
+
+ memset (&info, 0, sizeof (info));
+ info.cbSize = sizeof (info);
+ if (!GetMonitorInfoW (hMonitor, (MONITORINFO*) &info))
+ {
+ g_warning_once ("GetMonitorInfo failed with error code %u",
+ (unsigned) GetLastError ());
+ return CALLBACK_CONTINUE;
+ }
+
+ data.device = _wcsdup (info.szDevice);
+
+ memset (&devmode, 0, sizeof (devmode));
+ devmode.dmSize = sizeof (devmode);
+ if (!EnumDisplaySettingsExW (info.szDevice,
+ ENUM_CURRENT_SETTINGS,
+ &devmode, EDS_ROTATEDMODE))
+ {
+ g_warning_once ("EnumDisplaySettingsEx failed with error code %u",
+ (unsigned) GetLastError ());
+ return CALLBACK_CONTINUE;
+ }
+
+ if (devmode.dmPelsWidth)
+ data.physical_width = devmode.dmPelsWidth;
+
+ if (devmode.dmPelsHeight)
+ data.physical_height = devmode.dmPelsHeight;
+
+ g_array_append_val (result, data);
+
+ if (result->len >= 100)
+ return CALLBACK_STOP;
+
+ return CALLBACK_CONTINUE;
+}
+
+static GArray*
+enumerate_monitors (void)
+{
+ int count_monitors;
+ guint length_hint;
+ GArray *result;
+
+ count_monitors = GetSystemMetrics (SM_CMONITORS);
+
+ if (count_monitors > 0 && count_monitors < 100)
+ length_hint = (guint) count_monitors;
+ else
+ length_hint = 1;
+
+ result = g_array_sized_new (FALSE, TRUE, sizeof (MonitorData), length_hint);
+ g_array_set_clear_func (result, monitor_data_free);
+
+ if (!EnumDisplayMonitors (NULL, NULL, enum_monitor_callback, (LPARAM) result))
+ {
+ g_warning ("EnumDisplayMonitors failed with error code %u",
+ (unsigned) GetLastError ());
+ }
+
+ return result;
+}
+
+static void
+ensure_input_windows (void)
+{
+ ensure_monitors ();
+
+ if (!input_window_handles)
+ {
+ guint i;
+
+ input_window_handles = g_array_sized_new (FALSE, TRUE,
+ sizeof (HWND),
+ monitors->len);
+ g_array_set_clear_func (input_window_handles, destroy_window);
+
+ for (i = 0; i < monitors->len; i++)
+ {
+ MonitorData *data = &g_array_index (monitors, MonitorData, i);
+ HWND hwnd = create_input_window (data->screen_origin,
+ data->logical_width,
+ data->logical_height);
+
+ if (hwnd)
+ g_array_append_val (input_window_handles, hwnd);
+ }
+ }
+}
+
+static void
+remove_input_windows (void)
+{
+ if (input_window_handles)
+ {
+ g_array_free (input_window_handles, TRUE);
+ input_window_handles = NULL;
+ }
+}
+
+static void
+ensure_monitors (void)
+{
+ if (!monitors)
+ monitors = enumerate_monitors ();
+}
+
+static void
+remove_monitors (void)
+{
+ if (monitors)
+ {
+ g_array_free (monitors, TRUE);
+ monitors = NULL;
+ }
+}
+
+static void
+ensure_screen_data (void)
+{
+ ensure_monitors ();
+ ensure_input_windows ();
+}
+
+static void
+remove_screen_data (void)
+{
+ remove_input_windows ();
+ remove_monitors ();
+}
+
+static void
+screen_changed (void)
+{
+ remove_screen_data ();
+ ensure_screen_data ();
+}
+
+static void
+ensure_screen_tracking (void)
+{
+ ensure_notif_window ();
+ screen_changed ();
+}
+
+static void
+remove_screen_tracking (void)
+{
+ remove_notif_window ();
+ remove_screen_data ();
+}
+
+/* }}}
+ * {{{ GimpPickButtonWin32 */
+
+/* pick_color_with_gdi is based on the GDI GetPixel() function.
+ * Note that GDI only returns 8bit per-channel color data, but
+ * as of today there's no documented method to retrieve colors
+ * at higher bit depths. */
+
+static GimpRGB
+pick_color_with_gdi (POINT physical_point)
+{
+ GimpRGB rgb;
+ COLORREF color;
+ HDC hdc;
+
+ hdc = GetDC (HWND_DESKTOP);
+
+ if (!(GetDeviceCaps (hdc, RASTERCAPS) & RC_BITBLT))
+ g_warning_once ("RC_BITBLT capability missing from device context");
+
+ color = GetPixel (hdc, physical_point.x, physical_point.y);
+
+ gimp_rgba_set_uchar (&rgb, GetRValue (color), GetGValue (color), GetBValue (color), 255);
+
+ ReleaseDC (HWND_DESKTOP, hdc);
+
+ return rgb;
+}
+
+static void
+user_picked (MonitorData *monitor,
+ POINT physical_point)
+{
+ GimpRGB rgb;
+ GList *l;
+
+ /* Currently unused */
+ (void) monitor;
+
+ rgb = pick_color_with_gdi (physical_point);
+
+ for (l = pickers; l != NULL; l = l->next)
+ {
+ GimpPickButton *button = GIMP_PICK_BUTTON (l->data);
+ g_signal_emit_by_name (button, "color-picked", &rgb);
+ }
+}
+
+/* entry point to this file, called from gimppickbutton.c */
+void
+_gimp_pick_button_win32_pick (GimpPickButton *button)
+{
+ ensure_mouse_hook ();
+ ensure_keyboard_hook ();
+ ensure_screen_tracking ();
+
+ if (g_list_find (pickers, button))
+ return;
+
+ pickers = g_list_prepend (pickers,
+ g_object_ref (button));
+}
+
+static void
+stop_picking (void)
+{
+ remove_screen_tracking ();
+ remove_keyboard_hook ();
+ remove_mouse_hook ();
+
+ g_list_free_full (pickers, g_object_unref);
+ pickers = NULL;
+}
diff --git a/libgimpwidgets/gimppickbutton-win32.h b/libgimpwidgets/gimppickbutton-win32.h
new file mode 100644
index 0000000..eaeaa77
--- /dev/null
+++ b/libgimpwidgets/gimppickbutton-win32.h
@@ -0,0 +1,23 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppickbutton-win32.h
+ * Copyright (C) 2022 Luca Bacci <luca.bacci@outlook.com>
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* Private header file which is not meant to be exported. */
+#ifndef __GIMP_PICK_BUTTON_WIN32_H__
+#define __GIMP_PICK_BUTTON_WIN32_H__
+
+G_GNUC_INTERNAL void _gimp_pick_button_win32_pick (GimpPickButton *button);
+
+#endif /* __GIMP_PICK_BUTTON_WIN32_H__ */
diff --git a/libgimpwidgets/gimppickbutton-xdg.c b/libgimpwidgets/gimppickbutton-xdg.c
new file mode 100644
index 0000000..4ef42ab
--- /dev/null
+++ b/libgimpwidgets/gimppickbutton-xdg.c
@@ -0,0 +1,152 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppickbutton-xdg.c
+ * Copyright (C) 2021 Niels De Graef <nielsdegraef@gmail.com>
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpcolor/gimpcolor.h"
+
+#include "gimpwidgetstypes.h"
+#include "gimppickbutton.h"
+#include "gimppickbutton-default.h"
+#include "gimppickbutton-xdg.h"
+
+#include "libgimp/libgimp-intl.h"
+
+gboolean
+_gimp_pick_button_xdg_available (void)
+{
+ GDBusProxy *proxy = NULL;
+
+ proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ NULL,
+ "org.freedesktop.portal.Desktop",
+ "/org/freedesktop/portal/desktop",
+ "org.freedesktop.portal.Screenshot",
+ NULL, NULL);
+
+ if (proxy)
+ {
+ GError *error = NULL;
+
+ g_dbus_proxy_call_sync (proxy, "org.freedesktop.DBus.Peer.Ping",
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL, &error);
+ if (! error)
+ return TRUE;
+
+ g_clear_error (&error);
+
+ g_object_unref (proxy);
+ proxy = NULL;
+ }
+
+ return FALSE;
+}
+
+static void
+pick_color_xdg_dbus_signal (GDBusProxy *proxy,
+ gchar *sender_name,
+ gchar *signal_name,
+ GVariant *parameters,
+ GimpPickButton *button)
+{
+ if (g_strcmp0 (signal_name, "Response") == 0)
+ {
+ GVariant *results;
+ guint32 response;
+
+ g_variant_get (parameters, "(u@a{sv})",
+ &response,
+ &results);
+
+ /* Possible values:
+ * 0: Success, the request is carried out
+ * 1: The user cancelled the interaction
+ * 2: The user interaction was ended in some other way
+ * Cf. https://github.com/flatpak/xdg-desktop-portal/blob/master/data/org.freedesktop.portal.Request.xml
+ */
+ if (response == 0)
+ {
+ GimpRGB color;
+
+ if (g_variant_lookup (results, "color", "(ddd)", &color.r, &color.g, &color.b))
+ {
+ g_signal_emit_by_name (button, "color-picked", &color);
+ }
+ }
+
+ g_variant_unref (results);
+ /* Quit anyway. */
+ gtk_main_quit ();
+ }
+}
+
+/* entry point to this file, called from gimppickbutton.c */
+void
+_gimp_pick_button_xdg_pick (GimpPickButton *button)
+{
+ GDBusProxy *proxy = NULL;
+ GVariant *retval = NULL;
+ gchar *opath = NULL;
+
+ proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ NULL,
+ "org.freedesktop.portal.Desktop",
+ "/org/freedesktop/portal/desktop",
+ "org.freedesktop.portal.Screenshot",
+ NULL, NULL);
+ if (!proxy)
+ {
+ return;
+ }
+
+ retval = g_dbus_proxy_call_sync (proxy, "PickColor",
+ g_variant_new ("(sa{sv})", "", NULL),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL, NULL);
+ g_clear_object (&proxy);
+ if (retval)
+ {
+ g_variant_get (retval, "(o)", &opath);
+ g_variant_unref (retval);
+ }
+
+ if (opath)
+ {
+ GDBusProxy *proxy2 = NULL;
+
+ proxy2 = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ NULL,
+ "org.freedesktop.portal.Desktop",
+ opath,
+ "org.freedesktop.portal.Request",
+ NULL, NULL);
+ g_signal_connect (proxy2, "g-signal",
+ G_CALLBACK (pick_color_xdg_dbus_signal),
+ button);
+
+ gtk_main ();
+ g_object_unref (proxy2);
+ g_free (opath);
+ }
+}
diff --git a/libgimpwidgets/gimppickbutton-xdg.h b/libgimpwidgets/gimppickbutton-xdg.h
new file mode 100644
index 0000000..394665e
--- /dev/null
+++ b/libgimpwidgets/gimppickbutton-xdg.h
@@ -0,0 +1,25 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppickbutton-xdg.h
+ * Copyright (C) 2021 Niels De Graef <nielsdegraef@gmail.com>
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* Private header file which is not meant to be exported. */
+#ifndef __GIMP_PICK_BUTTON_XDG_H__
+#define __GIMP_PICK_BUTTON_XDG_H__
+
+gboolean _gimp_pick_button_xdg_available (void);
+void _gimp_pick_button_xdg_pick (GimpPickButton *button);
+
+#endif /* __GIMP_PICK_BUTTON_XDG_H__ */
+
diff --git a/libgimpwidgets/gimppickbutton.c b/libgimpwidgets/gimppickbutton.c
new file mode 100644
index 0000000..1611f5d
--- /dev/null
+++ b/libgimpwidgets/gimppickbutton.c
@@ -0,0 +1,188 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppickbutton.c
+ * Copyright (C) 2002 Michael Natterer <mitch@gimp.org>
+ *
+ * based on gtk+/gtk/gtkcolorsel.c
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#endif
+
+#include "libgimpcolor/gimpcolor.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpcairo-utils.h"
+#include "gimphelpui.h"
+#include "gimpicons.h"
+#include "gimppickbutton.h"
+
+#if defined (GDK_WINDOWING_QUARTZ)
+#include "gimppickbutton-quartz.h"
+#elif defined (GDK_WINDOWING_WIN32)
+#include "gimppickbutton-win32.h"
+#else
+#include "gimppickbutton-default.h"
+#include "gimppickbutton-kwin.h"
+#include "gimppickbutton-xdg.h"
+#endif
+
+#include "libgimp/libgimp-intl.h"
+
+/**
+ * SECTION: gimppickbutton
+ * @title: GimpPickButton
+ * @short_description: Widget to pick a color from screen.
+ *
+ * #GimpPickButton is a specialized button. When clicked, it changes
+ * the cursor to a color-picker pipette and allows the user to pick a
+ * color from any point on the screen.
+ **/
+
+
+enum
+{
+ COLOR_PICKED,
+ LAST_SIGNAL
+};
+
+static void gimp_pick_button_dispose (GObject *object);
+
+static void gimp_pick_button_clicked (GtkButton *button);
+
+
+G_DEFINE_TYPE (GimpPickButton, gimp_pick_button, GTK_TYPE_BUTTON)
+
+#define parent_class gimp_pick_button_parent_class
+
+static guint pick_button_signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+gimp_pick_button_class_init (GimpPickButtonClass* klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkButtonClass *button_class = GTK_BUTTON_CLASS (klass);
+
+ /**
+ * GimpPickButton::color-picked:
+ * @gimppickbutton: the object which received the signal.
+ * @arg1: pointer to a #GimpRGB structure that holds the picked color
+ *
+ * This signal is emitted when the user has picked a color.
+ **/
+ pick_button_signals[COLOR_PICKED] =
+ g_signal_new ("color-picked",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpPickButtonClass, color_picked),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1,
+ G_TYPE_POINTER);
+
+ object_class->dispose = gimp_pick_button_dispose;
+
+ button_class->clicked = gimp_pick_button_clicked;
+
+ klass->color_picked = NULL;
+}
+
+static void
+gimp_pick_button_init (GimpPickButton *button)
+{
+ GtkWidget *image;
+
+ image = gtk_image_new_from_icon_name (GIMP_ICON_COLOR_PICK_FROM_SCREEN,
+ GTK_ICON_SIZE_BUTTON);
+ gtk_container_add (GTK_CONTAINER (button), image);
+ gtk_widget_show (image);
+
+ gimp_help_set_help_data (GTK_WIDGET (button),
+ _("Click the eyedropper, then click a color "
+ "anywhere on your screen to select that color."),
+ NULL);
+}
+
+static void
+gimp_pick_button_dispose (GObject *object)
+{
+ GimpPickButton *button = GIMP_PICK_BUTTON (object);
+
+ if (button->cursor)
+ {
+ gdk_cursor_unref (button->cursor);
+ button->cursor = NULL;
+ }
+
+ if (button->grab_widget)
+ {
+ gtk_widget_destroy (button->grab_widget);
+ button->grab_widget = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gimp_pick_button_clicked (GtkButton *button)
+{
+#if defined (GDK_WINDOWING_QUARTZ)
+ _gimp_pick_button_quartz_pick (GIMP_PICK_BUTTON (button));
+#elif defined (GDK_WINDOWING_WIN32)
+ _gimp_pick_button_win32_pick (GIMP_PICK_BUTTON (button));
+#elif defined (GDK_WINDOWING_X11)
+ /* It's a bit weird as we use the default pick code both in first and
+ * last cases. It's because when running GIMP on X11 in particular,
+ * the portals don't return color space information. So the returned
+ * color is in the display space, not in the current image space and
+ * we have no way to convert the data back (well if running on X11, we
+ * could still get a profile from the display, but if there are
+ * several displays, we can't know for sure where the color was picked
+ * from.).
+ * See: https://github.com/flatpak/xdg-desktop-portal/issues/862
+ */
+ _gimp_pick_button_default_pick (GIMP_PICK_BUTTON (button));
+#else
+ if (_gimp_pick_button_xdg_available ())
+ _gimp_pick_button_xdg_pick (GIMP_PICK_BUTTON (button));
+ else if (_gimp_pick_button_kwin_available ())
+ _gimp_pick_button_kwin_pick (GIMP_PICK_BUTTON (button));
+ else
+ _gimp_pick_button_default_pick (GIMP_PICK_BUTTON (button));
+#endif
+}
+
+
+/* public functions */
+
+/**
+ * gimp_pick_button_new:
+ *
+ * Creates a new #GimpPickButton widget.
+ *
+ * Returns: A new #GimpPickButton widget.
+ **/
+GtkWidget *
+gimp_pick_button_new (void)
+{
+ return g_object_new (GIMP_TYPE_PICK_BUTTON, NULL);
+}
diff --git a/libgimpwidgets/gimppickbutton.h b/libgimpwidgets/gimppickbutton.h
new file mode 100644
index 0000000..e5114a0
--- /dev/null
+++ b/libgimpwidgets/gimppickbutton.h
@@ -0,0 +1,69 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppickbutton.h
+ * Copyright (C) 2002 Michael Natterer <mitch@gimp.org>
+ *
+ * based on gtk-2-0/gtk/gtkcolorsel.c
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PICK_BUTTON_H__
+#define __GIMP_PICK_BUTTON_H__
+
+G_BEGIN_DECLS
+
+
+#define GIMP_TYPE_PICK_BUTTON (gimp_pick_button_get_type ())
+#define GIMP_PICK_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_PICK_BUTTON, GimpPickButton))
+#define GIMP_PICK_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_PICK_BUTTON, GimpPickButtonClass))
+#define GIMP_IS_PICK_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_PICK_BUTTON))
+#define GIMP_IS_PICK_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_PICK_BUTTON))
+#define GIMP_PICK_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_PICK_BUTTON, GimpPickButtonClass))
+
+
+typedef struct _GimpPickButtonClass GimpPickButtonClass;
+
+struct _GimpPickButton
+{
+ GtkButton parent_instance;
+
+ /*< private >*/
+ GdkCursor *cursor;
+ GtkWidget *grab_widget;
+};
+
+struct _GimpPickButtonClass
+{
+ GtkButtonClass parent_class;
+
+ void (* color_picked) (GimpPickButton *button,
+ const GimpRGB *color);
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_pick_button_get_type (void) G_GNUC_CONST;
+GtkWidget * gimp_pick_button_new (void);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PICK_BUTTON_H__ */
diff --git a/libgimpwidgets/gimppixmap.c b/libgimpwidgets/gimppixmap.c
new file mode 100644
index 0000000..d841885
--- /dev/null
+++ b/libgimpwidgets/gimppixmap.c
@@ -0,0 +1,179 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppixmap.c
+ * Copyright (C) 2000 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#undef GSEAL_ENABLE
+#undef GTK_DISABLE_DEPRECATED
+#undef GDK_DISABLE_DEPRECATED
+
+#include <stdio.h>
+
+#include <gtk/gtk.h>
+
+#include "gimpwidgetstypes.h"
+
+#undef GIMP_DISABLE_DEPRECATED
+#include "gimppixmap.h"
+
+
+/**
+ * SECTION: gimppixmap
+ * @title: GimpPixmap
+ * @short_description: Widget which creates a #GtkPixmap from XPM data.
+ * @see_also: gimp_pixmap_button_new(), #GtkPixmap
+ *
+ * Widget which creates a #GtkPixmap from XPM data.
+ *
+ * Use this widget instead of #GtkPixmap if you don't want to worry
+ * about the parent container's "realized" state.
+ *
+ * Note that the drawback of the easy interface is that the actual
+ * #GdkPixmap and it's mask have to be constructed every time you call
+ * gimp_pixmap_new() and cannot be cached in memory without doing bad
+ * hacks.
+ **/
+
+
+static void gimp_pixmap_realize (GtkWidget *widget);
+static void gimp_pixmap_create_from_xpm_d (GimpPixmap *pixmap);
+
+
+G_DEFINE_TYPE (GimpPixmap, gimp_pixmap, GTK_TYPE_IMAGE)
+
+#define parent_class gimp_pixmap_parent_class
+
+
+static void
+gimp_pixmap_class_init (GimpPixmapClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ widget_class->realize = gimp_pixmap_realize;
+}
+
+static void
+gimp_pixmap_init (GimpPixmap *pixmap)
+{
+ pixmap->xpm_data = NULL;
+}
+
+/**
+ * gimp_pixmap_new:
+ * @xpm_data: A pointer to a XPM data structure as found in XPM files.
+ *
+ * Creates a new #GimpPixmap widget.
+ *
+ * Returns: A pointer to the new #GimpPixmap widget.
+ **/
+GtkWidget *
+gimp_pixmap_new (gchar **xpm_data)
+{
+ GimpPixmap *pixmap = g_object_new (GIMP_TYPE_PIXMAP, NULL);
+
+ gimp_pixmap_set (pixmap, xpm_data);
+
+ return GTK_WIDGET (pixmap);
+}
+
+/**
+ * gimp_pixmap_set:
+ * @pixmap: The pixmap widget you want to set the new xpm_data for.
+ * @xpm_data: A pointer to a XPM data structure as found in XPM files.
+ *
+ * Sets a new image for an existing #GimpPixmap widget.
+ **/
+void
+gimp_pixmap_set (GimpPixmap *pixmap,
+ gchar **xpm_data)
+{
+ g_return_if_fail (GIMP_IS_PIXMAP (pixmap));
+
+ pixmap->xpm_data = xpm_data;
+
+ GTK_WIDGET (pixmap)->requisition.width = 0;
+ GTK_WIDGET (pixmap)->requisition.height = 0;
+
+ if (! gtk_widget_get_realized (GTK_WIDGET (pixmap)))
+ {
+ if (xpm_data)
+ {
+ gint width, height;
+
+ if (sscanf (xpm_data[0], "%d %d", &width, &height) != 2)
+ {
+ g_warning ("%s: passed pointer is no XPM data", G_STRFUNC);
+ }
+ else
+ {
+ gint xpad, ypad;
+
+ gtk_misc_get_padding (GTK_MISC (pixmap), &xpad, &ypad);
+
+ GTK_WIDGET (pixmap)->requisition.width = width + xpad * 2;
+ GTK_WIDGET (pixmap)->requisition.height = height + ypad * 2;
+ }
+ }
+ }
+ else
+ {
+ gimp_pixmap_create_from_xpm_d (pixmap);
+ }
+}
+
+static void
+gimp_pixmap_realize (GtkWidget *widget)
+{
+ if (GTK_WIDGET_CLASS (parent_class)->realize)
+ GTK_WIDGET_CLASS (parent_class)->realize (widget);
+
+ gimp_pixmap_create_from_xpm_d (GIMP_PIXMAP (widget));
+}
+
+static void
+gimp_pixmap_create_from_xpm_d (GimpPixmap *pixmap)
+{
+ GtkStyle *style;
+ GdkPixmap *gdk_pixmap = NULL;
+ GdkBitmap *mask = NULL;
+
+ if (pixmap->xpm_data)
+ {
+ GtkWidget *widget;
+
+ widget = GTK_WIDGET (pixmap);
+
+ style = gtk_widget_get_style (widget);
+
+ gdk_pixmap = gdk_pixmap_create_from_xpm_d (gtk_widget_get_window (widget),
+ &mask,
+ &style->bg[GTK_STATE_NORMAL],
+ pixmap->xpm_data);
+ }
+
+ gtk_image_set_from_pixmap (GTK_IMAGE (pixmap), gdk_pixmap, mask);
+
+ if (gdk_pixmap)
+ g_object_unref (gdk_pixmap);
+
+ if (mask)
+ g_object_unref (mask);
+}
diff --git a/libgimpwidgets/gimppixmap.h b/libgimpwidgets/gimppixmap.h
new file mode 100644
index 0000000..3b71389
--- /dev/null
+++ b/libgimpwidgets/gimppixmap.h
@@ -0,0 +1,78 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppixmap.h
+ * Copyright (C) 2000 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef GIMP_DISABLE_DEPRECATED
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PIXMAP_H__
+#define __GIMP_PIXMAP_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+#define GIMP_TYPE_PIXMAP (gimp_pixmap_get_type ())
+#define GIMP_PIXMAP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_PIXMAP, GimpPixmap))
+#define GIMP_PIXMAP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_PIXMAP, GimpPixmapClass))
+#define GIMP_IS_PIXMAP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_PIXMAP))
+#define GIMP_IS_PIXMAP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_PIXMAP))
+#define GIMP_PIXMAP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_PIXMAP, GimpPixmapClass))
+
+
+typedef struct _GimpPixmapClass GimpPixmapClass;
+
+struct _GimpPixmap
+{
+ GtkImage parent_instance;
+
+ gchar **xpm_data;
+};
+
+struct _GimpPixmapClass
+{
+ GtkImageClass parent_class;
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_pixmap_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_pixmap_new (gchar **xpm_data);
+
+void gimp_pixmap_set (GimpPixmap *pixmap,
+ gchar **xpm_data);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PIXMAP_H__ */
+
+#endif /* GIMP_DISABLE_DEPRECATED */
+
diff --git a/libgimpwidgets/gimppreview.c b/libgimpwidgets/gimppreview.c
new file mode 100644
index 0000000..75b38ea
--- /dev/null
+++ b/libgimpwidgets/gimppreview.c
@@ -0,0 +1,851 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppreview.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpmath/gimpmath.h"
+
+#include "gimpwidgets.h"
+
+#include "gimppreview.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimppreview
+ * @title: GimpPreview
+ * @short_description: A widget providing a #GimpPreviewArea plus
+ * framework to update the preview.
+ *
+ * A widget providing a #GimpPreviewArea plus framework to update the
+ * preview.
+ **/
+
+
+#define DEFAULT_SIZE 200
+#define PREVIEW_TIMEOUT 200
+
+
+enum
+{
+ INVALIDATED,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_UPDATE
+};
+
+typedef struct
+{
+ GtkWidget *controls;
+} GimpPreviewPrivate;
+
+#define GIMP_PREVIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GIMP_TYPE_PREVIEW, GimpPreviewPrivate))
+
+
+static void gimp_preview_class_init (GimpPreviewClass *klass);
+static void gimp_preview_init (GimpPreview *preview);
+static void gimp_preview_dispose (GObject *object);
+static void gimp_preview_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_preview_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_preview_direction_changed (GtkWidget *widget,
+ GtkTextDirection prev_dir);
+static gboolean gimp_preview_popup_menu (GtkWidget *widget);
+
+static void gimp_preview_area_realize (GtkWidget *widget,
+ GimpPreview *preview);
+static void gimp_preview_area_unrealize (GtkWidget *widget,
+ GimpPreview *preview);
+static void gimp_preview_area_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation,
+ GimpPreview *preview);
+static void gimp_preview_area_set_cursor (GimpPreview *preview);
+static gboolean gimp_preview_area_event (GtkWidget *area,
+ GdkEvent *event,
+ GimpPreview *preview);
+
+static void gimp_preview_toggle_callback (GtkWidget *toggle,
+ GimpPreview *preview);
+
+static void gimp_preview_notify_checks (GimpPreview *preview);
+
+static gboolean gimp_preview_invalidate_now (GimpPreview *preview);
+static void gimp_preview_real_set_cursor (GimpPreview *preview);
+static void gimp_preview_real_transform (GimpPreview *preview,
+ gint src_x,
+ gint src_y,
+ gint *dest_x,
+ gint *dest_y);
+static void gimp_preview_real_untransform (GimpPreview *preview,
+ gint src_x,
+ gint src_y,
+ gint *dest_x,
+ gint *dest_y);
+
+
+static guint preview_signals[LAST_SIGNAL] = { 0 };
+
+static GtkBoxClass *parent_class = NULL;
+
+
+GType
+gimp_preview_get_type (void)
+{
+ static GType preview_type = 0;
+
+ if (! preview_type)
+ {
+ const GTypeInfo preview_info =
+ {
+ sizeof (GimpPreviewClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gimp_preview_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GimpPreview),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gimp_preview_init,
+ };
+
+ preview_type = g_type_register_static (GTK_TYPE_BOX,
+ "GimpPreview",
+ &preview_info,
+ G_TYPE_FLAG_ABSTRACT);
+ }
+
+ return preview_type;
+}
+
+static void
+gimp_preview_class_init (GimpPreviewClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ preview_signals[INVALIDATED] =
+ g_signal_new ("invalidated",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpPreviewClass, invalidated),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ object_class->dispose = gimp_preview_dispose;
+ object_class->get_property = gimp_preview_get_property;
+ object_class->set_property = gimp_preview_set_property;
+
+ widget_class->direction_changed = gimp_preview_direction_changed;
+ widget_class->popup_menu = gimp_preview_popup_menu;
+
+ klass->draw = NULL;
+ klass->draw_thumb = NULL;
+ klass->draw_buffer = NULL;
+ klass->set_cursor = gimp_preview_real_set_cursor;
+ klass->transform = gimp_preview_real_transform;
+ klass->untransform = gimp_preview_real_untransform;
+
+ g_type_class_add_private (object_class, sizeof (GimpPreviewPrivate));
+
+ g_object_class_install_property (object_class,
+ PROP_UPDATE,
+ g_param_spec_boolean ("update",
+ "Update",
+ "Whether the preview should update automatically",
+ TRUE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ gtk_widget_class_install_style_property (widget_class,
+ g_param_spec_int ("size",
+ "Size",
+ "The preview's size",
+ 1, 1024,
+ DEFAULT_SIZE,
+ GIMP_PARAM_READABLE));
+}
+
+static void
+gimp_preview_init (GimpPreview *preview)
+{
+ GimpPreviewPrivate *priv = GIMP_PREVIEW_GET_PRIVATE (preview);
+ GtkWidget *frame;
+ gdouble xalign = 0.0;
+
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (preview),
+ GTK_ORIENTATION_VERTICAL);
+
+ gtk_box_set_homogeneous (GTK_BOX (preview), FALSE);
+ gtk_box_set_spacing (GTK_BOX (preview), 6);
+
+ if (gtk_widget_get_direction (GTK_WIDGET (preview)) == GTK_TEXT_DIR_RTL)
+ xalign = 1.0;
+
+ preview->frame = gtk_aspect_frame_new (NULL, xalign, 0.0, 1.0, TRUE);
+ gtk_frame_set_shadow_type (GTK_FRAME (preview->frame), GTK_SHADOW_NONE);
+ gtk_box_pack_start (GTK_BOX (preview), preview->frame, TRUE, TRUE, 0);
+ gtk_widget_show (preview->frame);
+
+ preview->table = gtk_table_new (3, 2, FALSE);
+ gtk_table_set_row_spacing (GTK_TABLE (preview->table), 1, 3);
+ gtk_container_add (GTK_CONTAINER (preview->frame), preview->table);
+ gtk_widget_show (preview->table);
+
+ preview->timeout_id = 0;
+
+ preview->xmin = preview->ymin = 0;
+ preview->xmax = preview->ymax = 1;
+ preview->width = preview->xmax - preview->xmin;
+ preview->height = preview->ymax - preview->ymin;
+
+ preview->xoff = 0;
+ preview->yoff = 0;
+
+ preview->default_cursor = NULL;
+
+ /* preview area */
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_table_attach (GTK_TABLE (preview->table), frame, 0, 1, 0, 1,
+ GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
+ gtk_widget_show (frame);
+
+ preview->area = gimp_preview_area_new ();
+ gtk_container_add (GTK_CONTAINER (frame), preview->area);
+ gtk_widget_show (preview->area);
+
+ g_signal_connect_swapped (preview->area, "notify::check-size",
+ G_CALLBACK (gimp_preview_notify_checks),
+ preview);
+ g_signal_connect_swapped (preview->area, "notify::check-type",
+ G_CALLBACK (gimp_preview_notify_checks),
+ preview);
+
+ gtk_widget_add_events (preview->area,
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_HINT_MASK |
+ GDK_BUTTON_MOTION_MASK);
+
+ g_signal_connect (preview->area, "event",
+ G_CALLBACK (gimp_preview_area_event),
+ preview);
+
+ g_signal_connect (preview->area, "realize",
+ G_CALLBACK (gimp_preview_area_realize),
+ preview);
+ g_signal_connect (preview->area, "unrealize",
+ G_CALLBACK (gimp_preview_area_unrealize),
+ preview);
+
+ g_signal_connect_data (preview->area, "realize",
+ G_CALLBACK (gimp_preview_area_set_cursor),
+ preview, NULL, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
+
+ g_signal_connect (preview->area, "size-allocate",
+ G_CALLBACK (gimp_preview_area_size_allocate),
+ preview);
+
+ g_signal_connect_data (preview->area, "size-allocate",
+ G_CALLBACK (gimp_preview_area_set_cursor),
+ preview, NULL, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
+
+ priv->controls = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_table_attach (GTK_TABLE (preview->table), priv->controls, 0, 2, 2, 3,
+ GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+ gtk_widget_show (priv->controls);
+
+ /* toggle button to (de)activate the instant preview */
+ preview->toggle = gtk_check_button_new_with_mnemonic (_("_Preview"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (preview->toggle),
+ preview->update_preview);
+ gtk_box_pack_start (GTK_BOX (priv->controls), preview->toggle, TRUE, TRUE, 0);
+ gtk_widget_show (preview->toggle);
+
+ g_signal_connect (preview->toggle, "toggled",
+ G_CALLBACK (gimp_preview_toggle_callback),
+ preview);
+}
+
+static void
+gimp_preview_dispose (GObject *object)
+{
+ GimpPreview *preview = GIMP_PREVIEW (object);
+
+ if (preview->timeout_id)
+ {
+ g_source_remove (preview->timeout_id);
+ preview->timeout_id = 0;
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gimp_preview_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpPreview *preview = GIMP_PREVIEW (object);
+
+ switch (property_id)
+ {
+ case PROP_UPDATE:
+ g_value_set_boolean (value, preview->update_preview);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_preview_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpPreview *preview = GIMP_PREVIEW (object);
+
+ switch (property_id)
+ {
+ case PROP_UPDATE:
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (preview->toggle),
+ g_value_get_boolean (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_preview_direction_changed (GtkWidget *widget,
+ GtkTextDirection prev_dir)
+{
+ GimpPreview *preview = GIMP_PREVIEW (widget);
+ gdouble xalign = 0.0;
+
+ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+ xalign = 1.0;
+
+ gtk_aspect_frame_set (GTK_ASPECT_FRAME (preview->frame),
+ xalign, 0.0, 1.0, TRUE);
+}
+
+static gboolean
+gimp_preview_popup_menu (GtkWidget *widget)
+{
+ GimpPreview *preview = GIMP_PREVIEW (widget);
+
+ gimp_preview_area_menu_popup (GIMP_PREVIEW_AREA (preview->area), NULL);
+
+ return TRUE;
+}
+
+static void
+gimp_preview_area_realize (GtkWidget *widget,
+ GimpPreview *preview)
+{
+ GdkDisplay *display = gtk_widget_get_display (widget);
+
+ g_return_if_fail (preview->cursor_busy == NULL);
+
+ preview->cursor_busy = gdk_cursor_new_for_display (display, GDK_WATCH);
+
+}
+
+static void
+gimp_preview_area_unrealize (GtkWidget *widget,
+ GimpPreview *preview)
+{
+ if (preview->cursor_busy)
+ {
+ gdk_cursor_unref (preview->cursor_busy);
+ preview->cursor_busy = NULL;
+ }
+}
+
+static void
+gimp_preview_area_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation,
+ GimpPreview *preview)
+{
+ gint width = preview->xmax - preview->xmin;
+ gint height = preview->ymax - preview->ymin;
+
+ preview->width = MIN (width, allocation->width);
+ preview->height = MIN (height, allocation->height);
+
+ gimp_preview_draw (preview);
+ gimp_preview_invalidate (preview);
+}
+
+static void
+gimp_preview_area_set_cursor (GimpPreview *preview)
+{
+ GIMP_PREVIEW_GET_CLASS (preview)->set_cursor (preview);
+}
+
+static gboolean
+gimp_preview_area_event (GtkWidget *area,
+ GdkEvent *event,
+ GimpPreview *preview)
+{
+ GdkEventButton *button_event = (GdkEventButton *) event;
+
+ switch (event->type)
+ {
+ case GDK_BUTTON_PRESS:
+ switch (button_event->button)
+ {
+ case 3:
+ gimp_preview_area_menu_popup (GIMP_PREVIEW_AREA (area), button_event);
+ return TRUE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+gimp_preview_toggle_callback (GtkWidget *toggle,
+ GimpPreview *preview)
+{
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (toggle)))
+ {
+ preview->update_preview = TRUE;
+
+ g_object_notify (G_OBJECT (preview), "update");
+
+ if (preview->timeout_id)
+ g_source_remove (preview->timeout_id);
+
+ gimp_preview_invalidate_now (preview);
+ }
+ else
+ {
+ preview->update_preview = FALSE;
+
+ g_object_notify (G_OBJECT (preview), "update");
+
+ gimp_preview_draw (preview);
+ }
+}
+
+static void
+gimp_preview_notify_checks (GimpPreview *preview)
+{
+ gimp_preview_draw (preview);
+ gimp_preview_invalidate (preview);
+}
+
+static gboolean
+gimp_preview_invalidate_now (GimpPreview *preview)
+{
+ GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (preview));
+ GimpPreviewClass *class = GIMP_PREVIEW_GET_CLASS (preview);
+
+ gimp_preview_draw (preview);
+
+ preview->timeout_id = 0;
+
+ if (toplevel && gtk_widget_get_realized (toplevel))
+ {
+ gdk_window_set_cursor (gtk_widget_get_window (toplevel),
+ preview->cursor_busy);
+ gdk_window_set_cursor (gtk_widget_get_window (preview->area),
+ preview->cursor_busy);
+
+ gdk_flush ();
+
+ g_signal_emit (preview, preview_signals[INVALIDATED], 0);
+
+ class->set_cursor (preview);
+ gdk_window_set_cursor (gtk_widget_get_window (toplevel), NULL);
+ }
+ else
+ {
+ g_signal_emit (preview, preview_signals[INVALIDATED], 0);
+ }
+
+ return FALSE;
+}
+
+static void
+gimp_preview_real_set_cursor (GimpPreview *preview)
+{
+ if (gtk_widget_get_realized (preview->area))
+ gdk_window_set_cursor (gtk_widget_get_window (preview->area),
+ preview->default_cursor);
+}
+
+static void
+gimp_preview_real_transform (GimpPreview *preview,
+ gint src_x,
+ gint src_y,
+ gint *dest_x,
+ gint *dest_y)
+{
+ *dest_x = src_x - preview->xoff - preview->xmin;
+ *dest_y = src_y - preview->yoff - preview->ymin;
+}
+
+static void
+gimp_preview_real_untransform (GimpPreview *preview,
+ gint src_x,
+ gint src_y,
+ gint *dest_x,
+ gint *dest_y)
+{
+ *dest_x = src_x + preview->xoff + preview->xmin;
+ *dest_y = src_y + preview->yoff + preview->ymin;
+}
+
+/**
+ * gimp_preview_set_update:
+ * @preview: a #GimpPreview widget
+ * @update: %TRUE if the preview should invalidate itself when being
+ * scrolled or when gimp_preview_invalidate() is being called
+ *
+ * Sets the state of the "Preview" check button.
+ *
+ * Since: 2.2
+ **/
+void
+gimp_preview_set_update (GimpPreview *preview,
+ gboolean update)
+{
+ g_return_if_fail (GIMP_IS_PREVIEW (preview));
+
+ g_object_set (preview,
+ "update", update,
+ NULL);
+}
+
+/**
+ * gimp_preview_get_update:
+ * @preview: a #GimpPreview widget
+ *
+ * Return value: the state of the "Preview" check button.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_preview_get_update (GimpPreview *preview)
+{
+ g_return_val_if_fail (GIMP_IS_PREVIEW (preview), FALSE);
+
+ return preview->update_preview;
+}
+
+/**
+ * gimp_preview_set_bounds:
+ * @preview: a #GimpPreview widget
+ * @xmin: the minimum X value
+ * @ymin: the minimum Y value
+ * @xmax: the maximum X value
+ * @ymax: the maximum Y value
+ *
+ * Sets the lower and upper limits for the previewed area. The
+ * difference between the upper and lower value is used to set the
+ * maximum size of the #GimpPreviewArea used in the @preview.
+ *
+ * Since: 2.2
+ **/
+void
+gimp_preview_set_bounds (GimpPreview *preview,
+ gint xmin,
+ gint ymin,
+ gint xmax,
+ gint ymax)
+{
+ g_return_if_fail (GIMP_IS_PREVIEW (preview));
+ g_return_if_fail (xmax > xmin);
+ g_return_if_fail (ymax > ymin);
+
+ preview->xmin = xmin;
+ preview->ymin = ymin;
+ preview->xmax = xmax;
+ preview->ymax = ymax;
+
+ gimp_preview_area_set_max_size (GIMP_PREVIEW_AREA (preview->area),
+ xmax - xmin,
+ ymax - ymin);
+}
+
+/**
+ * gimp_preview_get_size:
+ * @preview: a #GimpPreview widget
+ * @width: return location for the preview area width
+ * @height: return location for the preview area height
+ *
+ * Since: 2.2
+ **/
+void
+gimp_preview_get_size (GimpPreview *preview,
+ gint *width,
+ gint *height)
+{
+ g_return_if_fail (GIMP_IS_PREVIEW (preview));
+
+ if (width)
+ *width = preview->width;
+
+ if (height)
+ *height = preview->height;
+}
+
+/**
+ * gimp_preview_get_position:
+ * @preview: a #GimpPreview widget
+ * @x: return location for the horizontal offset
+ * @y: return location for the vertical offset
+ *
+ * Since: 2.2
+ **/
+void
+gimp_preview_get_position (GimpPreview *preview,
+ gint *x,
+ gint *y)
+{
+ g_return_if_fail (GIMP_IS_PREVIEW (preview));
+
+ if (x)
+ *x = preview->xoff + preview->xmin;
+
+ if (y)
+ *y = preview->yoff + preview->ymin;
+}
+
+/**
+ * gimp_preview_transform:
+ * @preview: a #GimpPreview widget
+ * @src_x: horizontal position on the previewed image
+ * @src_y: vertical position on the previewed image
+ * @dest_x: returns the transformed horizontal position
+ * @dest_y: returns the transformed vertical position
+ *
+ * Transforms from image to widget coordinates.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_preview_transform (GimpPreview *preview,
+ gint src_x,
+ gint src_y,
+ gint *dest_x,
+ gint *dest_y)
+{
+ g_return_if_fail (GIMP_IS_PREVIEW (preview));
+ g_return_if_fail (dest_x != NULL && dest_y != NULL);
+
+ GIMP_PREVIEW_GET_CLASS (preview)->transform (preview,
+ src_x, src_y, dest_x, dest_y);
+}
+
+/**
+ * gimp_preview_untransform:
+ * @preview: a #GimpPreview widget
+ * @src_x: horizontal position relative to the preview area's origin
+ * @src_y: vertical position relative to preview area's origin
+ * @dest_x: returns the untransformed horizontal position
+ * @dest_y: returns the untransformed vertical position
+ *
+ * Transforms from widget to image coordinates.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_preview_untransform (GimpPreview *preview,
+ gint src_x,
+ gint src_y,
+ gint *dest_x,
+ gint *dest_y)
+{
+ g_return_if_fail (GIMP_IS_PREVIEW (preview));
+ g_return_if_fail (dest_x != NULL && dest_y != NULL);
+
+ GIMP_PREVIEW_GET_CLASS (preview)->untransform (preview,
+ src_x, src_y, dest_x, dest_y);
+}
+
+/**
+ * gimp_preview_get_area:
+ * @preview: a #GimpPreview widget
+ *
+ * In most cases, you shouldn't need to access the #GimpPreviewArea
+ * that is being used in the @preview. Sometimes however, you need to.
+ * For example if you want to receive mouse events from the area. In
+ * such cases, use gimp_preview_get_area().
+ *
+ * Return value: a pointer to the #GimpPreviewArea used in the @preview.
+ *
+ * Since: 2.4
+ **/
+GtkWidget *
+gimp_preview_get_area (GimpPreview *preview)
+{
+ g_return_val_if_fail (GIMP_IS_PREVIEW (preview), NULL);
+
+ return preview->area;
+}
+
+/**
+ * gimp_preview_draw:
+ * @preview: a #GimpPreview widget
+ *
+ * Calls the GimpPreview::draw method. GimpPreview itself doesn't
+ * implement a default draw method so the behaviour is determined by
+ * the derived class implementing this method.
+ *
+ * #GimpDrawablePreview implements gimp_preview_draw() by drawing the
+ * original, unmodified drawable to the @preview.
+ *
+ * Since: 2.2
+ **/
+void
+gimp_preview_draw (GimpPreview *preview)
+{
+ GimpPreviewClass *class = GIMP_PREVIEW_GET_CLASS (preview);
+
+ if (class->draw)
+ class->draw (preview);
+}
+
+/**
+ * gimp_preview_draw_buffer:
+ * @preview: a #GimpPreview widget
+ * @buffer: a pixel buffer the size of the preview
+ * @rowstride: the @buffer's rowstride
+ *
+ * Calls the GimpPreview::draw_buffer method. GimpPreview itself
+ * doesn't implement this method so the behaviour is determined by the
+ * derived class implementing this method.
+ *
+ * Since: 2.2
+ **/
+void
+gimp_preview_draw_buffer (GimpPreview *preview,
+ const guchar *buffer,
+ gint rowstride)
+{
+ GimpPreviewClass *class = GIMP_PREVIEW_GET_CLASS (preview);
+
+ if (class->draw_buffer)
+ class->draw_buffer (preview, buffer, rowstride);
+}
+
+/**
+ * gimp_preview_invalidate:
+ * @preview: a #GimpPreview widget
+ *
+ * This function starts or renews a short low-priority timeout. When
+ * the timeout expires, the GimpPreview::invalidated signal is emitted
+ * which will usually cause the @preview to be updated.
+ *
+ * This function does nothing unless the "Preview" button is checked.
+ *
+ * During the emission of the signal a busy cursor is set on the
+ * toplevel window containing the @preview and on the preview area
+ * itself.
+ *
+ * Since: 2.2
+ **/
+void
+gimp_preview_invalidate (GimpPreview *preview)
+{
+ g_return_if_fail (GIMP_IS_PREVIEW (preview));
+
+ if (preview->update_preview)
+ {
+ if (preview->timeout_id)
+ g_source_remove (preview->timeout_id);
+
+ preview->timeout_id =
+ g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE, PREVIEW_TIMEOUT,
+ (GSourceFunc) gimp_preview_invalidate_now,
+ preview, NULL);
+ }
+}
+
+/**
+ * gimp_preview_set_default_cursor:
+ * @preview: a #GimpPreview widget
+ * @cursor: a #GdkCursor or %NULL
+ *
+ * Sets the default mouse cursor for the preview. Note that this will
+ * be overridden by a %GDK_FLEUR if the preview has scrollbars, or by a
+ * %GDK_WATCH when the preview is invalidated.
+ *
+ * Since: 2.2
+ **/
+void
+gimp_preview_set_default_cursor (GimpPreview *preview,
+ GdkCursor *cursor)
+{
+ g_return_if_fail (GIMP_IS_PREVIEW (preview));
+
+ g_set_object (&preview->default_cursor, cursor);
+}
+
+/**
+ * gimp_preview_get_controls:
+ * @preview: a #GimpPreview widget
+ *
+ * Gives access to the #GtkHBox at the bottom of the preview that
+ * contains the update toggle. Derived widgets can use this function
+ * if they need to add controls to this area.
+ *
+ * Return value: the #GtkHBox at the bottom of the preview.
+ *
+ * Since: 2.4
+ **/
+GtkWidget *
+gimp_preview_get_controls (GimpPreview *preview)
+{
+ g_return_val_if_fail (GIMP_IS_PREVIEW (preview), NULL);
+
+ return GIMP_PREVIEW_GET_PRIVATE (preview)->controls;
+}
diff --git a/libgimpwidgets/gimppreview.h b/libgimpwidgets/gimppreview.h
new file mode 100644
index 0000000..25d2f78
--- /dev/null
+++ b/libgimpwidgets/gimppreview.h
@@ -0,0 +1,149 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppreview.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PREVIEW_H__
+#define __GIMP_PREVIEW_H__
+
+G_BEGIN_DECLS
+
+
+/* For information look into the C source or the html documentation */
+
+
+#define GIMP_TYPE_PREVIEW (gimp_preview_get_type ())
+#define GIMP_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_PREVIEW, GimpPreview))
+#define GIMP_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_PREVIEW, GimpPreviewClass))
+#define GIMP_IS_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_PREVIEW))
+#define GIMP_IS_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_PREVIEW))
+#define GIMP_PREVIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_PREVIEW, GimpPreviewClass))
+
+
+typedef struct _GimpPreviewClass GimpPreviewClass;
+
+struct _GimpPreview
+{
+ GtkBox parent_instance;
+
+ gboolean update_preview;
+
+ /*< protected >*/
+ GtkWidget *area;
+ GtkWidget *table;
+ GtkWidget *frame;
+ GtkWidget *toggle;
+ GdkCursor *cursor_busy;
+ GdkCursor *default_cursor;
+
+ /*< private >*/
+ gint xoff, yoff;
+ gint xmin, xmax, ymin, ymax;
+ gint width, height;
+
+ guint timeout_id;
+};
+
+struct _GimpPreviewClass
+{
+ GtkBoxClass parent_class;
+
+ /* virtual methods */
+ void (* draw) (GimpPreview *preview);
+ void (* draw_thumb) (GimpPreview *preview,
+ GimpPreviewArea *area,
+ gint width,
+ gint height);
+ void (* draw_buffer) (GimpPreview *preview,
+ const guchar *buffer,
+ gint rowstride);
+ void (* set_cursor) (GimpPreview *preview);
+
+ /* signal */
+ void (* invalidated) (GimpPreview *preview);
+
+ /* virtual methods */
+ void (* transform) (GimpPreview *preview,
+ gint src_x,
+ gint src_y,
+ gint *dest_x,
+ gint *dest_y);
+ void (* untransform) (GimpPreview *preview,
+ gint src_x,
+ gint src_y,
+ gint *dest_x,
+ gint *dest_y);
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_preview_get_type (void) G_GNUC_CONST;
+
+void gimp_preview_set_update (GimpPreview *preview,
+ gboolean update);
+gboolean gimp_preview_get_update (GimpPreview *preview);
+
+void gimp_preview_set_bounds (GimpPreview *preview,
+ gint xmin,
+ gint ymin,
+ gint xmax,
+ gint ymax);
+
+void gimp_preview_get_position (GimpPreview *preview,
+ gint *x,
+ gint *y);
+void gimp_preview_get_size (GimpPreview *preview,
+ gint *width,
+ gint *height);
+
+void gimp_preview_transform (GimpPreview *preview,
+ gint src_x,
+ gint src_y,
+ gint *dest_x,
+ gint *dest_y);
+void gimp_preview_untransform (GimpPreview *preview,
+ gint src_x,
+ gint src_y,
+ gint *dest_x,
+ gint *dest_y);
+
+GtkWidget * gimp_preview_get_area (GimpPreview *preview);
+
+void gimp_preview_draw (GimpPreview *preview);
+void gimp_preview_draw_buffer (GimpPreview *preview,
+ const guchar *buffer,
+ gint rowstride);
+
+void gimp_preview_invalidate (GimpPreview *preview);
+
+void gimp_preview_set_default_cursor (GimpPreview *preview,
+ GdkCursor *cursor);
+
+GtkWidget * gimp_preview_get_controls (GimpPreview *preview);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PREVIEW_H__ */
diff --git a/libgimpwidgets/gimppreviewarea.c b/libgimpwidgets/gimppreviewarea.c
new file mode 100644
index 0000000..7715ce0
--- /dev/null
+++ b/libgimpwidgets/gimppreviewarea.c
@@ -0,0 +1,1956 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpconfig/gimpconfig.h"
+#include "libgimpcolor/gimpcolor.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimppreviewarea.h"
+#include "gimpwidgetsutils.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimppreviewarea
+ * @title: GimpPreviewArea
+ * @short_description: A general purpose preview widget which caches
+ * its pixel data.
+ *
+ * A general purpose preview widget which caches its pixel data.
+ **/
+
+
+enum
+{
+ PROP_0,
+ PROP_CHECK_SIZE,
+ PROP_CHECK_TYPE
+};
+
+
+#define DEFAULT_CHECK_SIZE GIMP_CHECK_SIZE_MEDIUM_CHECKS
+#define DEFAULT_CHECK_TYPE GIMP_CHECK_TYPE_GRAY_CHECKS
+
+#define CHECK_COLOR(area, row, col) \
+ (((((area)->offset_y + (row)) & size) ^ \
+ (((area)->offset_x + (col)) & size)) ? dark : light)
+
+
+typedef struct _GimpPreviewAreaPrivate GimpPreviewAreaPrivate;
+
+struct _GimpPreviewAreaPrivate
+{
+ GimpColorConfig *config;
+ GimpColorTransform *transform;
+};
+
+#define GET_PRIVATE(obj) \
+ ((GimpPreviewAreaPrivate *) gimp_preview_area_get_instance_private ((GimpPreviewArea *) (obj)))
+
+
+static void gimp_preview_area_dispose (GObject *object);
+static void gimp_preview_area_finalize (GObject *object);
+static void gimp_preview_area_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_preview_area_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_preview_area_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static gboolean gimp_preview_area_expose (GtkWidget *widget,
+ GdkEventExpose *event);
+
+static void gimp_preview_area_queue_draw (GimpPreviewArea *area,
+ gint x,
+ gint y,
+ gint width,
+ gint height);
+static gint gimp_preview_area_image_type_bytes (GimpImageType type);
+
+static void gimp_preview_area_create_transform (GimpPreviewArea *area);
+static void gimp_preview_area_destroy_transform (GimpPreviewArea *area);
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpPreviewArea, gimp_preview_area,
+ GTK_TYPE_DRAWING_AREA)
+
+#define parent_class gimp_preview_area_parent_class
+
+
+static void
+gimp_preview_area_class_init (GimpPreviewAreaClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->dispose = gimp_preview_area_dispose;
+ object_class->finalize = gimp_preview_area_finalize;
+ object_class->set_property = gimp_preview_area_set_property;
+ object_class->get_property = gimp_preview_area_get_property;
+
+ widget_class->size_allocate = gimp_preview_area_size_allocate;
+ widget_class->expose_event = gimp_preview_area_expose;
+
+ g_object_class_install_property (object_class, PROP_CHECK_SIZE,
+ g_param_spec_enum ("check-size",
+ _("Check Size"),
+ "The size of the checkerboard pattern indicating transparency",
+ GIMP_TYPE_CHECK_SIZE,
+ DEFAULT_CHECK_SIZE,
+ GIMP_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_CHECK_TYPE,
+ g_param_spec_enum ("check-type",
+ _("Check Style"),
+ "The colors of the checkerboard pattern indicating transparency",
+ GIMP_TYPE_CHECK_TYPE,
+ DEFAULT_CHECK_TYPE,
+ GIMP_PARAM_READWRITE));
+}
+
+static void
+gimp_preview_area_init (GimpPreviewArea *area)
+{
+ area->check_size = DEFAULT_CHECK_SIZE;
+ area->check_type = DEFAULT_CHECK_TYPE;
+ area->buf = NULL;
+ area->colormap = NULL;
+ area->offset_x = 0;
+ area->offset_y = 0;
+ area->width = 0;
+ area->height = 0;
+ area->rowstride = 0;
+ area->max_width = -1;
+ area->max_height = -1;
+
+ gimp_widget_track_monitor (GTK_WIDGET (area),
+ G_CALLBACK (gimp_preview_area_destroy_transform),
+ NULL);
+}
+
+static void
+gimp_preview_area_dispose (GObject *object)
+{
+ GimpPreviewArea *area = GIMP_PREVIEW_AREA (object);
+
+ gimp_preview_area_set_color_config (area, NULL);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gimp_preview_area_finalize (GObject *object)
+{
+ GimpPreviewArea *area = GIMP_PREVIEW_AREA (object);
+
+ g_clear_pointer (&area->buf, g_free);
+ g_clear_pointer (&area->colormap, g_free);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_preview_area_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpPreviewArea *area = GIMP_PREVIEW_AREA (object);
+
+ switch (property_id)
+ {
+ case PROP_CHECK_SIZE:
+ area->check_size = g_value_get_enum (value);
+ break;
+ case PROP_CHECK_TYPE:
+ area->check_type = g_value_get_enum (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_preview_area_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpPreviewArea *area = GIMP_PREVIEW_AREA (object);
+
+ switch (property_id)
+ {
+ case PROP_CHECK_SIZE:
+ g_value_set_enum (value, area->check_size);
+ break;
+ case PROP_CHECK_TYPE:
+ g_value_set_enum (value, area->check_type);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_preview_area_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GimpPreviewArea *area = GIMP_PREVIEW_AREA (widget);
+ gint width;
+ gint height;
+
+ if (GTK_WIDGET_CLASS (parent_class)->size_allocate)
+ GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
+
+ width = (area->max_width > 0 ?
+ MIN (allocation->width, area->max_width) : allocation->width);
+ height = (area->max_height > 0 ?
+ MIN (allocation->height, area->max_height) : allocation->height);
+
+ if (width != area->width || height != area->height)
+ {
+ if (area->buf)
+ {
+ g_free (area->buf);
+
+ area->buf = NULL;
+ area->rowstride = 0;
+ }
+
+ area->width = width;
+ area->height = height;
+ }
+}
+
+static gboolean
+gimp_preview_area_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ GimpPreviewArea *area = GIMP_PREVIEW_AREA (widget);
+ GimpPreviewAreaPrivate *priv = GET_PRIVATE (area);
+ GtkAllocation allocation;
+ GdkPixbuf *pixbuf;
+ GdkRectangle rect;
+ cairo_t *cr;
+
+ if (! area->buf)
+ return FALSE;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ rect.x = (allocation.width - area->width) / 2;
+ rect.y = (allocation.height - area->height) / 2;
+ rect.width = area->width;
+ rect.height = area->height;
+
+ if (! priv->transform)
+ gimp_preview_area_create_transform (area);
+
+ if (priv->transform)
+ {
+ const Babl *format = babl_format ("R'G'B' u8");
+ gint rowstride = ((area->width * 3) + 3) & ~3;
+ guchar *buf = g_new (guchar, rowstride * area->height);
+ guchar *src = area->buf;
+ guchar *dest = buf;
+ gint i;
+
+ for (i = 0; i < area->height; i++)
+ {
+ gimp_color_transform_process_pixels (priv->transform,
+ format, src,
+ format, dest,
+ area->width);
+
+ src += area->rowstride;
+ dest += rowstride;
+ }
+
+ pixbuf = gdk_pixbuf_new_from_data (buf,
+ GDK_COLORSPACE_RGB,
+ FALSE,
+ 8,
+ rect.width,
+ rect.height,
+ rowstride,
+ (GdkPixbufDestroyNotify) g_free, NULL);
+ }
+ else
+ {
+ pixbuf = gdk_pixbuf_new_from_data (area->buf,
+ GDK_COLORSPACE_RGB,
+ FALSE,
+ 8,
+ rect.width,
+ rect.height,
+ area->rowstride,
+ NULL, NULL);
+ }
+
+ cr = gdk_cairo_create (gtk_widget_get_window (widget));
+
+ gdk_cairo_region (cr, event->region);
+ cairo_clip (cr);
+
+ gdk_cairo_set_source_pixbuf (cr, pixbuf, rect.x, rect.y);
+ cairo_paint (cr);
+
+ cairo_destroy (cr);
+ g_object_unref (pixbuf);
+
+ return FALSE;
+}
+
+static void
+gimp_preview_area_queue_draw (GimpPreviewArea *area,
+ gint x,
+ gint y,
+ gint width,
+ gint height)
+{
+ GtkWidget *widget = GTK_WIDGET (area);
+ GtkAllocation allocation;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ x += (allocation.width - area->width) / 2;
+ y += (allocation.height - area->height) / 2;
+
+ gtk_widget_queue_draw_area (widget, x, y, width, height);
+}
+
+static gint
+gimp_preview_area_image_type_bytes (GimpImageType type)
+{
+ switch (type)
+ {
+ case GIMP_GRAY_IMAGE:
+ case GIMP_INDEXED_IMAGE:
+ return 1;
+
+ case GIMP_GRAYA_IMAGE:
+ case GIMP_INDEXEDA_IMAGE:
+ return 2;
+
+ case GIMP_RGB_IMAGE:
+ return 3;
+
+ case GIMP_RGBA_IMAGE:
+ return 4;
+
+ default:
+ g_return_val_if_reached (0);
+ break;
+ }
+}
+
+static void
+gimp_preview_area_create_transform (GimpPreviewArea *area)
+{
+ GimpPreviewAreaPrivate *priv = GET_PRIVATE (area);
+
+ if (priv->config)
+ {
+ static GimpColorProfile *profile = NULL;
+
+ const Babl *format = babl_format ("R'G'B' u8");
+
+ if (G_UNLIKELY (! profile))
+ profile = gimp_color_profile_new_rgb_srgb ();
+
+ priv->transform = gimp_widget_get_color_transform (GTK_WIDGET (area),
+ priv->config,
+ profile,
+ format,
+ format);
+ }
+}
+
+static void
+gimp_preview_area_destroy_transform (GimpPreviewArea *area)
+{
+ GimpPreviewAreaPrivate *priv = GET_PRIVATE (area);
+
+ if (priv->transform)
+ {
+ g_object_unref (priv->transform);
+ priv->transform = NULL;
+ }
+
+ gtk_widget_queue_draw (GTK_WIDGET (area));
+}
+
+
+/**
+ * gimp_preview_area_new:
+ *
+ * Creates a new #GimpPreviewArea widget.
+ *
+ * Return value: a new #GimpPreviewArea widget.
+ *
+ * Since GIMP 2.2
+ **/
+GtkWidget *
+gimp_preview_area_new (void)
+{
+ return g_object_new (GIMP_TYPE_PREVIEW_AREA, NULL);
+}
+
+/**
+ * gimp_preview_area_draw:
+ * @area: a #GimpPreviewArea widget.
+ * @x: x offset in preview
+ * @y: y offset in preview
+ * @width: buffer width
+ * @height: buffer height
+ * @type: the #GimpImageType of @buf
+ * @buf: a #guchar buffer that contains the preview pixel data.
+ * @rowstride: rowstride of @buf
+ *
+ * Draws @buf on @area and queues a redraw on the given rectangle.
+ *
+ * Since GIMP 2.2
+ **/
+void
+gimp_preview_area_draw (GimpPreviewArea *area,
+ gint x,
+ gint y,
+ gint width,
+ gint height,
+ GimpImageType type,
+ const guchar *buf,
+ gint rowstride)
+{
+ const guchar *src;
+ guchar *dest;
+ guint size;
+ guchar light;
+ guchar dark;
+ gint row;
+ gint col;
+
+ g_return_if_fail (GIMP_IS_PREVIEW_AREA (area));
+ g_return_if_fail (width >= 0 && height >= 0);
+
+ if (width == 0 || height == 0)
+ return;
+
+ g_return_if_fail (buf != NULL);
+ g_return_if_fail (rowstride > 0);
+
+ if (x + width < 0 || x >= area->width)
+ return;
+
+ if (y + height < 0 || y >= area->height)
+ return;
+
+ if (x < 0)
+ {
+ gint bpp = gimp_preview_area_image_type_bytes (type);
+
+ buf -= x * bpp;
+ width += x;
+
+ x = 0;
+ }
+
+ if (x + width > area->width)
+ width = area->width - x;
+
+ if (y < 0)
+ {
+ buf -= y * rowstride;
+ height += y;
+
+ y = 0;
+ }
+
+ if (y + height > area->height)
+ height = area->height - y;
+
+ if (! area->buf)
+ {
+ area->rowstride = ((area->width * 3) + 3) & ~3;
+ area->buf = g_new (guchar, area->rowstride * area->height);
+ }
+
+ size = 1 << (2 + area->check_size);
+ gimp_checks_get_shades (area->check_type, &light, &dark);
+
+ src = buf;
+ dest = area->buf + x * 3 + y * area->rowstride;
+
+ switch (type)
+ {
+ case GIMP_RGB_IMAGE:
+ for (row = 0; row < height; row++)
+ {
+ memcpy (dest, src, 3 * width);
+
+ src += rowstride;
+ dest += area->rowstride;
+ }
+ break;
+
+ case GIMP_RGBA_IMAGE:
+ for (row = y; row < y + height; row++)
+ {
+ const guchar *s = src;
+ guchar *d = dest;
+
+ for (col = x; col < x + width; col++, s += 4, d+= 3)
+ {
+ switch (s[3])
+ {
+ case 0:
+ d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col);
+ break;
+
+ case 255:
+ d[0] = s[0];
+ d[1] = s[1];
+ d[2] = s[2];
+ break;
+
+ default:
+ {
+ register guint alpha = s[3] + 1;
+ register guint check = CHECK_COLOR (area, row, col);
+
+ d[0] = ((check << 8) + (s[0] - check) * alpha) >> 8;
+ d[1] = ((check << 8) + (s[1] - check) * alpha) >> 8;
+ d[2] = ((check << 8) + (s[2] - check) * alpha) >> 8;
+ }
+ break;
+ }
+ }
+
+ src += rowstride;
+ dest += area->rowstride;
+ }
+ break;
+
+ case GIMP_GRAY_IMAGE:
+ for (row = 0; row < height; row++)
+ {
+ const guchar *s = src;
+ guchar *d = dest;
+
+ for (col = 0; col < width; col++, s++, d += 3)
+ {
+ d[0] = d[1] = d[2] = s[0];
+ }
+
+ src += rowstride;
+ dest += area->rowstride;
+ }
+ break;
+
+ case GIMP_GRAYA_IMAGE:
+ for (row = y; row < y + height; row++)
+ {
+ const guchar *s = src;
+ guchar *d = dest;
+
+ for (col = x; col < x + width; col++, s += 2, d+= 3)
+ {
+ switch (s[1])
+ {
+ case 0:
+ d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col);
+ break;
+
+ case 255:
+ d[0] = d[1] = d[2] = s[0];
+ break;
+
+ default:
+ {
+ register guint alpha = s[1] + 1;
+ register guint check = CHECK_COLOR (area, row, col);
+
+ d[0] = d[1] = d[2] =
+ ((check << 8) + (s[0] - check) * alpha) >> 8;
+ }
+ break;
+ }
+ }
+
+ src += rowstride;
+ dest += area->rowstride;
+ }
+ break;
+
+ case GIMP_INDEXED_IMAGE:
+ g_return_if_fail (area->colormap != NULL);
+ for (row = 0; row < height; row++)
+ {
+ const guchar *s = src;
+ guchar *d = dest;
+
+ for (col = 0; col < width; col++, s++, d += 3)
+ {
+ const guchar *colormap = area->colormap + 3 * s[0];
+
+ d[0] = colormap[0];
+ d[1] = colormap[1];
+ d[2] = colormap[2];
+ }
+
+ src += rowstride;
+ dest += area->rowstride;
+ }
+ break;
+
+ case GIMP_INDEXEDA_IMAGE:
+ g_return_if_fail (area->colormap != NULL);
+ for (row = y; row < y + height; row++)
+ {
+ const guchar *s = src;
+ guchar *d = dest;
+
+ for (col = x; col < x + width; col++, s += 2, d += 3)
+ {
+ const guchar *colormap = area->colormap + 3 * s[0];
+
+ switch (s[1])
+ {
+ case 0:
+ d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col);
+ break;
+
+ case 255:
+ d[0] = colormap[0];
+ d[1] = colormap[1];
+ d[2] = colormap[2];
+ break;
+
+ default:
+ {
+ register guint alpha = s[3] + 1;
+ register guint check = CHECK_COLOR (area, row, col);
+
+ d[0] = ((check << 8) + (colormap[0] - check) * alpha) >> 8;
+ d[1] = ((check << 8) + (colormap[1] - check) * alpha) >> 8;
+ d[2] = ((check << 8) + (colormap[2] - check) * alpha) >> 8;
+ }
+ break;
+ }
+ }
+
+ src += rowstride;
+ dest += area->rowstride;
+ }
+ break;
+ }
+
+ gimp_preview_area_queue_draw (area, x, y, width, height);
+}
+
+/**
+ * gimp_preview_area_blend:
+ * @area: a #GimpPreviewArea widget.
+ * @x: x offset in preview
+ * @y: y offset in preview
+ * @width: buffer width
+ * @height: buffer height
+ * @type: the #GimpImageType of @buf1 and @buf2
+ * @buf1: a #guchar buffer that contains the pixel data for
+ * the lower layer
+ * @rowstride1: rowstride of @buf1
+ * @buf2: a #guchar buffer that contains the pixel data for
+ * the upper layer
+ * @rowstride2: rowstride of @buf2
+ * @opacity: The opacity of the first layer.
+ *
+ * Composites @buf1 on @buf2 with the given @opacity, draws the result
+ * to @area and queues a redraw on the given rectangle.
+ *
+ * Since GIMP 2.2
+ **/
+void
+gimp_preview_area_blend (GimpPreviewArea *area,
+ gint x,
+ gint y,
+ gint width,
+ gint height,
+ GimpImageType type,
+ const guchar *buf1,
+ gint rowstride1,
+ const guchar *buf2,
+ gint rowstride2,
+ guchar opacity)
+{
+ const guchar *src1;
+ const guchar *src2;
+ guchar *dest;
+ guint size;
+ guchar light;
+ guchar dark;
+ gint row;
+ gint col;
+ gint i;
+
+ g_return_if_fail (GIMP_IS_PREVIEW_AREA (area));
+ g_return_if_fail (width >= 0 && height >= 0);
+
+ if (width == 0 || height == 0)
+ return;
+
+ g_return_if_fail (buf1 != NULL);
+ g_return_if_fail (buf2 != NULL);
+ g_return_if_fail (rowstride1 > 0);
+ g_return_if_fail (rowstride2 > 0);
+
+ switch (opacity)
+ {
+ case 0:
+ gimp_preview_area_draw (area, x, y, width, height,
+ type, buf1, rowstride1);
+ return;
+
+ case 255:
+ gimp_preview_area_draw (area, x, y, width, height,
+ type, buf2, rowstride2);
+ return;
+
+ default:
+ break;
+ }
+
+ if (x + width < 0 || x >= area->width)
+ return;
+
+ if (y + height < 0 || y >= area->height)
+ return;
+
+ if (x < 0)
+ {
+ gint bpp = gimp_preview_area_image_type_bytes (type);
+
+ buf1 -= x * bpp;
+ buf2 -= x * bpp;
+ width += x;
+
+ x = 0;
+ }
+
+ if (x + width > area->width)
+ width = area->width - x;
+
+ if (y < 0)
+ {
+ buf1 -= y * rowstride1;
+ buf2 -= y * rowstride2;
+ height += y;
+
+ y = 0;
+ }
+
+ if (y + height > area->height)
+ height = area->height - y;
+
+ if (! area->buf)
+ {
+ area->rowstride = ((area->width * 3) + 3) & ~3;
+ area->buf = g_new (guchar, area->rowstride * area->height);
+ }
+
+ size = 1 << (2 + area->check_size);
+ gimp_checks_get_shades (area->check_type, &light, &dark);
+
+ src1 = buf1;
+ src2 = buf2;
+ dest = area->buf + x * 3 + y * area->rowstride;
+
+ switch (type)
+ {
+ case GIMP_RGB_IMAGE:
+ for (row = 0; row < height; row++)
+ {
+ const guchar *s1 = src1;
+ const guchar *s2 = src2;
+ guchar *d = dest;
+
+ for (col = x; col < x + width; col++, s1 += 3, s2 += 3, d+= 3)
+ {
+ d[0] = ((s1[0] << 8) + (s2[0] - s1[0]) * opacity) >> 8;
+ d[1] = ((s1[1] << 8) + (s2[1] - s1[1]) * opacity) >> 8;
+ d[2] = ((s1[2] << 8) + (s2[2] - s1[2]) * opacity) >> 8;
+ }
+
+ src1 += rowstride1;
+ src2 += rowstride2;
+ dest += area->rowstride;
+ }
+ break;
+
+ case GIMP_RGBA_IMAGE:
+ for (row = y; row < y + height; row++)
+ {
+ const guchar *s1 = src1;
+ const guchar *s2 = src2;
+ guchar *d = dest;
+
+ for (col = x; col < x + width; col++, s1 += 4, s2 += 4, d+= 3)
+ {
+ guchar inter[4];
+
+ if (s1[3] == s2[3])
+ {
+ inter[0] = ((s1[0] << 8) + (s2[0] - s1[0]) * opacity) >> 8;
+ inter[1] = ((s1[1] << 8) + (s2[1] - s1[1]) * opacity) >> 8;
+ inter[2] = ((s1[2] << 8) + (s2[2] - s1[2]) * opacity) >> 8;
+ inter[3] = s1[3];
+ }
+ else
+ {
+ inter[3] = ((s1[3] << 8) + (s2[3] - s1[3]) * opacity) >> 8;
+
+ if (inter[3])
+ {
+ for (i = 0; i < 3; i++)
+ {
+ gushort a = s1[i] * s1[3];
+ gushort b = s2[i] * s2[3];
+
+ inter[i] =
+ (((a << 8) + (b - a) * opacity) >> 8) / inter[3];
+ }
+ }
+ }
+
+ switch (inter[3])
+ {
+ case 0:
+ d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col);
+ break;
+
+ case 255:
+ d[0] = inter[0];
+ d[1] = inter[1];
+ d[2] = inter[2];
+ break;
+
+ default:
+ {
+ register guint alpha = inter[3] + 1;
+ register guint check = CHECK_COLOR (area, row, col);
+
+ d[0] = ((check << 8) + (inter[0] - check) * alpha) >> 8;
+ d[1] = ((check << 8) + (inter[1] - check) * alpha) >> 8;
+ d[2] = ((check << 8) + (inter[2] - check) * alpha) >> 8;
+ }
+ break;
+ }
+ }
+
+ src1 += rowstride1;
+ src2 += rowstride2;
+ dest += area->rowstride;
+ }
+ break;
+
+ case GIMP_GRAY_IMAGE:
+ for (row = 0; row < height; row++)
+ {
+ const guchar *s1 = src1;
+ const guchar *s2 = src2;
+ guchar *d = dest;
+
+ for (col = 0; col < width; col++, s1++, s2++, d += 3)
+ {
+ d[0] = d[1] = d[2] =
+ ((s1[0] << 8) + (s2[0] - s1[0]) * opacity) >> 8;
+ }
+
+ src1 += rowstride1;
+ src2 += rowstride2;
+ dest += area->rowstride;
+ }
+ break;
+
+ case GIMP_GRAYA_IMAGE:
+ for (row = y; row < y + height; row++)
+ {
+ const guchar *s1 = src1;
+ const guchar *s2 = src2;
+ guchar *d = dest;
+
+ for (col = x; col < x + width; col++, s1 += 2, s2 += 2, d+= 3)
+ {
+ guchar inter[2] = { 0, };
+
+ if (s1[1] == s2[1])
+ {
+ inter[0] = ((s1[0] << 8) + (s2[0] - s1[0]) * opacity) >> 8;
+ inter[1] = s1[1];
+ }
+ else
+ {
+ inter[1] = ((s1[1] << 8) + (s2[1] - s1[1]) * opacity) >> 8;
+
+ if (inter[1])
+ {
+ gushort a = s1[0] * s1[1];
+ gushort b = s2[0] * s2[1];
+
+ inter[0] =
+ (((a << 8) + (b - a) * opacity) >> 8) / inter[1];
+ }
+ }
+
+ switch (inter[1])
+ {
+ case 0:
+ d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col);
+ break;
+
+ case 255:
+ d[0] = d[1] = d[2] = inter[0];
+ break;
+
+ default:
+ {
+ register guint alpha = inter[1] + 1;
+ register guint check = CHECK_COLOR (area, row, col);
+
+ d[0] = d[1] = d[2] =
+ ((check << 8) + (inter[0] - check) * alpha) >> 8;
+ }
+ break;
+ }
+ }
+
+ src1 += rowstride1;
+ src2 += rowstride2;
+ dest += area->rowstride;
+ }
+ break;
+
+ case GIMP_INDEXED_IMAGE:
+ g_return_if_fail (area->colormap != NULL);
+ for (row = 0; row < height; row++)
+ {
+ const guchar *s1 = src1;
+ const guchar *s2 = src2;
+ guchar *d = dest;
+
+ for (col = 0; col < width; col++, s1++, s2++, d += 3)
+ {
+ const guchar *cmap1 = area->colormap + 3 * s1[0];
+ const guchar *cmap2 = area->colormap + 3 * s2[0];
+
+ d[0] = ((cmap1[0] << 8) + (cmap2[0] - cmap1[0]) * opacity) >> 8;
+ d[1] = ((cmap1[1] << 8) + (cmap2[1] - cmap1[1]) * opacity) >> 8;
+ d[2] = ((cmap1[2] << 8) + (cmap2[2] - cmap1[2]) * opacity) >> 8;
+ }
+
+ src1 += rowstride1;
+ src2 += rowstride2;
+ dest += area->rowstride;
+ }
+ break;
+
+ case GIMP_INDEXEDA_IMAGE:
+ g_return_if_fail (area->colormap != NULL);
+ for (row = y; row < y + height; row++)
+ {
+ const guchar *s1 = src1;
+ const guchar *s2 = src2;
+ guchar *d = dest;
+
+ for (col = x; col < x + width; col++, s1 += 2, s2 += 2, d += 3)
+ {
+ const guchar *cmap1 = area->colormap + 3 * s1[0];
+ const guchar *cmap2 = area->colormap + 3 * s2[0];
+ guchar inter[4];
+
+ if (s1[1] == s2[1])
+ {
+ inter[0] = (((cmap1[0] << 8) +
+ (cmap2[0] - cmap1[0]) * opacity) >> 8);
+ inter[1] = (((cmap1[1] << 8) +
+ (cmap2[1] - cmap1[1]) * opacity) >> 8);
+ inter[2] = (((cmap1[2] << 8) +
+ (cmap2[2] - cmap1[2]) * opacity) >> 8);
+ inter[3] = s1[1];
+ }
+ else
+ {
+ inter[3] = ((s1[1] << 8) + (s2[1] - s1[1]) * opacity) >> 8;
+
+ if (inter[3])
+ {
+ for (i = 0; i < 3; i++)
+ {
+ gushort a = cmap1[i] * s1[1];
+ gushort b = cmap2[i] * s2[1];
+
+ inter[i] =
+ (((a << 8) + (b - a) * opacity) >> 8) / inter[3];
+ }
+ }
+ }
+
+ switch (inter[3])
+ {
+ case 0:
+ d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col);
+ break;
+
+ case 255:
+ d[0] = inter[0];
+ d[1] = inter[1];
+ d[2] = inter[2];
+ break;
+
+ default:
+ {
+ register guint alpha = inter[3] + 1;
+ register guint check = CHECK_COLOR (area, row, col);
+
+ d[0] = ((check << 8) + (inter[0] - check) * alpha) >> 8;
+ d[1] = ((check << 8) + (inter[1] - check) * alpha) >> 8;
+ d[2] = ((check << 8) + (inter[2] - check) * alpha) >> 8;
+ }
+ break;
+ }
+ }
+
+ src1 += rowstride1;
+ src2 += rowstride2;
+ dest += area->rowstride;
+ }
+ break;
+ }
+
+ gimp_preview_area_queue_draw (area, x, y, width, height);
+}
+
+/**
+ * gimp_preview_area_mask:
+ * @area: a #GimpPreviewArea widget.
+ * @x: x offset in preview
+ * @y: y offset in preview
+ * @width: buffer width
+ * @height: buffer height
+ * @type: the #GimpImageType of @buf1 and @buf2
+ * @buf1: a #guchar buffer that contains the pixel data for
+ * the lower layer
+ * @rowstride1: rowstride of @buf1
+ * @buf2: a #guchar buffer that contains the pixel data for
+ * the upper layer
+ * @rowstride2: rowstride of @buf2
+ * @mask: a #guchar buffer representing the mask of the second
+ * layer.
+ * @rowstride_mask: rowstride for the mask.
+ *
+ * Composites @buf1 on @buf2 with the given @mask, draws the result on
+ * @area and queues a redraw on the given rectangle.
+ *
+ * Since GIMP 2.2
+ **/
+void
+gimp_preview_area_mask (GimpPreviewArea *area,
+ gint x,
+ gint y,
+ gint width,
+ gint height,
+ GimpImageType type,
+ const guchar *buf1,
+ gint rowstride1,
+ const guchar *buf2,
+ gint rowstride2,
+ const guchar *mask,
+ gint rowstride_mask)
+{
+ const guchar *src1;
+ const guchar *src2;
+ const guchar *src_mask;
+ guchar *dest;
+ guint size;
+ guchar light;
+ guchar dark;
+ gint row;
+ gint col;
+ gint i;
+
+ g_return_if_fail (GIMP_IS_PREVIEW_AREA (area));
+ g_return_if_fail (width >= 0 && height >= 0);
+
+ if (width == 0 || height == 0)
+ return;
+
+ g_return_if_fail (buf1 != NULL);
+ g_return_if_fail (buf2 != NULL);
+ g_return_if_fail (mask != NULL);
+ g_return_if_fail (rowstride1 > 0);
+ g_return_if_fail (rowstride2 > 0);
+ g_return_if_fail (rowstride_mask > 0);
+
+ if (x + width < 0 || x >= area->width)
+ return;
+
+ if (y + height < 0 || y >= area->height)
+ return;
+
+ if (x < 0)
+ {
+ gint bpp = gimp_preview_area_image_type_bytes (type);
+
+ buf1 -= x * bpp;
+ buf2 -= x * bpp;
+ mask -= x;
+ width += x;
+
+ x = 0;
+ }
+
+ if (x + width > area->width)
+ width = area->width - x;
+
+ if (y < 0)
+ {
+ buf1 -= y * rowstride1;
+ buf2 -= y * rowstride2;
+ mask -= y * rowstride_mask;
+ height += y;
+
+ y = 0;
+ }
+
+ if (y + height > area->height)
+ height = area->height - y;
+
+ if (! area->buf)
+ {
+ area->rowstride = ((area->width * 3) + 3) & ~3;
+ area->buf = g_new (guchar, area->rowstride * area->height);
+ }
+
+ size = 1 << (2 + area->check_size);
+ gimp_checks_get_shades (area->check_type, &light, &dark);
+
+ src1 = buf1;
+ src2 = buf2;
+ src_mask = mask;
+ dest = area->buf + x * 3 + y * area->rowstride;
+
+ switch (type)
+ {
+ case GIMP_RGB_IMAGE:
+ for (row = 0; row < height; row++)
+ {
+ const guchar *s1 = src1;
+ const guchar *s2 = src2;
+ const guchar *m = src_mask;
+ guchar *d = dest;
+
+ for (col = x; col < x + width; col++, s1 += 3, s2 += 3, m++, d+= 3)
+ {
+ d[0] = ((s1[0] << 8) + (s2[0] - s1[0]) * m[0]) >> 8;
+ d[1] = ((s1[1] << 8) + (s2[1] - s1[1]) * m[0]) >> 8;
+ d[2] = ((s1[2] << 8) + (s2[2] - s1[2]) * m[0]) >> 8;
+ }
+
+ src1 += rowstride1;
+ src2 += rowstride2;
+ src_mask += rowstride_mask;
+ dest += area->rowstride;
+ }
+ break;
+
+ case GIMP_RGBA_IMAGE:
+ for (row = y; row < y + height; row++)
+ {
+ const guchar *s1 = src1;
+ const guchar *s2 = src2;
+ const guchar *m = src_mask;
+ guchar *d = dest;
+
+ for (col = x; col < x + width; col++, s1 += 4, s2 += 4, m++, d+= 3)
+ {
+ switch (m[0])
+ {
+ case 0:
+ switch (s1[3])
+ {
+ case 0:
+ d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col);
+ break;
+
+ case 255:
+ d[0] = s1[0];
+ d[1] = s1[1];
+ d[2] = s1[2];
+ break;
+
+ default:
+ {
+ register guint alpha = s1[3] + 1;
+ register guint check = CHECK_COLOR (area, row, col);
+
+ d[0] = ((check << 8) + (s1[0] - check) * alpha) >> 8;
+ d[1] = ((check << 8) + (s1[1] - check) * alpha) >> 8;
+ d[2] = ((check << 8) + (s1[2] - check) * alpha) >> 8;
+ }
+ break;
+ }
+ break;
+
+ case 255:
+ switch (s2[3])
+ {
+ case 0:
+ d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col);
+ break;
+
+ case 255:
+ d[0] = s2[0];
+ d[1] = s2[1];
+ d[2] = s2[2];
+ break;
+
+ default:
+ {
+ register guint alpha = s2[3] + 1;
+ register guint check = CHECK_COLOR (area, row, col);
+
+ d[0] = ((check << 8) + (s2[0] - check) * alpha) >> 8;
+ d[1] = ((check << 8) + (s2[1] - check) * alpha) >> 8;
+ d[2] = ((check << 8) + (s2[2] - check) * alpha) >> 8;
+ }
+ break;
+ }
+ break;
+
+ default:
+ {
+ guchar inter[4];
+
+ if (s1[3] == s2[3])
+ {
+ inter[0] = ((s1[0] << 8) + (s2[0] - s1[0]) * m[0]) >> 8;
+ inter[1] = ((s1[1] << 8) + (s2[1] - s1[1]) * m[0]) >> 8;
+ inter[2] = ((s1[2] << 8) + (s2[2] - s1[2]) * m[0]) >> 8;
+ inter[3] = s1[3];
+ }
+ else
+ {
+ inter[3] = ((s1[3] << 8) + (s2[3] - s1[3]) * m[0]) >> 8;
+
+ if (inter[3])
+ {
+ for (i = 0; i < 3; i++)
+ {
+ gushort a = s1[i] * s1[3];
+ gushort b = s2[i] * s2[3];
+
+ inter[i] =
+ (((a << 8) + (b - a) * m[0]) >> 8) / inter[3];
+ }
+ }
+ }
+
+ switch (inter[3])
+ {
+ case 0:
+ d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col);
+ break;
+
+ case 255:
+ d[0] = inter[0];
+ d[1] = inter[1];
+ d[2] = inter[2];
+ break;
+
+ default:
+ {
+ register guint alpha = inter[3] + 1;
+ register guint check = CHECK_COLOR (area, row, col);
+
+ d[0] = (((check << 8) +
+ (inter[0] - check) * alpha) >> 8);
+ d[1] = (((check << 8) +
+ (inter[1] - check) * alpha) >> 8);
+ d[2] = (((check << 8) +
+ (inter[2] - check) * alpha) >> 8);
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ src1 += rowstride1;
+ src2 += rowstride2;
+ src_mask += rowstride_mask;
+ dest += area->rowstride;
+ }
+ break;
+
+ case GIMP_GRAY_IMAGE:
+ for (row = 0; row < height; row++)
+ {
+ const guchar *s1 = src1;
+ const guchar *s2 = src2;
+ const guchar *m = src_mask;
+ guchar *d = dest;
+
+ for (col = 0; col < width; col++, s1++, s2++, m++, d += 3)
+ d[0] = d[1] = d[2] = ((s1[0] << 8) + (s2[0] - s1[0]) * m[0]) >> 8;
+
+ src1 += rowstride1;
+ src2 += rowstride2;
+ src_mask += rowstride_mask;
+ dest += area->rowstride;
+ }
+ break;
+
+ case GIMP_GRAYA_IMAGE:
+ for (row = y; row < y + height; row++)
+ {
+ const guchar *s1 = src1;
+ const guchar *s2 = src2;
+ const guchar *m = src_mask;
+ guchar *d = dest;
+
+ for (col = x; col < x + width; col++, s1 += 2, s2 += 2, m++, d+= 3)
+ {
+ switch (m[0])
+ {
+ case 0:
+ switch (s1[1])
+ {
+ case 0:
+ d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col);
+ break;
+
+ case 255:
+ d[0] = d[1] = d[2] = s1[0];
+ break;
+
+ default:
+ {
+ register guint alpha = s1[1] + 1;
+ register guint check = CHECK_COLOR (area, row, col);
+
+ d[0] = d[1] = d[2] =
+ ((check << 8) + (s1[0] - check) * alpha) >> 8;
+ }
+ break;
+ }
+ break;
+
+ case 255:
+ switch (s2[1])
+ {
+ case 0:
+ d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col);
+ break;
+
+ case 255:
+ d[0] = d[1] = d[2] = s2[0];
+ break;
+
+ default:
+ {
+ register guint alpha = s2[1] + 1;
+ register guint check = CHECK_COLOR (area, row, col);
+
+ d[0] = d[1] = d[2] =
+ ((check << 8) + (s2[0] - check) * alpha) >> 8;
+ }
+ break;
+ }
+ break;
+
+ default:
+ {
+ guchar inter[2] = { 0, };
+
+ if (s1[1] == s2[1])
+ {
+ inter[0] = ((s1[0] << 8) + (s2[0] - s1[0]) * m[0]) >> 8;
+ inter[1] = s1[1];
+ }
+ else
+ {
+ inter[1] = ((s1[1] << 8) + (s2[1] - s1[1]) * m[0]) >> 8;
+
+ if (inter[1])
+ {
+ gushort a = s1[0] * s1[1];
+ gushort b = s2[0] * s2[1];
+
+ inter[0] =
+ (((a << 8) + (b - a) * m[0]) >> 8) / inter[1];
+ }
+ }
+
+ switch (inter[1])
+ {
+ case 0:
+ d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col);
+ break;
+
+ case 255:
+ d[0] = d[1] = d[2] = inter[0];
+ break;
+
+ default:
+ {
+ register guint alpha = inter[1] + 1;
+ register guint check = CHECK_COLOR (area, row, col);
+
+ d[0] = d[1] = d[2] =
+ ((check << 8) + (inter[0] - check) * alpha) >> 8;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ src1 += rowstride1;
+ src2 += rowstride2;
+ src_mask += rowstride_mask;
+ dest += area->rowstride;
+ }
+ break;
+
+ case GIMP_INDEXED_IMAGE:
+ g_return_if_fail (area->colormap != NULL);
+ for (row = 0; row < height; row++)
+ {
+ const guchar *s1 = src1;
+ const guchar *s2 = src2;
+ const guchar *m = src_mask;
+ guchar *d = dest;
+
+ for (col = 0; col < width; col++, s1++, s2++, m++, d += 3)
+ {
+ const guchar *cmap1 = area->colormap + 3 * s1[0];
+ const guchar *cmap2 = area->colormap + 3 * s2[0];
+
+ d[0] = ((cmap1[0] << 8) + (cmap2[0] - cmap1[0]) * m[0]) >> 8;
+ d[1] = ((cmap1[1] << 8) + (cmap2[1] - cmap1[1]) * m[0]) >> 8;
+ d[2] = ((cmap1[2] << 8) + (cmap2[2] - cmap1[2]) * m[0]) >> 8;
+ }
+
+ src1 += rowstride1;
+ src2 += rowstride2;
+ src_mask += rowstride_mask;
+ dest += area->rowstride;
+ }
+ break;
+
+ case GIMP_INDEXEDA_IMAGE:
+ g_return_if_fail (area->colormap != NULL);
+ for (row = y; row < y + height; row++)
+ {
+ const guchar *s1 = src1;
+ const guchar *s2 = src2;
+ const guchar *m = src_mask;
+ guchar *d = dest;
+
+ for (col = x; col < x + width; col++, s1 += 2, s2 += 2, m++, d += 3)
+ {
+ const guchar *cmap1 = area->colormap + 3 * s1[0];
+ const guchar *cmap2 = area->colormap + 3 * s2[0];
+
+ switch (m[0])
+ {
+ case 0:
+ switch (s1[1])
+ {
+ case 0:
+ d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col);
+ break;
+
+ case 255:
+ d[0] = cmap1[0];
+ d[1] = cmap1[1];
+ d[2] = cmap1[2];
+ break;
+
+ default:
+ {
+ register guint alpha = s1[1] + 1;
+ register guint check = CHECK_COLOR (area, row, col);
+
+ d[0] = ((check << 8) + (cmap1[0] - check) * alpha) >> 8;
+ d[1] = ((check << 8) + (cmap1[1] - check) * alpha) >> 8;
+ d[2] = ((check << 8) + (cmap1[2] - check) * alpha) >> 8;
+ }
+ break;
+ }
+ break;
+
+ case 255:
+ switch (s2[1])
+ {
+ case 0:
+ d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col);
+ break;
+
+ case 255:
+ d[0] = cmap2[0];
+ d[1] = cmap2[1];
+ d[2] = cmap2[2];
+ break;
+
+ default:
+ {
+ register guint alpha = s2[1] + 1;
+ register guint check = CHECK_COLOR (area, row, col);
+
+ d[0] = ((check << 8) + (cmap2[0] - check) * alpha) >> 8;
+ d[1] = ((check << 8) + (cmap2[1] - check) * alpha) >> 8;
+ d[2] = ((check << 8) + (cmap2[2] - check) * alpha) >> 8;
+ }
+ break;
+ }
+ break;
+
+ default:
+ {
+ guchar inter[4];
+
+ if (s1[1] == s2[1])
+ {
+ inter[0] = (((cmap1[0] << 8) +
+ (cmap2[0] - cmap1[0]) * m[0]) >> 8);
+ inter[1] = (((cmap1[1] << 8) +
+ (cmap2[1] - cmap1[1]) * m[0]) >> 8);
+ inter[2] = (((cmap1[2] << 8) +
+ (cmap2[2] - cmap1[2]) * m[0]) >> 8);
+ inter[3] = s1[1];
+ }
+ else
+ {
+ inter[3] = ((s1[1] << 8) + (s2[1] - s1[1]) * m[0]) >> 8;
+
+ if (inter[3])
+ {
+ for (i = 0 ; i < 3 ; i++)
+ {
+ gushort a = cmap1[i] * s1[1];
+ gushort b = cmap2[i] * s2[1];
+
+ inter[i] = ((((a << 8) + (b - a) * m[0]) >> 8)
+ / inter[3]);
+ }
+ }
+ }
+
+ switch (inter[3])
+ {
+ case 0:
+ d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col);
+ break;
+
+ case 255:
+ d[0] = inter[0];
+ d[1] = inter[1];
+ d[2] = inter[2];
+ break;
+
+ default:
+ {
+ register guint alpha = inter[3] + 1;
+ register guint check = CHECK_COLOR (area, row, col);
+
+ d[0] =
+ ((check << 8) + (inter[0] - check) * alpha) >> 8;
+ d[1] =
+ ((check << 8) + (inter[1] - check) * alpha) >> 8;
+ d[2] =
+ ((check << 8) + (inter[2] - check) * alpha) >> 8;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ src1 += rowstride1;
+ src2 += rowstride2;
+ src_mask += rowstride_mask;
+ dest += area->rowstride;
+ }
+ break;
+ }
+
+ gimp_preview_area_queue_draw (area, x, y, width, height);
+}
+
+/**
+ * gimp_preview_area_fill:
+ * @area: a #GimpPreviewArea widget.
+ * @x: x offset in preview
+ * @y: y offset in preview
+ * @width: width of the rectangle to fill
+ * @height: height of the rectangle to fill
+ * @red: red component of the fill color (0-255)
+ * @green: green component of the fill color (0-255)
+ * @blue: red component of the fill color (0-255)
+ *
+ * Fills the given rectangle of @area in the given color and queues a
+ * redraw.
+ *
+ * Since GIMP 2.2
+ **/
+void
+gimp_preview_area_fill (GimpPreviewArea *area,
+ gint x,
+ gint y,
+ gint width,
+ gint height,
+ guchar red,
+ guchar green,
+ guchar blue)
+{
+ guchar *dest;
+ guchar *d;
+ gint row;
+ gint col;
+
+ g_return_if_fail (GIMP_IS_PREVIEW_AREA (area));
+ g_return_if_fail (width >= 0 && height >= 0);
+
+ if (width == 0 || height == 0)
+ return;
+
+ if (x + width < 0 || x >= area->width)
+ return;
+
+ if (y + height < 0 || y >= area->height)
+ return;
+
+ if (x < 0)
+ {
+ width += x;
+ x = 0;
+ }
+
+ if (x + width > area->width)
+ width = area->width - x;
+
+ if (y < 0)
+ {
+ height += y;
+ y = 0;
+ }
+
+ if (y + height > area->height)
+ height = area->height - y;
+
+ if (! area->buf)
+ {
+ area->rowstride = ((area->width * 3) + 3) & ~3;
+ area->buf = g_new (guchar, area->rowstride * area->height);
+ }
+
+ dest = area->buf + x * 3 + y * area->rowstride;
+
+ /* colorize first row */
+ for (col = 0, d = dest; col < width; col++, d+= 3)
+ {
+ d[0] = red;
+ d[1] = green;
+ d[2] = blue;
+ }
+
+ /* copy first row to remaining rows */
+ for (row = 1, d = dest; row < height; row++)
+ {
+ d += area->rowstride;
+ memcpy (d, dest, width * 3);
+ }
+
+ gimp_preview_area_queue_draw (area, x, y, width, height);
+}
+
+/**
+ * gimp_preview_area_set_offsets:
+ * @area: a #GimpPreviewArea
+ * @x: horizontal offset
+ * @y: vertical offset
+ *
+ * Sets the offsets of the previewed area. This information is used
+ * when drawing the checkerboard and to determine the dither offsets.
+ *
+ * Since: 2.2
+ **/
+void
+gimp_preview_area_set_offsets (GimpPreviewArea *area,
+ gint x,
+ gint y)
+{
+ g_return_if_fail (GIMP_IS_PREVIEW_AREA (area));
+
+ area->offset_x = x;
+ area->offset_y = y;
+}
+
+/**
+ * gimp_preview_area_set_colormap:
+ * @area: a #GimpPreviewArea
+ * @colormap: a #guchar buffer that contains the colormap
+ * @num_colors: the number of colors in the colormap
+ *
+ * Sets the colormap for the #GimpPreviewArea widget. You need to
+ * call this function before you use gimp_preview_area_draw() with
+ * an image type of %GIMP_INDEXED_IMAGE or %GIMP_INDEXEDA_IMAGE.
+ *
+ * Since GIMP 2.2
+ **/
+void
+gimp_preview_area_set_colormap (GimpPreviewArea *area,
+ const guchar *colormap,
+ gint num_colors)
+{
+ g_return_if_fail (GIMP_IS_PREVIEW_AREA (area));
+ g_return_if_fail (colormap != NULL || num_colors == 0);
+ g_return_if_fail (num_colors >= 0 && num_colors <= 256);
+
+ if (num_colors > 0)
+ {
+ if (area->colormap)
+ memset (area->colormap, 0, 3 * 256);
+ else
+ area->colormap = g_new0 (guchar, 3 * 256);
+
+ memcpy (area->colormap, colormap, 3 * num_colors);
+ }
+ else
+ {
+ g_free (area->colormap);
+ area->colormap = NULL;
+ }
+}
+
+/**
+ * gimp_preview_area_set_color_config:
+ * @area: a #GimpPreviewArea widget.
+ * @config: a #GimpColorConfig object.
+ *
+ * Sets the color management configuration to use with this preview area.
+ *
+ * Since: 2.10
+ */
+void
+gimp_preview_area_set_color_config (GimpPreviewArea *area,
+ GimpColorConfig *config)
+{
+ GimpPreviewAreaPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_PREVIEW_AREA (area));
+ g_return_if_fail (config == NULL || GIMP_IS_COLOR_CONFIG (config));
+
+ priv = GET_PRIVATE (area);
+
+ if (config != priv->config)
+ {
+ if (priv->config)
+ {
+ g_signal_handlers_disconnect_by_func (priv->config,
+ gimp_preview_area_destroy_transform,
+ area);
+
+ gimp_preview_area_destroy_transform (area);
+ }
+
+ g_set_object (&priv->config, config);
+
+ if (priv->config)
+ {
+ g_signal_connect_swapped (priv->config, "notify",
+ G_CALLBACK (gimp_preview_area_destroy_transform),
+ area);
+ }
+ }
+}
+
+/**
+ * gimp_preview_area_set_max_size:
+ * @area: a #GimpPreviewArea widget
+ * @width: the maximum width in pixels or -1 to unset the limit
+ * @height: the maximum height in pixels or -1 to unset the limit
+ *
+ * Usually a #GimpPreviewArea fills the size that it is
+ * allocated. This function allows you to limit the preview area to a
+ * maximum size. If a larger size is allocated for the widget, the
+ * preview will draw itself centered into the allocated area.
+ *
+ * Since: 2.2
+ **/
+void
+gimp_preview_area_set_max_size (GimpPreviewArea *area,
+ gint width,
+ gint height)
+{
+ g_return_if_fail (GIMP_IS_PREVIEW_AREA (area));
+
+ area->max_width = width;
+ area->max_height = height;
+}
+
+
+
+/* popup menu */
+
+static void
+gimp_preview_area_menu_toggled (GtkWidget *item,
+ GimpPreviewArea *area)
+{
+ gboolean active = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item));
+
+ if (active)
+ {
+ const gchar *name = g_object_get_data (G_OBJECT (item),
+ "gimp-preview-area-prop-name");
+ if (name)
+ {
+ gint value =
+ GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item),
+ "gimp-preview-area-prop-value"));
+ g_object_set (area,
+ name, value,
+ NULL);
+ }
+ }
+}
+
+static GtkWidget *
+gimp_preview_area_menu_new (GimpPreviewArea *area,
+ const gchar *property)
+{
+ GParamSpec *pspec;
+ GEnumClass *enum_class;
+ GEnumValue *enum_value;
+ GtkWidget *menu;
+ GtkWidget *item;
+ GSList *group = NULL;
+ gint value;
+
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (area), property);
+
+ g_return_val_if_fail (G_IS_PARAM_SPEC_ENUM (pspec), NULL);
+
+ g_object_get (area,
+ property, &value,
+ NULL);
+
+ enum_class = G_PARAM_SPEC_ENUM (pspec)->enum_class;
+
+ menu = gtk_menu_new ();
+
+ for (enum_value = enum_class->values; enum_value->value_name; enum_value++)
+ {
+ const gchar *name = gimp_enum_value_get_desc (enum_class, enum_value);
+
+ item = gtk_radio_menu_item_new_with_label (group, name);
+
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+ gtk_widget_show (item);
+
+ g_object_set_data (G_OBJECT (item),
+ "gimp-preview-area-prop-name",
+ (gpointer) property);
+
+ g_object_set_data (G_OBJECT (item),
+ "gimp-preview-area-prop-value",
+ GINT_TO_POINTER (enum_value->value));
+
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item),
+ (enum_value->value == value));
+
+ g_signal_connect (item, "toggled",
+ G_CALLBACK (gimp_preview_area_menu_toggled),
+ area);
+
+ group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (item));
+ }
+
+ item = gtk_menu_item_new_with_label (g_param_spec_get_nick (pspec));
+
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
+
+ gtk_widget_show (item);
+
+ return item;
+}
+
+/**
+ * gimp_preview_area_menu_popup:
+ * @area: a #GimpPreviewArea
+ * @event: the button event that causes the menu to popup or %NULL
+ *
+ * Creates a popup menu that allows one to configure the size and type of
+ * the checkerboard pattern that the @area uses to visualize transparency.
+ *
+ * Since: 2.2
+ **/
+void
+gimp_preview_area_menu_popup (GimpPreviewArea *area,
+ GdkEventButton *event)
+{
+ GtkWidget *menu;
+
+ g_return_if_fail (GIMP_IS_PREVIEW_AREA (area));
+
+ menu = gtk_menu_new ();
+ gtk_menu_set_screen (GTK_MENU (menu),
+ gtk_widget_get_screen (GTK_WIDGET (area)));
+
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu),
+ gimp_preview_area_menu_new (area, "check-type"));
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu),
+ gimp_preview_area_menu_new (area, "check-size"));
+
+ if (event)
+ gtk_menu_popup (GTK_MENU (menu),
+ NULL, NULL, NULL, NULL, event->button, event->time);
+ else
+ gtk_menu_popup (GTK_MENU (menu),
+ NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time ());
+}
diff --git a/libgimpwidgets/gimppreviewarea.h b/libgimpwidgets/gimppreviewarea.h
new file mode 100644
index 0000000..cfcf1b2
--- /dev/null
+++ b/libgimpwidgets/gimppreviewarea.h
@@ -0,0 +1,133 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PREVIEW_AREA_H__
+#define __GIMP_PREVIEW_AREA_H__
+
+G_BEGIN_DECLS
+
+
+#define GIMP_TYPE_PREVIEW_AREA (gimp_preview_area_get_type ())
+#define GIMP_PREVIEW_AREA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_PREVIEW_AREA, GimpPreviewArea))
+#define GIMP_PREVIEW_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_PREVIEW_AREA, GimpPreviewAreaClass))
+#define GIMP_IS_PREVIEW_AREA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_PREVIEW_AREA))
+#define GIMP_IS_PREVIEW_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_PREVIEW_AREA))
+#define GIMP_PREVIEW_AREA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_PREVIEW_AREA, GimpPreviewArea))
+
+
+typedef struct _GimpPreviewAreaClass GimpPreviewAreaClass;
+
+struct _GimpPreviewArea
+{
+ GtkDrawingArea parent_instance;
+
+ GimpCheckSize check_size;
+ GimpCheckType check_type;
+ gint width;
+ gint height;
+ gint rowstride;
+ gint offset_x;
+ gint offset_y;
+ gint max_width;
+ gint max_height;
+ guchar *buf;
+ guchar *colormap;
+};
+
+struct _GimpPreviewAreaClass
+{
+ GtkDrawingAreaClass parent_class;
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_preview_area_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_preview_area_new (void);
+
+void gimp_preview_area_draw (GimpPreviewArea *area,
+ gint x,
+ gint y,
+ gint width,
+ gint height,
+ GimpImageType type,
+ const guchar *buf,
+ gint rowstride);
+void gimp_preview_area_blend (GimpPreviewArea *area,
+ gint x,
+ gint y,
+ gint width,
+ gint height,
+ GimpImageType type,
+ const guchar *buf1,
+ gint rowstride1,
+ const guchar *buf2,
+ gint rowstride2,
+ guchar opacity);
+void gimp_preview_area_mask (GimpPreviewArea *area,
+ gint x,
+ gint y,
+ gint width,
+ gint height,
+ GimpImageType type,
+ const guchar *buf1,
+ gint rowstride1,
+ const guchar *buf2,
+ gint rowstride2,
+ const guchar *mask,
+ gint rowstride_mask);
+void gimp_preview_area_fill (GimpPreviewArea *area,
+ gint x,
+ gint y,
+ gint width,
+ gint height,
+ guchar red,
+ guchar green,
+ guchar blue);
+
+void gimp_preview_area_set_offsets (GimpPreviewArea *area,
+ gint x,
+ gint y);
+
+void gimp_preview_area_set_colormap (GimpPreviewArea *area,
+ const guchar *colormap,
+ gint num_colors);
+
+void gimp_preview_area_set_color_config (GimpPreviewArea *area,
+ GimpColorConfig *config);
+
+void gimp_preview_area_set_max_size (GimpPreviewArea *area,
+ gint width,
+ gint height);
+
+void gimp_preview_area_menu_popup (GimpPreviewArea *area,
+ GdkEventButton *event);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PREVIEW_AREA_H__ */
diff --git a/libgimpwidgets/gimppropwidgets.c b/libgimpwidgets/gimppropwidgets.c
new file mode 100644
index 0000000..4304a9e
--- /dev/null
+++ b/libgimpwidgets/gimppropwidgets.c
@@ -0,0 +1,4352 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppropwidgets.c
+ * Copyright (C) 2002-2007 Michael Natterer <mitch@gimp.org>
+ * Sven Neumann <sven@gimp.org>
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gegl.h>
+/* FIXME: #undef GTK_DISABLE_DEPRECATED */
+#undef GTK_DISABLE_DEPRECATED
+#include <gtk/gtk.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpmath/gimpmath.h"
+#include "libgimpbase/gimpbase.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "gimpwidgetstypes.h"
+
+#undef GIMP_DISABLE_DEPRECATED
+#include "gimpoldwidgets.h"
+#include "gimppropwidgets.h"
+#include "gimpspinbutton.h"
+#include "gimpunitmenu.h"
+
+#define GIMP_DISABLE_DEPRECATED
+#include "gimpwidgets.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimppropwidgets
+ * @title: GimpPropWidgets
+ * @short_description: Editable views on #GObject properties.
+ *
+ * Editable views on #GObject properties.
+ **/
+
+
+/* utility function prototypes */
+
+static void set_param_spec (GObject *object,
+ GtkWidget *widget,
+ GParamSpec *param_spec);
+static void set_radio_spec (GObject *object,
+ GParamSpec *param_spec);
+static GParamSpec * get_param_spec (GObject *object);
+
+static GParamSpec * find_param_spec (GObject *object,
+ const gchar *property_name,
+ const gchar *strloc);
+static GParamSpec * check_param_spec (GObject *object,
+ const gchar *property_name,
+ GType type,
+ const gchar *strloc);
+static GParamSpec * check_param_spec_w (GObject *object,
+ const gchar *property_name,
+ GType type,
+ const gchar *strloc);
+
+static gboolean get_numeric_values (GObject *object,
+ GParamSpec *param_spec,
+ gdouble *value,
+ gdouble *lower,
+ gdouble *upper,
+ const gchar *strloc);
+
+static void connect_notify (GObject *config,
+ const gchar *property_name,
+ GCallback callback,
+ gpointer callback_data);
+
+
+/******************/
+/* check button */
+/******************/
+
+static void gimp_prop_check_button_callback (GtkWidget *widget,
+ GObject *config);
+static void gimp_prop_check_button_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *button);
+
+/**
+ * gimp_prop_check_button_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of boolean property controlled by checkbutton.
+ * @label: Label to give checkbutton (including mnemonic).
+ *
+ * Creates a #GtkCheckButton that displays and sets the specified
+ * boolean property.
+ * If @label is #NULL, the @property_name's nick will be used as label
+ * of the returned button.
+ *
+ * Return value: The newly created #GtkCheckButton widget.
+ *
+ * Since: 2.4
+ */
+GtkWidget *
+gimp_prop_check_button_new (GObject *config,
+ const gchar *property_name,
+ const gchar *label)
+{
+ GParamSpec *param_spec;
+ GtkWidget *button;
+ gboolean value;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (property_name != NULL, NULL);
+
+ param_spec = check_param_spec_w (config, property_name,
+ G_TYPE_PARAM_BOOLEAN, G_STRFUNC);
+ if (! param_spec)
+ return NULL;
+
+ if (! label)
+ label = g_param_spec_get_nick (param_spec);
+
+ g_object_get (config,
+ property_name, &value,
+ NULL);
+
+ button = gtk_check_button_new_with_mnemonic (label);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), value);
+
+ set_param_spec (G_OBJECT (button), button, param_spec);
+
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (gimp_prop_check_button_callback),
+ config);
+
+ connect_notify (config, property_name,
+ G_CALLBACK (gimp_prop_check_button_notify),
+ button);
+
+ return button;
+}
+
+static void
+gimp_prop_check_button_callback (GtkWidget *widget,
+ GObject *config)
+{
+ GParamSpec *param_spec;
+ gboolean value;
+ gboolean v;
+
+ param_spec = get_param_spec (G_OBJECT (widget));
+ if (! param_spec)
+ return;
+
+ value = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+
+ g_object_get (config, param_spec->name, &v, NULL);
+
+ if (v != value)
+ {
+ g_object_set (config, param_spec->name, value, NULL);
+
+ gimp_toggle_button_sensitive_update (GTK_TOGGLE_BUTTON (widget));
+ }
+}
+
+static void
+gimp_prop_check_button_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *button)
+{
+ gboolean value;
+
+ g_object_get (config,
+ param_spec->name, &value,
+ NULL);
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) != value)
+ {
+ g_signal_handlers_block_by_func (button,
+ gimp_prop_check_button_callback,
+ config);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), value);
+ gimp_toggle_button_sensitive_update (GTK_TOGGLE_BUTTON (button));
+
+ g_signal_handlers_unblock_by_func (button,
+ gimp_prop_check_button_callback,
+ config);
+ }
+}
+
+
+static void gimp_prop_enum_check_button_callback (GtkWidget *widget,
+ GObject *config);
+static void gimp_prop_enum_check_button_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *button);
+
+/**
+ * gimp_prop_enum_check_button_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of enum property controlled by checkbutton.
+ * @label: Label to give checkbutton (including mnemonic).
+ * @false_value: Enum value corresponding to unchecked state.
+ * @true_value: Enum value corresponding to checked state.
+ *
+ * Creates a #GtkCheckButton that displays and sets the specified
+ * property of type Enum. Note that this widget only allows two values
+ * for the enum, one corresponding to the "checked" state and the
+ * other to the "unchecked" state.
+ * If @label is #NULL, the @property_name's nick will be used as label
+ * of the returned button.
+ *
+ * Return value: The newly created #GtkCheckButton widget.
+ *
+ * Since: 2.4
+ */
+GtkWidget *
+gimp_prop_enum_check_button_new (GObject *config,
+ const gchar *property_name,
+ const gchar *label,
+ gint false_value,
+ gint true_value)
+{
+ GParamSpec *param_spec;
+ GtkWidget *button;
+ gint value;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (property_name != NULL, NULL);
+
+ param_spec = check_param_spec_w (config, property_name,
+ G_TYPE_PARAM_ENUM, G_STRFUNC);
+ if (! param_spec)
+ return NULL;
+
+ if (! label)
+ label = g_param_spec_get_nick (param_spec);
+
+ g_object_get (config,
+ property_name, &value,
+ NULL);
+
+ button = gtk_check_button_new_with_mnemonic (label);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+ value == true_value);
+
+ if (value != false_value && value != true_value)
+ gtk_toggle_button_set_inconsistent (GTK_TOGGLE_BUTTON (button), TRUE);
+
+ set_param_spec (G_OBJECT (button), button, param_spec);
+
+ g_object_set_data (G_OBJECT (button), "false-value",
+ GINT_TO_POINTER (false_value));
+ g_object_set_data (G_OBJECT (button), "true-value",
+ GINT_TO_POINTER (true_value));
+
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (gimp_prop_enum_check_button_callback),
+ config);
+
+ connect_notify (config, property_name,
+ G_CALLBACK (gimp_prop_enum_check_button_notify),
+ button);
+
+ return button;
+}
+
+static void
+gimp_prop_enum_check_button_callback (GtkWidget *widget,
+ GObject *config)
+{
+ GParamSpec *param_spec;
+ gint false_value;
+ gint true_value;
+ gint value;
+ gint v;
+
+ param_spec = get_param_spec (G_OBJECT (widget));
+ if (! param_spec)
+ return;
+
+ false_value = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
+ "false-value"));
+ true_value = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
+ "true-value"));
+
+ value = (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)) ?
+ true_value : false_value);
+
+ g_object_get (config, param_spec->name, &v, NULL);
+
+ if (v != value)
+ {
+ g_object_set (config, param_spec->name, value, NULL);
+
+ gtk_toggle_button_set_inconsistent (GTK_TOGGLE_BUTTON (widget), FALSE);
+
+ gimp_toggle_button_sensitive_update (GTK_TOGGLE_BUTTON (widget));
+ }
+}
+
+static void
+gimp_prop_enum_check_button_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *button)
+{
+ gint value;
+ gint false_value;
+ gint true_value;
+ gboolean active = FALSE;
+ gboolean inconsistent = FALSE;
+
+ g_object_get (config,
+ param_spec->name, &value,
+ NULL);
+
+ false_value = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button),
+ "false-value"));
+ true_value = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button),
+ "true-value"));
+
+ if (value == true_value)
+ active = TRUE;
+ else if (value != false_value)
+ inconsistent = TRUE;
+
+ gtk_toggle_button_set_inconsistent (GTK_TOGGLE_BUTTON (button),
+ inconsistent);
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) != active)
+ {
+ g_signal_handlers_block_by_func (button,
+ gimp_prop_enum_check_button_callback,
+ config);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
+ gimp_toggle_button_sensitive_update (GTK_TOGGLE_BUTTON (button));
+
+ g_signal_handlers_unblock_by_func (button,
+ gimp_prop_enum_check_button_callback,
+ config);
+ }
+}
+
+
+/*************************/
+/* int/enum combo box */
+/*************************/
+
+static void gimp_prop_int_combo_box_callback (GtkWidget *widget,
+ GObject *config);
+static void gimp_prop_int_combo_box_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *widget);
+
+static void gimp_prop_pointer_combo_box_callback (GtkWidget *widget,
+ GObject *config);
+static void gimp_prop_pointer_combo_box_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *combo_box);
+
+/**
+ * gimp_prop_int_combo_box_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of int property controlled by combo box.
+ * @store: #GimpIntStore holding list of labels, values, etc.
+ *
+ * Creates a #GimpIntComboBox widget to display and set the specified
+ * property. The contents of the widget are determined by @store,
+ * which should be created using gimp_int_store_new().
+ *
+ * Return value: The newly created #GimpIntComboBox widget.
+ *
+ * Since: 2.4
+ */
+GtkWidget *
+gimp_prop_int_combo_box_new (GObject *config,
+ const gchar *property_name,
+ GimpIntStore *store)
+{
+ GParamSpec *param_spec;
+ GtkWidget *combo_box;
+ gint value;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (property_name != NULL, NULL);
+
+ param_spec = check_param_spec_w (config, property_name,
+ G_TYPE_PARAM_INT, G_STRFUNC);
+ if (! param_spec)
+ return NULL;
+
+ g_object_get (config,
+ property_name, &value,
+ NULL);
+
+ combo_box = g_object_new (GIMP_TYPE_INT_COMBO_BOX,
+ "model", store,
+ NULL);
+
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo_box), value);
+
+ g_signal_connect (combo_box, "changed",
+ G_CALLBACK (gimp_prop_int_combo_box_callback),
+ config);
+
+ set_param_spec (G_OBJECT (combo_box), combo_box, param_spec);
+
+ connect_notify (config, property_name,
+ G_CALLBACK (gimp_prop_int_combo_box_notify),
+ combo_box);
+
+ return combo_box;
+}
+
+/**
+ * gimp_prop_pointer_combo_box_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of GType/gpointer property controlled by combo box.
+ * @store: #GimpIntStore holding list of labels, values, etc.
+ *
+ * Creates a #GimpIntComboBox widget to display and set the specified
+ * property. The contents of the widget are determined by @store,
+ * which should be created using gimp_int_store_new().
+ * Values are GType/gpointer data, and therefore must be stored in the
+ * "user-data" column, instead of the usual "value" column.
+ *
+ * Return value: The newly created #GimpIntComboBox widget.
+ *
+ * Since: 2.10
+ */
+GtkWidget *
+gimp_prop_pointer_combo_box_new (GObject *config,
+ const gchar *property_name,
+ GimpIntStore *store)
+{
+ GParamSpec *param_spec;
+ GtkWidget *combo_box;
+ gpointer property_value;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (property_name != NULL, NULL);
+
+ param_spec = check_param_spec_w (config, property_name,
+ G_TYPE_PARAM_GTYPE, G_STRFUNC);
+ if (! param_spec)
+ {
+ param_spec = check_param_spec_w (config, property_name,
+ G_TYPE_PARAM_POINTER, G_STRFUNC);
+ if (! param_spec)
+ return NULL;
+ }
+
+ g_object_get (config,
+ property_name, &property_value,
+ NULL);
+
+ /* We use a GimpIntComboBox but we cannot store gpointer in the
+ * "value" column, because gpointer is not a subset of gint. Instead
+ * we store the value in the "user-data" column which is a gpointer.
+ */
+ combo_box = g_object_new (GIMP_TYPE_INT_COMBO_BOX,
+ "model", store,
+ NULL);
+
+ gimp_int_combo_box_set_active_by_user_data (GIMP_INT_COMBO_BOX (combo_box),
+ property_value);
+
+ g_signal_connect (combo_box, "changed",
+ G_CALLBACK (gimp_prop_pointer_combo_box_callback),
+ config);
+
+ set_param_spec (G_OBJECT (combo_box), combo_box, param_spec);
+
+ connect_notify (config, property_name,
+ G_CALLBACK (gimp_prop_pointer_combo_box_notify),
+ combo_box);
+
+ return combo_box;
+}
+
+/**
+ * gimp_prop_enum_combo_box_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of enum property controlled by combo box.
+ * @minimum: Smallest allowed value of enum.
+ * @maximum: Largest allowed value of enum.
+ *
+ * Creates a #GimpIntComboBox widget to display and set the specified
+ * enum property. The @mimimum_value and @maximum_value give the
+ * possibility of restricting the allowed range to a subset of the
+ * enum. If the two values are equal (e.g., 0, 0), then the full
+ * range of the Enum is used.
+ *
+ * Return value: The newly created #GimpEnumComboBox widget.
+ *
+ * Since: 2.4
+ */
+GtkWidget *
+gimp_prop_enum_combo_box_new (GObject *config,
+ const gchar *property_name,
+ gint minimum,
+ gint maximum)
+{
+ GParamSpec *param_spec;
+ GtkListStore *store = NULL;
+ GtkWidget *combo_box;
+ gint value;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (property_name != NULL, NULL);
+
+ param_spec = check_param_spec_w (config, property_name,
+ G_TYPE_PARAM_ENUM, G_STRFUNC);
+ if (! param_spec)
+ return NULL;
+
+ g_object_get (config,
+ property_name, &value,
+ NULL);
+
+ if (minimum != maximum)
+ {
+ store = gimp_enum_store_new_with_range (param_spec->value_type,
+ minimum, maximum);
+ }
+ else if (param_spec->value_type == GIMP_TYPE_DESATURATE_MODE)
+ {
+ /* this is a bad hack, if we get more of those, we should probably
+ * think of something less ugly
+ */
+ store = gimp_enum_store_new_with_values (param_spec->value_type,
+ 5,
+ GIMP_DESATURATE_LUMINANCE,
+ GIMP_DESATURATE_LUMA,
+ GIMP_DESATURATE_LIGHTNESS,
+ GIMP_DESATURATE_AVERAGE,
+ GIMP_DESATURATE_VALUE);
+ }
+ else if (param_spec->value_type == GIMP_TYPE_SELECT_CRITERION)
+ {
+ /* ditto */
+ store = gimp_enum_store_new_with_values (param_spec->value_type,
+ 12,
+ GIMP_SELECT_CRITERION_COMPOSITE,
+ GIMP_SELECT_CRITERION_R,
+ GIMP_SELECT_CRITERION_G,
+ GIMP_SELECT_CRITERION_B,
+ GIMP_SELECT_CRITERION_A,
+ GIMP_SELECT_CRITERION_H,
+ GIMP_SELECT_CRITERION_S,
+ GIMP_SELECT_CRITERION_V,
+ GIMP_SELECT_CRITERION_LCH_L,
+ GIMP_SELECT_CRITERION_LCH_C,
+ GIMP_SELECT_CRITERION_LCH_H);
+ }
+
+ if (store)
+ {
+ combo_box = g_object_new (GIMP_TYPE_ENUM_COMBO_BOX,
+ "model", store,
+ NULL);
+ g_object_unref (store);
+ }
+ else
+ {
+ combo_box = gimp_enum_combo_box_new (param_spec->value_type);
+ }
+
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo_box), value);
+
+ g_signal_connect (combo_box, "changed",
+ G_CALLBACK (gimp_prop_int_combo_box_callback),
+ config);
+
+ set_param_spec (G_OBJECT (combo_box), combo_box, param_spec);
+
+ connect_notify (config, property_name,
+ G_CALLBACK (gimp_prop_int_combo_box_notify),
+ combo_box);
+
+ return combo_box;
+}
+
+static void
+gimp_prop_int_combo_box_callback (GtkWidget *widget,
+ GObject *config)
+{
+ GParamSpec *param_spec;
+ gint value;
+
+ param_spec = get_param_spec (G_OBJECT (widget));
+ if (! param_spec)
+ return;
+
+ if (gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &value))
+ {
+ gint v;
+
+ g_object_get (config, param_spec->name, &v, NULL);
+
+ if (v != value)
+ g_object_set (config, param_spec->name, value, NULL);
+ }
+}
+
+static void
+gimp_prop_int_combo_box_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *combo_box)
+{
+ gint value;
+
+ g_object_get (config,
+ param_spec->name, &value,
+ NULL);
+
+ g_signal_handlers_block_by_func (combo_box,
+ gimp_prop_int_combo_box_callback,
+ config);
+
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo_box), value);
+
+ g_signal_handlers_unblock_by_func (combo_box,
+ gimp_prop_int_combo_box_callback,
+ config);
+}
+
+static void
+gimp_prop_pointer_combo_box_callback (GtkWidget *widget,
+ GObject *config)
+{
+ GParamSpec *param_spec;
+ gpointer value;
+
+ param_spec = get_param_spec (G_OBJECT (widget));
+ if (! param_spec)
+ return;
+
+ if (gimp_int_combo_box_get_active_user_data (GIMP_INT_COMBO_BOX (widget),
+ &value))
+ {
+ gpointer v;
+
+ g_object_get (config, param_spec->name, &v, NULL);
+
+ if (v != value)
+ g_object_set (config, param_spec->name, value, NULL);
+ }
+}
+
+static void
+gimp_prop_pointer_combo_box_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *combo_box)
+{
+ gpointer value;
+
+ g_object_get (config,
+ param_spec->name, &value,
+ NULL);
+
+ g_signal_handlers_block_by_func (combo_box,
+ gimp_prop_pointer_combo_box_callback,
+ config);
+
+ gimp_int_combo_box_set_active_by_user_data (GIMP_INT_COMBO_BOX (combo_box),
+ value);
+
+ g_signal_handlers_unblock_by_func (combo_box,
+ gimp_prop_pointer_combo_box_callback,
+ config);
+}
+
+/************************/
+/* boolean combo box */
+/************************/
+
+static void gimp_prop_boolean_combo_box_callback (GtkWidget *combo,
+ GObject *config);
+static void gimp_prop_boolean_combo_box_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *combo);
+
+
+/**
+ * gimp_prop_boolean_combo_box_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of boolean property controlled by combo box.
+ * @true_text: Label used for entry corresponding to %TRUE value.
+ * @false_text: Label used for entry corresponding to %FALSE value.
+ *
+ * Creates a #GtkComboBox widget to display and set the specified
+ * boolean property. The combo box will have two entries, one
+ * displaying the @true_text label, the other displaying the
+ * @false_text label.
+ *
+ * Return value: The newly created #GtkComboBox widget.
+ *
+ * Since: 2.4
+ */
+GtkWidget *
+gimp_prop_boolean_combo_box_new (GObject *config,
+ const gchar *property_name,
+ const gchar *true_text,
+ const gchar *false_text)
+{
+ GParamSpec *param_spec;
+ GtkWidget *combo;
+ gboolean value;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (property_name != NULL, NULL);
+
+ param_spec = check_param_spec_w (config, property_name,
+ G_TYPE_PARAM_BOOLEAN, G_STRFUNC);
+ if (! param_spec)
+ return NULL;
+
+ g_object_get (config,
+ property_name, &value,
+ NULL);
+
+ combo = gimp_int_combo_box_new (true_text, TRUE,
+ false_text, FALSE,
+ NULL);
+
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), value);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (gimp_prop_boolean_combo_box_callback),
+ config);
+
+ set_param_spec (G_OBJECT (combo), combo, param_spec);
+
+ connect_notify (config, property_name,
+ G_CALLBACK (gimp_prop_boolean_combo_box_notify),
+ combo);
+
+ return combo;
+}
+
+static void
+gimp_prop_boolean_combo_box_callback (GtkWidget *combo,
+ GObject *config)
+{
+ GParamSpec *param_spec;
+ gint value;
+
+ param_spec = get_param_spec (G_OBJECT (combo));
+ if (! param_spec)
+ return;
+
+ if (gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo), &value))
+ {
+ gint v;
+
+ g_object_get (config, param_spec->name, &v, NULL);
+
+ if (v != value)
+ g_object_set (config, param_spec->name, value, NULL);
+ }
+}
+
+static void
+gimp_prop_boolean_combo_box_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *combo)
+{
+ gboolean value;
+
+ g_object_get (config,
+ param_spec->name, &value,
+ NULL);
+
+ g_signal_handlers_block_by_func (combo,
+ gimp_prop_boolean_combo_box_callback,
+ config);
+
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), value);
+
+ g_signal_handlers_unblock_by_func (combo,
+ gimp_prop_boolean_combo_box_callback,
+ config);
+}
+
+
+/*****************/
+/* radio boxes */
+/*****************/
+
+static void gimp_prop_radio_button_callback (GtkWidget *widget,
+ GObject *config);
+static void gimp_prop_radio_button_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *button);
+
+
+/**
+ * gimp_prop_enum_radio_frame_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of enum property controlled by the radio buttons.
+ * @title: Label for the frame holding the buttons
+ * @minimum: Smallest value of enum to be included.
+ * @maximum: Largest value of enum to be included.
+ *
+ * Creates a group of radio buttons which function to set and display
+ * the specified enum property. The @minimum and @maximum arguments
+ * allow only a subset of the enum to be used. If the two arguments
+ * are equal (e.g., 0, 0), then the full range of the enum will be used.
+ * If @title is #NULL, the @property_name's nick will be used as label
+ * of the returned frame.
+ *
+ * Return value: A #GimpFrame containing the radio buttons.
+ *
+ * Since: 2.4
+ */
+GtkWidget *
+gimp_prop_enum_radio_frame_new (GObject *config,
+ const gchar *property_name,
+ const gchar *title,
+ gint minimum,
+ gint maximum)
+{
+ GParamSpec *param_spec;
+ GtkWidget *frame;
+ GtkWidget *button;
+ gint value;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (property_name != NULL, NULL);
+
+ param_spec = check_param_spec_w (config, property_name,
+ G_TYPE_PARAM_ENUM, G_STRFUNC);
+ if (! param_spec)
+ return NULL;
+
+ if (! title)
+ title = g_param_spec_get_nick (param_spec);
+
+ g_object_get (config,
+ property_name, &value,
+ NULL);
+
+ if (minimum != maximum)
+ {
+ frame = gimp_enum_radio_frame_new_with_range (param_spec->value_type,
+ minimum, maximum,
+ gtk_label_new (title),
+ G_CALLBACK (gimp_prop_radio_button_callback),
+ config,
+ &button);
+ }
+ else
+ {
+ frame = gimp_enum_radio_frame_new (param_spec->value_type,
+ gtk_label_new (title),
+ G_CALLBACK (gimp_prop_radio_button_callback),
+ config,
+ &button);
+ }
+
+ gimp_int_radio_group_set_active (GTK_RADIO_BUTTON (button), value);
+
+ set_radio_spec (G_OBJECT (button), param_spec);
+
+ connect_notify (config, property_name,
+ G_CALLBACK (gimp_prop_radio_button_notify),
+ button);
+
+ g_object_set_data (G_OBJECT (frame), "radio-button", button);
+
+ return frame;
+}
+
+/**
+ * gimp_prop_enum_radio_box_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of enum property controlled by the radio buttons.
+ * @minimum: Smallest value of enum to be included.
+ * @maximum: Largest value of enum to be included.
+ *
+ * Creates a group of radio buttons which function to set and display
+ * the specified enum property. The @minimum and @maximum arguments
+ * allow only a subset of the enum to be used. If the two arguments
+ * are equal (e.g., 0, 0), then the full range of the enum will be used.
+ * If you want to assign a label to the group of radio buttons, use
+ * gimp_prop_enum_radio_frame_new() instead of this function.
+ *
+ * Return value: A #GtkVBox containing the radio buttons.
+ *
+ * Since: 2.4
+ */
+GtkWidget *
+gimp_prop_enum_radio_box_new (GObject *config,
+ const gchar *property_name,
+ gint minimum,
+ gint maximum)
+{
+ GParamSpec *param_spec;
+ GtkWidget *vbox;
+ GtkWidget *button;
+ gint value;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (property_name != NULL, NULL);
+
+ param_spec = check_param_spec_w (config, property_name,
+ G_TYPE_PARAM_ENUM, G_STRFUNC);
+ if (! param_spec)
+ return NULL;
+
+ g_object_get (config,
+ property_name, &value,
+ NULL);
+
+ if (minimum != maximum)
+ {
+ vbox = gimp_enum_radio_box_new_with_range (param_spec->value_type,
+ minimum, maximum,
+ G_CALLBACK (gimp_prop_radio_button_callback),
+ config,
+ &button);
+ }
+ else
+ {
+ vbox = gimp_enum_radio_box_new (param_spec->value_type,
+ G_CALLBACK (gimp_prop_radio_button_callback),
+ config,
+ &button);
+ }
+
+ gimp_int_radio_group_set_active (GTK_RADIO_BUTTON (button), value);
+
+ set_radio_spec (G_OBJECT (button), param_spec);
+
+ connect_notify (config, property_name,
+ G_CALLBACK (gimp_prop_radio_button_notify),
+ button);
+
+ g_object_set_data (G_OBJECT (vbox), "radio-button", button);
+
+ return vbox;
+}
+
+
+/***********/
+/* label */
+/***********/
+
+static void gimp_prop_enum_label_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *label);
+
+/**
+ * gimp_prop_enum_label_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of enum property to be displayed.
+ *
+ * Return value: The newly created #GimpEnumLabel widget.
+ *
+ * Since: 2.4
+ */
+GtkWidget *
+gimp_prop_enum_label_new (GObject *config,
+ const gchar *property_name)
+{
+ GParamSpec *param_spec;
+ GtkWidget *label;
+ gint value;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (property_name != NULL, NULL);
+
+ param_spec = check_param_spec (config, property_name,
+ G_TYPE_PARAM_ENUM, G_STRFUNC);
+ if (! param_spec)
+ return NULL;
+
+ g_object_get (config,
+ property_name, &value,
+ NULL);
+
+ label = gimp_enum_label_new (param_spec->value_type, value);
+
+ set_param_spec (G_OBJECT (label), label, param_spec);
+
+ connect_notify (config, property_name,
+ G_CALLBACK (gimp_prop_enum_label_notify),
+ label);
+
+ return label;
+}
+
+static void
+gimp_prop_enum_label_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *label)
+{
+ gint value;
+
+ g_object_get (config,
+ param_spec->name, &value,
+ NULL);
+
+ gimp_enum_label_set_value (GIMP_ENUM_LABEL (label), value);
+}
+
+
+/**
+ * gimp_prop_boolean_radio_frame_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of boolean property controlled by the radio buttons.
+ * @title: Label for the frame.
+ * @true_text: Label for the button corresponding to %TRUE.
+ * @false_text: Label for the button corresponding to %FALSE.
+ *
+ * Creates a pair of radio buttons which function to set and display
+ * the specified boolean property.
+ * If @title is #NULL, the @property_name's nick will be used as label
+ * of the returned frame.
+ *
+ * Return value: A #GimpFrame containing the radio buttons.
+ *
+ * Since: 2.4
+ */
+GtkWidget *
+gimp_prop_boolean_radio_frame_new (GObject *config,
+ const gchar *property_name,
+ const gchar *title,
+ const gchar *true_text,
+ const gchar *false_text)
+{
+ GParamSpec *param_spec;
+ GtkWidget *frame;
+ GtkWidget *button;
+ gboolean value;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (property_name != NULL, NULL);
+
+ param_spec = check_param_spec_w (config, property_name,
+ G_TYPE_PARAM_BOOLEAN, G_STRFUNC);
+ if (! param_spec)
+ return NULL;
+
+ if (! title)
+ title = g_param_spec_get_nick (param_spec);
+
+ g_object_get (config,
+ property_name, &value,
+ NULL);
+
+ frame =
+ gimp_int_radio_group_new (TRUE, title,
+ G_CALLBACK (gimp_prop_radio_button_callback),
+ config, value,
+
+ false_text, FALSE, &button,
+ true_text, TRUE, NULL,
+
+ NULL);
+
+ set_radio_spec (G_OBJECT (button), param_spec);
+
+ connect_notify (config, property_name,
+ G_CALLBACK (gimp_prop_radio_button_notify),
+ button);
+
+ g_object_set_data (G_OBJECT (frame), "radio-button", button);
+
+ return frame;
+}
+
+/**
+ * gimp_prop_enum_stock_box_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of enum property controlled by the radio buttons.
+ * @stock_prefix: The prefix of the group of stock ids to use.
+ * @minimum: Smallest value of enum to be included.
+ * @maximum: Largest value of enum to be included.
+ *
+ * Creates a horizontal box of radio buttons with stock icons, which
+ * function to set and display the value of the specified Enum
+ * property. The stock_id for each icon is created by appending the
+ * enum_value's nick to the given @stock_prefix. See
+ * gimp_enum_stock_box_new() for more information.
+ *
+ * Return value: A #libgimpwidgets-gimpenumstockbox containing the radio buttons.
+ *
+ * Since: 2.4
+ *
+ * Deprecated: 2.10
+ */
+GtkWidget *
+gimp_prop_enum_stock_box_new (GObject *config,
+ const gchar *property_name,
+ const gchar *stock_prefix,
+ gint minimum,
+ gint maximum)
+{
+ return gimp_prop_enum_icon_box_new (config, property_name,
+ stock_prefix, minimum, maximum);
+}
+
+/**
+ * gimp_prop_enum_icon_box_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of enum property controlled by the radio buttons.
+ * @icon_prefix: The prefix of the group of icon names to use.
+ * @minimum: Smallest value of enum to be included.
+ * @maximum: Largest value of enum to be included.
+ *
+ * Creates a horizontal box of radio buttons with named icons, which
+ * function to set and display the value of the specified Enum
+ * property. The icon name for each icon is created by appending the
+ * enum_value's nick to the given @icon_prefix. See
+ * gimp_enum_icon_box_new() for more information.
+ *
+ * Return value: A #libgimpwidgets-gimpenumiconbox containing the radio buttons.
+ *
+ * Since: 2.10
+ */
+GtkWidget *
+gimp_prop_enum_icon_box_new (GObject *config,
+ const gchar *property_name,
+ const gchar *icon_prefix,
+ gint minimum,
+ gint maximum)
+{
+ GParamSpec *param_spec;
+ GtkWidget *box;
+ GtkWidget *button;
+ gint value;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (property_name != NULL, NULL);
+
+ param_spec = check_param_spec_w (config, property_name,
+ G_TYPE_PARAM_ENUM, G_STRFUNC);
+ if (! param_spec)
+ return NULL;
+
+ g_object_get (config,
+ property_name, &value,
+ NULL);
+
+ if (minimum != maximum)
+ {
+ box = gimp_enum_icon_box_new_with_range (param_spec->value_type,
+ minimum, maximum,
+ icon_prefix,
+ GTK_ICON_SIZE_MENU,
+ G_CALLBACK (gimp_prop_radio_button_callback),
+ config,
+ &button);
+ }
+ else
+ {
+ box = gimp_enum_icon_box_new (param_spec->value_type,
+ icon_prefix,
+ GTK_ICON_SIZE_MENU,
+ G_CALLBACK (gimp_prop_radio_button_callback),
+ config,
+ &button);
+ }
+
+ gimp_int_radio_group_set_active (GTK_RADIO_BUTTON (button), value);
+
+ set_radio_spec (G_OBJECT (button), param_spec);
+
+ connect_notify (config, property_name,
+ G_CALLBACK (gimp_prop_radio_button_notify),
+ button);
+
+ return box;
+}
+
+static void
+gimp_prop_radio_button_callback (GtkWidget *widget,
+ GObject *config)
+{
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ {
+ GParamSpec *param_spec;
+ gint value;
+ gint v;
+
+ param_spec = get_param_spec (G_OBJECT (widget));
+ if (! param_spec)
+ return;
+
+ value = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
+ "gimp-item-data"));
+
+ g_object_get (config, param_spec->name, &v, NULL);
+
+ if (v != value)
+ g_object_set (config, param_spec->name, value, NULL);
+ }
+}
+
+static void
+gimp_prop_radio_button_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *button)
+{
+ gint value;
+
+ g_object_get (config,
+ param_spec->name, &value,
+ NULL);
+
+ gimp_int_radio_group_set_active (GTK_RADIO_BUTTON (button), value);
+}
+
+
+/*****************/
+/* adjustments */
+/*****************/
+
+static void gimp_prop_adjustment_callback (GtkAdjustment *adjustment,
+ GObject *config);
+static void gimp_prop_adjustment_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkAdjustment *adjustment);
+
+/**
+ * gimp_prop_spin_button_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of double property controlled by the spin button.
+ * @step_increment: Step size.
+ * @page_increment: Page size.
+ * @digits: Number of digits after decimal point to display.
+ *
+ * Creates a spin button to set and display the value of the
+ * specified double property.
+ *
+ * Return value: A new #libgimpwidgets-gimpspinbutton.
+ *
+ * Since: 2.4
+ */
+GtkWidget *
+gimp_prop_spin_button_new (GObject *config,
+ const gchar *property_name,
+ gdouble step_increment,
+ gdouble page_increment,
+ gint digits)
+{
+ GParamSpec *param_spec;
+ GtkWidget *spinbutton;
+ GtkAdjustment *adjustment;
+ gdouble value;
+ gdouble lower;
+ gdouble upper;
+
+ param_spec = find_param_spec (config, property_name, G_STRFUNC);
+ if (! param_spec)
+ return NULL;
+
+ if (! get_numeric_values (config,
+ param_spec, &value, &lower, &upper, G_STRFUNC))
+ return NULL;
+
+ if (! G_IS_PARAM_SPEC_DOUBLE (param_spec))
+ digits = 0;
+
+ adjustment = (GtkAdjustment *)
+ gtk_adjustment_new (value, lower, upper,
+ step_increment, page_increment, 0);
+
+ spinbutton = gimp_spin_button_new (adjustment, step_increment, digits);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+
+ set_param_spec (G_OBJECT (adjustment), spinbutton, param_spec);
+
+ g_signal_connect (adjustment, "value-changed",
+ G_CALLBACK (gimp_prop_adjustment_callback),
+ config);
+
+ connect_notify (config, property_name,
+ G_CALLBACK (gimp_prop_adjustment_notify),
+ adjustment);
+
+ return spinbutton;
+}
+
+/**
+ * gimp_prop_hscale_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of integer or double property controlled by the scale.
+ * @step_increment: Step size.
+ * @page_increment: Page size.
+ * @digits: Number of digits after decimal point to display.
+ *
+ * Creates a horizontal scale to control the value of the specified
+ * integer or double property.
+ *
+ * Return value: A new #GtkScale.
+ *
+ * Since: 2.4
+ */
+GtkWidget *
+gimp_prop_hscale_new (GObject *config,
+ const gchar *property_name,
+ gdouble step_increment,
+ gdouble page_increment,
+ gint digits)
+{
+ GParamSpec *param_spec;
+ GtkWidget *scale;
+ GtkAdjustment *adjustment;
+ gdouble value;
+ gdouble lower;
+ gdouble upper;
+
+ param_spec = find_param_spec (config, property_name, G_STRFUNC);
+ if (! param_spec)
+ return NULL;
+
+ if (! get_numeric_values (config,
+ param_spec, &value, &lower, &upper, G_STRFUNC))
+ return NULL;
+
+ if (! G_IS_PARAM_SPEC_DOUBLE (param_spec))
+ digits = 0;
+
+ adjustment = (GtkAdjustment *)
+ gtk_adjustment_new (value, lower, upper,
+ step_increment, page_increment, 0.0);
+
+ scale = g_object_new (GTK_TYPE_SCALE,
+ "orientation", GTK_ORIENTATION_HORIZONTAL,
+ "adjustment", adjustment,
+ "digits", digits,
+ NULL);
+
+ set_param_spec (G_OBJECT (adjustment), scale, param_spec);
+
+ g_signal_connect (adjustment, "value-changed",
+ G_CALLBACK (gimp_prop_adjustment_callback),
+ config);
+
+ connect_notify (config, property_name,
+ G_CALLBACK (gimp_prop_adjustment_notify),
+ adjustment);
+
+ return scale;
+}
+
+/**
+ * gimp_prop_scale_entry_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of double property controlled by the spin button.
+ * @table: The #GtkTable the widgets will be attached to.
+ * @column: The column to start with.
+ * @row: The row to attach the widgets.
+ * @label: The text for the #GtkLabel which will appear left of
+ * the #GtkHScale.
+ * @step_increment: Step size.
+ * @page_increment: Page size.
+ * @digits: Number of digits after decimal point to display.
+ * @limit_scale: %FALSE if the range of possible values of the
+ * GtkHScale should be the same as of the GtkSpinButton.
+ * @lower_limit: The scale's lower boundary if @scale_limits is %TRUE.
+ * @upper_limit: The scale's upper boundary if @scale_limits is %TRUE.
+ *
+ * Creates a #libgimpwidgets-gimpscaleentry (slider and spin button)
+ * to set and display the value of the specified double property. See
+ * gimp_scale_entry_new() for more information.
+ * If @label is #NULL, the @property_name's nick will be used as label
+ * of the returned object.
+ *
+ * Note that the @scale_limits boolean is the inverse of
+ * gimp_scale_entry_new()'s "constrain" parameter.
+ *
+ * Return value: The #GtkSpinButton's #GtkAdjustment.
+ *
+ * Since: 2.4
+ */
+GtkObject *
+gimp_prop_scale_entry_new (GObject *config,
+ const gchar *property_name,
+ GtkTable *table,
+ gint column,
+ gint row,
+ const gchar *label,
+ gdouble step_increment,
+ gdouble page_increment,
+ gint digits,
+ gboolean limit_scale,
+ gdouble lower_limit,
+ gdouble upper_limit)
+{
+ GParamSpec *param_spec;
+ GtkObject *adjustment;
+ const gchar *tooltip;
+ gdouble value;
+ gdouble lower;
+ gdouble upper;
+
+ param_spec = find_param_spec (config, property_name, G_STRFUNC);
+ if (! param_spec)
+ return NULL;
+
+ if (! get_numeric_values (config,
+ param_spec, &value, &lower, &upper, G_STRFUNC))
+ return NULL;
+
+ if (! label)
+ label = g_param_spec_get_nick (param_spec);
+
+ tooltip = g_param_spec_get_blurb (param_spec);
+
+ if (! limit_scale)
+ {
+ adjustment = gimp_scale_entry_new (table, column, row,
+ label, -1, -1,
+ value, lower, upper,
+ step_increment, page_increment,
+ digits,
+ TRUE, 0.0, 0.0,
+ tooltip,
+ NULL);
+ }
+ else
+ {
+ adjustment = gimp_scale_entry_new (table, column, row,
+ label, -1, -1,
+ value, lower_limit, upper_limit,
+ step_increment, page_increment,
+ digits,
+ FALSE, lower, upper,
+ tooltip,
+ NULL);
+ }
+
+ set_param_spec (G_OBJECT (adjustment), NULL, param_spec);
+
+ g_signal_connect (adjustment, "value-changed",
+ G_CALLBACK (gimp_prop_adjustment_callback),
+ config);
+
+ connect_notify (config, property_name,
+ G_CALLBACK (gimp_prop_adjustment_notify),
+ adjustment);
+
+ return adjustment;
+}
+
+static void
+gimp_prop_widget_set_factor (GtkWidget *widget,
+ GtkAdjustment *adjustment,
+ gdouble factor,
+ gdouble step_increment,
+ gdouble page_increment,
+ gint digits)
+{
+ gdouble *factor_store;
+ gdouble old_factor = 1.0;
+ gdouble f;
+
+ g_return_if_fail (widget == NULL || GTK_IS_SPIN_BUTTON (widget));
+ g_return_if_fail (widget != NULL || GTK_IS_ADJUSTMENT (adjustment));
+ g_return_if_fail (factor != 0.0);
+ g_return_if_fail (digits >= 0);
+
+ if (! adjustment)
+ adjustment = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (widget));
+
+ g_return_if_fail (get_param_spec (G_OBJECT (adjustment)) != NULL);
+
+ factor_store = g_object_get_data (G_OBJECT (adjustment),
+ "gimp-prop-adjustment-factor");
+ if (factor_store)
+ {
+ old_factor = *factor_store;
+ }
+ else
+ {
+ factor_store = g_new (gdouble, 1);
+ g_object_set_data_full (G_OBJECT (adjustment),
+ "gimp-prop-adjustment-factor",
+ factor_store, (GDestroyNotify) g_free);
+ }
+
+ *factor_store = factor;
+
+ f = factor / old_factor;
+
+ if (step_increment <= 0)
+ step_increment = f * gtk_adjustment_get_step_increment (adjustment);
+
+ if (page_increment <= 0)
+ page_increment = f * gtk_adjustment_get_page_increment (adjustment);
+
+ gtk_adjustment_configure (adjustment,
+ f * gtk_adjustment_get_value (adjustment),
+ f * gtk_adjustment_get_lower (adjustment),
+ f * gtk_adjustment_get_upper (adjustment),
+ step_increment,
+ page_increment,
+ f * gtk_adjustment_get_page_size (adjustment));
+
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (widget), digits);
+}
+
+/**
+ * gimp_prop_opacity_entry_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of double property controlled by the spin button.
+ * @table: The #GtkTable the widgets will be attached to.
+ * @column: The column to start with.
+ * @row: The row to attach the widgets.
+ * @label: The text for the #GtkLabel which will appear left of the
+ * #GtkHScale.
+ *
+ * Creates a #libgimpwidgets-gimpscaleentry (slider and spin button)
+ * to set and display the value of the specified double property,
+ * which should represent an "opacity" variable with range 0 to 100.
+ * See gimp_scale_entry_new() for more information.
+ *
+ * Return value: The #GtkSpinButton's #GtkAdjustment.
+ *
+ * Since: 2.4
+ */
+GtkObject *
+gimp_prop_opacity_entry_new (GObject *config,
+ const gchar *property_name,
+ GtkTable *table,
+ gint column,
+ gint row,
+ const gchar *label)
+{
+ GtkObject *adjustment;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (property_name != NULL, NULL);
+
+ adjustment = gimp_prop_scale_entry_new (config, property_name,
+ table, column, row, label,
+ 0.01, 0.1, 1,
+ FALSE, 0.0, 0.0);
+
+ if (adjustment)
+ {
+ gimp_prop_widget_set_factor (GIMP_SCALE_ENTRY_SPINBUTTON (adjustment),
+ GTK_ADJUSTMENT (adjustment),
+ 100.0, 0.0, 0.0, 1);
+ }
+
+ return adjustment;
+}
+
+
+static void
+gimp_prop_adjustment_callback (GtkAdjustment *adjustment,
+ GObject *config)
+{
+ GParamSpec *param_spec;
+ gdouble value;
+ gdouble *factor;
+
+ param_spec = get_param_spec (G_OBJECT (adjustment));
+ if (! param_spec)
+ return;
+
+ value = gtk_adjustment_get_value (adjustment);
+
+ factor = g_object_get_data (G_OBJECT (adjustment),
+ "gimp-prop-adjustment-factor");
+ if (factor)
+ value /= *factor;
+
+ if (G_IS_PARAM_SPEC_INT (param_spec))
+ {
+ gint v;
+
+ g_object_get (config, param_spec->name, &v, NULL);
+
+ if (v != (gint) value)
+ g_object_set (config, param_spec->name, (gint) value, NULL);
+ }
+ else if (G_IS_PARAM_SPEC_UINT (param_spec))
+ {
+ guint v;
+
+ g_object_get (config, param_spec->name, &v, NULL);
+
+ if (v != (guint) value)
+ g_object_set (config, param_spec->name, (guint) value, NULL);
+ }
+ else if (G_IS_PARAM_SPEC_LONG (param_spec))
+ {
+ glong v;
+
+ g_object_get (config, param_spec->name, &v, NULL);
+
+ if (v != (glong) value)
+ g_object_set (config, param_spec->name, (glong) value, NULL);
+ }
+ else if (G_IS_PARAM_SPEC_ULONG (param_spec))
+ {
+ gulong v;
+
+ g_object_get (config, param_spec->name, &v, NULL);
+
+ if (v != (gulong) value)
+ g_object_set (config, param_spec->name, (gulong) value, NULL);
+ }
+ else if (G_IS_PARAM_SPEC_INT64 (param_spec))
+ {
+ gint64 v;
+
+ g_object_get (config, param_spec->name, &v, NULL);
+
+ if (v != (gint64) value)
+ g_object_set (config, param_spec->name, (gint64) value, NULL);
+ }
+ else if (G_IS_PARAM_SPEC_UINT64 (param_spec))
+ {
+ guint64 v;
+
+ g_object_get (config, param_spec->name, &v, NULL);
+
+ if (v != (guint64) value)
+ g_object_set (config, param_spec->name, (guint64) value, NULL);
+ }
+ else if (G_IS_PARAM_SPEC_DOUBLE (param_spec))
+ {
+ gdouble v;
+
+ g_object_get (config, param_spec->name, &v, NULL);
+
+ if (v != value)
+ g_object_set (config, param_spec->name, value, NULL);
+ }
+}
+
+static void
+gimp_prop_adjustment_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkAdjustment *adjustment)
+{
+ gdouble value;
+ gdouble *factor;
+
+ if (G_IS_PARAM_SPEC_INT (param_spec))
+ {
+ gint int_value;
+
+ g_object_get (config, param_spec->name, &int_value, NULL);
+
+ value = int_value;
+ }
+ else if (G_IS_PARAM_SPEC_UINT (param_spec))
+ {
+ guint uint_value;
+
+ g_object_get (config, param_spec->name, &uint_value, NULL);
+
+ value = uint_value;
+ }
+ else if (G_IS_PARAM_SPEC_LONG (param_spec))
+ {
+ glong long_value;
+
+ g_object_get (config, param_spec->name, &long_value, NULL);
+
+ value = long_value;
+ }
+ else if (G_IS_PARAM_SPEC_ULONG (param_spec))
+ {
+ gulong ulong_value;
+
+ g_object_get (config, param_spec->name, &ulong_value, NULL);
+
+ value = ulong_value;
+ }
+ else if (G_IS_PARAM_SPEC_INT64 (param_spec))
+ {
+ gint64 int64_value;
+
+ g_object_get (config, param_spec->name, &int64_value, NULL);
+
+ value = int64_value;
+ }
+ else if (G_IS_PARAM_SPEC_UINT64 (param_spec))
+ {
+ guint64 uint64_value;
+
+ g_object_get (config, param_spec->name, &uint64_value, NULL);
+
+#if defined _MSC_VER && (_MSC_VER < 1300)
+ value = (gint64) uint64_value;
+#else
+ value = uint64_value;
+#endif
+ }
+ else if (G_IS_PARAM_SPEC_DOUBLE (param_spec))
+ {
+ g_object_get (config, param_spec->name, &value, NULL);
+ }
+ else
+ {
+ g_warning ("%s: unhandled param spec of type %s",
+ G_STRFUNC, G_PARAM_SPEC_TYPE_NAME (param_spec));
+ return;
+ }
+
+ factor = g_object_get_data (G_OBJECT (adjustment),
+ "gimp-prop-adjustment-factor");
+ if (factor)
+ value *= *factor;
+
+ if (gtk_adjustment_get_value (adjustment) != value)
+ {
+ g_signal_handlers_block_by_func (adjustment,
+ gimp_prop_adjustment_callback,
+ config);
+
+ gtk_adjustment_set_value (adjustment, value);
+
+ g_signal_handlers_unblock_by_func (adjustment,
+ gimp_prop_adjustment_callback,
+ config);
+ }
+}
+
+
+/*************/
+/* memsize */
+/*************/
+
+static void gimp_prop_memsize_callback (GimpMemsizeEntry *entry,
+ GObject *config);
+static void gimp_prop_memsize_notify (GObject *config,
+ GParamSpec *param_spec,
+ GimpMemsizeEntry *entry);
+
+/**
+ * gimp_prop_memsize_entry_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of memsize property.
+ *
+ * Creates a #GimpMemsizeEntry (spin button and option menu) to set
+ * and display the value of the specified memsize property. See
+ * gimp_memsize_entry_new() for more information.
+ *
+ * Return value: A new #GimpMemsizeEntry.
+ *
+ * Since: 2.4
+ */
+GtkWidget *
+gimp_prop_memsize_entry_new (GObject *config,
+ const gchar *property_name)
+{
+ GParamSpec *param_spec;
+ GParamSpecUInt64 *uint64_spec;
+ GtkWidget *entry;
+ guint64 value;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (property_name != NULL, NULL);
+
+ param_spec = check_param_spec_w (config, property_name,
+ GIMP_TYPE_PARAM_MEMSIZE, G_STRFUNC);
+ if (! param_spec)
+ return NULL;
+
+ g_object_get (config,
+ property_name, &value,
+ NULL);
+
+ uint64_spec = G_PARAM_SPEC_UINT64 (param_spec);
+
+ g_return_val_if_fail (uint64_spec->minimum <= GIMP_MAX_MEMSIZE, NULL);
+ g_return_val_if_fail (uint64_spec->maximum <= GIMP_MAX_MEMSIZE, NULL);
+
+ entry = gimp_memsize_entry_new (value,
+ uint64_spec->minimum,
+ uint64_spec->maximum);
+
+ set_param_spec (G_OBJECT (entry),
+ GIMP_MEMSIZE_ENTRY (entry)->spinbutton,
+ param_spec);
+
+ g_signal_connect (entry, "value-changed",
+ G_CALLBACK (gimp_prop_memsize_callback),
+ config);
+
+ connect_notify (config, property_name,
+ G_CALLBACK (gimp_prop_memsize_notify),
+ entry);
+
+ return entry;
+}
+
+
+static void
+gimp_prop_memsize_callback (GimpMemsizeEntry *entry,
+ GObject *config)
+{
+ GParamSpec *param_spec;
+ guint64 value;
+ guint64 v;
+
+ param_spec = get_param_spec (G_OBJECT (entry));
+ if (! param_spec)
+ return;
+
+ g_return_if_fail (G_IS_PARAM_SPEC_UINT64 (param_spec));
+
+ value = gimp_memsize_entry_get_value (entry);
+
+ g_object_get (config, param_spec->name, &v, NULL);
+
+ if (v != value)
+ g_object_set (config, param_spec->name, value, NULL);
+}
+
+static void
+gimp_prop_memsize_notify (GObject *config,
+ GParamSpec *param_spec,
+ GimpMemsizeEntry *entry)
+{
+ guint64 value;
+
+ g_return_if_fail (G_IS_PARAM_SPEC_UINT64 (param_spec));
+
+ g_object_get (config,
+ param_spec->name, &value,
+ NULL);
+
+ if (entry->value != value)
+ {
+ g_signal_handlers_block_by_func (entry,
+ gimp_prop_memsize_callback,
+ config);
+
+ gimp_memsize_entry_set_value (entry, value);
+
+ g_signal_handlers_unblock_by_func (entry,
+ gimp_prop_memsize_callback,
+ config);
+ }
+}
+
+
+/***********/
+/* label */
+/***********/
+
+static void gimp_prop_label_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *label);
+
+/**
+ * gimp_prop_label_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of string property.
+ *
+ * Creates a #GtkLabel to display the value of the specified property.
+ * The property should be a string property or at least transformable
+ * to a string. If the user should be able to edit the string, use
+ * gimp_prop_entry_new() instead.
+ *
+ * Return value: A new #GtkLabel widget.
+ *
+ * Since: 2.4
+ */
+GtkWidget *
+gimp_prop_label_new (GObject *config,
+ const gchar *property_name)
+{
+ GParamSpec *param_spec;
+ GtkWidget *label;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (property_name != NULL, NULL);
+
+ param_spec = find_param_spec (config, property_name, G_STRFUNC);
+
+ if (! param_spec)
+ return NULL;
+
+ if (! g_value_type_transformable (param_spec->value_type, G_TYPE_STRING))
+ {
+ g_warning ("%s: property '%s' of %s is not transformable to string",
+ G_STRLOC,
+ param_spec->name,
+ g_type_name (param_spec->owner_type));
+ return NULL;
+ }
+
+ label = gtk_label_new (NULL);
+
+ set_param_spec (G_OBJECT (label), label, param_spec);
+
+ connect_notify (config, property_name,
+ G_CALLBACK (gimp_prop_label_notify),
+ label);
+
+ gimp_prop_label_notify (config, param_spec, label);
+
+ return label;
+}
+
+static void
+gimp_prop_label_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *label)
+{
+ GValue value = G_VALUE_INIT;
+
+ g_value_init (&value, param_spec->value_type);
+
+ g_object_get_property (config, param_spec->name, &value);
+
+ if (G_VALUE_HOLDS_STRING (&value))
+ {
+ const gchar *str = g_value_get_string (&value);
+
+ gtk_label_set_text (GTK_LABEL (label), str ? str : "");
+ }
+ else
+ {
+ GValue str_value = G_VALUE_INIT;
+ const gchar *str;
+
+ g_value_init (&str_value, G_TYPE_STRING);
+ g_value_transform (&value, &str_value);
+
+ str = g_value_get_string (&str_value);
+
+ gtk_label_set_text (GTK_LABEL (label), str ? str : "");
+
+ g_value_unset (&str_value);
+ }
+
+ g_value_unset (&value);
+}
+
+
+/***********/
+/* entry */
+/***********/
+
+static void gimp_prop_entry_callback (GtkWidget *entry,
+ GObject *config);
+static void gimp_prop_entry_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *entry);
+
+/**
+ * gimp_prop_entry_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of string property.
+ * @max_len: Maximum allowed length of string.
+ *
+ * Creates a #GtkEntry to set and display the value of the specified
+ * string property.
+ *
+ * Return value: A new #GtkEntry widget.
+ *
+ * Since: 2.4
+ */
+GtkWidget *
+gimp_prop_entry_new (GObject *config,
+ const gchar *property_name,
+ gint max_len)
+{
+ GParamSpec *param_spec;
+ GtkWidget *entry;
+ gchar *value;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (property_name != NULL, NULL);
+
+ param_spec = check_param_spec (config, property_name,
+ G_TYPE_PARAM_STRING, G_STRFUNC);
+ if (! param_spec)
+ return NULL;
+
+ g_object_get (config,
+ property_name, &value,
+ NULL);
+
+ entry = gtk_entry_new ();
+ gtk_entry_set_text (GTK_ENTRY (entry), value ? value : "");
+
+ g_free (value);
+
+ if (max_len > 0)
+ gtk_entry_set_max_length (GTK_ENTRY (entry), max_len);
+
+ gtk_editable_set_editable (GTK_EDITABLE (entry),
+ param_spec->flags & G_PARAM_WRITABLE);
+
+ set_param_spec (G_OBJECT (entry), entry, param_spec);
+
+ g_signal_connect (entry, "changed",
+ G_CALLBACK (gimp_prop_entry_callback),
+ config);
+
+ connect_notify (config, property_name,
+ G_CALLBACK (gimp_prop_entry_notify),
+ entry);
+
+ return entry;
+}
+
+static void
+gimp_prop_entry_callback (GtkWidget *entry,
+ GObject *config)
+{
+ GParamSpec *param_spec;
+ const gchar *value;
+ gchar *v;
+
+ param_spec = get_param_spec (G_OBJECT (entry));
+ if (! param_spec)
+ return;
+
+ value = gtk_entry_get_text (GTK_ENTRY (entry));
+
+ g_object_get (config, param_spec->name, &v, NULL);
+
+ if (g_strcmp0 (v, value))
+ {
+ g_signal_handlers_block_by_func (config,
+ gimp_prop_entry_notify,
+ entry);
+
+ g_object_set (config, param_spec->name, value, NULL);
+
+ g_signal_handlers_unblock_by_func (config,
+ gimp_prop_entry_notify,
+ entry);
+ }
+
+ g_free (v);
+}
+
+static void
+gimp_prop_entry_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *entry)
+{
+ gchar *value;
+
+ g_object_get (config,
+ param_spec->name, &value,
+ NULL);
+
+ g_signal_handlers_block_by_func (entry,
+ gimp_prop_entry_callback,
+ config);
+
+ gtk_entry_set_text (GTK_ENTRY (entry), value ? value : "");
+
+ g_signal_handlers_unblock_by_func (entry,
+ gimp_prop_entry_callback,
+ config);
+
+ g_free (value);
+}
+
+
+/*****************/
+/* text buffer */
+/*****************/
+
+static void gimp_prop_text_buffer_callback (GtkTextBuffer *text_buffer,
+ GObject *config);
+static void gimp_prop_text_buffer_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkTextBuffer *text_buffer);
+
+/**
+ * gimp_prop_text_buffer_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of string property.
+ * @max_len: Maximum allowed length of text (in characters).
+ *
+ * Creates a #GtkTextBuffer to set and display the value of the
+ * specified string property. Unless the string is expected to
+ * contain multiple lines or a large amount of text, use
+ * gimp_prop_entry_new() instead. See #GtkTextView for information on
+ * how to insert a text buffer into a visible widget.
+ *
+ * If @max_len is 0 or negative, the text buffer allows an unlimited
+ * number of characters to be entered.
+ *
+ * Return value: A new #GtkTextBuffer.
+ *
+ * Since: 2.4
+ */
+GtkTextBuffer *
+gimp_prop_text_buffer_new (GObject *config,
+ const gchar *property_name,
+ gint max_len)
+{
+ GParamSpec *param_spec;
+ GtkTextBuffer *text_buffer;
+ gchar *value;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (property_name != NULL, NULL);
+
+ param_spec = check_param_spec_w (config, property_name,
+ G_TYPE_PARAM_STRING, G_STRFUNC);
+ if (! param_spec)
+ return NULL;
+
+ g_object_get (config,
+ property_name, &value,
+ NULL);
+
+ text_buffer = gtk_text_buffer_new (NULL);
+ gtk_text_buffer_set_text (text_buffer, value ? value : "", -1);
+
+ g_free (value);
+
+ if (max_len > 0)
+ g_object_set_data (G_OBJECT (text_buffer), "max-len",
+ GINT_TO_POINTER (max_len));
+
+ set_param_spec (G_OBJECT (text_buffer), NULL, param_spec);
+
+ g_signal_connect (text_buffer, "changed",
+ G_CALLBACK (gimp_prop_text_buffer_callback),
+ config);
+
+ connect_notify (config, property_name,
+ G_CALLBACK (gimp_prop_text_buffer_notify),
+ text_buffer);
+
+ return text_buffer;
+}
+
+static void
+gimp_prop_text_buffer_callback (GtkTextBuffer *text_buffer,
+ GObject *config)
+{
+ GParamSpec *param_spec;
+ GtkTextIter start_iter;
+ GtkTextIter end_iter;
+ gchar *text;
+ gint max_len;
+
+ param_spec = get_param_spec (G_OBJECT (text_buffer));
+ if (! param_spec)
+ return;
+
+ gtk_text_buffer_get_bounds (text_buffer, &start_iter, &end_iter);
+ text = gtk_text_buffer_get_text (text_buffer, &start_iter, &end_iter, FALSE);
+
+ max_len = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (text_buffer),
+ "max-length"));
+
+ if (max_len > 0 && g_utf8_strlen (text, -1) > max_len)
+ {
+ g_message (dngettext (GETTEXT_PACKAGE "-libgimp",
+ "This text input field is limited to %d character.",
+ "This text input field is limited to %d characters.",
+ max_len), max_len);
+
+ gtk_text_buffer_get_iter_at_offset (text_buffer,
+ &start_iter, max_len - 1);
+ gtk_text_buffer_get_end_iter (text_buffer, &end_iter);
+
+ /* this calls us recursively, but in the else branch */
+ gtk_text_buffer_delete (text_buffer, &start_iter, &end_iter);
+ }
+ else
+ {
+ gchar *v;
+
+ g_object_get (config, param_spec->name, &v, NULL);
+
+ if (g_strcmp0 (v, text))
+ {
+ g_signal_handlers_block_by_func (config,
+ gimp_prop_text_buffer_notify,
+ text_buffer);
+
+ g_object_set (config, param_spec->name, text, NULL);
+
+ g_signal_handlers_unblock_by_func (config,
+ gimp_prop_text_buffer_notify,
+ text_buffer);
+ }
+
+ g_free (v);
+ }
+
+ g_free (text);
+}
+
+static void
+gimp_prop_text_buffer_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkTextBuffer *text_buffer)
+{
+ gchar *value;
+
+ g_object_get (config,
+ param_spec->name, &value,
+ NULL);
+
+ g_signal_handlers_block_by_func (text_buffer,
+ gimp_prop_text_buffer_callback,
+ config);
+
+ gtk_text_buffer_set_text (text_buffer, value ? value : "", -1);
+
+ g_signal_handlers_unblock_by_func (text_buffer,
+ gimp_prop_text_buffer_callback,
+ config);
+
+ g_free (value);
+}
+
+
+/***********************/
+/* string combo box */
+/***********************/
+
+static void gimp_prop_string_combo_box_callback (GtkWidget *widget,
+ GObject *config);
+static void gimp_prop_string_combo_box_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *widget);
+
+/**
+ * gimp_prop_string_combo_box_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of int property controlled by combo box.
+ * @model: #GtkTreeStore holding list of values
+ * @id_column: column in @store that holds string IDs
+ * @label_column: column in @store that holds labels to use in the combo-box
+ *
+ * Creates a #GimpStringComboBox widget to display and set the
+ * specified property. The contents of the widget are determined by
+ * @store.
+ *
+ * Return value: The newly created #GimpStringComboBox widget.
+ *
+ * Since: 2.4
+ */
+GtkWidget *
+gimp_prop_string_combo_box_new (GObject *config,
+ const gchar *property_name,
+ GtkTreeModel *model,
+ gint id_column,
+ gint label_column)
+{
+ GParamSpec *param_spec;
+ GtkWidget *combo_box;
+ gchar *value;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (property_name != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
+
+ param_spec = check_param_spec_w (config, property_name,
+ G_TYPE_PARAM_STRING, G_STRFUNC);
+ if (! param_spec)
+ return NULL;
+
+ g_object_get (config,
+ property_name, &value,
+ NULL);
+
+ combo_box = gimp_string_combo_box_new (model, id_column, label_column);
+
+ gimp_string_combo_box_set_active (GIMP_STRING_COMBO_BOX (combo_box), value);
+
+ g_signal_connect (combo_box, "changed",
+ G_CALLBACK (gimp_prop_string_combo_box_callback),
+ config);
+
+ set_param_spec (G_OBJECT (combo_box), combo_box, param_spec);
+
+ connect_notify (config, property_name,
+ G_CALLBACK (gimp_prop_string_combo_box_notify),
+ combo_box);
+
+ return combo_box;
+}
+
+static void
+gimp_prop_string_combo_box_callback (GtkWidget *widget,
+ GObject *config)
+{
+ GParamSpec *param_spec;
+ gchar *value;
+ gchar *v;
+
+ param_spec = get_param_spec (G_OBJECT (widget));
+ if (! param_spec)
+ return;
+
+ value = gimp_string_combo_box_get_active (GIMP_STRING_COMBO_BOX (widget));
+
+ g_object_get (config, param_spec->name, &v, NULL);
+
+ if (g_strcmp0 (v, value))
+ g_object_set (config, param_spec->name, value, NULL);
+
+ g_free (value);
+ g_free (v);
+}
+
+static void
+gimp_prop_string_combo_box_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *combo_box)
+{
+ gchar *value;
+
+ g_object_get (config,
+ param_spec->name, &value,
+ NULL);
+
+ g_signal_handlers_block_by_func (combo_box,
+ gimp_prop_string_combo_box_callback,
+ config);
+
+ gimp_string_combo_box_set_active (GIMP_STRING_COMBO_BOX (combo_box), value);
+
+ g_signal_handlers_unblock_by_func (combo_box,
+ gimp_prop_string_combo_box_callback,
+ config);
+
+ g_free (value);
+}
+
+
+/*************************/
+/* file chooser button */
+/*************************/
+
+
+static GtkWidget * gimp_prop_file_chooser_button_setup (GtkWidget *button,
+ GObject *config,
+ GParamSpec *param_spec);
+static void gimp_prop_file_chooser_button_callback (GtkFileChooser *button,
+ GObject *config);
+static void gimp_prop_file_chooser_button_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkFileChooser *button);
+
+
+/**
+ * gimp_prop_file_chooser_button_new:
+ * @config: object to which property is attached.
+ * @property_name: name of path property.
+ * @title: the title of the browse dialog.
+ * @action: the open mode for the widget.
+ *
+ * Creates a #GtkFileChooserButton to edit the specified path property.
+ *
+ * Note that #GtkFileChooserButton implements the #GtkFileChooser
+ * interface; you can use the #GtkFileChooser API with it.
+ *
+ * Return value: A new #GtkFileChooserButton.
+ *
+ * Since: 2.4
+ */
+GtkWidget *
+gimp_prop_file_chooser_button_new (GObject *config,
+ const gchar *property_name,
+ const gchar *title,
+ GtkFileChooserAction action)
+{
+ GParamSpec *param_spec;
+ GtkWidget *button;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (property_name != NULL, NULL);
+
+ param_spec = check_param_spec_w (config, property_name,
+ GIMP_TYPE_PARAM_CONFIG_PATH, G_STRFUNC);
+ if (! param_spec)
+ return NULL;
+
+ button = gtk_file_chooser_button_new (title, action);
+
+ return gimp_prop_file_chooser_button_setup (button, config, param_spec);
+}
+
+/**
+ * gimp_prop_file_chooser_button_new_with_dialog:
+ * @config: object to which property is attached.
+ * @property_name: name of path property.
+ * @dialog: the #GtkFileChooserDialog widget to use.
+ *
+ * Creates a #GtkFileChooserButton to edit the specified path property.
+ *
+ * The button uses @dialog as it's file-picking window. Note that @dialog
+ * must be a #GtkFileChooserDialog (or subclass) and must not have
+ * %GTK_DIALOG_DESTROY_WITH_PARENT set.
+ *
+ * Note that #GtkFileChooserButton implements the #GtkFileChooser
+ * interface; you can use the #GtkFileChooser API with it.
+ *
+ * Return value: A new #GtkFileChooserButton.
+ *
+ * Since: 2.4
+ */
+GtkWidget *
+gimp_prop_file_chooser_button_new_with_dialog (GObject *config,
+ const gchar *property_name,
+ GtkWidget *dialog)
+{
+ GParamSpec *param_spec;
+ GtkWidget *button;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (property_name != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_FILE_CHOOSER_DIALOG (dialog), NULL);
+
+ param_spec = check_param_spec_w (config, property_name,
+ GIMP_TYPE_PARAM_CONFIG_PATH, G_STRFUNC);
+ if (! param_spec)
+ return NULL;
+
+ button = gtk_file_chooser_button_new_with_dialog (dialog);
+
+ return gimp_prop_file_chooser_button_setup (button, config, param_spec);
+}
+
+static GtkWidget *
+gimp_prop_file_chooser_button_setup (GtkWidget *button,
+ GObject *config,
+ GParamSpec *param_spec)
+{
+ gchar *value;
+ GFile *file = NULL;
+
+ g_object_get (config,
+ param_spec->name, &value,
+ NULL);
+
+ if (value)
+ {
+ file = gimp_file_new_for_config_path (value, NULL);
+ g_free (value);
+ }
+
+ if (file)
+ {
+ gchar *basename = g_file_get_basename (file);
+
+ if (basename && basename[0] == '.')
+ gtk_file_chooser_set_show_hidden (GTK_FILE_CHOOSER (button), TRUE);
+
+ g_free (basename);
+
+ gtk_file_chooser_set_file (GTK_FILE_CHOOSER (button), file, NULL);
+ g_object_unref (file);
+ }
+
+ set_param_spec (G_OBJECT (button), button, param_spec);
+
+ g_signal_connect (button, "file-set",
+ G_CALLBACK (gimp_prop_file_chooser_button_callback),
+ config);
+
+ connect_notify (config, param_spec->name,
+ G_CALLBACK (gimp_prop_file_chooser_button_notify),
+ button);
+
+ return button;
+}
+
+static void
+gimp_prop_file_chooser_button_callback (GtkFileChooser *button,
+ GObject *config)
+{
+ GParamSpec *param_spec;
+ GFile *file;
+ gchar *value = NULL;
+ gchar *v;
+
+ param_spec = get_param_spec (G_OBJECT (button));
+ if (! param_spec)
+ return;
+
+ file = gtk_file_chooser_get_file (button);
+
+ if (file)
+ {
+ value = gimp_file_get_config_path (file, NULL);
+ g_object_unref (file);
+ }
+
+ g_object_get (config, param_spec->name, &v, NULL);
+
+ if (g_strcmp0 (v, value))
+ {
+ g_signal_handlers_block_by_func (config,
+ gimp_prop_file_chooser_button_notify,
+ button);
+
+ g_object_set (config, param_spec->name, value, NULL);
+
+ g_signal_handlers_unblock_by_func (config,
+ gimp_prop_file_chooser_button_notify,
+ button);
+ }
+
+ g_free (value);
+ g_free (v);
+}
+
+static void
+gimp_prop_file_chooser_button_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkFileChooser *button)
+{
+ gchar *value;
+ GFile *file = NULL;
+
+ g_object_get (config,
+ param_spec->name, &value,
+ NULL);
+
+ if (value)
+ {
+ file = gimp_file_new_for_config_path (value, NULL);
+ g_free (value);
+ }
+
+ g_signal_handlers_block_by_func (button,
+ gimp_prop_file_chooser_button_callback,
+ config);
+
+ if (file)
+ {
+ gtk_file_chooser_set_file (button, file, NULL);
+ g_object_unref (file);
+ }
+ else
+ {
+ gtk_file_chooser_unselect_all (button);
+ }
+
+ g_signal_handlers_unblock_by_func (button,
+ gimp_prop_file_chooser_button_callback,
+ config);
+}
+
+
+/*****************/
+/* path editor */
+/*****************/
+
+static void gimp_prop_path_editor_path_callback (GimpPathEditor *editor,
+ GObject *config);
+static void gimp_prop_path_editor_writable_callback (GimpPathEditor *editor,
+ GObject *config);
+static void gimp_prop_path_editor_path_notify (GObject *config,
+ GParamSpec *param_spec,
+ GimpPathEditor *editor);
+static void gimp_prop_path_editor_writable_notify (GObject *config,
+ GParamSpec *param_spec,
+ GimpPathEditor *editor);
+
+GtkWidget *
+gimp_prop_path_editor_new (GObject *config,
+ const gchar *path_property_name,
+ const gchar *writable_property_name,
+ const gchar *filesel_title)
+{
+ GParamSpec *path_param_spec;
+ GParamSpec *writable_param_spec = NULL;
+ GtkWidget *editor;
+ gchar *value;
+ gchar *filename;
+
+ g_return_val_if_fail (G_IS_OBJECT (config), NULL);
+ g_return_val_if_fail (path_property_name != NULL, NULL);
+
+ path_param_spec = check_param_spec_w (config, path_property_name,
+ GIMP_TYPE_PARAM_CONFIG_PATH, G_STRFUNC);
+ if (! path_param_spec)
+ return NULL;
+
+ if (writable_property_name)
+ {
+ writable_param_spec = check_param_spec_w (config, writable_property_name,
+ GIMP_TYPE_PARAM_CONFIG_PATH,
+ G_STRFUNC);
+ if (! writable_param_spec)
+ return NULL;
+ }
+
+ g_object_get (config,
+ path_property_name, &value,
+ NULL);
+
+ filename = value ? gimp_config_path_expand (value, TRUE, NULL) : NULL;
+ g_free (value);
+
+ editor = gimp_path_editor_new (filesel_title, filename);
+ g_free (filename);
+
+ if (writable_property_name)
+ {
+ g_object_get (config,
+ writable_property_name, &value,
+ NULL);
+
+ filename = value ? gimp_config_path_expand (value, TRUE, NULL) : NULL;
+ g_free (value);
+
+ gimp_path_editor_set_writable_path (GIMP_PATH_EDITOR (editor), filename);
+ g_free (filename);
+ }
+
+ g_object_set_data (G_OBJECT (editor), "gimp-config-param-spec-path",
+ path_param_spec);
+
+ g_signal_connect (editor, "path-changed",
+ G_CALLBACK (gimp_prop_path_editor_path_callback),
+ config);
+
+ connect_notify (config, path_property_name,
+ G_CALLBACK (gimp_prop_path_editor_path_notify),
+ editor);
+
+ if (writable_property_name)
+ {
+ g_object_set_data (G_OBJECT (editor), "gimp-config-param-spec-writable",
+ writable_param_spec);
+
+ g_signal_connect (editor, "writable-changed",
+ G_CALLBACK (gimp_prop_path_editor_writable_callback),
+ config);
+
+ connect_notify (config, writable_property_name,
+ G_CALLBACK (gimp_prop_path_editor_writable_notify),
+ editor);
+ }
+
+ return editor;
+}
+
+static void
+gimp_prop_path_editor_path_callback (GimpPathEditor *editor,
+ GObject *config)
+{
+ GParamSpec *path_param_spec;
+ GParamSpec *writable_param_spec;
+ gchar *value;
+ gchar *utf8;
+
+ path_param_spec = g_object_get_data (G_OBJECT (editor),
+ "gimp-config-param-spec-path");
+ writable_param_spec = g_object_get_data (G_OBJECT (editor),
+ "gimp-config-param-spec-writable");
+ if (! path_param_spec)
+ return;
+
+ value = gimp_path_editor_get_path (editor);
+ utf8 = value ? gimp_config_path_unexpand (value, TRUE, NULL) : NULL;
+ g_free (value);
+
+ g_signal_handlers_block_by_func (config,
+ gimp_prop_path_editor_path_notify,
+ editor);
+
+ g_object_set (config,
+ path_param_spec->name, utf8,
+ NULL);
+
+ g_signal_handlers_unblock_by_func (config,
+ gimp_prop_path_editor_path_notify,
+ editor);
+
+ g_free (utf8);
+
+ if (writable_param_spec)
+ {
+ value = gimp_path_editor_get_writable_path (editor);
+ utf8 = value ? gimp_config_path_unexpand (value, TRUE, NULL) : NULL;
+ g_free (value);
+
+ g_signal_handlers_block_by_func (config,
+ gimp_prop_path_editor_writable_notify,
+ editor);
+
+ g_object_set (config,
+ writable_param_spec->name, utf8,
+ NULL);
+
+ g_signal_handlers_unblock_by_func (config,
+ gimp_prop_path_editor_writable_notify,
+ editor);
+
+ g_free (utf8);
+ }
+}
+
+static void
+gimp_prop_path_editor_writable_callback (GimpPathEditor *editor,
+ GObject *config)
+{
+ GParamSpec *param_spec;
+ gchar *value;
+ gchar *utf8;
+
+ param_spec = g_object_get_data (G_OBJECT (editor),
+ "gimp-config-param-spec-writable");
+ if (! param_spec)
+ return;
+
+ value = gimp_path_editor_get_writable_path (editor);
+ utf8 = value ? gimp_config_path_unexpand (value, TRUE, NULL) : NULL;
+ g_free (value);
+
+ g_signal_handlers_block_by_func (config,
+ gimp_prop_path_editor_writable_notify,
+ editor);
+
+ g_object_set (config,
+ param_spec->name, utf8,
+ NULL);
+
+ g_signal_handlers_unblock_by_func (config,
+ gimp_prop_path_editor_writable_notify,
+ editor);
+
+ g_free (utf8);
+}
+
+static void
+gimp_prop_path_editor_path_notify (GObject *config,
+ GParamSpec *param_spec,
+ GimpPathEditor *editor)
+{
+ gchar *value;
+ gchar *filename;
+
+ g_object_get (config,
+ param_spec->name, &value,
+ NULL);
+
+ filename = value ? gimp_config_path_expand (value, TRUE, NULL) : NULL;
+ g_free (value);
+
+ g_signal_handlers_block_by_func (editor,
+ gimp_prop_path_editor_path_callback,
+ config);
+
+ gimp_path_editor_set_path (editor, filename);
+
+ g_signal_handlers_unblock_by_func (editor,
+ gimp_prop_path_editor_path_callback,
+ config);
+
+ g_free (filename);
+}
+
+static void
+gimp_prop_path_editor_writable_notify (GObject *config,
+ GParamSpec *param_spec,
+ GimpPathEditor *editor)
+{
+ gchar *value;
+ gchar *filename;
+
+ g_object_get (config,
+ param_spec->name, &value,
+ NULL);
+
+ filename = value ? gimp_config_path_expand (value, TRUE, NULL) : NULL;
+ g_free (value);
+
+ g_signal_handlers_block_by_func (editor,
+ gimp_prop_path_editor_writable_callback,
+ config);
+
+ gimp_path_editor_set_writable_path (editor, filename);
+
+ g_signal_handlers_unblock_by_func (editor,
+ gimp_prop_path_editor_writable_callback,
+ config);
+
+ g_free (filename);
+}
+
+
+/***************/
+/* sizeentry */
+/***************/
+
+static void gimp_prop_size_entry_callback (GimpSizeEntry *entry,
+ GObject *config);
+static void gimp_prop_size_entry_notify (GObject *config,
+ GParamSpec *param_spec,
+ GimpSizeEntry *entry);
+static void gimp_prop_size_entry_notify_unit (GObject *config,
+ GParamSpec *param_spec,
+ GimpSizeEntry *entry);
+static gint gimp_prop_size_entry_num_chars (gdouble lower,
+ gdouble upper);
+
+
+/**
+ * gimp_prop_size_entry_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of int or double property.
+ * @property_is_pixel: When %TRUE, the property value is in pixels,
+ * and in the selected unit otherwise.
+ * @unit_property_name: Name of unit property.
+ * @unit_format: A printf-like unit-format string as is used with
+ * gimp_unit_menu_new().
+ * @update_policy: How the automatic pixel <-> real-world-unit
+ * calculations should be done.
+ * @resolution: The resolution (in dpi) for the field.
+ *
+ * Creates a #GimpSizeEntry to set and display the specified double or
+ * int property, and its associated unit property. Note that this
+ * function is only suitable for creating a size entry holding a
+ * single value. Use gimp_prop_coordinates_new() to create a size
+ * entry holding two values.
+ *
+ * Return value: A new #GimpSizeEntry widget.
+ *
+ * Since: 2.4
+ */
+GtkWidget *
+gimp_prop_size_entry_new (GObject *config,
+ const gchar *property_name,
+ gboolean property_is_pixel,
+ const gchar *unit_property_name,
+ const gchar *unit_format,
+ GimpSizeEntryUpdatePolicy update_policy,
+ gdouble resolution)
+{
+ GtkWidget *entry;
+ GParamSpec *param_spec;
+ GParamSpec *unit_param_spec;
+ gboolean show_pixels;
+ gboolean show_percent;
+ gdouble value;
+ gdouble lower;
+ gdouble upper;
+ GimpUnit unit_value;
+
+ param_spec = find_param_spec (config, property_name, G_STRFUNC);
+ if (! param_spec)
+ return NULL;
+
+ if (! get_numeric_values (config,
+ param_spec, &value, &lower, &upper, G_STRFUNC))
+ return NULL;
+
+ if (unit_property_name)
+ {
+ GValue value = G_VALUE_INIT;
+
+ unit_param_spec = check_param_spec_w (config, unit_property_name,
+ GIMP_TYPE_PARAM_UNIT, G_STRFUNC);
+ if (! unit_param_spec)
+ return NULL;
+
+ g_value_init (&value, unit_param_spec->value_type);
+
+ g_value_set_int (&value, GIMP_UNIT_PIXEL);
+ show_pixels = (g_param_value_validate (unit_param_spec,
+ &value) == FALSE);
+
+ g_value_set_int (&value, GIMP_UNIT_PERCENT);
+ show_percent = (g_param_value_validate (unit_param_spec,
+ &value) == FALSE);
+
+ g_value_unset (&value);
+
+ g_object_get (config,
+ unit_property_name, &unit_value,
+ NULL);
+ }
+ else
+ {
+ unit_param_spec = NULL;
+ unit_value = GIMP_UNIT_INCH;
+ show_pixels = FALSE;
+ show_percent = FALSE;
+ }
+
+ entry = gimp_size_entry_new (1, unit_value, unit_format,
+ show_pixels, show_percent, FALSE,
+ gimp_prop_size_entry_num_chars (lower, upper) + 1 +
+ gimp_unit_get_scaled_digits (unit_value, resolution),
+ update_policy);
+ gtk_table_set_col_spacing (GTK_TABLE (entry), 1, 2);
+
+ set_param_spec (NULL,
+ gimp_size_entry_get_help_widget (GIMP_SIZE_ENTRY (entry), 0),
+ param_spec);
+
+ if (unit_param_spec)
+ set_param_spec (NULL, GIMP_SIZE_ENTRY (entry)->unitmenu, unit_param_spec);
+
+ gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (entry), unit_value);
+
+ if (update_policy == GIMP_SIZE_ENTRY_UPDATE_SIZE)
+ gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (entry), 0,
+ resolution, FALSE);
+
+ gimp_size_entry_set_value_boundaries (GIMP_SIZE_ENTRY (entry), 0,
+ lower, upper);
+
+ g_object_set_data (G_OBJECT (entry), "value-is-pixel",
+ GINT_TO_POINTER (property_is_pixel ? TRUE : FALSE));
+
+ if (property_is_pixel)
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (entry), 0, value);
+ else
+ gimp_size_entry_set_value (GIMP_SIZE_ENTRY (entry), 0, value);
+
+ g_object_set_data (G_OBJECT (entry), "gimp-config-param-spec",
+ param_spec);
+
+ g_signal_connect (entry, "refval-changed",
+ G_CALLBACK (gimp_prop_size_entry_callback),
+ config);
+ g_signal_connect (entry, "value-changed",
+ G_CALLBACK (gimp_prop_size_entry_callback),
+ config);
+
+ connect_notify (config, property_name,
+ G_CALLBACK (gimp_prop_size_entry_notify),
+ entry);
+
+ if (unit_property_name)
+ {
+ g_object_set_data (G_OBJECT (entry), "gimp-config-param-spec-unit",
+ unit_param_spec);
+
+ g_signal_connect (entry, "unit-changed",
+ G_CALLBACK (gimp_prop_size_entry_callback),
+ config);
+
+ connect_notify (config, unit_property_name,
+ G_CALLBACK (gimp_prop_size_entry_notify_unit),
+ entry);
+ }
+
+ return entry;
+}
+
+static void
+gimp_prop_size_entry_callback (GimpSizeEntry *entry,
+ GObject *config)
+{
+ GParamSpec *param_spec;
+ GParamSpec *unit_param_spec;
+ gdouble value;
+ gboolean value_is_pixel;
+ GimpUnit unit_value;
+
+ param_spec = g_object_get_data (G_OBJECT (entry), "gimp-config-param-spec");
+ if (! param_spec)
+ return;
+
+ unit_param_spec = g_object_get_data (G_OBJECT (entry),
+ "gimp-config-param-spec-unit");
+
+ value_is_pixel = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (entry),
+ "value-is-pixel"));
+
+ if (value_is_pixel)
+ value = gimp_size_entry_get_refval (entry, 0);
+ else
+ value = gimp_size_entry_get_value (entry, 0);
+
+ unit_value = gimp_size_entry_get_unit (entry);
+
+ if (unit_param_spec)
+ {
+ GimpUnit old_unit;
+
+ g_object_get (config,
+ unit_param_spec->name, &old_unit,
+ NULL);
+
+ if (unit_value == old_unit)
+ unit_param_spec = NULL;
+ }
+
+ if (G_IS_PARAM_SPEC_INT (param_spec))
+ {
+ g_object_set (config,
+ param_spec->name, ROUND (value),
+
+ unit_param_spec ?
+ unit_param_spec->name : NULL, unit_value,
+
+ NULL);
+ }
+ else if (G_IS_PARAM_SPEC_DOUBLE (param_spec))
+ {
+ g_object_set (config,
+ param_spec->name, value,
+
+ unit_param_spec ?
+ unit_param_spec->name : NULL, unit_value,
+
+ NULL);
+ }
+}
+
+static void
+gimp_prop_size_entry_notify (GObject *config,
+ GParamSpec *param_spec,
+ GimpSizeEntry *entry)
+{
+ gdouble value;
+ gdouble entry_value;
+ gboolean value_is_pixel;
+
+ if (G_IS_PARAM_SPEC_INT (param_spec))
+ {
+ gint int_value;
+
+ g_object_get (config,
+ param_spec->name, &int_value,
+ NULL);
+
+ value = int_value;
+ }
+ else
+ {
+ g_object_get (config,
+ param_spec->name, &value,
+ NULL);
+ }
+
+ value_is_pixel = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (entry),
+ "value-is-pixel"));
+
+ if (value_is_pixel)
+ entry_value = gimp_size_entry_get_refval (entry, 0);
+ else
+ entry_value = gimp_size_entry_get_value (entry, 0);
+
+ if (value != entry_value)
+ {
+ g_signal_handlers_block_by_func (entry,
+ gimp_prop_size_entry_callback,
+ config);
+
+ if (value_is_pixel)
+ gimp_size_entry_set_refval (entry, 0, value);
+ else
+ gimp_size_entry_set_value (entry, 0, value);
+
+ g_signal_handlers_unblock_by_func (entry,
+ gimp_prop_size_entry_callback,
+ config);
+ }
+}
+
+static void
+gimp_prop_size_entry_notify_unit (GObject *config,
+ GParamSpec *param_spec,
+ GimpSizeEntry *entry)
+{
+ GimpUnit value;
+
+ g_object_get (config,
+ param_spec->name, &value,
+ NULL);
+
+ if (value != gimp_size_entry_get_unit (entry))
+ {
+ g_signal_handlers_block_by_func (entry,
+ gimp_prop_size_entry_callback,
+ config);
+
+ gimp_size_entry_set_unit (entry, value);
+
+ g_signal_handlers_unblock_by_func (entry,
+ gimp_prop_size_entry_callback,
+ config);
+ }
+}
+
+static gint
+gimp_prop_size_entry_num_chars (gdouble lower,
+ gdouble upper)
+{
+ gint lower_chars = log (fabs (lower)) / log (10);
+ gint upper_chars = log (fabs (upper)) / log (10);
+
+ if (lower < 0.0)
+ lower_chars++;
+
+ if (upper < 0.0)
+ upper_chars++;
+
+ return MAX (lower_chars, upper_chars);
+}
+
+
+/*****************/
+/* coordinates */
+/*****************/
+
+static void gimp_prop_coordinates_callback (GimpSizeEntry *entry,
+ GObject *config);
+static void gimp_prop_coordinates_notify_x (GObject *config,
+ GParamSpec *param_spec,
+ GimpSizeEntry *entry);
+static void gimp_prop_coordinates_notify_y (GObject *config,
+ GParamSpec *param_spec,
+ GimpSizeEntry *entry);
+static void gimp_prop_coordinates_notify_unit (GObject *config,
+ GParamSpec *param_spec,
+ GimpSizeEntry *entry);
+
+
+/**
+ * gimp_prop_coordinates_new:
+ * @config: Object to which property is attached.
+ * @x_property_name: Name of int or double property for X coordinate.
+ * @y_property_name: Name of int or double property for Y coordinate.
+ * @unit_property_name: Name of unit property.
+ * @unit_format: A printf-like unit-format string as is used with
+ * gimp_unit_menu_new().
+ * @update_policy: How the automatic pixel <-> real-world-unit
+ * calculations should be done.
+ * @xresolution: The resolution (in dpi) for the X coordinate.
+ * @yresolution: The resolution (in dpi) for the Y coordinate.
+ * @has_chainbutton: Whether to add a chainbutton to the size entry.
+ *
+ * Creates a #GimpSizeEntry to set and display two double or int
+ * properties, which will usually represent X and Y coordinates, and
+ * their associated unit property.
+ *
+ * Return value: A new #GimpSizeEntry widget.
+ *
+ * Since: 2.4
+ */
+GtkWidget *
+gimp_prop_coordinates_new (GObject *config,
+ const gchar *x_property_name,
+ const gchar *y_property_name,
+ const gchar *unit_property_name,
+ const gchar *unit_format,
+ GimpSizeEntryUpdatePolicy update_policy,
+ gdouble xresolution,
+ gdouble yresolution,
+ gboolean has_chainbutton)
+{
+ GtkWidget *entry;
+ GtkWidget *chainbutton = NULL;
+
+ entry = gimp_size_entry_new (2, GIMP_UNIT_INCH, unit_format,
+ FALSE, FALSE, TRUE, 10,
+ update_policy);
+
+ if (has_chainbutton)
+ {
+ chainbutton = gimp_chain_button_new (GIMP_CHAIN_BOTTOM);
+ gtk_table_attach_defaults (GTK_TABLE (entry), chainbutton, 1, 3, 3, 4);
+ gtk_widget_show (chainbutton);
+ }
+
+ if (! gimp_prop_coordinates_connect (config,
+ x_property_name,
+ y_property_name,
+ unit_property_name,
+ entry,
+ chainbutton,
+ xresolution,
+ yresolution))
+ {
+ gtk_widget_destroy (entry);
+ return NULL;
+ }
+
+ return entry;
+}
+
+gboolean
+gimp_prop_coordinates_connect (GObject *config,
+ const gchar *x_property_name,
+ const gchar *y_property_name,
+ const gchar *unit_property_name,
+ GtkWidget *entry,
+ GtkWidget *chainbutton,
+ gdouble xresolution,
+ gdouble yresolution)
+{
+ GParamSpec *x_param_spec;
+ GParamSpec *y_param_spec;
+ GParamSpec *unit_param_spec;
+ gdouble x_value, x_lower, x_upper;
+ gdouble y_value, y_lower, y_upper;
+ GimpUnit unit_value;
+ gdouble *old_x_value;
+ gdouble *old_y_value;
+ GimpUnit *old_unit_value;
+ gboolean chain_checked;
+
+ g_return_val_if_fail (GIMP_IS_SIZE_ENTRY (entry), FALSE);
+ g_return_val_if_fail (GIMP_SIZE_ENTRY (entry)->number_of_fields == 2, FALSE);
+ g_return_val_if_fail (chainbutton == NULL ||
+ GIMP_IS_CHAIN_BUTTON (chainbutton), FALSE);
+
+ x_param_spec = find_param_spec (config, x_property_name, G_STRFUNC);
+ if (! x_param_spec)
+ return FALSE;
+
+ y_param_spec = find_param_spec (config, y_property_name, G_STRFUNC);
+ if (! y_param_spec)
+ return FALSE;
+
+ if (! get_numeric_values (config, x_param_spec,
+ &x_value, &x_lower, &x_upper, G_STRFUNC) ||
+ ! get_numeric_values (config, y_param_spec,
+ &y_value, &y_lower, &y_upper, G_STRFUNC))
+ return FALSE;
+
+ if (unit_property_name)
+ {
+ unit_param_spec = check_param_spec_w (config, unit_property_name,
+ GIMP_TYPE_PARAM_UNIT, G_STRFUNC);
+ if (! unit_param_spec)
+ return FALSE;
+
+ g_object_get (config,
+ unit_property_name, &unit_value,
+ NULL);
+ }
+ else
+ {
+ unit_param_spec = NULL;
+ unit_value = GIMP_UNIT_INCH;
+ }
+
+ set_param_spec (NULL,
+ gimp_size_entry_get_help_widget (GIMP_SIZE_ENTRY (entry), 0),
+ x_param_spec);
+ set_param_spec (NULL,
+ gimp_size_entry_get_help_widget (GIMP_SIZE_ENTRY (entry), 1),
+ y_param_spec);
+
+ if (unit_param_spec)
+ set_param_spec (NULL,
+ GIMP_SIZE_ENTRY (entry)->unitmenu, unit_param_spec);
+
+ gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (entry), unit_value);
+
+ switch (GIMP_SIZE_ENTRY (entry)->update_policy)
+ {
+ case GIMP_SIZE_ENTRY_UPDATE_SIZE:
+ gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (entry), 0,
+ xresolution, FALSE);
+ gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (entry), 1,
+ yresolution, FALSE);
+ chain_checked = (ABS (x_value - y_value) < 1);
+ break;
+
+ case GIMP_SIZE_ENTRY_UPDATE_RESOLUTION:
+ chain_checked = (ABS (x_value - y_value) < GIMP_MIN_RESOLUTION);
+ break;
+
+ default:
+ chain_checked = (x_value == y_value);
+ break;
+ }
+
+ gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (entry), 0,
+ x_lower, x_upper);
+ gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (entry), 1,
+ y_lower, y_upper);
+
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (entry), 0, x_value);
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (entry), 1, y_value);
+
+ g_object_set_data (G_OBJECT (entry), "gimp-config-param-spec-x",
+ x_param_spec);
+ g_object_set_data (G_OBJECT (entry), "gimp-config-param-spec-y",
+ y_param_spec);
+
+ old_x_value = g_new0 (gdouble, 1);
+ *old_x_value = x_value;
+ g_object_set_data_full (G_OBJECT (entry), "old-x-value",
+ old_x_value,
+ (GDestroyNotify) g_free);
+
+ old_y_value = g_new0 (gdouble, 1);
+ *old_y_value = y_value;
+ g_object_set_data_full (G_OBJECT (entry), "old-y-value",
+ old_y_value,
+ (GDestroyNotify) g_free);
+
+ if (chainbutton)
+ {
+ if (chain_checked)
+ gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (chainbutton), TRUE);
+
+ g_object_set_data (G_OBJECT (entry), "chainbutton", chainbutton);
+ }
+
+ g_signal_connect (entry, "value-changed",
+ G_CALLBACK (gimp_prop_coordinates_callback),
+ config);
+ g_signal_connect (entry, "refval-changed",
+ G_CALLBACK (gimp_prop_coordinates_callback),
+ config);
+
+ connect_notify (config, x_property_name,
+ G_CALLBACK (gimp_prop_coordinates_notify_x),
+ entry);
+ connect_notify (config, y_property_name,
+ G_CALLBACK (gimp_prop_coordinates_notify_y),
+ entry);
+
+ if (unit_property_name)
+ {
+ g_object_set_data (G_OBJECT (entry), "gimp-config-param-spec-unit",
+ unit_param_spec);
+
+ old_unit_value = g_new0 (GimpUnit, 1);
+ *old_unit_value = unit_value;
+ g_object_set_data_full (G_OBJECT (entry), "old-unit-value",
+ old_unit_value,
+ (GDestroyNotify) g_free);
+
+ g_signal_connect (entry, "unit-changed",
+ G_CALLBACK (gimp_prop_coordinates_callback),
+ config);
+
+ connect_notify (config, unit_property_name,
+ G_CALLBACK (gimp_prop_coordinates_notify_unit),
+ entry);
+ }
+
+ return TRUE;
+}
+
+static void
+gimp_prop_coordinates_callback (GimpSizeEntry *entry,
+ GObject *config)
+{
+ GParamSpec *x_param_spec;
+ GParamSpec *y_param_spec;
+ GParamSpec *unit_param_spec;
+ gdouble x_value;
+ gdouble y_value;
+ GimpUnit unit_value;
+ gdouble *old_x_value;
+ gdouble *old_y_value;
+ GimpUnit *old_unit_value;
+ gboolean backwards;
+
+ x_param_spec = g_object_get_data (G_OBJECT (entry),
+ "gimp-config-param-spec-x");
+ y_param_spec = g_object_get_data (G_OBJECT (entry),
+ "gimp-config-param-spec-y");
+
+ if (! x_param_spec || ! y_param_spec)
+ return;
+
+ unit_param_spec = g_object_get_data (G_OBJECT (entry),
+ "gimp-config-param-spec-unit");
+
+ x_value = gimp_size_entry_get_refval (entry, 0);
+ y_value = gimp_size_entry_get_refval (entry, 1);
+ unit_value = gimp_size_entry_get_unit (entry);
+
+ old_x_value = g_object_get_data (G_OBJECT (entry), "old-x-value");
+ old_y_value = g_object_get_data (G_OBJECT (entry), "old-y-value");
+ old_unit_value = g_object_get_data (G_OBJECT (entry), "old-unit-value");
+
+ if (! old_x_value || ! old_y_value || (unit_param_spec && ! old_unit_value))
+ return;
+
+ /*
+ * FIXME: if the entry was created using gimp_coordinates_new, then
+ * the chain button is handled automatically and the following block
+ * of code is unnecessary (and, in fact, redundant).
+ */
+ if (x_value != y_value)
+ {
+ GtkWidget *chainbutton;
+
+ chainbutton = g_object_get_data (G_OBJECT (entry), "chainbutton");
+
+ if (chainbutton &&
+ gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (chainbutton)) &&
+ ! g_object_get_data (G_OBJECT (chainbutton), "constrains-ratio"))
+ {
+ if (x_value != *old_x_value)
+ y_value = x_value;
+ else if (y_value != *old_y_value)
+ x_value = y_value;
+ }
+ }
+
+ backwards = (*old_x_value == x_value);
+
+ if (*old_x_value == x_value &&
+ *old_y_value == y_value &&
+ (old_unit_value == NULL || *old_unit_value == unit_value))
+ return;
+
+ *old_x_value = x_value;
+ *old_y_value = y_value;
+
+ if (old_unit_value)
+ *old_unit_value = unit_value;
+
+ if (unit_param_spec)
+ g_object_set (config,
+ unit_param_spec->name, unit_value,
+ NULL);
+
+ if (G_IS_PARAM_SPEC_INT (x_param_spec) &&
+ G_IS_PARAM_SPEC_INT (y_param_spec))
+ {
+ if (backwards)
+ g_object_set (config,
+ y_param_spec->name, ROUND (y_value),
+ x_param_spec->name, ROUND (x_value),
+ NULL);
+ else
+ g_object_set (config,
+ x_param_spec->name, ROUND (x_value),
+ y_param_spec->name, ROUND (y_value),
+ NULL);
+
+ }
+ else if (G_IS_PARAM_SPEC_DOUBLE (x_param_spec) &&
+ G_IS_PARAM_SPEC_DOUBLE (y_param_spec))
+ {
+ if (backwards)
+ g_object_set (config,
+ y_param_spec->name, y_value,
+ x_param_spec->name, x_value,
+ NULL);
+ else
+ g_object_set (config,
+ x_param_spec->name, x_value,
+ y_param_spec->name, y_value,
+ NULL);
+ }
+}
+
+static void
+gimp_prop_coordinates_notify_x (GObject *config,
+ GParamSpec *param_spec,
+ GimpSizeEntry *entry)
+{
+ gdouble value;
+
+ if (G_IS_PARAM_SPEC_INT (param_spec))
+ {
+ gint int_value;
+
+ g_object_get (config,
+ param_spec->name, &int_value,
+ NULL);
+
+ value = int_value;
+ }
+ else
+ {
+ g_object_get (config,
+ param_spec->name, &value,
+ NULL);
+ }
+
+ if (value != gimp_size_entry_get_refval (entry, 0))
+ {
+ gdouble *old_x_value = g_object_get_data (G_OBJECT (entry),
+ "old-x-value");
+
+ g_signal_handlers_block_by_func (entry,
+ gimp_prop_coordinates_callback,
+ config);
+
+ gimp_size_entry_set_refval (entry, 0, value);
+
+ if (old_x_value)
+ *old_x_value = value;
+
+ g_signal_emit_by_name (entry, "value-changed",
+ gimp_size_entry_get_value (entry, 0));
+
+ g_signal_handlers_unblock_by_func (entry,
+ gimp_prop_coordinates_callback,
+ config);
+ }
+}
+
+static void
+gimp_prop_coordinates_notify_y (GObject *config,
+ GParamSpec *param_spec,
+ GimpSizeEntry *entry)
+{
+ gdouble value;
+
+ if (G_IS_PARAM_SPEC_INT (param_spec))
+ {
+ gint int_value;
+
+ g_object_get (config,
+ param_spec->name, &int_value,
+ NULL);
+
+ value = int_value;
+ }
+ else
+ {
+ g_object_get (config,
+ param_spec->name, &value,
+ NULL);
+ }
+
+ if (value != gimp_size_entry_get_refval (entry, 1))
+ {
+ gdouble *old_y_value = g_object_get_data (G_OBJECT (entry),
+ "old-y-value");
+
+ g_signal_handlers_block_by_func (entry,
+ gimp_prop_coordinates_callback,
+ config);
+
+ gimp_size_entry_set_refval (entry, 1, value);
+
+ if (old_y_value)
+ *old_y_value = value;
+
+ g_signal_emit_by_name (entry, "value-changed",
+ gimp_size_entry_get_value (entry, 1));
+
+ g_signal_handlers_unblock_by_func (entry,
+ gimp_prop_coordinates_callback,
+ config);
+ }
+}
+
+static void
+gimp_prop_coordinates_notify_unit (GObject *config,
+ GParamSpec *param_spec,
+ GimpSizeEntry *entry)
+{
+ GimpUnit value;
+
+ g_object_get (config,
+ param_spec->name, &value,
+ NULL);
+
+ if (value != gimp_size_entry_get_unit (entry))
+ {
+ g_signal_handlers_block_by_func (entry,
+ gimp_prop_coordinates_callback,
+ config);
+
+ gimp_size_entry_set_unit (entry, value);
+
+ g_signal_handlers_unblock_by_func (entry,
+ gimp_prop_coordinates_callback,
+ config);
+ }
+}
+
+
+/****************/
+/* color area */
+/****************/
+
+static void gimp_prop_color_area_callback (GtkWidget *widget,
+ GObject *config);
+static void gimp_prop_color_area_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *area);
+
+/**
+ * gimp_prop_color_area_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of RGB property.
+ * @width: Width of color area.
+ * @height: Height of color area.
+ * @type: How transparency is represented.
+ *
+ * Creates a #GimpColorArea to set and display the value of an RGB
+ * property.
+ *
+ * Return value: A new #GimpColorArea widget.
+ *
+ * Since: 2.4
+ */
+GtkWidget *
+gimp_prop_color_area_new (GObject *config,
+ const gchar *property_name,
+ gint width,
+ gint height,
+ GimpColorAreaType type)
+{
+ GParamSpec *param_spec;
+ GtkWidget *area;
+ GimpRGB *value;
+
+ param_spec = check_param_spec_w (config, property_name,
+ GIMP_TYPE_PARAM_RGB, G_STRFUNC);
+ if (! param_spec)
+ return NULL;
+
+ g_object_get (config,
+ property_name, &value,
+ NULL);
+
+ area = gimp_color_area_new (value, type,
+ GDK_BUTTON1_MASK | GDK_BUTTON2_MASK);
+ gtk_widget_set_size_request (area, width, height);
+
+ g_free (value);
+
+ set_param_spec (G_OBJECT (area), area, param_spec);
+
+ g_signal_connect (area, "color-changed",
+ G_CALLBACK (gimp_prop_color_area_callback),
+ config);
+
+ connect_notify (config, property_name,
+ G_CALLBACK (gimp_prop_color_area_notify),
+ area);
+
+ return area;
+}
+
+static void
+gimp_prop_color_area_callback (GtkWidget *area,
+ GObject *config)
+{
+ GParamSpec *param_spec;
+ GimpRGB value;
+
+ param_spec = get_param_spec (G_OBJECT (area));
+ if (! param_spec)
+ return;
+
+ gimp_color_area_get_color (GIMP_COLOR_AREA (area), &value);
+
+ g_signal_handlers_block_by_func (config,
+ gimp_prop_color_area_notify,
+ area);
+
+ g_object_set (config,
+ param_spec->name, &value,
+ NULL);
+
+ g_signal_handlers_unblock_by_func (config,
+ gimp_prop_color_area_notify,
+ area);
+}
+
+static void
+gimp_prop_color_area_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *area)
+{
+ GimpRGB *value;
+
+ g_object_get (config,
+ param_spec->name, &value,
+ NULL);
+
+ g_signal_handlers_block_by_func (area,
+ gimp_prop_color_area_callback,
+ config);
+
+ gimp_color_area_set_color (GIMP_COLOR_AREA (area), value);
+
+ g_free (value);
+
+ g_signal_handlers_unblock_by_func (area,
+ gimp_prop_color_area_callback,
+ config);
+}
+
+
+/********************/
+/* unit combo box */
+/********************/
+
+static void gimp_prop_unit_combo_box_callback (GtkWidget *combo,
+ GObject *config);
+static void gimp_prop_unit_combo_box_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *combo);
+
+/**
+ * gimp_prop_unit_combo_box_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of Unit property.
+ *
+ * Creates a #GimpUnitComboBox to set and display the value of a Unit
+ * property. See gimp_unit_combo_box_new() for more information.
+ *
+ * Return value: A new #GimpUnitComboBox widget.
+ *
+ * Since: 2.8
+ */
+GtkWidget *
+gimp_prop_unit_combo_box_new (GObject *config,
+ const gchar *property_name)
+{
+ GParamSpec *param_spec;
+ GtkWidget *combo;
+ GtkTreeModel *model;
+ GimpUnit unit;
+ GValue value = G_VALUE_INIT;
+ gboolean show_pixels;
+ gboolean show_percent;
+
+ param_spec = check_param_spec_w (config, property_name,
+ GIMP_TYPE_PARAM_UNIT, G_STRFUNC);
+ if (! param_spec)
+ return NULL;
+
+ g_value_init (&value, param_spec->value_type);
+
+ g_value_set_int (&value, GIMP_UNIT_PIXEL);
+ show_pixels = (g_param_value_validate (param_spec, &value) == FALSE);
+
+ g_value_set_int (&value, GIMP_UNIT_PERCENT);
+ show_percent = (g_param_value_validate (param_spec, &value) == FALSE);
+
+ g_value_unset (&value);
+
+ g_object_get (config,
+ property_name, &unit,
+ NULL);
+
+ combo = gimp_unit_combo_box_new ();
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
+ gimp_unit_store_set_has_pixels (GIMP_UNIT_STORE (model), show_pixels);
+ gimp_unit_store_set_has_percent (GIMP_UNIT_STORE (model), show_percent);
+
+ gimp_unit_combo_box_set_active (GIMP_UNIT_COMBO_BOX (combo), unit);
+
+ set_param_spec (G_OBJECT (combo), combo, param_spec);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (gimp_prop_unit_combo_box_callback),
+ config);
+
+ connect_notify (config, property_name,
+ G_CALLBACK (gimp_prop_unit_combo_box_notify),
+ combo);
+
+ return combo;
+}
+
+static void
+gimp_prop_unit_combo_box_callback (GtkWidget *combo,
+ GObject *config)
+{
+ GParamSpec *param_spec;
+ GimpUnit value;
+ GimpUnit v;
+
+ param_spec = get_param_spec (G_OBJECT (combo));
+ if (! param_spec)
+ return;
+
+ value = gimp_unit_combo_box_get_active (GIMP_UNIT_COMBO_BOX (combo));
+
+ g_object_get (config, param_spec->name, &v, NULL);
+
+ if (v != value)
+ {
+ /* FIXME gimp_unit_menu_update (menu, &unit); */
+
+ g_signal_handlers_block_by_func (config,
+ gimp_prop_unit_combo_box_notify,
+ combo);
+
+ g_object_set (config, param_spec->name, value, NULL);
+
+ g_signal_handlers_unblock_by_func (config,
+ gimp_prop_unit_combo_box_notify,
+ combo);
+ }
+}
+
+static void
+gimp_prop_unit_combo_box_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *combo)
+{
+ GimpUnit unit;
+
+ g_object_get (config,
+ param_spec->name, &unit,
+ NULL);
+
+ g_signal_handlers_block_by_func (combo,
+ gimp_prop_unit_combo_box_callback,
+ config);
+
+ gimp_unit_combo_box_set_active (GIMP_UNIT_COMBO_BOX (combo), unit);
+
+ /* FIXME gimp_unit_menu_update (menu, &unit); */
+
+ g_signal_handlers_unblock_by_func (combo,
+ gimp_prop_unit_combo_box_callback,
+ config);
+}
+
+
+/***************/
+/* unit menu */
+/***************/
+
+static void gimp_prop_unit_menu_callback (GtkWidget *menu,
+ GObject *config);
+static void gimp_prop_unit_menu_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *menu);
+
+/**
+ * gimp_prop_unit_menu_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of Unit property.
+ * @unit_format: A printf-like format string which is used to create
+ * the unit strings.
+ *
+ * Creates a #GimpUnitMenu to set and display the value of a Unit
+ * property. See gimp_unit_menu_new() for more information.
+ *
+ * Return value: A new #GimpUnitMenu widget.
+ *
+ * Since: 2.4
+ *
+ * Deprecated: 2.10
+ */
+GtkWidget *
+gimp_prop_unit_menu_new (GObject *config,
+ const gchar *property_name,
+ const gchar *unit_format)
+{
+ GParamSpec *param_spec;
+ GtkWidget *menu;
+ GimpUnit unit;
+ GValue value = G_VALUE_INIT;
+ gboolean show_pixels;
+ gboolean show_percent;
+
+ param_spec = check_param_spec_w (config, property_name,
+ GIMP_TYPE_PARAM_UNIT, G_STRFUNC);
+ if (! param_spec)
+ return NULL;
+
+ g_value_init (&value, param_spec->value_type);
+
+ g_value_set_int (&value, GIMP_UNIT_PIXEL);
+ show_pixels = (g_param_value_validate (param_spec, &value) == FALSE);
+
+ g_value_set_int (&value, GIMP_UNIT_PERCENT);
+ show_percent = (g_param_value_validate (param_spec, &value) == FALSE);
+
+ g_value_unset (&value);
+
+ g_object_get (config,
+ property_name, &unit,
+ NULL);
+
+ menu = gimp_unit_menu_new (unit_format,
+ unit, show_pixels, show_percent, TRUE);
+
+ set_param_spec (G_OBJECT (menu), menu, param_spec);
+
+ g_signal_connect (menu, "unit-changed",
+ G_CALLBACK (gimp_prop_unit_menu_callback),
+ config);
+
+ connect_notify (config, property_name,
+ G_CALLBACK (gimp_prop_unit_menu_notify),
+ menu);
+
+ return menu;
+}
+
+static void
+gimp_prop_unit_menu_callback (GtkWidget *menu,
+ GObject *config)
+{
+ GParamSpec *param_spec;
+ GimpUnit unit;
+
+ param_spec = get_param_spec (G_OBJECT (menu));
+ if (! param_spec)
+ return;
+
+ gimp_unit_menu_update (menu, &unit);
+
+ g_signal_handlers_block_by_func (config,
+ gimp_prop_unit_menu_notify,
+ menu);
+
+ g_object_set (config,
+ param_spec->name, unit,
+ NULL);
+
+ g_signal_handlers_unblock_by_func (config,
+ gimp_prop_unit_menu_notify,
+ menu);
+}
+
+static void
+gimp_prop_unit_menu_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *menu)
+{
+ GimpUnit unit;
+
+ g_object_get (config,
+ param_spec->name, &unit,
+ NULL);
+
+ g_signal_handlers_block_by_func (menu,
+ gimp_prop_unit_menu_callback,
+ config);
+
+ gimp_unit_menu_set_unit (GIMP_UNIT_MENU (menu), unit);
+ gimp_unit_menu_update (menu, &unit);
+
+ g_signal_handlers_unblock_by_func (menu,
+ gimp_prop_unit_menu_callback,
+ config);
+}
+
+
+/***************/
+/* icon name */
+/***************/
+
+static void gimp_prop_icon_image_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *image);
+
+/**
+ * gimp_prop_stock_image_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of string property.
+ * @icon_size: Size of desired stock image.
+ *
+ * Creates a widget to display a stock image representing the value of the
+ * specified string property, which should encode a Stock ID.
+ * See gtk_image_new_from_stock() for more information.
+ *
+ * Return value: A new #GtkImage widget.
+ *
+ * Since: 2.4
+ *
+ * Deprecated: 2.10
+ */
+GtkWidget *
+gimp_prop_stock_image_new (GObject *config,
+ const gchar *property_name,
+ GtkIconSize icon_size)
+{
+ return gimp_prop_icon_image_new (config, property_name, icon_size);
+}
+
+/**
+ * gimp_prop_icon_image_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of string property.
+ * @icon_size: Size of desired icon image.
+ *
+ * Creates a widget to display a icon image representing the value of the
+ * specified string property, which should encode an icon name.
+ * See gtk_image_new_from_icon_name() for more information.
+ *
+ * Return value: A new #GtkImage widget.
+ *
+ * Since: 2.10
+ */
+GtkWidget *
+gimp_prop_icon_image_new (GObject *config,
+ const gchar *property_name,
+ GtkIconSize icon_size)
+{
+ GParamSpec *param_spec;
+ GtkWidget *image;
+ gchar *icon_name;
+
+ param_spec = check_param_spec (config, property_name,
+ G_TYPE_PARAM_STRING, G_STRFUNC);
+ if (! param_spec)
+ return NULL;
+
+ g_object_get (config,
+ property_name, &icon_name,
+ NULL);
+
+ image = gtk_image_new_from_icon_name (icon_name, icon_size);
+
+ if (icon_name)
+ g_free (icon_name);
+
+ set_param_spec (G_OBJECT (image), image, param_spec);
+
+ connect_notify (config, property_name,
+ G_CALLBACK (gimp_prop_icon_image_notify),
+ image);
+
+ return image;
+}
+
+static void
+gimp_prop_icon_image_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkWidget *image)
+{
+ gchar *icon_name;
+ GtkIconSize icon_size;
+
+ g_object_get (config,
+ param_spec->name, &icon_name,
+ NULL);
+
+ gtk_image_get_icon_name (GTK_IMAGE (image), NULL, &icon_size);
+ gtk_image_set_from_icon_name (GTK_IMAGE (image), icon_name, icon_size);
+
+ if (icon_name)
+ g_free (icon_name);
+}
+
+
+/**************/
+/* expander */
+/**************/
+
+static void gimp_prop_expanded_notify (GtkExpander *expander,
+ GParamSpec *param_spec,
+ GObject *config);
+static void gimp_prop_expander_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkExpander *expander);
+
+
+/**
+ * gimp_prop_expander_new:
+ * @config: Object to which property is attached.
+ * @property_name: Name of boolean property.
+ * @label: Label for expander.
+ *
+ * Creates a #GtkExpander controlled by the specified boolean property.
+ * A value of %TRUE for the property corresponds to the expanded state
+ * for the widget.
+ * If @label is #NULL, the @property_name's nick will be used as label
+ * of the returned widget.
+ *
+ * Return value: A new #GtkExpander widget.
+ *
+ * Since: 2.4
+ */
+GtkWidget *
+gimp_prop_expander_new (GObject *config,
+ const gchar *property_name,
+ const gchar *label)
+{
+ GParamSpec *param_spec;
+ GtkWidget *expander;
+ gboolean value;
+
+ param_spec = check_param_spec_w (config, property_name,
+ G_TYPE_PARAM_BOOLEAN, G_STRFUNC);
+ if (! param_spec)
+ return NULL;
+
+ if (! label)
+ label = g_param_spec_get_nick (param_spec);
+
+ g_object_get (config,
+ property_name, &value,
+ NULL);
+
+ expander = g_object_new (GTK_TYPE_EXPANDER,
+ "label", label,
+ "expanded", value,
+ NULL);
+
+ set_param_spec (G_OBJECT (expander), expander, param_spec);
+
+ g_signal_connect (expander, "notify::expanded",
+ G_CALLBACK (gimp_prop_expanded_notify),
+ config);
+
+ connect_notify (config, property_name,
+ G_CALLBACK (gimp_prop_expander_notify),
+ expander);
+
+ return expander;
+}
+
+static void
+gimp_prop_expanded_notify (GtkExpander *expander,
+ GParamSpec *param_spec,
+ GObject *config)
+{
+ param_spec = get_param_spec (G_OBJECT (expander));
+ if (! param_spec)
+ return;
+
+ g_object_set (config,
+ param_spec->name, gtk_expander_get_expanded (expander),
+ NULL);
+}
+
+static void
+gimp_prop_expander_notify (GObject *config,
+ GParamSpec *param_spec,
+ GtkExpander *expander)
+{
+ gboolean value;
+
+ g_object_get (config,
+ param_spec->name, &value,
+ NULL);
+
+ if (gtk_expander_get_expanded (expander) != value)
+ {
+ g_signal_handlers_block_by_func (expander,
+ gimp_prop_expanded_notify,
+ config);
+
+ gtk_expander_set_expanded (expander, value);
+
+ g_signal_handlers_unblock_by_func (expander,
+ gimp_prop_expanded_notify,
+ config);
+ }
+}
+
+
+/*******************************/
+/* private utility functions */
+/*******************************/
+
+static GQuark gimp_prop_widgets_param_spec_quark (void) G_GNUC_CONST;
+
+#define PARAM_SPEC_QUARK (gimp_prop_widgets_param_spec_quark ())
+
+static GQuark
+gimp_prop_widgets_param_spec_quark (void)
+{
+ static GQuark param_spec_quark = 0;
+
+ if (! param_spec_quark)
+ param_spec_quark = g_quark_from_static_string ("gimp-config-param-spec");
+
+ return param_spec_quark;
+}
+
+static void
+set_param_spec (GObject *object,
+ GtkWidget *widget,
+ GParamSpec *param_spec)
+{
+ if (object)
+ {
+ g_object_set_qdata (object, PARAM_SPEC_QUARK, param_spec);
+ }
+
+ if (widget)
+ {
+ const gchar *blurb = g_param_spec_get_blurb (param_spec);
+
+ if (blurb)
+ gimp_help_set_help_data (widget, blurb, NULL);
+ }
+}
+
+static void
+set_radio_spec (GObject *object,
+ GParamSpec *param_spec)
+{
+ GSList *list;
+
+ for (list = gtk_radio_button_get_group (GTK_RADIO_BUTTON (object));
+ list;
+ list = g_slist_next (list))
+ {
+ set_param_spec (list->data, NULL, param_spec);
+ }
+}
+
+static GParamSpec *
+get_param_spec (GObject *object)
+{
+ return g_object_get_qdata (object, PARAM_SPEC_QUARK);
+}
+
+static GParamSpec *
+find_param_spec (GObject *object,
+ const gchar *property_name,
+ const gchar *strloc)
+{
+ GParamSpec *param_spec;
+
+ param_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (object),
+ property_name);
+
+ if (! param_spec)
+ g_warning ("%s: %s has no property named '%s'",
+ strloc,
+ g_type_name (G_TYPE_FROM_INSTANCE (object)),
+ property_name);
+
+ return param_spec;
+}
+
+static GParamSpec *
+check_param_spec (GObject *object,
+ const gchar *property_name,
+ GType type,
+ const gchar *strloc)
+{
+ GParamSpec *param_spec;
+
+ param_spec = find_param_spec (object, property_name, strloc);
+
+ if (param_spec && ! g_type_is_a (G_TYPE_FROM_INSTANCE (param_spec), type))
+ {
+ g_warning ("%s: property '%s' of %s is not a %s",
+ strloc,
+ param_spec->name,
+ g_type_name (param_spec->owner_type),
+ g_type_name (type));
+ return NULL;
+ }
+
+ return param_spec;
+}
+
+static GParamSpec *
+check_param_spec_w (GObject *object,
+ const gchar *property_name,
+ GType type,
+ const gchar *strloc)
+{
+ GParamSpec *param_spec;
+
+ param_spec = check_param_spec (object, property_name, type, strloc);
+
+ if (param_spec &&
+ (param_spec->flags & G_PARAM_WRITABLE) == 0)
+ {
+ g_warning ("%s: property '%s' of %s is not writable",
+ strloc,
+ param_spec->name,
+ g_type_name (param_spec->owner_type));
+ return NULL;
+ }
+
+ return param_spec;
+}
+
+static gboolean
+get_numeric_values (GObject *object,
+ GParamSpec *param_spec,
+ gdouble *value,
+ gdouble *lower,
+ gdouble *upper,
+ const gchar *strloc)
+{
+ if (G_IS_PARAM_SPEC_INT (param_spec))
+ {
+ GParamSpecInt *int_spec = G_PARAM_SPEC_INT (param_spec);
+ gint int_value;
+
+ g_object_get (object, param_spec->name, &int_value, NULL);
+
+ *value = int_value;
+ *lower = int_spec->minimum;
+ *upper = int_spec->maximum;
+ }
+ else if (G_IS_PARAM_SPEC_UINT (param_spec))
+ {
+ GParamSpecUInt *uint_spec = G_PARAM_SPEC_UINT (param_spec);
+ guint uint_value;
+
+ g_object_get (object, param_spec->name, &uint_value, NULL);
+
+ *value = uint_value;
+ *lower = uint_spec->minimum;
+ *upper = uint_spec->maximum;
+ }
+ else if (G_IS_PARAM_SPEC_DOUBLE (param_spec))
+ {
+ GParamSpecDouble *double_spec = G_PARAM_SPEC_DOUBLE (param_spec);
+
+ g_object_get (object, param_spec->name, value, NULL);
+
+ *lower = double_spec->minimum;
+ *upper = double_spec->maximum;
+ }
+ else
+ {
+ g_warning ("%s: property '%s' of %s is not numeric",
+ strloc,
+ param_spec->name,
+ g_type_name (G_TYPE_FROM_INSTANCE (object)));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+connect_notify (GObject *config,
+ const gchar *property_name,
+ GCallback callback,
+ gpointer callback_data)
+{
+ gchar *notify_name;
+
+ notify_name = g_strconcat ("notify::", property_name, NULL);
+
+ g_signal_connect_object (config, notify_name, callback, callback_data, 0);
+
+ g_free (notify_name);
+}
diff --git a/libgimpwidgets/gimppropwidgets.h b/libgimpwidgets/gimppropwidgets.h
new file mode 100644
index 0000000..c8702a5
--- /dev/null
+++ b/libgimpwidgets/gimppropwidgets.h
@@ -0,0 +1,243 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimppropwidgets.h
+ * Copyright (C) 2002 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PROP_WIDGETS_H__
+#define __GIMP_PROP_WIDGETS_H__
+
+G_BEGIN_DECLS
+
+
+/* GParamBoolean */
+
+GtkWidget * gimp_prop_check_button_new (GObject *config,
+ const gchar *property_name,
+ const gchar *label);
+GtkWidget * gimp_prop_boolean_combo_box_new (GObject *config,
+ const gchar *property_name,
+ const gchar *true_text,
+ const gchar *false_text);
+GtkWidget * gimp_prop_boolean_radio_frame_new (GObject *config,
+ const gchar *property_name,
+ const gchar *title,
+ const gchar *true_text,
+ const gchar *false_text);
+
+GtkWidget * gimp_prop_expander_new (GObject *config,
+ const gchar *property_name,
+ const gchar *label);
+
+
+/* GParamInt */
+
+GtkWidget * gimp_prop_int_combo_box_new (GObject *config,
+ const gchar *property_name,
+ GimpIntStore *store);
+
+/* GParamGType */
+
+GtkWidget * gimp_prop_pointer_combo_box_new (GObject *config,
+ const gchar *property_name,
+ GimpIntStore *store);
+
+/* GParamEnum */
+
+GtkWidget * gimp_prop_enum_combo_box_new (GObject *config,
+ const gchar *property_name,
+ gint minimum,
+ gint maximum);
+
+GtkWidget * gimp_prop_enum_check_button_new (GObject *config,
+ const gchar *property_name,
+ const gchar *label,
+ gint false_value,
+ gint true_value);
+
+GtkWidget * gimp_prop_enum_radio_frame_new (GObject *config,
+ const gchar *property_name,
+ const gchar *title,
+ gint minimum,
+ gint maximum);
+GtkWidget * gimp_prop_enum_radio_box_new (GObject *config,
+ const gchar *property_name,
+ gint minimum,
+ gint maximum);
+
+GIMP_DEPRECATED_FOR(gimp_prop_enum_icon_box_new)
+GtkWidget * gimp_prop_enum_stock_box_new (GObject *config,
+ const gchar *property_name,
+ const gchar *stock_prefix,
+ gint minimum,
+ gint maximum);
+
+GtkWidget * gimp_prop_enum_icon_box_new (GObject *config,
+ const gchar *property_name,
+ const gchar *icon_prefix,
+ gint minimum,
+ gint maximum);
+
+GtkWidget * gimp_prop_enum_label_new (GObject *config,
+ const gchar *property_name);
+
+
+/* GParamInt, GParamUInt, GParamLong, GParamULong, GParamDouble */
+
+GtkWidget * gimp_prop_spin_button_new (GObject *config,
+ const gchar *property_name,
+ gdouble step_increment,
+ gdouble page_increment,
+ gint digits);
+
+GtkWidget * gimp_prop_hscale_new (GObject *config,
+ const gchar *property_name,
+ gdouble step_increment,
+ gdouble page_increment,
+ gint digits);
+
+GtkObject * gimp_prop_scale_entry_new (GObject *config,
+ const gchar *property_name,
+ GtkTable *table,
+ gint column,
+ gint row,
+ const gchar *label,
+ gdouble step_increment,
+ gdouble page_increment,
+ gint digits,
+ gboolean limit_scale,
+ gdouble lower_limit,
+ gdouble upper_limit);
+
+/* special form of gimp_prop_scale_entry_new() for GParamDouble */
+
+GtkObject * gimp_prop_opacity_entry_new (GObject *config,
+ const gchar *property_name,
+ GtkTable *table,
+ gint column,
+ gint row,
+ const gchar *label);
+
+
+/* GimpParamMemsize */
+
+GtkWidget * gimp_prop_memsize_entry_new (GObject *config,
+ const gchar *property_name);
+
+
+/* GParamString */
+
+GtkWidget * gimp_prop_label_new (GObject *config,
+ const gchar *property_name);
+GtkWidget * gimp_prop_entry_new (GObject *config,
+ const gchar *property_name,
+ gint max_len);
+GtkTextBuffer * gimp_prop_text_buffer_new (GObject *config,
+ const gchar *property_name,
+ gint max_len);
+GtkWidget * gimp_prop_string_combo_box_new (GObject *config,
+ const gchar *property_name,
+ GtkTreeModel *model,
+ gint id_column,
+ gint label_column);
+
+
+/* GimpParamPath */
+
+GtkWidget * gimp_prop_file_chooser_button_new (GObject *config,
+ const gchar *property_name,
+ const gchar *title,
+ GtkFileChooserAction action);
+GtkWidget * gimp_prop_file_chooser_button_new_with_dialog (GObject *config,
+ const gchar *property_name,
+
+ GtkWidget *dialog);
+GtkWidget * gimp_prop_path_editor_new (GObject *config,
+ const gchar *path_property_name,
+ const gchar *writable_property_name,
+ const gchar *filesel_title);
+
+
+/* GParamInt, GParamUInt, GParamDouble unit: GimpParamUnit */
+
+GtkWidget * gimp_prop_size_entry_new (GObject *config,
+ const gchar *property_name,
+ gboolean property_is_pixel,
+ const gchar *unit_property_name,
+ const gchar *unit_format,
+ GimpSizeEntryUpdatePolicy update_policy,
+ gdouble resolution);
+
+
+/* x,y: GParamInt, GParamDouble unit: GimpParamUnit */
+
+GtkWidget * gimp_prop_coordinates_new (GObject *config,
+ const gchar *x_property_name,
+ const gchar *y_property_name,
+ const gchar *unit_property_name,
+ const gchar *unit_format,
+ GimpSizeEntryUpdatePolicy update_policy,
+ gdouble xresolution,
+ gdouble yresolution,
+ gboolean has_chainbutton);
+gboolean gimp_prop_coordinates_connect (GObject *config,
+ const gchar *x_property_name,
+ const gchar *y_property_name,
+ const gchar *unit_property_name,
+ GtkWidget *sizeentry,
+ GtkWidget *chainbutton,
+ gdouble xresolution,
+ gdouble yresolution);
+
+
+/* GimpParamColor */
+
+GtkWidget * gimp_prop_color_area_new (GObject *config,
+ const gchar *property_name,
+ gint width,
+ gint height,
+ GimpColorAreaType type);
+
+/* GimpParamUnit */
+
+GtkWidget * gimp_prop_unit_combo_box_new (GObject *config,
+ const gchar *property_name);
+GIMP_DEPRECATED_FOR(gimp_prop_unit_combo_box_new)
+GtkWidget * gimp_prop_unit_menu_new (GObject *config,
+ const gchar *property_name,
+ const gchar *unit_format);
+
+
+/* GParamString (icon name) */
+
+GIMP_DEPRECATED_FOR(gimp_prop_stock_image_new)
+GtkWidget * gimp_prop_stock_image_new (GObject *config,
+ const gchar *property_name,
+ GtkIconSize icon_size);
+GtkWidget * gimp_prop_icon_image_new (GObject *config,
+ const gchar *property_name,
+ GtkIconSize icon_size);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_PROP_WIDGETS_H__ */
diff --git a/libgimpwidgets/gimpquerybox.c b/libgimpwidgets/gimpquerybox.c
new file mode 100644
index 0000000..f26e609
--- /dev/null
+++ b/libgimpwidgets/gimpquerybox.c
@@ -0,0 +1,699 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpquerybox.c
+ * Copyright (C) 1999-2000 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpdialog.h"
+#include "gimppixmap.h"
+#include "gimpquerybox.h"
+#include "gimpsizeentry.h"
+#include "gimpspinbutton.h"
+#include "gimpwidgets.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpquerybox
+ * @title: GimpQueryBox
+ * @short_description: Some simple dialogs to enter a single int,
+ * double, string or boolean value.
+ * @see_also: #GimpSizeEntry, #GimpUnitMenu
+ *
+ * These functions provide simple dialogs for entering a single
+ * string, integer, double, boolean or pixel size value.
+ *
+ * They return a pointer to a #GtkDialog which has to be shown with
+ * gtk_widget_show() by the caller.
+ *
+ * The dialogs contain an entry widget for the kind of value they ask
+ * for and "OK" and "Cancel" buttons. On "Cancel", all query boxes
+ * except the boolean one silently destroy themselves. On "OK" the
+ * user defined callback function is called and returns the entered
+ * value.
+ **/
+
+
+/*
+ * String, integer, double and size query boxes
+ */
+
+typedef struct _QueryBox QueryBox;
+
+struct _QueryBox
+{
+ GtkWidget *qbox;
+ GtkWidget *vbox;
+ GtkWidget *entry;
+ GObject *object;
+ gulong response_handler;
+ GCallback callback;
+ gpointer callback_data;
+};
+
+
+static QueryBox * create_query_box (const gchar *title,
+ GtkWidget *parent,
+ GimpHelpFunc help_func,
+ const gchar *help_id,
+ GCallback response_callback,
+ const gchar *icon_name,
+ const gchar *message,
+ const gchar *ok_button,
+ const gchar *cancel_button,
+ GObject *object,
+ const gchar *signal,
+ GCallback callback,
+ gpointer callback_data);
+
+static void query_box_disconnect (QueryBox *query_box);
+static void query_box_destroy (QueryBox *query_box);
+
+static void string_query_box_response (GtkWidget *widget,
+ gint response_id,
+ QueryBox *query_box);
+static void int_query_box_response (GtkWidget *widget,
+ gint response_id,
+ QueryBox *query_box);
+static void double_query_box_response (GtkWidget *widget,
+ gint response_id,
+ QueryBox *query_box);
+static void size_query_box_response (GtkWidget *widget,
+ gint response_id,
+ QueryBox *query_box);
+static void boolean_query_box_response (GtkWidget *widget,
+ gint response_id,
+ QueryBox *query_box);
+
+static void query_box_cancel_callback (QueryBox *query_box);
+
+
+/*
+ * create a generic query box without any entry widget
+ */
+static QueryBox *
+create_query_box (const gchar *title,
+ GtkWidget *parent,
+ GimpHelpFunc help_func,
+ const gchar *help_id,
+ GCallback response_callback,
+ const gchar *icon_name,
+ const gchar *message,
+ const gchar *ok_button,
+ const gchar *cancel_button,
+ GObject *object,
+ const gchar *signal,
+ GCallback callback,
+ gpointer callback_data)
+{
+ QueryBox *query_box;
+ GtkWidget *hbox = NULL;
+ GtkWidget *label;
+
+ /* make sure the object / signal passed are valid
+ */
+ g_return_val_if_fail (parent == NULL || GTK_IS_WIDGET (parent), NULL);
+ g_return_val_if_fail (object == NULL || G_IS_OBJECT (object), NULL);
+ g_return_val_if_fail (object == NULL || signal != NULL, NULL);
+
+ query_box = g_slice_new0 (QueryBox);
+
+ query_box->qbox = gimp_dialog_new (title, "gimp-query-box",
+ parent, 0,
+ help_func, help_id,
+
+ cancel_button, GTK_RESPONSE_CANCEL,
+ ok_button, GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (query_box->qbox),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ query_box->response_handler =
+ g_signal_connect (query_box->qbox, "response",
+ G_CALLBACK (response_callback),
+ query_box);
+
+ g_signal_connect (query_box->qbox, "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &query_box->qbox);
+
+ /* if we are associated with an object, connect to the provided signal
+ */
+ if (object)
+ {
+ GClosure *closure;
+
+ closure = g_cclosure_new_swap (G_CALLBACK (query_box_cancel_callback),
+ query_box, NULL);
+ g_object_watch_closure (G_OBJECT (query_box->qbox), closure);
+
+ g_signal_connect_closure (object, signal, closure, FALSE);
+ }
+
+ if (icon_name)
+ {
+ GtkWidget *content_area;
+ GtkWidget *image;
+
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (query_box->qbox));
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
+ gtk_box_pack_start (GTK_BOX (content_area), hbox, TRUE, TRUE, 0);
+ gtk_widget_show (hbox);
+
+ image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_DIALOG);
+ gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
+ gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
+ gtk_widget_show (image);
+ }
+
+ query_box->vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+
+ g_object_set_data (G_OBJECT (query_box->qbox), "gimp-query-box-vbox",
+ query_box->vbox);
+
+ if (hbox)
+ {
+ gtk_box_pack_start (GTK_BOX (hbox), query_box->vbox, FALSE, FALSE, 0);
+ }
+ else
+ {
+ GtkWidget *content_area;
+
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (query_box->qbox));
+
+ gtk_container_set_border_width (GTK_CONTAINER (query_box->vbox), 12);
+ gtk_box_pack_start (GTK_BOX (content_area), query_box->vbox,
+ TRUE, TRUE, 0);
+ }
+
+ gtk_widget_show (query_box->vbox);
+
+ if (message)
+ {
+ label = gtk_label_new (message);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_box_pack_start (GTK_BOX (query_box->vbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+ }
+
+ query_box->entry = NULL;
+ query_box->object = object;
+ query_box->callback = callback;
+ query_box->callback_data = callback_data;
+
+ return query_box;
+}
+
+/**
+ * gimp_query_string_box:
+ * @title: The query box dialog's title.
+ * @parent: The dialog's parent widget.
+ * @help_func: The help function to show this dialog's help page.
+ * @help_id: A string identifying this dialog's help page.
+ * @message: A string which will be shown above the dialog's entry widget.
+ * @initial: The initial value.
+ * @object: The object this query box is associated with.
+ * @signal: The object's signal which will cause the query box to be closed.
+ * @callback: The function which will be called when the user selects "OK".
+ * @data: The callback's user data.
+ *
+ * Creates a new #GtkDialog that queries the user for a string value.
+ *
+ * Returns: A pointer to the new #GtkDialog.
+ **/
+GtkWidget *
+gimp_query_string_box (const gchar *title,
+ GtkWidget *parent,
+ GimpHelpFunc help_func,
+ const gchar *help_id,
+ const gchar *message,
+ const gchar *initial,
+ GObject *object,
+ const gchar *signal,
+ GimpQueryStringCallback callback,
+ gpointer data)
+{
+ QueryBox *query_box;
+ GtkWidget *entry;
+
+ query_box = create_query_box (title, parent, help_func, help_id,
+ G_CALLBACK (string_query_box_response),
+ "dialog-question",
+ message,
+ _("_OK"), _("_Cancel"),
+ object, signal,
+ G_CALLBACK (callback), data);
+
+ if (! query_box)
+ return NULL;
+
+ entry = gtk_entry_new ();
+ gtk_entry_set_text (GTK_ENTRY (entry), initial ? initial : "");
+ gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
+ gtk_box_pack_start (GTK_BOX (query_box->vbox), entry, FALSE, FALSE, 0);
+ gtk_widget_grab_focus (entry);
+ gtk_widget_show (entry);
+
+ query_box->entry = entry;
+
+ return query_box->qbox;
+}
+
+/**
+ * gimp_query_int_box:
+ * @title: The query box dialog's title.
+ * @parent: The dialog's parent widget.
+ * @help_func: The help function to show this dialog's help page.
+ * @help_id: A string identifying this dialog's help page.
+ * @message: A string which will be shown above the dialog's entry widget.
+ * @initial: The initial value.
+ * @lower: The lower boundary of the range of possible values.
+ * @upper: The upper boundray of the range of possible values.
+ * @object: The object this query box is associated with.
+ * @signal: The object's signal which will cause the query box to be closed.
+ * @callback: The function which will be called when the user selects "OK".
+ * @data: The callback's user data.
+ *
+ * Creates a new #GtkDialog that queries the user for an integer value.
+ *
+ * Returns: A pointer to the new #GtkDialog.
+ **/
+GtkWidget *
+gimp_query_int_box (const gchar *title,
+ GtkWidget *parent,
+ GimpHelpFunc help_func,
+ const gchar *help_id,
+ const gchar *message,
+ gint initial,
+ gint lower,
+ gint upper,
+ GObject *object,
+ const gchar *signal,
+ GimpQueryIntCallback callback,
+ gpointer data)
+{
+ QueryBox *query_box;
+ GtkWidget *spinbutton;
+ GtkAdjustment *adjustment;
+
+ query_box = create_query_box (title, parent, help_func, help_id,
+ G_CALLBACK (int_query_box_response),
+ "dialog-question",
+ message,
+ _("_OK"), _("_Cancel"),
+ object, signal,
+ G_CALLBACK (callback), data);
+
+ if (! query_box)
+ return NULL;
+
+ adjustment = (GtkAdjustment *)
+ gtk_adjustment_new (initial, lower, upper, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adjustment, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_entry_set_activates_default (GTK_ENTRY (spinbutton), TRUE);
+ gtk_box_pack_start (GTK_BOX (query_box->vbox), spinbutton, FALSE, FALSE, 0);
+ gtk_widget_grab_focus (spinbutton);
+ gtk_widget_show (spinbutton);
+
+ query_box->entry = spinbutton;
+
+ return query_box->qbox;
+}
+
+/**
+ * gimp_query_double_box:
+ * @title: The query box dialog's title.
+ * @parent: The dialog's parent widget.
+ * @help_func: The help function to show this dialog's help page.
+ * @help_id: A string identifying this dialog's help page.
+ * @message: A string which will be shown above the dialog's entry widget.
+ * @initial: The initial value.
+ * @lower: The lower boundary of the range of possible values.
+ * @upper: The upper boundray of the range of possible values.
+ * @digits: The number of decimal digits the #GtkSpinButton will provide.
+ * @object: The object this query box is associated with.
+ * @signal: The object's signal which will cause the query box to be closed.
+ * @callback: The function which will be called when the user selects "OK".
+ * @data: The callback's user data.
+ *
+ * Creates a new #GtkDialog that queries the user for a double value.
+ *
+ * Returns: A pointer to the new #GtkDialog.
+ **/
+GtkWidget *
+gimp_query_double_box (const gchar *title,
+ GtkWidget *parent,
+ GimpHelpFunc help_func,
+ const gchar *help_id,
+ const gchar *message,
+ gdouble initial,
+ gdouble lower,
+ gdouble upper,
+ gint digits,
+ GObject *object,
+ const gchar *signal,
+ GimpQueryDoubleCallback callback,
+ gpointer data)
+{
+ QueryBox *query_box;
+ GtkWidget *spinbutton;
+ GtkAdjustment *adjustment;
+
+ query_box = create_query_box (title, parent, help_func, help_id,
+ G_CALLBACK (double_query_box_response),
+ "dialog-question",
+ message,
+ _("_OK"), _("_Cancel"),
+ object, signal,
+ G_CALLBACK (callback), data);
+
+ if (! query_box)
+ return NULL;
+
+ adjustment = (GtkAdjustment *)
+ gtk_adjustment_new (initial, lower, upper, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adjustment, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_entry_set_activates_default (GTK_ENTRY (spinbutton), TRUE);
+ gtk_box_pack_start (GTK_BOX (query_box->vbox), spinbutton, FALSE, FALSE, 0);
+ gtk_widget_grab_focus (spinbutton);
+ gtk_widget_show (spinbutton);
+
+ query_box->entry = spinbutton;
+
+ return query_box->qbox;
+}
+
+/**
+ * gimp_query_size_box:
+ * @title: The query box dialog's title.
+ * @parent: The dialog's parent widget.
+ * @help_func: The help function to show this dialog's help page.
+ * @help_id: A string identifying this dialog's help page.
+ * @message: A string which will be shown above the dialog's entry widget.
+ * @initial: The initial value.
+ * @lower: The lower boundary of the range of possible values.
+ * @upper: The upper boundray of the range of possible values.
+ * @digits: The number of decimal digits the #GimpSizeEntry provide in
+ * "pixel" mode.
+ * @unit: The unit initially shown by the #GimpUnitMenu.
+ * @resolution: The resolution (in dpi) which will be used for pixel/unit
+ * calculations.
+ * @dot_for_dot: %TRUE if the #GimpUnitMenu's initial unit should be "pixels".
+ * @object: The object this query box is associated with.
+ * @signal: The object's signal which will cause the query box
+ * to be closed.
+ * @callback: The function which will be called when the user selects "OK".
+ * @data: The callback's user data.
+ *
+ * Creates a new #GtkDialog that queries the user for a size using a
+ * #GimpSizeEntry.
+ *
+ * Returns: A pointer to the new #GtkDialog.
+ **/
+GtkWidget *
+gimp_query_size_box (const gchar *title,
+ GtkWidget *parent,
+ GimpHelpFunc help_func,
+ const gchar *help_id,
+ const gchar *message,
+ gdouble initial,
+ gdouble lower,
+ gdouble upper,
+ gint digits,
+ GimpUnit unit,
+ gdouble resolution,
+ gboolean dot_for_dot,
+ GObject *object,
+ const gchar *signal,
+ GimpQuerySizeCallback callback,
+ gpointer data)
+{
+ QueryBox *query_box;
+ GtkWidget *sizeentry;
+ GtkWidget *spinbutton;
+
+ query_box = create_query_box (title, parent, help_func, help_id,
+ G_CALLBACK (size_query_box_response),
+ "dialog-question",
+ message,
+ _("_OK"), _("_Cancel"),
+ object, signal,
+ G_CALLBACK (callback), data);
+
+ if (! query_box)
+ return NULL;
+
+ sizeentry = gimp_size_entry_new (1, unit, "%p", TRUE, FALSE, FALSE, 12,
+ GIMP_SIZE_ENTRY_UPDATE_SIZE);
+ if (dot_for_dot)
+ gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (sizeentry), GIMP_UNIT_PIXEL);
+ gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (sizeentry), 0,
+ resolution, FALSE);
+ gimp_size_entry_set_refval_digits (GIMP_SIZE_ENTRY (sizeentry), 0, digits);
+ gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (sizeentry), 0,
+ lower, upper);
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (sizeentry), 0, initial);
+
+ spinbutton = gimp_size_entry_get_help_widget (GIMP_SIZE_ENTRY (sizeentry), 0);
+ gtk_entry_set_activates_default (GTK_ENTRY (spinbutton), TRUE);
+
+ gtk_box_pack_start (GTK_BOX (query_box->vbox), sizeentry, FALSE, FALSE, 0);
+ gimp_size_entry_grab_focus (GIMP_SIZE_ENTRY (sizeentry));
+ gtk_widget_show (sizeentry);
+
+ query_box->entry = sizeentry;
+
+ return query_box->qbox;
+}
+
+/**
+ * gimp_query_boolean_box:
+ * @title: The query box dialog's title.
+ * @parent: The dialog's parent widget.
+ * @help_func: The help function to show this dialog's help page.
+ * @help_id: A string identifying this dialog's help page.
+ * @icon_name: An icon name to specify an icon to appear on the left
+ * on the dialog's message.
+ * @message: A string which will be shown in the query box.
+ * @true_button: The string to be shown in the dialog's left button.
+ * @false_button: The string to be shown in the dialog's right button.
+ * @object: The object this query box is associated with.
+ * @signal: The object's signal which will cause the query box
+ * to be closed.
+ * @callback: The function which will be called when the user clicks one
+ * of the buttons.
+ * @data: The callback's user data.
+ *
+ * Creates a new #GtkDialog that asks the user to do a boolean decision.
+ *
+ * Returns: A pointer to the new #GtkDialog.
+ **/
+GtkWidget *
+gimp_query_boolean_box (const gchar *title,
+ GtkWidget *parent,
+ GimpHelpFunc help_func,
+ const gchar *help_id,
+ const gchar *icon_name,
+ const gchar *message,
+ const gchar *true_button,
+ const gchar *false_button,
+ GObject *object,
+ const gchar *signal,
+ GimpQueryBooleanCallback callback,
+ gpointer data)
+{
+ QueryBox *query_box;
+
+ query_box = create_query_box (title, parent, help_func, help_id,
+ G_CALLBACK (boolean_query_box_response),
+ icon_name,
+ message,
+ true_button, false_button,
+ object, signal,
+ G_CALLBACK (callback), data);
+
+ if (! query_box)
+ return NULL;
+
+ return query_box->qbox;
+}
+
+
+/*
+ * private functions
+ */
+
+static void
+query_box_disconnect (QueryBox *query_box)
+{
+ gtk_widget_set_sensitive (query_box->qbox, FALSE);
+
+ /* disconnect the response callback to avoid that it may be run twice */
+ if (query_box->response_handler)
+ {
+ g_signal_handler_disconnect (query_box->qbox,
+ query_box->response_handler);
+
+ query_box->response_handler = 0;
+ }
+
+ /* disconnect, if we are connected to some signal */
+ if (query_box->object)
+ g_signal_handlers_disconnect_by_func (query_box->object,
+ query_box_cancel_callback,
+ query_box);
+}
+
+static void
+query_box_destroy (QueryBox *query_box)
+{
+ /* Destroy the box */
+ if (query_box->qbox)
+ gtk_widget_destroy (query_box->qbox);
+
+ g_slice_free (QueryBox, query_box);
+}
+
+static void
+string_query_box_response (GtkWidget *widget,
+ gint response_id,
+ QueryBox *query_box)
+{
+ const gchar *string;
+
+ query_box_disconnect (query_box);
+
+ /* Get the entry data */
+ string = gtk_entry_get_text (GTK_ENTRY (query_box->entry));
+
+ /* Call the user defined callback */
+ if (response_id == GTK_RESPONSE_OK)
+ (* (GimpQueryStringCallback) query_box->callback) (query_box->qbox,
+ string,
+ query_box->callback_data);
+
+ query_box_destroy (query_box);
+}
+
+static void
+int_query_box_response (GtkWidget *widget,
+ gint response_id,
+ QueryBox *query_box)
+{
+ gint value;
+
+ query_box_disconnect (query_box);
+
+ /* Get the spinbutton data */
+ value = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (query_box->entry));
+
+ /* Call the user defined callback */
+ if (response_id == GTK_RESPONSE_OK)
+ (* (GimpQueryIntCallback) query_box->callback) (query_box->qbox,
+ value,
+ query_box->callback_data);
+
+ query_box_destroy (query_box);
+}
+
+static void
+double_query_box_response (GtkWidget *widget,
+ gint response_id,
+ QueryBox *query_box)
+{
+ gdouble value;
+
+ query_box_disconnect (query_box);
+
+ /* Get the spinbutton data */
+ value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (query_box->entry));
+
+ /* Call the user defined callback */
+ if (response_id == GTK_RESPONSE_OK)
+ (* (GimpQueryDoubleCallback) query_box->callback) (query_box->qbox,
+ value,
+ query_box->callback_data);
+
+ query_box_destroy (query_box);
+}
+
+static void
+size_query_box_response (GtkWidget *widget,
+ gint response_id,
+ QueryBox *query_box)
+{
+ gdouble size;
+ GimpUnit unit;
+
+ query_box_disconnect (query_box);
+
+ /* Get the sizeentry data */
+ size = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (query_box->entry), 0);
+ unit = gimp_size_entry_get_unit (GIMP_SIZE_ENTRY (query_box->entry));
+
+ /* Call the user defined callback */
+ if (response_id == GTK_RESPONSE_OK)
+ (* (GimpQuerySizeCallback) query_box->callback) (query_box->qbox,
+ size,
+ unit,
+ query_box->callback_data);
+
+ query_box_destroy (query_box);
+}
+
+static void
+boolean_query_box_response (GtkWidget *widget,
+ gint response_id,
+ QueryBox *query_box)
+{
+ query_box_disconnect (query_box);
+
+ /* Call the user defined callback */
+ (* (GimpQueryBooleanCallback) query_box->callback) (query_box->qbox,
+ (response_id ==
+ GTK_RESPONSE_OK),
+ query_box->callback_data);
+
+ query_box_destroy (query_box);
+}
+
+static void
+query_box_cancel_callback (QueryBox *query_box)
+{
+ query_box_disconnect (query_box);
+ query_box_destroy (query_box);
+}
diff --git a/libgimpwidgets/gimpquerybox.h b/libgimpwidgets/gimpquerybox.h
new file mode 100644
index 0000000..3535629
--- /dev/null
+++ b/libgimpwidgets/gimpquerybox.h
@@ -0,0 +1,180 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpquerybox.h
+ * Copyright (C) 1999-2000 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_QUERY_BOX_H__
+#define __GIMP_QUERY_BOX_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+/**
+ * GimpQueryStringCallback:
+ * @query_box: The query box.
+ * @string: The entered string.
+ * @data: The user data.
+ *
+ * Note that you must not g_free() the passed string.
+ **/
+typedef void (* GimpQueryStringCallback) (GtkWidget *query_box,
+ const gchar *string,
+ gpointer data);
+
+/**
+ * GimpQueryIntCallback:
+ * @query_box: The query box.
+ * @value: The entered integer value.
+ * @data: The user data.
+ *
+ * The callback for an int query box.
+ **/
+typedef void (* GimpQueryIntCallback) (GtkWidget *query_box,
+ gint value,
+ gpointer data);
+
+/**
+ * GimpQueryDoubleCallback:
+ * @query_box: The query box.
+ * @value: The entered double value.
+ * @data: The user data.
+ *
+ * The callback for a double query box.
+ **/
+typedef void (* GimpQueryDoubleCallback) (GtkWidget *query_box,
+ gdouble value,
+ gpointer data);
+
+/**
+ * GimpQuerySizeCallback:
+ * @query_box: The query box.
+ * @size: The entered size in pixels.
+ * @unit: The selected unit from the #GimpUnitMenu.
+ * @data: The user data.
+ *
+ * The callback for a size query box.
+ **/
+typedef void (* GimpQuerySizeCallback) (GtkWidget *query_box,
+ gdouble size,
+ GimpUnit unit,
+ gpointer data);
+
+/**
+ * GimpQueryBooleanCallback:
+ * @query_box: The query box.
+ * @value: The entered boolean value.
+ * @data: The user data.
+ *
+ * The callback for a boolean query box.
+ **/
+typedef void (* GimpQueryBooleanCallback) (GtkWidget *query_box,
+ gboolean value,
+ gpointer data);
+
+
+/**
+ * GIMP_QUERY_BOX_VBOX:
+ * @qbox: The query box.
+ *
+ * A macro to access the #GtkVBox in a #libgimpwidgets-gimpquerybox.
+ * Useful if you want to add more widgets.
+ **/
+#define GIMP_QUERY_BOX_VBOX(qbox) g_object_get_data (G_OBJECT (qbox), \
+ "gimp-query-box-vbox")
+
+
+/* some simple query dialogs */
+GtkWidget * gimp_query_string_box (const gchar *title,
+ GtkWidget *parent,
+ GimpHelpFunc help_func,
+ const gchar *help_id,
+ const gchar *message,
+ const gchar *initial,
+ GObject *object,
+ const gchar *signal,
+ GimpQueryStringCallback callback,
+ gpointer data);
+
+GtkWidget * gimp_query_int_box (const gchar *title,
+ GtkWidget *parent,
+ GimpHelpFunc help_func,
+ const gchar *help_id,
+ const gchar *message,
+ gint initial,
+ gint lower,
+ gint upper,
+ GObject *object,
+ const gchar *signal,
+ GimpQueryIntCallback callback,
+ gpointer data);
+
+GtkWidget * gimp_query_double_box (const gchar *title,
+ GtkWidget *parent,
+ GimpHelpFunc help_func,
+ const gchar *help_id,
+ const gchar *message,
+ gdouble initial,
+ gdouble lower,
+ gdouble upper,
+ gint digits,
+ GObject *object,
+ const gchar *signal,
+ GimpQueryDoubleCallback callback,
+ gpointer data);
+
+GtkWidget * gimp_query_size_box (const gchar *title,
+ GtkWidget *parent,
+ GimpHelpFunc help_func,
+ const gchar *help_id,
+ const gchar *message,
+ gdouble initial,
+ gdouble lower,
+ gdouble upper,
+ gint digits,
+ GimpUnit unit,
+ gdouble resolution,
+ gboolean dot_for_dot,
+ GObject *object,
+ const gchar *signal,
+ GimpQuerySizeCallback callback,
+ gpointer data);
+
+GtkWidget * gimp_query_boolean_box (const gchar *title,
+ GtkWidget *parent,
+ GimpHelpFunc help_func,
+ const gchar *help_id,
+ const gchar *icon_name,
+ const gchar *message,
+ const gchar *true_button,
+ const gchar *false_button,
+ GObject *object,
+ const gchar *signal,
+ GimpQueryBooleanCallback callback,
+ gpointer data);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_QUERY_BOX_H__ */
diff --git a/libgimpwidgets/gimpruler.c b/libgimpwidgets/gimpruler.c
new file mode 100644
index 0000000..db9f538
--- /dev/null
+++ b/libgimpwidgets/gimpruler.c
@@ -0,0 +1,1465 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpmath/gimpmath.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpruler.h"
+
+
+/**
+ * SECTION: gimpruler
+ * @title: GimpRuler
+ * @short_description: A ruler widget with configurable unit and orientation.
+ *
+ * A ruler widget with configurable unit and orientation.
+ **/
+
+
+#define DEFAULT_RULER_FONT_SCALE PANGO_SCALE_SMALL
+#define MINIMUM_INCR 5
+#define IMMEDIATE_REDRAW_THRESHOLD 20
+
+
+enum
+{
+ PROP_0,
+ PROP_ORIENTATION,
+ PROP_UNIT,
+ PROP_LOWER,
+ PROP_UPPER,
+ PROP_POSITION,
+ PROP_MAX_SIZE
+};
+
+
+/* All distances below are in 1/72nd's of an inch. (According to
+ * Adobe that's a point, but points are really 1/72.27 in.)
+ */
+typedef struct
+{
+ GtkOrientation orientation;
+ GimpUnit unit;
+ gdouble lower;
+ gdouble upper;
+ gdouble position;
+ gdouble max_size;
+
+ GdkWindow *input_window;
+ cairo_surface_t *backing_store;
+ gboolean backing_store_valid;
+ GdkRectangle last_pos_rect;
+ guint pos_redraw_idle_id;
+ PangoLayout *layout;
+ gdouble font_scale;
+
+ GList *track_widgets;
+} GimpRulerPrivate;
+
+#define GIMP_RULER_GET_PRIVATE(ruler) \
+ ((GimpRulerPrivate *) gimp_ruler_get_instance_private ((GimpRuler *) (ruler)))
+
+
+typedef struct
+{
+ const gdouble ruler_scale[16];
+ const gint subdivide[5];
+} RulerMetric;
+
+static const RulerMetric ruler_metric_decimal =
+{
+ { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000, 25000, 50000, 100000 },
+ { 1, 5, 10, 50, 100 }
+};
+
+static const RulerMetric ruler_metric_inches =
+{
+ { 1, 2, 6, 12, 36, 100, 250, 500, 1000, 2500, 5000, 10000, 25000, 50000, 100000 },
+ { 1, 4, 8, 16, 12 * 16 }
+};
+
+static const RulerMetric ruler_metric_feet =
+{
+ { 1, 2, 6, 12, 36, 100, 250, 500, 1000, 2500, 5000, 10000, 25000, 50000, 100000 },
+ { 1, 3, 6, 12, 12 * 8 }
+};
+
+static const RulerMetric ruler_metric_yards =
+{
+ { 1, 2, 6, 12, 36, 100, 250, 500, 1000, 2500, 5000, 10000, 25000, 50000, 100000 },
+ { 1, 3, 6, 12, 12 * 12 }
+};
+
+
+static void gimp_ruler_dispose (GObject *object);
+static void gimp_ruler_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_ruler_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_ruler_realize (GtkWidget *widget);
+static void gimp_ruler_unrealize (GtkWidget *widget);
+static void gimp_ruler_map (GtkWidget *widget);
+static void gimp_ruler_unmap (GtkWidget *widget);
+static void gimp_ruler_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static void gimp_ruler_size_request (GtkWidget *widget,
+ GtkRequisition *requisition);
+static void gimp_ruler_style_set (GtkWidget *widget,
+ GtkStyle *prev_style);
+static gboolean gimp_ruler_motion_notify (GtkWidget *widget,
+ GdkEventMotion *event);
+static gboolean gimp_ruler_expose (GtkWidget *widget,
+ GdkEventExpose *event);
+
+static void gimp_ruler_draw_ticks (GimpRuler *ruler);
+static GdkRectangle gimp_ruler_get_pos_rect (GimpRuler *ruler,
+ gdouble position);
+static gboolean gimp_ruler_idle_queue_pos_redraw (gpointer data);
+static void gimp_ruler_queue_pos_redraw (GimpRuler *ruler);
+static void gimp_ruler_draw_pos (GimpRuler *ruler,
+ cairo_t *cr);
+static void gimp_ruler_make_pixmap (GimpRuler *ruler);
+static PangoLayout * gimp_ruler_get_layout (GtkWidget *widget,
+ const gchar *text);
+static const RulerMetric *
+ gimp_ruler_get_metric (GimpUnit unit);
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpRuler, gimp_ruler, GTK_TYPE_WIDGET)
+
+#define parent_class gimp_ruler_parent_class
+
+
+static void
+gimp_ruler_class_init (GimpRulerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->dispose = gimp_ruler_dispose;
+ object_class->set_property = gimp_ruler_set_property;
+ object_class->get_property = gimp_ruler_get_property;
+
+ widget_class->realize = gimp_ruler_realize;
+ widget_class->unrealize = gimp_ruler_unrealize;
+ widget_class->map = gimp_ruler_map;
+ widget_class->unmap = gimp_ruler_unmap;
+ widget_class->size_allocate = gimp_ruler_size_allocate;
+ widget_class->size_request = gimp_ruler_size_request;
+ widget_class->style_set = gimp_ruler_style_set;
+ widget_class->motion_notify_event = gimp_ruler_motion_notify;
+ widget_class->expose_event = gimp_ruler_expose;
+
+ g_object_class_install_property (object_class,
+ PROP_ORIENTATION,
+ g_param_spec_enum ("orientation",
+ "Orientation",
+ "The orientation of the ruler",
+ GTK_TYPE_ORIENTATION,
+ GTK_ORIENTATION_HORIZONTAL,
+ GIMP_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_UNIT,
+ gimp_param_spec_unit ("unit",
+ "Unit",
+ "Unit of ruler",
+ TRUE, TRUE,
+ GIMP_UNIT_PIXEL,
+ GIMP_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_LOWER,
+ g_param_spec_double ("lower",
+ "Lower",
+ "Lower limit of ruler",
+ -G_MAXDOUBLE,
+ G_MAXDOUBLE,
+ 0.0,
+ GIMP_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_UPPER,
+ g_param_spec_double ("upper",
+ "Upper",
+ "Upper limit of ruler",
+ -G_MAXDOUBLE,
+ G_MAXDOUBLE,
+ 0.0,
+ GIMP_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_POSITION,
+ g_param_spec_double ("position",
+ "Position",
+ "Position of mark on the ruler",
+ -G_MAXDOUBLE,
+ G_MAXDOUBLE,
+ 0.0,
+ GIMP_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_MAX_SIZE,
+ g_param_spec_double ("max-size",
+ "Max Size",
+ "Maximum size of the ruler",
+ -G_MAXDOUBLE,
+ G_MAXDOUBLE,
+ 0.0,
+ GIMP_PARAM_READWRITE));
+
+ gtk_widget_class_install_style_property (widget_class,
+ g_param_spec_double ("font-scale",
+ "Font Scale",
+ "The size of the used font",
+ 0.0,
+ G_MAXDOUBLE,
+ DEFAULT_RULER_FONT_SCALE,
+ GIMP_PARAM_READABLE));
+}
+
+static void
+gimp_ruler_init (GimpRuler *ruler)
+{
+ GimpRulerPrivate *priv = GIMP_RULER_GET_PRIVATE (ruler);
+
+ gtk_widget_set_has_window (GTK_WIDGET (ruler), FALSE);
+
+ priv->orientation = GTK_ORIENTATION_HORIZONTAL;
+ priv->unit = GIMP_UNIT_PIXEL;
+ priv->lower = 0;
+ priv->upper = 0;
+ priv->position = 0;
+ priv->max_size = 0;
+
+ priv->backing_store = NULL;
+ priv->backing_store_valid = FALSE;
+
+ priv->last_pos_rect.x = 0;
+ priv->last_pos_rect.y = 0;
+ priv->last_pos_rect.width = 0;
+ priv->last_pos_rect.height = 0;
+ priv->pos_redraw_idle_id = 0;
+
+ priv->font_scale = DEFAULT_RULER_FONT_SCALE;
+}
+
+static void
+gimp_ruler_dispose (GObject *object)
+{
+ GimpRuler *ruler = GIMP_RULER (object);
+ GimpRulerPrivate *priv = GIMP_RULER_GET_PRIVATE (ruler);
+
+ while (priv->track_widgets)
+ gimp_ruler_remove_track_widget (ruler, priv->track_widgets->data);
+
+ if (priv->pos_redraw_idle_id)
+ {
+ g_source_remove (priv->pos_redraw_idle_id);
+ priv->pos_redraw_idle_id = 0;
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gimp_ruler_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpRuler *ruler = GIMP_RULER (object);
+ GimpRulerPrivate *priv = GIMP_RULER_GET_PRIVATE (ruler);
+
+ switch (prop_id)
+ {
+ case PROP_ORIENTATION:
+ priv->orientation = g_value_get_enum (value);
+ gtk_widget_queue_resize (GTK_WIDGET (ruler));
+ break;
+
+ case PROP_UNIT:
+ gimp_ruler_set_unit (ruler, g_value_get_int (value));
+ break;
+
+ case PROP_LOWER:
+ gimp_ruler_set_range (ruler,
+ g_value_get_double (value),
+ priv->upper,
+ priv->max_size);
+ break;
+ case PROP_UPPER:
+ gimp_ruler_set_range (ruler,
+ priv->lower,
+ g_value_get_double (value),
+ priv->max_size);
+ break;
+
+ case PROP_POSITION:
+ gimp_ruler_set_position (ruler, g_value_get_double (value));
+ break;
+
+ case PROP_MAX_SIZE:
+ gimp_ruler_set_range (ruler,
+ priv->lower,
+ priv->upper,
+ g_value_get_double (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_ruler_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpRuler *ruler = GIMP_RULER (object);
+ GimpRulerPrivate *priv = GIMP_RULER_GET_PRIVATE (ruler);
+
+ switch (prop_id)
+ {
+ case PROP_ORIENTATION:
+ g_value_set_enum (value, priv->orientation);
+ break;
+
+ case PROP_UNIT:
+ g_value_set_int (value, priv->unit);
+ break;
+
+ case PROP_LOWER:
+ g_value_set_double (value, priv->lower);
+ break;
+
+ case PROP_UPPER:
+ g_value_set_double (value, priv->upper);
+ break;
+
+ case PROP_POSITION:
+ g_value_set_double (value, priv->position);
+ break;
+
+ case PROP_MAX_SIZE:
+ g_value_set_double (value, priv->max_size);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/**
+ * gimp_ruler_new:
+ * @orientation: the ruler's orientation.
+ *
+ * Creates a new ruler.
+ *
+ * Return value: a new #GimpRuler widget.
+ *
+ * Since: 2.8
+ **/
+GtkWidget *
+gimp_ruler_new (GtkOrientation orientation)
+{
+ return g_object_new (GIMP_TYPE_RULER,
+ "orientation", orientation,
+ NULL);
+}
+
+static void
+gimp_ruler_update_position (GimpRuler *ruler,
+ gdouble x,
+ gdouble y)
+{
+ GimpRulerPrivate *priv = GIMP_RULER_GET_PRIVATE (ruler);
+ GtkAllocation allocation;
+ gdouble lower;
+ gdouble upper;
+
+ gtk_widget_get_allocation (GTK_WIDGET (ruler), &allocation);
+ gimp_ruler_get_range (ruler, &lower, &upper, NULL);
+
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ gimp_ruler_set_position (ruler,
+ lower +
+ (upper - lower) * x / allocation.width);
+ }
+ else
+ {
+ gimp_ruler_set_position (ruler,
+ lower +
+ (upper - lower) * y / allocation.height);
+ }
+}
+
+/* Returns TRUE if a translation should be done */
+static gboolean
+gtk_widget_get_translation_to_window (GtkWidget *widget,
+ GdkWindow *window,
+ int *x,
+ int *y)
+{
+ GdkWindow *w, *widget_window;
+
+ if (! gtk_widget_get_has_window (widget))
+ {
+ GtkAllocation allocation;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ *x = -allocation.x;
+ *y = -allocation.y;
+ }
+ else
+ {
+ *x = 0;
+ *y = 0;
+ }
+
+ widget_window = gtk_widget_get_window (widget);
+
+ for (w = window;
+ w && w != widget_window;
+ w = gdk_window_get_effective_parent (w))
+ {
+ gdouble px, py;
+
+ gdk_window_coords_to_parent (w, *x, *y, &px, &py);
+
+ *x += px;
+ *y += py;
+ }
+
+ if (w == NULL)
+ {
+ *x = 0;
+ *y = 0;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+gimp_ruler_event_to_widget_coords (GtkWidget *widget,
+ GdkWindow *window,
+ gdouble event_x,
+ gdouble event_y,
+ gint *widget_x,
+ gint *widget_y)
+{
+ gint tx, ty;
+
+ if (gtk_widget_get_translation_to_window (widget, window, &tx, &ty))
+ {
+ event_x += tx;
+ event_y += ty;
+ }
+
+ *widget_x = event_x;
+ *widget_y = event_y;
+}
+
+static gboolean
+gimp_ruler_track_widget_motion_notify (GtkWidget *widget,
+ GdkEventMotion *mevent,
+ GimpRuler *ruler)
+{
+ gint widget_x;
+ gint widget_y;
+ gint ruler_x;
+ gint ruler_y;
+
+ widget = gtk_get_event_widget ((GdkEvent *) mevent);
+
+ gimp_ruler_event_to_widget_coords (widget, mevent->window,
+ mevent->x, mevent->y,
+ &widget_x, &widget_y);
+
+ if (gtk_widget_translate_coordinates (widget, GTK_WIDGET (ruler),
+ widget_x, widget_y,
+ &ruler_x, &ruler_y))
+ {
+ gimp_ruler_update_position (ruler, ruler_x, ruler_y);
+ }
+
+ return FALSE;
+}
+
+/**
+ * gimp_ruler_add_track_widget:
+ * @ruler: a #GimpRuler
+ * @widget: the track widget to add
+ *
+ * Adds a "track widget" to the ruler. The ruler will connect to
+ * GtkWidget:motion-notify-event: on the track widget and update its
+ * position marker accordingly. The marker is correctly updated also
+ * for the track widget's children, regardless of whether they are
+ * ordinary children of off-screen children.
+ *
+ * Since: 2.8
+ */
+void
+gimp_ruler_add_track_widget (GimpRuler *ruler,
+ GtkWidget *widget)
+{
+ GimpRulerPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_RULER (ruler));
+ g_return_if_fail (GTK_IS_WIDGET (ruler));
+
+ priv = GIMP_RULER_GET_PRIVATE (ruler);
+
+ g_return_if_fail (g_list_find (priv->track_widgets, widget) == NULL);
+
+ priv->track_widgets = g_list_prepend (priv->track_widgets, widget);
+
+ g_signal_connect (widget, "motion-notify-event",
+ G_CALLBACK (gimp_ruler_track_widget_motion_notify),
+ ruler);
+ g_signal_connect_swapped (widget, "destroy",
+ G_CALLBACK (gimp_ruler_remove_track_widget),
+ ruler);
+}
+
+/**
+ * gimp_ruler_remove_track_widget:
+ * @ruler: a #GimpRuler
+ * @widget: the track widget to remove
+ *
+ * Removes a previously added track widget from the ruler. See
+ * gimp_ruler_add_track_widget().
+ *
+ * Since: 2.8
+ */
+void
+gimp_ruler_remove_track_widget (GimpRuler *ruler,
+ GtkWidget *widget)
+{
+ GimpRulerPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_RULER (ruler));
+ g_return_if_fail (GTK_IS_WIDGET (ruler));
+
+ priv = GIMP_RULER_GET_PRIVATE (ruler);
+
+ g_return_if_fail (g_list_find (priv->track_widgets, widget) != NULL);
+
+ priv->track_widgets = g_list_remove (priv->track_widgets, widget);
+
+ g_signal_handlers_disconnect_by_func (widget,
+ gimp_ruler_track_widget_motion_notify,
+ ruler);
+ g_signal_handlers_disconnect_by_func (widget,
+ gimp_ruler_remove_track_widget,
+ ruler);
+}
+
+/**
+ * gimp_ruler_set_unit:
+ * @ruler: a #GimpRuler
+ * @unit: the #GimpUnit to set the ruler to
+ *
+ * This sets the unit of the ruler.
+ *
+ * Since: 2.8
+ */
+void
+gimp_ruler_set_unit (GimpRuler *ruler,
+ GimpUnit unit)
+{
+ GimpRulerPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_RULER (ruler));
+
+ priv = GIMP_RULER_GET_PRIVATE (ruler);
+
+ if (priv->unit != unit)
+ {
+ priv->unit = unit;
+ g_object_notify (G_OBJECT (ruler), "unit");
+
+ priv->backing_store_valid = FALSE;
+ gtk_widget_queue_draw (GTK_WIDGET (ruler));
+ }
+}
+
+/**
+ * gimp_ruler_get_unit:
+ * @ruler: a #GimpRuler
+ *
+ * Return value: the unit currently used in the @ruler widget.
+ *
+ * Since: 2.8
+ **/
+GimpUnit
+gimp_ruler_get_unit (GimpRuler *ruler)
+{
+ g_return_val_if_fail (GIMP_IS_RULER (ruler), 0);
+
+ return GIMP_RULER_GET_PRIVATE (ruler)->unit;
+}
+
+/**
+ * gimp_ruler_set_position:
+ * @ruler: a #GimpRuler
+ * @position: the position to set the ruler to
+ *
+ * This sets the position of the ruler.
+ *
+ * Since: 2.8
+ */
+void
+gimp_ruler_set_position (GimpRuler *ruler,
+ gdouble position)
+{
+ GimpRulerPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_RULER (ruler));
+
+ priv = GIMP_RULER_GET_PRIVATE (ruler);
+
+ if (priv->position != position)
+ {
+ GdkRectangle rect;
+ gint xdiff, ydiff;
+
+ priv->position = position;
+ g_object_notify (G_OBJECT (ruler), "position");
+
+ rect = gimp_ruler_get_pos_rect (ruler, priv->position);
+
+ xdiff = rect.x - priv->last_pos_rect.x;
+ ydiff = rect.y - priv->last_pos_rect.y;
+
+ /*
+ * If the position has changed far enough, queue a redraw immediately.
+ * Otherwise, we only queue a redraw in a low priority idle handler, to
+ * allow for other things (like updating the canvas) to run.
+ *
+ * TODO: This might not be necessary any more in GTK3 with the frame
+ * clock. Investigate this more after the port to GTK3.
+ */
+ if (priv->last_pos_rect.width != 0 &&
+ priv->last_pos_rect.height != 0 &&
+ (ABS (xdiff) > IMMEDIATE_REDRAW_THRESHOLD ||
+ ABS (ydiff) > IMMEDIATE_REDRAW_THRESHOLD))
+ {
+ if (priv->pos_redraw_idle_id)
+ {
+ g_source_remove (priv->pos_redraw_idle_id);
+ priv->pos_redraw_idle_id = 0;
+ }
+
+ gimp_ruler_queue_pos_redraw (ruler);
+ }
+ else if (! priv->pos_redraw_idle_id)
+ {
+ priv->pos_redraw_idle_id =
+ g_idle_add_full (G_PRIORITY_LOW,
+ gimp_ruler_idle_queue_pos_redraw,
+ ruler, NULL);
+ }
+ }
+}
+
+/**
+ * gimp_ruler_get_position:
+ * @ruler: a #GimpRuler
+ *
+ * Return value: the current position of the @ruler widget.
+ *
+ * Since: 2.8
+ **/
+gdouble
+gimp_ruler_get_position (GimpRuler *ruler)
+{
+ g_return_val_if_fail (GIMP_IS_RULER (ruler), 0.0);
+
+ return GIMP_RULER_GET_PRIVATE (ruler)->position;
+}
+
+/**
+ * gimp_ruler_set_range:
+ * @ruler: a #GimpRuler
+ * @lower: the lower limit of the ruler
+ * @upper: the upper limit of the ruler
+ * @max_size: the maximum size of the ruler used when calculating the space to
+ * leave for the text
+ *
+ * This sets the range of the ruler.
+ *
+ * Since: 2.8
+ */
+void
+gimp_ruler_set_range (GimpRuler *ruler,
+ gdouble lower,
+ gdouble upper,
+ gdouble max_size)
+{
+ GimpRulerPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_RULER (ruler));
+
+ priv = GIMP_RULER_GET_PRIVATE (ruler);
+
+ g_object_freeze_notify (G_OBJECT (ruler));
+ if (priv->lower != lower)
+ {
+ priv->lower = lower;
+ g_object_notify (G_OBJECT (ruler), "lower");
+ }
+ if (priv->upper != upper)
+ {
+ priv->upper = upper;
+ g_object_notify (G_OBJECT (ruler), "upper");
+ }
+ if (priv->max_size != max_size)
+ {
+ priv->max_size = max_size;
+ g_object_notify (G_OBJECT (ruler), "max-size");
+ }
+ g_object_thaw_notify (G_OBJECT (ruler));
+
+ priv->backing_store_valid = FALSE;
+ gtk_widget_queue_draw (GTK_WIDGET (ruler));
+}
+
+/**
+ * gimp_ruler_get_range:
+ * @ruler: a #GimpRuler
+ * @lower: location to store lower limit of the ruler, or %NULL
+ * @upper: location to store upper limit of the ruler, or %NULL
+ * @max_size: location to store the maximum size of the ruler used when
+ * calculating the space to leave for the text, or %NULL.
+ *
+ * Retrieves values indicating the range and current position of a #GimpRuler.
+ * See gimp_ruler_set_range().
+ *
+ * Since: 2.8
+ **/
+void
+gimp_ruler_get_range (GimpRuler *ruler,
+ gdouble *lower,
+ gdouble *upper,
+ gdouble *max_size)
+{
+ GimpRulerPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_RULER (ruler));
+
+ priv = GIMP_RULER_GET_PRIVATE (ruler);
+
+ if (lower)
+ *lower = priv->lower;
+ if (upper)
+ *upper = priv->upper;
+ if (max_size)
+ *max_size = priv->max_size;
+}
+
+static void
+gimp_ruler_realize (GtkWidget *widget)
+{
+ GimpRuler *ruler = GIMP_RULER (widget);
+ GimpRulerPrivate *priv = GIMP_RULER_GET_PRIVATE (ruler);
+ GtkAllocation allocation;
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+
+ GTK_WIDGET_CLASS (gimp_ruler_parent_class)->realize (widget);
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.x = allocation.x;
+ attributes.y = allocation.y;
+ attributes.width = allocation.width;
+ attributes.height = allocation.height;
+ attributes.wclass = GDK_INPUT_ONLY;
+ attributes.event_mask = (gtk_widget_get_events (widget) |
+ GDK_EXPOSURE_MASK |
+ GDK_POINTER_MOTION_MASK);
+
+ attributes_mask = GDK_WA_X | GDK_WA_Y;
+
+ priv->input_window = gdk_window_new (gtk_widget_get_window (widget),
+ &attributes, attributes_mask);
+ gdk_window_set_user_data (priv->input_window, ruler);
+
+ gimp_ruler_make_pixmap (ruler);
+}
+
+static void
+gimp_ruler_unrealize (GtkWidget *widget)
+{
+ GimpRuler *ruler = GIMP_RULER (widget);
+ GimpRulerPrivate *priv = GIMP_RULER_GET_PRIVATE (ruler);
+
+ g_clear_pointer (&priv->backing_store, cairo_surface_destroy);
+ priv->backing_store_valid = FALSE;
+
+ g_clear_object (&priv->layout);
+
+ g_clear_pointer (&priv->input_window, gdk_window_destroy);
+
+ GTK_WIDGET_CLASS (gimp_ruler_parent_class)->unrealize (widget);
+}
+
+static void
+gimp_ruler_map (GtkWidget *widget)
+{
+ GimpRulerPrivate *priv = GIMP_RULER_GET_PRIVATE (widget);
+
+ GTK_WIDGET_CLASS (parent_class)->map (widget);
+
+ if (priv->input_window)
+ gdk_window_show (priv->input_window);
+}
+
+static void
+gimp_ruler_unmap (GtkWidget *widget)
+{
+ GimpRulerPrivate *priv = GIMP_RULER_GET_PRIVATE (widget);
+
+ if (priv->input_window)
+ gdk_window_hide (priv->input_window);
+
+ GTK_WIDGET_CLASS (parent_class)->unmap (widget);
+}
+
+static void
+gimp_ruler_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GimpRuler *ruler = GIMP_RULER (widget);
+ GimpRulerPrivate *priv = GIMP_RULER_GET_PRIVATE (ruler);
+ GtkAllocation widget_allocation;
+ gboolean resized;
+
+ gtk_widget_get_allocation (widget, &widget_allocation);
+
+ resized = (widget_allocation.width != allocation->width ||
+ widget_allocation.height != allocation->height);
+
+ GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
+
+ if (gtk_widget_get_realized (widget))
+ {
+ gdk_window_move_resize (priv->input_window,
+ allocation->x, allocation->y,
+ allocation->width, allocation->height);
+
+ if (resized)
+ gimp_ruler_make_pixmap (ruler);
+ }
+}
+
+static void
+gimp_ruler_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ GimpRulerPrivate *priv = GIMP_RULER_GET_PRIVATE (widget);
+ GtkStyle *style = gtk_widget_get_style (widget);
+ PangoLayout *layout;
+ PangoRectangle ink_rect;
+ gint size;
+
+ layout = gimp_ruler_get_layout (widget, "0123456789");
+ pango_layout_get_pixel_extents (layout, &ink_rect, NULL);
+
+ size = 2 + ink_rect.height * 1.7;
+
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ requisition->width = style->xthickness * 2 + 1;
+ requisition->height = style->ythickness * 2 + size;
+ }
+ else
+ {
+ requisition->width = style->xthickness * 2 + size;
+ requisition->height = style->ythickness * 2 + 1;
+ }
+}
+
+static void
+gimp_ruler_style_set (GtkWidget *widget,
+ GtkStyle *prev_style)
+{
+ GimpRulerPrivate *priv = GIMP_RULER_GET_PRIVATE (widget);
+
+ GTK_WIDGET_CLASS (gimp_ruler_parent_class)->style_set (widget, prev_style);
+
+ gtk_widget_style_get (widget,
+ "font-scale", &priv->font_scale,
+ NULL);
+
+ priv->backing_store_valid = FALSE;
+
+ g_clear_object (&priv->layout);
+}
+
+static gboolean
+gimp_ruler_motion_notify (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ GimpRuler *ruler = GIMP_RULER (widget);
+
+ gimp_ruler_update_position (ruler, event->x, event->y);
+
+ return FALSE;
+}
+
+static gboolean
+gimp_ruler_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ if (gtk_widget_is_drawable (widget))
+ {
+ GimpRuler *ruler = GIMP_RULER (widget);
+ GimpRulerPrivate *priv = GIMP_RULER_GET_PRIVATE (ruler);
+ GtkAllocation allocation;
+ cairo_t *cr;
+
+ if (! priv->backing_store_valid)
+ gimp_ruler_draw_ticks (ruler);
+
+ cr = gdk_cairo_create (gtk_widget_get_window (widget));
+ gdk_cairo_region (cr, event->region);
+ cairo_clip (cr);
+
+ gtk_widget_get_allocation (widget, &allocation);
+ cairo_set_source_surface (cr, priv->backing_store,
+ allocation.x, allocation.y);
+ cairo_paint (cr);
+
+ gimp_ruler_draw_pos (ruler, cr);
+
+ cairo_destroy (cr);
+ }
+
+ return FALSE;
+}
+
+static void
+gimp_ruler_draw_ticks (GimpRuler *ruler)
+{
+ GtkWidget *widget = GTK_WIDGET (ruler);
+ GtkStyle *style = gtk_widget_get_style (widget);
+ GimpRulerPrivate *priv = GIMP_RULER_GET_PRIVATE (ruler);
+ GtkStateType state = gtk_widget_get_state (widget);
+ GtkAllocation allocation;
+ cairo_t *cr;
+ gint i;
+ gint width, height;
+ gint xthickness;
+ gint ythickness;
+ gint length, ideal_length;
+ gdouble lower, upper; /* Upper and lower limits, in ruler units */
+ gdouble increment; /* Number of pixels per unit */
+ gint scale; /* Number of units per major unit */
+ gdouble start, end, cur;
+ gchar unit_str[32];
+ gint digit_height;
+ gint digit_offset;
+ gint text_size;
+ gint pos;
+ gdouble max_size;
+ GimpUnit unit;
+ PangoLayout *layout;
+ PangoRectangle logical_rect, ink_rect;
+ const RulerMetric *ruler_metric;
+
+ if (! gtk_widget_is_drawable (widget))
+ return;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ xthickness = style->xthickness;
+ ythickness = style->ythickness;
+
+ layout = gimp_ruler_get_layout (widget, "0123456789");
+ pango_layout_get_extents (layout, &ink_rect, &logical_rect);
+
+ digit_height = PANGO_PIXELS (ink_rect.height) + 2;
+ digit_offset = ink_rect.y;
+
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ width = allocation.width;
+ height = allocation.height - ythickness * 2;
+ }
+ else
+ {
+ width = allocation.height;
+ height = allocation.width - ythickness * 2;
+ }
+
+ cr = cairo_create (priv->backing_store);
+ gdk_cairo_set_source_color (cr, &style->bg[state]);
+
+#if 0
+ gtk_paint_box (style, priv->backing_store,
+ GTK_STATE_NORMAL, GTK_SHADOW_OUT,
+ NULL, widget,
+ priv->orientation == GTK_ORIENTATION_HORIZONTAL ?
+ "hruler" : "vruler",
+ 0, 0,
+ allocation.width, allocation.height);
+#else
+ cairo_paint (cr);
+#endif
+
+ gdk_cairo_set_source_color (cr, &style->fg[state]);
+
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ cairo_rectangle (cr,
+ xthickness,
+ height + ythickness,
+ allocation.width - 2 * xthickness,
+ 1);
+ }
+ else
+ {
+ cairo_rectangle (cr,
+ height + xthickness,
+ ythickness,
+ 1,
+ allocation.height - 2 * ythickness);
+ }
+
+ gimp_ruler_get_range (ruler, &lower, &upper, &max_size);
+
+ if ((upper - lower) == 0)
+ goto out;
+
+ increment = (gdouble) width / (upper - lower);
+
+ /* determine the scale
+ * use the maximum extents of the ruler to determine the largest
+ * possible number to be displayed. Calculate the height in pixels
+ * of this displayed text. Use this height to find a scale which
+ * leaves sufficient room for drawing the ruler.
+ *
+ * We calculate the text size as for the vruler instead of
+ * actually measuring the text width, so that the result for the
+ * scale looks consistent with an accompanying vruler.
+ */
+ scale = ceil (max_size);
+ g_snprintf (unit_str, sizeof (unit_str), "%d", scale);
+ text_size = strlen (unit_str) * digit_height + 1;
+
+ unit = gimp_ruler_get_unit (ruler);
+
+ ruler_metric = gimp_ruler_get_metric (unit);
+
+ for (scale = 0; scale < G_N_ELEMENTS (ruler_metric->ruler_scale); scale++)
+ if (ruler_metric->ruler_scale[scale] * fabs (increment) > 2 * text_size)
+ break;
+
+ if (scale == G_N_ELEMENTS (ruler_metric->ruler_scale))
+ scale = G_N_ELEMENTS (ruler_metric->ruler_scale) - 1;
+
+ /* drawing starts here */
+ length = 0;
+ for (i = G_N_ELEMENTS (ruler_metric->subdivide) - 1; i >= 0; i--)
+ {
+ gdouble subd_incr;
+
+ /* hack to get proper subdivisions at full pixels */
+ if (unit == GIMP_UNIT_PIXEL && scale == 1 && i == 1)
+ subd_incr = 1.0;
+ else
+ subd_incr = ((gdouble) ruler_metric->ruler_scale[scale] /
+ (gdouble) ruler_metric->subdivide[i]);
+
+ if (subd_incr * fabs (increment) <= MINIMUM_INCR)
+ continue;
+
+ /* don't subdivide pixels */
+ if (unit == GIMP_UNIT_PIXEL && subd_incr < 1.0)
+ continue;
+
+ /* Calculate the length of the tickmarks. Make sure that
+ * this length increases for each set of ticks
+ */
+ ideal_length = height / (i + 1) - 1;
+ if (ideal_length > ++length)
+ length = ideal_length;
+
+ if (lower < upper)
+ {
+ start = floor (lower / subd_incr) * subd_incr;
+ end = ceil (upper / subd_incr) * subd_incr;
+ }
+ else
+ {
+ start = floor (upper / subd_incr) * subd_incr;
+ end = ceil (lower / subd_incr) * subd_incr;
+ }
+
+ for (cur = start; cur <= end; cur += subd_incr)
+ {
+ pos = ROUND ((cur - lower) * increment);
+
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ cairo_rectangle (cr,
+ pos, height + ythickness - length,
+ 1, length);
+ }
+ else
+ {
+ cairo_rectangle (cr,
+ height + xthickness - length, pos,
+ length, 1);
+ }
+
+ /* draw label */
+ if (i == 0)
+ {
+ g_snprintf (unit_str, sizeof (unit_str), "%d", (int) cur);
+
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ pango_layout_set_text (layout, unit_str, -1);
+ pango_layout_get_extents (layout, &logical_rect, NULL);
+
+#if 0
+ gtk_paint_layout (style,
+ priv->backing_store,
+ state,
+ FALSE,
+ NULL,
+ widget,
+ "hruler",
+ pos + 2,
+ ythickness + PANGO_PIXELS (logical_rect.y - digit_offset),
+ layout);
+#else
+ cairo_move_to (cr,
+ pos + 2,
+ ythickness + PANGO_PIXELS (logical_rect.y - digit_offset));
+ pango_cairo_show_layout (cr, layout);
+#endif
+ }
+ else
+ {
+ gint j;
+
+ for (j = 0; j < (int) strlen (unit_str); j++)
+ {
+ pango_layout_set_text (layout, unit_str + j, 1);
+ pango_layout_get_extents (layout, NULL, &logical_rect);
+
+#if 0
+ gtk_paint_layout (style,
+ priv->backing_store,
+ state,
+ FALSE,
+ NULL,
+ widget,
+ "vruler",
+ xthickness + 1,
+ pos + digit_height * j + 2 + PANGO_PIXELS (logical_rect.y - digit_offset),
+ layout);
+#else
+ cairo_move_to (cr,
+ xthickness + 1,
+ pos + digit_height * j + 2 + PANGO_PIXELS (logical_rect.y - digit_offset));
+ pango_cairo_show_layout (cr, layout);
+#endif
+
+ }
+ }
+ }
+ }
+ }
+
+ cairo_fill (cr);
+
+ priv->backing_store_valid = TRUE;
+
+out:
+ cairo_destroy (cr);
+}
+
+static GdkRectangle
+gimp_ruler_get_pos_rect (GimpRuler *ruler,
+ gdouble position)
+{
+ GtkWidget *widget = GTK_WIDGET (ruler);
+ GtkStyle *style = gtk_widget_get_style (widget);
+ GimpRulerPrivate *priv = GIMP_RULER_GET_PRIVATE (ruler);
+ GtkAllocation allocation;
+ gint width, height;
+ gint xthickness;
+ gint ythickness;
+ gdouble upper, lower;
+ gdouble increment;
+ GdkRectangle rect = { 0, };
+
+ if (! gtk_widget_is_drawable (widget))
+ return rect;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ xthickness = style->xthickness;
+ ythickness = style->ythickness;
+
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ width = allocation.width;
+ height = allocation.height - ythickness * 2;
+
+ rect.width = height / 2 + 2;
+ rect.width |= 1; /* make sure it's odd */
+ rect.height = rect.width / 2 + 1;
+ }
+ else
+ {
+ width = allocation.width - xthickness * 2;
+ height = allocation.height;
+
+ rect.height = width / 2 + 2;
+ rect.height |= 1; /* make sure it's odd */
+ rect.width = rect.height / 2 + 1;
+ }
+
+ gimp_ruler_get_range (ruler, &lower, &upper, NULL);
+
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ increment = (gdouble) width / (upper - lower);
+
+ rect.x = ROUND ((position - lower) * increment) + (xthickness - rect.width) / 2 - 1;
+ rect.y = (height + rect.height) / 2 + ythickness;
+ }
+ else
+ {
+ increment = (gdouble) height / (upper - lower);
+
+ rect.x = (width + rect.width) / 2 + xthickness;
+ rect.y = ROUND ((position - lower) * increment) + (ythickness - rect.height) / 2 - 1;
+ }
+
+ rect.x += allocation.x;
+ rect.y += allocation.y;
+
+ return rect;
+}
+
+static gboolean
+gimp_ruler_idle_queue_pos_redraw (gpointer data)
+{
+ GimpRuler *ruler = data;
+ GimpRulerPrivate *priv = GIMP_RULER_GET_PRIVATE (ruler);
+
+ gimp_ruler_queue_pos_redraw (ruler);
+
+ priv->pos_redraw_idle_id = 0;
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+gimp_ruler_queue_pos_redraw (GimpRuler *ruler)
+{
+ GimpRulerPrivate *priv = GIMP_RULER_GET_PRIVATE (ruler);
+ const GdkRectangle rect = gimp_ruler_get_pos_rect (ruler, priv->position);
+
+ gtk_widget_queue_draw_area (GTK_WIDGET (ruler),
+ rect.x,
+ rect.y,
+ rect.width,
+ rect.height);
+
+ if (priv->last_pos_rect.width != 0 &&
+ priv->last_pos_rect.height != 0)
+ {
+ gtk_widget_queue_draw_area (GTK_WIDGET (ruler),
+ priv->last_pos_rect.x,
+ priv->last_pos_rect.y,
+ priv->last_pos_rect.width,
+ priv->last_pos_rect.height);
+
+ priv->last_pos_rect.x = 0;
+ priv->last_pos_rect.y = 0;
+ priv->last_pos_rect.width = 0;
+ priv->last_pos_rect.height = 0;
+ }
+}
+
+static void
+gimp_ruler_draw_pos (GimpRuler *ruler,
+ cairo_t *cr)
+{
+ GtkWidget *widget = GTK_WIDGET (ruler);
+ GtkStyle *style = gtk_widget_get_style (widget);
+ GimpRulerPrivate *priv = GIMP_RULER_GET_PRIVATE (ruler);
+ GtkStateType state = gtk_widget_get_state (widget);
+ GdkRectangle pos_rect;
+
+ if (! gtk_widget_is_drawable (widget))
+ return;
+
+ pos_rect = gimp_ruler_get_pos_rect (ruler, gimp_ruler_get_position (ruler));
+
+ if ((pos_rect.width > 0) && (pos_rect.height > 0))
+ {
+ gdk_cairo_set_source_color (cr, &style->fg[state]);
+
+ cairo_move_to (cr, pos_rect.x, pos_rect.y);
+
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ cairo_line_to (cr, pos_rect.x + pos_rect.width / 2.0,
+ pos_rect.y + pos_rect.height);
+ cairo_line_to (cr, pos_rect.x + pos_rect.width,
+ pos_rect.y);
+ }
+ else
+ {
+ cairo_line_to (cr, pos_rect.x + pos_rect.width,
+ pos_rect.y + pos_rect.height / 2.0);
+ cairo_line_to (cr, pos_rect.x,
+ pos_rect.y + pos_rect.height);
+ }
+
+ cairo_fill (cr);
+ }
+
+ if (priv->last_pos_rect.width != 0 &&
+ priv->last_pos_rect.height != 0)
+ {
+ gdk_rectangle_union (&priv->last_pos_rect,
+ &pos_rect,
+ &priv->last_pos_rect);
+ }
+ else
+ {
+ priv->last_pos_rect = pos_rect;
+ }
+}
+
+static void
+gimp_ruler_make_pixmap (GimpRuler *ruler)
+{
+ GtkWidget *widget = GTK_WIDGET (ruler);
+ GimpRulerPrivate *priv = GIMP_RULER_GET_PRIVATE (ruler);
+ GtkAllocation allocation;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ if (priv->backing_store)
+ cairo_surface_destroy (priv->backing_store);
+
+ priv->backing_store =
+ gdk_window_create_similar_surface (gtk_widget_get_window (widget),
+ CAIRO_CONTENT_COLOR,
+ allocation.width,
+ allocation.height);
+
+ priv->backing_store_valid = FALSE;
+}
+
+static PangoLayout *
+gimp_ruler_create_layout (GtkWidget *widget,
+ const gchar *text)
+{
+ GimpRulerPrivate *priv = GIMP_RULER_GET_PRIVATE (widget);
+ PangoLayout *layout;
+ PangoAttrList *attrs;
+ PangoAttribute *attr;
+
+ layout = gtk_widget_create_pango_layout (widget, text);
+
+ attrs = pango_attr_list_new ();
+
+ attr = pango_attr_scale_new (priv->font_scale);
+ attr->start_index = 0;
+ attr->end_index = -1;
+ pango_attr_list_insert (attrs, attr);
+
+ pango_layout_set_attributes (layout, attrs);
+ pango_attr_list_unref (attrs);
+
+ return layout;
+}
+
+static PangoLayout *
+gimp_ruler_get_layout (GtkWidget *widget,
+ const gchar *text)
+{
+ GimpRulerPrivate *priv = GIMP_RULER_GET_PRIVATE (widget);
+
+ if (priv->layout)
+ {
+ pango_layout_set_text (priv->layout, text, -1);
+ return priv->layout;
+ }
+
+ priv->layout = gimp_ruler_create_layout (widget, text);
+
+ return priv->layout;
+}
+
+#define FACTOR_EPSILON 0.0000001
+#define FACTOR_EQUAL(u, f) (ABS (f - gimp_unit_get_factor (u)) < FACTOR_EPSILON)
+
+static const RulerMetric *
+gimp_ruler_get_metric (GimpUnit unit)
+{
+ /* not enabled until we double checked the metrics */
+ return &ruler_metric_decimal;
+
+ if (unit == GIMP_UNIT_INCH)
+ {
+ return &ruler_metric_inches;
+ }
+ else if (FACTOR_EQUAL (unit, 0.083333))
+ {
+ return &ruler_metric_feet;
+ }
+ else if (FACTOR_EQUAL (unit, 0.027778))
+ {
+ return &ruler_metric_yards;
+ }
+
+ return &ruler_metric_decimal;
+}
diff --git a/libgimpwidgets/gimpruler.h b/libgimpwidgets/gimpruler.h
new file mode 100644
index 0000000..8e4a363
--- /dev/null
+++ b/libgimpwidgets/gimpruler.h
@@ -0,0 +1,81 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_RULER_H__
+#define __GIMP_RULER_H__
+
+G_BEGIN_DECLS
+
+#define GIMP_TYPE_RULER (gimp_ruler_get_type ())
+#define GIMP_RULER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_RULER, GimpRuler))
+#define GIMP_RULER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_RULER, GimpRulerClass))
+#define GIMP_IS_RULER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_RULER))
+#define GIMP_IS_RULER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_RULER))
+#define GIMP_RULER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_RULER, GimpRulerClass))
+
+
+typedef struct _GimpRulerClass GimpRulerClass;
+
+struct _GimpRuler
+{
+ GtkWidget parent_instance;
+};
+
+struct _GimpRulerClass
+{
+ GtkWidgetClass parent_class;
+
+ /* Padding for future expansion */
+ void (*_gimp_reserved1) (void);
+ void (*_gimp_reserved2) (void);
+ void (*_gimp_reserved3) (void);
+ void (*_gimp_reserved4) (void);
+};
+
+
+GType gimp_ruler_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_ruler_new (GtkOrientation orientation);
+
+void gimp_ruler_add_track_widget (GimpRuler *ruler,
+ GtkWidget *widget);
+void gimp_ruler_remove_track_widget (GimpRuler *ruler,
+ GtkWidget *widget);
+
+void gimp_ruler_set_unit (GimpRuler *ruler,
+ GimpUnit unit);
+GimpUnit gimp_ruler_get_unit (GimpRuler *ruler);
+void gimp_ruler_set_position (GimpRuler *ruler,
+ gdouble position);
+gdouble gimp_ruler_get_position (GimpRuler *ruler);
+void gimp_ruler_set_range (GimpRuler *ruler,
+ gdouble lower,
+ gdouble upper,
+ gdouble max_size);
+void gimp_ruler_get_range (GimpRuler *ruler,
+ gdouble *lower,
+ gdouble *upper,
+ gdouble *max_size);
+
+G_END_DECLS
+
+#endif /* __GIMP_RULER_H__ */
diff --git a/libgimpwidgets/gimpscaleentry.c b/libgimpwidgets/gimpscaleentry.c
new file mode 100644
index 0000000..8112434
--- /dev/null
+++ b/libgimpwidgets/gimpscaleentry.c
@@ -0,0 +1,479 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpscaleentry.c
+ * Copyright (C) 2000 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpmath/gimpmath.h"
+#include "libgimpbase/gimpbase.h"
+
+#include "gimpspinbutton.h"
+#include "gimpwidgets.h"
+
+
+static gboolean gimp_scale_entry_linear_to_log (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data);
+static gboolean gimp_scale_entry_log_to_linear (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data);
+
+static GtkAdjustment * gimp_scale_entry_new_internal (gboolean color_scale,
+ GtkTable *table,
+ gint column,
+ gint row,
+ const gchar *text,
+ gint scale_width,
+ gint spinbutton_width,
+ gdouble value,
+ gdouble lower,
+ gdouble upper,
+ gdouble step_increment,
+ gdouble page_increment,
+ guint digits,
+ gboolean constrain,
+ gdouble unconstrained_lower,
+ gdouble unconstrained_upper,
+ const gchar *tooltip,
+ const gchar *help_id);
+
+
+static gboolean
+gimp_scale_entry_linear_to_log (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data)
+{
+ GtkAdjustment *spin_adjustment;
+ gdouble value = g_value_get_double (from_value);
+
+ spin_adjustment = GTK_ADJUSTMENT (g_binding_get_source (binding));
+
+ if (gtk_adjustment_get_lower (spin_adjustment) <= 0.0)
+ value = log (value - gtk_adjustment_get_lower (spin_adjustment) + 0.1);
+ else
+ value = log (value);
+
+ g_value_set_double (to_value, value);
+
+ return TRUE;
+}
+
+static gboolean
+gimp_scale_entry_log_to_linear (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data)
+{
+ GtkAdjustment *spin_adjustment;
+ gdouble value = g_value_get_double (from_value);
+
+ spin_adjustment = GTK_ADJUSTMENT (g_binding_get_source (binding));
+
+ value = exp (value);
+
+ if (gtk_adjustment_get_lower (spin_adjustment) <= 0.0)
+ value += gtk_adjustment_get_lower (spin_adjustment) - 0.1;
+
+ g_value_set_double (to_value, value);
+
+ return TRUE;
+}
+
+static GtkAdjustment *
+gimp_scale_entry_new_internal (gboolean color_scale,
+ GtkTable *table,
+ gint column,
+ gint row,
+ const gchar *text,
+ gint scale_width,
+ gint spinbutton_width,
+ gdouble value,
+ gdouble lower,
+ gdouble upper,
+ gdouble step_increment,
+ gdouble page_increment,
+ guint digits,
+ gboolean constrain,
+ gdouble unconstrained_lower,
+ gdouble unconstrained_upper,
+ const gchar *tooltip,
+ const gchar *help_id)
+{
+ GtkWidget *label;
+ GtkWidget *scale;
+ GtkWidget *spinbutton;
+ GtkAdjustment *scale_adjustment;
+ GtkAdjustment *spin_adjustment;
+ GBinding *binding;
+
+ label = gtk_label_new_with_mnemonic (text);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_widget_show (label);
+
+ scale_adjustment = (GtkAdjustment *)
+ gtk_adjustment_new (value, lower, upper,
+ step_increment, page_increment, 0.0);
+
+ if (! constrain &&
+ unconstrained_lower <= lower &&
+ unconstrained_upper >= upper)
+ {
+ spin_adjustment = (GtkAdjustment *)
+ gtk_adjustment_new (value,
+ unconstrained_lower,
+ unconstrained_upper,
+ step_increment, page_increment, 0.0);
+ }
+ else
+ {
+ spin_adjustment = (GtkAdjustment *)
+ gtk_adjustment_new (value, lower, upper,
+ step_increment, page_increment, 0.0);
+ }
+
+ binding = g_object_bind_property (G_OBJECT (spin_adjustment), "value",
+ G_OBJECT (scale_adjustment), "value",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE);
+
+ spinbutton = gimp_spin_button_new (spin_adjustment, step_increment, digits);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_widget_show (spinbutton);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinbutton);
+
+ if (spinbutton_width > 0)
+ {
+ if (spinbutton_width < 17)
+ gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), spinbutton_width);
+ else
+ gtk_widget_set_size_request (spinbutton, spinbutton_width, -1);
+ }
+
+ if (color_scale)
+ {
+ scale = gimp_color_scale_new (GTK_ORIENTATION_HORIZONTAL,
+ GIMP_COLOR_SELECTOR_VALUE);
+
+ gtk_range_set_adjustment (GTK_RANGE (scale), scale_adjustment);
+ }
+ else
+ {
+ scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, scale_adjustment);
+ }
+
+ if (scale_width > 0)
+ gtk_widget_set_size_request (scale, scale_width, -1);
+ gtk_scale_set_digits (GTK_SCALE (scale), digits);
+ gtk_scale_set_draw_value (GTK_SCALE (scale), FALSE);
+ gtk_widget_show (scale);
+
+ gtk_table_attach (GTK_TABLE (table), label,
+ column, column + 1, row, row + 1,
+ GTK_FILL, GTK_FILL, 0, 0);
+
+ gtk_table_attach (GTK_TABLE (table), scale,
+ column + 1, column + 2, row, row + 1,
+ GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+
+ gtk_table_attach (GTK_TABLE (table), spinbutton,
+ column + 2, column + 3, row, row + 1,
+ GTK_FILL | GTK_SHRINK, GTK_SHRINK, 0, 0);
+
+ if (tooltip || help_id)
+ {
+ gimp_help_set_help_data (label, tooltip, help_id);
+ gimp_help_set_help_data (scale, tooltip, help_id);
+ gimp_help_set_help_data (spinbutton, tooltip, help_id);
+ }
+
+ g_object_set_data (G_OBJECT (spin_adjustment), "label", label);
+ g_object_set_data (G_OBJECT (spin_adjustment), "scale", scale);
+ g_object_set_data (G_OBJECT (spin_adjustment), "spinbutton", spinbutton);
+ g_object_set_data (G_OBJECT (spin_adjustment), "binding", binding);
+
+ return spin_adjustment;
+}
+
+/**
+ * gimp_scale_entry_new:
+ * @table: The #GtkTable the widgets will be attached to.
+ * @column: The column to start with.
+ * @row: The row to attach the widgets.
+ * @text: The text for the #GtkLabel which will appear
+ * left of the #GtkHScale.
+ * @scale_width: The minimum horizontal size of the #GtkHScale.
+ * @spinbutton_width: The minimum horizontal size of the #GtkSpinButton.
+ * @value: The initial value.
+ * @lower: The lower boundary.
+ * @upper: The upper boundary.
+ * @step_increment: The step increment.
+ * @page_increment: The page increment.
+ * @digits: The number of decimal digits.
+ * @constrain: %TRUE if the range of possible values of the
+ * #GtkSpinButton should be the same as of the #GtkHScale.
+ * @unconstrained_lower: The spinbutton's lower boundary
+ * if @constrain == %FALSE.
+ * @unconstrained_upper: The spinbutton's upper boundary
+ * if @constrain == %FALSE.
+ * @tooltip: A tooltip message for the scale and the spinbutton.
+ * @help_id: The widgets' help_id (see gimp_help_set_help_data()).
+ *
+ * This function creates a #GtkLabel, a #GtkHScale and a #GtkSpinButton and
+ * attaches them to a 3-column #GtkTable.
+ *
+ * Returns: The #GtkSpinButton's #GtkAdjustment.
+ **/
+GtkObject *
+gimp_scale_entry_new (GtkTable *table,
+ gint column,
+ gint row,
+ const gchar *text,
+ gint scale_width,
+ gint spinbutton_width,
+ gdouble value,
+ gdouble lower,
+ gdouble upper,
+ gdouble step_increment,
+ gdouble page_increment,
+ guint digits,
+ gboolean constrain,
+ gdouble unconstrained_lower,
+ gdouble unconstrained_upper,
+ const gchar *tooltip,
+ const gchar *help_id)
+{
+ return (GtkObject *)
+ gimp_scale_entry_new_internal (FALSE,
+ table, column, row,
+ text, scale_width, spinbutton_width,
+ value, lower, upper,
+ step_increment, page_increment,
+ digits,
+ constrain,
+ unconstrained_lower,
+ unconstrained_upper,
+ tooltip, help_id);
+}
+
+/**
+ * gimp_color_scale_entry_new:
+ * @table: The #GtkTable the widgets will be attached to.
+ * @column: The column to start with.
+ * @row: The row to attach the widgets.
+ * @text: The text for the #GtkLabel which will appear
+ * left of the #GtkHScale.
+ * @scale_width: The minimum horizontal size of the #GtkHScale.
+ * @spinbutton_width: The minimum horizontal size of the #GtkSpinButton.
+ * @value: The initial value.
+ * @lower: The lower boundary.
+ * @upper: The upper boundary.
+ * @step_increment: The step increment.
+ * @page_increment: The page increment.
+ * @digits: The number of decimal digits.
+ * @tooltip: A tooltip message for the scale and the spinbutton.
+ * @help_id: The widgets' help_id (see gimp_help_set_help_data()).
+ *
+ * This function creates a #GtkLabel, a #GimpColorScale and a
+ * #GtkSpinButton and attaches them to a 3-column #GtkTable.
+ *
+ * Returns: The #GtkSpinButton's #GtkAdjustment.
+ **/
+GtkObject *
+gimp_color_scale_entry_new (GtkTable *table,
+ gint column,
+ gint row,
+ const gchar *text,
+ gint scale_width,
+ gint spinbutton_width,
+ gdouble value,
+ gdouble lower,
+ gdouble upper,
+ gdouble step_increment,
+ gdouble page_increment,
+ guint digits,
+ const gchar *tooltip,
+ const gchar *help_id)
+{
+ return (GtkObject *)
+ gimp_scale_entry_new_internal (TRUE,
+ table, column, row,
+ text, scale_width, spinbutton_width,
+ value, lower, upper,
+ step_increment, page_increment,
+ digits,
+ TRUE, 0.0, 0.0,
+ tooltip, help_id);
+}
+
+/**
+ * gimp_scale_entry_set_logarithmic:
+ * @adjustment: a #GtkAdjustment as returned by gimp_scale_entry_new()
+ * @logarithmic: a boolean value to set or reset logarithmic behaviour
+ * of the scale widget
+ *
+ * Sets whether the scale_entry's scale widget will behave in a linear
+ * or logharithmic fashion. Useful when an entry has to attend large
+ * ranges, but smaller selections on that range require a finer
+ * adjustment.
+ *
+ * Since: 2.2
+ **/
+void
+gimp_scale_entry_set_logarithmic (GtkObject *adjustment,
+ gboolean logarithmic)
+{
+ GtkAdjustment *spin_adj;
+ GtkAdjustment *scale_adj;
+ GBinding *binding;
+
+ g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
+
+ spin_adj = GTK_ADJUSTMENT (adjustment);
+ scale_adj = GIMP_SCALE_ENTRY_SCALE_ADJ (adjustment);
+ binding = g_object_get_data (G_OBJECT (adjustment), "binding");
+
+ g_return_if_fail (GTK_IS_ADJUSTMENT (scale_adj));
+ g_return_if_fail (G_IS_BINDING (binding));
+
+ logarithmic = logarithmic ? TRUE : FALSE;
+
+ if (logarithmic == gimp_scale_entry_get_logarithmic (adjustment))
+ return;
+
+ g_object_unref (binding);
+
+ if (logarithmic)
+ {
+ gdouble correction;
+ gdouble log_value, log_lower, log_upper;
+ gdouble log_step_increment, log_page_increment;
+
+ correction = (gtk_adjustment_get_lower (scale_adj) > 0 ?
+ 0 : 0.1 + - gtk_adjustment_get_lower (scale_adj));
+
+ log_value = log (gtk_adjustment_get_value (scale_adj) + correction);
+ log_lower = log (gtk_adjustment_get_lower (scale_adj) + correction);
+ log_upper = log (gtk_adjustment_get_upper (scale_adj) + correction);
+ log_step_increment =
+ (log_upper - log_lower) / ((gtk_adjustment_get_upper (scale_adj) -
+ gtk_adjustment_get_lower (scale_adj)) /
+ gtk_adjustment_get_step_increment (scale_adj));
+ log_page_increment =
+ (log_upper - log_lower) / ((gtk_adjustment_get_upper (scale_adj) -
+ gtk_adjustment_get_lower (scale_adj)) /
+ gtk_adjustment_get_page_increment (scale_adj));
+
+ gtk_adjustment_configure (scale_adj,
+ log_value, log_lower, log_upper,
+ log_step_increment, log_page_increment, 0.0);
+
+ binding = g_object_bind_property_full (G_OBJECT (spin_adj), "value",
+ G_OBJECT (scale_adj), "value",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE,
+ gimp_scale_entry_linear_to_log,
+ gimp_scale_entry_log_to_linear,
+ NULL, NULL);
+ }
+ else
+ {
+ gdouble lower, upper;
+
+ lower = exp (gtk_adjustment_get_lower (scale_adj));
+ upper = exp (gtk_adjustment_get_upper (scale_adj));
+
+ if (gtk_adjustment_get_lower (spin_adj) <= 0.0)
+ {
+ lower += - 0.1 + gtk_adjustment_get_lower (spin_adj);
+ upper += - 0.1 + gtk_adjustment_get_lower (spin_adj);
+ }
+
+ gtk_adjustment_configure (scale_adj,
+ gtk_adjustment_get_value (spin_adj),
+ lower, upper,
+ gtk_adjustment_get_step_increment (spin_adj),
+ gtk_adjustment_get_page_increment (spin_adj),
+ 0.0);
+
+ binding = g_object_bind_property (G_OBJECT (spin_adj), "value",
+ G_OBJECT (scale_adj), "value",
+ G_BINDING_BIDIRECTIONAL |
+ G_BINDING_SYNC_CREATE);
+ }
+
+ g_object_set_data (G_OBJECT (spin_adj), "binding", binding);
+
+ g_object_set_data (G_OBJECT (adjustment), "logarithmic",
+ GINT_TO_POINTER (logarithmic));
+}
+
+/**
+ * gimp_scale_entry_get_logarithmic:
+ * @adjustment: a #GtkAdjustment as returned by gimp_scale_entry_new()
+ *
+ * Return value: %TRUE if the the entry's scale widget will behave in
+ * logharithmic fashion, %FALSE for linear behaviour.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_scale_entry_get_logarithmic (GtkObject *adjustment)
+{
+ return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (adjustment),
+ "logarithmic"));
+}
+
+/**
+ * gimp_scale_entry_set_sensitive:
+ * @adjustment: a #GtkAdjustment returned by gimp_scale_entry_new()
+ * @sensitive: a boolean value with the same semantics as the @sensitive
+ * parameter of gtk_widget_set_sensitive()
+ *
+ * Sets the sensitivity of the scale_entry's #GtkLabel, #GtkHScale and
+ * #GtkSpinButton.
+ **/
+void
+gimp_scale_entry_set_sensitive (GtkObject *adjustment,
+ gboolean sensitive)
+{
+ GtkWidget *widget;
+
+ g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
+
+ widget = GIMP_SCALE_ENTRY_LABEL (adjustment);
+ if (widget)
+ gtk_widget_set_sensitive (widget, sensitive);
+
+ widget = GIMP_SCALE_ENTRY_SCALE (adjustment);
+ if (widget)
+ gtk_widget_set_sensitive (widget, sensitive);
+
+ widget = GIMP_SCALE_ENTRY_SPINBUTTON (adjustment);
+ if (widget)
+ gtk_widget_set_sensitive (widget, sensitive);
+}
diff --git a/libgimpwidgets/gimpscaleentry.h b/libgimpwidgets/gimpscaleentry.h
new file mode 100644
index 0000000..b02d56c
--- /dev/null
+++ b/libgimpwidgets/gimpscaleentry.h
@@ -0,0 +1,125 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpscaleentry.h
+ * Copyright (C) 2000 Michael Natterer <mitch@gimp.org>,
+ * 2008 Bill Skaggs <weskaggs@primate.ucdavis.edu>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_SCALE_ENTRY_H__
+#define __GIMP_SCALE_ENTRY_H__
+
+G_BEGIN_DECLS
+
+
+/**
+ * GIMP_SCALE_ENTRY_LABEL:
+ * @adj: The #GtkAdjustment returned by gimp_scale_entry_new().
+ *
+ * Returns: the scale_entry's #GtkLabel.
+ **/
+#define GIMP_SCALE_ENTRY_LABEL(adj) \
+ (g_object_get_data (G_OBJECT (adj), "label"))
+
+/**
+ * GIMP_SCALE_ENTRY_SCALE:
+ * @adj: The #GtkAdjustment returned by gimp_scale_entry_new().
+ *
+ * Returns: the scale_entry's #GtkHScale.
+ **/
+#define GIMP_SCALE_ENTRY_SCALE(adj) \
+ (g_object_get_data (G_OBJECT (adj), "scale"))
+
+/**
+ * GIMP_SCALE_ENTRY_SCALE_ADJ:
+ * @adj: The #GtkAdjustment returned by gimp_scale_entry_new().
+ *
+ * Returns: the #GtkAdjustment of the scale_entry's #GtkHScale.
+ **/
+#define GIMP_SCALE_ENTRY_SCALE_ADJ(adj) \
+ gtk_range_get_adjustment \
+ (GTK_RANGE (g_object_get_data (G_OBJECT (adj), "scale")))
+
+/**
+ * GIMP_SCALE_ENTRY_SPINBUTTON:
+ * @adj: The #GtkAdjustment returned by gimp_scale_entry_new().
+ *
+ * Returns: the scale_entry's #GtkSpinButton.
+ **/
+#define GIMP_SCALE_ENTRY_SPINBUTTON(adj) \
+ (g_object_get_data (G_OBJECT (adj), "spinbutton"))
+
+/**
+ * GIMP_SCALE_ENTRY_SPINBUTTON_ADJ:
+ * @adj: The #GtkAdjustment returned by gimp_scale_entry_new().
+ *
+ * Returns: the #GtkAdjustment of the scale_entry's #GtkSpinButton.
+ **/
+#define GIMP_SCALE_ENTRY_SPINBUTTON_ADJ(adj) \
+ gtk_spin_button_get_adjustment \
+ (GTK_SPIN_BUTTON (g_object_get_data (G_OBJECT (adj), "spinbutton")))
+
+
+GtkObject * gimp_scale_entry_new (GtkTable *table,
+ gint column,
+ gint row,
+ const gchar *text,
+ gint scale_width,
+ gint spinbutton_width,
+ gdouble value,
+ gdouble lower,
+ gdouble upper,
+ gdouble step_increment,
+ gdouble page_increment,
+ guint digits,
+ gboolean constrain,
+ gdouble unconstrained_lower,
+ gdouble unconstrained_upper,
+ const gchar *tooltip,
+ const gchar *help_id);
+
+GtkObject * gimp_color_scale_entry_new (GtkTable *table,
+ gint column,
+ gint row,
+ const gchar *text,
+ gint scale_width,
+ gint spinbutton_width,
+ gdouble value,
+ gdouble lower,
+ gdouble upper,
+ gdouble step_increment,
+ gdouble page_increment,
+ guint digits,
+ const gchar *tooltip,
+ const gchar *help_id);
+
+void gimp_scale_entry_set_sensitive (GtkObject *adjustment,
+ gboolean sensitive);
+
+void gimp_scale_entry_set_logarithmic (GtkObject *adjustment,
+ gboolean logarithmic);
+gboolean gimp_scale_entry_get_logarithmic (GtkObject *adjustment);
+
+
+
+G_END_DECLS
+
+#endif /* __GIMP_SCALE_ENTRY_H__ */
diff --git a/libgimpwidgets/gimpscrolledpreview.c b/libgimpwidgets/gimpscrolledpreview.c
new file mode 100644
index 0000000..db2426e
--- /dev/null
+++ b/libgimpwidgets/gimpscrolledpreview.c
@@ -0,0 +1,917 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpscrolledpreview.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "libgimpmath/gimpmath.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpicons.h"
+#include "gimppreviewarea.h"
+#include "gimpscrolledpreview.h"
+#include "gimp3migration.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpscrolledpreview
+ * @title: GimpScrolledPreview
+ * @short_description: A widget providing a #GimpPreview enhanced by
+ * scrolling capabilities.
+ *
+ * A widget providing a #GimpPreview enhanced by scrolling capabilities.
+ **/
+
+
+#define POPUP_SIZE 100
+
+
+typedef struct
+{
+ GtkPolicyType hscr_policy;
+ GtkPolicyType vscr_policy;
+ gint drag_x;
+ gint drag_y;
+ gint drag_xoff;
+ gint drag_yoff;
+ gboolean in_drag;
+ gint frozen;
+} GimpScrolledPreviewPrivate;
+
+#define GIMP_SCROLLED_PREVIEW_GET_PRIVATE(obj) \
+ ((GimpScrolledPreviewPrivate *) ((GimpScrolledPreview *) (obj))->priv)
+
+
+static void gimp_scrolled_preview_class_init (GimpScrolledPreviewClass *klass);
+static void gimp_scrolled_preview_init (GimpScrolledPreview *preview);
+static void gimp_scrolled_preview_dispose (GObject *object);
+
+static void gimp_scrolled_preview_area_realize (GtkWidget *widget,
+ GimpScrolledPreview *preview);
+static void gimp_scrolled_preview_area_unrealize (GtkWidget *widget,
+ GimpScrolledPreview *preview);
+static void gimp_scrolled_preview_area_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation,
+ GimpScrolledPreview *preview);
+static gboolean gimp_scrolled_preview_area_event (GtkWidget *area,
+ GdkEvent *event,
+ GimpScrolledPreview *preview);
+
+static void gimp_scrolled_preview_h_scroll (GtkAdjustment *hadj,
+ GimpPreview *preview);
+static void gimp_scrolled_preview_v_scroll (GtkAdjustment *vadj,
+ GimpPreview *preview);
+
+static gboolean gimp_scrolled_preview_nav_button_press (GtkWidget *widget,
+ GdkEventButton *event,
+ GimpScrolledPreview *preview);
+
+static gboolean gimp_scrolled_preview_nav_popup_event (GtkWidget *widget,
+ GdkEvent *event,
+ GimpScrolledPreview *preview);
+static gboolean gimp_scrolled_preview_nav_popup_expose (GtkWidget *widget,
+ GdkEventExpose *event,
+ GimpScrolledPreview *preview);
+
+static void gimp_scrolled_preview_set_cursor (GimpPreview *preview);
+
+
+static GimpPreviewClass *parent_class = NULL;
+
+
+GType
+gimp_scrolled_preview_get_type (void)
+{
+ static GType preview_type = 0;
+
+ if (! preview_type)
+ {
+ const GTypeInfo preview_info =
+ {
+ sizeof (GimpScrolledPreviewClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gimp_scrolled_preview_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GimpScrolledPreview),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gimp_scrolled_preview_init,
+ };
+
+ preview_type = g_type_register_static (GIMP_TYPE_PREVIEW,
+ "GimpScrolledPreview",
+ &preview_info,
+ G_TYPE_FLAG_ABSTRACT);
+ }
+
+ return preview_type;
+}
+
+static void
+gimp_scrolled_preview_class_init (GimpScrolledPreviewClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpPreviewClass *preview_class = GIMP_PREVIEW_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ object_class->dispose = gimp_scrolled_preview_dispose;
+
+ preview_class->set_cursor = gimp_scrolled_preview_set_cursor;
+
+ g_type_class_add_private (object_class, sizeof (GimpScrolledPreviewPrivate));
+}
+
+static void
+gimp_scrolled_preview_init (GimpScrolledPreview *preview)
+{
+ GimpScrolledPreviewPrivate *priv;
+ GtkWidget *image;
+ GtkAdjustment *adj;
+
+ preview->priv = G_TYPE_INSTANCE_GET_PRIVATE (preview,
+ GIMP_TYPE_SCROLLED_PREVIEW,
+ GimpScrolledPreviewPrivate);
+
+ priv = GIMP_SCROLLED_PREVIEW_GET_PRIVATE (preview);
+
+ preview->nav_popup = NULL;
+
+ priv->hscr_policy = GTK_POLICY_AUTOMATIC;
+ priv->vscr_policy = GTK_POLICY_AUTOMATIC;
+
+ priv->in_drag = FALSE;
+ priv->frozen = 1; /* we are frozen during init */
+
+ /* scrollbars */
+ adj = GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, GIMP_PREVIEW (preview)->width - 1, 1.0,
+ GIMP_PREVIEW (preview)->width,
+ GIMP_PREVIEW (preview)->width));
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_scrolled_preview_h_scroll),
+ preview);
+
+ preview->hscr = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, adj);
+ gtk_table_attach (GTK_TABLE (GIMP_PREVIEW (preview)->table),
+ preview->hscr, 0, 1, 1, 2,
+ GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
+
+ adj = GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, GIMP_PREVIEW (preview)->height - 1, 1.0,
+ GIMP_PREVIEW (preview)->height,
+ GIMP_PREVIEW (preview)->height));
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_scrolled_preview_v_scroll),
+ preview);
+
+ preview->vscr = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, adj);
+ gtk_table_attach (GTK_TABLE (GIMP_PREVIEW (preview)->table),
+ preview->vscr, 1, 2, 0, 1,
+ GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+
+ /* Connect after here so that plug-ins get a chance to override the
+ * default behavior. See bug #364432.
+ */
+ g_signal_connect_after (GIMP_PREVIEW (preview)->area, "event",
+ G_CALLBACK (gimp_scrolled_preview_area_event),
+ preview);
+
+ g_signal_connect (GIMP_PREVIEW (preview)->area, "realize",
+ G_CALLBACK (gimp_scrolled_preview_area_realize),
+ preview);
+ g_signal_connect (GIMP_PREVIEW (preview)->area, "unrealize",
+ G_CALLBACK (gimp_scrolled_preview_area_unrealize),
+ preview);
+
+ g_signal_connect (GIMP_PREVIEW (preview)->area, "size-allocate",
+ G_CALLBACK (gimp_scrolled_preview_area_size_allocate),
+ preview);
+
+ /* navigation icon */
+ preview->nav_icon = gtk_event_box_new ();
+ gtk_table_attach (GTK_TABLE (GIMP_PREVIEW(preview)->table),
+ preview->nav_icon, 1,2, 1,2,
+ GTK_SHRINK, GTK_SHRINK, 0, 0);
+
+ image = gtk_image_new_from_icon_name (GIMP_ICON_DIALOG_NAVIGATION,
+ GTK_ICON_SIZE_MENU);
+ gtk_container_add (GTK_CONTAINER (preview->nav_icon), image);
+ gtk_widget_show (image);
+
+ g_signal_connect (preview->nav_icon, "button-press-event",
+ G_CALLBACK (gimp_scrolled_preview_nav_button_press),
+ preview);
+
+ priv->frozen = 0; /* thaw without actually calling draw/invalidate */
+}
+
+static void
+gimp_scrolled_preview_dispose (GObject *object)
+{
+ GimpScrolledPreview *preview = GIMP_SCROLLED_PREVIEW (object);
+
+ if (preview->nav_popup)
+ {
+ gtk_widget_destroy (preview->nav_popup);
+ preview->nav_popup = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gimp_scrolled_preview_area_realize (GtkWidget *widget,
+ GimpScrolledPreview *preview)
+{
+ GdkDisplay *display = gtk_widget_get_display (widget);
+
+ g_return_if_fail (preview->cursor_move == NULL);
+
+ preview->cursor_move = gdk_cursor_new_for_display (display, GDK_HAND1);
+}
+
+static void
+gimp_scrolled_preview_area_unrealize (GtkWidget *widget,
+ GimpScrolledPreview *preview)
+{
+ if (preview->cursor_move)
+ {
+ gdk_cursor_unref (preview->cursor_move);
+ preview->cursor_move = NULL;
+ }
+}
+
+static void
+gimp_scrolled_preview_hscr_update (GimpScrolledPreview *preview)
+{
+ GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (preview->hscr));
+ gint width;
+
+ width = GIMP_PREVIEW (preview)->xmax - GIMP_PREVIEW (preview)->xmin;
+
+ gtk_adjustment_configure (adj,
+ gtk_adjustment_get_value (adj),
+ 0, width,
+ 1.0,
+ MAX (GIMP_PREVIEW (preview)->width / 2.0, 1.0),
+ GIMP_PREVIEW (preview)->width);
+}
+
+static void
+gimp_scrolled_preview_vscr_update (GimpScrolledPreview *preview)
+{
+ GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (preview->vscr));
+ gint height;
+
+ height = GIMP_PREVIEW (preview)->ymax - GIMP_PREVIEW (preview)->ymin;
+
+ gtk_adjustment_configure (adj,
+ gtk_adjustment_get_value (adj),
+ 0, height,
+ 1.0,
+ MAX (GIMP_PREVIEW (preview)->height / 2.0, 1.0),
+ GIMP_PREVIEW (preview)->height);
+}
+
+static void
+gimp_scrolled_preview_area_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation,
+ GimpScrolledPreview *preview)
+{
+ GimpScrolledPreviewPrivate *priv = GIMP_SCROLLED_PREVIEW_GET_PRIVATE (preview);
+
+ gint width = GIMP_PREVIEW (preview)->xmax - GIMP_PREVIEW (preview)->xmin;
+ gint height = GIMP_PREVIEW (preview)->ymax - GIMP_PREVIEW (preview)->ymin;
+
+ gimp_scrolled_preview_freeze (preview);
+
+ GIMP_PREVIEW (preview)->width = MIN (width, allocation->width);
+ GIMP_PREVIEW (preview)->height = MIN (height, allocation->height);
+
+ gimp_scrolled_preview_hscr_update (preview);
+
+ switch (priv->hscr_policy)
+ {
+ case GTK_POLICY_AUTOMATIC:
+ gtk_widget_set_visible (preview->hscr,
+ width > GIMP_PREVIEW (preview)->width);
+ break;
+
+ case GTK_POLICY_ALWAYS:
+ gtk_widget_show (preview->hscr);
+ break;
+
+ case GTK_POLICY_NEVER:
+ gtk_widget_hide (preview->hscr);
+ break;
+ }
+
+ gimp_scrolled_preview_vscr_update (preview);
+
+ switch (priv->vscr_policy)
+ {
+ case GTK_POLICY_AUTOMATIC:
+ gtk_widget_set_visible (preview->vscr,
+ height > GIMP_PREVIEW (preview)->height);
+ break;
+
+ case GTK_POLICY_ALWAYS:
+ gtk_widget_show (preview->vscr);
+ break;
+
+ case GTK_POLICY_NEVER:
+ gtk_widget_hide (preview->vscr);
+ break;
+ }
+
+ gtk_widget_set_visible (preview->nav_icon,
+ gtk_widget_get_visible (preview->vscr) &&
+ gtk_widget_get_visible (preview->hscr) &&
+ GIMP_PREVIEW_GET_CLASS (preview)->draw_thumb);
+
+ gimp_scrolled_preview_thaw (preview);
+}
+
+static gboolean
+gimp_scrolled_preview_area_event (GtkWidget *area,
+ GdkEvent *event,
+ GimpScrolledPreview *preview)
+{
+ GimpScrolledPreviewPrivate *priv = GIMP_SCROLLED_PREVIEW_GET_PRIVATE (preview);
+ GdkEventButton *button_event = (GdkEventButton *) event;
+ GdkCursor *cursor;
+
+ switch (event->type)
+ {
+ case GDK_BUTTON_PRESS:
+ switch (button_event->button)
+ {
+ case 1:
+ case 2:
+ cursor = gdk_cursor_new_for_display (gtk_widget_get_display (area),
+ GDK_FLEUR);
+
+ if (gdk_pointer_grab (gtk_widget_get_window (area), TRUE,
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_BUTTON_MOTION_MASK |
+ GDK_POINTER_MOTION_HINT_MASK,
+ NULL, cursor,
+ gdk_event_get_time (event)) == GDK_GRAB_SUCCESS)
+ {
+ gtk_widget_get_pointer (area, &priv->drag_x, &priv->drag_y);
+
+ priv->drag_xoff = GIMP_PREVIEW (preview)->xoff;
+ priv->drag_yoff = GIMP_PREVIEW (preview)->yoff;
+ priv->in_drag = TRUE;
+ gtk_grab_add (area);
+ }
+
+ gdk_cursor_unref (cursor);
+
+ break;
+
+ case 3:
+ return TRUE;
+ }
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ if (priv->in_drag &&
+ (button_event->button == 1 || button_event->button == 2))
+ {
+ gdk_display_pointer_ungrab (gtk_widget_get_display (area),
+ gdk_event_get_time (event));
+
+ gtk_grab_remove (area);
+ priv->in_drag = FALSE;
+ }
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ if (priv->in_drag)
+ {
+ GdkEventMotion *mevent = (GdkEventMotion *) event;
+ GtkAdjustment *hadj;
+ GtkAdjustment *vadj;
+ gint x, y;
+
+ hadj = gtk_range_get_adjustment (GTK_RANGE (preview->hscr));
+ vadj = gtk_range_get_adjustment (GTK_RANGE (preview->vscr));
+
+ gtk_widget_get_pointer (area, &x, &y);
+
+ x = priv->drag_xoff - (x - priv->drag_x);
+ y = priv->drag_yoff - (y - priv->drag_y);
+
+ x = CLAMP (x,
+ gtk_adjustment_get_lower (hadj),
+ gtk_adjustment_get_upper (hadj) -
+ gtk_adjustment_get_page_size (hadj));
+ y = CLAMP (y,
+ gtk_adjustment_get_lower (vadj),
+ gtk_adjustment_get_upper (vadj) -
+ gtk_adjustment_get_page_size (vadj));
+
+ if (GIMP_PREVIEW (preview)->xoff != x ||
+ GIMP_PREVIEW (preview)->yoff != y)
+ {
+ gtk_adjustment_set_value (hadj, x);
+ gtk_adjustment_set_value (vadj, y);
+
+ gimp_preview_draw (GIMP_PREVIEW (preview));
+ gimp_preview_invalidate (GIMP_PREVIEW (preview));
+ }
+
+ gdk_event_request_motions (mevent);
+ }
+ break;
+
+ case GDK_SCROLL:
+ {
+ GdkEventScroll *sevent = (GdkEventScroll *) event;
+ GdkScrollDirection direction = sevent->direction;
+ GtkAdjustment *adj;
+ gfloat value;
+
+ /* Ctrl-Scroll is reserved for zooming */
+ if (sevent->state & GDK_CONTROL_MASK)
+ return FALSE;
+
+ if (sevent->state & GDK_SHIFT_MASK)
+ switch (direction)
+ {
+ case GDK_SCROLL_UP: direction = GDK_SCROLL_LEFT; break;
+ case GDK_SCROLL_DOWN: direction = GDK_SCROLL_RIGHT; break;
+ case GDK_SCROLL_LEFT: direction = GDK_SCROLL_UP; break;
+ case GDK_SCROLL_RIGHT: direction = GDK_SCROLL_DOWN; break;
+ }
+
+ switch (direction)
+ {
+ case GDK_SCROLL_UP:
+ case GDK_SCROLL_DOWN:
+ default:
+ adj = gtk_range_get_adjustment (GTK_RANGE (preview->vscr));
+ break;
+
+ case GDK_SCROLL_RIGHT:
+ case GDK_SCROLL_LEFT:
+ adj = gtk_range_get_adjustment (GTK_RANGE (preview->hscr));
+ break;
+ }
+
+ value = gtk_adjustment_get_value (adj);
+
+ switch (direction)
+ {
+ case GDK_SCROLL_UP:
+ case GDK_SCROLL_LEFT:
+ value -= gtk_adjustment_get_page_increment (adj) / 2;
+ break;
+
+ case GDK_SCROLL_DOWN:
+ case GDK_SCROLL_RIGHT:
+ value += gtk_adjustment_get_page_increment (adj) / 2;
+ break;
+ }
+
+ gtk_adjustment_set_value (adj,
+ CLAMP (value,
+ gtk_adjustment_get_lower (adj),
+ gtk_adjustment_get_upper (adj) -
+ gtk_adjustment_get_page_size (adj)));
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+gimp_scrolled_preview_h_scroll (GtkAdjustment *hadj,
+ GimpPreview *preview)
+{
+ GimpScrolledPreviewPrivate *priv = GIMP_SCROLLED_PREVIEW_GET_PRIVATE (preview);
+
+ preview->xoff = gtk_adjustment_get_value (hadj);
+
+ gimp_preview_area_set_offsets (GIMP_PREVIEW_AREA (preview->area),
+ preview->xoff, preview->yoff);
+
+ if (! (priv->in_drag || priv->frozen))
+ {
+ gimp_preview_draw (preview);
+ gimp_preview_invalidate (preview);
+ }
+}
+
+static void
+gimp_scrolled_preview_v_scroll (GtkAdjustment *vadj,
+ GimpPreview *preview)
+{
+ GimpScrolledPreviewPrivate *priv = GIMP_SCROLLED_PREVIEW_GET_PRIVATE (preview);
+
+ preview->yoff = gtk_adjustment_get_value (vadj);
+
+ gimp_preview_area_set_offsets (GIMP_PREVIEW_AREA (preview->area),
+ preview->xoff, preview->yoff);
+
+ if (! (priv->in_drag || priv->frozen))
+ {
+ gimp_preview_draw (preview);
+ gimp_preview_invalidate (preview);
+ }
+}
+
+static gboolean
+gimp_scrolled_preview_nav_button_press (GtkWidget *widget,
+ GdkEventButton *event,
+ GimpScrolledPreview *preview)
+{
+ GimpPreview *gimp_preview = GIMP_PREVIEW (preview);
+ GtkAdjustment *adj;
+
+ if (preview->nav_popup)
+ return TRUE;
+
+ if (event->type == GDK_BUTTON_PRESS && event->button == 1)
+ {
+ GtkStyle *style = gtk_widget_get_style (widget);
+ GtkWidget *outer;
+ GtkWidget *inner;
+ GtkWidget *area;
+ GdkCursor *cursor;
+ gint x, y;
+ gdouble h, v;
+
+ preview->nav_popup = gtk_window_new (GTK_WINDOW_POPUP);
+
+ gtk_window_set_screen (GTK_WINDOW (preview->nav_popup),
+ gtk_widget_get_screen (widget));
+
+ outer = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (outer), GTK_SHADOW_OUT);
+ gtk_container_add (GTK_CONTAINER (preview->nav_popup), outer);
+ gtk_widget_show (outer);
+
+ inner = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (inner), GTK_SHADOW_IN);
+ gtk_container_add (GTK_CONTAINER (outer), inner);
+ gtk_widget_show (inner);
+
+ area = g_object_new (GIMP_TYPE_PREVIEW_AREA,
+ "check-size", GIMP_CHECK_SIZE_SMALL_CHECKS,
+ "check-type", GIMP_PREVIEW_AREA (gimp_preview->area)->check_type,
+ NULL);
+
+ gtk_container_add (GTK_CONTAINER (inner), area);
+
+ g_signal_connect (area, "event",
+ G_CALLBACK (gimp_scrolled_preview_nav_popup_event),
+ preview);
+ g_signal_connect_after (area, "expose-event",
+ G_CALLBACK (gimp_scrolled_preview_nav_popup_expose),
+ preview);
+
+ GIMP_PREVIEW_GET_CLASS (preview)->draw_thumb (gimp_preview,
+ GIMP_PREVIEW_AREA (area),
+ POPUP_SIZE, POPUP_SIZE);
+ gtk_widget_realize (area);
+ gtk_widget_show (area);
+
+ gdk_window_get_origin (gtk_widget_get_window (widget), &x, &y);
+
+ adj = gtk_range_get_adjustment (GTK_RANGE (preview->hscr));
+ h = ((gtk_adjustment_get_value (adj) /
+ gtk_adjustment_get_upper (adj)) +
+ (gtk_adjustment_get_page_size (adj) /
+ gtk_adjustment_get_upper (adj)) / 2.0);
+
+ adj = gtk_range_get_adjustment (GTK_RANGE (preview->vscr));
+ v = ((gtk_adjustment_get_value (adj) /
+ gtk_adjustment_get_upper (adj)) +
+ (gtk_adjustment_get_page_size (adj) /
+ gtk_adjustment_get_upper (adj)) / 2.0);
+
+ x += event->x - h * (gdouble) GIMP_PREVIEW_AREA (area)->width;
+ y += event->y - v * (gdouble) GIMP_PREVIEW_AREA (area)->height;
+
+ gtk_window_move (GTK_WINDOW (preview->nav_popup),
+ x - 2 * style->xthickness,
+ y - 2 * style->ythickness);
+
+ gtk_widget_show (preview->nav_popup);
+
+ gtk_grab_add (area);
+
+ cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
+ GDK_FLEUR);
+
+ gdk_pointer_grab (gtk_widget_get_window (area), TRUE,
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_BUTTON_MOTION_MASK |
+ GDK_POINTER_MOTION_HINT_MASK,
+ gtk_widget_get_window (area), cursor,
+ event->time);
+
+ gdk_cursor_unref (cursor);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gimp_scrolled_preview_nav_popup_event (GtkWidget *widget,
+ GdkEvent *event,
+ GimpScrolledPreview *preview)
+{
+ switch (event->type)
+ {
+ case GDK_BUTTON_RELEASE:
+ {
+ GdkEventButton *button_event = (GdkEventButton *) event;
+
+ if (button_event->button == 1)
+ {
+ gtk_grab_remove (widget);
+ gdk_display_pointer_ungrab (gtk_widget_get_display (widget),
+ button_event->time);
+
+ gtk_widget_destroy (preview->nav_popup);
+ preview->nav_popup = NULL;
+ }
+ }
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ {
+ GdkEventMotion *mevent = (GdkEventMotion *) event;
+ GtkAdjustment *hadj;
+ GtkAdjustment *vadj;
+ GtkAllocation allocation;
+ gint cx, cy;
+ gdouble x, y;
+
+ hadj = gtk_range_get_adjustment (GTK_RANGE (preview->hscr));
+ vadj = gtk_range_get_adjustment (GTK_RANGE (preview->vscr));
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ gtk_widget_get_pointer (widget, &cx, &cy);
+
+ x = cx * (gtk_adjustment_get_upper (hadj) -
+ gtk_adjustment_get_lower (hadj)) / allocation.width;
+ y = cy * (gtk_adjustment_get_upper (vadj) -
+ gtk_adjustment_get_lower (vadj)) / allocation.height;
+
+ x += (gtk_adjustment_get_lower (hadj) -
+ gtk_adjustment_get_page_size (hadj) / 2);
+ y += (gtk_adjustment_get_lower (vadj) -
+ gtk_adjustment_get_page_size (vadj) / 2);
+
+ gtk_adjustment_set_value (hadj,
+ CLAMP (x,
+ gtk_adjustment_get_lower (hadj),
+ gtk_adjustment_get_upper (hadj) -
+ gtk_adjustment_get_page_size (hadj)));
+ gtk_adjustment_set_value (vadj,
+ CLAMP (y,
+ gtk_adjustment_get_lower (vadj),
+ gtk_adjustment_get_upper (vadj) -
+ gtk_adjustment_get_page_size (vadj)));
+
+ gtk_widget_queue_draw (widget);
+ gdk_window_process_updates (gtk_widget_get_window (widget), FALSE);
+
+ gdk_event_request_motions (mevent);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gimp_scrolled_preview_nav_popup_expose (GtkWidget *widget,
+ GdkEventExpose *event,
+ GimpScrolledPreview *preview)
+{
+ GtkAdjustment *adj;
+ GtkAllocation allocation;
+ cairo_t *cr;
+ gdouble x, y;
+ gdouble w, h;
+
+ adj = gtk_range_get_adjustment (GTK_RANGE (preview->hscr));
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ x = (gtk_adjustment_get_value (adj) /
+ (gtk_adjustment_get_upper (adj) -
+ gtk_adjustment_get_lower (adj)));
+ w = (gtk_adjustment_get_page_size (adj) /
+ (gtk_adjustment_get_upper (adj) -
+ gtk_adjustment_get_lower (adj)));
+
+ adj = gtk_range_get_adjustment (GTK_RANGE (preview->vscr));
+
+ y = (gtk_adjustment_get_value (adj) /
+ (gtk_adjustment_get_upper (adj) -
+ gtk_adjustment_get_lower (adj)));
+ h = (gtk_adjustment_get_page_size (adj) /
+ (gtk_adjustment_get_upper (adj) -
+ gtk_adjustment_get_lower (adj)));
+
+ if (w >= 1.0 && h >= 1.0)
+ return FALSE;
+
+ x = floor (x * (gdouble) allocation.width);
+ y = floor (y * (gdouble) allocation.height);
+ w = MAX (1, ceil (w * (gdouble) allocation.width));
+ h = MAX (1, ceil (h * (gdouble) allocation.height));
+
+ cr = gdk_cairo_create (gtk_widget_get_window (widget));
+
+ gdk_cairo_region (cr, event->region);
+ cairo_clip (cr);
+
+ cairo_rectangle (cr,
+ 0, 0, allocation.width, allocation.height);
+
+ cairo_rectangle (cr, x, y, w, h);
+
+ cairo_set_source_rgba (cr, 0, 0, 0, 0.5);
+ cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+ cairo_fill (cr);
+
+ cairo_rectangle (cr, x, y, w, h);
+
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_set_line_width (cr, 2);
+ cairo_stroke (cr);
+
+ cairo_destroy (cr);
+
+ return FALSE;
+}
+
+static void
+gimp_scrolled_preview_set_cursor (GimpPreview *preview)
+{
+ if (! gtk_widget_get_realized (preview->area))
+ return;
+
+ if (preview->xmax - preview->xmin > preview->width ||
+ preview->ymax - preview->ymin > preview->height)
+ {
+ gdk_window_set_cursor (gtk_widget_get_window (preview->area),
+ GIMP_SCROLLED_PREVIEW (preview)->cursor_move);
+ }
+ else
+ {
+ gdk_window_set_cursor (gtk_widget_get_window (preview->area),
+ preview->default_cursor);
+ }
+}
+
+/**
+ * gimp_scrolled_preview_set_position:
+ * @preview: a #GimpScrolledPreview
+ * @x: horizontal scroll offset
+ * @y: vertical scroll offset
+ *
+ * Since: 2.4
+ **/
+void
+gimp_scrolled_preview_set_position (GimpScrolledPreview *preview,
+ gint x,
+ gint y)
+{
+ GtkAdjustment *adj;
+
+ g_return_if_fail (GIMP_IS_SCROLLED_PREVIEW (preview));
+
+ gimp_scrolled_preview_freeze (preview);
+
+ gimp_scrolled_preview_hscr_update (preview);
+ gimp_scrolled_preview_vscr_update (preview);
+
+ adj = gtk_range_get_adjustment (GTK_RANGE (preview->hscr));
+ gtk_adjustment_set_value (adj, x - GIMP_PREVIEW (preview)->xmin);
+
+ adj = gtk_range_get_adjustment (GTK_RANGE (preview->vscr));
+ gtk_adjustment_set_value (adj, y - GIMP_PREVIEW (preview)->ymin);
+
+ gimp_scrolled_preview_thaw (preview);
+}
+
+/**
+ * gimp_scrolled_preview_set_policy
+ * @preview: a #GimpScrolledPreview
+ * @hscrollbar_policy: policy for horizontal scrollbar
+ * @vscrollbar_policy: policy for vertical scrollbar
+ *
+ * Since: 2.4
+ **/
+void
+gimp_scrolled_preview_set_policy (GimpScrolledPreview *preview,
+ GtkPolicyType hscrollbar_policy,
+ GtkPolicyType vscrollbar_policy)
+{
+ GimpScrolledPreviewPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_SCROLLED_PREVIEW (preview));
+
+ priv = GIMP_SCROLLED_PREVIEW_GET_PRIVATE (preview);
+
+ priv->hscr_policy = hscrollbar_policy;
+ priv->vscr_policy = vscrollbar_policy;
+
+ gtk_widget_queue_resize (GIMP_PREVIEW (preview)->area);
+}
+
+
+/**
+ * gimp_scrolled_preview_freeze:
+ * @preview: a #GimpScrolledPreview
+ *
+ * While the @preview is frozen, it is not going to redraw itself in
+ * response to scroll events.
+ *
+ * This function should only be used to implement widgets derived from
+ * #GimpScrolledPreview. There is no point in calling this from a plug-in.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_scrolled_preview_freeze (GimpScrolledPreview *preview)
+{
+ GimpScrolledPreviewPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_SCROLLED_PREVIEW (preview));
+
+ priv = GIMP_SCROLLED_PREVIEW_GET_PRIVATE (preview);
+
+ priv->frozen++;
+}
+
+/**
+ * gimp_scrolled_preview_thaw:
+ * @preview: a #GimpScrolledPreview
+ *
+ * While the @preview is frozen, it is not going to redraw itself in
+ * response to scroll events.
+ *
+ * This function should only be used to implement widgets derived from
+ * #GimpScrolledPreview. There is no point in calling this from a plug-in.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_scrolled_preview_thaw (GimpScrolledPreview *preview)
+{
+ GimpScrolledPreviewPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_SCROLLED_PREVIEW (preview));
+
+ priv = GIMP_SCROLLED_PREVIEW_GET_PRIVATE (preview);
+
+ g_return_if_fail (priv->frozen > 0);
+
+ priv->frozen--;
+
+ if (! priv->frozen)
+ {
+ gimp_preview_draw (GIMP_PREVIEW (preview));
+ gimp_preview_invalidate (GIMP_PREVIEW (preview));
+ }
+}
diff --git a/libgimpwidgets/gimpscrolledpreview.h b/libgimpwidgets/gimpscrolledpreview.h
new file mode 100644
index 0000000..92a8087
--- /dev/null
+++ b/libgimpwidgets/gimpscrolledpreview.h
@@ -0,0 +1,90 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpscrolledpreview.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_SCROLLED_PREVIEW_H__
+#define __GIMP_SCROLLED_PREVIEW_H__
+
+#include "gimppreview.h"
+
+G_BEGIN_DECLS
+
+
+/* For information look into the C source or the html documentation */
+
+
+#define GIMP_TYPE_SCROLLED_PREVIEW (gimp_scrolled_preview_get_type ())
+#define GIMP_SCROLLED_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_SCROLLED_PREVIEW, GimpScrolledPreview))
+#define GIMP_SCROLLED_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_SCROLLED_PREVIEW, GimpScrolledPreviewClass))
+#define GIMP_IS_SCROLLED_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_SCROLLED_PREVIEW))
+#define GIMP_IS_SCROLLED_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_SCROLLED_PREVIEW))
+#define GIMP_SCROLLED_PREVIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_SCROLLED_PREVIEW, GimpScrolledPreviewClass))
+
+
+typedef struct _GimpScrolledPreviewClass GimpScrolledPreviewClass;
+
+struct _GimpScrolledPreview
+{
+ GimpPreview parent_instance;
+
+ /*< protected >*/
+ GtkWidget *hscr;
+ GtkWidget *vscr;
+ GtkWidget *nav_icon;
+ GtkWidget *nav_popup;
+ GdkCursor *cursor_move;
+ gpointer nav_gc; /* unused */
+
+ /*< private >*/
+ gpointer priv;
+};
+
+struct _GimpScrolledPreviewClass
+{
+ GimpPreviewClass parent_class;
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_scrolled_preview_get_type (void) G_GNUC_CONST;
+
+void gimp_scrolled_preview_set_position (GimpScrolledPreview *preview,
+ gint x,
+ gint y);
+void gimp_scrolled_preview_set_policy (GimpScrolledPreview *preview,
+ GtkPolicyType hscrollbar_policy,
+ GtkPolicyType vscrollbar_policy);
+
+/* only for use from derived widgets */
+void gimp_scrolled_preview_freeze (GimpScrolledPreview *preview);
+void gimp_scrolled_preview_thaw (GimpScrolledPreview *preview);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_SCROLLED_PREVIEW_H__ */
diff --git a/libgimpwidgets/gimpsizeentry.c b/libgimpwidgets/gimpsizeentry.c
new file mode 100644
index 0000000..497899b
--- /dev/null
+++ b/libgimpwidgets/gimpsizeentry.c
@@ -0,0 +1,1594 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpsizeentry.c
+ * Copyright (C) 1999-2000 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "gimpwidgets.h"
+
+#include "gimpeevl.h"
+#include "gimpsizeentry.h"
+
+
+/**
+ * SECTION: gimpsizeentry
+ * @title: GimpSizeEntry
+ * @short_description: Widget for entering pixel values and resolutions.
+ * @see_also: #GimpUnit, #GimpUnitComboBox, gimp_coordinates_new()
+ *
+ * This widget is used to enter pixel distances/sizes and resolutions.
+ *
+ * You can specify the number of fields the widget should provide. For
+ * each field automatic mappings are performed between the field's
+ * "reference value" and its "value".
+ *
+ * There is a #GimpUnitComboBox right of the entry fields which lets
+ * you specify the #GimpUnit of the displayed values.
+ *
+ * For each field, there can be one or two #GtkSpinButton's to enter
+ * "value" and "reference value". If you specify @show_refval as
+ * #FALSE in gimp_size_entry_new() there will be only one
+ * #GtkSpinButton and the #GimpUnitComboBox will contain an item for
+ * selecting GIMP_UNIT_PIXEL.
+ *
+ * The "reference value" is either of GIMP_UNIT_PIXEL or dpi,
+ * depending on which #GimpSizeEntryUpdatePolicy you specify in
+ * gimp_size_entry_new(). The "value" is either the size in pixels
+ * mapped to the size in a real-world-unit (see #GimpUnit) or the dpi
+ * value mapped to pixels per real-world-unit.
+ **/
+
+
+#define SIZE_MAX_VALUE 500000.0
+
+#define GIMP_SIZE_ENTRY_DIGITS(unit) (MIN (gimp_unit_get_digits (unit), 5) + 1)
+
+
+enum
+{
+ VALUE_CHANGED,
+ REFVAL_CHANGED,
+ UNIT_CHANGED,
+ LAST_SIGNAL
+};
+
+
+struct _GimpSizeEntryField
+{
+ GimpSizeEntry *gse;
+
+ gdouble resolution;
+ gdouble lower;
+ gdouble upper;
+
+ GtkAdjustment *value_adjustment;
+ GtkWidget *value_spinbutton;
+ gdouble value;
+ gdouble min_value;
+ gdouble max_value;
+
+ GtkAdjustment *refval_adjustment;
+ GtkWidget *refval_spinbutton;
+ gdouble refval;
+ gdouble min_refval;
+ gdouble max_refval;
+ gint refval_digits;
+
+ gint stop_recursion;
+};
+
+
+static void gimp_size_entry_finalize (GObject *object);
+static void gimp_size_entry_update_value (GimpSizeEntryField *gsef,
+ gdouble value);
+static void gimp_size_entry_value_callback (GtkWidget *widget,
+ gpointer data);
+static void gimp_size_entry_update_refval (GimpSizeEntryField *gsef,
+ gdouble refval);
+static void gimp_size_entry_refval_callback (GtkWidget *widget,
+ gpointer data);
+static void gimp_size_entry_update_unit (GimpSizeEntry *gse,
+ GimpUnit unit);
+static void gimp_size_entry_unit_callback (GtkWidget *widget,
+ GimpSizeEntry *sizeentry);
+static void gimp_size_entry_attach_eevl (GtkSpinButton *spin_button,
+ GimpSizeEntryField *gsef);
+static gint gimp_size_entry_eevl_input_callback (GtkSpinButton *spinner,
+ gdouble *return_val,
+ gpointer *data);
+static gboolean gimp_size_entry_eevl_unit_resolver (const gchar *ident,
+ GimpEevlQuantity *factor,
+ gdouble *offset,
+ gpointer data);
+
+
+G_DEFINE_TYPE (GimpSizeEntry, gimp_size_entry, GTK_TYPE_TABLE)
+
+#define parent_class gimp_size_entry_parent_class
+
+static guint gimp_size_entry_signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+gimp_size_entry_class_init (GimpSizeEntryClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ gimp_size_entry_signals[VALUE_CHANGED] =
+ g_signal_new ("value-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpSizeEntryClass, value_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ gimp_size_entry_signals[REFVAL_CHANGED] =
+ g_signal_new ("refval-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpSizeEntryClass, refval_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ gimp_size_entry_signals[UNIT_CHANGED] =
+ g_signal_new ("unit-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpSizeEntryClass, unit_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ object_class->finalize = gimp_size_entry_finalize;
+
+ klass->value_changed = NULL;
+ klass->refval_changed = NULL;
+ klass->unit_changed = NULL;
+}
+
+static void
+gimp_size_entry_init (GimpSizeEntry *gse)
+{
+ gse->fields = NULL;
+ gse->number_of_fields = 0;
+ gse->unitmenu = NULL;
+ gse->unit = GIMP_UNIT_PIXEL;
+ gse->menu_show_pixels = TRUE;
+ gse->menu_show_percent = TRUE;
+ gse->show_refval = FALSE;
+ gse->update_policy = GIMP_SIZE_ENTRY_UPDATE_NONE;
+}
+
+static void
+gimp_size_entry_finalize (GObject *object)
+{
+ GimpSizeEntry *gse = GIMP_SIZE_ENTRY (object);
+
+ if (gse->fields)
+ {
+ GSList *list;
+
+ for (list = gse->fields; list; list = list->next)
+ g_slice_free (GimpSizeEntryField, list->data);
+
+ g_slist_free (gse->fields);
+ gse->fields = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+/**
+ * gimp_size_entry_new:
+ * @number_of_fields: The number of input fields.
+ * @unit: The initial unit.
+ * @unit_format: A printf-like unit-format string as is used with
+ * gimp_unit_menu_new().
+ * @menu_show_pixels: %TRUE if the unit menu should contain an item for
+ * GIMP_UNIT_PIXEL (ignored if the @update_policy is not
+ * GIMP_SIZE_ENTRY_UPDATE_NONE).
+ * @menu_show_percent: %TRUE if the unit menu should contain an item for
+ * GIMP_UNIT_PERCENT.
+ * @show_refval: %TRUE if you want an extra "reference value"
+ * spinbutton per input field.
+ * @spinbutton_width: The minimal horizontal size of the #GtkSpinButton's.
+ * @update_policy: How the automatic pixel <-> real-world-unit
+ * calculations should be done.
+ *
+ * Creates a new #GimpSizeEntry widget.
+ *
+ * To have all automatic calculations performed correctly, set up the
+ * widget in the following order:
+ *
+ * 1. gimp_size_entry_new()
+ *
+ * 2. (for each additional input field) gimp_size_entry_add_field()
+ *
+ * 3. gimp_size_entry_set_unit()
+ *
+ * For each input field:
+ *
+ * 4. gimp_size_entry_set_resolution()
+ *
+ * 5. gimp_size_entry_set_refval_boundaries()
+ * (or gimp_size_entry_set_value_boundaries())
+ *
+ * 6. gimp_size_entry_set_size()
+ *
+ * 7. gimp_size_entry_set_refval() (or gimp_size_entry_set_value())
+ *
+ * The #GimpSizeEntry is derived from #GtkTable and will have
+ * an empty border of one cell width on each side plus an empty column left
+ * of the #GimpUnitComboBox to allow the caller to add labels or a
+ * #GimpChainButton.
+ *
+ * Returns: A Pointer to the new #GimpSizeEntry widget.
+ **/
+GtkWidget *
+gimp_size_entry_new (gint number_of_fields,
+ GimpUnit unit,
+ const gchar *unit_format,
+ gboolean menu_show_pixels,
+ gboolean menu_show_percent,
+ gboolean show_refval,
+ gint spinbutton_width,
+ GimpSizeEntryUpdatePolicy update_policy)
+{
+ GimpSizeEntry *gse;
+ GimpUnitStore *store;
+ gint i;
+
+ g_return_val_if_fail ((number_of_fields >= 0) && (number_of_fields <= 16),
+ NULL);
+
+ gse = g_object_new (GIMP_TYPE_SIZE_ENTRY, NULL);
+
+ gse->number_of_fields = number_of_fields;
+ gse->unit = unit;
+ gse->show_refval = show_refval;
+ gse->update_policy = update_policy;
+
+ gtk_table_resize (GTK_TABLE (gse),
+ 1 + gse->show_refval + 2,
+ number_of_fields + 1 + 3);
+
+ /* show the 'pixels' menu entry only if we are a 'size' sizeentry and
+ * don't have the reference value spinbutton
+ */
+ if ((update_policy == GIMP_SIZE_ENTRY_UPDATE_RESOLUTION) ||
+ (show_refval == TRUE))
+ gse->menu_show_pixels = FALSE;
+ else
+ gse->menu_show_pixels = menu_show_pixels;
+
+ /* show the 'percent' menu entry only if we are a 'size' sizeentry
+ */
+ if (update_policy == GIMP_SIZE_ENTRY_UPDATE_RESOLUTION)
+ gse->menu_show_percent = FALSE;
+ else
+ gse->menu_show_percent = menu_show_percent;
+
+ for (i = 0; i < number_of_fields; i++)
+ {
+ GimpSizeEntryField *gsef = g_slice_new0 (GimpSizeEntryField);
+ gint digits;
+
+ gse->fields = g_slist_append (gse->fields, gsef);
+
+ gsef->gse = gse;
+ gsef->resolution = 1.0; /* just to avoid division by zero */
+ gsef->lower = 0.0;
+ gsef->upper = 100.0;
+ gsef->value = 0;
+ gsef->min_value = 0;
+ gsef->max_value = SIZE_MAX_VALUE;
+ gsef->refval_adjustment = NULL;
+ gsef->value_adjustment = NULL;
+ gsef->refval = 0;
+ gsef->min_refval = 0;
+ gsef->max_refval = SIZE_MAX_VALUE;
+ gsef->refval_digits =
+ (update_policy == GIMP_SIZE_ENTRY_UPDATE_SIZE) ? 0 : 3;
+ gsef->stop_recursion = 0;
+
+ digits = ((unit == GIMP_UNIT_PIXEL) ?
+ gsef->refval_digits : ((unit == GIMP_UNIT_PERCENT) ?
+ 2 : GIMP_SIZE_ENTRY_DIGITS (unit)));
+
+ gsef->value_adjustment = (GtkAdjustment *)
+ gtk_adjustment_new (gsef->value,
+ gsef->min_value, gsef->max_value,
+ 1.0, 10.0, 0.0);
+ gsef->value_spinbutton = gimp_spin_button_new (gsef->value_adjustment,
+ 1.0, digits);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (gsef->value_spinbutton),
+ TRUE);
+
+ gimp_size_entry_attach_eevl (GTK_SPIN_BUTTON (gsef->value_spinbutton),
+ gsef);
+
+ if (spinbutton_width > 0)
+ {
+ if (spinbutton_width < 17)
+ gtk_entry_set_width_chars (GTK_ENTRY (gsef->value_spinbutton),
+ spinbutton_width);
+ else
+ gtk_widget_set_size_request (gsef->value_spinbutton,
+ spinbutton_width, -1);
+ }
+
+ gtk_table_attach_defaults (GTK_TABLE (gse), gsef->value_spinbutton,
+ i+1, i+2,
+ gse->show_refval+1, gse->show_refval+2);
+ g_signal_connect (gsef->value_adjustment, "value-changed",
+ G_CALLBACK (gimp_size_entry_value_callback),
+ gsef);
+
+ gtk_widget_show (gsef->value_spinbutton);
+
+ if (gse->show_refval)
+ {
+ gsef->refval_adjustment = (GtkAdjustment *)
+ gtk_adjustment_new (gsef->refval,
+ gsef->min_refval, gsef->max_refval,
+ 1.0, 10.0, 0.0);
+ gsef->refval_spinbutton = gimp_spin_button_new (gsef->refval_adjustment,
+ 1.0,
+ gsef->refval_digits);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (gsef->refval_spinbutton),
+ TRUE);
+
+ gtk_widget_set_size_request (gsef->refval_spinbutton,
+ spinbutton_width, -1);
+ gtk_table_attach_defaults (GTK_TABLE (gse), gsef->refval_spinbutton,
+ i + 1, i + 2, 1, 2);
+ g_signal_connect (gsef->refval_adjustment,
+ "value-changed",
+ G_CALLBACK (gimp_size_entry_refval_callback),
+ gsef);
+
+ gtk_widget_show (gsef->refval_spinbutton);
+ }
+
+ if (gse->menu_show_pixels && (unit == GIMP_UNIT_PIXEL) &&
+ ! gse->show_refval)
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (gsef->value_spinbutton),
+ gsef->refval_digits);
+ }
+
+ store = gimp_unit_store_new (gse->number_of_fields);
+ gimp_unit_store_set_has_pixels (store, gse->menu_show_pixels);
+ gimp_unit_store_set_has_percent (store, gse->menu_show_percent);
+
+ if (unit_format)
+ {
+ gchar *short_format = g_strdup (unit_format);
+ gchar *p;
+
+ p = strstr (short_format, "%s");
+ if (p)
+ strcpy (p, "%a");
+
+ p = strstr (short_format, "%p");
+ if (p)
+ strcpy (p, "%a");
+
+ g_object_set (store,
+ "short-format", short_format,
+ "long-format", unit_format,
+ NULL);
+
+ g_free (short_format);
+ }
+
+ gse->unitmenu = gimp_unit_combo_box_new_with_model (store);
+ g_object_unref (store);
+
+ gimp_unit_combo_box_set_active (GIMP_UNIT_COMBO_BOX (gse->unitmenu), unit);
+
+ gtk_table_attach (GTK_TABLE (gse), gse->unitmenu,
+ i+2, i+3,
+ gse->show_refval+1, gse->show_refval+2,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ g_signal_connect (gse->unitmenu, "changed",
+ G_CALLBACK (gimp_size_entry_unit_callback),
+ gse);
+ gtk_widget_show (gse->unitmenu);
+
+ return GTK_WIDGET (gse);
+}
+
+
+/**
+ * gimp_size_entry_add_field:
+ * @gse: The sizeentry you want to add a field to.
+ * @value_spinbutton: The spinbutton to display the field's value.
+ * @refval_spinbutton: The spinbutton to display the field's reference value.
+ *
+ * Adds an input field to the #GimpSizeEntry.
+ *
+ * The new input field will have the index 0. If you specified @show_refval
+ * as %TRUE in gimp_size_entry_new() you have to pass an additional
+ * #GtkSpinButton to hold the reference value. If @show_refval was %FALSE,
+ * @refval_spinbutton will be ignored.
+ **/
+void
+gimp_size_entry_add_field (GimpSizeEntry *gse,
+ GtkSpinButton *value_spinbutton,
+ GtkSpinButton *refval_spinbutton)
+{
+ GimpSizeEntryField *gsef;
+ gint digits;
+
+ g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
+ g_return_if_fail (GTK_IS_SPIN_BUTTON (value_spinbutton));
+
+ if (gse->show_refval)
+ {
+ g_return_if_fail (GTK_IS_SPIN_BUTTON (refval_spinbutton));
+ }
+
+ gsef = g_slice_new0 (GimpSizeEntryField);
+
+ gse->fields = g_slist_prepend (gse->fields, gsef);
+ gse->number_of_fields++;
+
+ gsef->gse = gse;
+ gsef->resolution = 1.0; /* just to avoid division by zero */
+ gsef->lower = 0.0;
+ gsef->upper = 100.0;
+ gsef->value = 0;
+ gsef->min_value = 0;
+ gsef->max_value = SIZE_MAX_VALUE;
+ gsef->refval = 0;
+ gsef->min_refval = 0;
+ gsef->max_refval = SIZE_MAX_VALUE;
+ gsef->refval_digits =
+ (gse->update_policy == GIMP_SIZE_ENTRY_UPDATE_SIZE) ? 0 : 3;
+ gsef->stop_recursion = 0;
+
+ gsef->value_adjustment = gtk_spin_button_get_adjustment (value_spinbutton);
+ gsef->value_spinbutton = GTK_WIDGET (value_spinbutton);
+ g_signal_connect (gsef->value_adjustment, "value-changed",
+ G_CALLBACK (gimp_size_entry_value_callback),
+ gsef);
+
+ gimp_size_entry_attach_eevl (GTK_SPIN_BUTTON (gsef->value_spinbutton),
+ gsef);
+
+ if (gse->show_refval)
+ {
+ gsef->refval_adjustment = gtk_spin_button_get_adjustment (refval_spinbutton);
+ gsef->refval_spinbutton = GTK_WIDGET (refval_spinbutton);
+ g_signal_connect (gsef->refval_adjustment, "value-changed",
+ G_CALLBACK (gimp_size_entry_refval_callback),
+ gsef);
+ }
+
+ digits = ((gse->unit == GIMP_UNIT_PIXEL) ? gsef->refval_digits :
+ (gse->unit == GIMP_UNIT_PERCENT) ? 2 :
+ GIMP_SIZE_ENTRY_DIGITS (gse->unit));
+
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (value_spinbutton), digits);
+
+ if (gse->menu_show_pixels &&
+ !gse->show_refval &&
+ (gse->unit == GIMP_UNIT_PIXEL))
+ {
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (gsef->value_spinbutton),
+ gsef->refval_digits);
+ }
+}
+
+/**
+ * gimp_size_entry_attach_label:
+ * @gse: The sizeentry you want to add a label to.
+ * @text: The text of the label.
+ * @row: The row where the label will be attached.
+ * @column: The column where the label will be attached.
+ * @alignment: The horizontal alignment of the label.
+ *
+ * Attaches a #GtkLabel to the #GimpSizeEntry (which is a #GtkTable).
+ *
+ * Returns: A pointer to the new #GtkLabel widget.
+ **/
+GtkWidget *
+gimp_size_entry_attach_label (GimpSizeEntry *gse,
+ const gchar *text,
+ gint row,
+ gint column,
+ gfloat alignment)
+{
+ GtkWidget *label;
+
+ g_return_val_if_fail (GIMP_IS_SIZE_ENTRY (gse), NULL);
+ g_return_val_if_fail (text != NULL, NULL);
+
+ label = gtk_label_new_with_mnemonic (text);
+
+ if (column == 0)
+ {
+ GList *children;
+ GList *list;
+
+ children = gtk_container_get_children (GTK_CONTAINER (gse));
+
+ for (list = children; list; list = g_list_next (list))
+ {
+ GtkWidget *child = list->data;
+ gint left_attach;
+ gint top_attach;
+
+ gtk_container_child_get (GTK_CONTAINER (gse), child,
+ "left-attach", &left_attach,
+ "top-attach", &top_attach,
+ NULL);
+
+ if (left_attach == 1 && top_attach == row)
+ {
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), child);
+ break;
+ }
+ }
+
+ g_list_free (children);
+ }
+
+ gtk_label_set_xalign (GTK_LABEL (label), alignment);
+
+ gtk_table_attach (GTK_TABLE (gse), label, column, column+1, row, row+1,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ return label;
+}
+
+
+/**
+ * gimp_size_entry_set_resolution:
+ * @gse: The sizeentry you want to set a resolution for.
+ * @field: The index of the field you want to set the resolution for.
+ * @resolution: The new resolution (in dpi) for the chosen @field.
+ * @keep_size: %TRUE if the @field's size in pixels should stay the same.
+ * %FALSE if the @field's size in units should stay the same.
+ *
+ * Sets the resolution (in dpi) for field # @field of the #GimpSizeEntry.
+ *
+ * The @resolution passed will be clamped to fit in
+ * [#GIMP_MIN_RESOLUTION..#GIMP_MAX_RESOLUTION].
+ *
+ * This function does nothing if the #GimpSizeEntryUpdatePolicy specified in
+ * gimp_size_entry_new() doesn't equal to #GIMP_SIZE_ENTRY_UPDATE_SIZE.
+ **/
+void
+gimp_size_entry_set_resolution (GimpSizeEntry *gse,
+ gint field,
+ gdouble resolution,
+ gboolean keep_size)
+{
+ GimpSizeEntryField *gsef;
+ gfloat val;
+
+ g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
+ g_return_if_fail ((field >= 0) && (field < gse->number_of_fields));
+
+ resolution = CLAMP (resolution, GIMP_MIN_RESOLUTION, GIMP_MAX_RESOLUTION);
+
+ gsef = (GimpSizeEntryField*) g_slist_nth_data (gse->fields, field);
+ gsef->resolution = resolution;
+
+ val = gsef->value;
+
+ gsef->stop_recursion = 0;
+ gimp_size_entry_set_refval_boundaries (gse, field,
+ gsef->min_refval, gsef->max_refval);
+
+ if (! keep_size)
+ gimp_size_entry_set_value (gse, field, val);
+}
+
+
+/**
+ * gimp_size_entry_set_size:
+ * @gse: The sizeentry you want to set a size for.
+ * @field: The index of the field you want to set the size for.
+ * @lower: The reference value which will be treated as 0%.
+ * @upper: The reference value which will be treated as 100%.
+ *
+ * Sets the pixel values for field # @field of the #GimpSizeEntry
+ * which will be treated as 0% and 100%.
+ *
+ * These values will be used if you specified @menu_show_percent as %TRUE
+ * in gimp_size_entry_new() and the user has selected GIMP_UNIT_PERCENT in
+ * the #GimpSizeEntry's #GimpUnitComboBox.
+ *
+ * This function does nothing if the #GimpSizeEntryUpdatePolicy specified in
+ * gimp_size_entry_new() doesn't equal to GIMP_SIZE_ENTRY_UPDATE_SIZE.
+ **/
+void
+gimp_size_entry_set_size (GimpSizeEntry *gse,
+ gint field,
+ gdouble lower,
+ gdouble upper)
+{
+ GimpSizeEntryField *gsef;
+
+ g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
+ g_return_if_fail ((field >= 0) && (field < gse->number_of_fields));
+ g_return_if_fail (lower <= upper);
+
+ gsef = (GimpSizeEntryField*) g_slist_nth_data (gse->fields, field);
+ gsef->lower = lower;
+ gsef->upper = upper;
+
+ gimp_size_entry_set_refval (gse, field, gsef->refval);
+}
+
+
+/**
+ * gimp_size_entry_set_value_boundaries:
+ * @gse: The sizeentry you want to set value boundaries for.
+ * @field: The index of the field you want to set value boundaries for.
+ * @lower: The new lower boundary of the value of the chosen @field.
+ * @upper: The new upper boundary of the value of the chosen @field.
+ *
+ * Limits the range of possible values which can be entered in field # @field
+ * of the #GimpSizeEntry.
+ *
+ * The current value of the @field will be clamped to fit in the @field's
+ * new boundaries.
+ *
+ * NOTE: In most cases you won't be interested in this function because the
+ * #GimpSizeEntry's purpose is to shield the programmer from unit
+ * calculations. Use gimp_size_entry_set_refval_boundaries() instead.
+ * Whatever you do, don't mix these calls. A size entry should either
+ * be clamped by the value or the reference value.
+ **/
+void
+gimp_size_entry_set_value_boundaries (GimpSizeEntry *gse,
+ gint field,
+ gdouble lower,
+ gdouble upper)
+{
+ GimpSizeEntryField *gsef;
+
+ g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
+ g_return_if_fail ((field >= 0) && (field < gse->number_of_fields));
+ g_return_if_fail (lower <= upper);
+
+ gsef = (GimpSizeEntryField*) g_slist_nth_data (gse->fields, field);
+ gsef->min_value = lower;
+ gsef->max_value = upper;
+
+ g_object_freeze_notify (G_OBJECT (gsef->value_adjustment));
+
+ gtk_adjustment_set_lower (gsef->value_adjustment, gsef->min_value);
+ gtk_adjustment_set_upper (gsef->value_adjustment, gsef->max_value);
+
+ if (gsef->stop_recursion) /* this is a hack (but useful ;-) */
+ {
+ g_object_thaw_notify (G_OBJECT (gsef->value_adjustment));
+ return;
+ }
+
+ gsef->stop_recursion++;
+ switch (gsef->gse->update_policy)
+ {
+ case GIMP_SIZE_ENTRY_UPDATE_NONE:
+ break;
+
+ case GIMP_SIZE_ENTRY_UPDATE_SIZE:
+ switch (gse->unit)
+ {
+ case GIMP_UNIT_PIXEL:
+ gimp_size_entry_set_refval_boundaries (gse, field,
+ gsef->min_value,
+ gsef->max_value);
+ break;
+ case GIMP_UNIT_PERCENT:
+ gimp_size_entry_set_refval_boundaries (gse, field,
+ gsef->lower +
+ (gsef->upper - gsef->lower) *
+ gsef->min_value / 100,
+ gsef->lower +
+ (gsef->upper - gsef->lower) *
+ gsef->max_value / 100);
+ break;
+ default:
+ gimp_size_entry_set_refval_boundaries (gse, field,
+ gsef->min_value *
+ gsef->resolution /
+ gimp_unit_get_factor (gse->unit),
+ gsef->max_value *
+ gsef->resolution /
+ gimp_unit_get_factor (gse->unit));
+ break;
+ }
+ break;
+
+ case GIMP_SIZE_ENTRY_UPDATE_RESOLUTION:
+ gimp_size_entry_set_refval_boundaries (gse, field,
+ gsef->min_value *
+ gimp_unit_get_factor (gse->unit),
+ gsef->max_value *
+ gimp_unit_get_factor (gse->unit));
+ break;
+
+ default:
+ break;
+ }
+ gsef->stop_recursion--;
+
+ gimp_size_entry_set_value (gse, field, gsef->value);
+
+ g_object_thaw_notify (G_OBJECT (gsef->value_adjustment));
+}
+
+/**
+ * gimp_size_entry_get_value:
+ * @gse: The sizeentry you want to know a value of.
+ * @field: The index of the field you want to know the value of.
+ *
+ * Returns the value of field # @field of the #GimpSizeEntry.
+ *
+ * The @value returned is a distance or resolution
+ * in the #GimpUnit the user has selected in the #GimpSizeEntry's
+ * #GimpUnitComboBox.
+ *
+ * NOTE: In most cases you won't be interested in this value because the
+ * #GimpSizeEntry's purpose is to shield the programmer from unit
+ * calculations. Use gimp_size_entry_get_refval() instead.
+ *
+ * Returns: The value of the chosen @field.
+ **/
+gdouble
+gimp_size_entry_get_value (GimpSizeEntry *gse,
+ gint field)
+{
+ GimpSizeEntryField *gsef;
+
+ g_return_val_if_fail (GIMP_IS_SIZE_ENTRY (gse), 0);
+ g_return_val_if_fail ((field >= 0) && (field < gse->number_of_fields), 0);
+
+ gsef = (GimpSizeEntryField *) g_slist_nth_data (gse->fields, field);
+ return gsef->value;
+}
+
+static void
+gimp_size_entry_update_value (GimpSizeEntryField *gsef,
+ gdouble value)
+{
+ if (gsef->stop_recursion > 1)
+ return;
+
+ gsef->value = value;
+
+ switch (gsef->gse->update_policy)
+ {
+ case GIMP_SIZE_ENTRY_UPDATE_NONE:
+ break;
+
+ case GIMP_SIZE_ENTRY_UPDATE_SIZE:
+ switch (gsef->gse->unit)
+ {
+ case GIMP_UNIT_PIXEL:
+ gsef->refval = value;
+ break;
+ case GIMP_UNIT_PERCENT:
+ gsef->refval =
+ CLAMP (gsef->lower + (gsef->upper - gsef->lower) * value / 100,
+ gsef->min_refval, gsef->max_refval);
+ break;
+ default:
+ gsef->refval =
+ CLAMP (value * gsef->resolution /
+ gimp_unit_get_factor (gsef->gse->unit),
+ gsef->min_refval, gsef->max_refval);
+ break;
+ }
+ if (gsef->gse->show_refval)
+ gtk_adjustment_set_value (gsef->refval_adjustment, gsef->refval);
+ break;
+
+ case GIMP_SIZE_ENTRY_UPDATE_RESOLUTION:
+ gsef->refval =
+ CLAMP (value * gimp_unit_get_factor (gsef->gse->unit),
+ gsef->min_refval, gsef->max_refval);
+ if (gsef->gse->show_refval)
+ gtk_adjustment_set_value (gsef->refval_adjustment, gsef->refval);
+ break;
+
+ default:
+ break;
+ }
+
+ g_signal_emit (gsef->gse, gimp_size_entry_signals[VALUE_CHANGED], 0);
+}
+
+/**
+ * gimp_size_entry_set_value:
+ * @gse: The sizeentry you want to set a value for.
+ * @field: The index of the field you want to set a value for.
+ * @value: The new value for @field.
+ *
+ * Sets the value for field # @field of the #GimpSizeEntry.
+ *
+ * The @value passed is treated to be a distance or resolution
+ * in the #GimpUnit the user has selected in the #GimpSizeEntry's
+ * #GimpUnitComboBox.
+ *
+ * NOTE: In most cases you won't be interested in this value because the
+ * #GimpSizeEntry's purpose is to shield the programmer from unit
+ * calculations. Use gimp_size_entry_set_refval() instead.
+ **/
+void
+gimp_size_entry_set_value (GimpSizeEntry *gse,
+ gint field,
+ gdouble value)
+{
+ GimpSizeEntryField *gsef;
+
+ g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
+ g_return_if_fail ((field >= 0) && (field < gse->number_of_fields));
+
+ gsef = (GimpSizeEntryField *) g_slist_nth_data (gse->fields, field);
+
+ value = CLAMP (value, gsef->min_value, gsef->max_value);
+
+ gtk_adjustment_set_value (gsef->value_adjustment, value);
+ gimp_size_entry_update_value (gsef, value);
+}
+
+
+static void
+gimp_size_entry_value_callback (GtkWidget *widget,
+ gpointer data)
+{
+ GimpSizeEntryField *gsef;
+ gdouble new_value;
+
+ gsef = (GimpSizeEntryField *) data;
+
+ new_value = gtk_adjustment_get_value (GTK_ADJUSTMENT (widget));
+
+ if (gsef->value != new_value)
+ gimp_size_entry_update_value (gsef, new_value);
+}
+
+
+/**
+ * gimp_size_entry_set_refval_boundaries:
+ * @gse: The sizeentry you want to set the reference value boundaries for.
+ * @field: The index of the field you want to set the reference value
+ * boundaries for.
+ * @lower: The new lower boundary of the reference value of the chosen @field.
+ * @upper: The new upper boundary of the reference value of the chosen @field.
+ *
+ * Limits the range of possible reference values which can be entered in
+ * field # @field of the #GimpSizeEntry.
+ *
+ * The current reference value of the @field will be clamped to fit in the
+ * @field's new boundaries.
+ **/
+void
+gimp_size_entry_set_refval_boundaries (GimpSizeEntry *gse,
+ gint field,
+ gdouble lower,
+ gdouble upper)
+{
+ GimpSizeEntryField *gsef;
+
+ g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
+ g_return_if_fail ((field >= 0) && (field < gse->number_of_fields));
+ g_return_if_fail (lower <= upper);
+
+ gsef = (GimpSizeEntryField *) g_slist_nth_data (gse->fields, field);
+ gsef->min_refval = lower;
+ gsef->max_refval = upper;
+
+ if (gse->show_refval)
+ {
+ g_object_freeze_notify (G_OBJECT (gsef->refval_adjustment));
+
+ gtk_adjustment_set_lower (gsef->refval_adjustment, gsef->min_refval);
+ gtk_adjustment_set_upper (gsef->refval_adjustment, gsef->max_refval);
+ }
+
+ if (gsef->stop_recursion) /* this is a hack (but useful ;-) */
+ {
+ if (gse->show_refval)
+ g_object_thaw_notify (G_OBJECT (gsef->refval_adjustment));
+
+ return;
+ }
+
+ gsef->stop_recursion++;
+ switch (gsef->gse->update_policy)
+ {
+ case GIMP_SIZE_ENTRY_UPDATE_NONE:
+ break;
+
+ case GIMP_SIZE_ENTRY_UPDATE_SIZE:
+ switch (gse->unit)
+ {
+ case GIMP_UNIT_PIXEL:
+ gimp_size_entry_set_value_boundaries (gse, field,
+ gsef->min_refval,
+ gsef->max_refval);
+ break;
+ case GIMP_UNIT_PERCENT:
+ gimp_size_entry_set_value_boundaries (gse, field,
+ 100 * (gsef->min_refval -
+ gsef->lower) /
+ (gsef->upper - gsef->lower),
+ 100 * (gsef->max_refval -
+ gsef->lower) /
+ (gsef->upper - gsef->lower));
+ break;
+ default:
+ gimp_size_entry_set_value_boundaries (gse, field,
+ gsef->min_refval *
+ gimp_unit_get_factor (gse->unit) /
+ gsef->resolution,
+ gsef->max_refval *
+ gimp_unit_get_factor (gse->unit) /
+ gsef->resolution);
+ break;
+ }
+ break;
+
+ case GIMP_SIZE_ENTRY_UPDATE_RESOLUTION:
+ gimp_size_entry_set_value_boundaries (gse, field,
+ gsef->min_refval /
+ gimp_unit_get_factor (gse->unit),
+ gsef->max_refval /
+ gimp_unit_get_factor (gse->unit));
+ break;
+
+ default:
+ break;
+ }
+ gsef->stop_recursion--;
+
+ gimp_size_entry_set_refval (gse, field, gsef->refval);
+
+ if (gse->show_refval)
+ g_object_thaw_notify (G_OBJECT (gsef->refval_adjustment));
+}
+
+/**
+ * gimp_size_entry_set_refval_digits:
+ * @gse: The sizeentry you want to set the reference value digits for.
+ * @field: The index of the field you want to set the reference value for.
+ * @digits: The new number of decimal digits for the #GtkSpinButton which
+ * displays @field's reference value.
+ *
+ * Sets the decimal digits of field # @field of the #GimpSizeEntry to
+ * @digits.
+ *
+ * If you don't specify this value explicitly, the reference value's number
+ * of digits will equal to 0 for #GIMP_SIZE_ENTRY_UPDATE_SIZE and to 2 for
+ * #GIMP_SIZE_ENTRY_UPDATE_RESOLUTION.
+ **/
+void
+gimp_size_entry_set_refval_digits (GimpSizeEntry *gse,
+ gint field,
+ gint digits)
+{
+ GimpSizeEntryField *gsef;
+
+ g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
+ g_return_if_fail ((field >= 0) && (field < gse->number_of_fields));
+ g_return_if_fail ((digits >= 0) && (digits <= 6));
+
+ gsef = (GimpSizeEntryField*) g_slist_nth_data (gse->fields, field);
+ gsef->refval_digits = digits;
+
+ if (gse->update_policy == GIMP_SIZE_ENTRY_UPDATE_SIZE)
+ {
+ if (gse->show_refval)
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (gsef->refval_spinbutton),
+ gsef->refval_digits);
+ else if (gse->unit == GIMP_UNIT_PIXEL)
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (gsef->value_spinbutton),
+ gsef->refval_digits);
+ }
+}
+
+/**
+ * gimp_size_entry_get_refval:
+ * @gse: The sizeentry you want to know a reference value of.
+ * @field: The index of the field you want to know the reference value of.
+ *
+ * Returns the reference value for field # @field of the #GimpSizeEntry.
+ *
+ * The reference value is either a distance in pixels or a resolution
+ * in dpi, depending on which #GimpSizeEntryUpdatePolicy you chose in
+ * gimp_size_entry_new().
+ *
+ * Returns: The reference value of the chosen @field.
+ **/
+gdouble
+gimp_size_entry_get_refval (GimpSizeEntry *gse,
+ gint field)
+{
+ GimpSizeEntryField *gsef;
+
+ /* return 1.0 to avoid division by zero */
+ g_return_val_if_fail (GIMP_IS_SIZE_ENTRY (gse), 1.0);
+ g_return_val_if_fail ((field >= 0) && (field < gse->number_of_fields), 1.0);
+
+ gsef = (GimpSizeEntryField*) g_slist_nth_data (gse->fields, field);
+ return gsef->refval;
+}
+
+static void
+gimp_size_entry_update_refval (GimpSizeEntryField *gsef,
+ gdouble refval)
+{
+ if (gsef->stop_recursion > 1)
+ return;
+
+ gsef->refval = refval;
+
+ switch (gsef->gse->update_policy)
+ {
+ case GIMP_SIZE_ENTRY_UPDATE_NONE:
+ break;
+
+ case GIMP_SIZE_ENTRY_UPDATE_SIZE:
+ switch (gsef->gse->unit)
+ {
+ case GIMP_UNIT_PIXEL:
+ gsef->value = refval;
+ break;
+ case GIMP_UNIT_PERCENT:
+ gsef->value =
+ CLAMP (100 * (refval - gsef->lower) / (gsef->upper - gsef->lower),
+ gsef->min_value, gsef->max_value);
+ break;
+ default:
+ gsef->value =
+ CLAMP (refval * gimp_unit_get_factor (gsef->gse->unit) /
+ gsef->resolution,
+ gsef->min_value, gsef->max_value);
+ break;
+ }
+ gtk_adjustment_set_value (gsef->value_adjustment, gsef->value);
+ break;
+
+ case GIMP_SIZE_ENTRY_UPDATE_RESOLUTION:
+ gsef->value =
+ CLAMP (refval / gimp_unit_get_factor (gsef->gse->unit),
+ gsef->min_value, gsef->max_value);
+ gtk_adjustment_set_value (gsef->value_adjustment, gsef->value);
+ break;
+
+ default:
+ break;
+ }
+
+ g_signal_emit (gsef->gse, gimp_size_entry_signals[REFVAL_CHANGED], 0);
+}
+
+/**
+ * gimp_size_entry_set_refval:
+ * @gse: The sizeentry you want to set a reference value for.
+ * @field: The index of the field you want to set the reference value for.
+ * @refval: The new reference value for @field.
+ *
+ * Sets the reference value for field # @field of the #GimpSizeEntry.
+ *
+ * The @refval passed is either a distance in pixels or a resolution in dpi,
+ * depending on which #GimpSizeEntryUpdatePolicy you chose in
+ * gimp_size_entry_new().
+ **/
+void
+gimp_size_entry_set_refval (GimpSizeEntry *gse,
+ gint field,
+ gdouble refval)
+{
+ GimpSizeEntryField *gsef;
+
+ g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
+ g_return_if_fail ((field >= 0) && (field < gse->number_of_fields));
+
+ gsef = (GimpSizeEntryField *) g_slist_nth_data (gse->fields, field);
+
+ refval = CLAMP (refval, gsef->min_refval, gsef->max_refval);
+
+ if (gse->show_refval)
+ gtk_adjustment_set_value (gsef->refval_adjustment, refval);
+
+ gimp_size_entry_update_refval (gsef, refval);
+}
+
+static void
+gimp_size_entry_refval_callback (GtkWidget *widget,
+ gpointer data)
+{
+ GimpSizeEntryField *gsef;
+ gdouble new_refval;
+
+ gsef = (GimpSizeEntryField *) data;
+
+ new_refval = gtk_adjustment_get_value (GTK_ADJUSTMENT (widget));
+
+ if (gsef->refval != new_refval)
+ gimp_size_entry_update_refval (gsef, new_refval);
+}
+
+
+/**
+ * gimp_size_entry_get_unit:
+ * @gse: The sizeentry you want to know the unit of.
+ *
+ * Returns the #GimpUnit the user has selected in the #GimpSizeEntry's
+ * #GimpUnitComboBox.
+ *
+ * Returns: The sizeentry's unit.
+ **/
+GimpUnit
+gimp_size_entry_get_unit (GimpSizeEntry *gse)
+{
+ g_return_val_if_fail (GIMP_IS_SIZE_ENTRY (gse), GIMP_UNIT_INCH);
+
+ return gse->unit;
+}
+
+static void
+gimp_size_entry_update_unit (GimpSizeEntry *gse,
+ GimpUnit unit)
+{
+ GimpSizeEntryField *gsef;
+ gint i;
+ gint digits;
+
+ gse->unit = unit;
+
+ digits = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (gse),
+ "gimp-pixel-digits"));
+
+ for (i = 0; i < gse->number_of_fields; i++)
+ {
+ gsef = (GimpSizeEntryField *) g_slist_nth_data (gse->fields, i);
+
+ if (gse->update_policy == GIMP_SIZE_ENTRY_UPDATE_SIZE)
+ {
+ if (unit == GIMP_UNIT_PIXEL)
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (gsef->value_spinbutton),
+ gsef->refval_digits + digits);
+ else if (unit == GIMP_UNIT_PERCENT)
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (gsef->value_spinbutton),
+ 2 + digits);
+ else
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (gsef->value_spinbutton),
+ GIMP_SIZE_ENTRY_DIGITS (unit) + digits);
+ }
+ else if (gse->update_policy == GIMP_SIZE_ENTRY_UPDATE_RESOLUTION)
+ {
+ digits = (gimp_unit_get_digits (GIMP_UNIT_INCH) -
+ gimp_unit_get_digits (unit));
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (gsef->value_spinbutton),
+ MAX (3 + digits, 3));
+ }
+
+ gsef->stop_recursion = 0; /* hack !!! */
+
+ gimp_size_entry_set_refval_boundaries (gse, i,
+ gsef->min_refval,
+ gsef->max_refval);
+ }
+
+ g_signal_emit (gse, gimp_size_entry_signals[UNIT_CHANGED], 0);
+}
+
+
+/**
+ * gimp_size_entry_set_unit:
+ * @gse: The sizeentry you want to change the unit for.
+ * @unit: The new unit.
+ *
+ * Sets the #GimpSizeEntry's unit. The reference value for all fields will
+ * stay the same but the value in units or pixels per unit will change
+ * according to which #GimpSizeEntryUpdatePolicy you chose in
+ * gimp_size_entry_new().
+ **/
+void
+gimp_size_entry_set_unit (GimpSizeEntry *gse,
+ GimpUnit unit)
+{
+ g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
+ g_return_if_fail (gse->menu_show_pixels || (unit != GIMP_UNIT_PIXEL));
+ g_return_if_fail (gse->menu_show_percent || (unit != GIMP_UNIT_PERCENT));
+
+ gimp_unit_combo_box_set_active (GIMP_UNIT_COMBO_BOX (gse->unitmenu), unit);
+ gimp_size_entry_update_unit (gse, unit);
+}
+
+static void
+gimp_size_entry_unit_callback (GtkWidget *widget,
+ GimpSizeEntry *gse)
+{
+ GimpUnit new_unit;
+
+ new_unit = gimp_unit_combo_box_get_active (GIMP_UNIT_COMBO_BOX (widget));
+
+ if (gse->unit != new_unit)
+ gimp_size_entry_update_unit (gse, new_unit);
+}
+
+/**
+ * gimp_size_entry_attach_eevl:
+ * @spin_button: one of the size_entry's spinbuttons.
+ * @gsef: a size entry field.
+ *
+ * Hooks in the GimpEevl unit expression parser into the
+ * #GtkSpinButton of the #GimpSizeEntryField.
+ **/
+static void
+gimp_size_entry_attach_eevl (GtkSpinButton *spin_button,
+ GimpSizeEntryField *gsef)
+{
+ gtk_spin_button_set_numeric (spin_button, FALSE);
+ gtk_spin_button_set_update_policy (spin_button, GTK_UPDATE_IF_VALID);
+
+ g_signal_connect_after (spin_button, "input",
+ G_CALLBACK (gimp_size_entry_eevl_input_callback),
+ gsef);
+}
+
+static gint
+gimp_size_entry_eevl_input_callback (GtkSpinButton *spinner,
+ gdouble *return_val,
+ gpointer *data)
+{
+ GimpSizeEntryField *gsef = (GimpSizeEntryField *) data;
+ GimpEevlOptions options = GIMP_EEVL_OPTIONS_INIT;
+ gboolean success = FALSE;
+ const gchar *error_pos = 0;
+ GError *error = NULL;
+ GimpEevlQuantity result;
+
+ g_return_val_if_fail (GTK_IS_SPIN_BUTTON (spinner), FALSE);
+ g_return_val_if_fail (GIMP_IS_SIZE_ENTRY (gsef->gse), FALSE);
+
+ options.unit_resolver_proc = gimp_size_entry_eevl_unit_resolver;
+ options.data = data;
+
+ /* enable ratio expressions when there are two fields */
+ if (gsef->gse->number_of_fields == 2)
+ {
+ GimpSizeEntryField *other_gsef;
+ GimpEevlQuantity default_unit_factor;
+ gdouble default_unit_offset;
+
+ options.ratio_expressions = TRUE;
+
+ if (gsef == gsef->gse->fields->data)
+ {
+ other_gsef = gsef->gse->fields->next->data;
+
+ options.ratio_invert = FALSE;
+ }
+ else
+ {
+ other_gsef = gsef->gse->fields->data;
+
+ options.ratio_invert = TRUE;
+ }
+
+ options.unit_resolver_proc (NULL,
+ &default_unit_factor, &default_unit_offset,
+ options.data);
+
+ options.ratio_quantity.value = other_gsef->value /
+ default_unit_factor.value;
+ options.ratio_quantity.dimension = default_unit_factor.dimension;
+ }
+
+ success = gimp_eevl_evaluate (gtk_entry_get_text (GTK_ENTRY (spinner)),
+ &options,
+ &result,
+ &error_pos,
+ &error);
+ if (! success)
+ {
+ if (error && error_pos)
+ {
+ g_printerr ("ERROR: %s at '%s'\n",
+ error->message,
+ *error_pos ? error_pos : "<End of input>");
+ }
+ else
+ {
+ g_printerr ("ERROR: Expression evaluation failed without error.\n");
+ }
+
+ gtk_widget_error_bell (GTK_WIDGET (spinner));
+ return GTK_INPUT_ERROR;
+ }
+ else if (result.dimension != 1 && gsef->gse->unit != GIMP_UNIT_PERCENT)
+ {
+ g_printerr ("ERROR: result has wrong dimension (expected 1, got %d)\n", result.dimension);
+
+ gtk_widget_error_bell (GTK_WIDGET (spinner));
+ return GTK_INPUT_ERROR;
+ }
+ else if (result.dimension != 0 && gsef->gse->unit == GIMP_UNIT_PERCENT)
+ {
+ g_printerr ("ERROR: result has wrong dimension (expected 0, got %d)\n", result.dimension);
+
+ gtk_widget_error_bell (GTK_WIDGET (spinner));
+ return GTK_INPUT_ERROR;
+ }
+ else
+ {
+ /* transform back to UI-unit */
+ GimpEevlQuantity ui_unit;
+ GtkAdjustment *adj;
+ gdouble val;
+
+ switch (gsef->gse->unit)
+ {
+ case GIMP_UNIT_PIXEL:
+ ui_unit.value = gsef->resolution;
+ ui_unit.dimension = 1;
+ break;
+ case GIMP_UNIT_PERCENT:
+ ui_unit.value = 1.0;
+ ui_unit.dimension = 0;
+ break;
+ default:
+ ui_unit.value = gimp_unit_get_factor(gsef->gse->unit);
+ ui_unit.dimension = 1;
+ break;
+ }
+
+ *return_val = result.value * ui_unit.value;
+
+ /* CLAMP() to adjustment bounds, or too large/small values
+ * will make the validation machinery revert to the old value.
+ * See bug #694477.
+ */
+ adj = gtk_spin_button_get_adjustment (spinner);
+
+ val = CLAMP (*return_val,
+ gtk_adjustment_get_lower (adj),
+ gtk_adjustment_get_upper (adj));
+
+ if (val != *return_val)
+ {
+ gtk_widget_error_bell (GTK_WIDGET (spinner));
+ *return_val = val;
+ }
+
+ return TRUE;
+ }
+}
+
+static gboolean
+gimp_size_entry_eevl_unit_resolver (const gchar *identifier,
+ GimpEevlQuantity *factor,
+ gdouble *offset,
+ gpointer data)
+{
+ GimpSizeEntryField *gsef = (GimpSizeEntryField *) data;
+ gboolean resolve_default_unit = (identifier == NULL);
+ GimpUnit unit;
+
+ g_return_val_if_fail (gsef, FALSE);
+ g_return_val_if_fail (factor != NULL, FALSE);
+ g_return_val_if_fail (offset != NULL, FALSE);
+ g_return_val_if_fail (GIMP_IS_SIZE_ENTRY (gsef->gse), FALSE);
+
+ *offset = 0.0;
+
+ for (unit = 0;
+ unit <= gimp_unit_get_number_of_units ();
+ unit++)
+ {
+ /* Hack to handle percent within the loop */
+ if (unit == gimp_unit_get_number_of_units ())
+ unit = GIMP_UNIT_PERCENT;
+
+ if ((resolve_default_unit && unit == gsef->gse->unit) ||
+ (identifier &&
+ (strcmp (gimp_unit_get_symbol (unit), identifier) == 0 ||
+ strcmp (gimp_unit_get_abbreviation (unit), identifier) == 0)))
+ {
+ switch (unit)
+ {
+ case GIMP_UNIT_PERCENT:
+ if (gsef->gse->unit == GIMP_UNIT_PERCENT)
+ {
+ factor->value = 1;
+ factor->dimension = 0;
+ }
+ else
+ {
+ /* gsef->upper contains the '100%'-value */
+ factor->value = 100*gsef->resolution/(gsef->upper - gsef->lower);
+ /* gsef->lower contains the '0%'-value */
+ *offset = gsef->lower/gsef->resolution;
+ factor->dimension = 1;
+ }
+ /* return here, don't perform percentage conversion */
+ return TRUE;
+ case GIMP_UNIT_PIXEL:
+ factor->value = gsef->resolution;
+ break;
+ default:
+ factor->value = gimp_unit_get_factor (unit);
+ break;
+ }
+
+ if (gsef->gse->unit == GIMP_UNIT_PERCENT)
+ {
+ /* map non-percentages onto percent */
+ factor->value = gsef->upper/(100*gsef->resolution);
+ factor->dimension = 0;
+ }
+ else
+ {
+ factor->dimension = 1;
+ }
+
+ /* We are done */
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * gimp_size_entry_show_unit_menu:
+ * @gse: a #GimpSizeEntry
+ * @show: Boolean
+ *
+ * Controls whether a unit menu is shown in the size entry. If
+ * @show is #TRUE, the menu is shown; otherwise it is hidden.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_size_entry_show_unit_menu (GimpSizeEntry *gse,
+ gboolean show)
+{
+ g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
+
+ gtk_widget_set_visible (gse->unitmenu, show);
+}
+
+
+/**
+ * gimp_size_entry_set_pixel_digits:
+ * @gse: a #GimpSizeEntry
+ * @digits: the number of digits to display for a pixel size
+ *
+ * This function allows you set up a #GimpSizeEntry so that sub-pixel
+ * sizes can be entered.
+ **/
+void
+gimp_size_entry_set_pixel_digits (GimpSizeEntry *gse,
+ gint digits)
+{
+ GimpUnitComboBox *combo;
+
+ g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
+
+ combo = GIMP_UNIT_COMBO_BOX (gse->unitmenu);
+
+ g_object_set_data (G_OBJECT (gse), "gimp-pixel-digits",
+ GINT_TO_POINTER (digits));
+ gimp_size_entry_update_unit (gse, gimp_unit_combo_box_get_active (combo));
+}
+
+
+/**
+ * gimp_size_entry_grab_focus:
+ * @gse: The sizeentry you want to grab the keyboard focus.
+ *
+ * This function is rather ugly and just a workaround for the fact that
+ * it's impossible to implement gtk_widget_grab_focus() for a #GtkTable.
+ **/
+void
+gimp_size_entry_grab_focus (GimpSizeEntry *gse)
+{
+ GimpSizeEntryField *gsef;
+
+ g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
+
+ gsef = gse->fields->data;
+ if (gsef)
+ gtk_widget_grab_focus (gse->show_refval ?
+ gsef->refval_spinbutton : gsef->value_spinbutton);
+}
+
+/**
+ * gimp_size_entry_set_activates_default:
+ * @gse: A #GimpSizeEntry
+ * @setting: %TRUE to activate window's default widget on Enter keypress
+ *
+ * Iterates over all entries in the #GimpSizeEntry and calls
+ * gtk_entry_set_activates_default() on them.
+ *
+ * Since: 2.4
+ **/
+void
+gimp_size_entry_set_activates_default (GimpSizeEntry *gse,
+ gboolean setting)
+{
+ GSList *list;
+
+ g_return_if_fail (GIMP_IS_SIZE_ENTRY (gse));
+
+ for (list = gse->fields; list; list = g_slist_next (list))
+ {
+ GimpSizeEntryField *gsef = list->data;
+
+ if (gsef->value_spinbutton)
+ gtk_entry_set_activates_default (GTK_ENTRY (gsef->value_spinbutton),
+ setting);
+
+ if (gsef->refval_spinbutton)
+ gtk_entry_set_activates_default (GTK_ENTRY (gsef->refval_spinbutton),
+ setting);
+ }
+}
+
+/**
+ * gimp_size_entry_get_help_widget:
+ * @gse: a #GimpSizeEntry
+ * @field: the index of the widget you want to get a pointer to
+ *
+ * You shouldn't fiddle with the internals of a #GimpSizeEntry but
+ * if you want to set tooltips using gimp_help_set_help_data() you
+ * can use this function to get a pointer to the spinbuttons.
+ *
+ * Return value: a #GtkWidget pointer that you can attach a tooltip to.
+ **/
+GtkWidget *
+gimp_size_entry_get_help_widget (GimpSizeEntry *gse,
+ gint field)
+{
+ GimpSizeEntryField *gsef;
+
+ g_return_val_if_fail (GIMP_IS_SIZE_ENTRY (gse), NULL);
+ g_return_val_if_fail ((field >= 0) && (field < gse->number_of_fields), NULL);
+
+ gsef = g_slist_nth_data (gse->fields, field);
+ if (!gsef)
+ return NULL;
+
+ return (gsef->refval_spinbutton ?
+ gsef->refval_spinbutton : gsef->value_spinbutton);
+}
diff --git a/libgimpwidgets/gimpsizeentry.h b/libgimpwidgets/gimpsizeentry.h
new file mode 100644
index 0000000..2ead052
--- /dev/null
+++ b/libgimpwidgets/gimpsizeentry.h
@@ -0,0 +1,155 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpsizeentry.h
+ * Copyright (C) 1999-2000 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_SIZE_ENTRY_H__
+#define __GIMP_SIZE_ENTRY_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+#define GIMP_TYPE_SIZE_ENTRY (gimp_size_entry_get_type ())
+#define GIMP_SIZE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_SIZE_ENTRY, GimpSizeEntry))
+#define GIMP_SIZE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_SIZE_ENTRY, GimpSizeEntryClass))
+#define GIMP_IS_SIZE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GIMP_TYPE_SIZE_ENTRY))
+#define GIMP_IS_SIZE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_SIZE_ENTRY))
+#define GIMP_SIZE_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_SIZE_ENTRY, GimpSizeEntryClass))
+
+
+typedef struct _GimpSizeEntryClass GimpSizeEntryClass;
+
+typedef struct _GimpSizeEntryField GimpSizeEntryField;
+
+struct _GimpSizeEntry
+{
+ GtkTable parent_instance;
+
+ GSList *fields;
+ gint number_of_fields;
+
+ GtkWidget *unitmenu;
+ GimpUnit unit;
+ gboolean menu_show_pixels;
+ gboolean menu_show_percent;
+
+ gboolean show_refval;
+ GimpSizeEntryUpdatePolicy update_policy;
+};
+
+struct _GimpSizeEntryClass
+{
+ GtkTableClass parent_class;
+
+ void (* value_changed) (GimpSizeEntry *gse);
+ void (* refval_changed) (GimpSizeEntry *gse);
+ void (* unit_changed) (GimpSizeEntry *gse);
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+/* For information look into the C source or the html documentation */
+
+GType gimp_size_entry_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_size_entry_new (gint number_of_fields,
+ GimpUnit unit,
+ const gchar *unit_format,
+ gboolean menu_show_pixels,
+ gboolean menu_show_percent,
+ gboolean show_refval,
+ gint spinbutton_width,
+ GimpSizeEntryUpdatePolicy update_policy);
+
+void gimp_size_entry_add_field (GimpSizeEntry *gse,
+ GtkSpinButton *value_spinbutton,
+ GtkSpinButton *refval_spinbutton);
+
+GtkWidget * gimp_size_entry_attach_label (GimpSizeEntry *gse,
+ const gchar *text,
+ gint row,
+ gint column,
+ gfloat alignment);
+
+void gimp_size_entry_set_resolution (GimpSizeEntry *gse,
+ gint field,
+ gdouble resolution,
+ gboolean keep_size);
+
+void gimp_size_entry_set_size (GimpSizeEntry *gse,
+ gint field,
+ gdouble lower,
+ gdouble upper);
+
+void gimp_size_entry_set_value_boundaries (GimpSizeEntry *gse,
+ gint field,
+ gdouble lower,
+ gdouble upper);
+
+gdouble gimp_size_entry_get_value (GimpSizeEntry *gse,
+ gint field);
+void gimp_size_entry_set_value (GimpSizeEntry *gse,
+ gint field,
+ gdouble value);
+
+void gimp_size_entry_set_refval_boundaries (GimpSizeEntry *gse,
+ gint field,
+ gdouble lower,
+ gdouble upper);
+void gimp_size_entry_set_refval_digits (GimpSizeEntry *gse,
+ gint field,
+ gint digits);
+
+gdouble gimp_size_entry_get_refval (GimpSizeEntry *gse,
+ gint field);
+void gimp_size_entry_set_refval (GimpSizeEntry *gse,
+ gint field,
+ gdouble refval);
+
+GimpUnit gimp_size_entry_get_unit (GimpSizeEntry *gse);
+void gimp_size_entry_set_unit (GimpSizeEntry *gse,
+ GimpUnit unit);
+void gimp_size_entry_show_unit_menu (GimpSizeEntry *gse,
+ gboolean show);
+
+void gimp_size_entry_set_pixel_digits (GimpSizeEntry *gse,
+ gint digits);
+
+void gimp_size_entry_grab_focus (GimpSizeEntry *gse);
+void gimp_size_entry_set_activates_default (GimpSizeEntry *gse,
+ gboolean setting);
+GtkWidget * gimp_size_entry_get_help_widget (GimpSizeEntry *gse,
+ gint field);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_SIZE_ENTRY_H__ */
diff --git a/libgimpwidgets/gimpspinbutton.c b/libgimpwidgets/gimpspinbutton.c
new file mode 100644
index 0000000..4d87e9d
--- /dev/null
+++ b/libgimpwidgets/gimpspinbutton.c
@@ -0,0 +1,385 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpspinbutton.c
+ * Copyright (C) 2018 Ell
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "libgimpmath/gimpmath.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimp3migration.h"
+#include "gimpspinbutton.h"
+
+
+/**
+ * SECTION: gimpspinbutton
+ * @title: GimpSpinButton
+ * @short_description: A #GtkSpinButton with a some tweaked functionality.
+ *
+ * #GimpSpinButton is a drop-in replacement for #GtkSpinButton, with the
+ * following changes:
+ *
+ * - When the spin-button loses focus, its adjustment value is only
+ * updated if the entry text has been changed.
+ *
+ * - When the spin-button's "wrap" property is TRUE, values input through the
+ * entry are wrapped around.
+ *
+ * - Modifiers can be used during scrolling for smaller/bigger increments.
+ **/
+
+
+#define MAX_DIGITS 20
+
+
+struct _GimpSpinButtonPrivate
+{
+ gboolean changed;
+};
+
+
+/* local function prototypes */
+
+static gboolean gimp_spin_button_scroll (GtkWidget *widget,
+ GdkEventScroll *event);
+static gboolean gimp_spin_button_key_press (GtkWidget *widget,
+ GdkEventKey *event);
+static gboolean gimp_spin_button_focus_in (GtkWidget *widget,
+ GdkEventFocus *event);
+static gboolean gimp_spin_button_focus_out (GtkWidget *widget,
+ GdkEventFocus *event);
+
+static gint gimp_spin_button_input (GtkSpinButton *spin_button,
+ gdouble *new_value);
+
+static void gimp_spin_button_changed (GtkEditable *editable,
+ gpointer data);
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpSpinButton, gimp_spin_button,
+ GTK_TYPE_SPIN_BUTTON)
+
+#define parent_class gimp_spin_button_parent_class
+
+
+/* private functions */
+
+
+static void
+gimp_spin_button_class_init (GimpSpinButtonClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GtkSpinButtonClass *spin_button_class = GTK_SPIN_BUTTON_CLASS (klass);
+
+ widget_class->scroll_event = gimp_spin_button_scroll;
+ widget_class->key_press_event = gimp_spin_button_key_press;
+ widget_class->focus_in_event = gimp_spin_button_focus_in;
+ widget_class->focus_out_event = gimp_spin_button_focus_out;
+
+ spin_button_class->input = gimp_spin_button_input;
+}
+
+static void
+gimp_spin_button_init (GimpSpinButton *spin_button)
+{
+ spin_button->priv = gimp_spin_button_get_instance_private (spin_button);
+
+ g_signal_connect (spin_button, "changed",
+ G_CALLBACK (gimp_spin_button_changed),
+ NULL);
+}
+
+static gboolean
+gimp_spin_button_scroll (GtkWidget *widget,
+ GdkEventScroll *event)
+{
+ if (event->direction == GDK_SCROLL_UP ||
+ event->direction == GDK_SCROLL_DOWN)
+ {
+ GtkSpinButton *spin_button = GTK_SPIN_BUTTON (widget);
+ GtkAdjustment *adjustment = gtk_spin_button_get_adjustment (spin_button);
+ gdouble step_inc;
+ gdouble page_inc;
+ gint digits;
+ gdouble step;
+
+ step_inc = gtk_adjustment_get_step_increment (adjustment);
+ page_inc = gtk_adjustment_get_page_increment (adjustment);
+ digits = gtk_spin_button_get_digits (spin_button);
+
+ if (event->state & GDK_SHIFT_MASK)
+ {
+ step = step_inc * step_inc / page_inc;
+ step = MAX (step, pow (10.0, -digits));
+ }
+ else if (event->state & GDK_CONTROL_MASK)
+ {
+ step = page_inc;
+ }
+ else
+ {
+ step = step_inc;
+ }
+
+ if (event->direction == GDK_SCROLL_DOWN)
+ step = -step;
+
+ if (! gtk_widget_has_focus (widget))
+ gtk_widget_grab_focus (widget);
+
+ gtk_spin_button_spin (spin_button, GTK_SPIN_USER_DEFINED, step);
+
+ return TRUE;
+ }
+
+ return GTK_WIDGET_CLASS (parent_class)->scroll_event (widget, event);
+}
+
+static gboolean
+gimp_spin_button_key_press (GtkWidget *widget,
+ GdkEventKey *event)
+{
+ switch (event->keyval)
+ {
+ case GDK_KEY_Return:
+ case GDK_KEY_KP_Enter:
+ case GDK_KEY_ISO_Enter:
+ case GDK_KEY_Escape:
+ {
+ GtkEntry *entry = GTK_ENTRY (widget);
+ GtkSpinButton *spin_button = GTK_SPIN_BUTTON (widget);
+ gchar *text;
+ gboolean changed;
+
+ text = g_strdup (gtk_entry_get_text (entry));
+
+ if (event->keyval == GDK_KEY_Escape)
+ {
+ gtk_spin_button_set_value (
+ spin_button,
+ gtk_spin_button_get_value (spin_button));
+ }
+ else
+ {
+ gtk_spin_button_update (spin_button);
+ }
+
+ changed = strcmp (gtk_entry_get_text (entry), text);
+
+ g_free (text);
+
+ if (changed)
+ {
+ gtk_editable_set_position (GTK_EDITABLE (widget), -1);
+
+ return TRUE;
+ }
+ }
+ break;
+ }
+
+ return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
+}
+
+static gboolean
+gimp_spin_button_focus_in (GtkWidget *widget,
+ GdkEventFocus *event)
+{
+ GimpSpinButton *spin_button = GIMP_SPIN_BUTTON (widget);
+
+ spin_button->priv->changed = FALSE;
+
+ return GTK_WIDGET_CLASS (parent_class)->focus_in_event (widget, event);
+}
+
+static gboolean
+gimp_spin_button_focus_out (GtkWidget *widget,
+ GdkEventFocus *event)
+{
+ GimpSpinButton *spin_button = GIMP_SPIN_BUTTON (widget);
+ gboolean editable;
+ gboolean result;
+
+ editable = gtk_editable_get_editable (GTK_EDITABLE (widget));
+
+ if (! spin_button->priv->changed)
+ gtk_editable_set_editable (GTK_EDITABLE (widget), FALSE);
+
+ result = GTK_WIDGET_CLASS (parent_class)->focus_out_event (widget, event);
+
+ if (! spin_button->priv->changed)
+ gtk_editable_set_editable (GTK_EDITABLE (widget), editable);
+
+ return result;
+}
+
+static gint
+gimp_spin_button_input (GtkSpinButton *spin_button,
+ gdouble *new_value)
+{
+ if (gtk_spin_button_get_wrap (spin_button))
+ {
+ gdouble value;
+ gdouble min;
+ gdouble max;
+ gchar *endptr;
+
+ value = g_strtod (gtk_entry_get_text (GTK_ENTRY (spin_button)), &endptr);
+
+ if (*endptr)
+ return FALSE;
+
+ gtk_spin_button_get_range (spin_button, &min, &max);
+
+ if (min < max)
+ {
+ gdouble rem;
+
+ rem = fmod (value - min, max - min);
+
+ if (rem < 0.0)
+ rem += max - min;
+
+ if (rem == 0.0)
+ value = CLAMP (value, min, max);
+ else
+ value = min + rem;
+ }
+ else
+ {
+ value = min;
+ }
+
+ *new_value = value;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+gimp_spin_button_changed (GtkEditable *editable,
+ gpointer data)
+{
+ GimpSpinButton *spin_button = GIMP_SPIN_BUTTON (editable);
+
+ spin_button->priv->changed = TRUE;
+}
+
+
+/* public functions */
+
+
+/**
+ * gimp_spin_button_new:
+ * @adjustment: (allow-none): the #GtkAdjustment object that this spin
+ * button should use, or %NULL
+ * @climb_rate: specifies by how much the rate of change in the
+ * value will accelerate if you continue to hold
+ * down an up/down button or arrow key
+ * @digits: the number of decimal places to display
+ *
+ * Creates a new #GimpSpinButton.
+ *
+ * Returns: The new spin button as a #GtkWidget
+ *
+ * Since: 2.10.10
+ */
+GtkWidget *
+gimp_spin_button_new_ (GtkAdjustment *adjustment,
+ gdouble climb_rate,
+ guint digits)
+{
+ GtkWidget *spin_button;
+
+ g_return_val_if_fail (adjustment == NULL || GTK_IS_ADJUSTMENT (adjustment),
+ NULL);
+
+ spin_button = g_object_new (GIMP_TYPE_SPIN_BUTTON, NULL);
+
+ gtk_spin_button_configure (GTK_SPIN_BUTTON (spin_button),
+ adjustment, climb_rate, digits);
+
+ return spin_button;
+}
+
+/**
+ * gimp_spin_button_new_with_range:
+ * @min: Minimum allowable value
+ * @max: Maximum allowable value
+ * @step: Increment added or subtracted by spinning the widget
+ *
+ * This is a convenience constructor that allows creation of a numeric
+ * #GimpSpinButton without manually creating an adjustment. The value is
+ * initially set to the minimum value and a page increment of 10 * @step
+ * is the default. The precision of the spin button is equivalent to the
+ * precision of @step.
+ *
+ * Note that the way in which the precision is derived works best if @step
+ * is a power of ten. If the resulting precision is not suitable for your
+ * needs, use gtk_spin_button_set_digits() to correct it.
+ *
+ * Returns: The new spin button as a #GtkWidget
+ *
+ * Since: 2.10.10
+ */
+GtkWidget *
+gimp_spin_button_new_with_range (gdouble min,
+ gdouble max,
+ gdouble step)
+{
+ GtkAdjustment *adjustment;
+ GtkWidget *spin_button;
+ gint digits;
+
+ g_return_val_if_fail (min <= max, NULL);
+ g_return_val_if_fail (step != 0.0, NULL);
+
+ spin_button = g_object_new (GIMP_TYPE_SPIN_BUTTON, NULL);
+
+ adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (min, min, max,
+ step, 10.0 * step, 0.0));
+
+ if (fabs (step) >= 1.0 || step == 0.0)
+ {
+ digits = 0;
+ }
+ else
+ {
+ digits = abs ((gint) floor (log10 (fabs (step))));
+
+ if (digits > MAX_DIGITS)
+ digits = MAX_DIGITS;
+ }
+
+ gtk_spin_button_configure (GTK_SPIN_BUTTON (spin_button),
+ adjustment, step, digits);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spin_button), TRUE);
+
+ return spin_button;
+}
diff --git a/libgimpwidgets/gimpspinbutton.h b/libgimpwidgets/gimpspinbutton.h
new file mode 100644
index 0000000..ea77cd6
--- /dev/null
+++ b/libgimpwidgets/gimpspinbutton.h
@@ -0,0 +1,90 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpspinbutton.h
+ * Copyright (C) 2018 Ell
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_SPIN_BUTTON_H__
+#define __GIMP_SPIN_BUTTON_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+#define GIMP_TYPE_SPIN_BUTTON (gimp_spin_button_get_type ())
+#define GIMP_SPIN_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_SPIN_BUTTON, GimpSpinButton))
+#define GIMP_SPIN_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_SPIN_BUTTON, GimpSpinButtonClass))
+#define GIMP_IS_SPIN_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_SPIN_BUTTON))
+#define GIMP_IS_SPIN_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_SPIN_BUTTON))
+#define GIMP_SPIN_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_SPIN_BUTTON, GimpSpinButtonClass))
+
+
+typedef struct _GimpSpinButtonPrivate GimpSpinButtonPrivate;
+typedef struct _GimpSpinButtonClass GimpSpinButtonClass;
+
+struct _GimpSpinButton
+{
+ GtkSpinButton parent_instance;
+
+ GimpSpinButtonPrivate *priv;
+};
+
+struct _GimpSpinButtonClass
+{
+ GtkSpinButtonClass parent_class;
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_spin_button_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_spin_button_new_ (GtkAdjustment *adjustment,
+ gdouble climb_rate,
+ guint digits);
+GtkWidget * gimp_spin_button_new_with_range (gdouble min,
+ gdouble max,
+ gdouble step);
+
+
+/* compatibility magic, expanding to either the old (deprecated)
+ * gimp_spin_button_new(), defined in gimpwidgets.h, or the new
+ * gimp_spin_button_new(), defined here, based on the number of arguments.
+ */
+#define gimp_spin_button_new(...) gimp_spin_button_new_I (__VA_ARGS__, \
+ 9, , , , , , 3)
+#define gimp_spin_button_new_I(_1, _2, _3, _4, _5, _6, _7, _8, _9, n, ...) \
+ gimp_spin_button_new_I_##n (_1, _2, _3, _4, _5, _6, _7, _8, _9)
+#define gimp_spin_button_new_I_3(_1, _2, _3, _4, _5, _6, _7, _8, _9) \
+ gimp_spin_button_new_ (_1, _2, _3)
+#define gimp_spin_button_new_I_9(_1, _2, _3, _4, _5, _6, _7, _8, _9) \
+ gimp_spin_button_new (_1, _2, _3, _4, _5, _6, _7, _8, _9)
+
+
+G_END_DECLS
+
+#endif /* __GIMP_SPIN_BUTTON_H__ */
diff --git a/libgimpwidgets/gimpstringcombobox.c b/libgimpwidgets/gimpstringcombobox.c
new file mode 100644
index 0000000..56208a3
--- /dev/null
+++ b/libgimpwidgets/gimpstringcombobox.c
@@ -0,0 +1,363 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpstringcombobox.c
+ * Copyright (C) 2007 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpstringcombobox.h"
+
+
+/**
+ * SECTION: gimpstringcombobox
+ * @title: GimpStringComboBox
+ * @short_description: A #GtkComboBox subclass to select strings.
+ *
+ * A #GtkComboBox subclass to select strings.
+ **/
+
+
+enum
+{
+ PROP_0,
+ PROP_ID_COLUMN,
+ PROP_LABEL_COLUMN,
+ PROP_ELLIPSIZE
+};
+
+
+typedef struct
+{
+ gint id_column;
+ gint label_column;
+ GtkCellRenderer *text_renderer;
+} GimpStringComboBoxPrivate;
+
+#define GIMP_STRING_COMBO_BOX_GET_PRIVATE(obj) \
+ ((GimpStringComboBoxPrivate *) ((GimpStringComboBox *) (obj))->priv)
+
+
+static void gimp_string_combo_box_constructed (GObject *object);
+static void gimp_string_combo_box_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_string_combo_box_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpStringComboBox, gimp_string_combo_box,
+ GTK_TYPE_COMBO_BOX)
+
+#define parent_class gimp_string_combo_box_parent_class
+
+
+static void
+gimp_string_combo_box_class_init (GimpStringComboBoxClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = gimp_string_combo_box_constructed;
+ object_class->set_property = gimp_string_combo_box_set_property;
+ object_class->get_property = gimp_string_combo_box_get_property;
+
+ /**
+ * GimpStringComboBox:id-column:
+ *
+ * The column in the associated GtkTreeModel that holds unique
+ * string IDs.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class,
+ PROP_ID_COLUMN,
+ g_param_spec_int ("id-column",
+ "ID Column",
+ "The model column that holds the ID",
+ 0, G_MAXINT,
+ 0,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * GimpStringComboBox:id-column:
+ *
+ * The column in the associated GtkTreeModel that holds strings to
+ * be used as labels in the combo-box.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class,
+ PROP_LABEL_COLUMN,
+ g_param_spec_int ("label-column",
+ "Label Column",
+ "The model column that holds the label",
+ 0, G_MAXINT,
+ 0,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * GimpStringComboBox:ellipsize:
+ *
+ * Specifies the preferred place to ellipsize text in the combo-box,
+ * if the cell renderer does not have enough room to display the
+ * entire string.
+ *
+ * Since: 2.4
+ */
+ g_object_class_install_property (object_class,
+ PROP_ELLIPSIZE,
+ g_param_spec_enum ("ellipsize",
+ "Ellipsize",
+ "Ellipsize mode for the text cell renderer",
+ PANGO_TYPE_ELLIPSIZE_MODE,
+ PANGO_ELLIPSIZE_NONE,
+ GIMP_PARAM_READWRITE));
+}
+
+static void
+gimp_string_combo_box_init (GimpStringComboBox *combo_box)
+{
+ combo_box->priv = gimp_string_combo_box_get_instance_private (combo_box);
+}
+
+static void
+gimp_string_combo_box_constructed (GObject *object)
+{
+ GimpStringComboBoxPrivate *priv = GIMP_STRING_COMBO_BOX_GET_PRIVATE (object);
+ GtkCellRenderer *cell;
+
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ priv->text_renderer = cell = gtk_cell_renderer_text_new ();
+
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (object), cell, TRUE);
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (object), cell,
+ "text", priv->label_column,
+ NULL);
+}
+
+static void
+gimp_string_combo_box_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpStringComboBoxPrivate *priv = GIMP_STRING_COMBO_BOX_GET_PRIVATE (object);
+
+ switch (property_id)
+ {
+ case PROP_ID_COLUMN:
+ priv->id_column = g_value_get_int (value);
+ break;
+
+ case PROP_LABEL_COLUMN:
+ priv->label_column = g_value_get_int (value);
+ break;
+
+ case PROP_ELLIPSIZE:
+ g_object_set_property (G_OBJECT (priv->text_renderer),
+ pspec->name, value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_string_combo_box_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpStringComboBoxPrivate *priv = GIMP_STRING_COMBO_BOX_GET_PRIVATE (object);
+
+ switch (property_id)
+ {
+ case PROP_ID_COLUMN:
+ g_value_set_int (value, priv->id_column);
+ break;
+
+ case PROP_LABEL_COLUMN:
+ g_value_set_int (value, priv->label_column);
+ break;
+
+ case PROP_ELLIPSIZE:
+ g_object_get_property (G_OBJECT (priv->text_renderer),
+ pspec->name, value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+gimp_string_model_lookup (GtkTreeModel *model,
+ gint column,
+ const gchar *id,
+ GtkTreeIter *iter)
+{
+ GValue value = G_VALUE_INIT;
+ gboolean iter_valid;
+
+ /* This lookup could be backed up by a hash table or some other
+ * data structure instead of doing a list traversal. But since this
+ * is a GtkComboBox, there shouldn't be many entries anyway...
+ */
+
+ for (iter_valid = gtk_tree_model_get_iter_first (model, iter);
+ iter_valid;
+ iter_valid = gtk_tree_model_iter_next (model, iter))
+ {
+ const gchar *str;
+
+ gtk_tree_model_get_value (model, iter, column, &value);
+
+ str = g_value_get_string (&value);
+
+ if (str && strcmp (str, id) == 0)
+ {
+ g_value_unset (&value);
+ break;
+ }
+
+ g_value_unset (&value);
+ }
+
+ return iter_valid;
+}
+
+
+/**
+ * gimp_string_combo_box_new:
+ * @model: a #GtkTreeModel
+ * @id_column: the model column of the ID
+ * @label_column: the modl column of the label
+ *
+ * Return value: a new #GimpStringComboBox.
+ *
+ * Since: 2.4
+ **/
+GtkWidget *
+gimp_string_combo_box_new (GtkTreeModel *model,
+ gint id_column,
+ gint label_column)
+{
+ g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
+ g_return_val_if_fail (gtk_tree_model_get_column_type (model,
+ id_column) == G_TYPE_STRING, NULL);
+ g_return_val_if_fail (gtk_tree_model_get_column_type (model,
+ label_column) == G_TYPE_STRING, NULL);
+
+ return g_object_new (GIMP_TYPE_STRING_COMBO_BOX,
+ "model", model,
+ "id-column", id_column,
+ "label-column", label_column,
+ NULL);
+}
+
+/**
+ * gimp_string_combo_box_set_active:
+ * @combo_box: a #GimpStringComboBox
+ * @id: the ID of the item to select
+ *
+ * Looks up the item that belongs to the given @id and makes it the
+ * selected item in the @combo_box.
+ *
+ * Return value: %TRUE on success or %FALSE if there was no item for
+ * this value.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gimp_string_combo_box_set_active (GimpStringComboBox *combo_box,
+ const gchar *id)
+{
+ g_return_val_if_fail (GIMP_IS_STRING_COMBO_BOX (combo_box), FALSE);
+
+ if (id)
+ {
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gint column;
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));
+
+ column = GIMP_STRING_COMBO_BOX_GET_PRIVATE (combo_box)->id_column;
+
+ if (gimp_string_model_lookup (model, column, id, &iter))
+ {
+ gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_box), &iter);
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+ else
+ {
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), -1);
+
+ return TRUE;
+ }
+}
+
+/**
+ * gimp_string_combo_box_get_active:
+ * @combo_box: a #GimpStringComboBox
+ *
+ * Retrieves the value of the selected (active) item in the @combo_box.
+ *
+ * Return value: newly allocated ID string or %NULL if nothing was selected
+ *
+ * Since: 2.4
+ **/
+gchar *
+gimp_string_combo_box_get_active (GimpStringComboBox *combo_box)
+{
+ GtkTreeIter iter;
+
+ g_return_val_if_fail (GIMP_IS_STRING_COMBO_BOX (combo_box), NULL);
+
+ if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo_box), &iter))
+ {
+ GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));
+ gchar *value;
+ gint column;
+
+ column = GIMP_STRING_COMBO_BOX_GET_PRIVATE (combo_box)->id_column;
+
+ gtk_tree_model_get (model, &iter,
+ column, &value,
+ -1);
+
+ return value;
+ }
+
+ return NULL;
+}
diff --git a/libgimpwidgets/gimpstringcombobox.h b/libgimpwidgets/gimpstringcombobox.h
new file mode 100644
index 0000000..6dff2c6
--- /dev/null
+++ b/libgimpwidgets/gimpstringcombobox.h
@@ -0,0 +1,74 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpstringcombobox.h
+ * Copyright (C) 2007 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_STRING_COMBO_BOX_H__
+#define __GIMP_STRING_COMBO_BOX_H__
+
+G_BEGIN_DECLS
+
+
+#define GIMP_TYPE_STRING_COMBO_BOX (gimp_string_combo_box_get_type ())
+#define GIMP_STRING_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_STRING_COMBO_BOX, GimpStringComboBox))
+#define GIMP_STRING_COMBO_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_STRING_COMBO_BOX, GimpStringComboBoxClass))
+#define GIMP_IS_STRING_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_STRING_COMBO_BOX))
+#define GIMP_IS_STRING_COMBO_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_STRING_COMBO_BOX))
+#define GIMP_STRING_COMBO_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_STRING_COMBO_BOX, GimpStringComboBoxClass))
+
+
+typedef struct _GimpStringComboBoxClass GimpStringComboBoxClass;
+
+struct _GimpStringComboBox
+{
+ GtkComboBox parent_instance;
+
+ /*< private >*/
+ gpointer priv;
+};
+
+struct _GimpStringComboBoxClass
+{
+ GtkComboBoxClass parent_class;
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_string_combo_box_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_string_combo_box_new (GtkTreeModel *model,
+ gint id_column,
+ gint label_column);
+gboolean gimp_string_combo_box_set_active (GimpStringComboBox *combo_box,
+ const gchar *id);
+gchar * gimp_string_combo_box_get_active (GimpStringComboBox *combo_box);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_STRING_COMBO_BOX_H__ */
diff --git a/libgimpwidgets/gimpunitcombobox.c b/libgimpwidgets/gimpunitcombobox.c
new file mode 100644
index 0000000..fff5577
--- /dev/null
+++ b/libgimpwidgets/gimpunitcombobox.c
@@ -0,0 +1,218 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1999 Peter Mattis and Spencer Kimball
+ *
+ * gimpunitcombobox.c
+ * Copyright (C) 2004, 2008 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpunitcombobox.h"
+#include "gimpunitstore.h"
+
+
+/**
+ * SECTION: gimpunitcombobox
+ * @title: GimpUnitComboBox
+ * @short_description: A #GtkComboBox to select a #GimpUnit.
+ * @see_also: #GimpUnit, #GimpUnitStore
+ *
+ * #GimpUnitComboBox selects units stored in a #GimpUnitStore.
+ * It replaces the deprecated #GimpUnitMenu.
+ **/
+
+
+static void gimp_unit_combo_box_style_set (GtkWidget *widget,
+ GtkStyle *prev_style);
+static void gimp_unit_combo_box_popup_shown (GtkWidget *widget,
+ const GParamSpec *pspec);
+
+
+G_DEFINE_TYPE (GimpUnitComboBox, gimp_unit_combo_box, GTK_TYPE_COMBO_BOX)
+
+#define parent_class gimp_unit_combo_box_parent_class
+
+
+static void
+gimp_unit_combo_box_class_init (GimpUnitComboBoxClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ widget_class->style_set = gimp_unit_combo_box_style_set;
+
+ gtk_widget_class_install_style_property (widget_class,
+ g_param_spec_double ("label-scale",
+ "Label Scale",
+ "The scale for the text cell renderer",
+ 0.0,
+ G_MAXDOUBLE,
+ 1.0,
+ GIMP_PARAM_READABLE));
+}
+
+static void
+gimp_unit_combo_box_init (GimpUnitComboBox *combo)
+{
+ GtkCellLayout *layout = GTK_CELL_LAYOUT (combo);
+ GtkCellRenderer *cell;
+
+ cell = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (layout, cell, TRUE);
+ gtk_cell_layout_set_attributes (layout, cell,
+ "text", GIMP_UNIT_STORE_UNIT_LONG_FORMAT,
+ NULL);
+
+ g_signal_connect (combo, "notify::popup-shown",
+ G_CALLBACK (gimp_unit_combo_box_popup_shown),
+ NULL);
+}
+
+static void
+gimp_unit_combo_box_style_set (GtkWidget *widget,
+ GtkStyle *prev_style)
+{
+ GtkCellLayout *layout;
+ GtkCellRenderer *cell;
+ gdouble scale;
+
+ GTK_WIDGET_CLASS (parent_class)->style_set (widget, prev_style);
+
+ gtk_widget_style_get (widget, "label-scale", &scale, NULL);
+
+ /* hackedehack ... */
+ layout = GTK_CELL_LAYOUT (gtk_bin_get_child (GTK_BIN (widget)));
+ gtk_cell_layout_clear (layout);
+
+ cell = g_object_new (GTK_TYPE_CELL_RENDERER_TEXT,
+ "scale", scale,
+ NULL);
+ gtk_cell_layout_pack_start (layout, cell, TRUE);
+ gtk_cell_layout_set_attributes (layout, cell,
+ "text", GIMP_UNIT_STORE_UNIT_SHORT_FORMAT,
+ NULL);
+}
+
+static void
+gimp_unit_combo_box_popup_shown (GtkWidget *widget,
+ const GParamSpec *pspec)
+{
+ GimpUnitStore *store;
+ gboolean shown;
+
+ g_object_get (widget,
+ "model", &store,
+ "popup-shown", &shown,
+ NULL);
+
+ if (store)
+ {
+ if (shown)
+ _gimp_unit_store_sync_units (store);
+
+ g_object_unref (store);
+ }
+}
+
+
+/**
+ * gimp_unit_combo_box_new:
+ *
+ * Return value: a new #GimpUnitComboBox.
+ **/
+GtkWidget *
+gimp_unit_combo_box_new (void)
+{
+ GtkWidget *combo_box;
+ GimpUnitStore *store;
+
+ store = gimp_unit_store_new (0);
+
+ combo_box = g_object_new (GIMP_TYPE_UNIT_COMBO_BOX,
+ "model", store,
+ NULL);
+
+ g_object_unref (store);
+
+ return combo_box;
+}
+
+/**
+ * gimp_unit_combo_box_new_with_model:
+ * @model: a GimpUnitStore
+ *
+ * Return value: a new #GimpUnitComboBox.
+ **/
+GtkWidget *
+gimp_unit_combo_box_new_with_model (GimpUnitStore *model)
+{
+ return g_object_new (GIMP_TYPE_UNIT_COMBO_BOX,
+ "model", model,
+ NULL);
+}
+
+GimpUnit
+gimp_unit_combo_box_get_active (GimpUnitComboBox *combo)
+{
+ GtkTreeIter iter;
+ gint unit;
+
+ g_return_val_if_fail (GIMP_IS_UNIT_COMBO_BOX (combo), -1);
+
+ gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter);
+
+ gtk_tree_model_get (gtk_combo_box_get_model (GTK_COMBO_BOX (combo)), &iter,
+ GIMP_UNIT_STORE_UNIT, &unit,
+ -1);
+
+ return (GimpUnit) unit;
+}
+
+void
+gimp_unit_combo_box_set_active (GimpUnitComboBox *combo,
+ GimpUnit unit)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gboolean iter_valid;
+
+ g_return_if_fail (GIMP_IS_UNIT_COMBO_BOX (combo));
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
+
+ _gimp_unit_store_sync_units (GIMP_UNIT_STORE (model));
+
+ for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
+ iter_valid;
+ iter_valid = gtk_tree_model_iter_next (model, &iter))
+ {
+ gint iter_unit;
+
+ gtk_tree_model_get (model, &iter,
+ GIMP_UNIT_STORE_UNIT, &iter_unit,
+ -1);
+
+ if (unit == (GimpUnit) iter_unit)
+ {
+ gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter);
+ break;
+ }
+ }
+}
diff --git a/libgimpwidgets/gimpunitcombobox.h b/libgimpwidgets/gimpunitcombobox.h
new file mode 100644
index 0000000..0439369
--- /dev/null
+++ b/libgimpwidgets/gimpunitcombobox.h
@@ -0,0 +1,71 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1999 Peter Mattis and Spencer Kimball
+ *
+ * gimpunitcombobox.h
+ * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_UNIT_COMBO_BOX_H__
+#define __GIMP_UNIT_COMBO_BOX_H__
+
+G_BEGIN_DECLS
+
+
+#define GIMP_TYPE_UNIT_COMBO_BOX (gimp_unit_combo_box_get_type ())
+#define GIMP_UNIT_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_UNIT_COMBO_BOX, GimpUnitComboBox))
+#define GIMP_UNIT_COMBO_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_UNIT_COMBO_BOX, GimpUnitComboBoxClass))
+#define GIMP_IS_UNIT_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_UNIT_COMBO_BOX))
+#define GIMP_IS_UNIT_COMBO_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_UNIT_COMBO_BOX))
+#define GIMP_UNIT_COMBO_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_UNIT_COMBO_BOX, GimpUnitComboBoxClass))
+
+
+typedef struct _GimpUnitComboBoxClass GimpUnitComboBoxClass;
+
+struct _GimpUnitComboBox
+{
+ GtkComboBox parent_instance;
+};
+
+struct _GimpUnitComboBoxClass
+{
+ GtkComboBoxClass parent_class;
+
+ /* Padding for future expansion */
+ void (*_gimp_reserved1) (void);
+ void (*_gimp_reserved2) (void);
+ void (*_gimp_reserved3) (void);
+ void (*_gimp_reserved4) (void);
+};
+
+
+GType gimp_unit_combo_box_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_unit_combo_box_new (void);
+GtkWidget * gimp_unit_combo_box_new_with_model (GimpUnitStore *model);
+
+GimpUnit gimp_unit_combo_box_get_active (GimpUnitComboBox *combo);
+void gimp_unit_combo_box_set_active (GimpUnitComboBox *combo,
+ GimpUnit unit);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_UNIT_COMBO_BOX_H__ */
diff --git a/libgimpwidgets/gimpunitmenu.c b/libgimpwidgets/gimpunitmenu.c
new file mode 100644
index 0000000..3777a62
--- /dev/null
+++ b/libgimpwidgets/gimpunitmenu.c
@@ -0,0 +1,633 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1999 Peter Mattis and Spencer Kimball
+ *
+ * gimpunitmenu.c
+ * Copyright (C) 1999 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#undef GSEAL_ENABLE
+
+#include <gegl.h>
+/* FIXME: #undef GTK_DISABLE_DEPRECATED */
+#undef GTK_DISABLE_DEPRECATED
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpdialog.h"
+#include "gimphelpui.h"
+#include "gimpwidgets.h"
+
+#undef GIMP_DISABLE_DEPRECATED
+#include "gimpoldwidgets.h"
+#include "gimpunitmenu.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpunitmenu
+ * @title: GimpUnitMenu
+ * @short_description: Widget for selecting a #GimpUnit.
+ * @see_also: #GimpUnit, #GimpSizeEntry, gimp_coordinates_new()
+ *
+ * This widget provides a #GtkOptionMenu which contains a list of
+ * #GimpUnit's.
+ *
+ * You can specify the string that will be displayed for each unit by
+ * passing a printf-like @format string to gimp_unit_menu_new().
+ *
+ * The constructor also lets you choose if the menu should contain
+ * items for GIMP_UNIT_PIXEL, GIMP_UNIT_PERCENT and a "More..." item
+ * which will pop up a dialog for selecting user-defined units.
+ *
+ * Whenever the user selects a unit from the menu or the dialog, the
+ * "unit_changed" signal will be emitted.
+ **/
+
+
+enum
+{
+ UNIT_CHANGED,
+ LAST_SIGNAL
+};
+
+enum
+{
+ UNIT_COLUMN,
+ FACTOR_COLUMN,
+ DATA_COLUMN,
+ NUM_COLUMNS
+};
+
+
+static void gimp_unit_menu_finalize (GObject *object);
+static void gimp_unit_menu_callback (GtkWidget *widget,
+ gpointer data);
+
+
+G_DEFINE_TYPE (GimpUnitMenu, gimp_unit_menu, GTK_TYPE_OPTION_MENU)
+
+#define parent_class gimp_unit_menu_parent_class
+
+static guint gimp_unit_menu_signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+gimp_unit_menu_class_init (GimpUnitMenuClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ /**
+ * GimpUnitMenu::unit-changed:
+ *
+ * This signal is emitted whenever the user selects a #GimpUnit from
+ * the #GimpUnitMenu.
+ **/
+ gimp_unit_menu_signals[UNIT_CHANGED] =
+ g_signal_new ("unit-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpUnitMenuClass, unit_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ object_class->finalize = gimp_unit_menu_finalize;
+
+ klass->unit_changed = NULL;
+}
+
+static void
+gimp_unit_menu_init (GimpUnitMenu *menu)
+{
+ menu->format = NULL;
+ menu->unit = GIMP_UNIT_PIXEL;
+ menu->show_pixels = FALSE;
+ menu->show_percent = FALSE;
+ menu->selection = NULL;
+ menu->tv = NULL;
+}
+
+static void
+gimp_unit_menu_finalize (GObject *object)
+{
+ GimpUnitMenu *menu = GIMP_UNIT_MENU (object);
+
+ g_clear_pointer (&menu->format, g_free);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+/**
+ * gimp_unit_menu_new:
+ * @format: A printf-like format string which is used to create the unit
+ * strings.
+ * @unit: The initially selected unit.
+ * @show_pixels: %TRUE if the unit menu should contain an item for
+ * GIMP_UNIT_PIXEL.
+ * @show_percent: %TRUE in the unit menu should contain an item for
+ * GIMP_UNIT_PERCENT.
+ * @show_custom: %TRUE if the unit menu should contain a "More..." item for
+ * opening the user-defined-unit selection dialog.
+ *
+ * Creates a new #GimpUnitMenu widget.
+ *
+ * For the @format string's possible expansions, see gimp_unit_format_string().
+ *
+ * Returns: A pointer to the new #GimpUnitMenu widget.
+ **/
+GtkWidget *
+gimp_unit_menu_new (const gchar *format,
+ GimpUnit unit,
+ gboolean show_pixels,
+ gboolean show_percent,
+ gboolean show_custom)
+{
+ GimpUnitMenu *unit_menu;
+ GtkWidget *menu;
+ GtkWidget *menuitem;
+ gchar *string;
+ GimpUnit u;
+
+ g_return_val_if_fail (((unit >= GIMP_UNIT_PIXEL) &&
+ (unit < gimp_unit_get_number_of_units ())) ||
+ (unit == GIMP_UNIT_PERCENT), NULL);
+
+ if ((unit >= gimp_unit_get_number_of_built_in_units ()) &&
+ (unit != GIMP_UNIT_PERCENT))
+ show_custom = TRUE;
+
+ unit_menu = g_object_new (GIMP_TYPE_UNIT_MENU, NULL);
+
+ unit_menu->format = g_strdup (format);
+ unit_menu->show_pixels = show_pixels;
+ unit_menu->show_percent = show_percent;
+
+ menu = gtk_menu_new ();
+ for (u = show_pixels ? GIMP_UNIT_PIXEL : GIMP_UNIT_INCH;
+ u < gimp_unit_get_number_of_built_in_units ();
+ u++)
+ {
+ /* special cases "pixels" and "percent" */
+ if (u == GIMP_UNIT_INCH)
+ {
+ if (show_percent)
+ {
+ string = gimp_unit_format_string (format, GIMP_UNIT_PERCENT);
+ menuitem = gtk_menu_item_new_with_label (string);
+ g_free (string);
+
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+ g_object_set_data (G_OBJECT (menuitem), "gimp_unit_menu",
+ GINT_TO_POINTER (GIMP_UNIT_PERCENT));
+ gtk_widget_show (menuitem);
+
+ g_signal_connect (menuitem, "activate",
+ G_CALLBACK (gimp_unit_menu_callback),
+ unit_menu);
+ }
+
+ if (show_pixels || show_percent)
+ {
+ menuitem = gtk_menu_item_new ();
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+ gtk_widget_set_sensitive (menuitem, FALSE);
+ gtk_widget_show (menuitem);
+ }
+ }
+
+ string = gimp_unit_format_string (format, u);
+ menuitem = gtk_menu_item_new_with_label (string);
+ g_free (string);
+
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+ g_object_set_data (G_OBJECT (menuitem), "gimp_unit_menu",
+ GINT_TO_POINTER (u));
+ gtk_widget_show (menuitem);
+
+ g_signal_connect (menuitem, "activate",
+ G_CALLBACK (gimp_unit_menu_callback),
+ unit_menu);
+ }
+
+ if ((unit >= gimp_unit_get_number_of_built_in_units ()) &&
+ (unit != GIMP_UNIT_PERCENT))
+ {
+ menuitem = gtk_menu_item_new ();
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+ gtk_widget_set_sensitive (menuitem, FALSE);
+ gtk_widget_show (menuitem);
+
+ string = gimp_unit_format_string (format, unit);
+ menuitem = gtk_menu_item_new_with_label (string);
+ g_free (string);
+
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+ g_object_set_data (G_OBJECT (menuitem), "gimp_unit_menu",
+ GINT_TO_POINTER (unit));
+ gtk_widget_show (menuitem);
+
+ g_signal_connect (menuitem, "activate",
+ G_CALLBACK (gimp_unit_menu_callback),
+ unit_menu);
+ }
+
+ if (show_custom)
+ {
+ menuitem = gtk_menu_item_new ();
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+ gtk_widget_set_sensitive (menuitem, FALSE);
+ gtk_widget_show (menuitem);
+
+ menuitem = gtk_menu_item_new_with_label (_("More..."));
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+ g_object_set_data (G_OBJECT (menuitem), "gimp_unit_menu",
+ GINT_TO_POINTER (GIMP_UNIT_PERCENT + 1));
+ gtk_widget_show (menuitem);
+
+ g_signal_connect (menuitem, "activate",
+ G_CALLBACK (gimp_unit_menu_callback),
+ unit_menu);
+ }
+
+ gtk_option_menu_set_menu (GTK_OPTION_MENU (unit_menu), menu);
+
+ unit_menu->unit = unit;
+ gtk_option_menu_set_history (GTK_OPTION_MENU (unit_menu),
+ (unit == GIMP_UNIT_PIXEL) ? 0 :
+ ((unit == GIMP_UNIT_PERCENT) ?
+ (show_pixels ? 1 : 0) :
+ (((show_pixels || show_percent) ? 2 : 0) +
+ ((show_pixels && show_percent) ? 1 : 0) +
+ ((unit < GIMP_UNIT_END) ?
+ (unit - 1) : GIMP_UNIT_END))));
+
+ return GTK_WIDGET (unit_menu);
+}
+
+/**
+ * gimp_unit_menu_set_unit:
+ * @menu: The unit menu you want to set the unit for.
+ * @unit: The new unit.
+ *
+ * Sets a new #GimpUnit for the specified #GimpUnitMenu.
+ **/
+void
+gimp_unit_menu_set_unit (GimpUnitMenu *menu,
+ GimpUnit unit)
+{
+ GtkWidget *menuitem = NULL;
+ GList *items;
+ gint user_unit;
+
+ g_return_if_fail (GIMP_IS_UNIT_MENU (menu));
+ g_return_if_fail (((unit >= GIMP_UNIT_PIXEL) &&
+ ((unit > GIMP_UNIT_PIXEL) || menu->show_pixels) &&
+ (unit < gimp_unit_get_number_of_units ())) ||
+ ((unit == GIMP_UNIT_PERCENT) && menu->show_percent));
+
+ if (unit == menu->unit)
+ return;
+
+ items = GTK_MENU_SHELL (GTK_OPTION_MENU (menu)->menu)->children;
+ user_unit = (GIMP_UNIT_END +
+ (((menu->show_pixels || menu->show_percent) ? 2 : 0) +
+ ((menu->show_pixels && menu->show_percent) ? 1 : 0)));
+
+ if ((unit >= GIMP_UNIT_END) && (unit != GIMP_UNIT_PERCENT))
+ {
+ gchar *string;
+
+ if ((g_list_length (items) - 3) >= user_unit)
+ {
+ gtk_widget_destroy (GTK_WIDGET (g_list_nth_data (items,
+ user_unit - 1)));
+ gtk_widget_destroy (GTK_WIDGET (g_list_nth_data (items,
+ user_unit - 1)));
+ }
+
+ menuitem = gtk_menu_item_new ();
+ gtk_menu_shell_append (GTK_MENU_SHELL (GTK_OPTION_MENU (menu)->menu),
+ menuitem);
+ gtk_widget_set_sensitive (menuitem, FALSE);
+ gtk_menu_reorder_child (GTK_MENU (GTK_OPTION_MENU (menu)->menu),
+ menuitem, user_unit - 1);
+ gtk_widget_show (menuitem);
+
+ string = gimp_unit_format_string (menu->format, unit);
+ menuitem = gtk_menu_item_new_with_label (string);
+ g_free (string);
+
+ gtk_menu_shell_append (GTK_MENU_SHELL (GTK_OPTION_MENU (menu)->menu),
+ menuitem);
+ g_object_set_data (G_OBJECT (menuitem), "gimp_unit_menu",
+ GINT_TO_POINTER (unit));
+ gtk_menu_reorder_child (GTK_MENU (GTK_OPTION_MENU (menu)->menu),
+ menuitem, user_unit);
+ gtk_widget_show (menuitem);
+
+ g_signal_connect (menuitem, "activate",
+ G_CALLBACK (gimp_unit_menu_callback),
+ menu);
+ }
+
+ menu->unit = unit;
+ gtk_option_menu_set_history (GTK_OPTION_MENU (menu),
+ (unit == GIMP_UNIT_PIXEL) ? 0 :
+ ((unit == GIMP_UNIT_PERCENT) ?
+ (menu->show_pixels ? 1 : 0) :
+ (((menu->show_pixels ||
+ menu->show_percent) ? 2 : 0) +
+ ((menu->show_pixels &&
+ menu->show_percent) ? 1 : 0) +
+ ((unit < GIMP_UNIT_END) ?
+ (unit - 1) : GIMP_UNIT_END))));
+
+ g_signal_emit (menu, gimp_unit_menu_signals[UNIT_CHANGED], 0);
+}
+
+/**
+ * gimp_unit_menu_get_unit:
+ * @menu: The unit menu you want to know the unit of.
+ *
+ * Returns the #GimpUnit the user has selected from the #GimpUnitMenu.
+ *
+ * Returns: The unit the user has selected.
+ **/
+GimpUnit
+gimp_unit_menu_get_unit (GimpUnitMenu *menu)
+{
+ g_return_val_if_fail (GIMP_IS_UNIT_MENU (menu), GIMP_UNIT_INCH);
+
+ return menu->unit;
+}
+
+
+/**
+ * gimp_unit_menu_set_pixel_digits:
+ * @menu: a #GimpUnitMenu
+ * @digits: the number of digits to display for a pixel size
+ *
+ * A GimpUnitMenu can be setup to control the number of digits shown
+ * by attached spinbuttons. Please refer to the documentation of
+ * gimp_unit_menu_update() to see how this is done.
+ *
+ * This function specifies the number of digits shown for a size in
+ * pixels. Usually this is 0 (only full pixels). If you want to allow
+ * the user to specify sub-pixel sizes using the attached spinbuttons,
+ * specify the number of digits after the decimal point here. You
+ * should do this after attaching your spinbuttons.
+ **/
+void
+gimp_unit_menu_set_pixel_digits (GimpUnitMenu *menu,
+ gint digits)
+{
+ GimpUnit unit;
+
+ g_return_if_fail (GIMP_IS_UNIT_MENU (menu));
+
+ menu->pixel_digits = digits;
+
+ gimp_unit_menu_update (GTK_WIDGET (menu), &unit);
+}
+
+/**
+ * gimp_unit_menu_get_pixel_digits:
+ * @menu: a #GimpUnitMenu
+ *
+ * Retrieve the number of digits for a pixel size as set by
+ * gimp_unit_menu_set_pixel_digits().
+ *
+ * Return value: the configured number of digits for a pixel size
+ **/
+gint
+gimp_unit_menu_get_pixel_digits (GimpUnitMenu *menu)
+{
+ g_return_val_if_fail (GIMP_IS_UNIT_MENU (menu), 0);
+
+ return menu->pixel_digits;
+}
+
+/* private callback of gimp_unit_menu_create_selection () */
+static void
+gimp_unit_menu_selection_response (GtkWidget *widget,
+ gint response_id,
+ GimpUnitMenu *menu)
+{
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ GtkTreeSelection *sel;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (menu->tv));
+ if (menu->selection && gtk_tree_selection_get_selected (sel, &model,
+ &iter))
+ {
+ GValue val = G_VALUE_INIT;
+ GimpUnit unit;
+
+ gtk_tree_model_get_value (model, &iter, 2, &val);
+ unit = (GimpUnit) g_value_get_int (&val);
+ g_value_unset (&val);
+
+ gimp_unit_menu_set_unit (menu, unit);
+ }
+ }
+
+ gtk_widget_destroy (menu->selection);
+}
+
+static void
+gimp_unit_menu_selection_row_activated_callback (GtkTreeView *tv,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ GimpUnitMenu *menu)
+{
+ gtk_dialog_response (GTK_DIALOG (menu->selection), GTK_RESPONSE_OK);
+}
+
+/* private function of gimp_unit_menu_callback () */
+static void
+gimp_unit_menu_create_selection (GimpUnitMenu *menu)
+{
+ GtkWidget *parent = gtk_widget_get_toplevel (GTK_WIDGET (menu));
+ GtkWidget *vbox;
+ GtkWidget *scrolled_win;
+ GtkListStore *list;
+ GtkTreeSelection *sel;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ GtkDialogFlags flags = GTK_DIALOG_DESTROY_WITH_PARENT;
+ GimpUnit unit;
+ gint num_units;
+
+ if (gtk_window_get_modal (GTK_WINDOW (parent)))
+ flags |= GTK_DIALOG_MODAL;
+
+ menu->selection = gimp_dialog_new (_("Unit Selection"), "gimp-unit-selection",
+ parent, flags,
+ gimp_standard_help_func,
+ "gimp-unit-dialog",
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (menu->selection),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ g_object_add_weak_pointer (G_OBJECT (menu->selection),
+ (gpointer) &menu->selection);
+
+ g_signal_connect (menu->selection, "response",
+ G_CALLBACK (gimp_unit_menu_selection_response),
+ menu);
+
+ g_signal_connect_object (menu, "unmap",
+ G_CALLBACK (gtk_widget_destroy),
+ menu->selection, G_CONNECT_SWAPPED);
+
+ /* the main vbox */
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 2);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (menu->selection))),
+ vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ /* the selection list */
+ scrolled_win = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win),
+ GTK_SHADOW_ETCHED_IN);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
+ GTK_POLICY_NEVER,
+ GTK_POLICY_ALWAYS);
+ gtk_box_pack_start (GTK_BOX (vbox), scrolled_win, TRUE, TRUE, 0);
+ gtk_widget_show (scrolled_win);
+
+ list = gtk_list_store_new (NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_INT);
+ menu->tv = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list));
+ g_object_unref (list);
+
+ gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (menu->tv),
+ -1, _("Unit"),
+ gtk_cell_renderer_text_new (),
+ "text", UNIT_COLUMN, NULL);
+ gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (menu->tv),
+ -1, _("Factor"),
+ gtk_cell_renderer_text_new (),
+ "text", FACTOR_COLUMN, NULL);
+
+ /* the unit lines */
+ num_units = gimp_unit_get_number_of_units ();
+ for (unit = GIMP_UNIT_END; unit < num_units; unit++)
+ {
+ gchar *string;
+
+ gtk_list_store_append (list, &iter);
+
+ string = gimp_unit_format_string (menu->format, unit);
+ gtk_list_store_set (list, &iter,
+ UNIT_COLUMN, string,
+ -1);
+ g_free (string);
+
+ string = gimp_unit_format_string ("(%f)", unit);
+ gtk_list_store_set (list, &iter,
+ FACTOR_COLUMN, string,
+ -1);
+ g_free (string);
+
+ gtk_list_store_set (list, &iter, DATA_COLUMN, unit, -1);
+ }
+
+ gtk_widget_set_size_request (menu->tv, -1, 150);
+
+ gtk_container_add (GTK_CONTAINER (scrolled_win), menu->tv);
+
+ g_signal_connect (menu->tv, "row-activated",
+ G_CALLBACK (gimp_unit_menu_selection_row_activated_callback),
+ menu);
+
+ gtk_widget_show (menu->tv);
+
+ g_signal_connect (menu->tv, "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &menu->tv);
+
+ gtk_widget_show (vbox);
+ gtk_widget_show (menu->selection);
+
+ if (menu->unit >= GIMP_UNIT_END)
+ {
+ path = gtk_tree_path_new ();
+ gtk_tree_path_append_index (path, menu->unit - GIMP_UNIT_END);
+
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (menu->tv));
+ gtk_tree_selection_select_path (sel, path);
+
+ gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (menu->tv), path, NULL,
+ FALSE, 0.0, 0.0);
+ }
+}
+
+static void
+gimp_unit_menu_callback (GtkWidget *widget,
+ gpointer data)
+{
+ GimpUnitMenu *menu = data;
+ GimpUnit new_unit;
+
+ new_unit = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (widget),
+ "gimp_unit_menu"));
+
+ if (menu->unit == new_unit)
+ return;
+
+ /* was "More..." selected? */
+ if (new_unit == (GIMP_UNIT_PERCENT + 1))
+ {
+ gtk_option_menu_set_history (GTK_OPTION_MENU (menu),
+ (menu->unit == GIMP_UNIT_PIXEL) ? 0 :
+ ((menu->unit == GIMP_UNIT_PERCENT) ?
+ (menu->show_pixels ? 1 : 0) :
+ ((menu->show_pixels ||
+ menu->show_percent ? 2 : 0) +
+ (menu->show_pixels &&
+ menu->show_percent ? 1 : 0) +
+ ((menu->unit < GIMP_UNIT_END) ?
+ menu->unit - 1 : GIMP_UNIT_END))));
+ if (! menu->selection)
+ gimp_unit_menu_create_selection (menu);
+ return;
+ }
+ else if (menu->selection)
+ {
+ gtk_widget_destroy (menu->selection);
+ }
+
+ gimp_unit_menu_set_unit (menu, new_unit);
+}
diff --git a/libgimpwidgets/gimpunitmenu.h b/libgimpwidgets/gimpunitmenu.h
new file mode 100644
index 0000000..99c371d
--- /dev/null
+++ b/libgimpwidgets/gimpunitmenu.h
@@ -0,0 +1,105 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpunitmenu.h
+ * Copyright (C) 1999 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef GIMP_DISABLE_DEPRECATED
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_UNIT_MENU_H__
+#define __GIMP_UNIT_MENU_H__
+
+#ifdef GTK_DISABLE_DEPRECATED
+#undef GTK_DISABLE_DEPRECATED
+#include <gtk/gtkoptionmenu.h>
+#define GTK_DISABLE_DEPRECATED
+#endif
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+#define GIMP_TYPE_UNIT_MENU (gimp_unit_menu_get_type ())
+#define GIMP_UNIT_MENU(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_UNIT_MENU, GimpUnitMenu))
+#define GIMP_UNIT_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_UNIT_MENU, GimpUnitMenuClass))
+#define GIMP_IS_UNIT_MENU(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GIMP_TYPE_UNIT_MENU))
+#define GIMP_IS_UNIT_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_UNIT_MENU))
+#define GIMP_UNIT_MENU_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_UNIT_MENU, GimpUnitMenuClass))
+
+
+typedef struct _GimpUnitMenuClass GimpUnitMenuClass;
+
+struct _GimpUnitMenu
+{
+ GtkOptionMenu parent_instance;
+
+ /* public (read only) */
+ gchar *format;
+ GimpUnit unit;
+ gint pixel_digits;
+
+ gboolean show_pixels;
+ gboolean show_percent;
+
+ /* private */
+ GtkWidget *selection;
+ GtkWidget *tv;
+};
+
+struct _GimpUnitMenuClass
+{
+ GtkOptionMenuClass parent_class;
+
+ void (* unit_changed) (GimpUnitMenu *menu);
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_unit_menu_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_unit_menu_new (const gchar *format,
+ GimpUnit unit,
+ gboolean show_pixels,
+ gboolean show_percent,
+ gboolean show_custom);
+
+void gimp_unit_menu_set_unit (GimpUnitMenu *menu,
+ GimpUnit unit);
+
+GimpUnit gimp_unit_menu_get_unit (GimpUnitMenu *menu);
+
+void gimp_unit_menu_set_pixel_digits (GimpUnitMenu *menu,
+ gint digits);
+gint gimp_unit_menu_get_pixel_digits (GimpUnitMenu *menu);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_UNIT_MENU_H__ */
+
+#endif /* GIMP_DISABLE_DEPRECATED */
diff --git a/libgimpwidgets/gimpunitstore.c b/libgimpwidgets/gimpunitstore.c
new file mode 100644
index 0000000..40ecc97
--- /dev/null
+++ b/libgimpwidgets/gimpunitstore.c
@@ -0,0 +1,937 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpunitstore.c
+ * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpunitstore.h"
+
+
+/**
+ * SECTION: gimpunitstore
+ * @title: GimpUnitStore
+ * @short_description: A model for units
+ *
+ * A model for #GimpUnit views
+ **/
+
+
+enum
+{
+ PROP_0,
+ PROP_NUM_VALUES,
+ PROP_HAS_PIXELS,
+ PROP_HAS_PERCENT,
+ PROP_SHORT_FORMAT,
+ PROP_LONG_FORMAT
+};
+
+typedef struct
+{
+ gint num_values;
+ gboolean has_pixels;
+ gboolean has_percent;
+
+ gchar *short_format;
+ gchar *long_format;
+
+ gdouble *values;
+ gdouble *resolutions;
+
+ GimpUnit synced_unit;
+} GimpUnitStorePrivate;
+
+#define GET_PRIVATE(obj) ((GimpUnitStorePrivate *) gimp_unit_store_get_instance_private ((GimpUnitStore *) (obj)))
+
+
+static void gimp_unit_store_tree_model_init (GtkTreeModelIface *iface);
+
+static void gimp_unit_store_finalize (GObject *object);
+static void gimp_unit_store_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_unit_store_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static GtkTreeModelFlags gimp_unit_store_get_flags (GtkTreeModel *tree_model);
+static gint gimp_unit_store_get_n_columns (GtkTreeModel *tree_model);
+static GType gimp_unit_store_get_column_type (GtkTreeModel *tree_model,
+ gint index);
+static gboolean gimp_unit_store_get_iter (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreePath *path);
+static GtkTreePath *gimp_unit_store_get_path (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static void gimp_unit_store_tree_model_get_value (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gint column,
+ GValue *value);
+static gboolean gimp_unit_store_iter_next (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static gboolean gimp_unit_store_iter_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent);
+static gboolean gimp_unit_store_iter_has_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static gint gimp_unit_store_iter_n_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static gboolean gimp_unit_store_iter_nth_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint n);
+static gboolean gimp_unit_store_iter_parent (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *child);
+
+
+G_DEFINE_TYPE_WITH_CODE (GimpUnitStore, gimp_unit_store, G_TYPE_OBJECT,
+ G_ADD_PRIVATE (GimpUnitStore)
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
+ gimp_unit_store_tree_model_init))
+
+#define parent_class gimp_unit_store_parent_class
+
+
+static GType column_types[GIMP_UNIT_STORE_UNIT_COLUMNS] =
+{
+ G_TYPE_INVALID,
+ G_TYPE_DOUBLE,
+ G_TYPE_INT,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING
+};
+
+
+static void
+gimp_unit_store_class_init (GimpUnitStoreClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ column_types[GIMP_UNIT_STORE_UNIT] = GIMP_TYPE_UNIT;
+
+ object_class->finalize = gimp_unit_store_finalize;
+ object_class->set_property = gimp_unit_store_set_property;
+ object_class->get_property = gimp_unit_store_get_property;
+
+ g_object_class_install_property (object_class, PROP_NUM_VALUES,
+ g_param_spec_int ("num-values",
+ "Num Values",
+ "The number of values this store provides",
+ 0, G_MAXINT, 0,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (object_class, PROP_HAS_PIXELS,
+ g_param_spec_boolean ("has-pixels",
+ "Has Pixels",
+ "Whether the store has GIMP_UNIT_PIXELS",
+ TRUE,
+ GIMP_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_HAS_PERCENT,
+ g_param_spec_boolean ("has-percent",
+ "Has Percent",
+ "Whether the store has GIMP_UNIT_PERCENT",
+ FALSE,
+ GIMP_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_SHORT_FORMAT,
+ g_param_spec_string ("short-format",
+ "Short Format",
+ "Format string for a short label",
+ "%a",
+ GIMP_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_LONG_FORMAT,
+ g_param_spec_string ("long-format",
+ "Long Format",
+ "Format string for a long label",
+ "%p",
+ GIMP_PARAM_READWRITE));
+}
+
+static void
+gimp_unit_store_init (GimpUnitStore *store)
+{
+ GimpUnitStorePrivate *private = GET_PRIVATE (store);
+
+ private->has_pixels = TRUE;
+ private->has_percent = FALSE;
+ private->short_format = g_strdup ("%a");
+ private->long_format = g_strdup ("%p");
+ private->synced_unit = gimp_unit_get_number_of_units () - 1;
+}
+
+static void
+gimp_unit_store_tree_model_init (GtkTreeModelIface *iface)
+{
+ iface->get_flags = gimp_unit_store_get_flags;
+ iface->get_n_columns = gimp_unit_store_get_n_columns;
+ iface->get_column_type = gimp_unit_store_get_column_type;
+ iface->get_iter = gimp_unit_store_get_iter;
+ iface->get_path = gimp_unit_store_get_path;
+ iface->get_value = gimp_unit_store_tree_model_get_value;
+ iface->iter_next = gimp_unit_store_iter_next;
+ iface->iter_children = gimp_unit_store_iter_children;
+ iface->iter_has_child = gimp_unit_store_iter_has_child;
+ iface->iter_n_children = gimp_unit_store_iter_n_children;
+ iface->iter_nth_child = gimp_unit_store_iter_nth_child;
+ iface->iter_parent = gimp_unit_store_iter_parent;
+}
+
+static void
+gimp_unit_store_finalize (GObject *object)
+{
+ GimpUnitStorePrivate *private = GET_PRIVATE (object);
+
+ g_clear_pointer (&private->short_format, g_free);
+ g_clear_pointer (&private->long_format, g_free);
+
+ g_clear_pointer (&private->values, g_free);
+ g_clear_pointer (&private->resolutions, g_free);
+ private->num_values = 0;
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_unit_store_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpUnitStorePrivate *private = GET_PRIVATE (object);
+
+ switch (property_id)
+ {
+ case PROP_NUM_VALUES:
+ g_return_if_fail (private->num_values == 0);
+ private->num_values = g_value_get_int (value);
+ if (private->num_values)
+ {
+ private->values = g_new0 (gdouble, private->num_values);
+ private->resolutions = g_new0 (gdouble, private->num_values);
+ }
+ break;
+ case PROP_HAS_PIXELS:
+ gimp_unit_store_set_has_pixels (GIMP_UNIT_STORE (object),
+ g_value_get_boolean (value));
+ break;
+ case PROP_HAS_PERCENT:
+ gimp_unit_store_set_has_percent (GIMP_UNIT_STORE (object),
+ g_value_get_boolean (value));
+ break;
+ case PROP_SHORT_FORMAT:
+ g_free (private->short_format);
+ private->short_format = g_value_dup_string (value);
+ if (! private->short_format)
+ private->short_format = g_strdup ("%a");
+ break;
+ case PROP_LONG_FORMAT:
+ g_free (private->long_format);
+ private->long_format = g_value_dup_string (value);
+ if (! private->long_format)
+ private->long_format = g_strdup ("%a");
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_unit_store_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpUnitStorePrivate *private = GET_PRIVATE (object);
+
+ switch (property_id)
+ {
+ case PROP_NUM_VALUES:
+ g_value_set_int (value, private->num_values);
+ break;
+ case PROP_HAS_PIXELS:
+ g_value_set_boolean (value, private->has_pixels);
+ break;
+ case PROP_HAS_PERCENT:
+ g_value_set_boolean (value, private->has_percent);
+ break;
+ case PROP_SHORT_FORMAT:
+ g_value_set_string (value, private->short_format);
+ break;
+ case PROP_LONG_FORMAT:
+ g_value_set_string (value, private->long_format);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static GtkTreeModelFlags
+gimp_unit_store_get_flags (GtkTreeModel *tree_model)
+{
+ return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY;
+}
+
+static gint
+gimp_unit_store_get_n_columns (GtkTreeModel *tree_model)
+{
+ GimpUnitStorePrivate *private = GET_PRIVATE (tree_model);
+
+ return GIMP_UNIT_STORE_UNIT_COLUMNS + private->num_values;
+}
+
+static GType
+gimp_unit_store_get_column_type (GtkTreeModel *tree_model,
+ gint index)
+{
+ g_return_val_if_fail (index >= 0, G_TYPE_INVALID);
+
+ if (index < GIMP_UNIT_STORE_UNIT_COLUMNS)
+ return column_types[index];
+
+ return G_TYPE_DOUBLE;
+}
+
+static gboolean
+gimp_unit_store_get_iter (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreePath *path)
+{
+ GimpUnitStorePrivate *private = GET_PRIVATE (tree_model);
+ gint index;
+ GimpUnit unit;
+
+ g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
+
+ index = gtk_tree_path_get_indices (path)[0];
+
+ unit = index;
+
+ if (! private->has_pixels)
+ unit++;
+
+ if (private->has_percent)
+ {
+ unit--;
+
+ if (private->has_pixels)
+ {
+ if (index == 0)
+ unit = GIMP_UNIT_PIXEL;
+ else if (index == 1)
+ unit = GIMP_UNIT_PERCENT;
+ }
+ else
+ {
+ if (index == 0)
+ unit = GIMP_UNIT_PERCENT;
+ }
+ }
+
+ if ((unit >= 0 && unit < gimp_unit_get_number_of_units ()) ||
+ ((unit == GIMP_UNIT_PERCENT && private->has_percent)))
+ {
+ iter->user_data = GINT_TO_POINTER (unit);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static GtkTreePath *
+gimp_unit_store_get_path (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ GimpUnitStorePrivate *private = GET_PRIVATE (tree_model);
+ GtkTreePath *path = gtk_tree_path_new ();
+ GimpUnit unit = GPOINTER_TO_INT (iter->user_data);
+ gint index;
+
+ index = unit;
+
+ if (! private->has_pixels)
+ index--;
+
+ if (private->has_percent)
+ {
+ index++;
+
+ if (private->has_pixels)
+ {
+ if (unit == GIMP_UNIT_PIXEL)
+ index = 0;
+ else if (unit == GIMP_UNIT_PERCENT)
+ index = 1;
+ }
+ else
+ {
+ if (unit == GIMP_UNIT_PERCENT)
+ index = 0;
+ }
+ }
+
+ gtk_tree_path_append_index (path, index);
+
+ return path;
+}
+
+static void
+gimp_unit_store_tree_model_get_value (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gint column,
+ GValue *value)
+{
+ GimpUnitStorePrivate *private = GET_PRIVATE (tree_model);
+ GimpUnit unit;
+
+ g_return_if_fail (column >= 0 &&
+ column < GIMP_UNIT_STORE_UNIT_COLUMNS + private->num_values);
+
+ g_value_init (value,
+ column < GIMP_UNIT_STORE_UNIT_COLUMNS ?
+ column_types[column] :
+ G_TYPE_DOUBLE);
+
+ unit = GPOINTER_TO_INT (iter->user_data);
+
+ if ((unit >= 0 && unit < gimp_unit_get_number_of_units ()) ||
+ ((unit == GIMP_UNIT_PERCENT && private->has_percent)))
+ {
+ switch (column)
+ {
+ case GIMP_UNIT_STORE_UNIT:
+ g_value_set_int (value, unit);
+ break;
+ case GIMP_UNIT_STORE_UNIT_FACTOR:
+ g_value_set_double (value, gimp_unit_get_factor (unit));
+ break;
+ case GIMP_UNIT_STORE_UNIT_DIGITS:
+ g_value_set_int (value, gimp_unit_get_digits (unit));
+ break;
+ case GIMP_UNIT_STORE_UNIT_IDENTIFIER:
+ g_value_set_static_string (value, gimp_unit_get_identifier (unit));
+ break;
+ case GIMP_UNIT_STORE_UNIT_SYMBOL:
+ g_value_set_static_string (value, gimp_unit_get_symbol (unit));
+ break;
+ case GIMP_UNIT_STORE_UNIT_ABBREVIATION:
+ g_value_set_static_string (value, gimp_unit_get_abbreviation (unit));
+ break;
+ case GIMP_UNIT_STORE_UNIT_SINGULAR:
+ g_value_set_static_string (value, gimp_unit_get_singular (unit));
+ break;
+ case GIMP_UNIT_STORE_UNIT_PLURAL:
+ g_value_set_static_string (value, gimp_unit_get_plural (unit));
+ break;
+ case GIMP_UNIT_STORE_UNIT_SHORT_FORMAT:
+ g_value_take_string (value,
+ gimp_unit_format_string (private->short_format,
+ unit));
+ break;
+ case GIMP_UNIT_STORE_UNIT_LONG_FORMAT:
+ g_value_take_string (value,
+ gimp_unit_format_string (private->long_format,
+ unit));
+ break;
+
+ default:
+ column -= GIMP_UNIT_STORE_UNIT_COLUMNS;
+ if (unit == GIMP_UNIT_PIXEL)
+ {
+ g_value_set_double (value, private->values[column]);
+ }
+ else if (private->resolutions[column])
+ {
+ g_value_set_double (value,
+ private->values[column] *
+ gimp_unit_get_factor (unit) /
+ private->resolutions[column]);
+ }
+ break;
+ }
+ }
+}
+
+static gboolean
+gimp_unit_store_iter_next (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ GimpUnitStorePrivate *private = GET_PRIVATE (tree_model);
+ GimpUnit unit = GPOINTER_TO_INT (iter->user_data);
+
+ if (unit == GIMP_UNIT_PIXEL && private->has_percent)
+ {
+ unit = GIMP_UNIT_PERCENT;
+ }
+ else if (unit == GIMP_UNIT_PERCENT)
+ {
+ unit = GIMP_UNIT_INCH;
+ }
+ else if (unit >= 0 && unit < gimp_unit_get_number_of_units () - 1)
+ {
+ unit++;
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ iter->user_data = GINT_TO_POINTER (unit);
+
+ return TRUE;
+}
+
+static gboolean
+gimp_unit_store_iter_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent)
+{
+ GimpUnitStorePrivate *private = GET_PRIVATE (tree_model);
+ GimpUnit unit;
+
+ /* this is a list, nodes have no children */
+ if (parent)
+ return FALSE;
+
+ if (private->has_pixels)
+ {
+ unit = GIMP_UNIT_PIXEL;
+ }
+ else if (private->has_percent)
+ {
+ unit = GIMP_UNIT_PERCENT;
+ }
+ else
+ {
+ unit = GIMP_UNIT_INCH;
+ }
+
+ iter->user_data = GINT_TO_POINTER (unit);
+
+ return TRUE;
+}
+
+static gboolean
+gimp_unit_store_iter_has_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ return FALSE;
+}
+
+static gint
+gimp_unit_store_iter_n_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ GimpUnitStorePrivate *private = GET_PRIVATE (tree_model);
+ gint n_children;
+
+ if (iter)
+ return 0;
+
+ n_children = gimp_unit_get_number_of_units ();
+
+ if (! private->has_pixels)
+ n_children--;
+
+ if (private->has_percent)
+ n_children++;
+
+ return n_children;
+}
+
+static gboolean
+gimp_unit_store_iter_nth_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint n)
+{
+ GimpUnitStorePrivate *private = GET_PRIVATE (tree_model);
+ gint n_children;
+
+ if (parent)
+ return FALSE;
+
+ n_children = gimp_unit_store_iter_n_children (tree_model, NULL);
+
+ if (n >= 0 && n < n_children)
+ {
+ GimpUnit unit = n;
+
+ if (! private->has_pixels)
+ unit++;
+
+ if (private->has_percent)
+ {
+ unit--;
+
+ if (private->has_pixels)
+ {
+ if (n == 0)
+ unit = GIMP_UNIT_PIXEL;
+ else if (n == 1)
+ unit = GIMP_UNIT_PERCENT;
+ }
+ else
+ {
+ if (n == 0)
+ unit = GIMP_UNIT_PERCENT;
+ }
+ }
+
+ iter->user_data = GINT_TO_POINTER (unit);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gimp_unit_store_iter_parent (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *child)
+{
+ return FALSE;
+}
+
+
+GimpUnitStore *
+gimp_unit_store_new (gint num_values)
+{
+ return g_object_new (GIMP_TYPE_UNIT_STORE,
+ "num-values", num_values,
+ NULL);
+}
+
+void
+gimp_unit_store_set_has_pixels (GimpUnitStore *store,
+ gboolean has_pixels)
+{
+ GimpUnitStorePrivate *private;
+
+ g_return_if_fail (GIMP_IS_UNIT_STORE (store));
+
+ private = GET_PRIVATE (store);
+
+ has_pixels = has_pixels ? TRUE : FALSE;
+
+ if (has_pixels != private->has_pixels)
+ {
+ GtkTreeModel *model = GTK_TREE_MODEL (store);
+ GtkTreePath *deleted_path = NULL;
+
+ if (! has_pixels)
+ {
+ GtkTreeIter iter;
+
+ gtk_tree_model_get_iter_first (model, &iter);
+ deleted_path = gtk_tree_model_get_path (model, &iter);
+ }
+
+ private->has_pixels = has_pixels;
+
+ if (has_pixels)
+ {
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ gtk_tree_model_get_iter_first (model, &iter);
+ path = gtk_tree_model_get_path (model, &iter);
+ gtk_tree_model_row_inserted (model, path, &iter);
+ gtk_tree_path_free (path);
+ }
+ else if (deleted_path)
+ {
+ gtk_tree_model_row_deleted (model, deleted_path);
+ gtk_tree_path_free (deleted_path);
+ }
+
+ g_object_notify (G_OBJECT (store), "has-pixels");
+ }
+}
+
+gboolean
+gimp_unit_store_get_has_pixels (GimpUnitStore *store)
+{
+ GimpUnitStorePrivate *private;
+
+ g_return_val_if_fail (GIMP_IS_UNIT_STORE (store), FALSE);
+
+ private = GET_PRIVATE (store);
+
+ return private->has_pixels;
+}
+
+void
+gimp_unit_store_set_has_percent (GimpUnitStore *store,
+ gboolean has_percent)
+{
+ GimpUnitStorePrivate *private;
+
+ g_return_if_fail (GIMP_IS_UNIT_STORE (store));
+
+ private = GET_PRIVATE (store);
+
+ has_percent = has_percent ? TRUE : FALSE;
+
+ if (has_percent != private->has_percent)
+ {
+ GtkTreeModel *model = GTK_TREE_MODEL (store);
+ GtkTreePath *deleted_path = NULL;
+
+ if (! has_percent)
+ {
+ GtkTreeIter iter;
+
+ gtk_tree_model_get_iter_first (model, &iter);
+ if (private->has_pixels)
+ gtk_tree_model_iter_next (model, &iter);
+ deleted_path = gtk_tree_model_get_path (model, &iter);
+ }
+
+ private->has_percent = has_percent;
+
+ if (has_percent)
+ {
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ gtk_tree_model_get_iter_first (model, &iter);
+ if (private->has_pixels)
+ gtk_tree_model_iter_next (model, &iter);
+ path = gtk_tree_model_get_path (model, &iter);
+ gtk_tree_model_row_inserted (model, path, &iter);
+ gtk_tree_path_free (path);
+ }
+ else if (deleted_path)
+ {
+ gtk_tree_model_row_deleted (model, deleted_path);
+ gtk_tree_path_free (deleted_path);
+ }
+
+ g_object_notify (G_OBJECT (store), "has-percent");
+ }
+}
+
+gboolean
+gimp_unit_store_get_has_percent (GimpUnitStore *store)
+{
+ GimpUnitStorePrivate *private;
+
+ g_return_val_if_fail (GIMP_IS_UNIT_STORE (store), FALSE);
+
+ private = GET_PRIVATE (store);
+
+ return private->has_percent;
+}
+
+void
+gimp_unit_store_set_pixel_value (GimpUnitStore *store,
+ gint index,
+ gdouble value)
+{
+ GimpUnitStorePrivate *private;
+
+ g_return_if_fail (GIMP_IS_UNIT_STORE (store));
+
+ private = GET_PRIVATE (store);
+
+ g_return_if_fail (index > 0 && index < private->num_values);
+
+ private->values[index] = value;
+}
+
+void
+gimp_unit_store_set_pixel_values (GimpUnitStore *store,
+ gdouble first_value,
+ ...)
+{
+ GimpUnitStorePrivate *private;
+ va_list args;
+ gint i;
+
+ g_return_if_fail (GIMP_IS_UNIT_STORE (store));
+
+ private = GET_PRIVATE (store);
+
+ va_start (args, first_value);
+
+ for (i = 0; i < private->num_values; )
+ {
+ private->values[i] = first_value;
+
+ if (++i < private->num_values)
+ first_value = va_arg (args, gdouble);
+ }
+
+ va_end (args);
+}
+
+void
+gimp_unit_store_set_resolution (GimpUnitStore *store,
+ gint index,
+ gdouble resolution)
+{
+ GimpUnitStorePrivate *private;
+
+ g_return_if_fail (GIMP_IS_UNIT_STORE (store));
+
+ private = GET_PRIVATE (store);
+
+ g_return_if_fail (index > 0 && index < private->num_values);
+
+ private->resolutions[index] = resolution;
+}
+
+void
+gimp_unit_store_set_resolutions (GimpUnitStore *store,
+ gdouble first_resolution,
+ ...)
+{
+ GimpUnitStorePrivate *private;
+ va_list args;
+ gint i;
+
+ g_return_if_fail (GIMP_IS_UNIT_STORE (store));
+
+ private = GET_PRIVATE (store);
+
+ va_start (args, first_resolution);
+
+ for (i = 0; i < private->num_values; )
+ {
+ private->resolutions[i] = first_resolution;
+
+ if (++i < private->num_values)
+ first_resolution = va_arg (args, gdouble);
+ }
+
+ va_end (args);
+}
+
+gdouble
+gimp_unit_store_get_value (GimpUnitStore *store,
+ GimpUnit unit,
+ gint index)
+{
+ GimpUnitStorePrivate *private;
+ GtkTreeIter iter;
+ GValue value = G_VALUE_INIT;
+
+ g_return_val_if_fail (GIMP_IS_UNIT_STORE (store), 0.0);
+
+ private = GET_PRIVATE (store);
+
+ g_return_val_if_fail (index >= 0 && index < private->num_values, 0.0);
+
+ iter.user_data = GINT_TO_POINTER (unit);
+
+ gimp_unit_store_tree_model_get_value (GTK_TREE_MODEL (store),
+ &iter,
+ GIMP_UNIT_STORE_FIRST_VALUE + index,
+ &value);
+
+ return g_value_get_double (&value);
+}
+
+void
+gimp_unit_store_get_values (GimpUnitStore *store,
+ GimpUnit unit,
+ gdouble *first_value,
+ ...)
+{
+ GimpUnitStorePrivate *private;
+ va_list args;
+ gint i;
+
+ g_return_if_fail (GIMP_IS_UNIT_STORE (store));
+
+ private = GET_PRIVATE (store);
+
+ va_start (args, first_value);
+
+ for (i = 0; i < private->num_values; )
+ {
+ if (first_value)
+ *first_value = gimp_unit_store_get_value (store, unit, i);
+
+ if (++i < private->num_values)
+ first_value = va_arg (args, gdouble *);
+ }
+
+ va_end (args);
+}
+
+void
+_gimp_unit_store_sync_units (GimpUnitStore *store)
+{
+ GimpUnitStorePrivate *private;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gboolean iter_valid;
+
+ g_return_if_fail (GIMP_IS_UNIT_STORE (store));
+
+ private = GET_PRIVATE (store);
+ model = GTK_TREE_MODEL (store);
+
+ for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
+ iter_valid;
+ iter_valid = gtk_tree_model_iter_next (model, &iter))
+ {
+ gint unit;
+
+ gtk_tree_model_get (model, &iter,
+ GIMP_UNIT_STORE_UNIT, &unit,
+ -1);
+
+ if (unit != GIMP_UNIT_PERCENT &&
+ unit > private->synced_unit)
+ {
+ GtkTreePath *path;
+
+ path = gtk_tree_model_get_path (model, &iter);
+ gtk_tree_model_row_inserted (model, path, &iter);
+ gtk_tree_path_free (path);
+ }
+ }
+
+ private->synced_unit = gimp_unit_get_number_of_units () - 1;
+}
diff --git a/libgimpwidgets/gimpunitstore.h b/libgimpwidgets/gimpunitstore.h
new file mode 100644
index 0000000..318449e
--- /dev/null
+++ b/libgimpwidgets/gimpunitstore.h
@@ -0,0 +1,113 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpunitstore.h
+ * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_UNIT_STORE_H__
+#define __GIMP_UNIT_STORE_H__
+
+G_BEGIN_DECLS
+
+
+enum
+{
+ GIMP_UNIT_STORE_UNIT,
+ GIMP_UNIT_STORE_UNIT_FACTOR,
+ GIMP_UNIT_STORE_UNIT_DIGITS,
+ GIMP_UNIT_STORE_UNIT_IDENTIFIER,
+ GIMP_UNIT_STORE_UNIT_SYMBOL,
+ GIMP_UNIT_STORE_UNIT_ABBREVIATION,
+ GIMP_UNIT_STORE_UNIT_SINGULAR,
+ GIMP_UNIT_STORE_UNIT_PLURAL,
+ GIMP_UNIT_STORE_UNIT_SHORT_FORMAT,
+ GIMP_UNIT_STORE_UNIT_LONG_FORMAT,
+ GIMP_UNIT_STORE_UNIT_COLUMNS,
+ GIMP_UNIT_STORE_FIRST_VALUE = GIMP_UNIT_STORE_UNIT_COLUMNS
+};
+
+
+#define GIMP_TYPE_UNIT_STORE (gimp_unit_store_get_type ())
+#define GIMP_UNIT_STORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_UNIT_STORE, GimpUnitStore))
+#define GIMP_UNIT_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_UNIT_STORE, GimpUnitStoreClass))
+#define GIMP_IS_UNIT_STORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_UNIT_STORE))
+#define GIMP_IS_UNIT_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_UNIT_STORE))
+#define GIMP_UNIT_STORE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_UNIT_STORE, GimpUnitStoreClass))
+
+
+typedef struct _GimpUnitStoreClass GimpUnitStoreClass;
+
+struct _GimpUnitStore
+{
+ GObject parent_instance;
+};
+
+struct _GimpUnitStoreClass
+{
+ GObjectClass parent_class;
+
+ /* Padding for future expansion */
+ void (*_gtk_reserved1) (void);
+ void (*_gtk_reserved2) (void);
+ void (*_gtk_reserved3) (void);
+ void (*_gtk_reserved4) (void);
+};
+
+
+GType gimp_unit_store_get_type (void) G_GNUC_CONST;
+
+GimpUnitStore * gimp_unit_store_new (gint num_values);
+
+void gimp_unit_store_set_has_pixels (GimpUnitStore *store,
+ gboolean has_pixels);
+gboolean gimp_unit_store_get_has_pixels (GimpUnitStore *store);
+
+void gimp_unit_store_set_has_percent (GimpUnitStore *store,
+ gboolean has_percent);
+gboolean gimp_unit_store_get_has_percent (GimpUnitStore *store);
+
+void gimp_unit_store_set_pixel_value (GimpUnitStore *store,
+ gint index,
+ gdouble value);
+void gimp_unit_store_set_pixel_values (GimpUnitStore *store,
+ gdouble first_value,
+ ...);
+void gimp_unit_store_set_resolution (GimpUnitStore *store,
+ gint index,
+ gdouble resolution);
+void gimp_unit_store_set_resolutions (GimpUnitStore *store,
+ gdouble first_resolution,
+ ...);
+gdouble gimp_unit_store_get_value (GimpUnitStore *store,
+ GimpUnit unit,
+ gint index);
+void gimp_unit_store_get_values (GimpUnitStore *store,
+ GimpUnit unit,
+ gdouble *first_value,
+ ...);
+
+void _gimp_unit_store_sync_units (GimpUnitStore *store);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_UNIT_STORE_H__ */
diff --git a/libgimpwidgets/gimpwidgets-error.c b/libgimpwidgets/gimpwidgets-error.c
new file mode 100644
index 0000000..bda4187
--- /dev/null
+++ b/libgimpwidgets/gimpwidgets-error.c
@@ -0,0 +1,40 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpwidgets-error.c
+ * Copyright (C) 2008 Martin Nordholts <martinn@svn.gnome.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+
+#include "gimpwidgets-error.h"
+
+
+/**
+ * gimp_widgets_error_quark:
+ *
+ * This function is never called directly. Use GIMP_WIDGETS_ERROR() instead.
+ *
+ * Return value: the #GQuark that defines the GIMP widgets error domain.
+ **/
+GQuark
+gimp_widgets_error_quark (void)
+{
+ return g_quark_from_static_string ("gimp-widgets-error-quark");
+}
diff --git a/libgimpwidgets/gimpwidgets-error.h b/libgimpwidgets/gimpwidgets-error.h
new file mode 100644
index 0000000..8722106
--- /dev/null
+++ b/libgimpwidgets/gimpwidgets-error.h
@@ -0,0 +1,58 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpwidgets-error.h
+ * Copyright (C) 2008 Martin Nordholts <martinn@svn.gnome.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_WIDGETS_ERROR_H__
+#define __GIMP_WIDGETS_ERROR_H__
+
+G_BEGIN_DECLS
+
+
+/**
+ * GimpWidgetsError:
+ * @GIMP_WIDGETS_PARSE_ERROR: A parse error has occured
+ *
+ * Types of errors returned by libgimpwidgets functions
+ **/
+typedef enum
+{
+ GIMP_WIDGETS_PARSE_ERROR
+} GimpWidgetsError;
+
+
+/**
+ * GIMP_WIDGETS_ERROR:
+ *
+ * The GIMP widgets error domain.
+ *
+ * Since: 2.8
+ */
+#define GIMP_WIDGETS_ERROR (gimp_widgets_error_quark ())
+
+GQuark gimp_widgets_error_quark (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif /* __GIMP_WIDGETS_ERROR_H__ */
diff --git a/libgimpwidgets/gimpwidgets-private.c b/libgimpwidgets/gimpwidgets-private.c
new file mode 100644
index 0000000..7c856c5
--- /dev/null
+++ b/libgimpwidgets/gimpwidgets-private.c
@@ -0,0 +1,106 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpwidgets-private.c
+ * Copyright (C) 2003 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <babl/babl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimpicons.h"
+#include "gimpwidgets-private.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+static gboolean gimp_widgets_initialized = FALSE;
+
+GimpHelpFunc _gimp_standard_help_func = NULL;
+GimpGetColorFunc _gimp_get_foreground_func = NULL;
+GimpGetColorFunc _gimp_get_background_func = NULL;
+GimpEnsureModulesFunc _gimp_ensure_modules_func = NULL;
+
+
+static void
+gimp_widgets_init_foreign_enums (void)
+{
+ static const GimpEnumDesc input_mode_descs[] =
+ {
+ { GDK_MODE_DISABLED, NC_("input-mode", "Disabled"), NULL },
+ { GDK_MODE_SCREEN, NC_("input-mode", "Screen"), NULL },
+ { GDK_MODE_WINDOW, NC_("input-mode", "Window"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ gimp_type_set_translation_domain (GDK_TYPE_INPUT_MODE,
+ GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (GDK_TYPE_INPUT_MODE, "input-mode");
+ gimp_enum_set_value_descriptions (GDK_TYPE_INPUT_MODE, input_mode_descs);
+}
+
+void
+gimp_widgets_init (GimpHelpFunc standard_help_func,
+ GimpGetColorFunc get_foreground_func,
+ GimpGetColorFunc get_background_func,
+ GimpEnsureModulesFunc ensure_modules_func)
+{
+ g_return_if_fail (standard_help_func != NULL);
+
+ if (gimp_widgets_initialized)
+ g_error ("gimp_widgets_init() must only be called once!");
+
+ _gimp_standard_help_func = standard_help_func;
+ _gimp_get_foreground_func = get_foreground_func;
+ _gimp_get_background_func = get_background_func;
+ _gimp_ensure_modules_func = ensure_modules_func;
+
+ babl_init (); /* color selectors use babl */
+
+ gimp_icons_init ();
+
+ gtk_window_set_default_icon_name (GIMP_ICON_WILBER);
+
+ gimp_widgets_init_foreign_enums ();
+
+ gimp_widgets_initialized = TRUE;
+}
+
+/* clean up babl (in particular, so that the fish cache is constructed) if the
+ * compiler supports destructors
+ */
+#ifdef HAVE_FUNC_ATTRIBUTE_DESTRUCTOR
+
+__attribute__ ((destructor))
+static void
+gimp_widgets_exit (void)
+{
+ if (gimp_widgets_initialized)
+ babl_exit ();
+}
+
+#elif defined (__GNUC__)
+
+#warning babl_init() not paired with babl_exit()
+
+#endif
diff --git a/libgimpwidgets/gimpwidgets-private.h b/libgimpwidgets/gimpwidgets-private.h
new file mode 100644
index 0000000..4cb1245
--- /dev/null
+++ b/libgimpwidgets/gimpwidgets-private.h
@@ -0,0 +1,47 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpwidgets-private.h
+ * Copyright (C) 2003 Sven Neumann <sven@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_WIDGETS_PRIVATE_H__
+#define __GIMP_WIDGETS_PRIVATE_H__
+
+
+typedef gboolean (* GimpGetColorFunc) (GimpRGB *color);
+typedef void (* GimpEnsureModulesFunc) (void);
+
+
+extern GimpHelpFunc _gimp_standard_help_func;
+extern GimpGetColorFunc _gimp_get_foreground_func;
+extern GimpGetColorFunc _gimp_get_background_func;
+extern GimpEnsureModulesFunc _gimp_ensure_modules_func;
+
+
+G_BEGIN_DECLS
+
+
+void gimp_widgets_init (GimpHelpFunc standard_help_func,
+ GimpGetColorFunc get_foreground_func,
+ GimpGetColorFunc get_background_func,
+ GimpEnsureModulesFunc ensure_modules_func);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_WIDGETS_PRIVATE_H__ */
diff --git a/libgimpwidgets/gimpwidgets.c b/libgimpwidgets/gimpwidgets.c
new file mode 100644
index 0000000..2958de9
--- /dev/null
+++ b/libgimpwidgets/gimpwidgets.c
@@ -0,0 +1,997 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpwidgets.c
+ * Copyright (C) 2000 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpmath/gimpmath.h"
+#include "libgimpbase/gimpbase.h"
+
+#include "gimpwidgets.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/* hack: declare prototype here instead of #undef GIMP_DISABLE_DEPRECATED */
+void gimp_toggle_button_sensitive_update (GtkToggleButton *toggle_button);
+
+
+/**
+ * SECTION: gimpwidgets
+ * @title: GimpWidgets
+ * @short_description: A collection of convenient widget constructors,
+ * standard callbacks and helper functions.
+ *
+ * A collection of convenient widget constructors, standard callbacks
+ * and helper functions.
+ **/
+
+
+/**
+ * gimp_radio_group_new:
+ * @in_frame: %TRUE if you want a #GtkFrame around the radio button group.
+ * @frame_title: The title of the Frame or %NULL if you don't want a title.
+ * @...: A %NULL-terminated @va_list describing the radio buttons.
+ *
+ * Convenience function to create a group of radio buttons embedded into
+ * a #GtkFrame or #GtkVBox.
+ *
+ * Returns: A #GtkFrame or #GtkVBox (depending on @in_frame).
+ **/
+GtkWidget *
+gimp_radio_group_new (gboolean in_frame,
+ const gchar *frame_title,
+
+ /* specify radio buttons as va_list:
+ * const gchar *label,
+ * GCallback callback,
+ * gpointer callback_data,
+ * gpointer item_data,
+ * GtkWidget **widget_ptr,
+ * gboolean active,
+ */
+
+ ...)
+{
+ GtkWidget *vbox;
+ GtkWidget *button;
+ GSList *group;
+
+ /* radio button variables */
+ const gchar *label;
+ GCallback callback;
+ gpointer callback_data;
+ gpointer item_data;
+ GtkWidget **widget_ptr;
+ gboolean active;
+
+ va_list args;
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+
+ group = NULL;
+
+ /* create the radio buttons */
+ va_start (args, frame_title);
+ label = va_arg (args, const gchar *);
+ while (label)
+ {
+ callback = va_arg (args, GCallback);
+ callback_data = va_arg (args, gpointer);
+ item_data = va_arg (args, gpointer);
+ widget_ptr = va_arg (args, GtkWidget **);
+ active = va_arg (args, gboolean);
+
+ if (label != (gpointer) 1)
+ button = gtk_radio_button_new_with_mnemonic (group, label);
+ else
+ button = gtk_radio_button_new (group);
+
+ group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+
+ if (item_data)
+ {
+ g_object_set_data (G_OBJECT (button), "gimp-item-data", item_data);
+
+ /* backward compatibility */
+ g_object_set_data (G_OBJECT (button), "user_data", item_data);
+ }
+
+ if (widget_ptr)
+ *widget_ptr = button;
+
+ if (active)
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
+
+ g_signal_connect (button, "toggled",
+ callback,
+ callback_data);
+
+ gtk_widget_show (button);
+
+ label = va_arg (args, const gchar *);
+ }
+ va_end (args);
+
+ if (in_frame)
+ {
+ GtkWidget *frame;
+
+ frame = gimp_frame_new (frame_title);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ return frame;
+ }
+
+ return vbox;
+}
+
+/**
+ * gimp_radio_group_new2:
+ * @in_frame: %TRUE if you want a #GtkFrame around the
+ * radio button group.
+ * @frame_title: The title of the Frame or %NULL if you don't want
+ * a title.
+ * @radio_button_callback: The callback each button's "toggled" signal will
+ * be connected with.
+ * @radio_button_callback_data:
+ * The data which will be passed to g_signal_connect().
+ * @initial: The @item_data of the initially pressed radio button.
+ * @...: A %NULL-terminated @va_list describing
+ * the radio buttons.
+ *
+ * Convenience function to create a group of radio buttons embedded into
+ * a #GtkFrame or #GtkVBox.
+ *
+ * Returns: A #GtkFrame or #GtkVBox (depending on @in_frame).
+ **/
+GtkWidget *
+gimp_radio_group_new2 (gboolean in_frame,
+ const gchar *frame_title,
+ GCallback radio_button_callback,
+ gpointer callback_data,
+ gpointer initial, /* item_data */
+
+ /* specify radio buttons as va_list:
+ * const gchar *label,
+ * gpointer item_data,
+ * GtkWidget **widget_ptr,
+ */
+
+ ...)
+{
+ GtkWidget *vbox;
+ GtkWidget *button;
+ GSList *group;
+
+ /* radio button variables */
+ const gchar *label;
+ gpointer item_data;
+ GtkWidget **widget_ptr;
+
+ va_list args;
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+
+ group = NULL;
+
+ /* create the radio buttons */
+ va_start (args, initial);
+ label = va_arg (args, const gchar *);
+
+ while (label)
+ {
+ item_data = va_arg (args, gpointer);
+ widget_ptr = va_arg (args, GtkWidget **);
+
+ if (label != (gpointer) 1)
+ button = gtk_radio_button_new_with_mnemonic (group, label);
+ else
+ button = gtk_radio_button_new (group);
+
+ group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+
+ if (item_data)
+ {
+ g_object_set_data (G_OBJECT (button), "gimp-item-data", item_data);
+
+ /* backward compatibility */
+ g_object_set_data (G_OBJECT (button), "user_data", item_data);
+ }
+
+ if (widget_ptr)
+ *widget_ptr = button;
+
+ if (initial == item_data)
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
+
+ g_signal_connect (button, "toggled",
+ radio_button_callback,
+ callback_data);
+
+ gtk_widget_show (button);
+
+ label = va_arg (args, const gchar *);
+ }
+ va_end (args);
+
+ if (in_frame)
+ {
+ GtkWidget *frame;
+
+ frame = gimp_frame_new (frame_title);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ return frame;
+ }
+
+ return vbox;
+}
+
+/**
+ * gimp_int_radio_group_new:
+ * @in_frame: %TRUE if you want a #GtkFrame around the
+ * radio button group.
+ * @frame_title: The title of the Frame or %NULL if you don't want
+ * a title.
+ * @radio_button_callback: The callback each button's "toggled" signal will
+ * be connected with.
+ * @radio_button_callback_data:
+ * The data which will be passed to g_signal_connect().
+ * @initial: The @item_data of the initially pressed radio button.
+ * @...: A %NULL-terminated @va_list describing
+ * the radio buttons.
+ *
+ * Convenience function to create a group of radio buttons embedded into
+ * a #GtkFrame or #GtkVBox. This function does the same thing as
+ * gimp_radio_group_new2(), but it takes integers as @item_data instead of
+ * pointers, since that is a very common case (mapping an enum to a radio
+ * group).
+ *
+ * Returns: A #GtkFrame or #GtkVBox (depending on @in_frame).
+ **/
+GtkWidget *
+gimp_int_radio_group_new (gboolean in_frame,
+ const gchar *frame_title,
+ GCallback radio_button_callback,
+ gpointer callback_data,
+ gint initial, /* item_data */
+
+ /* specify radio buttons as va_list:
+ * const gchar *label,
+ * gint item_data,
+ * GtkWidget **widget_ptr,
+ */
+
+ ...)
+{
+ GtkWidget *vbox;
+ GtkWidget *button;
+ GSList *group;
+
+ /* radio button variables */
+ const gchar *label;
+ gint item_data;
+ gpointer item_ptr;
+ GtkWidget **widget_ptr;
+
+ va_list args;
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+
+ group = NULL;
+
+ /* create the radio buttons */
+ va_start (args, initial);
+ label = va_arg (args, const gchar *);
+
+ while (label)
+ {
+ item_data = va_arg (args, gint);
+ widget_ptr = va_arg (args, GtkWidget **);
+
+ item_ptr = GINT_TO_POINTER (item_data);
+
+ if (label != GINT_TO_POINTER (1))
+ button = gtk_radio_button_new_with_mnemonic (group, label);
+ else
+ button = gtk_radio_button_new (group);
+
+ group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+
+ if (item_data)
+ {
+ g_object_set_data (G_OBJECT (button), "gimp-item-data", item_ptr);
+
+ /* backward compatibility */
+ g_object_set_data (G_OBJECT (button), "user_data", item_ptr);
+ }
+
+ if (widget_ptr)
+ *widget_ptr = button;
+
+ if (initial == item_data)
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
+
+ g_signal_connect (button, "toggled",
+ radio_button_callback,
+ callback_data);
+
+ gtk_widget_show (button);
+
+ label = va_arg (args, const gchar *);
+ }
+ va_end (args);
+
+ if (in_frame)
+ {
+ GtkWidget *frame;
+
+ frame = gimp_frame_new (frame_title);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ return frame;
+ }
+
+ return vbox;
+}
+
+/**
+ * gimp_radio_group_set_active:
+ * @radio_button: Pointer to a #GtkRadioButton.
+ * @item_data: The @item_data of the radio button you want to select.
+ *
+ * Calls gtk_toggle_button_set_active() with the radio button that was
+ * created with a matching @item_data.
+ **/
+void
+gimp_radio_group_set_active (GtkRadioButton *radio_button,
+ gpointer item_data)
+{
+ GtkWidget *button;
+ GSList *group;
+
+ g_return_if_fail (GTK_IS_RADIO_BUTTON (radio_button));
+
+ for (group = gtk_radio_button_get_group (radio_button);
+ group;
+ group = g_slist_next (group))
+ {
+ button = GTK_WIDGET (group->data);
+
+ if (g_object_get_data (G_OBJECT (button), "gimp-item-data") == item_data)
+ {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
+ return;
+ }
+ }
+}
+
+/**
+ * gimp_int_radio_group_set_active:
+ * @radio_button: Pointer to a #GtkRadioButton.
+ * @item_data: The @item_data of the radio button you want to select.
+ *
+ * Calls gtk_toggle_button_set_active() with the radio button that was created
+ * with a matching @item_data. This function does the same thing as
+ * gimp_radio_group_set_active(), but takes integers as @item_data instead
+ * of pointers.
+ **/
+void
+gimp_int_radio_group_set_active (GtkRadioButton *radio_button,
+ gint item_data)
+{
+ g_return_if_fail (GTK_IS_RADIO_BUTTON (radio_button));
+
+ gimp_radio_group_set_active (radio_button, GINT_TO_POINTER (item_data));
+}
+
+/**
+ * gimp_spin_button_new:
+ * @adjustment: Returns the spinbutton's #GtkAdjustment.
+ * @value: The initial value of the spinbutton.
+ * @lower: The lower boundary.
+ * @upper: The upper boundary.
+ * @step_increment: The spinbutton's step increment.
+ * @page_increment: The spinbutton's page increment (mouse button 2).
+ * @page_size: Ignored, spin buttons must always have a zero page size.
+ * @climb_rate: The spinbutton's climb rate.
+ * @digits: The spinbutton's number of decimal digits.
+ *
+ * This function is a shortcut for gtk_adjustment_new() and a
+ * subsequent gtk_spin_button_new(). It also calls
+ * gtk_spin_button_set_numeric() so that non-numeric text cannot be
+ * entered.
+ *
+ * Deprecated: 2.10: Use gtk_spin_button_new() instead.
+ *
+ * Returns: A #GtkSpinButton and its #GtkAdjustment.
+ **/
+GtkWidget *
+gimp_spin_button_new (GtkObject **adjustment, /* return value */
+ gdouble value,
+ gdouble lower,
+ gdouble upper,
+ gdouble step_increment,
+ gdouble page_increment,
+ gdouble page_size,
+ gdouble climb_rate,
+ guint digits)
+{
+ GtkWidget *spinbutton;
+
+ *adjustment = gtk_adjustment_new (value, lower, upper,
+ step_increment, page_increment, 0);
+
+ spinbutton = gimp_spin_button_new (GTK_ADJUSTMENT (*adjustment),
+ climb_rate, digits);
+
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+
+ return spinbutton;
+}
+
+static void
+gimp_random_seed_update (GtkWidget *widget,
+ gpointer data)
+{
+ GtkWidget *spinbutton = data;
+
+ /* Generate a new seed if the "New Seed" button was clicked or
+ * of the "Randomize" toggle is activated
+ */
+ if (! GTK_IS_TOGGLE_BUTTON (widget) ||
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ {
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (spinbutton),
+ (guint) g_random_int ());
+ }
+}
+
+/**
+ * gimp_random_seed_new:
+ * @seed: A pointer to the variable which stores the random seed.
+ * @random_seed: A pointer to a boolean indicating whether seed should be
+ * initialised randomly or not.
+ *
+ * Creates a widget that allows the user to control how the random number
+ * generator is initialized.
+ *
+ * Returns: A #GtkHBox containing a #GtkSpinButton for the seed and
+ * a #GtkButton for setting a random seed.
+ **/
+GtkWidget *
+gimp_random_seed_new (guint *seed,
+ gboolean *random_seed)
+{
+ GtkWidget *hbox;
+ GtkWidget *toggle;
+ GtkWidget *spinbutton;
+ GtkAdjustment *adj;
+ GtkWidget *button;
+
+ g_return_val_if_fail (seed != NULL, NULL);
+ g_return_val_if_fail (random_seed != NULL, NULL);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+
+ /* If we're being asked to generate a random seed, generate one. */
+ if (*random_seed)
+ *seed = g_random_int ();
+
+ adj = (GtkAdjustment *)
+ gtk_adjustment_new (*seed, 0, (guint32) -1, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adj, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
+ gtk_widget_show (spinbutton);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_uint_adjustment_update),
+ seed);
+
+ gimp_help_set_help_data (spinbutton,
+ _("Use this value for random number generator "
+ "seed - this allows you to repeat a "
+ "given \"random\" operation"), NULL);
+
+ button = gtk_button_new_with_mnemonic (_("_New Seed"));
+ gtk_misc_set_padding (GTK_MISC (gtk_bin_get_child (GTK_BIN (button))), 2, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ /* Send spinbutton as data so that we can change the value in
+ * gimp_random_seed_update()
+ */
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (gimp_random_seed_update),
+ spinbutton);
+
+ gimp_help_set_help_data (button,
+ _("Seed random number generator with a generated "
+ "random number"),
+ NULL);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("_Randomize"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), *random_seed);
+ gtk_box_pack_start (GTK_BOX (hbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ random_seed);
+
+ /* Need to create a new seed when the "Randomize" toggle is activated */
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_random_seed_update),
+ spinbutton);
+
+ g_object_set_data (G_OBJECT (hbox), "spinbutton", spinbutton);
+ g_object_set_data (G_OBJECT (hbox), "button", button);
+ g_object_set_data (G_OBJECT (hbox), "toggle", toggle);
+
+ g_object_bind_property (toggle, "active",
+ spinbutton, "sensitive",
+ G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
+ g_object_bind_property (toggle, "active",
+ button, "sensitive",
+ G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
+
+ return hbox;
+}
+
+typedef struct
+{
+ GimpChainButton *chainbutton;
+ gboolean chain_constrains_ratio;
+ gdouble orig_x;
+ gdouble orig_y;
+ gdouble last_x;
+ gdouble last_y;
+} GimpCoordinatesData;
+
+static void
+gimp_coordinates_callback (GtkWidget *widget,
+ GimpCoordinatesData *data)
+{
+ gdouble new_x;
+ gdouble new_y;
+
+ new_x = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 0);
+ new_y = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 1);
+
+ if (gimp_chain_button_get_active (data->chainbutton))
+ {
+ if (data->chain_constrains_ratio)
+ {
+ if ((data->orig_x != 0) && (data->orig_y != 0))
+ {
+ g_signal_handlers_block_by_func (widget,
+ gimp_coordinates_callback,
+ data);
+
+ if (ROUND (new_x) != ROUND (data->last_x))
+ {
+ data->last_x = new_x;
+ new_y = (new_x * data->orig_y) / data->orig_x;
+
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (widget), 1,
+ new_y);
+ data->last_y
+ = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 1);
+ }
+ else if (ROUND (new_y) != ROUND (data->last_y))
+ {
+ data->last_y = new_y;
+ new_x = (new_y * data->orig_x) / data->orig_y;
+
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (widget), 0,
+ new_x);
+ data->last_x
+ = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 0);
+ }
+
+ g_signal_handlers_unblock_by_func (widget,
+ gimp_coordinates_callback,
+ data);
+ }
+ }
+ else
+ {
+ if (new_x != data->last_x)
+ {
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (widget), 1, new_x);
+ data->last_y = data->last_x
+ = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 1);
+ }
+ else if (new_y != data->last_y)
+ {
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (widget), 0, new_y);
+ data->last_x = data->last_y
+ = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 0);
+ }
+ }
+ }
+ else
+ {
+ if (new_x != data->last_x)
+ data->last_x = new_x;
+ if (new_y != data->last_y)
+ data->last_y = new_y;
+ }
+}
+
+static void
+gimp_coordinates_data_free (GimpCoordinatesData *data)
+{
+ g_slice_free (GimpCoordinatesData, data);
+}
+
+static void
+gimp_coordinates_chainbutton_toggled (GimpChainButton *button,
+ GimpSizeEntry *entry)
+{
+ if (gimp_chain_button_get_active (button))
+ {
+ GimpCoordinatesData *data;
+
+ data = g_object_get_data (G_OBJECT (entry), "coordinates-data");
+
+ data->orig_x = gimp_size_entry_get_refval (entry, 0);
+ data->orig_y = gimp_size_entry_get_refval (entry, 1);
+ }
+}
+
+/**
+ * gimp_coordinates_new:
+ * @unit: The initial unit of the #GimpUnitMenu.
+ * @unit_format: A printf-like unit-format string as is used with
+ * gimp_unit_menu_new().
+ * @menu_show_pixels: %TRUE if the #GimpUnitMenu should contain an item
+ * for GIMP_UNIT_PIXEL.
+ * @menu_show_percent: %TRUE if the #GimpUnitMenu should contain an item
+ * for GIMP_UNIT_PERCENT.
+ * @spinbutton_width: The horizontal size of the #GimpSizeEntry's
+ * #GtkSpinButton's.
+ * @update_policy: The update policy for the #GimpSizeEntry.
+ * @chainbutton_active: %TRUE if the attached #GimpChainButton should be
+ * active.
+ * @chain_constrains_ratio: %TRUE if the chainbutton should constrain the
+ * fields' aspect ratio. If %FALSE, the values will
+ * be constrained.
+ * @xlabel: The label for the X coordinate.
+ * @x: The initial value of the X coordinate.
+ * @xres: The horizontal resolution in DPI.
+ * @lower_boundary_x: The lower boundary of the X coordinate.
+ * @upper_boundary_x: The upper boundary of the X coordinate.
+ * @xsize_0: The X value which will be treated as 0%.
+ * @xsize_100: The X value which will be treated as 100%.
+ * @ylabel: The label for the Y coordinate.
+ * @y: The initial value of the Y coordinate.
+ * @yres: The vertical resolution in DPI.
+ * @lower_boundary_y: The lower boundary of the Y coordinate.
+ * @upper_boundary_y: The upper boundary of the Y coordinate.
+ * @ysize_0: The Y value which will be treated as 0%.
+ * @ysize_100: The Y value which will be treated as 100%.
+ *
+ * Convenience function that creates a #GimpSizeEntry with two fields for x/y
+ * coordinates/sizes with a #GimpChainButton attached to constrain either the
+ * two fields' values or the ratio between them.
+ *
+ * Returns: The new #GimpSizeEntry.
+ **/
+GtkWidget *
+gimp_coordinates_new (GimpUnit unit,
+ const gchar *unit_format,
+ gboolean menu_show_pixels,
+ gboolean menu_show_percent,
+ gint spinbutton_width,
+ GimpSizeEntryUpdatePolicy update_policy,
+
+ gboolean chainbutton_active,
+ gboolean chain_constrains_ratio,
+
+ const gchar *xlabel,
+ gdouble x,
+ gdouble xres,
+ gdouble lower_boundary_x,
+ gdouble upper_boundary_x,
+ gdouble xsize_0, /* % */
+ gdouble xsize_100, /* % */
+
+ const gchar *ylabel,
+ gdouble y,
+ gdouble yres,
+ gdouble lower_boundary_y,
+ gdouble upper_boundary_y,
+ gdouble ysize_0, /* % */
+ gdouble ysize_100 /* % */)
+{
+ GimpCoordinatesData *data;
+ GtkAdjustment *adjustment;
+ GtkWidget *spinbutton;
+ GtkWidget *sizeentry;
+ GtkWidget *chainbutton;
+
+ adjustment = (GtkAdjustment *) gtk_adjustment_new (1, 0, 1, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adjustment, 1.0, 2);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+
+ if (spinbutton_width > 0)
+ {
+ if (spinbutton_width < 17)
+ gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), spinbutton_width);
+ else
+ gtk_widget_set_size_request (spinbutton, spinbutton_width, -1);
+ }
+
+ sizeentry = gimp_size_entry_new (1, unit, unit_format,
+ menu_show_pixels,
+ menu_show_percent,
+ FALSE,
+ spinbutton_width,
+ update_policy);
+ gtk_table_set_col_spacing (GTK_TABLE (sizeentry), 0, 4);
+ gtk_table_set_col_spacing (GTK_TABLE (sizeentry), 2, 4);
+ gimp_size_entry_add_field (GIMP_SIZE_ENTRY (sizeentry),
+ GTK_SPIN_BUTTON (spinbutton), NULL);
+ gtk_table_attach_defaults (GTK_TABLE (sizeentry), spinbutton, 1, 2, 0, 1);
+ gtk_widget_show (spinbutton);
+
+ gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (sizeentry),
+ (update_policy == GIMP_SIZE_ENTRY_UPDATE_RESOLUTION) ||
+ (menu_show_pixels == FALSE) ?
+ GIMP_UNIT_INCH : GIMP_UNIT_PIXEL);
+
+ gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (sizeentry), 0, xres, TRUE);
+ gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (sizeentry), 1, yres, TRUE);
+ gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (sizeentry), 0,
+ lower_boundary_x, upper_boundary_x);
+ gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (sizeentry), 1,
+ lower_boundary_y, upper_boundary_y);
+
+ if (menu_show_percent)
+ {
+ gimp_size_entry_set_size (GIMP_SIZE_ENTRY (sizeentry), 0,
+ xsize_0, xsize_100);
+ gimp_size_entry_set_size (GIMP_SIZE_ENTRY (sizeentry), 1,
+ ysize_0, ysize_100);
+ }
+
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (sizeentry), 0, x);
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (sizeentry), 1, y);
+
+ gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (sizeentry),
+ xlabel, 0, 0, 0.0);
+ gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (sizeentry),
+ ylabel, 1, 0, 0.0);
+
+ chainbutton = gimp_chain_button_new (GIMP_CHAIN_RIGHT);
+
+ if (chainbutton_active)
+ gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (chainbutton), TRUE);
+
+ gtk_table_attach (GTK_TABLE (sizeentry), chainbutton, 2, 3, 0, 2,
+ GTK_SHRINK | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ gtk_widget_show (chainbutton);
+
+ data = g_slice_new (GimpCoordinatesData);
+
+ data->chainbutton = GIMP_CHAIN_BUTTON (chainbutton);
+ data->chain_constrains_ratio = chain_constrains_ratio;
+ data->orig_x = x;
+ data->orig_y = y;
+ data->last_x = x;
+ data->last_y = y;
+
+ g_object_set_data_full (G_OBJECT (sizeentry), "coordinates-data",
+ data,
+ (GDestroyNotify) gimp_coordinates_data_free);
+
+ g_signal_connect (sizeentry, "value-changed",
+ G_CALLBACK (gimp_coordinates_callback),
+ data);
+
+ g_object_set_data (G_OBJECT (sizeentry), "chainbutton", chainbutton);
+
+ g_signal_connect (chainbutton, "toggled",
+ G_CALLBACK (gimp_coordinates_chainbutton_toggled),
+ sizeentry);
+
+ return sizeentry;
+}
+
+
+/*
+ * Standard Callbacks
+ */
+
+/**
+ * gimp_toggle_button_sensitive_update:
+ * @toggle_button: The #GtkToggleButton the "set_sensitive" and
+ * "inverse_sensitive" lists are attached to.
+ *
+ * If you attached a pointer to a #GtkWidget with g_object_set_data() and
+ * the "set_sensitive" key to the #GtkToggleButton, the sensitive state of
+ * the attached widget will be set according to the toggle button's
+ * "active" state.
+ *
+ * You can attach an arbitrary list of widgets by attaching another
+ * "set_sensitive" data pointer to the first widget (and so on...).
+ *
+ * This function can also set the sensitive state according to the toggle
+ * button's inverse "active" state by attaching widgets with the
+ * "inverse_sensitive" key.
+ *
+ * Deprecated: use g_object_bind_property() instead of using the
+ * "set_sensitive" and "inverse_sensitive" data pointers.
+ **/
+void
+gimp_toggle_button_sensitive_update (GtkToggleButton *toggle_button)
+{
+ GtkWidget *set_sensitive;
+ gboolean active;
+
+ active = gtk_toggle_button_get_active (toggle_button);
+
+ set_sensitive =
+ g_object_get_data (G_OBJECT (toggle_button), "set_sensitive");
+ while (set_sensitive)
+ {
+ gtk_widget_set_sensitive (set_sensitive, active);
+ set_sensitive =
+ g_object_get_data (G_OBJECT (set_sensitive), "set_sensitive");
+ }
+
+ set_sensitive =
+ g_object_get_data (G_OBJECT (toggle_button), "inverse_sensitive");
+ while (set_sensitive)
+ {
+ gtk_widget_set_sensitive (set_sensitive, ! active);
+ set_sensitive =
+ g_object_get_data (G_OBJECT (set_sensitive), "inverse_sensitive");
+ }
+}
+
+/**
+ * gimp_toggle_button_update:
+ * @widget: A #GtkToggleButton.
+ * @data: A pointer to a #gint variable which will store the value of
+ * gtk_toggle_button_get_active().
+ *
+ * Note that this function calls gimp_toggle_button_sensitive_update()
+ * which is a deprecated hack you shouldn't use. See that function's
+ * documentation for a proper replacement of its functionality.
+ **/
+void
+gimp_toggle_button_update (GtkWidget *widget,
+ gpointer data)
+{
+ gint *toggle_val = (gint *) data;
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ *toggle_val = TRUE;
+ else
+ *toggle_val = FALSE;
+
+ gimp_toggle_button_sensitive_update (GTK_TOGGLE_BUTTON (widget));
+}
+
+/**
+ * gimp_radio_button_update:
+ * @widget: A #GtkRadioButton.
+ * @data: A pointer to a #gint variable which will store the value of
+ * GPOINTER_TO_INT (g_object_get_data (@widget, "gimp-item-data")).
+ *
+ * Note that this function calls gimp_toggle_button_sensitive_update()
+ * which is a deprecated hack you shouldn't use. See that function's
+ * documentation for a proper replacement of its functionality.
+ **/
+void
+gimp_radio_button_update (GtkWidget *widget,
+ gpointer data)
+{
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ {
+ gint *toggle_val = (gint *) data;
+
+ *toggle_val = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
+ "gimp-item-data"));
+ }
+
+ gimp_toggle_button_sensitive_update (GTK_TOGGLE_BUTTON (widget));
+}
+
+/**
+ * gimp_int_adjustment_update:
+ * @adjustment: A #GtkAdjustment.
+ * @data: A pointer to a #gint variable which will store the
+ * @adjustment's value.
+ *
+ * Note that the #GtkAdjustment's value (which is a #gdouble) will be
+ * rounded with RINT().
+ **/
+void
+gimp_int_adjustment_update (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ gint *val = (gint *) data;
+
+ *val = RINT (gtk_adjustment_get_value (adjustment));
+}
+
+/**
+ * gimp_uint_adjustment_update:
+ * @adjustment: A #GtkAdjustment.
+ * @data: A pointer to a #guint variable which will store the
+ * @adjustment's value.
+ *
+ * Note that the #GtkAdjustment's value (which is a #gdouble) will be rounded
+ * with (#guint) (value + 0.5).
+ **/
+void
+gimp_uint_adjustment_update (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ guint *val = (guint *) data;
+
+ *val = (guint) (gtk_adjustment_get_value (adjustment) + 0.5);
+}
+
+/**
+ * gimp_float_adjustment_update:
+ * @adjustment: A #GtkAdjustment.
+ * @data: A pointer to a #gfloat variable which will store the
+ * @adjustment's value.
+ **/
+void
+gimp_float_adjustment_update (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ gfloat *val = (gfloat *) data;
+
+ *val = gtk_adjustment_get_value (adjustment);
+
+}
+
+/**
+ * gimp_double_adjustment_update:
+ * @adjustment: A #GtkAdjustment.
+ * @data: A pointer to a #gdouble variable which will store the
+ * @adjustment's value.
+ **/
+void
+gimp_double_adjustment_update (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ gdouble *val = (gdouble *) data;
+
+ *val = gtk_adjustment_get_value (adjustment);
+}
diff --git a/libgimpwidgets/gimpwidgets.def b/libgimpwidgets/gimpwidgets.def
new file mode 100644
index 0000000..ec1e44c
--- /dev/null
+++ b/libgimpwidgets/gimpwidgets.def
@@ -0,0 +1,470 @@
+EXPORTS
+ gdk_cairo_get_clip_rectangle
+ gdk_event_triggers_context_menu
+ gdk_keymap_get_modifier_mask
+ gdk_screen_get_monitor_workarea
+ gimp_aspect_type_get_type
+ gimp_browser_add_search_types
+ gimp_browser_get_type
+ gimp_browser_new
+ gimp_browser_set_widget
+ gimp_browser_show_message
+ gimp_busy_box_get_message
+ gimp_busy_box_get_type
+ gimp_busy_box_new
+ gimp_busy_box_set_message
+ gimp_button_extended_clicked
+ gimp_button_get_type
+ gimp_button_new
+ gimp_cairo_set_focus_line_pattern
+ gimp_cairo_surface_create_from_pixbuf
+ gimp_cell_renderer_color_get_type
+ gimp_cell_renderer_color_new
+ gimp_cell_renderer_toggle_clicked
+ gimp_cell_renderer_toggle_get_type
+ gimp_cell_renderer_toggle_new
+ gimp_chain_button_get_active
+ gimp_chain_button_get_icon_size
+ gimp_chain_button_get_type
+ gimp_chain_button_new
+ gimp_chain_button_set_active
+ gimp_chain_button_set_icon_size
+ gimp_chain_position_get_type
+ gimp_color_area_get_color
+ gimp_color_area_get_type
+ gimp_color_area_has_alpha
+ gimp_color_area_new
+ gimp_color_area_set_color
+ gimp_color_area_set_color_config
+ gimp_color_area_set_draw_border
+ gimp_color_area_set_type
+ gimp_color_area_type_get_type
+ gimp_color_button_get_color
+ gimp_color_button_get_title
+ gimp_color_button_get_type
+ gimp_color_button_get_ui_manager
+ gimp_color_button_get_update
+ gimp_color_button_has_alpha
+ gimp_color_button_new
+ gimp_color_button_set_color
+ gimp_color_button_set_color_config
+ gimp_color_button_set_title
+ gimp_color_button_set_type
+ gimp_color_button_set_update
+ gimp_color_display_changed
+ gimp_color_display_clone
+ gimp_color_display_configure
+ gimp_color_display_configure_reset
+ gimp_color_display_convert
+ gimp_color_display_convert_buffer
+ gimp_color_display_convert_surface
+ gimp_color_display_get_config
+ gimp_color_display_get_enabled
+ gimp_color_display_get_managed
+ gimp_color_display_get_type
+ gimp_color_display_load_state
+ gimp_color_display_new
+ gimp_color_display_save_state
+ gimp_color_display_set_enabled
+ gimp_color_display_stack_add
+ gimp_color_display_stack_changed
+ gimp_color_display_stack_clone
+ gimp_color_display_stack_convert
+ gimp_color_display_stack_convert_buffer
+ gimp_color_display_stack_convert_surface
+ gimp_color_display_stack_get_type
+ gimp_color_display_stack_new
+ gimp_color_display_stack_remove
+ gimp_color_display_stack_reorder_down
+ gimp_color_display_stack_reorder_up
+ gimp_color_hex_entry_get_color
+ gimp_color_hex_entry_get_type
+ gimp_color_hex_entry_new
+ gimp_color_hex_entry_set_color
+ gimp_color_notebook_get_type
+ gimp_color_notebook_set_has_page
+ gimp_color_picker_cursors_get_resource
+ gimp_color_profile_chooser_dialog_get_type
+ gimp_color_profile_chooser_dialog_new
+ gimp_color_profile_combo_box_add
+ gimp_color_profile_combo_box_add_file
+ gimp_color_profile_combo_box_get_active
+ gimp_color_profile_combo_box_get_active_file
+ gimp_color_profile_combo_box_get_type
+ gimp_color_profile_combo_box_new
+ gimp_color_profile_combo_box_new_with_model
+ gimp_color_profile_combo_box_set_active
+ gimp_color_profile_combo_box_set_active_file
+ gimp_color_profile_store_add
+ gimp_color_profile_store_add_file
+ gimp_color_profile_store_get_type
+ gimp_color_profile_store_new
+ gimp_color_profile_view_get_type
+ gimp_color_profile_view_new
+ gimp_color_profile_view_set_error
+ gimp_color_profile_view_set_profile
+ gimp_color_scale_entry_new
+ gimp_color_scale_get_type
+ gimp_color_scale_new
+ gimp_color_scale_set_channel
+ gimp_color_scale_set_color
+ gimp_color_scale_set_color_config
+ gimp_color_scales_get_show_rgb_u8
+ gimp_color_scales_get_type
+ gimp_color_scales_set_show_rgb_u8
+ gimp_color_select_get_type
+ gimp_color_selection_color_changed
+ gimp_color_selection_get_color
+ gimp_color_selection_get_old_color
+ gimp_color_selection_get_show_alpha
+ gimp_color_selection_get_type
+ gimp_color_selection_new
+ gimp_color_selection_reset
+ gimp_color_selection_set_color
+ gimp_color_selection_set_config
+ gimp_color_selection_set_old_color
+ gimp_color_selection_set_show_alpha
+ gimp_color_selector_channel_changed
+ gimp_color_selector_channel_get_type
+ gimp_color_selector_color_changed
+ gimp_color_selector_get_channel
+ gimp_color_selector_get_color
+ gimp_color_selector_get_model_visible
+ gimp_color_selector_get_show_alpha
+ gimp_color_selector_get_toggles_sensitive
+ gimp_color_selector_get_toggles_visible
+ gimp_color_selector_get_type
+ gimp_color_selector_model_get_type
+ gimp_color_selector_model_visible_changed
+ gimp_color_selector_new
+ gimp_color_selector_set_channel
+ gimp_color_selector_set_color
+ gimp_color_selector_set_config
+ gimp_color_selector_set_model_visible
+ gimp_color_selector_set_show_alpha
+ gimp_color_selector_set_toggles_sensitive
+ gimp_color_selector_set_toggles_visible
+ gimp_context_help
+ gimp_controller_event
+ gimp_controller_get_event_blurb
+ gimp_controller_get_event_name
+ gimp_controller_get_n_events
+ gimp_controller_get_type
+ gimp_controller_new
+ gimp_coordinates_new
+ gimp_dialog_add_button
+ gimp_dialog_add_buttons
+ gimp_dialog_add_buttons_valist
+ gimp_dialog_get_type
+ gimp_dialog_new
+ gimp_dialog_new_valist
+ gimp_dialog_run
+ gimp_dialogs_show_help_button
+ gimp_double_adjustment_update
+ gimp_eevl_evaluate
+ gimp_enum_combo_box_get_type
+ gimp_enum_combo_box_new
+ gimp_enum_combo_box_new_with_model
+ gimp_enum_combo_box_set_icon_prefix
+ gimp_enum_combo_box_set_stock_prefix
+ gimp_enum_icon_box_new
+ gimp_enum_icon_box_new_with_range
+ gimp_enum_icon_box_set_child_padding
+ gimp_enum_label_get_type
+ gimp_enum_label_new
+ gimp_enum_label_set_value
+ gimp_enum_radio_box_new
+ gimp_enum_radio_box_new_with_range
+ gimp_enum_radio_frame_new
+ gimp_enum_radio_frame_new_with_range
+ gimp_enum_stock_box_new
+ gimp_enum_stock_box_new_with_range
+ gimp_enum_stock_box_set_child_padding
+ gimp_enum_store_get_type
+ gimp_enum_store_new
+ gimp_enum_store_new_with_range
+ gimp_enum_store_new_with_values
+ gimp_enum_store_new_with_values_valist
+ gimp_enum_store_set_icon_prefix
+ gimp_enum_store_set_stock_prefix
+ gimp_file_entry_get_filename
+ gimp_file_entry_get_type
+ gimp_file_entry_new
+ gimp_file_entry_set_filename
+ gimp_float_adjustment_update
+ gimp_frame_get_type
+ gimp_frame_new
+ gimp_get_monitor_at_pointer
+ gimp_help_connect
+ gimp_help_disable_tooltips
+ gimp_help_enable_tooltips
+ gimp_help_id_quark
+ gimp_help_set_help_data
+ gimp_help_set_help_data_with_markup
+ gimp_hint_box_get_type
+ gimp_hint_box_new
+ gimp_icon_pixbufs_get_resource
+ gimp_icons_init
+ gimp_icons_set_icon_theme
+ gimp_int_adjustment_update
+ gimp_int_combo_box_append
+ gimp_int_combo_box_connect
+ gimp_int_combo_box_get_active
+ gimp_int_combo_box_get_active_user_data
+ gimp_int_combo_box_get_label
+ gimp_int_combo_box_get_layout
+ gimp_int_combo_box_get_type
+ gimp_int_combo_box_layout_get_type
+ gimp_int_combo_box_new
+ gimp_int_combo_box_new_array
+ gimp_int_combo_box_new_valist
+ gimp_int_combo_box_prepend
+ gimp_int_combo_box_set_active
+ gimp_int_combo_box_set_active_by_user_data
+ gimp_int_combo_box_set_label
+ gimp_int_combo_box_set_layout
+ gimp_int_combo_box_set_sensitivity
+ gimp_int_option_menu_new
+ gimp_int_option_menu_set_history
+ gimp_int_option_menu_set_sensitive
+ gimp_int_radio_group_new
+ gimp_int_radio_group_set_active
+ gimp_int_store_get_type
+ gimp_int_store_lookup_by_user_data
+ gimp_int_store_lookup_by_value
+ gimp_int_store_new
+ gimp_label_set_attributes
+ gimp_memsize_entry_get_type
+ gimp_memsize_entry_get_value
+ gimp_memsize_entry_new
+ gimp_memsize_entry_set_value
+ gimp_menu_item_update
+ gimp_number_pair_entry_get_aspect
+ gimp_number_pair_entry_get_default_text
+ gimp_number_pair_entry_get_default_values
+ gimp_number_pair_entry_get_ratio
+ gimp_number_pair_entry_get_type
+ gimp_number_pair_entry_get_user_override
+ gimp_number_pair_entry_get_values
+ gimp_number_pair_entry_new
+ gimp_number_pair_entry_set_aspect
+ gimp_number_pair_entry_set_default_text
+ gimp_number_pair_entry_set_default_values
+ gimp_number_pair_entry_set_ratio
+ gimp_number_pair_entry_set_user_override
+ gimp_number_pair_entry_set_values
+ gimp_offset_area_get_type
+ gimp_offset_area_new
+ gimp_offset_area_set_offsets
+ gimp_offset_area_set_pixbuf
+ gimp_offset_area_set_size
+ gimp_option_menu_new
+ gimp_option_menu_new2
+ gimp_option_menu_set_history
+ gimp_option_menu_set_sensitive
+ gimp_page_selector_get_n_pages
+ gimp_page_selector_get_page_label
+ gimp_page_selector_get_page_thumbnail
+ gimp_page_selector_get_selected_pages
+ gimp_page_selector_get_selected_range
+ gimp_page_selector_get_target
+ gimp_page_selector_get_type
+ gimp_page_selector_new
+ gimp_page_selector_page_is_selected
+ gimp_page_selector_select_all
+ gimp_page_selector_select_page
+ gimp_page_selector_select_range
+ gimp_page_selector_set_n_pages
+ gimp_page_selector_set_page_label
+ gimp_page_selector_set_page_thumbnail
+ gimp_page_selector_set_target
+ gimp_page_selector_target_get_type
+ gimp_page_selector_unselect_all
+ gimp_page_selector_unselect_page
+ gimp_path_editor_get_dir_writable
+ gimp_path_editor_get_path
+ gimp_path_editor_get_type
+ gimp_path_editor_get_writable_path
+ gimp_path_editor_new
+ gimp_path_editor_set_dir_writable
+ gimp_path_editor_set_path
+ gimp_path_editor_set_writable_path
+ gimp_pick_button_get_type
+ gimp_pick_button_new
+ gimp_pixmap_button_new
+ gimp_pixmap_get_type
+ gimp_pixmap_new
+ gimp_pixmap_set
+ gimp_preview_area_blend
+ gimp_preview_area_draw
+ gimp_preview_area_fill
+ gimp_preview_area_get_type
+ gimp_preview_area_mask
+ gimp_preview_area_menu_popup
+ gimp_preview_area_new
+ gimp_preview_area_set_color_config
+ gimp_preview_area_set_colormap
+ gimp_preview_area_set_max_size
+ gimp_preview_area_set_offsets
+ gimp_preview_draw
+ gimp_preview_draw_buffer
+ gimp_preview_get_area
+ gimp_preview_get_controls
+ gimp_preview_get_position
+ gimp_preview_get_size
+ gimp_preview_get_type
+ gimp_preview_get_update
+ gimp_preview_invalidate
+ gimp_preview_set_bounds
+ gimp_preview_set_default_cursor
+ gimp_preview_set_update
+ gimp_preview_transform
+ gimp_preview_untransform
+ gimp_prop_boolean_combo_box_new
+ gimp_prop_boolean_radio_frame_new
+ gimp_prop_check_button_new
+ gimp_prop_color_area_new
+ gimp_prop_coordinates_connect
+ gimp_prop_coordinates_new
+ gimp_prop_entry_new
+ gimp_prop_enum_check_button_new
+ gimp_prop_enum_combo_box_new
+ gimp_prop_enum_icon_box_new
+ gimp_prop_enum_label_new
+ gimp_prop_enum_radio_box_new
+ gimp_prop_enum_radio_frame_new
+ gimp_prop_enum_stock_box_new
+ gimp_prop_expander_new
+ gimp_prop_file_chooser_button_new
+ gimp_prop_file_chooser_button_new_with_dialog
+ gimp_prop_hscale_new
+ gimp_prop_icon_image_new
+ gimp_prop_int_combo_box_new
+ gimp_prop_label_new
+ gimp_prop_memsize_entry_new
+ gimp_prop_opacity_entry_new
+ gimp_prop_path_editor_new
+ gimp_prop_pointer_combo_box_new
+ gimp_prop_scale_entry_new
+ gimp_prop_size_entry_new
+ gimp_prop_spin_button_new
+ gimp_prop_stock_image_new
+ gimp_prop_string_combo_box_new
+ gimp_prop_text_buffer_new
+ gimp_prop_unit_combo_box_new
+ gimp_prop_unit_menu_new
+ gimp_query_boolean_box
+ gimp_query_double_box
+ gimp_query_int_box
+ gimp_query_size_box
+ gimp_query_string_box
+ gimp_radio_button_update
+ gimp_radio_group_new
+ gimp_radio_group_new2
+ gimp_radio_group_set_active
+ gimp_random_seed_new
+ gimp_ruler_add_track_widget
+ gimp_ruler_get_position
+ gimp_ruler_get_range
+ gimp_ruler_get_type
+ gimp_ruler_get_unit
+ gimp_ruler_new
+ gimp_ruler_remove_track_widget
+ gimp_ruler_set_position
+ gimp_ruler_set_range
+ gimp_ruler_set_unit
+ gimp_scale_entry_get_logarithmic
+ gimp_scale_entry_new
+ gimp_scale_entry_set_logarithmic
+ gimp_scale_entry_set_sensitive
+ gimp_screen_get_color_profile
+ gimp_scrolled_preview_freeze
+ gimp_scrolled_preview_get_type
+ gimp_scrolled_preview_set_policy
+ gimp_scrolled_preview_set_position
+ gimp_scrolled_preview_thaw
+ gimp_size_entry_add_field
+ gimp_size_entry_attach_label
+ gimp_size_entry_get_help_widget
+ gimp_size_entry_get_refval
+ gimp_size_entry_get_type
+ gimp_size_entry_get_unit
+ gimp_size_entry_get_value
+ gimp_size_entry_grab_focus
+ gimp_size_entry_new
+ gimp_size_entry_set_activates_default
+ gimp_size_entry_set_pixel_digits
+ gimp_size_entry_set_refval
+ gimp_size_entry_set_refval_boundaries
+ gimp_size_entry_set_refval_digits
+ gimp_size_entry_set_resolution
+ gimp_size_entry_set_size
+ gimp_size_entry_set_unit
+ gimp_size_entry_set_value
+ gimp_size_entry_set_value_boundaries
+ gimp_size_entry_show_unit_menu
+ gimp_size_entry_update_policy_get_type
+ gimp_spin_button_get_type
+ gimp_spin_button_new
+ gimp_spin_button_new_
+ gimp_spin_button_new_with_range
+ gimp_standard_help_func
+ gimp_stock_init
+ gimp_string_combo_box_get_active
+ gimp_string_combo_box_get_type
+ gimp_string_combo_box_new
+ gimp_string_combo_box_set_active
+ gimp_table_attach_aligned
+ gimp_toggle_button_sensitive_update
+ gimp_toggle_button_update
+ gimp_uint_adjustment_update
+ gimp_unit_combo_box_get_active
+ gimp_unit_combo_box_get_type
+ gimp_unit_combo_box_new
+ gimp_unit_combo_box_new_with_model
+ gimp_unit_combo_box_set_active
+ gimp_unit_menu_get_pixel_digits
+ gimp_unit_menu_get_type
+ gimp_unit_menu_get_unit
+ gimp_unit_menu_new
+ gimp_unit_menu_set_pixel_digits
+ gimp_unit_menu_set_unit
+ gimp_unit_menu_update
+ gimp_unit_store_get_has_percent
+ gimp_unit_store_get_has_pixels
+ gimp_unit_store_get_type
+ gimp_unit_store_get_value
+ gimp_unit_store_get_values
+ gimp_unit_store_new
+ gimp_unit_store_set_has_percent
+ gimp_unit_store_set_has_pixels
+ gimp_unit_store_set_pixel_value
+ gimp_unit_store_set_pixel_values
+ gimp_unit_store_set_resolution
+ gimp_unit_store_set_resolutions
+ gimp_widget_get_color_profile
+ gimp_widget_get_color_transform
+ gimp_widget_get_monitor
+ gimp_widget_track_monitor
+ gimp_widgets_error_quark
+ gimp_widgets_init
+ gimp_zoom_button_new
+ gimp_zoom_model_get_factor
+ gimp_zoom_model_get_fraction
+ gimp_zoom_model_get_type
+ gimp_zoom_model_new
+ gimp_zoom_model_set_range
+ gimp_zoom_model_zoom
+ gimp_zoom_model_zoom_step
+ gimp_zoom_type_get_type
+ gtk_box_new
+ gtk_button_box_new
+ gtk_label_get_xalign
+ gtk_label_get_yalign
+ gtk_label_set_xalign
+ gtk_label_set_yalign
+ gtk_paned_new
+ gtk_scale_new
+ gtk_scrollbar_new
+ gtk_separator_new
+ gtk_widget_get_modifier_mask
diff --git a/libgimpwidgets/gimpwidgets.h b/libgimpwidgets/gimpwidgets.h
new file mode 100644
index 0000000..f3d8b77
--- /dev/null
+++ b/libgimpwidgets/gimpwidgets.h
@@ -0,0 +1,256 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpwidgets.h
+ * Copyright (C) 2000 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_WIDGETS_H__
+#define __GIMP_WIDGETS_H__
+
+#define __GIMP_WIDGETS_H_INSIDE__
+
+#include <libgimpwidgets/gimpwidgetstypes.h>
+
+#include <libgimpwidgets/gimpbrowser.h>
+#include <libgimpwidgets/gimpbusybox.h>
+#include <libgimpwidgets/gimpbutton.h>
+#include <libgimpwidgets/gimpcairo-utils.h>
+#include <libgimpwidgets/gimpcellrenderercolor.h>
+#include <libgimpwidgets/gimpcellrenderertoggle.h>
+#include <libgimpwidgets/gimpchainbutton.h>
+#include <libgimpwidgets/gimpcolorarea.h>
+#include <libgimpwidgets/gimpcolorbutton.h>
+#include <libgimpwidgets/gimpcolordisplay.h>
+#include <libgimpwidgets/gimpcolordisplaystack.h>
+#include <libgimpwidgets/gimpcolorhexentry.h>
+#include <libgimpwidgets/gimpcolornotebook.h>
+#include <libgimpwidgets/gimpcolorprofilechooserdialog.h>
+#include <libgimpwidgets/gimpcolorprofilecombobox.h>
+#include <libgimpwidgets/gimpcolorprofilestore.h>
+#include <libgimpwidgets/gimpcolorprofileview.h>
+#include <libgimpwidgets/gimpcolorscale.h>
+#include <libgimpwidgets/gimpcolorscales.h>
+#include <libgimpwidgets/gimpcolorselector.h>
+#include <libgimpwidgets/gimpcolorselect.h>
+#include <libgimpwidgets/gimpcolorselection.h>
+#include <libgimpwidgets/gimpdialog.h>
+#include <libgimpwidgets/gimpenumcombobox.h>
+#include <libgimpwidgets/gimpenumlabel.h>
+#include <libgimpwidgets/gimpenumstore.h>
+#include <libgimpwidgets/gimpenumwidgets.h>
+#include <libgimpwidgets/gimpfileentry.h>
+#include <libgimpwidgets/gimpframe.h>
+#include <libgimpwidgets/gimphelpui.h>
+#include <libgimpwidgets/gimphintbox.h>
+#include <libgimpwidgets/gimpicons.h>
+#include <libgimpwidgets/gimpintcombobox.h>
+#include <libgimpwidgets/gimpintstore.h>
+#include <libgimpwidgets/gimpmemsizeentry.h>
+#include <libgimpwidgets/gimpnumberpairentry.h>
+#include <libgimpwidgets/gimpoffsetarea.h>
+#include <libgimpwidgets/gimppageselector.h>
+#include <libgimpwidgets/gimppatheditor.h>
+#include <libgimpwidgets/gimppickbutton.h>
+#include <libgimpwidgets/gimppixmap.h>
+#include <libgimpwidgets/gimppreview.h>
+#include <libgimpwidgets/gimppreviewarea.h>
+#include <libgimpwidgets/gimppropwidgets.h>
+#include <libgimpwidgets/gimpquerybox.h>
+#include <libgimpwidgets/gimpruler.h>
+#include <libgimpwidgets/gimpscaleentry.h>
+#include <libgimpwidgets/gimpscrolledpreview.h>
+#include <libgimpwidgets/gimpsizeentry.h>
+#include <libgimpwidgets/gimpspinbutton.h>
+#include <libgimpwidgets/gimpstringcombobox.h>
+#include <libgimpwidgets/gimpunitcombobox.h>
+#include <libgimpwidgets/gimpunitmenu.h>
+#include <libgimpwidgets/gimpunitstore.h>
+#include <libgimpwidgets/gimpwidgets-error.h>
+#include <libgimpwidgets/gimpwidgetsutils.h>
+#include <libgimpwidgets/gimpzoommodel.h>
+
+#include <libgimpwidgets/gimp3migration.h>
+#include <libgimpwidgets/gimpoldwidgets.h>
+
+#undef __GIMP_WIDGETS_H_INSIDE__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+/*
+ * Widget Constructors
+ */
+
+GtkWidget * gimp_int_radio_group_new (gboolean in_frame,
+ const gchar *frame_title,
+ GCallback radio_button_callback,
+ gpointer radio_button_callback_data,
+ gint initial, /* item_data */
+
+ /* specify radio buttons as va_list:
+ * const gchar *label,
+ * gint item_data,
+ * GtkWidget **widget_ptr,
+ */
+
+ ...) G_GNUC_NULL_TERMINATED;
+
+void gimp_int_radio_group_set_active (GtkRadioButton *radio_button,
+ gint item_data);
+
+
+GtkWidget * gimp_radio_group_new (gboolean in_frame,
+ const gchar *frame_title,
+
+ /* specify radio buttons as va_list:
+ * const gchar *label,
+ * GCallback callback,
+ * gpointer callback_data,
+ * gpointer item_data,
+ * GtkWidget **widget_ptr,
+ * gboolean active,
+ */
+
+ ...) G_GNUC_NULL_TERMINATED;
+GtkWidget * gimp_radio_group_new2 (gboolean in_frame,
+ const gchar *frame_title,
+ GCallback radio_button_callback,
+ gpointer radio_button_callback_data,
+ gpointer initial, /* item_data */
+
+ /* specify radio buttons as va_list:
+ * const gchar *label,
+ * gpointer item_data,
+ * GtkWidget **widget_ptr,
+ */
+
+ ...) G_GNUC_NULL_TERMINATED;
+
+void gimp_radio_group_set_active (GtkRadioButton *radio_button,
+ gpointer item_data);
+
+
+GIMP_DEPRECATED_FOR(gtk_spin_button_new)
+GtkWidget * gimp_spin_button_new (/* return value: */
+ GtkObject **adjustment,
+
+ gdouble value,
+ gdouble lower,
+ gdouble upper,
+ gdouble step_increment,
+ gdouble page_increment,
+ gdouble page_size,
+ gdouble climb_rate,
+ guint digits);
+
+/**
+ * GIMP_RANDOM_SEED_SPINBUTTON:
+ * @hbox: The #GtkHBox returned by gimp_random_seed_new().
+ *
+ * Returns: the random_seed's #GtkSpinButton.
+ **/
+#define GIMP_RANDOM_SEED_SPINBUTTON(hbox) \
+ (g_object_get_data (G_OBJECT (hbox), "spinbutton"))
+
+/**
+ * GIMP_RANDOM_SEED_SPINBUTTON_ADJ:
+ * @hbox: The #GtkHBox returned by gimp_random_seed_new().
+ *
+ * Returns: the #GtkAdjustment of the random_seed's #GtkSpinButton.
+ **/
+#define GIMP_RANDOM_SEED_SPINBUTTON_ADJ(hbox) \
+ gtk_spin_button_get_adjustment \
+ (GTK_SPIN_BUTTON (g_object_get_data (G_OBJECT (hbox), "spinbutton")))
+
+/**
+ * GIMP_RANDOM_SEED_TOGGLE:
+ * @hbox: The #GtkHBox returned by gimp_random_seed_new().
+ *
+ * Returns: the random_seed's #GtkToggleButton.
+ **/
+#define GIMP_RANDOM_SEED_TOGGLE(hbox) \
+ (g_object_get_data (G_OBJECT(hbox), "toggle"))
+
+GtkWidget * gimp_random_seed_new (guint32 *seed,
+ gboolean *random_seed);
+
+/**
+ * GIMP_COORDINATES_CHAINBUTTON:
+ * @sizeentry: The #GimpSizeEntry returned by gimp_coordinates_new().
+ *
+ * Returns: the #GimpChainButton which is attached to the
+ * #GimpSizeEntry.
+ **/
+#define GIMP_COORDINATES_CHAINBUTTON(sizeentry) \
+ (g_object_get_data (G_OBJECT (sizeentry), "chainbutton"))
+
+GtkWidget * gimp_coordinates_new (GimpUnit unit,
+ const gchar *unit_format,
+ gboolean menu_show_pixels,
+ gboolean menu_show_percent,
+ gint spinbutton_width,
+ GimpSizeEntryUpdatePolicy update_policy,
+
+ gboolean chainbutton_active,
+ gboolean chain_constrains_ratio,
+
+ const gchar *xlabel,
+ gdouble x,
+ gdouble xres,
+ gdouble lower_boundary_x,
+ gdouble upper_boundary_x,
+ gdouble xsize_0, /* % */
+ gdouble xsize_100, /* % */
+
+ const gchar *ylabel,
+ gdouble y,
+ gdouble yres,
+ gdouble lower_boundary_y,
+ gdouble upper_boundary_y,
+ gdouble ysize_0, /* % */
+ gdouble ysize_100 /* % */);
+
+
+/*
+ * Standard Callbacks
+ */
+
+void gimp_toggle_button_update (GtkWidget *widget,
+ gpointer data);
+
+void gimp_radio_button_update (GtkWidget *widget,
+ gpointer data);
+
+void gimp_int_adjustment_update (GtkAdjustment *adjustment,
+ gpointer data);
+
+void gimp_uint_adjustment_update (GtkAdjustment *adjustment,
+ gpointer data);
+
+void gimp_float_adjustment_update (GtkAdjustment *adjustment,
+ gpointer data);
+
+void gimp_double_adjustment_update (GtkAdjustment *adjustment,
+ gpointer data);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_WIDGETS_H__ */
diff --git a/libgimpwidgets/gimpwidgetsenums.c b/libgimpwidgets/gimpwidgetsenums.c
new file mode 100644
index 0000000..82b16a1
--- /dev/null
+++ b/libgimpwidgets/gimpwidgetsenums.c
@@ -0,0 +1,313 @@
+
+/* Generated data (by gimp-mkenums) */
+
+#include "config.h"
+#include <gio/gio.h>
+#include "libgimpbase/gimpbase.h"
+#include "gimpwidgetsenums.h"
+#include "libgimp/libgimp-intl.h"
+
+/* enumerations from "gimpwidgetsenums.h" */
+GType
+gimp_aspect_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_ASPECT_SQUARE, "GIMP_ASPECT_SQUARE", "square" },
+ { GIMP_ASPECT_PORTRAIT, "GIMP_ASPECT_PORTRAIT", "portrait" },
+ { GIMP_ASPECT_LANDSCAPE, "GIMP_ASPECT_LANDSCAPE", "landscape" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_ASPECT_SQUARE, NC_("aspect-type", "Square"), NULL },
+ { GIMP_ASPECT_PORTRAIT, NC_("aspect-type", "Portrait"), NULL },
+ { GIMP_ASPECT_LANDSCAPE, NC_("aspect-type", "Landscape"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpAspectType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "aspect-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_chain_position_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_CHAIN_TOP, "GIMP_CHAIN_TOP", "top" },
+ { GIMP_CHAIN_LEFT, "GIMP_CHAIN_LEFT", "left" },
+ { GIMP_CHAIN_BOTTOM, "GIMP_CHAIN_BOTTOM", "bottom" },
+ { GIMP_CHAIN_RIGHT, "GIMP_CHAIN_RIGHT", "right" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_CHAIN_TOP, "GIMP_CHAIN_TOP", NULL },
+ { GIMP_CHAIN_LEFT, "GIMP_CHAIN_LEFT", NULL },
+ { GIMP_CHAIN_BOTTOM, "GIMP_CHAIN_BOTTOM", NULL },
+ { GIMP_CHAIN_RIGHT, "GIMP_CHAIN_RIGHT", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpChainPosition", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "chain-position");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_color_area_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_COLOR_AREA_FLAT, "GIMP_COLOR_AREA_FLAT", "flat" },
+ { GIMP_COLOR_AREA_SMALL_CHECKS, "GIMP_COLOR_AREA_SMALL_CHECKS", "small-checks" },
+ { GIMP_COLOR_AREA_LARGE_CHECKS, "GIMP_COLOR_AREA_LARGE_CHECKS", "large-checks" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_COLOR_AREA_FLAT, "GIMP_COLOR_AREA_FLAT", NULL },
+ { GIMP_COLOR_AREA_SMALL_CHECKS, "GIMP_COLOR_AREA_SMALL_CHECKS", NULL },
+ { GIMP_COLOR_AREA_LARGE_CHECKS, "GIMP_COLOR_AREA_LARGE_CHECKS", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpColorAreaType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "color-area-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_color_selector_channel_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_COLOR_SELECTOR_HUE, "GIMP_COLOR_SELECTOR_HUE", "hue" },
+ { GIMP_COLOR_SELECTOR_SATURATION, "GIMP_COLOR_SELECTOR_SATURATION", "saturation" },
+ { GIMP_COLOR_SELECTOR_VALUE, "GIMP_COLOR_SELECTOR_VALUE", "value" },
+ { GIMP_COLOR_SELECTOR_RED, "GIMP_COLOR_SELECTOR_RED", "red" },
+ { GIMP_COLOR_SELECTOR_GREEN, "GIMP_COLOR_SELECTOR_GREEN", "green" },
+ { GIMP_COLOR_SELECTOR_BLUE, "GIMP_COLOR_SELECTOR_BLUE", "blue" },
+ { GIMP_COLOR_SELECTOR_ALPHA, "GIMP_COLOR_SELECTOR_ALPHA", "alpha" },
+ { GIMP_COLOR_SELECTOR_LCH_LIGHTNESS, "GIMP_COLOR_SELECTOR_LCH_LIGHTNESS", "lch-lightness" },
+ { GIMP_COLOR_SELECTOR_LCH_CHROMA, "GIMP_COLOR_SELECTOR_LCH_CHROMA", "lch-chroma" },
+ { GIMP_COLOR_SELECTOR_LCH_HUE, "GIMP_COLOR_SELECTOR_LCH_HUE", "lch-hue" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_COLOR_SELECTOR_HUE, NC_("color-selector-channel", "_H"), N_("HSV Hue") },
+ { GIMP_COLOR_SELECTOR_SATURATION, NC_("color-selector-channel", "_S"), N_("HSV Saturation") },
+ { GIMP_COLOR_SELECTOR_VALUE, NC_("color-selector-channel", "_V"), N_("HSV Value") },
+ { GIMP_COLOR_SELECTOR_RED, NC_("color-selector-channel", "_R"), N_("Red") },
+ { GIMP_COLOR_SELECTOR_GREEN, NC_("color-selector-channel", "_G"), N_("Green") },
+ { GIMP_COLOR_SELECTOR_BLUE, NC_("color-selector-channel", "_B"), N_("Blue") },
+ { GIMP_COLOR_SELECTOR_ALPHA, NC_("color-selector-channel", "_A"), N_("Alpha") },
+ { GIMP_COLOR_SELECTOR_LCH_LIGHTNESS, NC_("color-selector-channel", "_L"), N_("LCh Lightness") },
+ { GIMP_COLOR_SELECTOR_LCH_CHROMA, NC_("color-selector-channel", "_C"), N_("LCh Chroma") },
+ { GIMP_COLOR_SELECTOR_LCH_HUE, NC_("color-selector-channel", "_h"), N_("LCh Hue") },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpColorSelectorChannel", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "color-selector-channel");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_color_selector_model_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_COLOR_SELECTOR_MODEL_RGB, "GIMP_COLOR_SELECTOR_MODEL_RGB", "rgb" },
+ { GIMP_COLOR_SELECTOR_MODEL_LCH, "GIMP_COLOR_SELECTOR_MODEL_LCH", "lch" },
+ { GIMP_COLOR_SELECTOR_MODEL_HSV, "GIMP_COLOR_SELECTOR_MODEL_HSV", "hsv" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_COLOR_SELECTOR_MODEL_RGB, NC_("color-selector-model", "RGB"), N_("RGB color model") },
+ { GIMP_COLOR_SELECTOR_MODEL_LCH, NC_("color-selector-model", "LCH"), N_("CIE LCh color model") },
+ { GIMP_COLOR_SELECTOR_MODEL_HSV, NC_("color-selector-model", "HSV"), N_("HSV color model") },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpColorSelectorModel", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "color-selector-model");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_int_combo_box_layout_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_INT_COMBO_BOX_LAYOUT_ICON_ONLY, "GIMP_INT_COMBO_BOX_LAYOUT_ICON_ONLY", "icon-only" },
+ { GIMP_INT_COMBO_BOX_LAYOUT_ABBREVIATED, "GIMP_INT_COMBO_BOX_LAYOUT_ABBREVIATED", "abbreviated" },
+ { GIMP_INT_COMBO_BOX_LAYOUT_FULL, "GIMP_INT_COMBO_BOX_LAYOUT_FULL", "full" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_INT_COMBO_BOX_LAYOUT_ICON_ONLY, "GIMP_INT_COMBO_BOX_LAYOUT_ICON_ONLY", NULL },
+ { GIMP_INT_COMBO_BOX_LAYOUT_ABBREVIATED, "GIMP_INT_COMBO_BOX_LAYOUT_ABBREVIATED", NULL },
+ { GIMP_INT_COMBO_BOX_LAYOUT_FULL, "GIMP_INT_COMBO_BOX_LAYOUT_FULL", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpIntComboBoxLayout", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "int-combo-box-layout");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_page_selector_target_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_PAGE_SELECTOR_TARGET_LAYERS, "GIMP_PAGE_SELECTOR_TARGET_LAYERS", "layers" },
+ { GIMP_PAGE_SELECTOR_TARGET_IMAGES, "GIMP_PAGE_SELECTOR_TARGET_IMAGES", "images" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_PAGE_SELECTOR_TARGET_LAYERS, NC_("page-selector-target", "Layers"), NULL },
+ { GIMP_PAGE_SELECTOR_TARGET_IMAGES, NC_("page-selector-target", "Images"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpPageSelectorTarget", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "page-selector-target");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_size_entry_update_policy_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_SIZE_ENTRY_UPDATE_NONE, "GIMP_SIZE_ENTRY_UPDATE_NONE", "none" },
+ { GIMP_SIZE_ENTRY_UPDATE_SIZE, "GIMP_SIZE_ENTRY_UPDATE_SIZE", "size" },
+ { GIMP_SIZE_ENTRY_UPDATE_RESOLUTION, "GIMP_SIZE_ENTRY_UPDATE_RESOLUTION", "resolution" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_SIZE_ENTRY_UPDATE_NONE, "GIMP_SIZE_ENTRY_UPDATE_NONE", NULL },
+ { GIMP_SIZE_ENTRY_UPDATE_SIZE, "GIMP_SIZE_ENTRY_UPDATE_SIZE", NULL },
+ { GIMP_SIZE_ENTRY_UPDATE_RESOLUTION, "GIMP_SIZE_ENTRY_UPDATE_RESOLUTION", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpSizeEntryUpdatePolicy", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "size-entry-update-policy");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_zoom_type_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_ZOOM_IN, "GIMP_ZOOM_IN", "in" },
+ { GIMP_ZOOM_OUT, "GIMP_ZOOM_OUT", "out" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_ZOOM_IN, NC_("zoom-type", "Zoom in"), NULL },
+ { GIMP_ZOOM_OUT, NC_("zoom-type", "Zoom out"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpZoomType", values);
+ gimp_type_set_translation_domain (type, GETTEXT_PACKAGE "-libgimp");
+ gimp_type_set_translation_context (type, "zoom-type");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+
+/* Generated data ends here */
+
diff --git a/libgimpwidgets/gimpwidgetsenums.h b/libgimpwidgets/gimpwidgetsenums.h
new file mode 100644
index 0000000..23f162e
--- /dev/null
+++ b/libgimpwidgets/gimpwidgetsenums.h
@@ -0,0 +1,239 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_WIDGETS_ENUMS_H__
+#define __GIMP_WIDGETS_ENUMS_H__
+
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+/**
+ * GimpAspectType:
+ * @GIMP_ASPECT_SQUARE: it's a 1:1 square
+ * @GIMP_ASPECT_PORTRAIT: it's higher than it's wide
+ * @GIMP_ASPECT_LANDSCAPE: it's wider than it's high
+ *
+ * Aspect ratios.
+ **/
+#define GIMP_TYPE_ASPECT_TYPE (gimp_aspect_type_get_type ())
+
+GType gimp_aspect_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_ASPECT_SQUARE, /*< desc="Square" >*/
+ GIMP_ASPECT_PORTRAIT, /*< desc="Portrait" >*/
+ GIMP_ASPECT_LANDSCAPE /*< desc="Landscape" >*/
+} GimpAspectType;
+
+
+/**
+ * GimpChainPosition:
+ * @GIMP_CHAIN_TOP: the chain is on top
+ * @GIMP_CHAIN_LEFT: the chain is to the left
+ * @GIMP_CHAIN_BOTTOM: the chain is on bottom
+ * @GIMP_CHAIN_RIGHT: the chain is to the right
+ *
+ * Possible chain positions for #GimpChainButton.
+ **/
+#define GIMP_TYPE_CHAIN_POSITION (gimp_chain_position_get_type ())
+
+GType gimp_chain_position_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_CHAIN_TOP,
+ GIMP_CHAIN_LEFT,
+ GIMP_CHAIN_BOTTOM,
+ GIMP_CHAIN_RIGHT
+} GimpChainPosition;
+
+
+/**
+ * GimpColorAreaType:
+ * @GIMP_COLOR_AREA_FLAT: don't display transparency
+ * @GIMP_COLOR_AREA_SMALL_CHECKS: display transparency using small checks
+ * @GIMP_COLOR_AREA_LARGE_CHECKS: display transparency using large checks
+ *
+ * The types of transparency display for #GimpColorArea.
+ **/
+#define GIMP_TYPE_COLOR_AREA_TYPE (gimp_color_area_type_get_type ())
+
+GType gimp_color_area_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_COLOR_AREA_FLAT = 0,
+ GIMP_COLOR_AREA_SMALL_CHECKS,
+ GIMP_COLOR_AREA_LARGE_CHECKS
+} GimpColorAreaType;
+
+
+/**
+ * GimpColorSelectorChannel:
+ * @GIMP_COLOR_SELECTOR_HUE: the hue channel
+ * @GIMP_COLOR_SELECTOR_SATURATION: the saturation channel
+ * @GIMP_COLOR_SELECTOR_VALUE: the value channel
+ * @GIMP_COLOR_SELECTOR_RED: the red channel
+ * @GIMP_COLOR_SELECTOR_GREEN: the green channel
+ * @GIMP_COLOR_SELECTOR_BLUE: the blue channel
+ * @GIMP_COLOR_SELECTOR_ALPHA: the alpha channel
+ * @GIMP_COLOR_SELECTOR_LCH_LIGHTNESS: the lightness channel
+ * @GIMP_COLOR_SELECTOR_LCH_CHROMA: the chroma channel
+ * @GIMP_COLOR_SELECTOR_LCH_HUE: the hue channel
+ *
+ * An enum to specify the types of color channels edited in
+ * #GimpColorSelector widgets.
+ **/
+#define GIMP_TYPE_COLOR_SELECTOR_CHANNEL (gimp_color_selector_channel_get_type ())
+
+GType gimp_color_selector_channel_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_COLOR_SELECTOR_HUE, /*< desc="_H", help="HSV Hue" >*/
+ GIMP_COLOR_SELECTOR_SATURATION, /*< desc="_S", help="HSV Saturation" >*/
+ GIMP_COLOR_SELECTOR_VALUE, /*< desc="_V", help="HSV Value" >*/
+ GIMP_COLOR_SELECTOR_RED, /*< desc="_R", help="Red" >*/
+ GIMP_COLOR_SELECTOR_GREEN, /*< desc="_G", help="Green" >*/
+ GIMP_COLOR_SELECTOR_BLUE, /*< desc="_B", help="Blue" >*/
+ GIMP_COLOR_SELECTOR_ALPHA, /*< desc="_A", help="Alpha" >*/
+ GIMP_COLOR_SELECTOR_LCH_LIGHTNESS, /*< desc="_L", help="LCh Lightness" >*/
+ GIMP_COLOR_SELECTOR_LCH_CHROMA, /*< desc="_C", help="LCh Chroma" >*/
+ GIMP_COLOR_SELECTOR_LCH_HUE /*< desc="_h", help="LCh Hue" >*/
+} GimpColorSelectorChannel;
+
+
+/**
+ * GimpColorSelectorModel:
+ * @GIMP_COLOR_SELECTOR_MODEL_RGB: RGB color model
+ * @GIMP_COLOR_SELECTOR_MODEL_LCH: CIE LCh color model
+ * @GIMP_COLOR_SELECTOR_MODEL_HSV: HSV color model
+ *
+ * An enum to specify the types of color spaces edited in
+ * #GimpColorSelector widgets.
+ *
+ * Since: 2.10
+ **/
+#define GIMP_TYPE_COLOR_SELECTOR_MODEL (gimp_color_selector_model_get_type ())
+
+GType gimp_color_selector_model_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_COLOR_SELECTOR_MODEL_RGB, /*< desc="RGB", help="RGB color model" >*/
+ GIMP_COLOR_SELECTOR_MODEL_LCH, /*< desc="LCH", help="CIE LCh color model" >*/
+ GIMP_COLOR_SELECTOR_MODEL_HSV /*< desc="HSV", help="HSV color model" >*/
+} GimpColorSelectorModel;
+
+
+/**
+ * GimpIntComboBoxLayout:
+ * @GIMP_INT_COMBO_BOX_LAYOUT_ICON_ONLY: show icons only
+ * @GIMP_INT_COMBO_BOX_LAYOUT_ABBREVIATED: show icons and abbreviated labels,
+ * when available
+ * @GIMP_INT_COMBO_BOX_LAYOUT_FULL: show icons and full labels
+ *
+ * Possible layouts for #GimpIntComboBox.
+ *
+ * Since: 2.10
+ **/
+#define GIMP_TYPE_INT_COMBO_BOX_LAYOUT (gimp_int_combo_box_layout_get_type ())
+
+GType gimp_int_combo_box_layout_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_INT_COMBO_BOX_LAYOUT_ICON_ONLY,
+ GIMP_INT_COMBO_BOX_LAYOUT_ABBREVIATED,
+ GIMP_INT_COMBO_BOX_LAYOUT_FULL
+} GimpIntComboBoxLayout;
+
+
+/**
+ * GimpPageSelectorTarget:
+ * @GIMP_PAGE_SELECTOR_TARGET_LAYERS: import as layers of one image
+ * @GIMP_PAGE_SELECTOR_TARGET_IMAGES: import as separate images
+ *
+ * Import targets for #GimpPageSelector.
+ **/
+#define GIMP_TYPE_PAGE_SELECTOR_TARGET (gimp_page_selector_target_get_type ())
+
+GType gimp_page_selector_target_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_PAGE_SELECTOR_TARGET_LAYERS, /*< desc="Layers" >*/
+ GIMP_PAGE_SELECTOR_TARGET_IMAGES /*< desc="Images" >*/
+} GimpPageSelectorTarget;
+
+
+/**
+ * GimpSizeEntryUpdatePolicy:
+ * @GIMP_SIZE_ENTRY_UPDATE_NONE: the size entry's meaning is up to the user
+ * @GIMP_SIZE_ENTRY_UPDATE_SIZE: the size entry displays values
+ * @GIMP_SIZE_ENTRY_UPDATE_RESOLUTION: the size entry displays resolutions
+ *
+ * Update policies for #GimpSizeEntry.
+ **/
+#define GIMP_TYPE_SIZE_ENTRY_UPDATE_POLICY (gimp_size_entry_update_policy_get_type ())
+
+GType gimp_size_entry_update_policy_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_SIZE_ENTRY_UPDATE_NONE = 0,
+ GIMP_SIZE_ENTRY_UPDATE_SIZE = 1,
+ GIMP_SIZE_ENTRY_UPDATE_RESOLUTION = 2
+} GimpSizeEntryUpdatePolicy;
+
+
+/**
+ * GimpZoomType:
+ * @GIMP_ZOOM_IN: zoom in
+ * @GIMP_ZOOM_OUT: zoom out
+ * @GIMP_ZOOM_IN_MORE: zoom in a lot
+ * @GIMP_ZOOM_OUT_MORE: zoom out a lot
+ * @GIMP_ZOOM_IN_MAX: zoom in as far as possible
+ * @GIMP_ZOOM_OUT_MAX: zoom out as far as possible
+ * @GIMP_ZOOM_TO: zoom to a specific zoom factor
+ *
+ * the zoom types for #GimpZoomModel.
+ **/
+#define GIMP_TYPE_ZOOM_TYPE (gimp_zoom_type_get_type ())
+
+GType gimp_zoom_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_ZOOM_IN, /*< desc="Zoom in" >*/
+ GIMP_ZOOM_OUT, /*< desc="Zoom out" >*/
+ GIMP_ZOOM_IN_MORE, /*< skip >*/
+ GIMP_ZOOM_OUT_MORE, /*< skip >*/
+ GIMP_ZOOM_IN_MAX, /*< skip >*/
+ GIMP_ZOOM_OUT_MAX, /*< skip >*/
+ GIMP_ZOOM_TO /*< skip >*/
+} GimpZoomType;
+
+
+G_END_DECLS
+
+#endif /* __GIMP_WIDGETS_ENUMS_H__ */
diff --git a/libgimpwidgets/gimpwidgetsmarshal.c b/libgimpwidgets/gimpwidgetsmarshal.c
new file mode 100644
index 0000000..08cc8f4
--- /dev/null
+++ b/libgimpwidgets/gimpwidgetsmarshal.c
@@ -0,0 +1,433 @@
+/* This file is generated by glib-genmarshal, do not modify it. This code is licensed under the same license as the containing project. Note that it links to GLib, so must comply with the LGPL linking clauses. */
+#include <glib-object.h>
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v) g_value_get_schar (v)
+#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v) g_value_get_int (v)
+#define g_marshal_value_peek_uint(v) g_value_get_uint (v)
+#define g_marshal_value_peek_long(v) g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v) g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v) g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v) g_value_get_flags (v)
+#define g_marshal_value_peek_float(v) g_value_get_float (v)
+#define g_marshal_value_peek_double(v) g_value_get_double (v)
+#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v) g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v) g_value_get_object (v)
+#define g_marshal_value_peek_variant(v) g_value_get_variant (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ * Do not access GValues directly in your code. Instead, use the
+ * g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int
+#define g_marshal_value_peek_char(v) (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v) (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v) (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v) (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v) (v)->data[0].v_float
+#define g_marshal_value_peek_double(v) (v)->data[0].v_double
+#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer
+#define g_marshal_value_peek_variant(v) (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+/* VOID: ENUM (../../libgimpwidgets/gimpwidgetsmarshal.list:25) */
+#define _gimp_widgets_marshal_VOID__ENUM g_cclosure_marshal_VOID__ENUM
+
+/* VOID: ENUM, BOOLEAN (../../libgimpwidgets/gimpwidgetsmarshal.list:26) */
+/* Prototype for -Wmissing-prototypes */
+G_BEGIN_DECLS
+extern
+void _gimp_widgets_marshal_VOID__ENUM_BOOLEAN (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+G_END_DECLS
+void
+_gimp_widgets_marshal_VOID__ENUM_BOOLEAN (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__ENUM_BOOLEAN) (gpointer data1,
+ gint arg1,
+ gboolean arg2,
+ gpointer data2);
+ GCClosure *cc = (GCClosure *) closure;
+ gpointer data1, data2;
+ GMarshalFunc_VOID__ENUM_BOOLEAN callback;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__ENUM_BOOLEAN) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_enum (param_values + 1),
+ g_marshal_value_peek_boolean (param_values + 2),
+ data2);
+}
+
+/* VOID: INT, INT (../../libgimpwidgets/gimpwidgetsmarshal.list:27) */
+/* Prototype for -Wmissing-prototypes */
+G_BEGIN_DECLS
+extern
+void _gimp_widgets_marshal_VOID__INT_INT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+G_END_DECLS
+void
+_gimp_widgets_marshal_VOID__INT_INT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__INT_INT) (gpointer data1,
+ gint arg1,
+ gint arg2,
+ gpointer data2);
+ GCClosure *cc = (GCClosure *) closure;
+ gpointer data1, data2;
+ GMarshalFunc_VOID__INT_INT callback;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__INT_INT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_int (param_values + 1),
+ g_marshal_value_peek_int (param_values + 2),
+ data2);
+}
+
+/* VOID: OBJECT (../../libgimpwidgets/gimpwidgetsmarshal.list:28) */
+#define _gimp_widgets_marshal_VOID__OBJECT g_cclosure_marshal_VOID__OBJECT
+
+/* VOID: OBJECT, INT (../../libgimpwidgets/gimpwidgetsmarshal.list:29) */
+/* Prototype for -Wmissing-prototypes */
+G_BEGIN_DECLS
+extern
+void _gimp_widgets_marshal_VOID__OBJECT_INT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+G_END_DECLS
+void
+_gimp_widgets_marshal_VOID__OBJECT_INT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__OBJECT_INT) (gpointer data1,
+ gpointer arg1,
+ gint arg2,
+ gpointer data2);
+ GCClosure *cc = (GCClosure *) closure;
+ gpointer data1, data2;
+ GMarshalFunc_VOID__OBJECT_INT callback;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__OBJECT_INT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_object (param_values + 1),
+ g_marshal_value_peek_int (param_values + 2),
+ data2);
+}
+
+/* VOID: POINTER, POINTER (../../libgimpwidgets/gimpwidgetsmarshal.list:30) */
+/* Prototype for -Wmissing-prototypes */
+G_BEGIN_DECLS
+extern
+void _gimp_widgets_marshal_VOID__POINTER_POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+G_END_DECLS
+void
+_gimp_widgets_marshal_VOID__POINTER_POINTER (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__POINTER_POINTER) (gpointer data1,
+ gpointer arg1,
+ gpointer arg2,
+ gpointer data2);
+ GCClosure *cc = (GCClosure *) closure;
+ gpointer data1, data2;
+ GMarshalFunc_VOID__POINTER_POINTER callback;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__POINTER_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_pointer (param_values + 1),
+ g_marshal_value_peek_pointer (param_values + 2),
+ data2);
+}
+
+/* VOID: STRING, FLAGS (../../libgimpwidgets/gimpwidgetsmarshal.list:31) */
+/* Prototype for -Wmissing-prototypes */
+G_BEGIN_DECLS
+extern
+void _gimp_widgets_marshal_VOID__STRING_FLAGS (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+G_END_DECLS
+void
+_gimp_widgets_marshal_VOID__STRING_FLAGS (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__STRING_FLAGS) (gpointer data1,
+ gpointer arg1,
+ guint arg2,
+ gpointer data2);
+ GCClosure *cc = (GCClosure *) closure;
+ gpointer data1, data2;
+ GMarshalFunc_VOID__STRING_FLAGS callback;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__STRING_FLAGS) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_string (param_values + 1),
+ g_marshal_value_peek_flags (param_values + 2),
+ data2);
+}
+
+/* VOID: STRING, INT (../../libgimpwidgets/gimpwidgetsmarshal.list:32) */
+/* Prototype for -Wmissing-prototypes */
+G_BEGIN_DECLS
+extern
+void _gimp_widgets_marshal_VOID__STRING_INT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+G_END_DECLS
+void
+_gimp_widgets_marshal_VOID__STRING_INT (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__STRING_INT) (gpointer data1,
+ gpointer arg1,
+ gint arg2,
+ gpointer data2);
+ GCClosure *cc = (GCClosure *) closure;
+ gpointer data1, data2;
+ GMarshalFunc_VOID__STRING_INT callback;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__STRING_INT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_string (param_values + 1),
+ g_marshal_value_peek_int (param_values + 2),
+ data2);
+}
+
+/* VOID: DOUBLE, DOUBLE (../../libgimpwidgets/gimpwidgetsmarshal.list:33) */
+/* Prototype for -Wmissing-prototypes */
+G_BEGIN_DECLS
+extern
+void _gimp_widgets_marshal_VOID__DOUBLE_DOUBLE (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+G_END_DECLS
+void
+_gimp_widgets_marshal_VOID__DOUBLE_DOUBLE (GClosure *closure,
+ GValue *return_value G_GNUC_UNUSED,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__DOUBLE_DOUBLE) (gpointer data1,
+ gdouble arg1,
+ gdouble arg2,
+ gpointer data2);
+ GCClosure *cc = (GCClosure *) closure;
+ gpointer data1, data2;
+ GMarshalFunc_VOID__DOUBLE_DOUBLE callback;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__DOUBLE_DOUBLE) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_double (param_values + 1),
+ g_marshal_value_peek_double (param_values + 2),
+ data2);
+}
+
+/* BOOLEAN: POINTER (../../libgimpwidgets/gimpwidgetsmarshal.list:35) */
+/* Prototype for -Wmissing-prototypes */
+G_BEGIN_DECLS
+extern
+void _gimp_widgets_marshal_BOOLEAN__POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+G_END_DECLS
+void
+_gimp_widgets_marshal_BOOLEAN__POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint G_GNUC_UNUSED,
+ gpointer marshal_data)
+{
+ typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER) (gpointer data1,
+ gpointer arg1,
+ gpointer data2);
+ GCClosure *cc = (GCClosure *) closure;
+ gpointer data1, data2;
+ GMarshalFunc_BOOLEAN__POINTER callback;
+ gboolean v_return;
+
+ g_return_if_fail (return_value != NULL);
+ g_return_if_fail (n_param_values == 2);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_BOOLEAN__POINTER) (marshal_data ? marshal_data : cc->callback);
+
+ v_return = callback (data1,
+ g_marshal_value_peek_pointer (param_values + 1),
+ data2);
+
+ g_value_set_boolean (return_value, v_return);
+}
+
diff --git a/libgimpwidgets/gimpwidgetsmarshal.h b/libgimpwidgets/gimpwidgetsmarshal.h
new file mode 100644
index 0000000..e4010b6
--- /dev/null
+++ b/libgimpwidgets/gimpwidgetsmarshal.h
@@ -0,0 +1,90 @@
+/* This file is generated by glib-genmarshal, do not modify it. This code is licensed under the same license as the containing project. Note that it links to GLib, so must comply with the LGPL linking clauses. */
+#ifndef ___GIMP_WIDGETS_MARSHAL_MARSHAL_H__
+#define ___GIMP_WIDGETS_MARSHAL_MARSHAL_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/* VOID: ENUM (../../libgimpwidgets/gimpwidgetsmarshal.list:25) */
+#define _gimp_widgets_marshal_VOID__ENUM g_cclosure_marshal_VOID__ENUM
+
+/* VOID: ENUM, BOOLEAN (../../libgimpwidgets/gimpwidgetsmarshal.list:26) */
+extern
+void _gimp_widgets_marshal_VOID__ENUM_BOOLEAN (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* VOID: INT, INT (../../libgimpwidgets/gimpwidgetsmarshal.list:27) */
+extern
+void _gimp_widgets_marshal_VOID__INT_INT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* VOID: OBJECT (../../libgimpwidgets/gimpwidgetsmarshal.list:28) */
+#define _gimp_widgets_marshal_VOID__OBJECT g_cclosure_marshal_VOID__OBJECT
+
+/* VOID: OBJECT, INT (../../libgimpwidgets/gimpwidgetsmarshal.list:29) */
+extern
+void _gimp_widgets_marshal_VOID__OBJECT_INT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* VOID: POINTER, POINTER (../../libgimpwidgets/gimpwidgetsmarshal.list:30) */
+extern
+void _gimp_widgets_marshal_VOID__POINTER_POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* VOID: STRING, FLAGS (../../libgimpwidgets/gimpwidgetsmarshal.list:31) */
+extern
+void _gimp_widgets_marshal_VOID__STRING_FLAGS (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* VOID: STRING, INT (../../libgimpwidgets/gimpwidgetsmarshal.list:32) */
+extern
+void _gimp_widgets_marshal_VOID__STRING_INT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* VOID: DOUBLE, DOUBLE (../../libgimpwidgets/gimpwidgetsmarshal.list:33) */
+extern
+void _gimp_widgets_marshal_VOID__DOUBLE_DOUBLE (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+/* BOOLEAN: POINTER (../../libgimpwidgets/gimpwidgetsmarshal.list:35) */
+extern
+void _gimp_widgets_marshal_BOOLEAN__POINTER (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+
+G_END_DECLS
+
+#endif /* ___GIMP_WIDGETS_MARSHAL_MARSHAL_H__ */
diff --git a/libgimpwidgets/gimpwidgetsmarshal.list b/libgimpwidgets/gimpwidgetsmarshal.list
new file mode 100644
index 0000000..8983fa8
--- /dev/null
+++ b/libgimpwidgets/gimpwidgetsmarshal.list
@@ -0,0 +1,35 @@
+# see glib-genmarshal(1) for a detailed description of the file format,
+# possible parameter types are:
+# VOID indicates no return type, or no extra
+# parameters. if VOID is used as the parameter
+# list, no additional parameters may be present.
+# BOOLEAN for boolean types (gboolean)
+# CHAR for signed char types (gchar)
+# UCHAR for unsigned char types (guchar)
+# INT for signed integer types (gint)
+# UINT for unsigned integer types (guint)
+# LONG for signed long integer types (glong)
+# ULONG for unsigned long integer types (gulong)
+# ENUM for enumeration types (gint)
+# FLAGS for flag enumeration types (guint)
+# FLOAT for single-precision float types (gfloat)
+# DOUBLE for double-precision float types (gdouble)
+# STRING for string types (gchar*)
+# BOXED for boxed (anonymous but reference counted) types (GBoxed*)
+# POINTER for anonymous pointer types (gpointer)
+# PARAM for GParamSpec or derived types (GParamSpec*)
+# OBJECT for GObject or derived types (GObject*)
+# NONE deprecated alias for VOID
+# BOOL deprecated alias for BOOLEAN
+
+VOID: ENUM
+VOID: ENUM, BOOLEAN
+VOID: INT, INT
+VOID: OBJECT
+VOID: OBJECT, INT
+VOID: POINTER, POINTER
+VOID: STRING, FLAGS
+VOID: STRING, INT
+VOID: DOUBLE, DOUBLE
+
+BOOLEAN: POINTER
diff --git a/libgimpwidgets/gimpwidgetstypes.h b/libgimpwidgets/gimpwidgetstypes.h
new file mode 100644
index 0000000..cf2ac14
--- /dev/null
+++ b/libgimpwidgets/gimpwidgetstypes.h
@@ -0,0 +1,110 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpwidgetstypes.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_WIDGETS_TYPES_H__
+#define __GIMP_WIDGETS_TYPES_H__
+
+#include <libgimpconfig/gimpconfigtypes.h>
+
+#include <libgimpwidgets/gimpwidgetsenums.h>
+
+G_BEGIN_DECLS
+
+/* For information look into the html documentation */
+
+
+typedef struct _GimpBrowser GimpBrowser;
+typedef struct _GimpBusyBox GimpBusyBox;
+typedef struct _GimpButton GimpButton;
+typedef struct _GimpCellRendererColor GimpCellRendererColor;
+typedef struct _GimpCellRendererToggle GimpCellRendererToggle;
+typedef struct _GimpChainButton GimpChainButton;
+typedef struct _GimpColorArea GimpColorArea;
+typedef struct _GimpColorButton GimpColorButton;
+typedef struct _GimpColorDisplay GimpColorDisplay;
+typedef struct _GimpColorDisplayStack GimpColorDisplayStack;
+typedef struct _GimpColorHexEntry GimpColorHexEntry;
+typedef struct _GimpColorNotebook GimpColorNotebook;
+typedef struct _GimpColorProfileChooserDialog GimpColorProfileChooserDialog;
+typedef struct _GimpColorProfileComboBox GimpColorProfileComboBox;
+typedef struct _GimpColorProfileStore GimpColorProfileStore;
+typedef struct _GimpColorProfileView GimpColorProfileView;
+typedef struct _GimpColorScale GimpColorScale;
+typedef struct _GimpColorScales GimpColorScales;
+typedef struct _GimpColorSelector GimpColorSelector;
+typedef struct _GimpColorSelect GimpColorSelect;
+typedef struct _GimpColorSelection GimpColorSelection;
+typedef struct _GimpController GimpController;
+typedef struct _GimpDialog GimpDialog;
+typedef struct _GimpEnumStore GimpEnumStore;
+typedef struct _GimpEnumComboBox GimpEnumComboBox;
+typedef struct _GimpEnumLabel GimpEnumLabel;
+typedef struct _GimpFileEntry GimpFileEntry;
+typedef struct _GimpFrame GimpFrame;
+typedef struct _GimpHintBox GimpHintBox;
+typedef struct _GimpIntComboBox GimpIntComboBox;
+typedef struct _GimpIntStore GimpIntStore;
+typedef struct _GimpMemsizeEntry GimpMemsizeEntry;
+typedef struct _GimpNumberPairEntry GimpNumberPairEntry;
+typedef struct _GimpOffsetArea GimpOffsetArea;
+typedef struct _GimpPageSelector GimpPageSelector;
+typedef struct _GimpPathEditor GimpPathEditor;
+typedef struct _GimpPickButton GimpPickButton;
+typedef struct _GimpPreview GimpPreview;
+typedef struct _GimpPreviewArea GimpPreviewArea;
+typedef struct _GimpPixmap GimpPixmap;
+typedef struct _GimpRuler GimpRuler;
+typedef struct _GimpScrolledPreview GimpScrolledPreview;
+typedef struct _GimpSizeEntry GimpSizeEntry;
+typedef struct _GimpSpinButton GimpSpinButton;
+typedef struct _GimpStringComboBox GimpStringComboBox;
+typedef struct _GimpUnitComboBox GimpUnitComboBox;
+typedef struct _GimpUnitMenu GimpUnitMenu;
+typedef struct _GimpUnitStore GimpUnitStore;
+typedef struct _GimpZoomModel GimpZoomModel;
+
+
+/**
+ * GimpHelpFunc:
+ * @help_id: the help ID
+ * @help_data: the help user data
+ *
+ * This is the prototype for all functions you pass as @help_func to
+ * the various GIMP dialog constructors like gimp_dialog_new(),
+ * gimp_query_int_box() etc.
+ *
+ * Help IDs are textual identifiers the help system uses to figure
+ * which page to display.
+ *
+ * All these dialog constructors functions call gimp_help_connect().
+ *
+ * In most cases it will be ok to use gimp_standard_help_func() which
+ * does nothing but passing the @help_id string to gimp_help(). If
+ * your plug-in needs some more sophisticated help handling you can
+ * provide your own @help_func which has to call gimp_help() to
+ * actually display the help.
+ **/
+typedef void (* GimpHelpFunc) (const gchar *help_id,
+ gpointer help_data);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_WIDGETS_TYPES_H__ */
diff --git a/libgimpwidgets/gimpwidgetsutils.c b/libgimpwidgets/gimpwidgetsutils.c
new file mode 100644
index 0000000..9cc507c
--- /dev/null
+++ b/libgimpwidgets/gimpwidgetsutils.c
@@ -0,0 +1,921 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpwidgets.c
+ * Copyright (C) 2000 Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <lcms2.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#ifdef G_OS_WIN32
+#ifdef _WIN32_WINNT
+#undef _WIN32_WINNT
+#endif
+#define _WIN32_WINNT 0x0600
+#include <windows.h>
+#include <icm.h>
+#endif
+
+#ifdef GDK_WINDOWING_QUARTZ
+#include <Carbon/Carbon.h>
+#include <ApplicationServices/ApplicationServices.h>
+#include <CoreServices/CoreServices.h>
+#endif
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "gimpwidgetstypes.h"
+
+#include "gimp3migration.h"
+#include "gimpsizeentry.h"
+#include "gimpwidgetsutils.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpwidgetsutils
+ * @title: GimpWidgetsUtils
+ * @short_description: A collection of helper functions.
+ *
+ * A collection of helper functions.
+ **/
+
+
+static GtkWidget *
+find_mnemonic_widget (GtkWidget *widget,
+ gint level)
+{
+ gboolean can_focus;
+
+ g_object_get (widget, "can-focus", &can_focus, NULL);
+
+ if (GTK_WIDGET_GET_CLASS (widget)->activate_signal ||
+ can_focus ||
+ GTK_WIDGET_GET_CLASS (widget)->mnemonic_activate !=
+ GTK_WIDGET_CLASS (g_type_class_peek (GTK_TYPE_WIDGET))->mnemonic_activate)
+ {
+ return widget;
+ }
+
+ if (GIMP_IS_SIZE_ENTRY (widget))
+ {
+ GimpSizeEntry *entry = GIMP_SIZE_ENTRY (widget);
+
+ return gimp_size_entry_get_help_widget (entry,
+ entry->number_of_fields - 1);
+ }
+ else if (GTK_IS_CONTAINER (widget))
+ {
+ GtkWidget *mnemonic_widget = NULL;
+ GList *children;
+ GList *list;
+
+ children = gtk_container_get_children (GTK_CONTAINER (widget));
+
+ for (list = children; list; list = g_list_next (list))
+ {
+ mnemonic_widget = find_mnemonic_widget (list->data, level + 1);
+
+ if (mnemonic_widget)
+ break;
+ }
+
+ g_list_free (children);
+
+ return mnemonic_widget;
+ }
+
+ return NULL;
+}
+
+/**
+ * gimp_table_attach_aligned:
+ * @table: The #GtkTable the widgets will be attached to.
+ * @column: The column to start with.
+ * @row: The row to attach the widgets.
+ * @label_text: The text for the #GtkLabel which will be attached left of
+ * the widget.
+ * @xalign: The horizontal alignment of the #GtkLabel.
+ * @yalign: The vertical alignment of the #GtkLabel.
+ * @widget: The #GtkWidget to attach right of the label.
+ * @colspan: The number of columns the widget will use.
+ * @left_align: %TRUE if the widget should be left-aligned.
+ *
+ * Note that the @label_text can be %NULL and that the widget will be
+ * attached starting at (@column + 1) in this case, too.
+ *
+ * Returns: The created #GtkLabel.
+ **/
+GtkWidget *
+gimp_table_attach_aligned (GtkTable *table,
+ gint column,
+ gint row,
+ const gchar *label_text,
+ gfloat xalign,
+ gfloat yalign,
+ GtkWidget *widget,
+ gint colspan,
+ gboolean left_align)
+{
+ GtkWidget *label = NULL;
+
+ if (label_text)
+ {
+ GtkWidget *mnemonic_widget;
+
+ label = gtk_label_new_with_mnemonic (label_text);
+ gtk_label_set_xalign (GTK_LABEL (label), xalign);
+ gtk_label_set_yalign (GTK_LABEL (label), yalign);
+ gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
+ gtk_table_attach (table, label,
+ column, column + 1,
+ row, row + 1,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ mnemonic_widget = find_mnemonic_widget (widget, 0);
+
+ if (mnemonic_widget)
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), mnemonic_widget);
+ }
+
+ if (left_align)
+ {
+ GtkWidget *hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+
+ gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
+ gtk_widget_show (widget);
+
+ widget = hbox;
+ }
+
+ gtk_table_attach (table, widget,
+ column + 1, column + 1 + colspan,
+ row, row + 1,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+
+ gtk_widget_show (widget);
+
+ return label;
+}
+
+/**
+ * gimp_label_set_attributes:
+ * @label: a #GtkLabel
+ * @...: a list of PangoAttrType and value pairs terminated by -1.
+ *
+ * Sets Pango attributes on a #GtkLabel in a more convenient way than
+ * gtk_label_set_attributes().
+ *
+ * This function is useful if you want to change the font attributes
+ * of a #GtkLabel. This is an alternative to using PangoMarkup which
+ * is slow to parse and awkward to handle in an i18n-friendly way.
+ *
+ * The attributes are set on the complete label, from start to end. If
+ * you need to set attributes on part of the label, you will have to
+ * use the PangoAttributes API directly.
+ *
+ * Since: 2.2
+ **/
+void
+gimp_label_set_attributes (GtkLabel *label,
+ ...)
+{
+ PangoAttribute *attr = NULL;
+ PangoAttrList *attrs;
+ va_list args;
+
+ g_return_if_fail (GTK_IS_LABEL (label));
+
+ attrs = pango_attr_list_new ();
+
+ va_start (args, label);
+
+ do
+ {
+ PangoAttrType attr_type = va_arg (args, PangoAttrType);
+
+ if (attr_type == -1)
+ attr_type = PANGO_ATTR_INVALID;
+
+ switch (attr_type)
+ {
+ case PANGO_ATTR_LANGUAGE:
+ attr = pango_attr_language_new (va_arg (args, PangoLanguage *));
+ break;
+
+ case PANGO_ATTR_FAMILY:
+ attr = pango_attr_family_new (va_arg (args, const gchar *));
+ break;
+
+ case PANGO_ATTR_STYLE:
+ attr = pango_attr_style_new (va_arg (args, PangoStyle));
+ break;
+
+ case PANGO_ATTR_WEIGHT:
+ attr = pango_attr_weight_new (va_arg (args, PangoWeight));
+ break;
+
+ case PANGO_ATTR_VARIANT:
+ attr = pango_attr_variant_new (va_arg (args, PangoVariant));
+ break;
+
+ case PANGO_ATTR_STRETCH:
+ attr = pango_attr_stretch_new (va_arg (args, PangoStretch));
+ break;
+
+ case PANGO_ATTR_SIZE:
+ attr = pango_attr_size_new (va_arg (args, gint));
+ break;
+
+ case PANGO_ATTR_FONT_DESC:
+ attr = pango_attr_font_desc_new (va_arg (args,
+ const PangoFontDescription *));
+ break;
+
+ case PANGO_ATTR_FOREGROUND:
+ {
+ const PangoColor *color = va_arg (args, const PangoColor *);
+
+ attr = pango_attr_foreground_new (color->red,
+ color->green,
+ color->blue);
+ }
+ break;
+
+ case PANGO_ATTR_BACKGROUND:
+ {
+ const PangoColor *color = va_arg (args, const PangoColor *);
+
+ attr = pango_attr_background_new (color->red,
+ color->green,
+ color->blue);
+ }
+ break;
+
+ case PANGO_ATTR_UNDERLINE:
+ attr = pango_attr_underline_new (va_arg (args, PangoUnderline));
+ break;
+
+ case PANGO_ATTR_STRIKETHROUGH:
+ attr = pango_attr_strikethrough_new (va_arg (args, gboolean));
+ break;
+
+ case PANGO_ATTR_RISE:
+ attr = pango_attr_rise_new (va_arg (args, gint));
+ break;
+
+ case PANGO_ATTR_SCALE:
+ attr = pango_attr_scale_new (va_arg (args, gdouble));
+ break;
+
+ default:
+ g_warning ("%s: invalid PangoAttribute type %d",
+ G_STRFUNC, attr_type);
+ case PANGO_ATTR_INVALID:
+ attr = NULL;
+ break;
+ }
+
+ if (attr)
+ {
+ attr->start_index = 0;
+ attr->end_index = -1;
+ pango_attr_list_insert (attrs, attr);
+ }
+ }
+ while (attr);
+
+ va_end (args);
+
+ gtk_label_set_attributes (label, attrs);
+ pango_attr_list_unref (attrs);
+}
+
+gint
+gimp_widget_get_monitor (GtkWidget *widget)
+{
+ GdkWindow *window;
+ GdkScreen *screen;
+ GtkAllocation allocation;
+ gint x, y;
+
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
+
+ window = gtk_widget_get_window (widget);
+
+ if (! window)
+ return gimp_get_monitor_at_pointer (&screen);
+
+ screen = gtk_widget_get_screen (widget);
+
+ gdk_window_get_origin (window, &x, &y);
+ gtk_widget_get_allocation (widget, &allocation);
+
+ if (! gtk_widget_get_has_window (widget))
+ {
+ x += allocation.x;
+ y += allocation.y;
+ }
+
+ x += allocation.width / 2;
+ y += allocation.height / 2;
+
+ return gdk_screen_get_monitor_at_point (screen, x, y);
+}
+
+gint
+gimp_get_monitor_at_pointer (GdkScreen **screen)
+{
+ gint x, y;
+
+ g_return_val_if_fail (screen != NULL, 0);
+
+ gdk_display_get_pointer (gdk_display_get_default (),
+ screen, &x, &y, NULL);
+
+ return gdk_screen_get_monitor_at_point (*screen, x, y);
+}
+
+typedef void (* MonitorChangedCallback) (GtkWidget *, gpointer);
+
+typedef struct
+{
+ GtkWidget *widget;
+ gint monitor;
+
+ MonitorChangedCallback callback;
+ gpointer user_data;
+} TrackMonitorData;
+
+static gboolean
+track_monitor_configure_event (GtkWidget *toplevel,
+ GdkEvent *event,
+ TrackMonitorData *track_data)
+{
+ gint monitor = gimp_widget_get_monitor (toplevel);
+
+ if (monitor != track_data->monitor)
+ {
+ track_data->monitor = monitor;
+
+ track_data->callback (track_data->widget, track_data->user_data);
+ }
+
+ return FALSE;
+}
+
+static void
+track_monitor_hierarchy_changed (GtkWidget *widget,
+ GtkWidget *previous_toplevel,
+ TrackMonitorData *track_data)
+{
+ GtkWidget *toplevel;
+
+ if (previous_toplevel)
+ {
+ g_signal_handlers_disconnect_by_func (previous_toplevel,
+ track_monitor_configure_event,
+ track_data);
+ }
+
+ toplevel = gtk_widget_get_toplevel (widget);
+
+ if (GTK_IS_WINDOW (toplevel))
+ {
+ GClosure *closure;
+ gint monitor;
+
+ closure = g_cclosure_new (G_CALLBACK (track_monitor_configure_event),
+ track_data, NULL);
+ g_object_watch_closure (G_OBJECT (widget), closure);
+ g_signal_connect_closure (toplevel, "configure-event", closure, FALSE);
+
+ monitor = gimp_widget_get_monitor (toplevel);
+
+ if (monitor != track_data->monitor)
+ {
+ track_data->monitor = monitor;
+
+ track_data->callback (track_data->widget, track_data->user_data);
+ }
+ }
+}
+
+/**
+ * gimp_widget_track_monitor:
+ * @widget: a #GtkWidget
+ * @monitor_changed_callback: the callback when @widget's monitor changes
+ * @user_data: data passed to @monitor_changed_callback
+ *
+ * This function behaves as if #GtkWidget had a signal
+ *
+ * GtkWidget::monitor_changed(GtkWidget *widget, gpointer user_data)
+ *
+ * That is emitted whenever @widget's toplevel window is moved from
+ * one monitor to another. This function automatically connects to
+ * the right toplevel #GtkWindow, even across moving @widget between
+ * toplevel windows.
+ *
+ * Note that this function tracks the toplevel, not @widget itself, so
+ * all a window's widgets are always considered to be on the same
+ * monitor. This is because this function is mainly used for fetching
+ * the new monitor's color profile, and it makes little sense to use
+ * different profiles for the widgets of one window.
+ *
+ * Since: 2.10
+ **/
+void
+gimp_widget_track_monitor (GtkWidget *widget,
+ GCallback monitor_changed_callback,
+ gpointer user_data)
+{
+ TrackMonitorData *track_data;
+ GtkWidget *toplevel;
+
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (monitor_changed_callback != NULL);
+
+ track_data = g_new0 (TrackMonitorData, 1);
+
+ track_data->widget = widget;
+ track_data->callback = (MonitorChangedCallback) monitor_changed_callback;
+ track_data->user_data = user_data;
+
+ g_object_weak_ref (G_OBJECT (widget), (GWeakNotify) g_free, track_data);
+
+ g_signal_connect (widget, "hierarchy-changed",
+ G_CALLBACK (track_monitor_hierarchy_changed),
+ track_data);
+
+ toplevel = gtk_widget_get_toplevel (widget);
+
+ if (GTK_IS_WINDOW (toplevel))
+ track_monitor_hierarchy_changed (widget, NULL, track_data);
+}
+
+/**
+ * gimp_screen_get_color_profile:
+ * @screen: a #GdkScreen
+ * @monitor: the monitor number
+ *
+ * This function returns the #GimpColorProfile of monitor number @monitor
+ * of @screen, or %NULL if there is no profile configured.
+ *
+ * Since: 2.10
+ *
+ * Return value: the monitor's #GimpColorProfile, or %NULL.
+ **/
+GimpColorProfile *
+gimp_screen_get_color_profile (GdkScreen *screen,
+ gint monitor)
+{
+ GimpColorProfile *profile = NULL;
+
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+ g_return_val_if_fail (monitor >= 0, NULL);
+ g_return_val_if_fail (monitor < gdk_screen_get_n_monitors (screen), NULL);
+
+#if defined GDK_WINDOWING_X11
+ {
+ GdkAtom type = GDK_NONE;
+ gint format = 0;
+ gint nitems = 0;
+ gchar *atom_name;
+ guchar *data = NULL;
+
+ if (monitor > 0)
+ atom_name = g_strdup_printf ("_ICC_PROFILE_%d", monitor);
+ else
+ atom_name = g_strdup ("_ICC_PROFILE");
+
+ if (gdk_property_get (gdk_screen_get_root_window (screen),
+ gdk_atom_intern (atom_name, FALSE),
+ GDK_NONE,
+ 0, 64 * 1024 * 1024, FALSE,
+ &type, &format, &nitems, &data) && nitems > 0)
+ {
+ profile = gimp_color_profile_new_from_icc_profile (data, nitems,
+ NULL);
+ g_free (data);
+ }
+
+ g_free (atom_name);
+ }
+#elif defined GDK_WINDOWING_QUARTZ
+ {
+ CGColorSpaceRef space = NULL;
+
+ space = CGDisplayCopyColorSpace (monitor);
+
+ if (space)
+ {
+ CFDataRef data;
+
+ data = CGColorSpaceCopyICCProfile (space);
+
+ if (data)
+ {
+ UInt8 *buffer = g_malloc (CFDataGetLength (data));
+
+ /* We cannot use CFDataGetBytesPtr(), because that returns
+ * a const pointer where cmsOpenProfileFromMem wants a
+ * non-const pointer.
+ */
+ CFDataGetBytes (data, CFRangeMake (0, CFDataGetLength (data)),
+ buffer);
+
+ profile = gimp_color_profile_new_from_icc_profile (buffer,
+ CFDataGetLength (data),
+ NULL);
+
+ g_free (buffer);
+ CFRelease (data);
+ }
+
+ CFRelease (space);
+ }
+ }
+#elif defined G_OS_WIN32
+ {
+ GdkRectangle monitor_geometry;
+ POINT point;
+ gint offsetx = GetSystemMetrics (SM_XVIRTUALSCREEN);
+ gint offsety = GetSystemMetrics (SM_YVIRTUALSCREEN);
+ HMONITOR monitor_handle;
+ MONITORINFOEX info;
+ DISPLAY_DEVICE display_device;
+
+ info.cbSize = sizeof (MONITORINFOEX);
+ display_device.cb = sizeof (DISPLAY_DEVICE);
+
+ /* If the first monitor is not set as the main monitor,
+ * the monitor variable may not match the index used in
+ * EnumDisplayDevices(devicename, index, displaydevice, flags).
+ */
+ gdk_screen_get_monitor_geometry (screen, monitor, &monitor_geometry);
+ point.x = monitor_geometry.x + offsetx;
+ point.y = monitor_geometry.y + offsety;
+ monitor_handle = MonitorFromPoint (point, MONITOR_DEFAULTTONEAREST);
+
+ if (GetMonitorInfo (monitor_handle, (LPMONITORINFO)&info))
+ {
+ if (EnumDisplayDevices (info.szDevice, 0, &display_device, 0))
+ {
+ gchar *device_key = g_convert (display_device.DeviceKey, -1, "UTF-16LE", "WINDOWS-1252", NULL, NULL, NULL);
+ gchar *filename = NULL;
+ gchar *dir = NULL;
+ gchar *fullpath = NULL;
+ GFile *file;
+ DWORD len = 0;
+ gboolean per_user;
+ WCS_PROFILE_MANAGEMENT_SCOPE scope;
+
+ WcsGetUsePerUserProfiles ((LPWSTR)device_key, CLASS_MONITOR, &per_user);
+ scope = per_user ? WCS_PROFILE_MANAGEMENT_SCOPE_CURRENT_USER : WCS_PROFILE_MANAGEMENT_SCOPE_SYSTEM_WIDE;
+
+ if (WcsGetDefaultColorProfileSize (scope,
+ (LPWSTR)device_key,
+ CPT_ICC,
+ CPST_NONE,
+ 0,
+ &len))
+ {
+ gchar *filename_utf16 = g_new (gchar, len);
+
+ WcsGetDefaultColorProfile (scope,
+ (LPWSTR)device_key,
+ CPT_ICC,
+ CPST_NONE,
+ 0,
+ len,
+ (LPWSTR)filename_utf16);
+
+ /* filename_utf16 must be native endian */
+ filename = g_utf16_to_utf8 ((gunichar2 *)filename_utf16, -1, NULL, NULL, NULL);
+ g_free (filename_utf16);
+ }
+ else
+ {
+ /* Due to a bug in Windows, the meanings of LCS_sRGB and
+ * LCS_WINDOWS_COLOR_SPACE are swapped.
+ */
+ GetStandardColorSpaceProfile (NULL, LCS_sRGB, NULL, &len);
+ filename = g_new (gchar, len);
+ GetStandardColorSpaceProfile (NULL, LCS_sRGB, filename, &len);
+ }
+
+ GetColorDirectory (NULL, NULL, &len);
+ dir = g_new (gchar, len);
+ GetColorDirectory (NULL, dir, &len);
+
+ fullpath = g_build_filename (dir, filename, NULL);
+ file = g_file_new_for_path (fullpath);
+
+ profile = gimp_color_profile_new_from_file (file, NULL);
+ g_object_unref (file);
+
+ g_free (fullpath);
+ g_free (dir);
+ g_free (filename);
+ g_free (device_key);
+ }
+ }
+ }
+#endif
+
+ return profile;
+}
+
+GimpColorProfile *
+gimp_widget_get_color_profile (GtkWidget *widget)
+{
+ GdkScreen *screen;
+ gint monitor;
+
+ g_return_val_if_fail (widget == NULL || GTK_IS_WIDGET (widget), NULL);
+
+ if (widget)
+ {
+ screen = gtk_widget_get_screen (widget);
+ monitor = gimp_widget_get_monitor (widget);
+ }
+ else
+ {
+ screen = gdk_screen_get_default ();
+ monitor = 0;
+ }
+
+ return gimp_screen_get_color_profile (screen, monitor);
+}
+
+static GimpColorProfile *
+get_display_profile (GtkWidget *widget,
+ GimpColorConfig *config)
+{
+ GimpColorProfile *profile = NULL;
+
+ if (gimp_color_config_get_display_profile_from_gdk (config))
+ /* get the toplevel's profile so all a window's colors look the same */
+ profile = gimp_widget_get_color_profile (gtk_widget_get_toplevel (widget));
+
+ if (! profile)
+ profile = gimp_color_config_get_display_color_profile (config, NULL);
+
+ if (! profile)
+ profile = gimp_color_profile_new_rgb_srgb ();
+
+ return profile;
+}
+
+typedef struct _TransformCache TransformCache;
+
+struct _TransformCache
+{
+ GimpColorTransform *transform;
+
+ GimpColorConfig *config;
+ GimpColorProfile *src_profile;
+ const Babl *src_format;
+ GimpColorProfile *dest_profile;
+ const Babl *dest_format;
+ GimpColorProfile *proof_profile;
+
+ gulong notify_id;
+};
+
+static GList *transform_caches = NULL;
+static gboolean debug_cache = FALSE;
+
+static gboolean
+profiles_equal (GimpColorProfile *profile1,
+ GimpColorProfile *profile2)
+{
+ return ((profile1 == NULL && profile2 == NULL) ||
+ (profile1 != NULL && profile2 != NULL &&
+ gimp_color_profile_is_equal (profile1, profile2)));
+}
+
+static TransformCache *
+transform_cache_get (GimpColorConfig *config,
+ GimpColorProfile *src_profile,
+ const Babl *src_format,
+ GimpColorProfile *dest_profile,
+ const Babl *dest_format,
+ GimpColorProfile *proof_profile)
+{
+ GList *list;
+
+ for (list = transform_caches; list; list = g_list_next (list))
+ {
+ TransformCache *cache = list->data;
+
+ if (config == cache->config &&
+ src_format == cache->src_format &&
+ dest_format == cache->dest_format &&
+ profiles_equal (src_profile, cache->src_profile) &&
+ profiles_equal (dest_profile, cache->dest_profile) &&
+ profiles_equal (proof_profile, cache->proof_profile))
+ {
+ if (debug_cache)
+ g_printerr ("found cache %p\n", cache);
+
+ return cache;
+ }
+ }
+
+ return NULL;
+}
+
+static void
+transform_cache_config_notify (GObject *config,
+ const GParamSpec *pspec,
+ TransformCache *cache)
+{
+ transform_caches = g_list_remove (transform_caches, cache);
+
+ g_signal_handler_disconnect (config, cache->notify_id);
+
+ if (cache->transform)
+ g_object_unref (cache->transform);
+
+ g_object_unref (cache->src_profile);
+ g_object_unref (cache->dest_profile);
+
+ if (cache->proof_profile)
+ g_object_unref (cache->proof_profile);
+
+ g_free (cache);
+
+ if (debug_cache)
+ g_printerr ("deleted cache %p\n", cache);
+}
+
+GimpColorTransform *
+gimp_widget_get_color_transform (GtkWidget *widget,
+ GimpColorConfig *config,
+ GimpColorProfile *src_profile,
+ const Babl *src_format,
+ const Babl *dest_format)
+{
+ static gboolean initialized = FALSE;
+ GimpColorProfile *dest_profile = NULL;
+ GimpColorProfile *proof_profile = NULL;
+ TransformCache *cache;
+
+ g_return_val_if_fail (widget == NULL || GTK_IS_WIDGET (widget), NULL);
+ g_return_val_if_fail (GIMP_IS_COLOR_CONFIG (config), NULL);
+ g_return_val_if_fail (GIMP_IS_COLOR_PROFILE (src_profile), NULL);
+ g_return_val_if_fail (src_format != NULL, NULL);
+ g_return_val_if_fail (dest_format != NULL, NULL);
+
+ if (G_UNLIKELY (! initialized))
+ {
+ initialized = TRUE;
+
+ debug_cache = g_getenv ("GIMP_DEBUG_TRANSFORM_CACHE") != NULL;
+ }
+
+ switch (gimp_color_config_get_mode (config))
+ {
+ case GIMP_COLOR_MANAGEMENT_OFF:
+ return NULL;
+
+ case GIMP_COLOR_MANAGEMENT_SOFTPROOF:
+ proof_profile = gimp_color_config_get_simulation_color_profile (config,
+ NULL);
+ /* fallthru */
+
+ case GIMP_COLOR_MANAGEMENT_DISPLAY:
+ dest_profile = get_display_profile (widget, config);
+ break;
+ }
+
+ cache = transform_cache_get (config,
+ src_profile,
+ src_format,
+ dest_profile,
+ dest_format,
+ proof_profile);
+
+ if (cache)
+ {
+ g_object_unref (dest_profile);
+
+ if (proof_profile)
+ g_object_unref (proof_profile);
+
+ if (cache->transform)
+ return g_object_ref (cache->transform);
+
+ return NULL;
+ }
+
+ if (! proof_profile &&
+ gimp_color_profile_is_equal (src_profile, dest_profile))
+ {
+ g_object_unref (dest_profile);
+
+ return NULL;
+ }
+
+ cache = g_new0 (TransformCache, 1);
+
+ if (debug_cache)
+ g_printerr ("creating cache %p\n", cache);
+
+ cache->config = g_object_ref (config);
+ cache->src_profile = g_object_ref (src_profile);
+ cache->src_format = src_format;
+ cache->dest_profile = dest_profile;
+ cache->dest_format = dest_format;
+ cache->proof_profile = proof_profile;
+
+ cache->notify_id =
+ g_signal_connect (cache->config, "notify",
+ G_CALLBACK (transform_cache_config_notify),
+ cache);
+
+ transform_caches = g_list_prepend (transform_caches, cache);
+
+ if (cache->proof_profile)
+ {
+ GimpColorTransformFlags flags = 0;
+
+ if (gimp_color_config_get_simulation_bpc (config))
+ flags |= GIMP_COLOR_TRANSFORM_FLAGS_BLACK_POINT_COMPENSATION;
+
+ if (! gimp_color_config_get_simulation_optimize (config))
+ flags |= GIMP_COLOR_TRANSFORM_FLAGS_NOOPTIMIZE;
+
+ if (gimp_color_config_get_simulation_gamut_check (config))
+ {
+ cmsUInt16Number alarmCodes[cmsMAXCHANNELS] = { 0, };
+ guchar r, g, b;
+
+ flags |= GIMP_COLOR_TRANSFORM_FLAGS_GAMUT_CHECK;
+
+ gimp_rgb_get_uchar (&config->out_of_gamut_color, &r, &g, &b);
+
+ alarmCodes[0] = (cmsUInt16Number) r * 256;
+ alarmCodes[1] = (cmsUInt16Number) g * 256;
+ alarmCodes[2] = (cmsUInt16Number) b * 256;
+
+ cmsSetAlarmCodes (alarmCodes);
+ }
+
+ cache->transform =
+ gimp_color_transform_new_proofing (cache->src_profile,
+ cache->src_format,
+ cache->dest_profile,
+ cache->dest_format,
+ cache->proof_profile,
+ gimp_color_config_get_simulation_intent (config),
+ gimp_color_config_get_display_intent (config),
+ flags);
+ }
+ else
+ {
+ GimpColorTransformFlags flags = 0;
+
+ if (gimp_color_config_get_display_bpc (config))
+ flags |= GIMP_COLOR_TRANSFORM_FLAGS_BLACK_POINT_COMPENSATION;
+
+ if (! gimp_color_config_get_display_optimize (config))
+ flags |= GIMP_COLOR_TRANSFORM_FLAGS_NOOPTIMIZE;
+
+ cache->transform =
+ gimp_color_transform_new (cache->src_profile,
+ cache->src_format,
+ cache->dest_profile,
+ cache->dest_format,
+ gimp_color_config_get_display_intent (config),
+ flags);
+ }
+
+ if (cache->transform)
+ return g_object_ref (cache->transform);
+
+ return NULL;
+}
diff --git a/libgimpwidgets/gimpwidgetsutils.h b/libgimpwidgets/gimpwidgetsutils.h
new file mode 100644
index 0000000..54925b4
--- /dev/null
+++ b/libgimpwidgets/gimpwidgetsutils.h
@@ -0,0 +1,66 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpwidgetsutils.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_WIDGETS_UTILS_H__
+#define __GIMP_WIDGETS_UTILS_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+GtkWidget * gimp_table_attach_aligned (GtkTable *table,
+ gint column,
+ gint row,
+ const gchar *label_text,
+ gfloat xalign,
+ gfloat yalign,
+ GtkWidget *widget,
+ gint colspan,
+ gboolean left_align);
+
+void gimp_label_set_attributes (GtkLabel *label,
+ ...);
+
+gint gimp_widget_get_monitor (GtkWidget *widget);
+gint gimp_get_monitor_at_pointer (GdkScreen **screen);
+
+void gimp_widget_track_monitor (GtkWidget *widget,
+ GCallback monitor_changed_callback,
+ gpointer user_data);
+
+GimpColorProfile * gimp_screen_get_color_profile (GdkScreen *screen,
+ gint monitor);
+GimpColorProfile * gimp_widget_get_color_profile (GtkWidget *widget);
+
+GimpColorTransform * gimp_widget_get_color_transform (GtkWidget *widget,
+ GimpColorConfig *config,
+ GimpColorProfile *src_profile,
+ const Babl *src_format,
+ const Babl *dest_format);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_WIDGETS_UTILS_H__ */
diff --git a/libgimpwidgets/gimpzoommodel.c b/libgimpwidgets/gimpzoommodel.c
new file mode 100644
index 0000000..2860595
--- /dev/null
+++ b/libgimpwidgets/gimpzoommodel.c
@@ -0,0 +1,698 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpzoommodel.c
+ * Copyright (C) 2005 David Odin <dindinx@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "gimpwidgetstypes.h"
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpmath/gimpmath.h"
+
+#include "gimphelpui.h"
+#include "gimpwidgetsmarshal.h"
+#include "gimpzoommodel.h"
+
+
+/**
+ * SECTION: gimpzoommodel
+ * @title: GimpZoomModel
+ * @short_description: A model for zoom values.
+ *
+ * A model for zoom values.
+ **/
+
+
+#define ZOOM_MIN (1.0 / 256.0)
+#define ZOOM_MAX (256.0)
+
+enum
+{
+ ZOOMED,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_VALUE,
+ PROP_MINIMUM,
+ PROP_MAXIMUM,
+ PROP_FRACTION,
+ PROP_PERCENTAGE
+};
+
+
+typedef struct
+{
+ gdouble value;
+ gdouble minimum;
+ gdouble maximum;
+} GimpZoomModelPrivate;
+
+#define GIMP_ZOOM_MODEL_GET_PRIVATE(obj) \
+ ((GimpZoomModelPrivate *) ((GimpZoomModel *) (obj))->priv)
+
+
+static void gimp_zoom_model_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_zoom_model_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+
+static guint zoom_model_signals[LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpZoomModel, gimp_zoom_model, G_TYPE_OBJECT)
+
+#define parent_class gimp_zoom_model_parent_class
+
+
+static void
+gimp_zoom_model_class_init (GimpZoomModelClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ /**
+ * GimpZoomModel::zoomed:
+ * @model: the object that received the signal
+ * @old_factor: the zoom factor before it changes
+ * @new_factor: the zoom factor after it has changed.
+ *
+ * Emitted when the zoom factor of the zoom model changes.
+ */
+ zoom_model_signals[ZOOMED] =
+ g_signal_new ("zoomed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GimpZoomModelClass,
+ zoomed),
+ NULL, NULL,
+ _gimp_widgets_marshal_VOID__DOUBLE_DOUBLE,
+ G_TYPE_NONE, 2,
+ G_TYPE_DOUBLE, G_TYPE_DOUBLE);
+
+ object_class->set_property = gimp_zoom_model_set_property;
+ object_class->get_property = gimp_zoom_model_get_property;
+
+ /**
+ * GimpZoomModel:value:
+ *
+ * The zoom factor.
+ */
+ g_object_class_install_property (object_class, PROP_VALUE,
+ g_param_spec_double ("value",
+ "Value",
+ "Zoom factor",
+ ZOOM_MIN, ZOOM_MAX,
+ 1.0,
+ GIMP_PARAM_READWRITE));
+ /**
+ * GimpZoomModel:minimum:
+ *
+ * The minimum zoom factor.
+ */
+ g_object_class_install_property (object_class, PROP_MINIMUM,
+ g_param_spec_double ("minimum",
+ "Minimum",
+ "Lower limit for the zoom factor",
+ ZOOM_MIN, ZOOM_MAX,
+ ZOOM_MIN,
+ GIMP_PARAM_READWRITE));
+ /**
+ * GimpZoomModel:maximum:
+ *
+ * The maximum zoom factor.
+ */
+ g_object_class_install_property (object_class, PROP_MAXIMUM,
+ g_param_spec_double ("maximum",
+ "Maximum",
+ "Upper limit for the zoom factor",
+ ZOOM_MIN, ZOOM_MAX,
+ ZOOM_MAX,
+ GIMP_PARAM_READWRITE));
+
+ /**
+ * GimpZoomModel:fraction:
+ *
+ * The zoom factor expressed as a fraction.
+ */
+ g_object_class_install_property (object_class, PROP_FRACTION,
+ g_param_spec_string ("fraction",
+ "Fraction",
+ "The zoom factor expressed as a fraction",
+ "1:1",
+ GIMP_PARAM_READABLE));
+ /**
+ * GimpZoomModel:percentage:
+ *
+ * The zoom factor expressed as percentage.
+ */
+ g_object_class_install_property (object_class, PROP_PERCENTAGE,
+ g_param_spec_string ("percentage",
+ "Percentage",
+ "The zoom factor expressed as a percentage",
+ "100%",
+ GIMP_PARAM_READABLE));
+}
+
+static void
+gimp_zoom_model_init (GimpZoomModel *model)
+{
+ GimpZoomModelPrivate *priv;
+
+ model->priv = gimp_zoom_model_get_instance_private (model);
+
+ priv = GIMP_ZOOM_MODEL_GET_PRIVATE (model);
+
+ priv->value = 1.0;
+ priv->minimum = ZOOM_MIN;
+ priv->maximum = ZOOM_MAX;
+}
+
+static void
+gimp_zoom_model_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpZoomModelPrivate *priv = GIMP_ZOOM_MODEL_GET_PRIVATE (object);
+ gdouble previous_value;
+
+ previous_value = priv->value;
+ g_object_freeze_notify (object);
+
+ switch (property_id)
+ {
+ case PROP_VALUE:
+ priv->value = g_value_get_double (value);
+
+ g_object_notify (object, "value");
+ g_object_notify (object, "fraction");
+ g_object_notify (object, "percentage");
+ break;
+
+ case PROP_MINIMUM:
+ priv->minimum = MIN (g_value_get_double (value), priv->maximum);
+ break;
+
+ case PROP_MAXIMUM:
+ priv->maximum = MAX (g_value_get_double (value), priv->minimum);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+
+ if (priv->value > priv->maximum || priv->value < priv->minimum)
+ {
+ priv->value = CLAMP (priv->value, priv->minimum, priv->maximum);
+
+ g_object_notify (object, "value");
+ g_object_notify (object, "fraction");
+ g_object_notify (object, "percentage");
+ }
+
+ g_object_thaw_notify (object);
+
+ if (priv->value != previous_value)
+ {
+ g_signal_emit (object, zoom_model_signals[ZOOMED],
+ 0, previous_value, priv->value);
+ }
+}
+
+static void
+gimp_zoom_model_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpZoomModelPrivate *priv = GIMP_ZOOM_MODEL_GET_PRIVATE (object);
+ gchar *tmp;
+
+ switch (property_id)
+ {
+ case PROP_VALUE:
+ g_value_set_double (value, priv->value);
+ break;
+
+ case PROP_MINIMUM:
+ g_value_set_double (value, priv->minimum);
+ break;
+
+ case PROP_MAXIMUM:
+ g_value_set_double (value, priv->maximum);
+ break;
+
+ case PROP_FRACTION:
+ {
+ gint numerator;
+ gint denominator;
+
+ gimp_zoom_model_get_fraction (GIMP_ZOOM_MODEL (object),
+ &numerator, &denominator);
+
+ tmp = g_strdup_printf ("%d:%d", numerator, denominator);
+ g_value_set_string (value, tmp);
+ g_free (tmp);
+ }
+ break;
+
+ case PROP_PERCENTAGE:
+ tmp = g_strdup_printf (priv->value >= 0.15 ? "%.0f%%" : "%.2f%%",
+ priv->value * 100.0);
+ g_value_set_string (value, tmp);
+ g_free (tmp);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_zoom_model_zoom_in (GimpZoomModel *model)
+{
+ GimpZoomModelPrivate *priv = GIMP_ZOOM_MODEL_GET_PRIVATE (model);
+
+ if (priv->value < priv->maximum)
+ gimp_zoom_model_zoom (model, GIMP_ZOOM_IN, 0.0);
+}
+
+static void
+gimp_zoom_model_zoom_out (GimpZoomModel *model)
+{
+ GimpZoomModelPrivate *priv = GIMP_ZOOM_MODEL_GET_PRIVATE (model);
+
+ if (priv->value > priv->minimum)
+ gimp_zoom_model_zoom (model, GIMP_ZOOM_OUT, 0.0);
+}
+
+/**
+ * gimp_zoom_model_new:
+ *
+ * Creates a new #GimpZoomModel.
+ *
+ * Return value: a new #GimpZoomModel.
+ *
+ * Since GIMP 2.4
+ **/
+GimpZoomModel *
+gimp_zoom_model_new (void)
+{
+ return g_object_new (GIMP_TYPE_ZOOM_MODEL, NULL);
+}
+
+
+/**
+ * gimp_zoom_model_set_range:
+ * @model: a #GimpZoomModel
+ * @min: new lower limit for zoom factor
+ * @max: new upper limit for zoom factor
+ *
+ * Sets the allowed range of the @model.
+ *
+ * Since GIMP 2.4
+ **/
+void
+gimp_zoom_model_set_range (GimpZoomModel *model,
+ gdouble min,
+ gdouble max)
+{
+ g_return_if_fail (GIMP_IS_ZOOM_MODEL (model));
+ g_return_if_fail (min < max);
+ g_return_if_fail (min >= ZOOM_MIN);
+ g_return_if_fail (max <= ZOOM_MAX);
+
+ g_object_set (model,
+ "minimum", min,
+ "maximum", max,
+ NULL);
+}
+
+/**
+ * gimp_zoom_model_zoom:
+ * @model: a #GimpZoomModel
+ * @zoom_type: the #GimpZoomType
+ * @scale: ignored unless @zoom_type == %GIMP_ZOOM_TO
+ *
+ * Since GIMP 2.4
+ **/
+void
+gimp_zoom_model_zoom (GimpZoomModel *model,
+ GimpZoomType zoom_type,
+ gdouble scale)
+{
+ g_return_if_fail (GIMP_IS_ZOOM_MODEL (model));
+
+ if (zoom_type != GIMP_ZOOM_TO)
+ scale = gimp_zoom_model_get_factor (model);
+
+ g_object_set (model,
+ "value", gimp_zoom_model_zoom_step (zoom_type, scale),
+ NULL);
+}
+
+/**
+ * gimp_zoom_model_get_factor:
+ * @model: a #GimpZoomModel
+ *
+ * Retrieves the current zoom factor of @model.
+ *
+ * Return value: the current scale factor
+ *
+ * Since GIMP 2.4
+ **/
+gdouble
+gimp_zoom_model_get_factor (GimpZoomModel *model)
+{
+ g_return_val_if_fail (GIMP_IS_ZOOM_MODEL (model), 1.0);
+
+ return GIMP_ZOOM_MODEL_GET_PRIVATE (model)->value;
+}
+
+
+/**
+ * gimp_zoom_model_get_fraction
+ * @model: a #GimpZoomModel
+ * @numerator: return location for numerator
+ * @denominator: return location for denominator
+ *
+ * Retrieves the current zoom factor of @model as a fraction.
+ *
+ * Since GIMP 2.4
+ **/
+void
+gimp_zoom_model_get_fraction (GimpZoomModel *model,
+ gint *numerator,
+ gint *denominator)
+{
+ gint p0, p1, p2;
+ gint q0, q1, q2;
+ gdouble zoom_factor;
+ gdouble remainder, next_cf;
+ gboolean swapped = FALSE;
+
+ g_return_if_fail (GIMP_IS_ZOOM_MODEL (model));
+ g_return_if_fail (numerator != NULL && denominator != NULL);
+
+ zoom_factor = gimp_zoom_model_get_factor (model);
+
+ /* make sure that zooming behaves symmetrically */
+ if (zoom_factor < 1.0)
+ {
+ zoom_factor = 1.0 / zoom_factor;
+ swapped = TRUE;
+ }
+
+ /* calculate the continued fraction for the desired zoom factor */
+
+ p0 = 1;
+ q0 = 0;
+ p1 = floor (zoom_factor);
+ q1 = 1;
+
+ remainder = zoom_factor - p1;
+
+ while (fabs (remainder) >= 0.0001 &&
+ fabs (((gdouble) p1 / q1) - zoom_factor) > 0.0001)
+ {
+ remainder = 1.0 / remainder;
+
+ next_cf = floor (remainder);
+
+ p2 = next_cf * p1 + p0;
+ q2 = next_cf * q1 + q0;
+
+ /* Numerator and Denominator are limited by 256 */
+ /* also absurd ratios like 170:171 are excluded */
+ if (p2 > 256 || q2 > 256 || (p2 > 1 && q2 > 1 && p2 * q2 > 200))
+ break;
+
+ /* remember the last two fractions */
+ p0 = p1;
+ p1 = p2;
+ q0 = q1;
+ q1 = q2;
+
+ remainder = remainder - next_cf;
+ }
+
+ zoom_factor = (gdouble) p1 / q1;
+
+ /* hard upper and lower bounds for zoom ratio */
+
+ if (zoom_factor > 256.0)
+ {
+ p1 = 256;
+ q1 = 1;
+ }
+ else if (zoom_factor < 1.0 / 256.0)
+ {
+ p1 = 1;
+ q1 = 256;
+ }
+
+ if (swapped)
+ {
+ *numerator = q1;
+ *denominator = p1;
+ }
+ else
+ {
+ *numerator = p1;
+ *denominator = q1;
+ }
+}
+
+static GtkWidget *
+zoom_button_new (const gchar *icon_name,
+ GtkIconSize icon_size)
+{
+ GtkWidget *button;
+ GtkWidget *image;
+
+ image = gtk_image_new_from_icon_name (icon_name,
+ icon_size > 0 ?
+ icon_size : GTK_ICON_SIZE_BUTTON);
+
+ button = gtk_button_new ();
+ gtk_container_add (GTK_CONTAINER (button), image);
+ gtk_widget_show (image);
+
+ return button;
+}
+
+static void
+zoom_in_button_callback (GimpZoomModel *model,
+ gdouble old,
+ gdouble new,
+ GtkWidget *button)
+{
+ GimpZoomModelPrivate *priv = GIMP_ZOOM_MODEL_GET_PRIVATE (model);
+
+ gtk_widget_set_sensitive (button, priv->value != priv->maximum);
+}
+
+static void
+zoom_out_button_callback (GimpZoomModel *model,
+ gdouble old,
+ gdouble new,
+ GtkWidget *button)
+{
+ GimpZoomModelPrivate *priv = GIMP_ZOOM_MODEL_GET_PRIVATE (model);
+
+ gtk_widget_set_sensitive (button, priv->value != priv->minimum);
+}
+
+/**
+ * gimp_zoom_button_new:
+ * @model: a #GimpZoomModel
+ * @zoom_type:
+ * @icon_size: use 0 for a button with text labels
+ *
+ * Return value: a newly created GtkButton
+ *
+ * Since GIMP 2.4
+ **/
+GtkWidget *
+gimp_zoom_button_new (GimpZoomModel *model,
+ GimpZoomType zoom_type,
+ GtkIconSize icon_size)
+{
+ GtkWidget *button = NULL;
+
+ g_return_val_if_fail (GIMP_IS_ZOOM_MODEL (model), NULL);
+
+ switch (zoom_type)
+ {
+ case GIMP_ZOOM_IN:
+ button = zoom_button_new ("zoom-in", icon_size);
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (gimp_zoom_model_zoom_in),
+ model);
+ g_signal_connect_object (model, "zoomed",
+ G_CALLBACK (zoom_in_button_callback),
+ button, 0);
+ break;
+
+ case GIMP_ZOOM_OUT:
+ button = zoom_button_new ("zoom-out", icon_size);
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (gimp_zoom_model_zoom_out),
+ model);
+ g_signal_connect_object (model, "zoomed",
+ G_CALLBACK (zoom_out_button_callback),
+ button, 0);
+ break;
+
+ default:
+ g_warning ("sorry, no button for this zoom type (%d)", zoom_type);
+ break;
+ }
+
+ if (button)
+ {
+ gdouble zoom = gimp_zoom_model_get_factor (model);
+
+ /* set initial button sensitivity */
+ g_signal_emit (model, zoom_model_signals[ZOOMED], 0, zoom, zoom);
+
+ if (icon_size > 0)
+ {
+ const gchar *desc;
+
+ if (gimp_enum_get_value (GIMP_TYPE_ZOOM_TYPE, zoom_type,
+ NULL, NULL, &desc, NULL))
+ {
+ gimp_help_set_help_data (button, desc, NULL);
+ }
+ }
+ }
+
+ return button;
+}
+
+/**
+ * gimp_zoom_model_zoom_step:
+ * @zoom_type: the zoom type
+ * @scale: ignored unless @zoom_type == %GIMP_ZOOM_TO
+ *
+ * Utility function to calculate a new scale factor.
+ *
+ * Return value: the new scale factor
+ *
+ * Since GIMP 2.4
+ **/
+gdouble
+gimp_zoom_model_zoom_step (GimpZoomType zoom_type,
+ gdouble scale)
+{
+ gint i, n_presets;
+ gdouble new_scale = 1.0;
+
+ /* This table is constructed to have fractions, that approximate
+ * sqrt(2)^k. This gives a smooth feeling regardless of the starting
+ * zoom level.
+ *
+ * Zooming in/out always jumps to a zoom step from the list below.
+ * However, we try to guarantee a certain size of the step, to
+ * avoid silly jumps from 101% to 100%.
+ *
+ * The factor 1.1 is chosen a bit arbitrary, but feels better
+ * than the geometric median of the zoom steps (2^(1/4)).
+ */
+
+#define ZOOM_MIN_STEP 1.1
+
+ const gdouble presets[] = {
+ 1.0 / 256, 1.0 / 180, 1.0 / 128, 1.0 / 90,
+ 1.0 / 64, 1.0 / 45, 1.0 / 32, 1.0 / 23,
+ 1.0 / 16, 1.0 / 11, 1.0 / 8, 2.0 / 11,
+ 1.0 / 4, 1.0 / 3, 1.0 / 2, 2.0 / 3,
+ 1.0,
+ 3.0 / 2, 2.0, 3.0,
+ 4.0, 11.0 / 2, 8.0, 11.0,
+ 16.0, 23.0, 32.0, 45.0,
+ 64.0, 90.0, 128.0, 180.0,
+ 256.0,
+ };
+
+ n_presets = G_N_ELEMENTS (presets);
+
+ switch (zoom_type)
+ {
+ case GIMP_ZOOM_IN:
+ scale *= ZOOM_MIN_STEP;
+
+ new_scale = presets[n_presets - 1];
+ for (i = n_presets - 1; i >= 0 && presets[i] > scale; i--)
+ new_scale = presets[i];
+
+ break;
+
+ case GIMP_ZOOM_OUT:
+ scale /= ZOOM_MIN_STEP;
+
+ new_scale = presets[0];
+ for (i = 0; i < n_presets && presets[i] < scale; i++)
+ new_scale = presets[i];
+
+ break;
+
+ case GIMP_ZOOM_IN_MORE:
+ scale = gimp_zoom_model_zoom_step (GIMP_ZOOM_IN, scale);
+ scale = gimp_zoom_model_zoom_step (GIMP_ZOOM_IN, scale);
+ scale = gimp_zoom_model_zoom_step (GIMP_ZOOM_IN, scale);
+ new_scale = scale;
+ break;
+
+ case GIMP_ZOOM_OUT_MORE:
+ scale = gimp_zoom_model_zoom_step (GIMP_ZOOM_OUT, scale);
+ scale = gimp_zoom_model_zoom_step (GIMP_ZOOM_OUT, scale);
+ scale = gimp_zoom_model_zoom_step (GIMP_ZOOM_OUT, scale);
+ new_scale = scale;
+ break;
+
+ case GIMP_ZOOM_IN_MAX:
+ new_scale = ZOOM_MAX;
+ break;
+
+ case GIMP_ZOOM_OUT_MAX:
+ new_scale = ZOOM_MIN;
+ break;
+
+ case GIMP_ZOOM_TO:
+ new_scale = scale;
+ break;
+ }
+
+ return CLAMP (new_scale, ZOOM_MIN, ZOOM_MAX);
+
+#undef ZOOM_MIN_STEP
+}
diff --git a/libgimpwidgets/gimpzoommodel.h b/libgimpwidgets/gimpzoommodel.h
new file mode 100644
index 0000000..6e64035
--- /dev/null
+++ b/libgimpwidgets/gimpzoommodel.h
@@ -0,0 +1,89 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * gimpzoommodel.h
+ * Copyright (C) 2005 David Odin <dindinx@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_WIDGETS_H_INSIDE__) && !defined (GIMP_WIDGETS_COMPILATION)
+#error "Only <libgimpwidgets/gimpwidgets.h> can be included directly."
+#endif
+
+#ifndef __GIMP_ZOOM_MODEL_H__
+#define __GIMP_ZOOM_MODEL_H__
+
+G_BEGIN_DECLS
+
+
+#define GIMP_TYPE_ZOOM_MODEL (gimp_zoom_model_get_type ())
+#define GIMP_ZOOM_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_ZOOM_MODEL, GimpZoomModel))
+#define GIMP_ZOOM_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_ZOOM_MODEL, GimpZoomModelClass))
+#define GIMP_IS_ZOOM_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_ZOOM_MODEL))
+#define GIMP_IS_ZOOM_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_ZOOM_MODEL))
+#define GIMP_ZOOM_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_ZOOM_MODEL, GimpZoomModel))
+
+
+typedef struct _GimpZoomModelClass GimpZoomModelClass;
+
+struct _GimpZoomModel
+{
+ GObject parent_instance;
+
+ /*< private >*/
+ gpointer priv;
+};
+
+struct _GimpZoomModelClass
+{
+ GObjectClass parent_class;
+
+ void (* zoomed) (GimpZoomModel *model,
+ gdouble old_factor,
+ gdouble new_factor);
+
+ /* Padding for future expansion */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+};
+
+
+GType gimp_zoom_model_get_type (void) G_GNUC_CONST;
+
+GimpZoomModel * gimp_zoom_model_new (void);
+void gimp_zoom_model_set_range (GimpZoomModel *model,
+ gdouble min,
+ gdouble max);
+void gimp_zoom_model_zoom (GimpZoomModel *model,
+ GimpZoomType zoom_type,
+ gdouble scale);
+gdouble gimp_zoom_model_get_factor (GimpZoomModel *model);
+void gimp_zoom_model_get_fraction (GimpZoomModel *model,
+ gint *numerator,
+ gint *denominator);
+
+GtkWidget * gimp_zoom_button_new (GimpZoomModel *model,
+ GimpZoomType zoom_type,
+ GtkIconSize icon_size);
+
+gdouble gimp_zoom_model_zoom_step (GimpZoomType zoom_type,
+ gdouble scale);
+
+G_END_DECLS
+
+#endif /* __GIMP_ZOOM_MODEL_H__ */
diff --git a/libgimpwidgets/test-eevl.c b/libgimpwidgets/test-eevl.c
new file mode 100644
index 0000000..7c5b9c0
--- /dev/null
+++ b/libgimpwidgets/test-eevl.c
@@ -0,0 +1,196 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * test-eevl.c
+ * Copyright (C) 2008 Fredrik Alstromer <roe@excu.se>
+ * Copyright (C) 2008 Martin Nordholts <martinn@svn.gnome.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* A small regression test case for the evaluator */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib-object.h>
+
+#include "libgimpmath/gimpmath.h"
+
+#include "gimpeevl.h"
+
+
+typedef struct
+{
+ const gchar *string;
+ GimpEevlQuantity result;
+ gboolean should_succeed;
+} TestCase;
+
+static gboolean
+test_units (const gchar *ident,
+ GimpEevlQuantity *factor,
+ gdouble *offset,
+ gpointer data)
+{
+ gboolean resolved = FALSE;
+ gboolean default_unit = (ident == NULL);
+
+ *offset = 0.0;
+
+ if (default_unit ||
+ (ident && strcmp ("in", ident) == 0))
+ {
+ factor->dimension = 1;
+ factor->value = 1.;
+
+ resolved = TRUE;
+ }
+ else if (ident && strcmp ("mm", ident) == 0)
+ {
+ factor->dimension = 1;
+ factor->value = 25.4;
+
+ resolved = TRUE;
+ }
+
+ return resolved;
+}
+
+
+int
+main(void)
+{
+ gint i;
+ gint failed = 0;
+ gint succeeded = 0;
+
+ TestCase cases[] =
+ {
+ /* "Default" test case */
+ { "2in + 3in", { 2 + 3, 1}, TRUE },
+
+ /* Whitespace variations */
+ { "2in+3in", { 2 + 3, 1}, TRUE },
+ { " 2in + 3in", { 2 + 3, 1}, TRUE },
+ { "2in + 3in ", { 2 + 3, 1}, TRUE },
+ { "2 in + 3 in", { 2 + 3, 1}, TRUE },
+ { " 2 in + 3 in ", { 2 + 3, 1}, TRUE },
+
+ /* Make sure the default unit is applied as it should */
+ { "2 + 3in", { 2 + 3, 1 }, TRUE },
+ { "3", { 3, 1 }, TRUE },
+
+ /* Somewhat complicated input */
+ { "(2 + 3)in", { 2 + 3, 1}, TRUE },
+ // { "2 / 3 in", { 2 / 3., 1}, TRUE },
+ { "(2 + 2/3)in", { 2 + 2 / 3., 1}, TRUE },
+ { "1/2 + 1/2", { 1, 1}, TRUE },
+ { "2 ^ 3 ^ 4", { pow (2, pow (3, 4)), 1}, TRUE },
+
+ /* Mixing of units */
+ { "2mm + 3in", { 2 / 25.4 + 3, 1}, TRUE },
+
+ /* 'odd' behavior */
+ { "2 ++ 1", { 3, 1}, TRUE },
+ { "2 +- 1", { 1, 1}, TRUE },
+ { "2 -- 1", { 3, 1}, TRUE },
+
+ /* End of test cases */
+ { NULL, { 0, 0 }, TRUE }
+ };
+
+ g_print ("Testing Eevl Eva, the Evaluator\n\n");
+
+ for (i = 0; cases[i].string; i++)
+ {
+ const gchar *test = cases[i].string;
+ GimpEevlOptions options = GIMP_EEVL_OPTIONS_INIT;
+ GimpEevlQuantity should = cases[i].result;
+ GimpEevlQuantity result = { 0, -1 };
+ gboolean should_succeed = cases[i].should_succeed;
+ GError *error = NULL;
+ const gchar *error_pos = 0;
+
+ options.unit_resolver_proc = test_units;
+
+ gimp_eevl_evaluate (test,
+ &options,
+ &result,
+ &error_pos,
+ &error);
+
+ g_print ("%s = %lg (%d): ", test, result.value, result.dimension);
+ if (error || error_pos)
+ {
+ if (should_succeed)
+ {
+ failed++;
+ g_print ("evaluation failed ");
+ if (error)
+ {
+ g_print ("with: %s, ", error->message);
+ }
+ else
+ {
+ g_print ("without reason, ");
+ }
+ if (error_pos)
+ {
+ if (*error_pos) g_print ("'%s'.", error_pos);
+ else g_print ("at end of input.");
+ }
+ else
+ {
+ g_print ("but didn't say where.");
+ }
+ g_print ("\n");
+ }
+ else
+ {
+ g_print ("OK (failure test case)\n");
+ succeeded++;
+ }
+ }
+ else if (!should_succeed)
+ {
+ g_print ("evaluation should've failed, but didn't.\n");
+ failed++;
+ }
+ else if (should.value != result.value || should.dimension != result.dimension)
+ {
+ g_print ("results don't match, should be: %lg (%d)\n",
+ should.value, should.dimension);
+ failed++;
+ }
+ else
+ {
+ g_print ("OK\n");
+ succeeded++;
+ }
+ }
+
+ g_print ("\n");
+ if (!failed)
+ g_print ("All OK. ");
+ else
+ g_print ("Test failed! ");
+
+ g_print ("(%d/%d) %lg%%\n\n", succeeded, succeeded+failed,
+ 100*succeeded/(gdouble)(succeeded+failed));
+
+ return failed;
+}
diff --git a/libgimpwidgets/test-preview-area.c b/libgimpwidgets/test-preview-area.c
new file mode 100644
index 0000000..fc37966
--- /dev/null
+++ b/libgimpwidgets/test-preview-area.c
@@ -0,0 +1,240 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
+ * test-preview-area.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+/* This code is based on testrgb.c from GTK+ - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+
+#include <gtk/gtk.h>
+
+#include "gimpwidgetstypes.h"
+
+#include "gimppreviewarea.h"
+
+
+#define WIDTH 256
+#define HEIGHT 256
+#define NUM_ITERS 100
+
+
+static void
+test_run (GtkWidget *area,
+ gboolean visible)
+{
+ guchar buf[WIDTH * HEIGHT * 8];
+ gint i, j;
+ gint num_iters = NUM_ITERS;
+ guchar val;
+ gdouble start_time, total_time;
+ GTimer *timer;
+ GEnumClass *enum_class;
+ GEnumValue *enum_value;
+
+ if (! visible)
+ num_iters *= 4;
+
+ gtk_widget_realize (area);
+
+ g_print ("\nPerformance tests for GimpPreviewArea "
+ "(%d x %d, %s, %d iterations):\n\n",
+ WIDTH, HEIGHT, visible ? "visible" : "hidden", num_iters);
+
+ val = 0;
+ for (j = 0; j < WIDTH * HEIGHT * 8; j++)
+ {
+ val = (val + ((val + (rand () & 0xff)) >> 1)) >> 1;
+ buf[j] = val;
+ }
+
+ gimp_preview_area_set_colormap (GIMP_PREVIEW_AREA (area), buf, 256);
+
+ /* Let's warm up the cache, and also wait for the window manager
+ to settle. */
+ for (i = 0; i < NUM_ITERS; i++)
+ {
+ gint offset = (rand () % (WIDTH * HEIGHT * 4)) & -4;
+
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (area),
+ 0, 0, WIDTH, HEIGHT,
+ GIMP_RGB_IMAGE,
+ buf + offset,
+ WIDTH * 4);
+ }
+
+ gdk_window_process_all_updates ();
+ gdk_flush ();
+
+ timer = g_timer_new ();
+
+ enum_class = g_type_class_ref (GIMP_TYPE_IMAGE_TYPE);
+
+ for (enum_value = enum_class->values; enum_value->value_name; enum_value++)
+ {
+ /* gimp_preview_area_draw */
+ start_time = g_timer_elapsed (timer, NULL);
+
+ for (i = 0; i < num_iters; i++)
+ {
+ gint offset = (rand () % (WIDTH * HEIGHT * 4)) & -4;
+
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (area),
+ 0, 0, WIDTH, HEIGHT,
+ enum_value->value,
+ buf + offset,
+ WIDTH * 4);
+
+ gdk_window_process_updates (area->window, FALSE);
+ }
+
+ gdk_flush ();
+ total_time = g_timer_elapsed (timer, NULL) - start_time;
+
+ g_print ("%-20s "
+ "draw : %5.2fs, %8.1f fps, %8.2f megapixels/s\n",
+ enum_value->value_name,
+ total_time,
+ num_iters / total_time,
+ num_iters * (WIDTH * HEIGHT * 1e-6) / total_time);
+
+ /* gimp_preview_area_blend */
+ start_time = g_timer_elapsed (timer, NULL);
+
+ for (i = 0; i < num_iters; i++)
+ {
+ gint offset = (rand () % (WIDTH * HEIGHT * 4)) & -4;
+ gint offset2 = (rand () % (WIDTH * HEIGHT * 4)) & -4;
+
+ gimp_preview_area_blend (GIMP_PREVIEW_AREA (area),
+ 0, 0, WIDTH, HEIGHT,
+ enum_value->value,
+ buf + offset,
+ WIDTH * 4,
+ buf + offset2,
+ WIDTH * 4,
+ rand () & 0xFF);
+
+ gdk_window_process_updates (area->window, FALSE);
+ }
+
+ gdk_flush ();
+ total_time = g_timer_elapsed (timer, NULL) - start_time;
+
+ g_print ("%-20s "
+ "blend : %5.2fs, %8.1f fps, %8.2f megapixels/s\n",
+ enum_value->value_name,
+ total_time,
+ num_iters / total_time,
+ num_iters * (WIDTH * HEIGHT * 1e-6) / total_time);
+
+ /* gimp_preview_area_mask */
+ start_time = g_timer_elapsed (timer, NULL);
+
+ for (i = 0; i < num_iters; i++)
+ {
+ gint offset = (rand () % (WIDTH * HEIGHT * 4)) & -4;
+ gint offset2 = (rand () % (WIDTH * HEIGHT * 4)) & -4;
+ gint offset3 = (rand () % (WIDTH * HEIGHT * 4)) & -4;
+
+ gimp_preview_area_mask (GIMP_PREVIEW_AREA (area),
+ 0, 0, WIDTH, HEIGHT,
+ enum_value->value,
+ buf + offset,
+ WIDTH * 4,
+ buf + offset2,
+ WIDTH * 4,
+ buf + offset3,
+ WIDTH);
+
+ gdk_window_process_updates (area->window, FALSE);
+ }
+
+ gdk_flush ();
+ total_time = g_timer_elapsed (timer, NULL) - start_time;
+
+ g_print ("%-20s "
+ "mask : %5.2fs, %8.1f fps, %8.2f megapixels/s\n",
+ enum_value->value_name,
+ total_time,
+ num_iters / total_time,
+ num_iters * (WIDTH * HEIGHT * 1e-6) / total_time);
+ g_print ("\n");
+ }
+
+ start_time = g_timer_elapsed (timer, NULL);
+
+ for (i = 0; i < num_iters; i++)
+ {
+ guchar r = rand () % 0xFF;
+ guchar g = rand () % 0xFF;
+ guchar b = rand () % 0xFF;
+
+ gimp_preview_area_fill (GIMP_PREVIEW_AREA (area),
+ 0, 0, WIDTH, HEIGHT,
+ r, g, b);
+
+ gdk_window_process_updates (area->window, FALSE);
+ }
+
+ gdk_flush ();
+ total_time = g_timer_elapsed (timer, NULL) - start_time;
+ g_print ("%-20s "
+ "fill : %5.2fs, %8.1f fps, %8.2f megapixels/s\n",
+ "Color fill",
+ total_time,
+ num_iters / total_time,
+ num_iters * (WIDTH * HEIGHT * 1e-6) / total_time);
+ g_print ("\n");
+}
+
+static void
+test_preview_area (void)
+{
+ GtkWidget *window;
+ GtkWidget *area;
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gtk_window_set_accept_focus (GTK_WINDOW (window), FALSE);
+
+ area = gimp_preview_area_new ();
+ gtk_container_add (GTK_CONTAINER (window), area);
+ gtk_widget_show (area);
+
+ test_run (area, FALSE);
+
+ gtk_widget_show (window);
+
+ test_run (area, TRUE);
+}
+
+int
+main (int argc, char **argv)
+{
+ gtk_init (&argc, &argv);
+
+ test_preview_area ();
+
+ return EXIT_SUCCESS;
+}