summaryrefslogtreecommitdiffstats
path: root/kolourpaint
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commit47d455dd55be855e4cc691c32f687f723d9247ee (patch)
tree52e236aaa2576bdb3840ebede26619692fed6d7d /kolourpaint
downloadtdegraphics-47d455dd55be855e4cc691c32f687f723d9247ee.tar.gz
tdegraphics-47d455dd55be855e4cc691c32f687f723d9247ee.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdegraphics@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kolourpaint')
-rw-r--r--kolourpaint/AUTHORS112
-rw-r--r--kolourpaint/BUGS154
-rw-r--r--kolourpaint/COPYING23
-rw-r--r--kolourpaint/ChangeLog15
-rw-r--r--kolourpaint/Makefile.am76
-rw-r--r--kolourpaint/NEWS349
-rw-r--r--kolourpaint/README102
-rw-r--r--kolourpaint/VERSION1
-rw-r--r--kolourpaint/cursors/Makefile.am11
-rw-r--r--kolourpaint/cursors/kpcursorlightcross.cpp128
-rw-r--r--kolourpaint/cursors/kpcursorlightcross.h38
-rw-r--r--kolourpaint/cursors/kpcursorprovider.cpp49
-rw-r--r--kolourpaint/cursors/kpcursorprovider.h43
-rw-r--r--kolourpaint/kolourpaint.cpp229
-rw-r--r--kolourpaint/kolourpaint.desktop92
-rw-r--r--kolourpaint/kolourpaintui.rc169
-rw-r--r--kolourpaint/kpcolor.cpp360
-rw-r--r--kolourpaint/kpcolor.h101
-rw-r--r--kolourpaint/kpcommandhistory.cpp939
-rw-r--r--kolourpaint/kpcommandhistory.h255
-rw-r--r--kolourpaint/kpdefs.h151
-rw-r--r--kolourpaint/kpdocument.cpp1539
-rw-r--r--kolourpaint/kpdocument.h260
-rw-r--r--kolourpaint/kpdocumentmetainfo.cpp186
-rw-r--r--kolourpaint/kpdocumentmetainfo.h90
-rw-r--r--kolourpaint/kpdocumentsaveoptions.cpp561
-rw-r--r--kolourpaint/kpdocumentsaveoptions.h150
-rw-r--r--kolourpaint/kpdocumentsaveoptionswidget.cpp951
-rw-r--r--kolourpaint/kpdocumentsaveoptionswidget.h200
-rw-r--r--kolourpaint/kpmainwindow.cpp1061
-rw-r--r--kolourpaint/kpmainwindow.h739
-rw-r--r--kolourpaint/kpmainwindow_edit.cpp1069
-rw-r--r--kolourpaint/kpmainwindow_file.cpp1409
-rw-r--r--kolourpaint/kpmainwindow_help.cpp219
-rw-r--r--kolourpaint/kpmainwindow_image.cpp474
-rw-r--r--kolourpaint/kpmainwindow_p.h49
-rw-r--r--kolourpaint/kpmainwindow_settings.cpp209
-rw-r--r--kolourpaint/kpmainwindow_statusbar.cpp417
-rw-r--r--kolourpaint/kpmainwindow_text.cpp395
-rw-r--r--kolourpaint/kpmainwindow_tools.cpp646
-rw-r--r--kolourpaint/kpmainwindow_view.cpp1151
-rw-r--r--kolourpaint/kpselection.cpp1446
-rw-r--r--kolourpaint/kpselection.h237
-rw-r--r--kolourpaint/kpselectiondrag.cpp294
-rw-r--r--kolourpaint/kpselectiondrag.h71
-rw-r--r--kolourpaint/kpselectiontransparency.cpp178
-rw-r--r--kolourpaint/kpselectiontransparency.h80
-rw-r--r--kolourpaint/kpsinglekeytriggersaction.cpp155
-rw-r--r--kolourpaint/kpsinglekeytriggersaction.h82
-rw-r--r--kolourpaint/kptemppixmap.cpp148
-rw-r--r--kolourpaint/kptemppixmap.h90
-rw-r--r--kolourpaint/kptextstyle.cpp279
-rw-r--r--kolourpaint/kptextstyle.h108
-rw-r--r--kolourpaint/kpthumbnail.cpp213
-rw-r--r--kolourpaint/kpthumbnail.h68
-rw-r--r--kolourpaint/kptool.cpp1666
-rw-r--r--kolourpaint/kptool.h422
-rw-r--r--kolourpaint/kpview.cpp1910
-rw-r--r--kolourpaint/kpview.h535
-rw-r--r--kolourpaint/kpviewmanager.cpp766
-rw-r--r--kolourpaint/kpviewmanager.h220
-rw-r--r--kolourpaint/kpviewscrollablecontainer.cpp1390
-rw-r--r--kolourpaint/kpviewscrollablecontainer.h255
-rw-r--r--kolourpaint/kpwidgetmapper.cpp76
-rw-r--r--kolourpaint/kpwidgetmapper.h47
-rw-r--r--kolourpaint/patches/checkerboard-faster-render.diff141
-rw-r--r--kolourpaint/patches/color_eraser_speedup.diff264
-rw-r--r--kolourpaint/patches/doc_resize_no_flicker.diff614
-rw-r--r--kolourpaint/pics/Makefile.am13
-rw-r--r--kolourpaint/pics/cr16-action-tool_brush.pngbin0 -> 675 bytes
-rw-r--r--kolourpaint/pics/cr16-action-tool_color_picker.pngbin0 -> 656 bytes
-rw-r--r--kolourpaint/pics/cr16-action-tool_color_washer.pngbin0 -> 1010 bytes
-rw-r--r--kolourpaint/pics/cr16-action-tool_curve.pngbin0 -> 483 bytes
-rw-r--r--kolourpaint/pics/cr16-action-tool_ellipse.pngbin0 -> 837 bytes
-rw-r--r--kolourpaint/pics/cr16-action-tool_elliptical_selection.pngbin0 -> 906 bytes
-rw-r--r--kolourpaint/pics/cr16-action-tool_eraser.pngbin0 -> 806 bytes
-rw-r--r--kolourpaint/pics/cr16-action-tool_flood_fill.pngbin0 -> 638 bytes
-rw-r--r--kolourpaint/pics/cr16-action-tool_free_form_selection.pngbin0 -> 855 bytes
-rw-r--r--kolourpaint/pics/cr16-action-tool_line.pngbin0 -> 439 bytes
-rw-r--r--kolourpaint/pics/cr16-action-tool_pen.pngbin0 -> 579 bytes
-rw-r--r--kolourpaint/pics/cr16-action-tool_polygon.pngbin0 -> 907 bytes
-rw-r--r--kolourpaint/pics/cr16-action-tool_polyline.pngbin0 -> 543 bytes
-rw-r--r--kolourpaint/pics/cr16-action-tool_rect_selection.pngbin0 -> 921 bytes
-rw-r--r--kolourpaint/pics/cr16-action-tool_rectangle.pngbin0 -> 557 bytes
-rw-r--r--kolourpaint/pics/cr16-action-tool_rounded_rectangle.pngbin0 -> 729 bytes
-rw-r--r--kolourpaint/pics/cr16-action-tool_spraycan.pngbin0 -> 784 bytes
-rw-r--r--kolourpaint/pics/cr16-action-tool_text.pngbin0 -> 401 bytes
-rw-r--r--kolourpaint/pics/cr22-action-tool_brush.pngbin0 -> 981 bytes
-rw-r--r--kolourpaint/pics/cr22-action-tool_color_picker.pngbin0 -> 981 bytes
-rw-r--r--kolourpaint/pics/cr22-action-tool_color_washer.pngbin0 -> 1555 bytes
-rw-r--r--kolourpaint/pics/cr22-action-tool_curve.pngbin0 -> 639 bytes
-rw-r--r--kolourpaint/pics/cr22-action-tool_ellipse.pngbin0 -> 1206 bytes
-rw-r--r--kolourpaint/pics/cr22-action-tool_elliptical_selection.pngbin0 -> 1317 bytes
-rw-r--r--kolourpaint/pics/cr22-action-tool_eraser.pngbin0 -> 1226 bytes
-rw-r--r--kolourpaint/pics/cr22-action-tool_flood_fill.pngbin0 -> 865 bytes
-rw-r--r--kolourpaint/pics/cr22-action-tool_free_form_selection.pngbin0 -> 1259 bytes
-rw-r--r--kolourpaint/pics/cr22-action-tool_line.pngbin0 -> 502 bytes
-rw-r--r--kolourpaint/pics/cr22-action-tool_pen.pngbin0 -> 857 bytes
-rw-r--r--kolourpaint/pics/cr22-action-tool_polygon.pngbin0 -> 1357 bytes
-rw-r--r--kolourpaint/pics/cr22-action-tool_polyline.pngbin0 -> 776 bytes
-rw-r--r--kolourpaint/pics/cr22-action-tool_rect_selection.pngbin0 -> 1337 bytes
-rw-r--r--kolourpaint/pics/cr22-action-tool_rectangle.pngbin0 -> 815 bytes
-rw-r--r--kolourpaint/pics/cr22-action-tool_rounded_rectangle.pngbin0 -> 1111 bytes
-rw-r--r--kolourpaint/pics/cr22-action-tool_spraycan.pngbin0 -> 1158 bytes
-rw-r--r--kolourpaint/pics/cr22-action-tool_text.pngbin0 -> 734 bytes
-rw-r--r--kolourpaint/pics/cr32-action-tool_brush.pngbin0 -> 1601 bytes
-rw-r--r--kolourpaint/pics/cr32-action-tool_color_picker.pngbin0 -> 1590 bytes
-rw-r--r--kolourpaint/pics/cr32-action-tool_color_washer.pngbin0 -> 2612 bytes
-rw-r--r--kolourpaint/pics/cr32-action-tool_curve.pngbin0 -> 947 bytes
-rw-r--r--kolourpaint/pics/cr32-action-tool_ellipse.pngbin0 -> 1872 bytes
-rw-r--r--kolourpaint/pics/cr32-action-tool_elliptical_selection.pngbin0 -> 2179 bytes
-rw-r--r--kolourpaint/pics/cr32-action-tool_eraser.pngbin0 -> 2047 bytes
-rw-r--r--kolourpaint/pics/cr32-action-tool_flood_fill.pngbin0 -> 1275 bytes
-rw-r--r--kolourpaint/pics/cr32-action-tool_free_form_selection.pngbin0 -> 2078 bytes
-rw-r--r--kolourpaint/pics/cr32-action-tool_line.pngbin0 -> 685 bytes
-rw-r--r--kolourpaint/pics/cr32-action-tool_pen.pngbin0 -> 1307 bytes
-rw-r--r--kolourpaint/pics/cr32-action-tool_polygon.pngbin0 -> 2036 bytes
-rw-r--r--kolourpaint/pics/cr32-action-tool_polyline.pngbin0 -> 1101 bytes
-rw-r--r--kolourpaint/pics/cr32-action-tool_rect_selection.pngbin0 -> 2128 bytes
-rw-r--r--kolourpaint/pics/cr32-action-tool_rectangle.pngbin0 -> 1002 bytes
-rw-r--r--kolourpaint/pics/cr32-action-tool_rounded_rectangle.pngbin0 -> 1683 bytes
-rw-r--r--kolourpaint/pics/cr32-action-tool_spraycan.pngbin0 -> 1928 bytes
-rw-r--r--kolourpaint/pics/cr32-action-tool_text.pngbin0 -> 1078 bytes
-rw-r--r--kolourpaint/pics/cr48-action-tool_brush.pngbin0 -> 2792 bytes
-rw-r--r--kolourpaint/pics/cr48-action-tool_color_picker.pngbin0 -> 2672 bytes
-rw-r--r--kolourpaint/pics/cr48-action-tool_color_washer.pngbin0 -> 4572 bytes
-rw-r--r--kolourpaint/pics/cr48-action-tool_curve.pngbin0 -> 1404 bytes
-rw-r--r--kolourpaint/pics/cr48-action-tool_ellipse.pngbin0 -> 2873 bytes
-rw-r--r--kolourpaint/pics/cr48-action-tool_elliptical_selection.pngbin0 -> 3442 bytes
-rw-r--r--kolourpaint/pics/cr48-action-tool_eraser.pngbin0 -> 3544 bytes
-rw-r--r--kolourpaint/pics/cr48-action-tool_flood_fill.pngbin0 -> 2052 bytes
-rw-r--r--kolourpaint/pics/cr48-action-tool_free_form_selection.pngbin0 -> 3607 bytes
-rw-r--r--kolourpaint/pics/cr48-action-tool_line.pngbin0 -> 981 bytes
-rw-r--r--kolourpaint/pics/cr48-action-tool_pen.pngbin0 -> 2163 bytes
-rw-r--r--kolourpaint/pics/cr48-action-tool_polygon.pngbin0 -> 3351 bytes
-rw-r--r--kolourpaint/pics/cr48-action-tool_polyline.pngbin0 -> 1647 bytes
-rw-r--r--kolourpaint/pics/cr48-action-tool_rect_selection.pngbin0 -> 3658 bytes
-rw-r--r--kolourpaint/pics/cr48-action-tool_rectangle.pngbin0 -> 1503 bytes
-rw-r--r--kolourpaint/pics/cr48-action-tool_rounded_rectangle.pngbin0 -> 2539 bytes
-rw-r--r--kolourpaint/pics/cr48-action-tool_spraycan.pngbin0 -> 3341 bytes
-rw-r--r--kolourpaint/pics/cr48-action-tool_text.pngbin0 -> 1539 bytes
-rw-r--r--kolourpaint/pics/crsc-action-tool_brush.svgzbin0 -> 9005 bytes
-rw-r--r--kolourpaint/pics/crsc-action-tool_color_picker.svgzbin0 -> 12495 bytes
-rw-r--r--kolourpaint/pics/crsc-action-tool_color_washer.svgzbin0 -> 8859 bytes
-rw-r--r--kolourpaint/pics/crsc-action-tool_curve.svgzbin0 -> 2442 bytes
-rw-r--r--kolourpaint/pics/crsc-action-tool_ellipse.svgzbin0 -> 2061 bytes
-rw-r--r--kolourpaint/pics/crsc-action-tool_elliptical_selection.svgzbin0 -> 3828 bytes
-rw-r--r--kolourpaint/pics/crsc-action-tool_eraser.svgzbin0 -> 7708 bytes
-rw-r--r--kolourpaint/pics/crsc-action-tool_flood_fill.svgzbin0 -> 2992 bytes
-rw-r--r--kolourpaint/pics/crsc-action-tool_free_form_selection.svgzbin0 -> 5071 bytes
-rw-r--r--kolourpaint/pics/crsc-action-tool_line.svgzbin0 -> 1359 bytes
-rw-r--r--kolourpaint/pics/crsc-action-tool_pen.svgzbin0 -> 5120 bytes
-rw-r--r--kolourpaint/pics/crsc-action-tool_polygon.svgzbin0 -> 2360 bytes
-rw-r--r--kolourpaint/pics/crsc-action-tool_polyline.svgzbin0 -> 2139 bytes
-rw-r--r--kolourpaint/pics/crsc-action-tool_rect_selection.svgzbin0 -> 4031 bytes
-rw-r--r--kolourpaint/pics/crsc-action-tool_rectangle.svgzbin0 -> 2021 bytes
-rw-r--r--kolourpaint/pics/crsc-action-tool_rounded_rectangle.svgzbin0 -> 2294 bytes
-rw-r--r--kolourpaint/pics/crsc-action-tool_spraycan.svgzbin0 -> 9431 bytes
-rw-r--r--kolourpaint/pics/crsc-action-tool_text.svgzbin0 -> 2732 bytes
-rw-r--r--kolourpaint/pics/custom/Makefile.am10
-rw-r--r--kolourpaint/pics/custom/color_transparent_26x26.pngbin0 -> 1055 bytes
-rw-r--r--kolourpaint/pics/custom/colorbutton_swap_16x16.pngbin0 -> 166 bytes
-rw-r--r--kolourpaint/pics/custom/image_rotate_anticlockwise.pngbin0 -> 371 bytes
-rw-r--r--kolourpaint/pics/custom/image_rotate_clockwise.pngbin0 -> 363 bytes
-rw-r--r--kolourpaint/pics/custom/image_skew_horizontal.pngbin0 -> 347 bytes
-rw-r--r--kolourpaint/pics/custom/image_skew_vertical.pngbin0 -> 424 bytes
-rw-r--r--kolourpaint/pics/custom/option_opaque.pngbin0 -> 787 bytes
-rw-r--r--kolourpaint/pics/custom/option_transparent.pngbin0 -> 888 bytes
-rw-r--r--kolourpaint/pics/custom/resize.pngbin0 -> 4465 bytes
-rw-r--r--kolourpaint/pics/custom/scale.pngbin0 -> 2222 bytes
-rw-r--r--kolourpaint/pics/custom/smooth_scale.pngbin0 -> 5556 bytes
-rw-r--r--kolourpaint/pics/custom/tool_spraycan_17x17.pngbin0 -> 188 bytes
-rw-r--r--kolourpaint/pics/custom/tool_spraycan_29x29.pngbin0 -> 364 bytes
-rw-r--r--kolourpaint/pics/custom/tool_spraycan_9x9.pngbin0 -> 121 bytes
-rw-r--r--kolourpaint/pics/hi16-app-kolourpaint.pngbin0 -> 814 bytes
-rw-r--r--kolourpaint/pics/hi22-app-kolourpaint.pngbin0 -> 1269 bytes
-rw-r--r--kolourpaint/pics/hi32-app-kolourpaint.pngbin0 -> 2231 bytes
-rw-r--r--kolourpaint/pics/hi48-app-kolourpaint.pngbin0 -> 4087 bytes
-rw-r--r--kolourpaint/pics/hisc-app-kolourpaint.svgzbin0 -> 14604 bytes
-rw-r--r--kolourpaint/pixmapfx/Makefile.am19
-rw-r--r--kolourpaint/pixmapfx/kpcoloreffect.cpp168
-rw-r--r--kolourpaint/pixmapfx/kpcoloreffect.h111
-rw-r--r--kolourpaint/pixmapfx/kpeffectbalance.cpp517
-rw-r--r--kolourpaint/pixmapfx/kpeffectbalance.h116
-rw-r--r--kolourpaint/pixmapfx/kpeffectblursharpen.cpp291
-rw-r--r--kolourpaint/pixmapfx/kpeffectblursharpen.h105
-rw-r--r--kolourpaint/pixmapfx/kpeffectemboss.cpp228
-rw-r--r--kolourpaint/pixmapfx/kpeffectemboss.h93
-rw-r--r--kolourpaint/pixmapfx/kpeffectflatten.cpp266
-rw-r--r--kolourpaint/pixmapfx/kpeffectflatten.h115
-rw-r--r--kolourpaint/pixmapfx/kpeffectinvert.cpp315
-rw-r--r--kolourpaint/pixmapfx/kpeffectinvert.h130
-rw-r--r--kolourpaint/pixmapfx/kpeffectreducecolors.cpp446
-rw-r--r--kolourpaint/pixmapfx/kpeffectreducecolors.h110
-rw-r--r--kolourpaint/pixmapfx/kpeffectsdialog.cpp369
-rw-r--r--kolourpaint/pixmapfx/kpeffectsdialog.h90
-rw-r--r--kolourpaint/pixmapfx/kpfloodfill.cpp362
-rw-r--r--kolourpaint/pixmapfx/kpfloodfill.h106
-rw-r--r--kolourpaint/pixmapfx/kppixmapfx.cpp1677
-rw-r--r--kolourpaint/pixmapfx/kppixmapfx.h450
-rw-r--r--kolourpaint/tests/45deg_line.pngbin0 -> 106 bytes
-rw-r--r--kolourpaint/tests/4x4-transparent.pngbin0 -> 98 bytes
-rw-r--r--kolourpaint/tests/5x5.pngbin0 -> 109 bytes
-rw-r--r--kolourpaint/tests/depth1.bmpbin0 -> 462 bytes
-rw-r--r--kolourpaint/tests/dither.pngbin0 -> 2894 bytes
-rw-r--r--kolourpaint/tests/rotate.pngbin0 -> 84 bytes
-rw-r--r--kolourpaint/tests/small16x16.pngbin0 -> 155 bytes
-rw-r--r--kolourpaint/tests/tool_fill_xlimit.pngbin0 -> 173 bytes
-rw-r--r--kolourpaint/tests/transparent.pngbin0 -> 212 bytes
-rw-r--r--kolourpaint/tests/transparent_selection.pngbin0 -> 694 bytes
-rw-r--r--kolourpaint/tools/Makefile.am53
-rw-r--r--kolourpaint/tools/kptoolaction.cpp107
-rw-r--r--kolourpaint/tools/kptoolaction.h78
-rw-r--r--kolourpaint/tools/kptoolairspray.cpp376
-rw-r--r--kolourpaint/tools/kptoolairspray.h110
-rw-r--r--kolourpaint/tools/kptoolautocrop.cpp780
-rw-r--r--kolourpaint/tools/kptoolautocrop.h127
-rw-r--r--kolourpaint/tools/kptoolbrush.cpp45
-rw-r--r--kolourpaint/tools/kptoolbrush.h43
-rw-r--r--kolourpaint/tools/kptoolclear.cpp135
-rw-r--r--kolourpaint/tools/kptoolclear.h68
-rw-r--r--kolourpaint/tools/kptoolcolorpicker.cpp197
-rw-r--r--kolourpaint/tools/kptoolcolorpicker.h95
-rw-r--r--kolourpaint/tools/kptoolcolorwasher.cpp45
-rw-r--r--kolourpaint/tools/kptoolcolorwasher.h43
-rw-r--r--kolourpaint/tools/kptoolconverttograyscale.cpp106
-rw-r--r--kolourpaint/tools/kptoolconverttograyscale.h57
-rw-r--r--kolourpaint/tools/kptoolcrop.cpp335
-rw-r--r--kolourpaint/tools/kptoolcrop.h39
-rw-r--r--kolourpaint/tools/kptoolcurve.cpp47
-rw-r--r--kolourpaint/tools/kptoolcurve.h45
-rw-r--r--kolourpaint/tools/kptoolellipse.cpp45
-rw-r--r--kolourpaint/tools/kptoolellipse.h45
-rw-r--r--kolourpaint/tools/kptoolellipticalselection.cpp46
-rw-r--r--kolourpaint/tools/kptoolellipticalselection.h43
-rw-r--r--kolourpaint/tools/kptooleraser.cpp44
-rw-r--r--kolourpaint/tools/kptooleraser.h43
-rw-r--r--kolourpaint/tools/kptoolflip.cpp213
-rw-r--r--kolourpaint/tools/kptoolflip.h88
-rw-r--r--kolourpaint/tools/kptoolfloodfill.cpp261
-rw-r--r--kolourpaint/tools/kptoolfloodfill.h94
-rw-r--r--kolourpaint/tools/kptoolfreeformselection.cpp46
-rw-r--r--kolourpaint/tools/kptoolfreeformselection.h43
-rw-r--r--kolourpaint/tools/kptoolline.cpp47
-rw-r--r--kolourpaint/tools/kptoolline.h45
-rw-r--r--kolourpaint/tools/kptoolpen.cpp1145
-rw-r--r--kolourpaint/tools/kptoolpen.h160
-rw-r--r--kolourpaint/tools/kptoolpolygon.cpp895
-rw-r--r--kolourpaint/tools/kptoolpolygon.h157
-rw-r--r--kolourpaint/tools/kptoolpolyline.cpp47
-rw-r--r--kolourpaint/tools/kptoolpolyline.h46
-rw-r--r--kolourpaint/tools/kptoolpreviewdialog.cpp431
-rw-r--r--kolourpaint/tools/kptoolpreviewdialog.h131
-rw-r--r--kolourpaint/tools/kptoolrectangle.cpp638
-rw-r--r--kolourpaint/tools/kptoolrectangle.h142
-rw-r--r--kolourpaint/tools/kptoolrectselection.cpp46
-rw-r--r--kolourpaint/tools/kptoolrectselection.h43
-rw-r--r--kolourpaint/tools/kptoolresizescale.cpp1222
-rw-r--r--kolourpaint/tools/kptoolresizescale.h196
-rw-r--r--kolourpaint/tools/kptoolrotate.cpp500
-rw-r--r--kolourpaint/tools/kptoolrotate.h129
-rw-r--r--kolourpaint/tools/kptoolroundedrectangle.cpp45
-rw-r--r--kolourpaint/tools/kptoolroundedrectangle.h45
-rw-r--r--kolourpaint/tools/kptoolselection.cpp2371
-rw-r--r--kolourpaint/tools/kptoolselection.h313
-rw-r--r--kolourpaint/tools/kptoolskew.cpp449
-rw-r--r--kolourpaint/tools/kptoolskew.h121
-rw-r--r--kolourpaint/tools/kptooltext.cpp1394
-rw-r--r--kolourpaint/tools/kptooltext.h203
-rw-r--r--kolourpaint/views/Makefile.am14
-rw-r--r--kolourpaint/views/kpthumbnailview.cpp98
-rw-r--r--kolourpaint/views/kpthumbnailview.h90
-rw-r--r--kolourpaint/views/kpunzoomedthumbnailview.cpp212
-rw-r--r--kolourpaint/views/kpunzoomedthumbnailview.h106
-rw-r--r--kolourpaint/views/kpzoomedthumbnailview.cpp140
-rw-r--r--kolourpaint/views/kpzoomedthumbnailview.h95
-rw-r--r--kolourpaint/views/kpzoomedview.cpp103
-rw-r--r--kolourpaint/views/kpzoomedview.h96
-rw-r--r--kolourpaint/widgets/Makefile.am21
-rw-r--r--kolourpaint/widgets/kpcolorsimilaritycube.cpp348
-rw-r--r--kolourpaint/widgets/kpcolorsimilaritycube.h72
-rw-r--r--kolourpaint/widgets/kpcolorsimilaritydialog.cpp123
-rw-r--r--kolourpaint/widgets/kpcolorsimilaritydialog.h62
-rw-r--r--kolourpaint/widgets/kpcolortoolbar.cpp1112
-rw-r--r--kolourpaint/widgets/kpcolortoolbar.h297
-rw-r--r--kolourpaint/widgets/kpresizesignallinglabel.cpp67
-rw-r--r--kolourpaint/widgets/kpresizesignallinglabel.h52
-rw-r--r--kolourpaint/widgets/kpsqueezedtextlabel.cpp215
-rw-r--r--kolourpaint/widgets/kpsqueezedtextlabel.h65
-rw-r--r--kolourpaint/widgets/kptooltoolbar.cpp640
-rw-r--r--kolourpaint/widgets/kptooltoolbar.h155
-rw-r--r--kolourpaint/widgets/kptoolwidgetbase.cpp608
-rw-r--r--kolourpaint/widgets/kptoolwidgetbase.h112
-rw-r--r--kolourpaint/widgets/kptoolwidgetbrush.cpp184
-rw-r--r--kolourpaint/widgets/kptoolwidgetbrush.h61
-rw-r--r--kolourpaint/widgets/kptoolwidgeterasersize.cpp161
-rw-r--r--kolourpaint/widgets/kptoolwidgeterasersize.h59
-rw-r--r--kolourpaint/widgets/kptoolwidgetfillstyle.cpp222
-rw-r--r--kolourpaint/widgets/kptoolwidgetfillstyle.h80
-rw-r--r--kolourpaint/widgets/kptoolwidgetlinewidth.cpp97
-rw-r--r--kolourpaint/widgets/kptoolwidgetlinewidth.h51
-rw-r--r--kolourpaint/widgets/kptoolwidgetopaqueortransparent.cpp100
-rw-r--r--kolourpaint/widgets/kptoolwidgetopaqueortransparent.h56
-rw-r--r--kolourpaint/widgets/kptoolwidgetspraycansize.cpp119
-rw-r--r--kolourpaint/widgets/kptoolwidgetspraycansize.h51
305 files changed, 54081 insertions, 0 deletions
diff --git a/kolourpaint/AUTHORS b/kolourpaint/AUTHORS
new file mode 100644
index 00000000..c4befef2
--- /dev/null
+++ b/kolourpaint/AUTHORS
@@ -0,0 +1,112 @@
+
+Authors
+=======
+
+Clarence Dang <dang@kde.org>
+Maintainer
+
+Thurston Dang <thurston_dang@users.sourceforge.net>
+Chief Investigator
+
+Kristof Borrey <borrey@kde.org>
+Icons
+
+Kazuki Ohta <mover@hct.zaq.ne.jp>
+InputMethod Support
+
+Nuno Pinheiro <nf.pinheiro@gmail.com>
+Icons
+
+Danny Allen <dannya40uk@yahoo.co.uk>
+Icons
+
+Martin Koller <m.koller@surfeu.at>
+Scanning Support
+
+
+Thanks To
+=========
+
+Rashid N. Achilov
+Toyohiro Asukai
+Bela-Andreas Bargel
+Waldo Bastian
+Ismail Belhachmi
+Sashmit Bhaduri
+Antonio Bianco
+Stephan Binner
+Markus Brueffer
+Rob Buis
+Lucijan Busch
+Mikhail Capone
+Enrico Ceppi
+Tom Chance
+Albert Astals Cid
+Jennifer Dang
+Lawrence Dang
+Christoph Eckert
+David Faure
+P. Fisher
+Nicolas Goutte
+Herbert Graeber
+Brad Grant
+David Greenaway
+Wilco Greven
+Hubert Grininger
+Adriaan de Groot
+Esben Mose Hansen
+Nadeem Hasan
+Simon Hausmann
+Michael Hoehne
+Andrew J
+Werner Joss
+Derek Kite
+Tobias Koenig
+Dmitry Kolesnikov
+Stephan Kulow
+Eric Laffoon
+Michael Lake
+Sebastien Laout
+David Ling
+Volker Lochte
+Anders Lund
+Jacek Masiulaniec
+Benjamin Meyer
+Amir Michail
+Robert Moszczynski
+Dirk Mueller
+Ruivaldo Neto
+Ralf Nolden
+Steven Pasternak
+Cedric Pasteur
+Erik K. Pedersen
+Dennis Pennekamp
+Jos Poortvliet
+Boudewijn Rempt
+Marcos Rodriguez
+Matt Rogers
+Francisco Jose Canizares Santofimia
+Bram Schoenmakers
+Dirk Schonberger
+Lutz Schweizer
+Emmeran Seehuber
+Peter Simonsson
+Andrew Simpson
+A T Somers
+Igor Stepin
+Stephen Sweeney
+Bart Symons
+Stefan Taferner
+Hogne Titlestad
+Brandon Mark Turner
+Jonathan Turner
+Stephan Unknown
+Dries Verachtert
+Simon Vermeersch
+Lauri Watts
+Mark Wege
+Christoph Wiesen
+Andre Wobbeking
+Luke-Jr
+Maxim_86ualb2
+Michele
diff --git a/kolourpaint/BUGS b/kolourpaint/BUGS
new file mode 100644
index 00000000..84f3391f
--- /dev/null
+++ b/kolourpaint/BUGS
@@ -0,0 +1,154 @@
+
+Please send bug reports and feature requests to http://bugs.kde.org/.
+Don't hesitate to report bugs nor hesitate to send us your wishes - it
+provides valuable feedback that will help to improve future versions of
+KolourPaint and you will not receive flames for reporting duplicates.
+
+
+This file lists known bugs in this version that are not considered
+"release critical" and are difficult to fix:
+
+
+1. Flicker when zooming in/out.
+
+3. Tool Box & Colour Box RMB ToolBar Menus do not work.
+
+4. Image dialog spinboxes should accept Enter Key (instead of the dialog's
+ OK button) after the user has typed something.
+
+ OR
+
+ Spinboxes should signal that their values have changed every time the
+ user changes the text (rather than after pressing Enter or clicking on
+ another spinbox etc.).
+
+ The need for the "Update Preview" button and the difficulty of keeping
+ the percentages and dimensions in sync in the Resize / Scale dialog are
+ manifestations of the current QSpinBox behaviour.
+
+6. a) The undo history and document modified state are not updated during
+ the drawing of multi-segment shapes (Polygon, Connected Lines,
+ Curve). They are however updated after shapes' completion.
+
+ b) The text and brush-like tools set the document modified flag even if
+ user cancels the draw operation.
+
+ c) Select a region, manipulate it (e.g. move), undo - the document is
+ still marked as modified (because 2 commands - the create selection
+ and the move - were added but only one was undone).
+
+7. Certain shapes may have the wrong size (usually only a pixel off and
+ only in extreme cases) e.g. an ellipse of height 1 always has a width 1
+ pixel less than it should be. This is a Qt bug.
+
+8. At zoom levels that aren't multiples of 100%, parts of the image may
+ appear to move when the user interacts with it. Other minor redraw
+ glitches may also occur at such zoom levels.
+
+9. Keyboard shortcut changes do not propagate to other KolourPaint windows
+ (but will propagate to future windows).
+
+10. "File/Open Recent" entries are not updated interprocess.
+
+11. The blinking text cursor will "disappear" if you type more text than
+ you can fit in a text box.
+
+12. You cannot select only parts of the text you write.
+
+13. Due to a workaround for a Qt bug, writing text with the foreground
+ colour set to transparent is incredibly slow. Write your text in
+ another colour and then set the foreground colour to transparent after
+ you've finished typing to avoid this issue.
+
+14. The text cursor may be momentarily misrendered when scrolling the view.
+
+17. a) Using KolourPaint on a remote X display may result in redraw errors
+ and pixel data corruption.
+
+ b) KolourPaint is screen depth dependent. Opening an image with a
+ an alpha channel and/or a depth higher than the screen and then
+ saving it will likely result in loss of colour information. Also,
+ 8-bit screens are not supported at all. To reduce data loss, run
+ your screen at 24-bit. This bug will be addressed in a future
+ version of KolourPaint.
+
+19. Read support for EPS files is extremely slow. You should not enable
+ the "Save Preview" dialog when saving to EPS. This is an issue with
+ KDE.
+
+20. Pasting a large image (esp. one that doesn't compress well as PNG)
+ into an image editor (not necessarily KolourPaint) running as
+ different process from the KolourPaint which was the source of the
+ image, on a sufficiently slow computer, may fail with the following
+ output to STDERR:
+
+ "kolourpaint: ERROR: kpMainWindow::paste() with sel without pixmap
+ QClipboard: timed out while sending data"
+
+ This is a Qt bug.
+
+21. It is not always possible to copy and paste between 2 instances of
+ KolourPaint running different Qt versions. See
+ QDataStream::setVersion().
+
+22. The Emboss, Blur and Sharpen effects give different results depending
+ on _both_:
+
+ a) The KDE version KolourPaint was compiled with
+ (due to KImageEffect not supporting strength settings for these
+ effects in KDE 3.0, KolourPaint repeats these effects in order to
+ simulate strength)
+
+ b) The KDE version KolourPaint is running under
+ (e.g. for the same function calls, KDE 3.2's effects are slower but
+ give better results than those in KDE 3.0)
+
+23. Changing tool options while in the middle of a drawing option may
+ confuse KolourPaint. For instance:
+
+ a) With the brush tools, the cursor incorrectly appears.
+
+ b) With the rectangle-based tools, the temporary pixmap does not resize
+ when the line width increases.
+
+25. Sometimes when you take a screenshot of a window, and then paste in a
+ new window, it will be greyscale. When pasting again, it will still be
+ greyscale. Cannot consistently reproduce. [Thurston]
+
+26. Drawing with the keyboard is unreliable. Depending on the X server,
+ either holding down Enter may continually switch between drawing and
+ not drawing or KolourPaint may fail to detect the release of the Enter
+ key.
+
+27. InputMethod has not been tested at zoom levels other than 100%.
+
+28. KolourPaint has not been tested against invalid or malicious clipboard
+ data.
+
+
+Issue with XFree86 <= 3.3.6 with the "Emulate3Buttons" Option
+=============================================================
+
+When drawing, clicking the left or right mouse button that did not
+initiate the current operation will, in this order:
+
+1. finalise the current drawing operation
+2. attempt to paste the contents of the middle-mouse-button clipboard
+
+instead of canceling the current drawing operation.
+
+This is due to XFree86 sending a release notification for the button that
+initiated the drawing operation, followed by a press notification for the
+emulated 3rd button; instead of just a single press notification for the
+button that is intended to cancel the operation. This works correctly in
+XFree86 4.x with "Emulate3Buttons" on because it is harder to trigger the
+emulation for the 3rd button as it is only invoked if the left and right
+buttons are pressed at almost the same time.
+
+Possible solutions:
+
+a) Use XFree86 4.x or an X server from another vendor (e.g. X.org).
+b) Press Escape in KolourPaint to cancel the current drawing operation
+ instead of using the problematic click method described above.
+c) Disable "Emulate3Buttons".
+
diff --git a/kolourpaint/COPYING b/kolourpaint/COPYING
new file mode 100644
index 00000000..df47eb9b
--- /dev/null
+++ b/kolourpaint/COPYING
@@ -0,0 +1,23 @@
+Copyright (c) 2003,2004,2005,2006 Clarence Dang <dang@kde.org>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/kolourpaint/ChangeLog b/kolourpaint/ChangeLog
new file mode 100644
index 00000000..9acd0397
--- /dev/null
+++ b/kolourpaint/ChangeLog
@@ -0,0 +1,15 @@
+
+For logs of _every_ single change made to KolourPaint between any date or
+revision, visit:
+
+ http://websvn.kde.org/trunk/KDE/kdegraphics/kolourpaint
+
+ http://websvn.kde.org/branches/KDE/<version>/kdegraphics/kolourpaint
+ http://websvn.kde.org/tags/KDE/<version>/kdegraphics/kolourpaint
+
+ http://websvn.kde.org/branches/kolourpaint
+ http://websvn.kde.org/tags/kolourpaint
+
+
+For a summary of user-visible changes between each release, read NEWS.
+
diff --git a/kolourpaint/Makefile.am b/kolourpaint/Makefile.am
new file mode 100644
index 00000000..35da2859
--- /dev/null
+++ b/kolourpaint/Makefile.am
@@ -0,0 +1,76 @@
+SUBDIRS = cursors pics pixmapfx tools views widgets
+
+bin_PROGRAMS = kolourpaint
+
+
+kolourpaint.o: kolourpaintlicense.h kolourpaintversion.h
+
+kolourpaintlicense.h : $(srcdir)/COPYING
+ echo "static const char * const kpLicenseText =" > kolourpaintlicense.h
+ cat $(srcdir)/COPYING | sed -e 's/"/\\"/g' -e 's/$$/\\n"/g' -e 's/^/ "/g' >> kolourpaintlicense.h
+ echo ";" >> kolourpaintlicense.h
+
+kolourpaintversion.h : $(srcdir)/VERSION
+ echo "static const char * const kpVersionText =" > kolourpaintversion.h
+ cat $(srcdir)/VERSION | sed -e 's/"/\\"/g' -e 's/$$/"/g' -e 's/^/ "/g' >> kolourpaintversion.h
+ echo ";" >> kolourpaintversion.h
+
+CLEANFILES = kolourpaintlicense.h kolourpaintversion.h
+
+
+kolourpaint_SOURCES = kolourpaint.cpp \
+ kpdocument.cpp \
+ kpdocumentmetainfo.cpp \
+ kpdocumentsaveoptions.cpp \
+ kpdocumentsaveoptionswidget.cpp \
+ kpview.cpp \
+ kpcolor.cpp kpcommandhistory.cpp \
+ kpmainwindow.cpp \
+ kpmainwindow_edit.cpp kpmainwindow_help.cpp \
+ kpmainwindow_image.cpp kpmainwindow_tools.cpp \
+ kpmainwindow_file.cpp kpmainwindow_settings.cpp kpmainwindow_statusbar.cpp \
+ kpmainwindow_text.cpp \
+ kpmainwindow_view.cpp \
+ kpselection.cpp kpselectiondrag.cpp kpselectiontransparency.cpp \
+ kpsinglekeytriggersaction.cpp \
+ kptemppixmap.cpp kptextstyle.cpp \
+ kpthumbnail.cpp \
+ kptool.cpp \
+ kpviewmanager.cpp \
+ kpviewscrollablecontainer.cpp \
+ kpwidgetmapper.cpp
+kolourpaint_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+kolourpaint_LDADD = $(LIB_KDEPRINT) \
+ cursors/libkolourpaintcursors.la \
+ pixmapfx/libkolourpaintpixmapfx.la \
+ tools/libkolourpainttools.la \
+ views/libkolourpaintviews.la \
+ widgets/libkolourpaintwidgets.la
+
+AM_CPPFLAGS = -I$(srcdir)/cursors -I$(srcdir)/interfaces \
+ -I$(srcdir)/pixmapfx \
+ -I$(srcdir)/tools \
+ -I$(srcdir)/views \
+ -I$(srcdir)/widgets $(all_includes)
+
+METASOURCES = AUTO
+
+rcdir = $(kde_datadir)/kolourpaint
+rc_DATA = kolourpaintui.rc
+
+xdg_apps_DATA = kolourpaint.desktop
+
+messages: rc.cpp
+ $(EXTRACTRC) *.rc *.ui \
+ cursors/*.rc cursors/*.ui \
+ pixmapfx/*.rc pixmapfx/*.ui \
+ tools/*.rc tools/*.ui \
+ widgets/*.rc widgets/*.ui \
+ >> rc.cpp
+ $(XGETTEXT) *.cpp *.h \
+ cursors/*.cpp cursors/*.h \
+ pixmapfx/*.cpp pixmapfx/*.h \
+ tools/*.cpp tools/*.h \
+ widgets/*.cpp widgets/*.h \
+ -o $(podir)/kolourpaint.pot
+
diff --git a/kolourpaint/NEWS b/kolourpaint/NEWS
new file mode 100644
index 00000000..43828069
--- /dev/null
+++ b/kolourpaint/NEWS
@@ -0,0 +1,349 @@
+
+KolourPaint 1.4_relight Series (branches/KDE/3.5/)
+===============================
+
+KolourPaint 1.4.9_relight (Frozen ???)
+
+ * Ensure selection operations always repaint correctly
+ [the effects of this change are unlikely to be functionality visible]
+
+KolourPaint 1.4.8_relight (Frozen 2007-10-08)
+
+ * Always enable the paste actions to guarantee that pasting from
+ non-Qt applications is always allowed (non-Qt applications do not
+ notify KolourPaint when they place objects into the clipboard)
+
+ * Paste transparent pixels as white instead of uninitialized colors,
+ when the app does not support pasting transparent pixels (such as
+ OpenOffice.org)
+
+ * Make "Edit / Paste in New Window" always paste white pixels as white
+ (it used to paste them as transparent when the selection transparency
+ mode was set to Transparent)
+
+ * Saving, exporting and printing a document with an active text box,
+ that has opaque text and a transparent background, antialiases the
+ text with the document below
+
+ * "Edit / Paste From File..." respects the "Transparent" selection mode
+
+ * Focus an input field when the "Skew", "Rotate" and "Resize / Scale"
+ dialogs are displayed -- this allows the user to edit values without
+ an extra mouse click
+
+ * Add error dialogs for:
+ - if scanning support is unavailable
+ - running out of graphics memory during a scan
+
+ * Other minor changes -- some of these are:
+ - Finish the current shape in more cases of menu item accesses
+ - [internal] kpDocument::selectionCopyOntoDocument() marks the document
+ as modified
+ - More comments
+
+KolourPaint 1.4.7_relight (Frozen 2007-05-14)
+
+ * Save local files atomically - KolourPaint will no longer truncate
+ an existing file if the KImageIO library for the file format is
+ missing or if you run out of disk space.
+
+ * Add "File / Scan..." feature (Martin Koller)
+
+ * Add global session save/restore (Bug #94651)
+
+ * Make "File / Open Recent" consistently work when multiple windows are
+ open
+
+ * CTRL+C'ing a text box also places the text in the middle-mouse-button
+ clipboard, in lieu of being able to highlight the text to do this
+
+ * Change minimum allowed zoom level for the grid from 600% to 400%
+
+KolourPaint 1.4.6_relight (Frozen 2007-01-13)
+
+ * Fix crash triggered by rapidly deselecting the selection after
+ drag-scaling it (Bug #117866)
+ [also in branches/KDE/3.[34]/, branches/kolourpaint/1.2_kde3/]
+
+KolourPaint 1.4.5_relight (Frozen 2006-09-19)
+
+ * Translation updates
+
+KolourPaint 1.4.4_relight (Frozen 2006-07-12)
+
+ * Minor code cleanups and corrections
+
+KolourPaint 1.4.3_relight (Frozen 2006-05-02)
+
+ * Probably translation updates
+
+KolourPaint 1.4.2_relight (Frozen 2006-03-12)
+
+ * Printing improvements (Bug #108976)
+ - Respect image DPI
+ - Fit image to page if image is too big
+ - Centre image on page
+ [also in branches/KDE/3.[34]/, branches/kolourpaint/1.2_kde3/]
+
+KolourPaint 1.4.1_relight (Frozen 2006-01-15)
+
+ * Updated documentation (Thurston)
+
+KolourPaint 1.4_relight (Frozen 2005-11-08)
+
+ * New icons (Danny Allen, Nuno Pinheiro)
+
+ * Tool Box icon size is 22x22, not 16x16, at screen resolution >= 1024x768
+
+ * CTRL + Mouse Wheel = Zoom
+
+ * While freehand selection scaling, holding Shift maintains aspect ratio
+
+ * Prevent accidental drags in the Colour Palette from pasting text
+ containing the colour code
+ [also in branches/KDE/3.[34]/, branches/kolourpaint/1.2_kde3/]
+
+ * Cells in the bottom row and cells in the rightmost column of the Colour
+ Palette are now the same size as the other cells
+ [also in branches/KDE/3.[34]/, branches/kolourpaint/1.2_kde3/]
+
+ * Text drops to the empty part of the scrollview will not be placed
+ outside the document
+ [also in branches/KDE/3.[34]/, branches/kolourpaint/1.2_kde3/]
+
+ * Rename icons from "hi" to "cr" - back to the state of 1.0 (Danny Allen)
+ but leave application icons as "hi" (Jonathan Riddell)
+
+ * Enforce text box font height to prevent e.g. Chinese characters in
+ buggy fonts from enlarging the text box and putting the cursor out of
+ sync with the text
+ [also in branches/KDE/3.[34]/, branches/kolourpaint/1.2_kde3/]
+
+ * Clicking in a text box selects a character based on its midpoint -
+ not leftmost point - to be consistent with all text editors
+ (esp. noticeable with big fonts)
+ [also in branches/KDE/3.[34]/, branches/kolourpaint/1.2_kde3/]
+
+ * Return and Numpad 5 Key now draw
+ [also in branches/KDE/3.[34]/, branches/kolourpaint/1.2_kde3/]
+
+ * Tool Actions placed outside the Tool Box resize with their toolbars
+ [also in branches/KDE/3.[34]/, branches/kolourpaint/1.2_kde3/]
+
+ * Ensure Color Similarity maximum is 30, not 29 due to gcc4
+ [also in branches/KDE/3.[34]/, branches/kolourpaint/1.2_kde3/]
+
+ * Tool Box traps right clicks (for the RMB Menu) on top of tool options
+ widgets and the empty part of the Tool Box
+ [also in branches/KDE/3.[34]/, branches/kolourpaint/1.2_kde3/]
+
+ * Correct and update image format associations to all formats supported
+ by KDE 3.5 (kdelibs/kimgio/:r466654)
+
+ * String fixes (Stefan Winter)
+ [also in branches/KDE/3.4/]
+
+ * Other string fixes (Malcolm Hunter, Clarence Dang, Stephan Binner)
+
+
+KolourPaint 1.4_light Series (branches/KDE/3.4/)
+============================
+
+KolourPaint 1.4_light (Frozen 2005-02-22)
+ * Antialias text when the text box has a transparent background (Bug #24)
+ [later backported to branches/KDE/3.3/, branches/kolourpaint/1.2_kde3/]
+ * Add Unzoomed Thumbnail Mode and Thumbnail Rectangle
+ * Add RMB context menu for when a selection tool is active (closing KDE
+ Bug #92882)
+ * More intuitive "Set as Image" behaviour (esp. with selection borders).
+ Thanks to Michael Lake for the feedback.
+ [later backported to branches/KDE/3.3/, branches/kolourpaint/1.2_kde3/]
+ * InputMethod support
+ [later backported to branches/kolourpaint/1.2_kde3/]
+ * Save "More Effects" dialog's last effect to config file
+ * Save "Resize / Scale" dialog's last "Keep aspect ratio" setting to
+ config file
+ * Add "Help / Acquiring Screenshots"
+ * Fix selection regressions introduced in 1.2:
+ - Make selection dragging with CTRL work again (copies selection onto
+ document)
+ - When creating freeform selections, include the starting point; also
+ avoids a QRegion crash with constructing 1-point regions
+ [also in branches/KDE/3.3/, branches/kolourpaint/1.2_kde3/]
+ * Fix other selection bugs:
+ - When the user drags very quickly on a resize handle, resize the
+ selection instead of moving it
+ - Draw resize handles above the grid lines - not below - so that the
+ handles are always visible if they are supposed to be there
+ [also in branches/KDE/3.3/, branches/kolourpaint/1.2_kde3/]
+ * Smaller selection and text box resize handles (visually not
+ actually) - covers up fewer selected pixels, doesn't cover up text
+ [also in branches/KDE/3.3/, branches/kolourpaint/1.2_kde3/]
+ * Restore mouse cursor after deselecting selection/text tools
+ [also in branches/KDE/3.3/, branches/kolourpaint/1.2_kde3/]
+ * Empty text clipboard fixes:
+ - Don't get stuck on a wait cursor after attempting to paste empty
+ text into a text box
+ - Prevent pasting text from creating a new text box if text is empty
+ - Prevent copying of empty text box
+ [also in branches/KDE/3.3/, branches/kolourpaint/1.2_kde3/]
+ * Speed up renderer (most noticeable with diagonal drag-scrolling at
+ high zoom)
+ - Don't paint anything outside of the view's visible region
+ (previously, clipped only on view _widget_ region)
+ - Region-aware: paint component rectangles of the update region,
+ rather than the bounding rectangle
+ [also in branches/KDE/3.3/, branches/kolourpaint/1.2_kde3/]
+ * When changing between colour depth and quality widgets in the save
+ filedialog, make sure "Convert to:" and "Quality:" are correctly
+ rendered (hacking around a Qt redraw glitch)
+ [also in branches/KDE/3.3/, branches/kolourpaint/1.2_kde3/]
+ * Fix crash after using the Colour Picker if it was the first used tool
+ [kolourpaint-1.2.2_kde3-color_picker_crash.diff]
+ [also in branches/KDE/3.3/, branches/kolourpaint/1.2_kde3/]
+ * Fix crash due to text box when scaling image behind it
+ [also in branches/KDE/3.3/, branches/kolourpaint/1.2_kde3/]
+ * Even when the thumbnail has focus (and not the main window), blink the
+ text cursor in all views
+ [kolourpaint-1.2.2_kde3-thumbnail_blink_text_cursor.diff]
+ [also in branches/KDE/3.3/, branches/kolourpaint/1.2_kde3/]
+ * Correct "Soften" and "Sharpen" commands' command history names
+ * Correct invert commands' command history names
+ * Fix remaining untranslatable strings (closing KDE Bug #85785)
+ [also in branches/KDE/3.3/, branches/kolourpaint/1.2_kde3/]
+ * Update image format associations to all formats supported by KDE 3.4
+ * Remove unused images in doc directory
+ [also in branches/KDE/3.3/, branches/kolourpaint/1.2_kde3/]
+ * Correct kolourpaint.desktop "Terminal=" and "Categories=" syntax
+ (Benjamin Meyer)
+
+
+KolourPaint 1.2 Series (branches/KDE/3.3/)
+======================
+
+Version 1.2 "ByFiat Everytime" (2004-08-18)
+ * Add up to 500 levels of Undo/Redo (minimum of 10 levels, maximum of
+ 500 as long as the total history size < 16MB)
+ * Add freehand resizing of image
+ * Add freehand smooth scaling of selections
+ * [also in 1.0 branch] New icons (Kristof Borrey)
+ * [also in 1.0 branch] Prefer Crystal SVG text icons over KolourPaint's
+ * [also in 1.0 branch] Add documentation in the KDE Help Centre
+ * Add drag scrolling
+ * Add "More Effects" dialog:
+ - Balance (Brightness, Contrast, Gamma)
+ - Emboss
+ - Flatten
+ - Invert (with choice of channels)
+ - Reduce Colours
+ - Soften & Sharpen
+ * File saving improvements:
+ - Support colour depths (optional dithering) and "colour monochrome"
+ - Support JPEG quality
+ - Realtime file dialog preview with estimated file size
+ - Retain PNG metadata
+ - Prompt when attempting lossy save
+ - Correctly save transparent selections (not as opaque)
+ * Dither more often when loading (and pasting) images for better quality
+ * Single key shortcuts for all tools and tool options (automatically
+ turned off when editing text but can then use Alt+Shift+<key>)
+ * Arrow keys now move one document pixel - not view pixel - at a time
+ (more usable when zoomed in)
+ * Fix selection bugs:
+ - Fix duplicate "Selection: Create" undo entries (Bug #5a)
+ - Allow redoing of selection operation if border deselected (Bug #5b)
+ - Don't print to STDERR when undoing a selection border create
+ operation and border has already been deselected
+ - [also in 1.0 branch] When pulling a selection from the document,
+ only set the bits of the document to the background colour where the
+ transparent selection is opaque in the same place (this is only
+ noticeable with colour similarity turned on). Now moving a
+ selection away and then back to its original place is always a NOP
+ as it should be.
+ * Selections can be deselected using Esc or clicking on icon in Tool Box
+ * Accidental drag detection when deselecting selections or text boxes
+ * Prevent selection from being moved completely offscreen (at least 1
+ pixel of the selection will stay within the view)
+ * Speed up copying selection when transparency is on
+ * Improve Text Tool usability:
+ - Allow single click creation of text box with a sane default size
+ - Allow freehand resizing of text boxes
+ - Add Opaque/Transparent selector for greater usability and
+ consistency with selections
+ - Minimum size is now 7x7 document pixels (1x1 - not 4x4 - border)
+ - Text cursor doesn't overlap border anymore
+ - When dropping text, paste at drop point
+ - When MMB pasting creates a new text box, do so at mouse position
+ * When MMB pasting text in an existing box, correctly paste multiline
+ clipboard contents
+ * Improve text quality:
+ - With a transparent background, don't antialias foreground opaque
+ text with arbitrarily chosen black
+ - Make sure transparent text shows up on opaque (usually, grey was
+ problematic) background
+ * Improve Resize/Scale dialog usability:
+ - Add Smooth Scale (useful for creating screenshot thumbnails)
+ - Allow manipulating image when selection is active
+ - Operation choices stand out as massive, easily clickable buttons
+ - Default focus on operation choices
+ * Warn if Resize/Scale, Rotate or Skew will take lots of memory
+ * Limit startup image size to 2048x2048
+ * Eliminate flicker when scrolling
+ * Thumbnail fixes:
+ - Reduce flicker when appearing (Bug #2)
+ - More reasonable minimum size (actually enforce it)
+ - [also in 1.0 branch] Use deleteLater()
+ - [also in 1.0 branch] Save geometry even if it's closed very quickly
+ after a geometry change
+ * Restore last used tool and tool options on startup
+ * Add Export, Copy To File, Paste From File, Paste in New Window,
+ Full Screen Mode
+ * Add Zoom In/Out buttons to main toolbar
+ * Rename Crop options in an attempt to reduce confusion:
+ - "Autocrop" --> "Remove Internal Border" when selection active
+ - "Crop Outside Selection" --> "Set as Image (Crop)"
+ * "Set as Image" changes:
+ - Enable for text boxes
+ - Underneath transparent bits of selection, fill image with
+ transparent rather than with background colour
+ * Permit "reloading" of an empty document
+ * Fixes when the current URL doesn't exist:
+ - Don't reload if underlying file disappeared
+ - Don't add non-existent file to Recent Files history
+ - Ask to save before mailing or setting as wallpaper
+ * Only enable Show Path when there is a URL
+ * Pop up dialog (instead of printing to STDERR) and disable Edit/Paste
+ on CTRL+V if the clipboard contents disappeared due to the source
+ application quitting (and Klipper didn't retain clipboard contents)
+ * Image/Clear now always sets _everything_ within the selection boundary
+ to the background colour - including transparent pixels
+ * Add Preview button to Colour Similarity Dialog to work around Bug #4
+ regarding spinboxes and enter key
+ * Colour Picker disallows trying to pick colour outside of image
+ * Make sure colour palette contains valid and visible colours at 8-bit
+ * [also in 1.0 branch] Fix (big) memory leak on kpSelection destruction
+ (Albert Astals Cid)
+ * Don't leak image dialogs' memory
+ * [also in 1.0 branch] Don't let C++ destruct the mask bitmap before its
+ painter when dbl-clicking the color eraser does NOP (avoids
+ QPaintDevice and X error)
+ * [also in 1.0 branch] Check for QImageDrag::canDecode() before calling
+ QImageDrag::decode() (prevents X and valgrind errors)
+ * [also in 1.0 branch] Fix compilation problem with QT_NO_ASCII_CAST
+ (Waldo Bastian)
+ * [also in 1.0 branch] Decrease application preference to below that of
+ a viewer (Stephan Kulow)
+ * Remember dialog dimensions
+ * Remove double dialog margins
+ * Fix missing i18n()'s
+ * Fix some untranslatable strings
+ * [also in 1.0 branch] Corrected several strings
+ * Remove unused icons
+
+
+KolourPaint 1.0 Series (branches/kolourpaint/1.0/)
+======================
+
+Version 1.0 "Seagull" (2004-02-29)
+ * First stable release
+
diff --git a/kolourpaint/README b/kolourpaint/README
new file mode 100644
index 00000000..b045e3b9
--- /dev/null
+++ b/kolourpaint/README
@@ -0,0 +1,102 @@
+
+KolourPaint Version 1.4.9_relight (KDE 3.5.9 Release Frozen ???)
+http://www.kolourpaint.org/
+
+Copyright (c) 2003,2004,2005,2006 Clarence Dang <dang@kde.org>
+
+
+For licensing and warranty information, read COPYING.
+For known problems with this release of KolourPaint, read BUGS.
+For what changes have been made, read NEWS.
+For developer information, checkout branches/kolourpaint/control/.
+For general information, read this file (README):
+
+
+1. What is KolourPaint?
+=======================
+
+KolourPaint is a free, easy-to-use paint program for KDE.
+
+It aims to be conceptually simple to understand; providing a level of
+functionality targeted towards the average user. It's designed for daily
+tasks like:
+
+* Painting - drawing diagrams and "finger painting"
+* Image Manipulation - editing screenshots and photos; applying effects
+* Icon Editing - drawing clipart and logos with transparency
+
+It's not an unusable and monolithic program where simple tasks like drawing
+lines become near impossible. Nor is it so simple that it lacks essential
+features like Undo/Redo.
+
+KolourPaint is opensource software written in C++ using the Qt and KDE
+libraries.
+
+
+2. Features
+===========
+
+* Undo/Redo Support (10-500 levels of history depending on memory usage)
+
+* Tools (single key shortcuts available for all tools)
+ - Brush, Color Eraser, Color Picker, Connected Lines a.k.a. Polyline
+ - Curve, Ellipse, Eraser, Flood Fill, Line, Pen, Polygon, Rectangle
+ - Rounded Rectangle, Spraycan, Text
+
+* Selections (fully undo- and redo-able)
+ - Rectangular, Elliptical, Free-Form shapes
+ - Choice between Opaque and Transparent selections
+ - Full Clipboard/Edit Menu support
+ - Freehand resizeable
+
+* Colour Similarity means that you can fill regions in dithered images and
+ photos
+
+* Transparency
+ - Draw transparent icons and logos on a checkerboard background
+ - All tools can draw in the "Transparent Colour"
+
+* Image Effects
+ - Autocrop / Remove Internal Border
+ - Balance (Brightness, Contrast, Gamma)
+ - Clear, Emboss, Flatten, Flip, Invert (with choice of channels)
+ - Reduce Colours, Reduce to Greyscale, Resize, Rotate
+ - Scale, Set as Image (Crop), Skew, Smooth Scale, Soften & Sharpen
+
+* Close-up Editing
+ - Zoom (from 0.01x to 16x)
+ - Grid
+ - Thumbnail
+
+* File Operations
+ - Open/Save in all file formats provided by KImageIO
+ (PNG, JPEG, BMP, ICO, PCX, TIFF,...) with preview
+ - Print, Print Preview
+ - Mail
+ - Set as Wallpaper
+
+
+3. Updates & More Information
+=============================
+
+Visit: http://www.kolourpaint.org/
+
+
+4. Support
+==========
+
+Visit: http://www.kolourpaint.org/
+
+If you have any questions about compiling, installing or using KolourPaint,
+don't be afraid to contact us. We try to support all versions of
+KolourPaint and even issues with 3rd party binary packages.
+
+
+5. Feedback
+===========
+
+Please send bug reports and feature requests to http://bugs.kde.org/.
+Don't hesitate to report bugs nor hesitate to send us your wishes - it
+provides valuable feedback that will help to improve future versions of
+KolourPaint and you will not receive flames for reporting duplicates.
+
diff --git a/kolourpaint/VERSION b/kolourpaint/VERSION
new file mode 100644
index 00000000..8b2f0f9b
--- /dev/null
+++ b/kolourpaint/VERSION
@@ -0,0 +1 @@
+1.4.8_relight-post
diff --git a/kolourpaint/cursors/Makefile.am b/kolourpaint/cursors/Makefile.am
new file mode 100644
index 00000000..5ae0504a
--- /dev/null
+++ b/kolourpaint/cursors/Makefile.am
@@ -0,0 +1,11 @@
+INCLUDES = -I$(srcdir)/.. -I$(srcdir)/../cursors -I$(srcdir)/../interfaces \
+ -I$(srcdir)/../pixmapfx \
+ -I$(srcdir)/../tools \
+ -I$(srcdir)/../views \
+ -I$(srcdir)/../widgets $(all_includes)
+
+noinst_LTLIBRARIES = libkolourpaintcursors.la
+libkolourpaintcursors_la_SOURCES = kpcursorlightcross.cpp kpcursorprovider.cpp
+
+METASOURCES = AUTO
+
diff --git a/kolourpaint/cursors/kpcursorlightcross.cpp b/kolourpaint/cursors/kpcursorlightcross.cpp
new file mode 100644
index 00000000..0595d320
--- /dev/null
+++ b/kolourpaint/cursors/kpcursorlightcross.cpp
@@ -0,0 +1,128 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG_KP_CURSOR_LIGHT_CROSS 0
+
+
+#include <kpcursorlightcross.h>
+
+#include <qbitmap.h>
+#include <qcursor.h>
+
+#include <kdebug.h>
+
+
+enum PixelValue
+{
+ White, Black, Transparent
+};
+
+static void setPixel (unsigned char *colorBitmap,
+ unsigned char *maskBitmap,
+ int width,
+ int y, int x, enum PixelValue pv)
+{
+ const int ColorBlack = 1;
+ const int ColorWhite = 0;
+
+ const int MaskOpaque = 1;
+ const int MaskTransparent = 0;
+
+ int colorValue, maskValue;
+
+ switch (pv)
+ {
+ case White:
+ colorValue = ColorWhite;
+ maskValue = MaskOpaque;
+ break;
+
+ case Black:
+ colorValue = ColorBlack;
+ maskValue = MaskOpaque;
+ break;
+
+ case Transparent:
+ default:
+ colorValue = ColorWhite;
+ maskValue = MaskTransparent;
+ break;
+ }
+
+ if (colorValue)
+ colorBitmap [y * (width / 8) + (x / 8)] |= (1 << (x % 8));
+
+ if (maskValue)
+ maskBitmap [y * (width / 8) + (x / 8)] |= (1 << (x % 8));
+}
+
+
+const QCursor *kpMakeCursorLightCross ()
+{
+#if DEBUG_KP_CURSOR_LIGHT_CROSS
+ kdDebug () << "kpMakeCursorLightCross() " << endl;
+#endif
+
+ const int side = 24;
+ const int byteSize = (side * side) / 8;
+ unsigned char *colorBitmap = new unsigned char [byteSize];
+ unsigned char *maskBitmap = new unsigned char [byteSize];
+
+ memset (colorBitmap, 0, byteSize);
+ memset (maskBitmap, 0, byteSize);
+
+ const int oddSide = side - 1;
+ const int strokeLen = oddSide * 3 / 8;
+
+ for (int i = 0; i < strokeLen; i++)
+ {
+ const enum PixelValue pv = (i % 2) ? Black : White;
+
+ #define X_(val) (val)
+ #define Y_(val) (val)
+ #define DRAW(y,x) setPixel (colorBitmap, maskBitmap, side, (y), (x), pv)
+ // horizontal
+ DRAW (Y_(side / 2), X_(1 + i));
+ DRAW (Y_(side / 2), X_(side - 1 - i));
+
+ // vertical
+ DRAW (Y_(1 + i), X_(side / 2));
+ DRAW (Y_(side - 1 - i), X_(side / 2));
+ #undef DRAW
+ #undef Y_
+ #undef X_
+ }
+
+ QCursor *cursor = new QCursor (QBitmap (side, side, colorBitmap, true/*little endian bit order*/),
+ QBitmap (side, side, maskBitmap, true/*little endian bit order*/));
+
+ delete [] maskBitmap;
+ delete [] colorBitmap;
+
+ return cursor;
+}
+
diff --git a/kolourpaint/cursors/kpcursorlightcross.h b/kolourpaint/cursors/kpcursorlightcross.h
new file mode 100644
index 00000000..d2bf83e1
--- /dev/null
+++ b/kolourpaint/cursors/kpcursorlightcross.h
@@ -0,0 +1,38 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kp_cursor_light_cross_h__
+#define __kp_cursor_light_cross_h__
+
+
+class QCursor;
+
+const QCursor *kpMakeCursorLightCross ();
+
+
+#endif // __kp_cursor_light_cross_h__
diff --git a/kolourpaint/cursors/kpcursorprovider.cpp b/kolourpaint/cursors/kpcursorprovider.cpp
new file mode 100644
index 00000000..45d43801
--- /dev/null
+++ b/kolourpaint/cursors/kpcursorprovider.cpp
@@ -0,0 +1,49 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <kpcursorprovider.h>
+
+#include <qcursor.h>
+
+#include <kstaticdeleter.h>
+
+#include <kpcursorlightcross.h>
+
+
+static const QCursor *theLightCursor = 0;
+
+
+// public static
+QCursor kpCursorProvider::lightCross ()
+{
+ // TODO: don't leak (although it's cleaned up on exit by OS anyway)
+ if (!theLightCursor)
+ theLightCursor = kpMakeCursorLightCross ();
+
+ return *theLightCursor;
+}
diff --git a/kolourpaint/cursors/kpcursorprovider.h b/kolourpaint/cursors/kpcursorprovider.h
new file mode 100644
index 00000000..191b4f78
--- /dev/null
+++ b/kolourpaint/cursors/kpcursorprovider.h
@@ -0,0 +1,43 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kp_cursor_provider_h__
+#define __kp_cursor_provider_h__
+
+
+class QCursor;
+
+
+class kpCursorProvider
+{
+public:
+ static QCursor lightCross ();
+};
+
+
+#endif // __kp_cursor_provider_h__
diff --git a/kolourpaint/kolourpaint.cpp b/kolourpaint/kolourpaint.cpp
new file mode 100644
index 00000000..b9ba0f0c
--- /dev/null
+++ b/kolourpaint/kolourpaint.cpp
@@ -0,0 +1,229 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <qfile.h>
+
+#include <dcopclient.h>
+#include <kaboutdata.h>
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <kdebug.h>
+#include <kimageio.h>
+#include <klocale.h>
+
+// for srand
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <kpdefs.h>
+#include <kpmainwindow.h>
+
+#include <kolourpaintlicense.h>
+#include <kolourpaintversion.h>
+
+
+static const KCmdLineOptions cmdLineOptions [] =
+{
+ {"+[file]", I18N_NOOP ("Image file to open"), 0},
+ KCmdLineLastOption
+};
+
+
+int main (int argc, char *argv [])
+{
+ KAboutData aboutData
+ (
+ "kolourpaint",
+ I18N_NOOP ("KolourPaint"),
+ kpVersionText,
+ I18N_NOOP ("Paint Program for KDE"),
+ KAboutData::License_Custom,
+ 0/*copyright statement - see licence instead*/,
+ 0/*no free text*/,
+ "http://www.kolourpaint.org/"
+ );
+
+
+ // this is _not_ the same as KAboutData::License_BSD
+ aboutData.setLicenseText (kpLicenseText);
+
+
+ // SYNC: with AUTHORS
+
+ aboutData.addAuthor ("Clarence Dang", I18N_NOOP ("Maintainer"), "dang@kde.org");
+ aboutData.addAuthor ("Thurston Dang", I18N_NOOP ("Chief Investigator"),
+ "thurston_dang@users.sourceforge.net");
+ aboutData.addAuthor ("Kristof Borrey", I18N_NOOP ("Icons"), "borrey@kde.org");
+ aboutData.addAuthor ("Kazuki Ohta", I18N_NOOP ("InputMethod Support"), "mover@hct.zaq.ne.jp");
+ aboutData.addAuthor ("Nuno Pinheiro", I18N_NOOP ("Icons"), "nf.pinheiro@gmail.com");
+ aboutData.addAuthor ("Danny Allen", I18N_NOOP ("Icons"), "dannya40uk@yahoo.co.uk");
+ aboutData.addAuthor ("Martin Koller",
+ 0/*STRING: string freeze prevents us from writing: Scanning Support*/,
+ "m.koller@surfeu.at");
+
+
+ aboutData.addCredit ("Rashid N. Achilov");
+ aboutData.addCredit ("Toyohiro Asukai");
+ aboutData.addCredit ("Bela-Andreas Bargel");
+ aboutData.addCredit ("Waldo Bastian");
+ aboutData.addCredit ("Ismail Belhachmi");
+ aboutData.addCredit ("Sashmit Bhaduri");
+ aboutData.addCredit ("Antonio Bianco");
+ aboutData.addCredit ("Stephan Binner");
+ aboutData.addCredit ("Markus Brueffer");
+ aboutData.addCredit ("Rob Buis");
+ aboutData.addCredit ("Lucijan Busch");
+ aboutData.addCredit ("Mikhail Capone");
+ aboutData.addCredit ("Enrico Ceppi");
+ aboutData.addCredit ("Tom Chance");
+ aboutData.addCredit ("Albert Astals Cid");
+ aboutData.addCredit ("Jennifer Dang");
+ aboutData.addCredit ("Lawrence Dang");
+ aboutData.addCredit ("Christoph Eckert");
+ aboutData.addCredit ("David Faure");
+ aboutData.addCredit ("P. Fisher");
+ aboutData.addCredit ("Nicolas Goutte");
+ aboutData.addCredit ("Herbert Graeber");
+ aboutData.addCredit ("Brad Grant");
+ aboutData.addCredit ("David Greenaway");
+ aboutData.addCredit ("Wilco Greven");
+ aboutData.addCredit ("Hubert Grininger");
+ aboutData.addCredit ("Adriaan de Groot");
+ aboutData.addCredit ("Esben Mose Hansen");
+ aboutData.addCredit ("Nadeem Hasan");
+ aboutData.addCredit ("Simon Hausmann");
+ aboutData.addCredit ("Michael Hoehne");
+ aboutData.addCredit ("Andrew J");
+ aboutData.addCredit ("Werner Joss");
+ aboutData.addCredit ("Derek Kite");
+ aboutData.addCredit ("Tobias Koenig");
+ aboutData.addCredit ("Dmitry Kolesnikov");
+ aboutData.addCredit ("Stephan Kulow");
+ aboutData.addCredit ("Eric Laffoon");
+ aboutData.addCredit ("Michael Lake");
+ aboutData.addCredit ("Sebastien Laout");
+ aboutData.addCredit ("David Ling");
+ aboutData.addCredit ("Volker Lochte");
+ aboutData.addCredit ("Anders Lund");
+ aboutData.addCredit ("Jacek Masiulaniec");
+ aboutData.addCredit ("Benjamin Meyer");
+ aboutData.addCredit ("Amir Michail");
+ aboutData.addCredit ("Robert Moszczynski");
+ aboutData.addCredit ("Dirk Mueller");
+ aboutData.addCredit ("Ruivaldo Neto");
+ aboutData.addCredit ("Ralf Nolden");
+ aboutData.addCredit ("Steven Pasternak");
+ aboutData.addCredit ("Cédric Pasteur");
+ aboutData.addCredit ("Erik K. Pedersen");
+ aboutData.addCredit ("Dennis Pennekamp");
+ aboutData.addCredit ("Jos Poortvliet");
+ aboutData.addCredit ("Boudewijn Rempt");
+ aboutData.addCredit ("Marcos Rodriguez");
+ aboutData.addCredit ("Matt Rogers");
+ aboutData.addCredit ("Francisco Jose Canizares Santofimia");
+ aboutData.addCredit ("Bram Schoenmakers");
+ aboutData.addCredit ("Dirk Schönberger");
+ aboutData.addCredit ("Lutz Schweizer");
+ aboutData.addCredit ("Emmeran Seehuber");
+ aboutData.addCredit ("Peter Simonsson");
+ aboutData.addCredit ("Andrew Simpson");
+ aboutData.addCredit ("A T Somers");
+ aboutData.addCredit ("Igor Stepin");
+ aboutData.addCredit ("Stephen Sweeney");
+ aboutData.addCredit ("Bart Symons");
+ aboutData.addCredit ("Stefan Taferner");
+ aboutData.addCredit ("Hogne Titlestad");
+ aboutData.addCredit ("Brandon Mark Turner");
+ aboutData.addCredit ("Jonathan Turner");
+ aboutData.addCredit ("Stephan Unknown");
+ aboutData.addCredit ("Dries Verachtert");
+ aboutData.addCredit ("Simon Vermeersch");
+ aboutData.addCredit ("Lauri Watts");
+ aboutData.addCredit ("Mark Wege");
+ aboutData.addCredit ("Christoph Wiesen");
+ aboutData.addCredit ("Andre Wobbeking");
+ aboutData.addCredit ("Luke-Jr");
+ aboutData.addCredit ("Maxim_86ualb2");
+ aboutData.addCredit ("Michele");
+
+
+ KCmdLineArgs::init (argc, argv, &aboutData);
+ KCmdLineArgs::addCmdLineOptions (cmdLineOptions);
+
+ KApplication app;
+
+
+ // mainly for changing wallpaper :)
+ DCOPClient *client = app.dcopClient ();
+ if (!client->attach ())
+ kdError () << "Could not contact DCOP server" << endl;
+
+ // mainly for the Spraycan Tool
+ srand ((unsigned int) (getpid () + getppid ()));
+
+ // access more formats
+ KImageIO::registerFormats ();
+
+
+ // Qt says this is necessary but I don't think it is...
+ QObject::connect (&app, SIGNAL (lastWindowClosed ()),
+ &app, SLOT (quit ()));
+
+
+ if (app.isRestored ())
+ {
+ // Creates a kpMainWindow using the default constructor and then
+ // calls kpMainWindow::readProperties().
+ RESTORE (kpMainWindow)
+ }
+ else
+ {
+ kpMainWindow *mainWindow;
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs ();
+
+ if (args->count () >= 1)
+ {
+ for (int i = 0; i < args->count (); i++)
+ {
+ mainWindow = new kpMainWindow (args->url (i));
+ mainWindow->show ();
+ }
+ }
+ else
+ {
+ mainWindow = new kpMainWindow ();
+ mainWindow->show ();
+ }
+
+ args->clear ();
+ }
+
+
+ return app.exec ();
+}
diff --git a/kolourpaint/kolourpaint.desktop b/kolourpaint/kolourpaint.desktop
new file mode 100644
index 00000000..8a586219
--- /dev/null
+++ b/kolourpaint/kolourpaint.desktop
@@ -0,0 +1,92 @@
+[Desktop Entry]
+
+Name=KolourPaint
+Name[nb]=KPaint
+Name[ne]=रङ पेन्ट
+Name[pa]=ਕੇ-ਰੰਗ-ਪੇਂਟ
+Name[sv]=Kolourpaint
+Name[ta]=நிற பெயின்ட்
+Name[zh_TW]=KolourPaint 小畫家
+GenericName=Paint Program
+GenericName[af]=Verf Program
+GenericName[ar]=برنامج تلوين
+GenericName[bg]=Графичен редактор
+GenericName[br]=Goulev tresañ
+GenericName[bs]=Jednostavni program za crtanje
+GenericName[ca]=Programa de pintura
+GenericName[cs]=Kreslící program
+GenericName[cy]=Rhaglen Peintio
+GenericName[da]=Maleprogram
+GenericName[de]=Mal- und Zeichenprogramm
+GenericName[el]=Πρόγραμμα ζωγραφικής
+GenericName[eo]=Pentrilo
+GenericName[es]=Programa de pintura
+GenericName[et]=Joonistusprogramm
+GenericName[eu]=Marrazteko programa
+GenericName[fa]=برنامۀ رنگ
+GenericName[fi]=Piirto-ohjelma
+GenericName[fr]=Petit programme de dessin
+GenericName[ga]=Clár Péinteála
+GenericName[gl]=Programa de debuxo
+GenericName[he]=תוכנית ציור
+GenericName[hi]=छवि बनाने का प्रोग्राम
+GenericName[hr]=Program za slikanje
+GenericName[hu]=Rajzolóprogram
+GenericName[is]=Teikniforrit
+GenericName[it]=Programma di disegno
+GenericName[ja]=ペイントプログラム
+GenericName[kk]=Сурет салу бағдарламасы
+GenericName[km]=កម្មវិធី​គូរ
+GenericName[lt]=Piešimo programa
+GenericName[lv]=Krāsošanas Programma
+GenericName[ms]=Program Mewarna
+GenericName[mt]=Programm sempliċi tat-tpinġija
+GenericName[nb]=Maleprogram
+GenericName[nds]=Maalprogramm
+GenericName[ne]=पेन्ट कार्यक्रम
+GenericName[nl]=Tekenprogramma
+GenericName[nn]=Måleprogram
+GenericName[nso]=Lenaneo la Paint
+GenericName[pa]=ਰੰਗ ਕਾਰਜ
+GenericName[pl]=Program Paint
+GenericName[pt]=Programa de Pintura
+GenericName[pt_BR]=Programa de Pintura
+GenericName[ro]=Program de desenare
+GenericName[ru]=Графический редактор
+GenericName[rw]=Porogaramu Gusiga irangi
+GenericName[se]=Málenprográmma
+GenericName[sk]=Kreslenie
+GenericName[sl]=Slikarski program
+GenericName[sr]=Програм за сликање
+GenericName[sr@Latn]=Program za slikanje
+GenericName[sv]=Ritprogram
+GenericName[ta]=பெயிண்ட் நிரலி
+GenericName[tg]=Муҳаррири графикӣ
+GenericName[th]=โปรแกรมวาดภาพธรรมดาๆ
+GenericName[tr]=Boyama Programı
+GenericName[uk]=Програма для малювання
+GenericName[uz]=Chizish dasturi
+GenericName[uz@cyrillic]=Чизиш дастури
+GenericName[ven]=Mbekanyamushumo ya Pennde
+GenericName[wa]=Program di dessinaedje
+GenericName[xh]=Udweliso lwenkqubo lwepeyinti
+GenericName[zh_CN]=绘图程序
+GenericName[zh_HK]=繪圖程式
+GenericName[zh_TW]=繪圖程式
+GenericName[zu]=Elila Iprogremu Kapende
+Icon=kolourpaint
+
+Type=Application
+Exec=kolourpaint %u
+DocPath=kolourpaint/index.html
+
+# SYNC: Run branches/kolourpaint/control/scripts/gen_mimetype_line.sh in
+# the version of kdelibs/kimgio/ (e.g. KDE 3.5) KolourPaint is
+# shipped with.
+MimeType=image/fax-g3;image/gif;image/jp2;image/jpeg;image/png;image/tiff;image/x-bmp;image/x-dds;image/x-eps;image/x-exr;image/x-hdr;image/x-ico;image/x-pcx;image/x-portable-bitmap;image/x-portable-greymap;image/x-portable-pixmap;image/x-rgb;image/x-targa;image/x-vnd.adobe.photoshop;image/x-xbm;image/x-xcf-gimp;image/x-xpm;video/x-mng;
+
+Categories=Qt;KDE;Graphics;
+Terminal=false
+X-KDE-StartupNotify=true
+X-DCOP-ServiceType=Multi
+
diff --git a/kolourpaint/kolourpaintui.rc b/kolourpaint/kolourpaintui.rc
new file mode 100644
index 00000000..876c3e38
--- /dev/null
+++ b/kolourpaint/kolourpaintui.rc
@@ -0,0 +1,169 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+<!--
+SYNC: Do not change the number of quotes before the version number
+ - it is parsed by the KolourPaint wrapper shell script (in standalone
+ backport releases of KolourPaint)
+-->
+<gui name="kolourpaint" version="25">
+
+<!--
+SYNC: Check for duplicate actions in menus caused by some of our actions
+ being added to ui_standards.rc. Makes me wonder why we are using
+ merging in the first place.
+-->
+
+<MenuBar>
+ <Menu name="file">
+ <!-- <Action name="file_new_window" append="new_merge" /> -->
+
+ <Action name="file_scan" append="open_merge" />
+
+ <Action name="file_export" append="save_merge" />
+ <Separator append="save_merge" />
+
+ <Action name="file_set_as_wallpaper_centered" />
+ <Action name="file_set_as_wallpaper_tiled" />
+ </Menu>
+
+ <Menu name="edit">
+ <Action name="edit_paste_in_new_window" append="edit_paste_merge" />
+
+ <Action name="edit_copy_to_file" />
+ <Action name="edit_paste_from_file" />
+ </Menu>
+
+ <!-- SRC: ui_standards.rc v10 (KDE 3.3) -->
+ <Menu name="view" noMerge="1"><text>&amp;View</text>
+ <Action name="view_actual_size"/>
+ <Action name="view_fit_to_page"/>
+ <Action name="view_fit_to_width"/>
+ <Action name="view_fit_to_height"/>
+
+ <Separator />
+
+ <!-- <MergeLocal name="view_zoom_merge"/> -->
+ <Action name="view_zoom_in"/>
+
+ <!-- Changed from "view_zoom" to allow custom ordering of zoom
+ actions in "mainToolBar" (which has a hardcoded position for
+ "view_zoom").
+ -->
+ <Action name="view_zoom_to"/>
+
+ <Action name="view_zoom_out"/>
+
+ <WeakSeparator/>
+
+ <Action name="view_redisplay"/>
+
+ <Separator/>
+
+ <!-- <MergeLocal/> -->
+ <Action name="view_show_grid" />
+ <Action name="view_show_thumbnail" />
+
+ <Separator/>
+
+ <Action name="view_zoomed_thumbnail" />
+ <Action name="view_show_thumbnail_rectangle" />
+ </Menu>
+
+ <Menu name="image"><text>&amp;Image</text>
+ <Action name="image_crop" />
+ <Action name="image_auto_crop" />
+ <Separator />
+ <Action name="image_resize_scale" />
+ <Action name="image_flip" />
+ <Action name="image_rotate" />
+ <Action name="image_skew" />
+ <Separator />
+ <Action name="image_convert_to_black_and_white" />
+ <Action name="image_convert_to_grayscale" />
+ <Action name="image_more_effects" />
+ <Separator />
+ <Action name="image_invert_colors" />
+ <Action name="image_clear" />
+ </Menu>
+
+ <Menu name="settings">
+ <Action name="settings_show_path" append="show_merge" />
+ </Menu>
+
+ <Menu name="help">
+ <Action name="help_taking_screenshots" />
+ </Menu>
+
+</MenuBar>
+
+<ToolBar name="mainToolBar">
+ <Action name="view_zoom_in" />
+ <Action name="view_zoom_out" />
+ <Action name="view_zoom_to" />
+</ToolBar>
+
+<ToolBar name="textToolBar" fullWidth="false" position="top" hidden="true"><text>Text Toolbar</text>
+ <Action name="text_font_family" />
+ <Action name="text_font_size" />
+ <Separator />
+ <Action name="text_bold" />
+ <Action name="text_italic" />
+ <Action name="text_underline" />
+ <Action name="text_strike_thru" />
+</ToolBar>
+
+<Menu name="selectionToolRMBMenu"><text>Selection Tool RMB Menu</text>
+ <!-- SRC: ui_standards.rc v10 (KDE 3.3) -->
+ <!-- <Menu name="edit"><text>&amp;Edit</text> -->
+
+ <!-- <Action name="edit_undo"/>
+ <Action name="edit_redo"/>
+ <MergeLocal name="edit_undo_merge"/>
+ <Separator/> -->
+ <Action name="edit_cut"/>
+ <Action name="edit_copy"/>
+ <Action name="edit_paste"/>
+ <!-- CUSTOM --> <!-- <Action name="edit_paste_in_new_window" /> -->
+ <MergeLocal name="edit_paste_merge"/>
+ <Action name="edit_clear"/>
+ <!-- <Separator/> -->
+ <Action name="edit_select_all"/>
+ <!-- <Action name="edit_deselect"/> -->
+ <MergeLocal name="edit_select_merge"/>
+ <Separator/>
+ <Action name="edit_find"/>
+ <Action name="edit_find_next"/>
+ <Action name="edit_find_last"/>
+ <Action name="edit_replace"/>
+ <MergeLocal name="edit_find_merge"/>
+ <Separator/>
+ <!-- CUSTOM --> <Action name="edit_copy_to_file" />
+ <!-- CUSTOM --> <Action name="edit_paste_from_file" />
+ <MergeLocal/>
+
+ <!-- </Menu> -->
+
+
+ <Separator/>
+
+
+ <!-- <Menu name="image"><text>&amp;Image</text> -->
+
+ <Action name="image_crop" />
+ <!-- <Action name="image_auto_crop" /> -->
+ <Separator />
+ <Action name="image_resize_scale" />
+ <Action name="image_flip" />
+ <Action name="image_rotate" />
+ <Action name="image_skew" />
+ <Separator />
+ <!-- <Action name="image_convert_to_black_and_white" /> -->
+ <!-- <Action name="image_convert_to_grayscale" /> -->
+ <!-- <Action name="image_more_effects" /> -->
+ <!-- <Separator /> -->
+ <Action name="image_invert_colors" />
+ <!-- <Action name="image_clear" /> -->
+
+ <!-- </Menu> -->
+</Menu>
+
+</gui>
diff --git a/kolourpaint/kpcolor.cpp b/kolourpaint/kpcolor.cpp
new file mode 100644
index 00000000..a9dc000b
--- /dev/null
+++ b/kolourpaint/kpcolor.cpp
@@ -0,0 +1,360 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define DEBUG_KP_COLOR 0
+
+
+#include <kpcolor.h>
+
+#include <qdatastream.h>
+
+#include <kdebug.h>
+
+
+// public static
+const int kpColor::Exact = 0;
+
+// public static
+const kpColor kpColor::invalid; // TODO: what's wrong with explicitly specifying () constructor?
+const kpColor kpColor::transparent (0, 0, 0, true/*isTransparent*/);
+
+
+kpColor::kpColor ()
+ : m_rgbaIsValid (false),
+ m_colorCacheIsValid (false)
+{
+}
+
+kpColor::kpColor (int red, int green, int blue, bool isTransparent)
+ : m_colorCacheIsValid (false)
+{
+ if (red < 0 || red > 255 ||
+ green < 0 || green > 255 ||
+ blue < 0 || blue > 255)
+ {
+ kdError () << "kpColor::<ctor>(r=" << red
+ << ",g=" << green
+ << ",b=" << blue
+ << ",t=" << isTransparent
+ << ") passed out of range values" << endl;
+ m_rgbaIsValid = false;
+ return;
+ }
+
+ m_rgba = qRgba (red, green, blue, isTransparent ? 0 : 255/*opaque*/);
+ m_rgbaIsValid = true;
+}
+
+kpColor::kpColor (const QRgb &rgba)
+ : m_colorCacheIsValid (false)
+{
+ if (qAlpha (rgba) > 0 && qAlpha (rgba) < 255)
+ {
+ kdError () << "kpColor::<ctor>(QRgb) passed translucent alpha "
+ << qAlpha (rgba)
+ << " - trying to recover"
+ << endl;
+
+ // Forget the alpha channel - make it opaque
+ m_rgba = qRgb (qRed (m_rgba), qGreen (m_rgba), qBlue (m_rgba));
+ m_rgbaIsValid = true;
+ }
+ else
+ {
+ m_rgba = rgba;
+ m_rgbaIsValid = true;
+ }
+}
+
+kpColor::kpColor (const kpColor &rhs)
+ : m_rgbaIsValid (rhs.m_rgbaIsValid),
+ m_rgba (rhs.m_rgba),
+ m_colorCacheIsValid (rhs.m_colorCacheIsValid),
+ m_colorCache (rhs.m_colorCache)
+{
+}
+
+// friend
+QDataStream &operator<< (QDataStream &stream, const kpColor &color)
+{
+ stream << int (color.m_rgbaIsValid) << int (color.m_rgba);
+
+ return stream;
+}
+
+// friend
+QDataStream &operator>> (QDataStream &stream, kpColor &color)
+{
+ int a, b;
+ stream >> a >> b;
+ color.m_rgbaIsValid = a;
+ color.m_rgba = b;
+
+ color.m_colorCacheIsValid = false;
+
+ return stream;
+}
+
+kpColor &kpColor::operator= (const kpColor &rhs)
+{
+ // (as soon as you add a ptr, you won't be complaining to me that this
+ // method was unnecessary :))
+
+ if (this == &rhs)
+ return *this;
+
+ m_rgbaIsValid = rhs.m_rgbaIsValid;
+ m_rgba = rhs.m_rgba;
+ m_colorCacheIsValid = rhs.m_colorCacheIsValid;
+ m_colorCache = rhs.m_colorCache;
+
+ return *this;
+}
+
+bool kpColor::operator== (const kpColor &rhs) const
+{
+ return isSimilarTo (rhs, kpColor::Exact);
+}
+
+bool kpColor::operator!= (const kpColor &rhs) const
+{
+ return !(*this == rhs);
+}
+
+
+template <class dtype>
+inline dtype square (dtype val)
+{
+ return val * val;
+}
+
+// public static
+int kpColor::processSimilarity (double colorSimilarity)
+{
+ // sqrt (dr ^ 2 + dg ^ 2 + db ^ 2) <= colorSimilarity * sqrt (255 ^ 2 * 3)
+ // dr ^ 2 + dg ^ 2 + db ^ 2 <= (colorSimilarity ^ 2) * (255 ^ 2 * 3)
+
+ return int (square (colorSimilarity) * (square (255) * 3));
+}
+
+bool kpColor::isSimilarTo (const kpColor &rhs, int processedSimilarity) const
+{
+ // Are we the same?
+ if (this == &rhs)
+ return true;
+
+
+ // Do we dither in terms of validity?
+ if (isValid () != rhs.isValid ())
+ return false;
+
+ // Are both of us invalid?
+ if (!isValid ())
+ return true;
+
+ // --- both are now valid ---
+
+
+ if (isTransparent () != rhs.isTransparent ())
+ return false;
+
+ // Are both of us transparent?
+ if (isTransparent ())
+ return true;
+
+ // --- both are now valid and opaque ---
+
+
+ if (m_rgba == rhs.m_rgba)
+ return true;
+
+
+ if (processedSimilarity == kpColor::Exact)
+ return false;
+ else
+ {
+ return (square (qRed (m_rgba) - qRed (rhs.m_rgba)) +
+ square (qGreen (m_rgba) - qGreen (rhs.m_rgba)) +
+ square (qBlue (m_rgba) - qBlue (rhs.m_rgba))
+ <= processedSimilarity);
+ }
+}
+
+kpColor::~kpColor ()
+{
+}
+
+
+// public
+bool kpColor::isValid () const
+{
+ return m_rgbaIsValid;
+}
+
+
+// public
+int kpColor::red () const
+{
+ if (!m_rgbaIsValid)
+ {
+ kdError () << "kpColor::red() called with invalid kpColor" << endl;
+ return 0;
+ }
+
+ if (isTransparent ())
+ {
+ kdError () << "kpColor::red() called with transparent kpColor" << endl;
+ return 0;
+ }
+
+ return qRed (m_rgba);
+}
+
+// public
+int kpColor::green () const
+{
+ if (!m_rgbaIsValid)
+ {
+ kdError () << "kpColor::green() called with invalid kpColor" << endl;
+ return 0;
+ }
+
+ if (isTransparent ())
+ {
+ kdError () << "kpColor::green() called with transparent kpColor" << endl;
+ return 0;
+ }
+
+ return qGreen (m_rgba);
+}
+
+// public
+int kpColor::blue () const
+{
+ if (!m_rgbaIsValid)
+ {
+ kdError () << "kpColor::blue() called with invalid kpColor" << endl;
+ return 0;
+ }
+
+ if (isTransparent ())
+ {
+ kdError () << "kpColor::blue() called with transparent kpColor" << endl;
+ return 0;
+ }
+
+ return qBlue (m_rgba);
+}
+
+// public
+int kpColor::alpha () const
+{
+ if (!m_rgbaIsValid)
+ {
+ kdError () << "kpColor::alpha() called with invalid kpColor" << endl;
+ return 0;
+ }
+
+ const int alpha = qAlpha (m_rgba);
+
+ if (alpha > 0 && alpha < 255)
+ {
+ kdError () << "kpColor::alpha() called with translucent kpColor alpha=" << alpha << endl;
+
+ // no translucency
+ return alpha ? 255 : 0;
+ }
+ else
+ {
+ return alpha;
+ }
+}
+
+// public
+bool kpColor::isTransparent () const
+{
+ return (alpha () == 0);
+}
+
+// public
+bool kpColor::isOpaque () const
+{
+ return (alpha () == 255);
+}
+
+
+// public
+QRgb kpColor::toQRgb () const
+{
+ if (!m_rgbaIsValid)
+ {
+ kdError () << "kpColor::toQRgb() called with invalid kpColor" << endl;
+ return 0;
+ }
+
+ return m_rgba;
+}
+
+// public
+const QColor &kpColor::toQColor () const
+{
+ if (!m_rgbaIsValid)
+ {
+ kdError () << "kpColor::toQColor() called with invalid kpColor" << endl;
+ return Qt::black;
+ }
+
+ if (m_colorCacheIsValid)
+ return m_colorCache;
+
+ if (qAlpha (m_rgba) < 255)
+ {
+ kdError () << "kpColor::toQColor() called with not fully opaque kpColor alpha="
+ << qAlpha (m_rgba)
+ << endl;
+ return Qt::black;
+ }
+
+ m_colorCache = QColor (m_rgba);
+ if (!m_colorCache.isValid ())
+ {
+ kdError () << "kpColor::toQColor () internal error - could not return valid QColor"
+ << endl;
+ return Qt::black;
+ }
+
+ m_colorCacheIsValid = true;
+
+ return m_colorCache;
+}
+
+// public
+QColor kpColor::maskColor () const
+{
+ return isTransparent () ? Qt::color0 : Qt::color1;
+}
diff --git a/kolourpaint/kpcolor.h b/kolourpaint/kpcolor.h
new file mode 100644
index 00000000..131d4b61
--- /dev/null
+++ b/kolourpaint/kpcolor.h
@@ -0,0 +1,101 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kp_color_h__
+#define __kp_color_h__
+
+
+#include <qcolor.h>
+
+class QDataStream;
+
+
+//
+// kpColor is an object-oriented abstraction of QRgb, with the additional
+// restriction of following the KolourPaint convention of only supporting
+// totally transparent and totally opaque colors. It also provides better
+// error handling, reporting (noisy kdError()'s) and recovery.
+//
+// In general, you should pass around kpColor objects instead of QRgb
+// and QColor. Only convert an opaque kpColor to a QColor (using toQColor())
+// if you need to draw something onscreen. Constructing a kpColor object
+// from QColor is probably wrong since onscreen representations of color
+// are not guaranteed to be faithful (due to QColor color allocation).
+//
+class kpColor
+{
+public:
+ kpColor ();
+ kpColor (int red, int green, int blue, bool isTransparent = false);
+ kpColor (const QRgb &rgba);
+ kpColor (const kpColor &rhs);
+ friend QDataStream &operator<< (QDataStream &stream, const kpColor &color);
+ friend QDataStream &operator>> (QDataStream &stream, kpColor &color);
+ kpColor &operator= (const kpColor &rhs);
+ bool operator== (const kpColor &rhs) const;
+ bool operator!= (const kpColor &rhs) const;
+
+ static int processSimilarity (double colorSimilarity);
+ static const int Exact; // "isSimilarTo (rhs, kpColor::Exact)" == "== rhs"
+ // Usage: isSimilarTo (rhs, kpColor::processSimilarity (.1)) checks for
+ // Color Similarity within 10%
+ bool isSimilarTo (const kpColor &rhs, int processedSimilarity) const;
+ ~kpColor ();
+
+ static const kpColor invalid;
+ static const kpColor transparent;
+
+ bool isValid () const;
+
+ int red () const;
+ int green () const;
+ int blue () const;
+ int alpha () const;
+ bool isTransparent () const;
+ bool isOpaque () const;
+
+ // Cast operators will most likely result in careless conversions so
+ // use explicit functions instead:
+ QRgb toQRgb () const;
+
+ // (only valid if isOpaque())
+ // (const QColor & return results in fewer color reallocations)
+ const QColor &toQColor () const;
+
+ QColor maskColor () const;
+
+private:
+ bool m_rgbaIsValid;
+ QRgb m_rgba;
+
+ mutable bool m_colorCacheIsValid;
+ mutable QColor m_colorCache;
+};
+
+
+#endif // __kp_color_h__
diff --git a/kolourpaint/kpcommandhistory.cpp b/kolourpaint/kpcommandhistory.cpp
new file mode 100644
index 00000000..33010918
--- /dev/null
+++ b/kolourpaint/kpcommandhistory.cpp
@@ -0,0 +1,939 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define DEBUG_KP_COMMAND_HISTORY 0
+
+
+#include <kpcommandhistory.h>
+
+#include <limits.h>
+
+#include <qdatetime.h>
+
+#include <kactionclasses.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <kstdaccel.h>
+#include <kstdaction.h>
+
+#include <kpdefs.h>
+#include <kpdocument.h>
+#include <kpmainwindow.h>
+#include <kptool.h>
+
+
+//template <typename T>
+static void clearPointerList (QValueList <kpCommand *> *listPtr)
+{
+ if (!listPtr)
+ return;
+
+ for (QValueList <kpCommand *>::iterator it = listPtr->begin ();
+ it != listPtr->end ();
+ it++)
+ {
+ delete (*it);
+ }
+
+ listPtr->clear ();
+}
+
+
+//
+// kpCommand
+//
+
+kpCommand::kpCommand (kpMainWindow *mainWindow)
+ : m_mainWindow (mainWindow)
+{
+ if (!mainWindow)
+ kdError () << "kpCommand::kpCommand() passed 0 mainWindow" << endl;
+}
+
+kpCommand::~kpCommand ()
+{
+}
+
+
+// protected
+kpMainWindow *kpCommand::mainWindow () const
+{
+ return m_mainWindow;
+}
+
+
+// protected
+kpDocument *kpCommand::document () const
+{
+ return m_mainWindow ? m_mainWindow->document () : 0;
+}
+
+// protected
+kpSelection *kpCommand::selection () const
+{
+ kpDocument *doc = document ();
+ if (!doc)
+ return 0;
+
+ return doc->selection ();
+}
+
+
+// protected
+kpViewManager *kpCommand::viewManager () const
+{
+ return m_mainWindow ? m_mainWindow->viewManager () : 0;
+}
+
+
+//
+// kpNamedCommand
+//
+
+kpNamedCommand::kpNamedCommand (const QString &name, kpMainWindow *mainWindow)
+ : kpCommand (mainWindow),
+ m_name (name)
+{
+}
+
+kpNamedCommand::~kpNamedCommand ()
+{
+}
+
+
+// public virtual [base kpCommand]
+QString kpNamedCommand::name () const
+{
+ return m_name;
+}
+
+
+//
+// kpMacroCommand
+//
+
+struct kpMacroCommandPrivate
+{
+};
+
+kpMacroCommand::kpMacroCommand (const QString &name, kpMainWindow *mainWindow)
+ : kpNamedCommand (name, mainWindow),
+ d (new kpMacroCommandPrivate ())
+{
+}
+
+kpMacroCommand::~kpMacroCommand ()
+{
+ clearPointerList (&m_commandList);
+ delete d;
+}
+
+
+// public virtual [base kpCommand]
+int kpMacroCommand::size () const
+{
+#if DEBUG_KP_COMMAND_HISTORY && 0
+ kdDebug () << "kpMacroCommand::size()" << endl;
+#endif
+ int s = 0;
+
+#if DEBUG_KP_COMMAND_HISTORY && 0
+ kdDebug () << "\tcalculating:" << endl;
+#endif
+ for (QValueList <kpCommand *>::const_iterator it = m_commandList.begin ();
+ it != m_commandList.end ();
+ it++)
+ {
+ #if DEBUG_KP_COMMAND_HISTORY && 0
+ kdDebug () << "\t\tcurrentSize=" << s << " + "
+ << (*it)->name () << ".size=" << (*it)->size ()
+ << endl;
+ #endif
+ if (s > INT_MAX - (*it)->size ())
+ {
+ #if DEBUG_KP_COMMAND_HISTORY && 0
+ kdDebug () << "\t\t\toverflow" << endl;
+ #endif
+ s = INT_MAX;
+ break;
+ }
+ else
+ {
+ s += (*it)->size ();
+ }
+ }
+
+#if DEBUG_KP_COMMAND_HISTORY && 0
+ kdDebug () << "\treturning " << s << endl;
+#endif
+ return s;
+}
+
+
+// public virtual [base kpCommand]
+void kpMacroCommand::execute ()
+{
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "kpMacroCommand::execute()" << endl;
+#endif
+ for (QValueList <kpCommand *>::const_iterator it = m_commandList.begin ();
+ it != m_commandList.end ();
+ it++)
+ {
+ #if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "\texecuting " << (*it)->name () << endl;
+ #endif
+ (*it)->execute ();
+ }
+}
+
+// public virtual [base kpCommand]
+void kpMacroCommand::unexecute ()
+{
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "kpMacroCommand::unexecute()" << endl;
+#endif
+ QValueList <kpCommand *>::const_iterator it = m_commandList.end ();
+ it--;
+
+ while (it != m_commandList.end ())
+ {
+ #if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "\tunexecuting " << (*it)->name () << endl;
+ #endif
+ (*it)->unexecute ();
+
+ it--;
+ }
+}
+
+
+// public
+void kpMacroCommand::addCommand (kpCommand *command)
+{
+ m_commandList.push_back (command);
+}
+
+
+//
+// kpCommandHistoryBase
+//
+
+struct kpCommandHistoryBasePrivate
+{
+};
+
+
+kpCommandHistoryBase::kpCommandHistoryBase (bool doReadConfig,
+ KActionCollection *ac)
+ : d (new kpCommandHistoryBasePrivate ())
+{
+ m_actionUndo = new KToolBarPopupAction (undoActionText (),
+ QString::fromLatin1 ("undo"),
+ KStdAccel::shortcut (KStdAccel::Undo),
+ this, SLOT (undo ()),
+ ac, KStdAction::name (KStdAction::Undo));
+
+ m_actionRedo = new KToolBarPopupAction (redoActionText (),
+ QString::fromLatin1 ("redo"),
+ KStdAccel::shortcut (KStdAccel::Redo),
+ this, SLOT (redo ()),
+ ac, KStdAction::name (KStdAction::Redo));
+
+
+ m_actionUndo->setEnabled (false);
+ m_actionRedo->setEnabled (false);
+
+
+ connect (m_actionUndo->popupMenu (), SIGNAL (activated (int)),
+ this, SLOT (undoUpToNumber (int)));
+ connect (m_actionRedo->popupMenu (), SIGNAL (activated (int)),
+ this, SLOT (redoUpToNumber (int)));
+
+
+ m_undoMinLimit = 10;
+ m_undoMaxLimit = 500;
+ m_undoMaxLimitSizeLimit = 16 * 1048576;
+
+
+ m_documentRestoredPosition = 0;
+
+
+ if (doReadConfig)
+ readConfig ();
+}
+
+kpCommandHistoryBase::~kpCommandHistoryBase ()
+{
+ clearPointerList (&m_undoCommandList);
+ clearPointerList (&m_redoCommandList);
+
+ delete d;
+}
+
+
+// public
+int kpCommandHistoryBase::undoLimit () const
+{
+ return undoMinLimit ();
+}
+
+// public
+void kpCommandHistoryBase::setUndoLimit (int limit)
+{
+ setUndoMinLimit (limit);
+}
+
+
+// public
+int kpCommandHistoryBase::undoMinLimit () const
+{
+ return m_undoMinLimit;
+}
+
+// public
+void kpCommandHistoryBase::setUndoMinLimit (int limit)
+{
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "kpCommandHistoryBase::setUndoMinLimit("
+ << limit << ")"
+ << endl;
+#endif
+
+ if (limit < 1 || limit > 5000/*"ought to be enough for anybody"*/)
+ {
+ kdError () << "kpCommandHistoryBase::setUndoMinLimit("
+ << limit << ")"
+ << endl;
+ return;
+ }
+
+ if (limit == m_undoMinLimit)
+ return;
+
+ m_undoMinLimit = limit;
+ trimCommandListsUpdateActions ();
+}
+
+
+// public
+int kpCommandHistoryBase::undoMaxLimit () const
+{
+ return m_undoMaxLimit;
+}
+
+// public
+void kpCommandHistoryBase::setUndoMaxLimit (int limit)
+{
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "kpCommandHistoryBase::setUndoMaxLimit("
+ << limit << ")"
+ << endl;
+#endif
+
+ if (limit < 1 || limit > 5000/*"ought to be enough for anybody"*/)
+ {
+ kdError () << "kpCommandHistoryBase::setUndoMaxLimit("
+ << limit << ")"
+ << endl;
+ return;
+ }
+
+ if (limit == m_undoMaxLimit)
+ return;
+
+ m_undoMaxLimit = limit;
+ trimCommandListsUpdateActions ();
+}
+
+
+// public
+int kpCommandHistoryBase::undoMaxLimitSizeLimit () const
+{
+ return m_undoMaxLimitSizeLimit;
+}
+
+// public
+void kpCommandHistoryBase::setUndoMaxLimitSizeLimit (int sizeLimit)
+{
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "kpCommandHistoryBase::setUndoMaxLimitSizeLimit("
+ << sizeLimit << ")"
+ << endl;
+#endif
+
+ if (sizeLimit < 0 ||
+ sizeLimit > (500 * 1048576)/*"ought to be enough for anybody"*/)
+ {
+ kdError () << "kpCommandHistoryBase::setUndoMaxLimitSizeLimit("
+ << sizeLimit << ")"
+ << endl;
+ return;
+ }
+
+ if (sizeLimit == m_undoMaxLimitSizeLimit)
+ return;
+
+ m_undoMaxLimitSizeLimit = sizeLimit;
+ trimCommandListsUpdateActions ();
+}
+
+
+// public
+void kpCommandHistoryBase::readConfig ()
+{
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "kpCommandHistoryBase::readConfig()" << endl;
+#endif
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupUndoRedo);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ setUndoMinLimit (cfg->readNumEntry (kpSettingUndoMinLimit, undoMinLimit ()));
+ setUndoMaxLimit (cfg->readNumEntry (kpSettingUndoMaxLimit, undoMaxLimit ()));
+ setUndoMaxLimitSizeLimit (cfg->readNumEntry (kpSettingUndoMaxLimitSizeLimit,
+ undoMaxLimitSizeLimit ()));
+
+ trimCommandListsUpdateActions ();
+}
+
+// public
+void kpCommandHistoryBase::writeConfig ()
+{
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "kpCommandHistoryBase::writeConfig()" << endl;
+#endif
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupUndoRedo);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ cfg->writeEntry (kpSettingUndoMinLimit, undoMinLimit ());
+ cfg->writeEntry (kpSettingUndoMaxLimit, undoMaxLimit ());
+ cfg->writeEntry (kpSettingUndoMaxLimitSizeLimit, undoMaxLimitSizeLimit ());
+
+ cfg->sync ();
+}
+
+
+// public
+void kpCommandHistoryBase::addCommand (kpCommand *command, bool execute)
+{
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "kpCommandHistoryBase::addCommand("
+ << command
+ << ",execute=" << execute << ")"
+ << endl;
+#endif
+
+ if (execute)
+ command->execute ();
+
+ m_undoCommandList.push_front (command);
+ clearPointerList (&m_redoCommandList);
+
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "\tdocumentRestoredPosition=" << m_documentRestoredPosition
+ << endl;
+#endif
+ if (m_documentRestoredPosition != INT_MAX)
+ {
+ if (m_documentRestoredPosition > 0)
+ m_documentRestoredPosition = INT_MAX;
+ else
+ m_documentRestoredPosition--;
+ #if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "\t\tdocumentRestoredPosition=" << m_documentRestoredPosition
+ << endl;
+ #endif
+ }
+
+ trimCommandListsUpdateActions ();
+}
+
+// public
+void kpCommandHistoryBase::clear ()
+{
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "kpCommandHistoryBase::clear()" << endl;
+#endif
+
+ clearPointerList (&m_undoCommandList);
+ clearPointerList (&m_redoCommandList);
+
+ m_documentRestoredPosition = 0;
+
+ updateActions ();
+}
+
+
+// protected slot
+void kpCommandHistoryBase::undoInternal ()
+{
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "kpCommandHistoryBase::undoInternal()" << endl;
+#endif
+
+ kpCommand *undoCommand = nextUndoCommand ();
+ if (!undoCommand)
+ return;
+
+ undoCommand->unexecute ();
+
+
+ m_undoCommandList.erase (m_undoCommandList.begin ());
+ m_redoCommandList.push_front (undoCommand);
+
+
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "\tdocumentRestoredPosition=" << m_documentRestoredPosition
+ << endl;
+#endif
+ if (m_documentRestoredPosition != INT_MAX)
+ {
+ m_documentRestoredPosition++;
+ if (m_documentRestoredPosition == 0)
+ emit documentRestored ();
+ #if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "\t\tdocumentRestoredPosition=" << m_documentRestoredPosition
+ << endl;
+ #endif
+ }
+}
+
+// protected slot
+void kpCommandHistoryBase::redoInternal ()
+{
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "kpCommandHistoryBase::redoInternal()" << endl;
+#endif
+
+ kpCommand *redoCommand = nextRedoCommand ();
+ if (!redoCommand)
+ return;
+
+ redoCommand->execute ();
+
+
+ m_redoCommandList.erase (m_redoCommandList.begin ());
+ m_undoCommandList.push_front (redoCommand);
+
+
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "\tdocumentRestoredPosition=" << m_documentRestoredPosition
+ << endl;
+#endif
+ if (m_documentRestoredPosition != INT_MAX)
+ {
+ m_documentRestoredPosition--;
+ if (m_documentRestoredPosition == 0)
+ emit documentRestored ();
+ #if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "\t\tdocumentRestoredPosition=" << m_documentRestoredPosition
+ << endl;
+ #endif
+ }
+}
+
+
+// public slot virtual
+void kpCommandHistoryBase::undo ()
+{
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "kpCommandHistoryBase::undo()" << endl;
+#endif
+
+ undoInternal ();
+ trimCommandListsUpdateActions ();
+}
+
+// public slot virtual
+void kpCommandHistoryBase::redo ()
+{
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "kpCommandHistoryBase::redo()" << endl;
+#endif
+
+ redoInternal ();
+ trimCommandListsUpdateActions ();
+}
+
+
+// public slot virtual
+void kpCommandHistoryBase::undoUpToNumber (int which)
+{
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "kpCommandHistoryBase::undoUpToNumber(" << which << ")" << endl;
+#endif
+
+ for (int i = 0;
+ i <= which && !m_undoCommandList.isEmpty ();
+ i++)
+ {
+ undoInternal ();
+ }
+
+ trimCommandListsUpdateActions ();
+}
+
+// public slot virtual
+void kpCommandHistoryBase::redoUpToNumber (int which)
+{
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "kpCommandHistoryBase::redoUpToNumber(" << which << ")" << endl;
+#endif
+
+ for (int i = 0;
+ i <= which && !m_redoCommandList.isEmpty ();
+ i++)
+ {
+ redoInternal ();
+ }
+
+ trimCommandListsUpdateActions ();
+}
+
+
+// protected
+QString kpCommandHistoryBase::undoActionText () const
+{
+ kpCommand *undoCommand = nextUndoCommand ();
+
+ if (undoCommand)
+ return i18n ("&Undo: %1").arg (undoCommand->name ());
+ else
+ return i18n ("&Undo");
+}
+
+// protected
+QString kpCommandHistoryBase::redoActionText () const
+{
+ kpCommand *redoCommand = nextRedoCommand ();
+
+ if (redoCommand)
+ return i18n ("&Redo: %1").arg (redoCommand->name ());
+ else
+ return i18n ("&Redo");
+}
+
+
+// protected
+void kpCommandHistoryBase::trimCommandListsUpdateActions ()
+{
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "kpCommandHistoryBase::trimCommandListsUpdateActions()" << endl;
+#endif
+
+ trimCommandLists ();
+ updateActions ();
+}
+
+// protected
+void kpCommandHistoryBase::trimCommandList (QValueList <kpCommand *> *commandList)
+{
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "kpCommandHistoryBase::trimCommandList()" << endl;
+ QTime timer; timer.start ();
+#endif
+
+ if (!commandList)
+ {
+ kdError () << "kpCommandHistoryBase::trimCommandList() passed 0 commandList"
+ << endl;
+ return;
+ }
+
+
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "\tsize=" << commandList->size ()
+ << " undoMinLimit=" << m_undoMinLimit
+ << " undoMaxLimit=" << m_undoMaxLimit
+ << " undoMaxLimitSizeLimit=" << m_undoMaxLimitSizeLimit
+ << endl;
+#endif
+ if ((int) commandList->size () <= m_undoMinLimit)
+ {
+ #if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "\t\tsize under undoMinLimit - done" << endl;
+ #endif
+ return;
+ }
+
+
+#if DEBUG_KP_COMMAND_HISTORY && 0
+ kdDebug () << "\tsize over undoMinLimit - iterating thru cmds:" << endl;
+#endif
+
+ QValueList <kpCommand *>::iterator it = commandList->begin ();
+ int upto = 0;
+
+ int sizeSoFar = 0;
+
+ while (it != commandList->end ())
+ {
+ bool advanceIt = true;
+
+ if (sizeSoFar <= m_undoMaxLimitSizeLimit)
+ {
+ if (sizeSoFar > INT_MAX - (*it)->size ())
+ sizeSoFar = INT_MAX;
+ else
+ sizeSoFar += (*it)->size ();
+ }
+
+ #if DEBUG_KP_COMMAND_HISTORY && 0
+ kdDebug () << "\t\t" << upto << ":"
+ << " name='" << (*it)->name ()
+ << "' size=" << (*it)->size ()
+ << " sizeSoFar=" << sizeSoFar
+ << endl;
+ #endif
+
+ if (upto >= m_undoMinLimit)
+ {
+ if (upto >= m_undoMaxLimit ||
+ sizeSoFar > m_undoMaxLimitSizeLimit)
+ {
+ #if DEBUG_KP_COMMAND_HISTORY && 0
+ kdDebug () << "\t\t\tkill" << endl;
+ #endif
+ delete (*it);
+ it = m_undoCommandList.erase (it);
+ advanceIt = false;
+ }
+ }
+
+ if (advanceIt)
+ it++;
+ upto++;
+ }
+
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "\ttook " << timer.elapsed () << "ms" << endl;
+#endif
+}
+
+// protected
+void kpCommandHistoryBase::trimCommandLists ()
+{
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "kpCommandHistoryBase::trimCommandLists()" << endl;
+#endif
+
+ trimCommandList (&m_undoCommandList);
+ trimCommandList (&m_redoCommandList);
+
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "\tdocumentRestoredPosition=" << m_documentRestoredPosition
+ << endl;
+#endif
+ if (m_documentRestoredPosition != INT_MAX)
+ {
+ #if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "\t\tundoCmdList.size=" << m_undoCommandList.size ()
+ << " redoCmdList.size=" << m_redoCommandList.size ()
+ << endl;
+ #endif
+ if (m_documentRestoredPosition > (int) m_redoCommandList.size () ||
+ -m_documentRestoredPosition > (int) m_undoCommandList.size ())
+ {
+ #if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "\t\t\tinvalidate documentRestoredPosition" << endl;
+ #endif
+ m_documentRestoredPosition = INT_MAX;
+ }
+ }
+}
+
+
+static void populatePopupMenu (KPopupMenu *popupMenu,
+ const QString &undoOrRedo,
+ const QValueList <kpCommand *> &commandList)
+{
+ if (!popupMenu)
+ return;
+
+ popupMenu->clear ();
+
+ QValueList <kpCommand *>::const_iterator it = commandList.begin ();
+ int i = 0;
+ while (i < 10 && it != commandList.end ())
+ {
+ popupMenu->insertItem (i18n ("%1: %2").arg (undoOrRedo).arg ((*it)->name ()), i/*id*/);
+ i++, it++;
+ }
+
+ if (it != commandList.end ())
+ {
+ // TODO: maybe have a scrollview show all the items instead
+ KPopupTitle *title = new KPopupTitle (popupMenu);
+ title->setTitle (i18n ("%n more item", "%n more items",
+ commandList.size () - i));
+
+ popupMenu->insertItem (title);
+ }
+}
+
+
+// protected
+void kpCommandHistoryBase::updateActions ()
+{
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "kpCommandHistoryBase::updateActions()" << endl;
+#endif
+
+ m_actionUndo->setEnabled ((bool) nextUndoCommand ());
+ m_actionUndo->setText (undoActionText ());
+#if DEBUG_KP_COMMAND_HISTORY
+ QTime timer; timer.start ();
+#endif
+ populatePopupMenu (m_actionUndo->popupMenu (),
+ i18n ("Undo"),
+ m_undoCommandList);
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "\tpopuplatePopupMenu undo=" << timer.elapsed ()
+ << "ms" << endl;;
+#endif
+
+ m_actionRedo->setEnabled ((bool) nextRedoCommand ());
+ m_actionRedo->setText (redoActionText ());
+#if DEBUG_KP_COMMAND_HISTORY
+ timer.restart ();
+#endif
+ populatePopupMenu (m_actionRedo->popupMenu (),
+ i18n ("Redo"),
+ m_redoCommandList);
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "\tpopuplatePopupMenu redo=" << timer.elapsed ()
+ << "ms" << endl;
+#endif
+}
+
+
+// public
+kpCommand *kpCommandHistoryBase::nextUndoCommand () const
+{
+ if (m_undoCommandList.isEmpty ())
+ return 0;
+
+ return m_undoCommandList.first ();
+}
+
+// public
+kpCommand *kpCommandHistoryBase::nextRedoCommand () const
+{
+ if (m_redoCommandList.isEmpty ())
+ return 0;
+
+ return m_redoCommandList.first ();
+}
+
+
+// public
+void kpCommandHistoryBase::setNextUndoCommand (kpCommand *command)
+{
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "kpCommandHistoryBase::setNextUndoCommand("
+ << command
+ << ")"
+ << endl;
+#endif
+
+ if (m_undoCommandList.isEmpty ())
+ return;
+
+
+ delete m_undoCommandList [0];
+ m_undoCommandList [0] = command;
+
+
+ trimCommandListsUpdateActions ();
+}
+
+
+// public slot virtual
+void kpCommandHistoryBase::documentSaved ()
+{
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "kpCommandHistoryBase::documentSaved()" << endl;
+#endif
+
+ m_documentRestoredPosition = 0;
+}
+
+
+//
+// kpCommandHistory
+//
+
+kpCommandHistory::kpCommandHistory (bool doReadConfig, kpMainWindow *mainWindow)
+ : kpCommandHistoryBase (doReadConfig, mainWindow->actionCollection ()),
+ m_mainWindow (mainWindow)
+{
+}
+
+kpCommandHistory::~kpCommandHistory ()
+{
+}
+
+
+// public slot virtual [base KCommandHistory]
+void kpCommandHistory::undo ()
+{
+#if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "kpCommandHistory::undo() CALLED!" << endl;
+#endif
+ if (m_mainWindow && m_mainWindow->toolHasBegunShape ())
+ {
+ #if DEBUG_KP_COMMAND_HISTORY
+ kdDebug () << "\thas begun shape - cancel draw" << endl;
+ #endif
+ m_mainWindow->tool ()->cancelShapeInternal ();
+ }
+ else
+ kpCommandHistoryBase::undo ();
+}
+
+// public slot virtual [base KCommandHistory]
+void kpCommandHistory::redo ()
+{
+ if (m_mainWindow && m_mainWindow->toolHasBegunShape ())
+ {
+ // Not completely obvious but what else can we do?
+ //
+ // Ignoring the request would not be intuitive for tools like
+ // Polygon & Polyline (where it's not always apparent to the user
+ // that s/he's still drawing a shape even though the mouse isn't
+ // down).
+ m_mainWindow->tool ()->cancelShapeInternal ();
+ }
+ else
+ kpCommandHistoryBase::redo ();
+}
+
+#include <kpcommandhistory.moc>
diff --git a/kolourpaint/kpcommandhistory.h b/kolourpaint/kpcommandhistory.h
new file mode 100644
index 00000000..a1541512
--- /dev/null
+++ b/kolourpaint/kpcommandhistory.h
@@ -0,0 +1,255 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef KP_COMMAND_HISTORY_H
+#define KP_COMMAND_HISTORY_H
+
+#include <qobject.h>
+#include <qstring.h>
+#include <qvaluelist.h>
+
+
+class KActionCollection;
+class KToolBarPopupAction;
+
+class kpDocument;
+class kpMainWindow;
+class kpSelection;
+class kpViewManager;
+
+
+class kpCommand
+{
+public:
+ kpCommand (kpMainWindow *mainWindow);
+ virtual ~kpCommand ();
+
+public:
+ virtual QString name () const = 0;
+
+ // Returns the estimated size in bytes.
+ //
+ // You only have to factor in the size of variables that change according
+ // to the amount of input e.g. pixmap size, text size. There is no need
+ // to include the size of O(1) variables unless they are huge.
+ //
+ // If in doubt, return the largest possible amount of memory that your
+ // command will take. This is better than making the user unexpectedly
+ // run out of memory.
+ virtual int size () const = 0;
+
+ virtual void execute () = 0;
+ virtual void unexecute () = 0;
+
+protected:
+ kpMainWindow *mainWindow () const;
+
+ kpDocument *document () const;
+ kpSelection *selection () const;
+
+ kpViewManager *viewManager () const;
+
+protected:
+ kpMainWindow *m_mainWindow;
+};
+
+
+class kpNamedCommand : public kpCommand
+{
+public:
+ kpNamedCommand (const QString &name, kpMainWindow *mainWindow);
+ virtual ~kpNamedCommand ();
+
+ virtual QString name () const;
+
+protected:
+ QString m_name;
+};
+
+
+class kpMacroCommand : public kpNamedCommand
+{
+public:
+ kpMacroCommand (const QString &name, kpMainWindow *mainWindow);
+ virtual ~kpMacroCommand ();
+
+
+ //
+ // kpCommand Interface
+ //
+
+ virtual int size () const;
+
+ virtual void execute ();
+ virtual void unexecute ();
+
+
+ //
+ // Interface
+ //
+
+ void addCommand (kpCommand *command);
+
+protected:
+ QValueList <kpCommand *> m_commandList;
+
+private:
+ class kpMacroCommandPrivate *d;
+};
+
+
+// Clone of KCommandHistory with features required by KolourPaint:
+// - nextUndoCommand()/nextRedoCommand()
+// - undo/redo history limited by both number and size
+//
+// Features not required by KolourPaint (e.g. commandExecuted()) are not
+// implemented and undo limit == redo limit. So compared to
+// KCommandHistory, this is only "almost source compatible".
+class kpCommandHistoryBase : public QObject
+{
+Q_OBJECT
+
+public:
+ kpCommandHistoryBase (bool doReadConfig, KActionCollection *ac);
+ virtual ~kpCommandHistoryBase ();
+
+public:
+ // (provided for compatibility with KCommandHistory)
+ int undoLimit () const;
+ void setUndoLimit (int limit);
+
+
+ int undoMinLimit () const;
+ void setUndoMinLimit (int limit);
+
+ int undoMaxLimit () const;
+ void setUndoMaxLimit (int limit);
+
+ int undoMaxLimitSizeLimit () const;
+ void setUndoMaxLimitSizeLimit (int sizeLimit);
+
+public:
+ // Read and write above config
+ void readConfig ();
+ void writeConfig ();
+
+public:
+ void addCommand (kpCommand *command, bool execute = true);
+ void clear ();
+
+protected slots:
+ // (same as undo() & redo() except they don't call
+ // trimCommandListsUpdateActions())
+ void undoInternal ();
+ void redoInternal ();
+
+public slots:
+ virtual void undo ();
+ virtual void redo ();
+
+ virtual void undoUpToNumber (int which);
+ virtual void redoUpToNumber (int which);
+
+protected:
+ QString undoActionText () const;
+ QString redoActionText () const;
+
+ void trimCommandListsUpdateActions ();
+ void trimCommandList (QValueList <kpCommand *> *commandList);
+ void trimCommandLists ();
+ void updateActions ();
+
+public:
+ kpCommand *nextUndoCommand () const;
+ kpCommand *nextRedoCommand () const;
+
+ void setNextUndoCommand (kpCommand *command);
+
+public slots:
+ virtual void documentSaved ();
+
+signals:
+ void documentRestored ();
+
+protected:
+ KToolBarPopupAction *m_actionUndo, *m_actionRedo;
+
+ // (Front element is the next one)
+ QValueList <kpCommand *> m_undoCommandList;
+ QValueList <kpCommand *> m_redoCommandList;
+
+ int m_undoMinLimit, m_undoMaxLimit, m_undoMaxLimitSizeLimit;
+
+ // What you have to do to get back to the document's unmodified state:
+ // * -x: must Undo x times
+ // * 0: unmodified
+ // * +x: must Redo x times
+ // * INT_MAX: can never become unmodified again
+ //
+ // ASSUMPTION: will never have INT_MAX commands in any list.
+ int m_documentRestoredPosition;
+
+private:
+ class kpCommandHistoryBasePrivate *d;
+};
+
+
+// Intercepts Undo/Redo requests:
+//
+// If the user is currently drawing a shape, it cancels it.
+// Else it passes on the Undo/Redo request to kpCommandHistoryBase.
+//
+// TODO: This is wrong. It won't work if the Undo action is disabled,
+// for instance.
+//
+// Maybe the real solution is to call kpCommandHistoryBase::addCommand()
+// as _soon_ as the shape starts - not after it ends. But the
+// trouble with this solution is that if the user Undoes/cancels
+// the shape s/he's currently drawing, it would replace a Redo
+// slot in the history. Arguably you shouldn't be able to Redo
+// something you never finished drawing.
+//
+// The solution is to add this functionality to kpCommandHistoryBase.
+class kpCommandHistory : public kpCommandHistoryBase
+{
+Q_OBJECT
+
+public:
+ kpCommandHistory (bool doReadConfig, kpMainWindow *mainWindow);
+ virtual ~kpCommandHistory ();
+
+public slots:
+ virtual void undo ();
+ virtual void redo ();
+
+protected:
+ kpMainWindow *m_mainWindow;
+};
+
+
+#endif // KP_COMMAND_HISTORY_H
diff --git a/kolourpaint/kpdefs.h b/kolourpaint/kpdefs.h
new file mode 100644
index 00000000..15faaee0
--- /dev/null
+++ b/kolourpaint/kpdefs.h
@@ -0,0 +1,151 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kp_defs_h__
+#define __kp_defs_h__
+
+
+#include <limits.h>
+
+#include <qglobal.h>
+#include <qpoint.h>
+#include <qsize.h>
+#include <qstring.h>
+
+#include <kdeversion.h>
+
+
+#define KP_IS_QT_3_3 (QT_VERSION >= 0x030300 && 1)
+#define KP_IS_KDE_3_3 ((KDE_VERSION_MAJOR >= 3 && KDE_VERSION_MINOR >= 3) && 1)
+
+
+// approx. 2896x2896x32bpp or 3344x3344x24bpp (TODO: 24==32?) or 4096*4096x16bpp
+#define KP_BIG_IMAGE_SIZE (32 * 1048576)
+
+
+#define KP_PI 3.141592653589793238462
+
+
+#define KP_DEGREES_TO_RADIANS(deg) ((deg) * KP_PI / 180.0)
+#define KP_RADIANS_TO_DEGREES(rad) ((rad) * 180.0 / KP_PI)
+
+
+#define KP_INVALID_POINT QPoint (INT_MIN / 8, INT_MIN / 8)
+#define KP_INVALID_WIDTH (INT_MIN / 8)
+#define KP_INVALID_HEIGHT (INT_MIN / 8)
+#define KP_INVALID_SIZE QSize (INT_MIN / 8, INT_MIN / 8)
+
+
+//
+// Settings
+//
+
+#define kpSettingsGroupGeneral QString::fromLatin1 ("General Settings")
+#define kpSettingFirstTime QString::fromLatin1 ("First Time")
+#define kpSettingShowGrid QString::fromLatin1 ("Show Grid")
+#define kpSettingShowPath QString::fromLatin1 ("Show Path")
+#define kpSettingColorSimilarity QString::fromLatin1 ("Color Similarity")
+#define kpSettingDitherOnOpen QString::fromLatin1 ("Dither on Open if Screen is 15/16bpp and Image Num Colors More Than")
+#define kpSettingPrintImageCenteredOnPage QString::fromLatin1 ("Print Image Centered On Page")
+
+#define kpSettingsGroupFileSaveAs QString::fromLatin1 ("File/Save As")
+#define kpSettingsGroupFileExport QString::fromLatin1 ("File/Export")
+#define kpSettingsGroupEditCopyTo QString::fromLatin1 ("Edit/Copy To")
+
+#define kpSettingForcedMimeType QString::fromLatin1 ("Forced MimeType")
+#define kpSettingForcedColorDepth QString::fromLatin1 ("Forced Color Depth")
+#define kpSettingForcedDither QString::fromLatin1 ("Forced Dither")
+#define kpSettingForcedQuality QString::fromLatin1 ("Forced Quality")
+
+#define kpSettingLastDocSize QString::fromLatin1 ("Last Document Size")
+
+#define kpSettingMoreEffectsLastEffect QString::fromLatin1 ("More Effects - Last Effect")
+
+#define kpSettingResizeScaleLastKeepAspect QString::fromLatin1 ("Resize Scale - Last Keep Aspect")
+
+
+#define kpSettingsGroupMimeTypeProperties QString::fromLatin1 ("MimeType Properties Version 1.2-2")
+#define kpSettingMimeTypeMaximumColorDepth QString::fromLatin1 ("Maximum Color Depth")
+#define kpSettingMimeTypeHasConfigurableColorDepth QString::fromLatin1 ("Configurable Color Depth")
+#define kpSettingMimeTypeHasConfigurableQuality QString::fromLatin1 ("Configurable Quality Setting")
+
+
+#define kpSettingsGroupUndoRedo QString::fromLatin1 ("Undo/Redo Settings")
+#define kpSettingUndoMinLimit QString::fromLatin1 ("Min Limit")
+#define kpSettingUndoMaxLimit QString::fromLatin1 ("Max Limit")
+#define kpSettingUndoMaxLimitSizeLimit QString::fromLatin1 ("Max Limit Size Limit")
+
+
+#define kpSettingsGroupThumbnail QString::fromLatin1 ("Thumbnail Settings")
+#define kpSettingThumbnailShown QString::fromLatin1 ("Shown")
+#define kpSettingThumbnailGeometry QString::fromLatin1 ("Geometry")
+#define kpSettingThumbnailZoomed QString::fromLatin1 ("Zoomed")
+#define kpSettingThumbnailShowRectangle QString::fromLatin1 ("ShowRectangle")
+
+
+#define kpSettingsGroupPreviewSave QString::fromLatin1 ("Save Preview Settings")
+#define kpSettingPreviewSaveGeometry QString::fromLatin1 ("Geometry")
+#define kpSettingPreviewSaveUpdateDelay QString::fromLatin1 ("Update Delay")
+
+
+#define kpSettingsGroupTools QString::fromLatin1 ("Tool Settings")
+#define kpSettingLastTool QString::fromLatin1 ("Last Used Tool")
+#define kpSettingToolBoxIconSize QString::fromLatin1 ("Tool Box Icon Size")
+
+
+#define kpSettingsGroupText QString::fromLatin1 ("Text Settings")
+#define kpSettingFontFamily QString::fromLatin1 ("Font Family")
+#define kpSettingFontSize QString::fromLatin1 ("Font Size")
+#define kpSettingBold QString::fromLatin1 ("Bold")
+#define kpSettingItalic QString::fromLatin1 ("Italic")
+#define kpSettingUnderline QString::fromLatin1 ("Underline")
+#define kpSettingStrikeThru QString::fromLatin1 ("Strike Thru")
+
+
+#define kpSettingsGroupFlattenEffect QString::fromLatin1 ("Flatten Effect Settings")
+#define kpSettingFlattenEffectColor1 QString::fromLatin1 ("Color1")
+#define kpSettingFlattenEffectColor2 QString::fromLatin1 ("Color2")
+
+
+//
+// Session Restore Setting
+//
+
+// URL of the document in the main window.
+//
+// This key only exists if the document does. If it exists, it can be empty.
+// The URL need not point to a file that exists e.g. "kolourpaint doesnotexist.png".
+#define kpSessionSettingDocumentUrl QString::fromLatin1 ("Session Document Url")
+
+// The size of a document which is not from a URL e.g. "kolourpaint doesnotexist.png".
+// This key does not exist for documents from URLs.
+#define kpSessionSettingNotFromUrlDocumentSize QString::fromLatin1 ("Session Not-From-Url Document Size")
+
+
+#endif // __kp_defs_h__
+
diff --git a/kolourpaint/kpdocument.cpp b/kolourpaint/kpdocument.cpp
new file mode 100644
index 00000000..801b922e
--- /dev/null
+++ b/kolourpaint/kpdocument.cpp
@@ -0,0 +1,1539 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define DEBUG_KP_DOCUMENT 0
+
+
+#include <kpdocument.h>
+
+#include <math.h>
+
+#include <qcolor.h>
+#include <qbitmap.h>
+#include <qbrush.h>
+#include <qfile.h>
+#include <qimage.h>
+#include <qpixmap.h>
+#include <qpainter.h>
+#include <qrect.h>
+#include <qsize.h>
+#include <qvaluelist.h>
+#include <qwmatrix.h>
+
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kimageio.h>
+#include <kio/netaccess.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kmimetype.h>
+#include <ksavefile.h>
+#include <ktempfile.h>
+
+#include <kpcolor.h>
+#include <kpcolortoolbar.h>
+#include <kpdefs.h>
+#include <kpdocumentsaveoptions.h>
+#include <kpdocumentmetainfo.h>
+#include <kpeffectreducecolors.h>
+#include <kpmainwindow.h>
+#include <kppixmapfx.h>
+#include <kpselection.h>
+#include <kptool.h>
+#include <kptooltoolbar.h>
+#include <kpviewmanager.h>
+
+
+struct kpDocumentPrivate
+{
+ kpDocumentPrivate ()
+ {
+ }
+};
+
+
+kpDocument::kpDocument (int w, int h, kpMainWindow *mainWindow)
+ : m_constructorWidth (w), m_constructorHeight (h),
+ m_mainWindow (mainWindow),
+ m_isFromURL (false),
+ m_savedAtLeastOnceBefore (false),
+ m_saveOptions (new kpDocumentSaveOptions ()),
+ m_metaInfo (new kpDocumentMetaInfo ()),
+ m_modified (false),
+ m_selection (0),
+ m_oldWidth (-1), m_oldHeight (-1),
+ d (new kpDocumentPrivate ())
+{
+#if DEBUG_KP_DOCUMENT && 0
+ kdDebug () << "kpDocument::kpDocument (" << w << "," << h << ")" << endl;
+#endif
+
+ m_pixmap = new QPixmap (w, h);
+ m_pixmap->fill (Qt::white);
+}
+
+kpDocument::~kpDocument ()
+{
+ delete d;
+
+ delete m_pixmap;
+
+ delete m_saveOptions;
+ delete m_metaInfo;
+
+ delete m_selection;
+}
+
+
+kpMainWindow *kpDocument::mainWindow () const
+{
+ return m_mainWindow;
+}
+
+void kpDocument::setMainWindow (kpMainWindow *mainWindow)
+{
+ m_mainWindow = mainWindow;
+}
+
+
+/*
+ * File I/O
+ */
+
+// public static
+QPixmap kpDocument::convertToPixmapAsLosslessAsPossible (
+ const QImage &image,
+ const kpPixmapFX::WarnAboutLossInfo &wali,
+
+ kpDocumentSaveOptions *saveOptions,
+ kpDocumentMetaInfo *metaInfo)
+{
+ if (image.isNull ())
+ return QPixmap ();
+
+
+#if DEBUG_KP_DOCUMENT
+ kdDebug () << "\timage: depth=" << image.depth ()
+ << " (X display=" << QColor::numBitPlanes () << ")"
+ << " hasAlphaBuffer=" << image.hasAlphaBuffer ()
+ << endl;
+#endif
+
+ if (saveOptions)
+ {
+ saveOptions->setColorDepth (image.depth ());
+ saveOptions->setDither (false); // avoid double dithering when saving
+ }
+
+ if (metaInfo)
+ {
+ metaInfo->setDotsPerMeterX (image.dotsPerMeterX ());
+ metaInfo->setDotsPerMeterY (image.dotsPerMeterY ());
+ metaInfo->setOffset (image.offset ());
+
+ QValueList <QImageTextKeyLang> keyList = image.textList ();
+ for (QValueList <QImageTextKeyLang>::const_iterator it = keyList.begin ();
+ it != keyList.end ();
+ it++)
+ {
+ metaInfo->setText (*it, image.text (*it));
+ }
+
+ #if DEBUG_KP_DOCUMENT
+ metaInfo->printDebug ("\tmetaInfo");
+ #endif
+ }
+
+#if DEBUG_KP_DOCUMENT && 1
+{
+ if (image.width () <= 16 && image.height () <= 16)
+ {
+ kdDebug () << "Image dump:" << endl;
+
+ for (int y = 0; y < image.height (); y++)
+ {
+ for (int x = 0; x < image.width (); x++)
+ {
+ const QRgb rgb = image.pixel (x, y);
+ fprintf (stderr, " %08X", rgb);
+ }
+ fprintf (stderr, "\n");
+ }
+ }
+}
+#endif
+
+
+ QPixmap newPixmap = kpPixmapFX::convertToPixmapAsLosslessAsPossible (image, wali);
+
+
+#if DEBUG_KP_DOCUMENT && 1
+{
+ const QImage image2 = kpPixmapFX::convertToImage (newPixmap);
+ kdDebug () << "(Converted to pixmap) Image dump:" << endl;
+
+ bool differsFromOrgImage = false;
+ unsigned long hash = 0;
+ int numDiff = 0;
+ for (int y = 0; y < image2.height (); y++)
+ {
+ for (int x = 0; x < image2.width (); x++)
+ {
+ const QRgb rgb = image2.pixel (x, y);
+ hash += ((x % 2) + 1) * rgb;
+ if (rgb != image.pixel (x, y))
+ {
+ differsFromOrgImage = true;
+ numDiff++;
+ }
+ if (image2.width () <= 16 && image2.height () <= 16)
+ fprintf (stderr, " %08X", rgb);
+ }
+ if (image2.width () <= 16 && image2.height () <= 16)
+ fprintf (stderr, "\n");
+ }
+
+ kdDebug () << "\tdiffersFromOrgImage="
+ << differsFromOrgImage
+ << " numDiff="
+ << numDiff
+ << " hash=" << hash << endl;
+}
+#endif
+
+ return newPixmap;
+}
+
+// public static
+QPixmap kpDocument::getPixmapFromFile (const KURL &url, bool suppressDoesntExistDialog,
+ QWidget *parent,
+ kpDocumentSaveOptions *saveOptions,
+ kpDocumentMetaInfo *metaInfo)
+{
+#if DEBUG_KP_DOCUMENT
+ kdDebug () << "kpDocument::getPixmapFromFile(" << url << "," << parent << ")" << endl;
+#endif
+
+ if (saveOptions)
+ *saveOptions = kpDocumentSaveOptions ();
+
+ if (metaInfo)
+ *metaInfo = kpDocumentMetaInfo ();
+
+
+ QString tempFile;
+ if (url.isEmpty () || !KIO::NetAccess::download (url, tempFile, parent))
+ {
+ if (!suppressDoesntExistDialog)
+ {
+ KMessageBox::sorry (parent,
+ i18n ("Could not open \"%1\".")
+ .arg (kpDocument::prettyFilenameForURL (url)));
+ }
+
+ return QPixmap ();
+ }
+
+
+ QImage image;
+
+ // sync: remember to "KIO::NetAccess::removeTempFile (tempFile)" in all exit paths
+ {
+ QString detectedMimeType = KImageIO::mimeType (tempFile);
+ if (saveOptions)
+ saveOptions->setMimeType (detectedMimeType);
+
+ #if DEBUG_KP_DOCUMENT
+ kdDebug () << "\ttempFile=" << tempFile << endl;
+ kdDebug () << "\tmimetype=" << detectedMimeType << endl;
+ kdDebug () << "\tsrc=" << url.path () << endl;
+ kdDebug () << "\tmimetype of src=" << KImageIO::mimeType (url.path ()) << endl;
+ #endif
+
+ if (detectedMimeType.isEmpty ())
+ {
+ KMessageBox::sorry (parent,
+ i18n ("Could not open \"%1\" - unknown mimetype.")
+ .arg (kpDocument::prettyFilenameForURL (url)));
+ KIO::NetAccess::removeTempFile (tempFile);
+ return QPixmap ();
+ }
+
+
+ image = QImage (tempFile);
+ KIO::NetAccess::removeTempFile (tempFile);
+ }
+
+
+ if (image.isNull ())
+ {
+ KMessageBox::sorry (parent,
+ i18n ("Could not open \"%1\" - unsupported image format.\n"
+ "The file may be corrupt.")
+ .arg (kpDocument::prettyFilenameForURL (url)));
+ return QPixmap ();
+ }
+
+ const QPixmap newPixmap = kpDocument::convertToPixmapAsLosslessAsPossible (image,
+ kpPixmapFX::WarnAboutLossInfo (
+ i18n ("The image \"%1\""
+ " may have more colors than the current screen mode."
+ " In order to display it, some colors may be changed."
+ " Try increasing your screen depth to at least %2bpp."
+
+ "\nIt also"
+
+ " contains translucency which is not fully"
+ " supported. The translucency data will be"
+ " approximated with a 1-bit transparency mask.")
+ .arg (prettyFilenameForURL (url)),
+ i18n ("The image \"%1\""
+ " may have more colors than the current screen mode."
+ " In order to display it, some colors may be changed."
+ " Try increasing your screen depth to at least %2bpp.")
+ .arg (prettyFilenameForURL (url)),
+ i18n ("The image \"%1\""
+ " contains translucency which is not fully"
+ " supported. The translucency data will be"
+ " approximated with a 1-bit transparency mask.")
+ .arg (prettyFilenameForURL (url)),
+ "docOpen",
+ parent),
+ saveOptions,
+ metaInfo);
+
+ if (newPixmap.isNull ())
+ {
+ KMessageBox::sorry (parent,
+ i18n ("Could not open \"%1\" - out of graphics memory.")
+ .arg (kpDocument::prettyFilenameForURL (url)));
+ return QPixmap ();
+ }
+
+#if DEBUG_KP_DOCUMENT
+ kdDebug () << "\tpixmap: depth=" << newPixmap.depth ()
+ << " hasAlphaChannelOrMask=" << newPixmap.hasAlpha ()
+ << " hasAlphaChannel=" << newPixmap.hasAlphaChannel ()
+ << endl;
+#endif
+
+
+ return newPixmap;
+}
+
+void kpDocument::openNew (const KURL &url)
+{
+#if DEBUG_KP_DOCUMENT
+ kdDebug () << "KpDocument::openNew (" << url << ")" << endl;
+#endif
+
+ m_pixmap->fill (Qt::white);
+
+ setURL (url, false/*not from url*/);
+ *m_saveOptions = kpDocumentSaveOptions ();
+ *m_metaInfo = kpDocumentMetaInfo ();
+ m_modified = false;
+
+ emit documentOpened ();
+}
+
+bool kpDocument::open (const KURL &url, bool newDocSameNameIfNotExist)
+{
+#if DEBUG_KP_DOCUMENT
+ kdDebug () << "kpDocument::open (" << url << ")" << endl;
+#endif
+
+ kpDocumentSaveOptions newSaveOptions;
+ kpDocumentMetaInfo newMetaInfo;
+ QPixmap newPixmap = kpDocument::getPixmapFromFile (url,
+ newDocSameNameIfNotExist/*suppress "doesn't exist" dialog*/,
+ m_mainWindow,
+ &newSaveOptions,
+ &newMetaInfo);
+
+ if (!newPixmap.isNull ())
+ {
+ delete m_pixmap;
+ m_pixmap = new QPixmap (newPixmap);
+
+ setURL (url, true/*is from url*/);
+ *m_saveOptions = newSaveOptions;
+ *m_metaInfo = newMetaInfo;
+ m_modified = false;
+
+ emit documentOpened ();
+ return true;
+ }
+
+ if (newDocSameNameIfNotExist)
+ {
+ if (!url.isEmpty () &&
+ // not just a permission error?
+ !KIO::NetAccess::exists (url, true/*open*/, m_mainWindow))
+ {
+ openNew (url);
+ }
+ else
+ {
+ openNew (KURL ());
+ }
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+bool kpDocument::save (bool overwritePrompt, bool lossyPrompt)
+{
+#if DEBUG_KP_DOCUMENT
+ kdDebug () << "kpDocument::save("
+ << "overwritePrompt=" << overwritePrompt
+ << ",lossyPrompt=" << lossyPrompt
+ << ") url=" << m_url
+ << " savedAtLeastOnceBefore=" << savedAtLeastOnceBefore ()
+ << endl;
+#endif
+
+ // TODO: check feels weak
+ if (m_url.isEmpty () || m_saveOptions->mimeType ().isEmpty ())
+ {
+ KMessageBox::detailedError (m_mainWindow,
+ i18n ("Could not save image - insufficient information."),
+ i18n ("URL: %1\n"
+ "Mimetype: %2")
+ .arg (prettyURL ())
+ .arg (m_saveOptions->mimeType ().isEmpty () ?
+ i18n ("<empty>") :
+ m_saveOptions->mimeType ()),
+ i18n ("Internal Error"));
+ return false;
+ }
+
+ return saveAs (m_url, *m_saveOptions,
+ overwritePrompt,
+ lossyPrompt);
+}
+
+
+// public static
+bool kpDocument::lossyPromptContinue (const QPixmap &pixmap,
+ const kpDocumentSaveOptions &saveOptions,
+ QWidget *parent)
+{
+#if DEBUG_KP_DOCUMENT
+ kdDebug () << "kpDocument::lossyPromptContinue()" << endl;
+#endif
+
+#define QUIT_IF_CANCEL(messageBoxCommand) \
+{ \
+ if (messageBoxCommand != KMessageBox::Continue) \
+ { \
+ return false; \
+ } \
+}
+
+ const int lossyType = saveOptions.isLossyForSaving (pixmap);
+ if (lossyType & (kpDocumentSaveOptions::MimeTypeMaximumColorDepthLow |
+ kpDocumentSaveOptions::Quality))
+ {
+ QUIT_IF_CANCEL (
+ KMessageBox::warningContinueCancel (parent,
+ i18n ("<qt><p>The <b>%1</b> format may not be able"
+ " to preserve all of the image's color information.</p>"
+
+ "<p>Are you sure you want to save in this format?</p></qt>")
+ .arg (KMimeType::mimeType (saveOptions.mimeType ())->comment ()),
+ // TODO: caption misleading for lossless formats that have
+ // low maximum colour depth
+ i18n ("Lossy File Format"),
+ KStdGuiItem::save (),
+ QString::fromLatin1 ("SaveInLossyMimeTypeDontAskAgain")));
+ }
+ else if (lossyType & kpDocumentSaveOptions::ColorDepthLow)
+ {
+ QUIT_IF_CANCEL (
+ KMessageBox::warningContinueCancel (parent,
+ i18n ("<qt><p>Saving the image at the low color depth of %1-bit"
+ " may result in the loss of color information."
+
+ " Any transparency will also be removed.</p>"
+
+ "<p>Are you sure you want to save at this color depth?</p></qt>")
+ .arg (saveOptions.colorDepth ()),
+ i18n ("Low Color Depth"),
+ KStdGuiItem::save (),
+ QString::fromLatin1 ("SaveAtLowColorDepthDontAskAgain")));
+ }
+#undef QUIT_IF_CANCEL
+
+ return true;
+}
+
+// public static
+bool kpDocument::savePixmapToDevice (const QPixmap &pixmap,
+ QIODevice *device,
+ const kpDocumentSaveOptions &saveOptions,
+ const kpDocumentMetaInfo &metaInfo,
+ bool lossyPrompt,
+ QWidget *parent,
+ bool *userCancelled)
+{
+ if (userCancelled)
+ *userCancelled = false;
+
+ QString type = KImageIO::typeForMime (saveOptions.mimeType ());
+#if DEBUG_KP_DOCUMENT
+ kdDebug () << "\tmimeType=" << saveOptions.mimeType ()
+ << " type=" << type << endl;
+#endif
+
+ if (lossyPrompt && !lossyPromptContinue (pixmap, saveOptions, parent))
+ {
+ if (userCancelled)
+ *userCancelled = true;
+
+ #if DEBUG_KP_DOCUMENT
+ kdDebug () << "\treturning false because of lossyPrompt" << endl;
+ #endif
+ return false;
+ }
+
+
+ QPixmap pixmapToSave =
+ kpPixmapFX::pixmapWithDefinedTransparentPixels (pixmap,
+ Qt::white); // CONFIG
+ QImage imageToSave = kpPixmapFX::convertToImage (pixmapToSave);
+
+
+ // TODO: fix dup with kpDocumentSaveOptions::isLossyForSaving()
+ const bool useSaveOptionsColorDepth =
+ (saveOptions.mimeTypeHasConfigurableColorDepth () &&
+ !saveOptions.colorDepthIsInvalid ());
+ const bool useSaveOptionsQuality =
+ (saveOptions.mimeTypeHasConfigurableQuality () &&
+ !saveOptions.qualityIsInvalid ());
+
+
+ //
+ // Reduce colors if required
+ //
+
+ if (useSaveOptionsColorDepth &&
+ imageToSave.depth () != saveOptions.colorDepth ())
+ {
+ imageToSave = ::convertImageDepth (imageToSave,
+ saveOptions.colorDepth (),
+ saveOptions.dither ());
+ }
+
+
+ //
+ // Write Meta Info
+ //
+
+ imageToSave.setDotsPerMeterX (metaInfo.dotsPerMeterX ());
+ imageToSave.setDotsPerMeterY (metaInfo.dotsPerMeterY ());
+ imageToSave.setOffset (metaInfo.offset ());
+
+ QValueList <QImageTextKeyLang> keyList = metaInfo.textList ();
+ for (QValueList <QImageTextKeyLang>::const_iterator it = keyList.begin ();
+ it != keyList.end ();
+ it++)
+ {
+ imageToSave.setText ((*it).key, (*it).lang, metaInfo.text (*it));
+ }
+
+
+ //
+ // Save at required quality
+ //
+
+ int quality = -1; // default
+
+ if (useSaveOptionsQuality)
+ quality = saveOptions.quality ();
+
+ if (!imageToSave.save (device, type.latin1 (), quality))
+ {
+ #if DEBUG_KP_DOCUMENT
+ kdDebug () << "\tQImage::save() returned false" << endl;
+ #endif
+ return false;
+ }
+
+
+#if DEBUG_KP_DOCUMENT
+ kdDebug () << "\tsave OK" << endl;
+#endif
+ return true;
+}
+
+static void CouldNotCreateTemporaryFileDialog (QWidget *parent)
+{
+ KMessageBox::error (parent,
+ i18n ("Could not save image - unable to create temporary file."));
+}
+
+static void CouldNotSaveDialog (const KURL &url, QWidget *parent)
+{
+ // TODO: use file.errorString()
+ KMessageBox::error (parent,
+ i18n ("Could not save as \"%1\".")
+ .arg (kpDocument::prettyFilenameForURL (url)));
+}
+
+// public static
+bool kpDocument::savePixmapToFile (const QPixmap &pixmap,
+ const KURL &url,
+ const kpDocumentSaveOptions &saveOptions,
+ const kpDocumentMetaInfo &metaInfo,
+ bool overwritePrompt,
+ bool lossyPrompt,
+ QWidget *parent)
+{
+ // TODO: Use KIO::NetAccess:mostLocalURL() for accessing home:/ (and other
+ // such local URLs) for efficiency and because only local writes
+ // are atomic.
+#if DEBUG_KP_DOCUMENT
+ kdDebug () << "kpDocument::savePixmapToFile ("
+ << url
+ << ",overwritePrompt=" << overwritePrompt
+ << ",lossyPrompt=" << lossyPrompt
+ << ")" << endl;
+ saveOptions.printDebug (QString::fromLatin1 ("\tsaveOptions"));
+ metaInfo.printDebug (QString::fromLatin1 ("\tmetaInfo"));
+#endif
+
+ if (overwritePrompt && KIO::NetAccess::exists (url, false/*write*/, parent))
+ {
+ int result = KMessageBox::warningContinueCancel (parent,
+ i18n ("A document called \"%1\" already exists.\n"
+ "Do you want to overwrite it?")
+ .arg (prettyFilenameForURL (url)),
+ QString::null,
+ i18n ("Overwrite"));
+
+ if (result != KMessageBox::Continue)
+ {
+ #if DEBUG_KP_DOCUMENT
+ kdDebug () << "\tuser doesn't want to overwrite" << endl;
+ #endif
+
+ return false;
+ }
+ }
+
+
+ if (lossyPrompt && !lossyPromptContinue (pixmap, saveOptions, parent))
+ {
+ #if DEBUG_KP_DOCUMENT
+ kdDebug () << "\treturning false because of lossyPrompt" << endl;
+ #endif
+ return false;
+ }
+
+
+ // Local file?
+ if (url.isLocalFile ())
+ {
+ const QString filename = url.path ();
+
+ // sync: All failure exit paths _must_ call KSaveFile::abort() or
+ // else, the KSaveFile destructor will overwrite the file,
+ // <filename>, despite the failure.
+ KSaveFile atomicFileWriter (filename);
+ {
+ if (atomicFileWriter.status () != 0)
+ {
+ // We probably don't need this as <filename> has not been
+ // opened.
+ atomicFileWriter.abort ();
+
+ #if DEBUG_KP_DOCUMENT
+ kdDebug () << "\treturning false because could not open KSaveFile"
+ << " status=" << atomicFileWriter.status () << endl;
+ #endif
+ ::CouldNotCreateTemporaryFileDialog (parent);
+ return false;
+ }
+
+ // Write to local temporary file.
+ if (!savePixmapToDevice (pixmap, atomicFileWriter.file (),
+ saveOptions, metaInfo,
+ false/*no lossy prompt*/,
+ parent))
+ {
+ atomicFileWriter.abort ();
+
+ #if DEBUG_KP_DOCUMENT
+ kdDebug () << "\treturning false because could not save pixmap to device"
+ << endl;
+ #endif
+ ::CouldNotSaveDialog (url, parent);
+ return false;
+ }
+
+ // Atomically overwrite local file with the temporary file
+ // we saved to.
+ if (!atomicFileWriter.close ())
+ {
+ atomicFileWriter.abort ();
+
+ #if DEBUG_KP_DOCUMENT
+ kdDebug () << "\tcould not close KSaveFile" << endl;
+ #endif
+ ::CouldNotSaveDialog (url, parent);
+ return false;
+ }
+ } // sync KSaveFile.abort()
+ }
+ // Remote file?
+ else
+ {
+ // Create temporary file that is deleted when the variable goes
+ // out of scope.
+ KTempFile tempFile;
+ tempFile.setAutoDelete (true);
+
+ QString filename = tempFile.name ();
+ if (filename.isEmpty ())
+ {
+ #if DEBUG_KP_DOCUMENT
+ kdDebug () << "\treturning false because tempFile empty" << endl;
+ #endif
+ ::CouldNotCreateTemporaryFileDialog (parent);
+ return false;
+ }
+
+ // Write to local temporary file.
+ QFile file (filename);
+ {
+ if (!file.open (IO_WriteOnly))
+ {
+ #if DEBUG_KP_DOCUMENT
+ kdDebug () << "\treturning false because can't open file"
+ << " errorString=" << file.errorString () << endl;
+ #endif
+ ::CouldNotCreateTemporaryFileDialog (parent);
+ return false;
+ }
+
+ if (!savePixmapToDevice (pixmap, &file,
+ saveOptions, metaInfo,
+ false/*no lossy prompt*/,
+ parent))
+ {
+ #if DEBUG_KP_DOCUMENT
+ kdDebug () << "\treturning false because could not save pixmap to device"
+ << endl;
+ #endif
+ ::CouldNotSaveDialog (url, parent);
+ return false;
+ }
+ }
+ file.close ();
+ if (file.status () != IO_Ok)
+ {
+ #if DEBUG_KP_DOCUMENT
+ kdDebug () << "\treturning false because could not close" << endl;
+ #endif
+ ::CouldNotSaveDialog (url, parent);
+ return false;
+ }
+
+ // Copy local temporary file to overwrite remote.
+ // TODO: No one seems to know how to do this atomically
+ // [http://lists.kde.org/?l=kde-core-devel&m=117845162728484&w=2].
+ // At least, fish:// (ssh) is definitely not atomic.
+ if (!KIO::NetAccess::upload (filename, url, parent))
+ {
+ #if DEBUG_KP_DOCUMENT
+ kdDebug () << "\treturning false because could not upload" << endl;
+ #endif
+ KMessageBox::error (parent,
+ i18n ("Could not save image - failed to upload."));
+ return false;
+ }
+ }
+
+
+ return true;
+}
+
+bool kpDocument::saveAs (const KURL &url,
+ const kpDocumentSaveOptions &saveOptions,
+ bool overwritePrompt,
+ bool lossyPrompt)
+{
+#if DEBUG_KP_DOCUMENT
+ kdDebug () << "kpDocument::saveAs (" << url << ","
+ << saveOptions.mimeType () << ")" << endl;
+#endif
+
+ if (kpDocument::savePixmapToFile (pixmapWithSelection (),
+ url,
+ saveOptions, *metaInfo (),
+ overwritePrompt,
+ lossyPrompt,
+ m_mainWindow))
+ {
+ setURL (url, true/*is from url*/);
+ *m_saveOptions = saveOptions;
+ m_modified = false;
+
+ m_savedAtLeastOnceBefore = true;
+
+ emit documentSaved ();
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+// public
+bool kpDocument::savedAtLeastOnceBefore () const
+{
+ return m_savedAtLeastOnceBefore;
+}
+
+// public
+KURL kpDocument::url () const
+{
+ return m_url;
+}
+
+// public
+void kpDocument::setURL (const KURL &url, bool isFromURL)
+{
+ m_url = url;
+ m_isFromURL = isFromURL;
+}
+
+// public
+bool kpDocument::isFromURL (bool checkURLStillExists) const
+{
+ if (!m_isFromURL)
+ return false;
+
+ if (!checkURLStillExists)
+ return true;
+
+ return (!m_url.isEmpty () &&
+ KIO::NetAccess::exists (m_url, true/*open*/, m_mainWindow));
+}
+
+
+// static
+QString kpDocument::prettyURLForURL (const KURL &url)
+{
+ if (url.isEmpty ())
+ return i18n ("Untitled");
+ else
+ return url.prettyURL (0, KURL::StripFileProtocol);
+}
+
+QString kpDocument::prettyURL () const
+{
+ return prettyURLForURL (m_url);
+}
+
+
+// static
+QString kpDocument::prettyFilenameForURL (const KURL &url)
+{
+ if (url.isEmpty ())
+ return i18n ("Untitled");
+ else if (url.fileName ().isEmpty ())
+ return prettyURLForURL (url); // better than the name ""
+ else
+ return url.fileName ();
+}
+
+QString kpDocument::prettyFilename () const
+{
+ return prettyFilenameForURL (m_url);
+}
+
+
+// public
+const kpDocumentSaveOptions *kpDocument::saveOptions () const
+{
+ return m_saveOptions;
+}
+
+// public
+void kpDocument::setSaveOptions (const kpDocumentSaveOptions &saveOptions)
+{
+ *m_saveOptions = saveOptions;
+}
+
+
+// public
+const kpDocumentMetaInfo *kpDocument::metaInfo () const
+{
+ return m_metaInfo;
+}
+
+// public
+void kpDocument::setMetaInfo (const kpDocumentMetaInfo &metaInfo)
+{
+ *m_metaInfo = metaInfo;
+}
+
+
+/*
+ * Properties
+ */
+
+void kpDocument::setModified (bool yes)
+{
+ if (yes == m_modified)
+ return;
+
+ m_modified = yes;
+
+ if (yes)
+ emit documentModified ();
+}
+
+bool kpDocument::isModified () const
+{
+ return m_modified;
+}
+
+bool kpDocument::isEmpty () const
+{
+ return url ().isEmpty () && !isModified ();
+}
+
+
+int kpDocument::constructorWidth () const
+{
+ return m_constructorWidth;
+}
+
+int kpDocument::width (bool ofSelection) const
+{
+ if (ofSelection && m_selection)
+ return m_selection->width ();
+ else
+ return m_pixmap->width ();
+}
+
+int kpDocument::oldWidth () const
+{
+ return m_oldWidth;
+}
+
+void kpDocument::setWidth (int w, const kpColor &backgroundColor)
+{
+ resize (w, height (), backgroundColor);
+}
+
+
+int kpDocument::constructorHeight () const
+{
+ return m_constructorHeight;
+}
+
+int kpDocument::height (bool ofSelection) const
+{
+ if (ofSelection && m_selection)
+ return m_selection->height ();
+ else
+ return m_pixmap->height ();
+}
+
+int kpDocument::oldHeight () const
+{
+ return m_oldHeight;
+}
+
+void kpDocument::setHeight (int h, const kpColor &backgroundColor)
+{
+ resize (width (), h, backgroundColor);
+}
+
+QRect kpDocument::rect (bool ofSelection) const
+{
+ if (ofSelection && m_selection)
+ return m_selection->boundingRect ();
+ else
+ return m_pixmap->rect ();
+}
+
+
+/*
+ * Pixmap access
+ */
+
+// public
+QPixmap kpDocument::getPixmapAt (const QRect &rect) const
+{
+ return kpPixmapFX::getPixmapAt (*m_pixmap, rect);
+}
+
+// public
+void kpDocument::setPixmapAt (const QPixmap &pixmap, const QPoint &at)
+{
+#if DEBUG_KP_DOCUMENT && 0
+ kdDebug () << "kpDocument::setPixmapAt (pixmap (w="
+ << pixmap.width ()
+ << ",h=" << pixmap.height ()
+ << "), x=" << at.x ()
+ << ",y=" << at.y ()
+ << endl;
+#endif
+
+ kpPixmapFX::setPixmapAt (m_pixmap, at, pixmap);
+ slotContentsChanged (QRect (at.x (), at.y (), pixmap.width (), pixmap.height ()));
+}
+
+// public
+void kpDocument::paintPixmapAt (const QPixmap &pixmap, const QPoint &at)
+{
+ kpPixmapFX::paintPixmapAt (m_pixmap, at, pixmap);
+ slotContentsChanged (QRect (at.x (), at.y (), pixmap.width (), pixmap.height ()));
+}
+
+
+// public
+QPixmap *kpDocument::pixmap (bool ofSelection) const
+{
+ if (ofSelection)
+ {
+ if (m_selection && m_selection->pixmap ())
+ return m_selection->pixmap ();
+ else
+ return 0;
+ }
+ else
+ return m_pixmap;
+}
+
+// public
+void kpDocument::setPixmap (const QPixmap &pixmap)
+{
+ m_oldWidth = width (), m_oldHeight = height ();
+
+ *m_pixmap = pixmap;
+
+ if (m_oldWidth == width () && m_oldHeight == height ())
+ slotContentsChanged (pixmap.rect ());
+ else
+ slotSizeChanged (width (), height ());
+}
+
+// public
+void kpDocument::setPixmap (bool ofSelection, const QPixmap &pixmap)
+{
+ if (ofSelection)
+ {
+ if (!m_selection)
+ {
+ kdError () << "kpDocument::setPixmap(ofSelection=true) without sel" << endl;
+ return;
+ }
+
+ m_selection->setPixmap (pixmap);
+ }
+ else
+ setPixmap (pixmap);
+}
+
+
+// private
+void kpDocument::updateToolsSingleKeyTriggersEnabled ()
+{
+ if (m_mainWindow)
+ {
+ // Disable single key shortcuts when the user is editing text
+ m_mainWindow->enableActionsSingleKeyTriggers (!m_selection || !m_selection->isText ());
+ }
+}
+
+
+// public
+kpSelection *kpDocument::selection () const
+{
+ return m_selection;
+}
+
+// public
+void kpDocument::setSelection (const kpSelection &selection)
+{
+#if DEBUG_KP_DOCUMENT && 0
+ kdDebug () << "kpDocument::setSelection() sel boundingRect="
+ << selection.boundingRect ()
+ << endl;
+#endif
+
+ kpViewManager *vm = m_mainWindow ? m_mainWindow->viewManager () : 0;
+ if (vm)
+ vm->setQueueUpdates ();
+
+ bool hadSelection = (bool) m_selection;
+
+
+ const bool isTextChanged = (m_mainWindow->toolIsTextTool () !=
+ (selection.type () == kpSelection::Text));
+
+ // We don't change the Selection Tool if the new selection's
+ // shape is merely different to the current tool's (e.g. rectangular
+ // vs elliptical) because:
+ //
+ // 1. All image selection tools support editing selections of all the
+ // different shapes anyway.
+ // 2. Suppose the user is trying out different drags of selection borders
+ // and then decides to paste a differently shaped selection before continuing
+ // to try out different borders. If the pasting were to switch to
+ // a differently shaped tool, the borders drawn after the paste would
+ // be using a new shape rather than the shape before the paste. This
+ // could get irritating so we don't do the switch.
+ //
+ if (m_mainWindow &&
+ (!m_mainWindow->toolIsASelectionTool () || isTextChanged))
+ {
+ // Switch to the appropriately shaped selection tool
+ // _before_ we change the selection
+ // (all selection tool's ::end() functions nuke the current selection)
+ switch (selection.type ())
+ {
+ case kpSelection::Rectangle:
+ m_mainWindow->slotToolRectSelection ();
+ break;
+ case kpSelection::Ellipse:
+ m_mainWindow->slotToolEllipticalSelection ();
+ break;
+ case kpSelection::Points:
+ m_mainWindow->slotToolFreeFormSelection ();
+ break;
+ case kpSelection::Text:
+ m_mainWindow->slotToolText ();
+ break;
+ default:
+ break;
+ }
+ }
+
+
+ if (m_selection)
+ {
+ // TODO: Emitting this, before setting the new selection, is bogus
+ // since it would redraw the old selection.
+ //
+ // Luckily, this doesn't matter thanks to the
+ // kpViewManager::setQueueUpdates() call above.
+ if (m_selection->pixmap ())
+ slotContentsChanged (m_selection->boundingRect ());
+ else
+ // TODO: Should emit contentsChanged() instead?
+ // I don't think it matters since contentsChanged() is
+ // connected to updateViews() anyway (see
+ // kpMainWindow::setDocument ()).
+ vm->updateViews (m_selection->boundingRect ());
+
+ delete m_selection;
+ }
+
+ m_selection = new kpSelection (selection);
+
+ // TODO: this coupling is bad, careless and lazy
+ if (m_mainWindow)
+ {
+ if (!m_selection->isText ())
+ {
+ if (m_selection->transparency () != m_mainWindow->selectionTransparency ())
+ {
+ kdDebug () << "kpDocument::setSelection() sel's transparency differs "
+ "from mainWindow's transparency - setting mainWindow's transparency "
+ "to sel"
+ << endl;
+ kdDebug () << "\tisOpaque: sel=" << m_selection->transparency ().isOpaque ()
+ << " mainWindow=" << m_mainWindow->selectionTransparency ().isOpaque ()
+ << endl;
+ m_mainWindow->setSelectionTransparency (m_selection->transparency ());
+ }
+ }
+ else
+ {
+ if (m_selection->textStyle () != m_mainWindow->textStyle ())
+ {
+ kdDebug () << "kpDocument::setSelection() sel's textStyle differs "
+ "from mainWindow's textStyle - setting mainWindow's textStyle "
+ "to sel"
+ << endl;
+ m_mainWindow->setTextStyle (m_selection->textStyle ());
+ }
+ }
+ }
+
+ updateToolsSingleKeyTriggersEnabled ();
+
+#if DEBUG_KP_DOCUMENT && 0
+ kdDebug () << "\tcheck sel " << (int *) m_selection
+ << " boundingRect=" << m_selection->boundingRect ()
+ << endl;
+#endif
+ if (m_selection->pixmap ())
+ slotContentsChanged (m_selection->boundingRect ());
+ else
+ // TODO: Should emit contentsChanged() instead?
+ // I don't think it matters since contentsChanged() is
+ // connected to updateViews() anyway (see
+ // kpMainWindow::setDocument ()).
+ vm->updateViews (m_selection->boundingRect ());
+
+ // There's no need to disconnect() the old selection since we:
+ //
+ // 1. Connect our _copy_ of the given selection.
+ // 2. We delete our copy when setSelection() is called again.
+ //
+ // See code above for both.
+ connect (m_selection, SIGNAL (changed (const QRect &)),
+ this, SLOT (slotContentsChanged (const QRect &)));
+
+
+ if (!hadSelection)
+ emit selectionEnabled (true);
+
+ if (isTextChanged)
+ emit selectionIsTextChanged (selection.type () == kpSelection::Text);
+
+ if (vm)
+ vm->restoreQueueUpdates ();
+}
+
+// public
+QPixmap kpDocument::getSelectedPixmap (const QBitmap &maskBitmap_) const
+{
+ kpSelection *sel = selection ();
+
+ // must have a selection region
+ if (!sel)
+ {
+ kdError () << "kpDocument::getSelectedPixmap() no sel region" << endl;
+ return QPixmap ();
+ }
+
+ // easy if we already have it :)
+ if (sel->pixmap ())
+ return *sel->pixmap ();
+
+
+ const QRect boundingRect = sel->boundingRect ();
+ if (!boundingRect.isValid ())
+ {
+ kdError () << "kpDocument::getSelectedPixmap() boundingRect invalid" << endl;
+ return QPixmap ();
+ }
+
+
+ QBitmap maskBitmap = maskBitmap_;
+ if (maskBitmap.isNull () &&
+ !sel->isRectangular ())
+ {
+ maskBitmap = sel->maskForOwnType ();
+
+ if (maskBitmap.isNull ())
+ {
+ kdError () << "kpDocument::getSelectedPixmap() could not get mask" << endl;
+ return QPixmap ();
+ }
+ }
+
+
+ QPixmap selPixmap = getPixmapAt (boundingRect);
+
+ if (!maskBitmap.isNull ())
+ {
+ // Src Dest = Result
+ // -----------------
+ // 0 0 0
+ // 0 1 0
+ // 1 0 0
+ // 1 1 1
+ QBitmap selMaskBitmap = kpPixmapFX::getNonNullMask (selPixmap);
+ bitBlt (&selMaskBitmap,
+ QPoint (0, 0),
+ &maskBitmap,
+ QRect (0, 0, maskBitmap.width (), maskBitmap.height ()),
+ Qt::AndROP);
+ selPixmap.setMask (selMaskBitmap);
+ }
+
+ return selPixmap;
+}
+
+// public
+bool kpDocument::selectionPullFromDocument (const kpColor &backgroundColor)
+{
+ kpViewManager *vm = m_mainWindow ? m_mainWindow->viewManager () : 0;
+
+ kpSelection *sel = selection ();
+
+ // must have a selection region
+ if (!sel)
+ {
+ kdError () << "kpDocument::selectionPullFromDocument() no sel region" << endl;
+ return false;
+ }
+
+ // should not already have a pixmap
+ if (sel->pixmap ())
+ {
+ kdError () << "kpDocument::selectionPullFromDocument() already has pixmap" << endl;
+ return false;
+ }
+
+ const QRect boundingRect = sel->boundingRect ();
+ if (!boundingRect.isValid ())
+ {
+ kdError () << "kpDocument::selectionPullFromDocument() boundingRect invalid" << endl;
+ return false;
+ }
+
+
+ //
+ // Figure out mask for non-rectangular selections
+ //
+
+ QBitmap maskBitmap = sel->maskForOwnType (true/*return null bitmap for rectangular*/);
+
+
+ //
+ // Get selection pixmap from document
+ //
+
+ QPixmap selPixmap = getSelectedPixmap (maskBitmap);
+
+ if (vm)
+ vm->setQueueUpdates ();
+
+ sel->setPixmap (selPixmap);
+
+
+ //
+ // Fill opaque bits of the hole in the document
+ //
+
+ // TODO: this assumes backgroundColor == sel->transparency ().transparentColor()
+ const QPixmap selTransparentPixmap = sel->transparentPixmap ();
+
+ if (backgroundColor.isOpaque ())
+ {
+ QPixmap erasePixmap (boundingRect.width (), boundingRect.height ());
+ erasePixmap.fill (backgroundColor.toQColor ());
+
+ if (selTransparentPixmap.mask ())
+ erasePixmap.setMask (*selTransparentPixmap.mask ());
+
+ paintPixmapAt (erasePixmap, boundingRect.topLeft ());
+ }
+ else
+ {
+ kpPixmapFX::paintMaskTransparentWithBrush (m_pixmap,
+ boundingRect.topLeft (),
+ kpPixmapFX::getNonNullMask (selTransparentPixmap));
+ slotContentsChanged (boundingRect);
+ }
+
+ if (vm)
+ vm->restoreQueueUpdates ();
+
+ return true;
+}
+
+// public
+bool kpDocument::selectionDelete ()
+{
+ kpSelection *sel = selection ();
+
+ if (!sel)
+ return false;
+
+ const QRect boundingRect = sel->boundingRect ();
+ if (!boundingRect.isValid ())
+ return false;
+
+ bool selectionHadPixmap = m_selection ? (bool) m_selection->pixmap () : false;
+
+ delete m_selection;
+ m_selection = 0;
+
+
+ // HACK to prevent document from being modified when
+ // user cancels dragging out a new selection
+ if (selectionHadPixmap)
+ slotContentsChanged (boundingRect);
+ else
+ emit contentsChanged (boundingRect);
+
+ emit selectionEnabled (false);
+
+
+ updateToolsSingleKeyTriggersEnabled ();
+
+ return true;
+}
+
+// public
+bool kpDocument::selectionCopyOntoDocument (bool useTransparentPixmap)
+{
+ kpSelection *sel = selection ();
+
+ // must have a pixmap already
+ if (!sel)
+ return false;
+
+ // hasn't actually been lifted yet
+ if (!sel->pixmap ())
+ return true;
+
+ const QRect boundingRect = sel->boundingRect ();
+ if (!boundingRect.isValid ())
+ return false;
+
+ if (!sel->isText ())
+ {
+ // We can't use kpSelection::paint() since that always uses the
+ // transparent pixmap.
+ paintPixmapAt (useTransparentPixmap ? sel->transparentPixmap () : sel->opaquePixmap (),
+ boundingRect.topLeft ());
+ }
+ else
+ {
+ // (for antialiasing with background)
+ sel->paint (m_pixmap, rect ());
+ }
+
+ slotContentsChanged (boundingRect);
+
+ return true;
+}
+
+// public
+bool kpDocument::selectionPushOntoDocument (bool useTransparentPixmap)
+{
+ return (selectionCopyOntoDocument (useTransparentPixmap) && selectionDelete ());
+}
+
+// public
+QPixmap kpDocument::pixmapWithSelection () const
+{
+#if DEBUG_KP_DOCUMENT && 1
+ kdDebug () << "kpDocument::pixmapWithSelection()" << endl;
+#endif
+
+ // Have floating selection?
+ if (m_selection && m_selection->pixmap ())
+ {
+ #if DEBUG_KP_DOCUMENT && 1
+ kdDebug () << "\tselection @ " << m_selection->boundingRect () << endl;
+ #endif
+ QPixmap output = *m_pixmap;
+
+ m_selection->paint (&output, rect ());
+
+ return output;
+ }
+ else
+ {
+ #if DEBUG_KP_DOCUMENT && 1
+ kdDebug () << "\tno selection" << endl;
+ #endif
+ return *m_pixmap;
+ }
+}
+
+
+/*
+ * Transformations
+ */
+
+void kpDocument::fill (const kpColor &color)
+{
+#if DEBUG_KP_DOCUMENT
+ kdDebug () << "kpDocument::fill ()" << endl;
+#endif
+
+ kpPixmapFX::fill (m_pixmap, color);
+ slotContentsChanged (m_pixmap->rect ());
+}
+
+void kpDocument::resize (int w, int h, const kpColor &backgroundColor, bool fillNewAreas)
+{
+#if DEBUG_KP_DOCUMENT
+ kdDebug () << "kpDocument::resize (" << w << "," << h << "," << fillNewAreas << ")" << endl;
+#endif
+
+ m_oldWidth = width (), m_oldHeight = height ();
+
+#if DEBUG_KP_DOCUMENT && 1
+ kdDebug () << "\toldWidth=" << m_oldWidth
+ << " oldHeight=" << m_oldHeight
+ << endl;
+#endif
+
+ if (w == m_oldWidth && h == m_oldHeight)
+ return;
+
+ kpPixmapFX::resize (m_pixmap, w, h, backgroundColor, fillNewAreas);
+
+ slotSizeChanged (width (), height ());
+}
+
+
+/*
+ * Slots
+ */
+
+void kpDocument::slotContentsChanged (const QRect &rect)
+{
+ setModified ();
+ emit contentsChanged (rect);
+}
+
+void kpDocument::slotSizeChanged (int newWidth, int newHeight)
+{
+ setModified ();
+ emit sizeChanged (newWidth, newHeight);
+ emit sizeChanged (QSize (newWidth, newHeight));
+}
+
+void kpDocument::slotSizeChanged (const QSize &newSize)
+{
+ slotSizeChanged (newSize.width (), newSize.height ());
+}
+
+#include <kpdocument.moc>
diff --git a/kolourpaint/kpdocument.h b/kolourpaint/kpdocument.h
new file mode 100644
index 00000000..d75e36ff
--- /dev/null
+++ b/kolourpaint/kpdocument.h
@@ -0,0 +1,260 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef KP_DOCUMENT_H
+#define KP_DOCUMENT_H
+
+#include <qbitmap.h>
+#include <qobject.h>
+#include <qstring.h>
+
+#include <kurl.h>
+
+#include <kppixmapfx.h>
+
+
+class QImage;
+class QIODevice;
+class QPixmap;
+class QPoint;
+class QRect;
+class QSize;
+
+class kpColor;
+class kpDocumentSaveOptions;
+class kpDocumentMetaInfo;
+class kpMainWindow;
+class kpSelection;
+
+
+class kpDocument : public QObject
+{
+Q_OBJECT
+
+public:
+ kpDocument (int w, int h, kpMainWindow *mainWindow);
+ ~kpDocument ();
+
+ kpMainWindow *mainWindow () const;
+ void setMainWindow (kpMainWindow *mainWindow);
+
+
+ /*
+ * File I/O
+ */
+
+ // Wraps kpPixmapFX::convertToPixmapAsLosslessAsPossible() but also
+ // returns document meta information.
+ static QPixmap convertToPixmapAsLosslessAsPossible (
+ const QImage &image,
+ const kpPixmapFX::WarnAboutLossInfo &wali = kpPixmapFX::WarnAboutLossInfo (),
+ kpDocumentSaveOptions *saveOptions = 0,
+ kpDocumentMetaInfo *metaInfo = 0);
+
+ static QPixmap getPixmapFromFile (const KURL &url, bool suppressDoesntExistDialog,
+ QWidget *parent,
+ kpDocumentSaveOptions *saveOptions = 0,
+ kpDocumentMetaInfo *metaInfo = 0);
+ // TODO: fix: open*() should only be called once.
+ // Create a new kpDocument() if you want to open again.
+ void openNew (const KURL &url);
+ bool open (const KURL &url, bool newDocSameNameIfNotExist = false);
+
+ static bool lossyPromptContinue (const QPixmap &pixmap,
+ const kpDocumentSaveOptions &saveOptions,
+ QWidget *parent);
+ static bool savePixmapToDevice (const QPixmap &pixmap,
+ QIODevice *device,
+ const kpDocumentSaveOptions &saveOptions,
+ const kpDocumentMetaInfo &metaInfo,
+ bool lossyPrompt,
+ QWidget *parent,
+ bool *userCancelled = 0);
+ static bool savePixmapToFile (const QPixmap &pixmap,
+ const KURL &url,
+ const kpDocumentSaveOptions &saveOptions,
+ const kpDocumentMetaInfo &metaInfo,
+ bool overwritePrompt,
+ bool lossyPrompt,
+ QWidget *parent);
+ bool save (bool overwritePrompt = false, bool lossyPrompt = false);
+ bool saveAs (const KURL &url,
+ const kpDocumentSaveOptions &saveOptions,
+ bool overwritePrompt = true,
+ bool lossyPrompt = true);
+
+ // Returns whether save() or saveAs() have ever been called and returned true
+ bool savedAtLeastOnceBefore () const;
+
+ KURL url () const;
+ void setURL (const KURL &url, bool isFromURL);
+
+ // Returns whether the document's pixmap was successfully opened from
+ // or saved to the URL returned by url(). This is not true for a
+ // new kpDocument and in the case of open() being passed
+ // "newDocSameNameIfNotExist = true" when the URL doesn't exist.
+ //
+ // If this returns true and the kpDocument hasn't been modified,
+ // this gives a pretty good indication that the pixmap stored at url()
+ // is equal to pixmap() (unless the something has happened to that url
+ // outside of KolourPaint).
+ bool isFromURL (bool checkURLStillExists = true) const;
+
+ // (will convert: empty URL --> "Untitled")
+ static QString prettyURLForURL (const KURL &url);
+ QString prettyURL () const;
+
+ // (will convert: empty URL --> "Untitled")
+ static QString prettyFilenameForURL (const KURL &url);
+ QString prettyFilename () const;
+
+ // (guaranteed to return valid pointer)
+
+ const kpDocumentSaveOptions *saveOptions () const;
+ void setSaveOptions (const kpDocumentSaveOptions &saveOptions);
+
+ const kpDocumentMetaInfo *metaInfo () const;
+ void setMetaInfo (const kpDocumentMetaInfo &metaInfo);
+
+
+ /*
+ * Properties (modified, width, height, color depth...)
+ */
+
+ void setModified (bool yes = true);
+ bool isModified () const;
+ bool isEmpty () const;
+
+ int constructorWidth () const; // as passed to the constructor
+ int width (bool ofSelection = false) const;
+ int oldWidth () const; // only valid in a slot connected to sizeChanged()
+ void setWidth (int w, const kpColor &backgroundColor);
+
+ int constructorHeight () const; // as passed to the constructor
+ int height (bool ofSelection = false) const;
+ int oldHeight () const; // only valid in a slot connected to sizeChanged()
+ void setHeight (int h, const kpColor &backgroundColor);
+
+ QRect rect (bool ofSelection = false) const;
+
+
+ /*
+ * Pixmap access
+ */
+
+ // get a copy of a bit of the doc's pixmap
+ // (not including the selection)
+ QPixmap getPixmapAt (const QRect &rect) const;
+
+ void setPixmapAt (const QPixmap &pixmap, const QPoint &at);
+
+ void paintPixmapAt (const QPixmap &pixmap, const QPoint &at);
+
+ // (not including the selection)
+ QPixmap *pixmap (bool ofSelection = false) const;
+ void setPixmap (const QPixmap &pixmap);
+ void setPixmap (bool ofSelection, const QPixmap &pixmap);
+
+private:
+ void updateToolsSingleKeyTriggersEnabled ();
+
+public:
+ kpSelection *selection () const;
+ void setSelection (const kpSelection &selection);
+
+ // TODO: this always returns opaque pixmap - need transparent ver
+ QPixmap getSelectedPixmap (const QBitmap &maskBitmap = QBitmap ()) const;
+
+ bool selectionPullFromDocument (const kpColor &backgroundColor);
+ bool selectionDelete ();
+ bool selectionCopyOntoDocument (bool useTransparentPixmap = true);
+ bool selectionPushOntoDocument (bool useTransparentPixmap = true);
+
+ // same as pixmap() but returns a _copy_ of the current pixmap
+ // + any selection pasted on top
+ QPixmap pixmapWithSelection () const;
+
+
+ /*
+ * Transformations
+ * (convenience only - you could achieve the same effect (and more) with
+ * kpPixmapFX: these functions do not affect the selection)
+ */
+
+ void fill (const kpColor &color);
+ void resize (int w, int h, const kpColor &backgroundColor, bool fillNewAreas = true);
+
+
+public slots:
+ // these will emit signals!
+ void slotContentsChanged (const QRect &rect);
+ void slotSizeChanged (int newWidth, int newHeight);
+ void slotSizeChanged (const QSize &newSize);
+
+signals:
+ void documentOpened ();
+ void documentSaved ();
+
+ // Emitted whenever the isModified() flag changes from false to true.
+ // This is the _only_ signal that may be emitted in addition to the others.
+ void documentModified ();
+
+ void contentsChanged (const QRect &rect);
+ void sizeChanged (int newWidth, int newHeight); // see oldWidth(), oldHeight()
+ void sizeChanged (const QSize &newSize);
+
+ void selectionEnabled (bool on);
+
+ // HACK: until we support Text Selection -> Rectangular Selection for Image ops
+ void selectionIsTextChanged (bool isText);
+
+private:
+ int m_constructorWidth, m_constructorHeight;
+ kpMainWindow *m_mainWindow;
+ QPixmap *m_pixmap;
+
+ KURL m_url;
+ bool m_isFromURL;
+ bool m_savedAtLeastOnceBefore;
+
+ kpDocumentSaveOptions *m_saveOptions;
+ kpDocumentMetaInfo *m_metaInfo;
+
+ bool m_modified;
+
+ kpSelection *m_selection;
+
+ int m_oldWidth, m_oldHeight;
+
+ // There is no need to maintain binary compatibility at this stage.
+ // The d-pointer is just so that you can experiment without recompiling
+ // the kitchen sink.
+ class kpDocumentPrivate *d;
+};
+
+#endif // KP_DOCUMENT_H
diff --git a/kolourpaint/kpdocumentmetainfo.cpp b/kolourpaint/kpdocumentmetainfo.cpp
new file mode 100644
index 00000000..5e5fc6ae
--- /dev/null
+++ b/kolourpaint/kpdocumentmetainfo.cpp
@@ -0,0 +1,186 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <kpdocumentmetainfo.h>
+
+#include <qpoint.h>
+
+#include <kdebug.h>
+
+
+struct kpDocumentMetaInfoPrivate
+{
+ int m_dotsPerMeterX, m_dotsPerMeterY;
+ QPoint m_offset;
+
+ QMap <QImageTextKeyLang, QString> m_textMap;
+};
+
+
+// public
+kpDocumentMetaInfo::kpDocumentMetaInfo ()
+ : d (new kpDocumentMetaInfoPrivate ())
+{
+ d->m_dotsPerMeterX = 0;
+ d->m_dotsPerMeterY = 0;
+ d->m_offset = QPoint (0, 0);
+}
+
+kpDocumentMetaInfo::kpDocumentMetaInfo (const kpDocumentMetaInfo &rhs)
+ : d (new kpDocumentMetaInfoPrivate ())
+{
+ d->m_dotsPerMeterX = rhs.dotsPerMeterX ();
+ d->m_dotsPerMeterY = rhs.dotsPerMeterY ();
+ d->m_offset = rhs.offset ();
+ d->m_textMap = rhs.textMap ();
+}
+
+// public
+kpDocumentMetaInfo::~kpDocumentMetaInfo ()
+{
+ delete d;
+}
+
+
+// public
+kpDocumentMetaInfo &kpDocumentMetaInfo::operator= (const kpDocumentMetaInfo &rhs)
+{
+ d->m_dotsPerMeterX = rhs.dotsPerMeterX ();
+ d->m_dotsPerMeterY = rhs.dotsPerMeterY ();
+ d->m_offset = rhs.offset ();
+ d->m_textMap = rhs.textMap ();
+
+ return *this;
+}
+
+
+// public
+void kpDocumentMetaInfo::printDebug (const QString &prefix) const
+{
+ const QString usedPrefix = !prefix.isEmpty () ?
+ prefix + QString::fromLatin1 (":") :
+ QString::null;
+
+ kdDebug () << usedPrefix << endl;
+
+ kdDebug () << "dotsPerMeter X=" << dotsPerMeterX ()
+ << " Y=" << dotsPerMeterY ()
+ << " offset=" << offset () << endl;
+
+ QValueList <QImageTextKeyLang> keyList = textList ();
+ for (QValueList <QImageTextKeyLang>::const_iterator it = keyList.begin ();
+ it != keyList.end ();
+ it++)
+ {
+ kdDebug () << "key=" << (*it).key
+ << " lang=" << (*it).lang
+ << " text=" << text (*it)
+ << endl;
+ }
+
+ kdDebug () << usedPrefix << "ENDS" << endl;
+}
+
+
+// public
+int kpDocumentMetaInfo::dotsPerMeterX () const
+{
+ return d->m_dotsPerMeterX;
+}
+
+// public
+void kpDocumentMetaInfo::setDotsPerMeterX (int val)
+{
+ d->m_dotsPerMeterX = val;
+}
+
+
+// public
+int kpDocumentMetaInfo::dotsPerMeterY () const
+{
+ return d->m_dotsPerMeterY;
+}
+
+// public
+void kpDocumentMetaInfo::setDotsPerMeterY (int val)
+{
+ d->m_dotsPerMeterY = val;
+}
+
+
+// public
+QPoint kpDocumentMetaInfo::offset () const
+{
+ return d->m_offset;
+}
+
+// public
+void kpDocumentMetaInfo::setOffset (const QPoint &point)
+{
+ d->m_offset = point;
+}
+
+
+// public
+QMap <QImageTextKeyLang, QString> kpDocumentMetaInfo::textMap () const
+{
+ return d->m_textMap;
+}
+
+// public
+QValueList <QImageTextKeyLang> kpDocumentMetaInfo::textList () const
+{
+ return d->m_textMap.keys ();
+}
+
+
+// public
+QString kpDocumentMetaInfo::text (const QImageTextKeyLang &itkl) const
+{
+ return d->m_textMap [itkl];
+}
+
+// public
+QString kpDocumentMetaInfo::text (const char *key, const char *lang) const
+{
+ return text (QImageTextKeyLang (key, lang));
+}
+
+
+// public
+void kpDocumentMetaInfo::setText (const QImageTextKeyLang &itkl,
+ const QString &string)
+{
+ d->m_textMap [itkl] = string;
+}
+
+// public
+void kpDocumentMetaInfo::setText (const char *key, const char *lang,
+ const QString &string)
+{
+ setText (QImageTextKeyLang (key, lang), string);
+}
diff --git a/kolourpaint/kpdocumentmetainfo.h b/kolourpaint/kpdocumentmetainfo.h
new file mode 100644
index 00000000..15e1408f
--- /dev/null
+++ b/kolourpaint/kpdocumentmetainfo.h
@@ -0,0 +1,90 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef KP_DOCUMENT_META_INFO
+#define KP_DOCUMENT_META_INFO
+
+
+#include <qimage.h>
+#include <qmap.h>
+#include <qstring.h>
+#include <qvaluelist.h>
+
+
+class QPoint;
+
+
+class kpDocumentMetaInfo
+{
+public:
+ kpDocumentMetaInfo ();
+ kpDocumentMetaInfo (const kpDocumentMetaInfo &rhs);
+ virtual ~kpDocumentMetaInfo ();
+
+private:
+ bool operator== (const kpDocumentMetaInfo &rhs) const;
+ bool operator!= (const kpDocumentMetaInfo &rhs) const;
+
+public:
+ kpDocumentMetaInfo &operator= (const kpDocumentMetaInfo &rhs);
+
+
+ void printDebug (const QString &prefix) const;
+
+
+ // See QImage documentation
+
+ int dotsPerMeterX () const;
+ void setDotsPerMeterX (int val);
+
+ int dotsPerMeterY () const;
+ void setDotsPerMeterY (int val);
+
+
+ QPoint offset () const;
+ void setOffset (const QPoint &point);
+
+
+ QMap <QImageTextKeyLang, QString> textMap () const;
+ QValueList <QImageTextKeyLang> textList () const;
+
+ QString text (const QImageTextKeyLang &itkl) const;
+ QString text (const char *key, const char *lang) const;
+ void setText (const QImageTextKeyLang &itkl, const QString &string);
+ void setText (const char *key, const char *lang, const QString &string);
+
+
+private:
+ // There is no need to maintain binary compatibility at this stage.
+ // The d-pointer is just so that you can experiment without recompiling
+ // the kitchen sink.
+ class kpDocumentMetaInfoPrivate *d;
+};
+
+
+#endif // KP_DOCUMENT_META_INFO
diff --git a/kolourpaint/kpdocumentsaveoptions.cpp b/kolourpaint/kpdocumentsaveoptions.cpp
new file mode 100644
index 00000000..701b6b51
--- /dev/null
+++ b/kolourpaint/kpdocumentsaveoptions.cpp
@@ -0,0 +1,561 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG_KP_DOCUMENT_SAVE_OPTIONS 0
+
+
+#include <kpdocumentsaveoptions.h>
+
+#include <qpixmap.h>
+#include <qstring.h>
+
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kglobal.h>
+
+#include <kpdefs.h>
+
+
+struct kpDocumentSaveOptionsPrivate
+{
+ QString m_mimeType;
+ int m_colorDepth;
+ bool m_dither;
+ int m_quality;
+};
+
+
+kpDocumentSaveOptions::kpDocumentSaveOptions ()
+ : d (new kpDocumentSaveOptionsPrivate ())
+{
+ d->m_mimeType = invalidMimeType ();
+ d->m_colorDepth = invalidColorDepth ();
+ d->m_dither = initialDither ();
+ d->m_quality = invalidQuality ();
+}
+
+kpDocumentSaveOptions::kpDocumentSaveOptions (const kpDocumentSaveOptions &rhs)
+ : d (new kpDocumentSaveOptionsPrivate ())
+{
+ d->m_mimeType = rhs.mimeType ();
+ d->m_colorDepth = rhs.colorDepth ();
+ d->m_dither = rhs.dither ();
+ d->m_quality = rhs.quality ();
+}
+
+kpDocumentSaveOptions::kpDocumentSaveOptions (QString mimeType, int colorDepth, bool dither, int quality)
+ : d (new kpDocumentSaveOptionsPrivate ())
+{
+ d->m_mimeType = mimeType;
+ d->m_colorDepth = colorDepth;
+ d->m_dither = dither;
+ d->m_quality = quality;
+}
+
+kpDocumentSaveOptions::~kpDocumentSaveOptions ()
+{
+ delete d;
+}
+
+
+// public
+bool kpDocumentSaveOptions::operator== (const kpDocumentSaveOptions &rhs) const
+{
+ return (mimeType () == rhs.mimeType () &&
+ colorDepth () == rhs.colorDepth () &&
+ dither () == rhs.dither () &&
+ quality () == rhs.quality ());
+}
+
+// public
+bool kpDocumentSaveOptions::operator!= (const kpDocumentSaveOptions &rhs) const
+{
+ return !(*this == rhs);
+}
+
+
+// public
+kpDocumentSaveOptions &kpDocumentSaveOptions::operator= (const kpDocumentSaveOptions &rhs)
+{
+ setMimeType (rhs.mimeType ());
+ setColorDepth (rhs.colorDepth ());
+ setDither (rhs.dither ());
+ setQuality (rhs.quality ());
+
+ return *this;
+}
+
+
+// public
+void kpDocumentSaveOptions::printDebug (const QString &prefix) const
+{
+ const QString usedPrefix = !prefix.isEmpty () ?
+ prefix + QString::fromLatin1 (": ") :
+ QString::null;
+
+ kdDebug () << usedPrefix
+ << "mimeType=" << mimeType ()
+ << " colorDepth=" << colorDepth ()
+ << " dither=" << dither ()
+ << " quality=" << quality ()
+ << endl;
+}
+
+
+// public
+QString kpDocumentSaveOptions::mimeType () const
+{
+ return d->m_mimeType;
+}
+
+// public
+void kpDocumentSaveOptions::setMimeType (const QString &mimeType)
+{
+ d->m_mimeType = mimeType;
+}
+
+
+// public static
+QString kpDocumentSaveOptions::invalidMimeType ()
+{
+ return QString::null;
+}
+
+// public static
+bool kpDocumentSaveOptions::mimeTypeIsInvalid (const QString &mimeType)
+{
+ return (mimeType == invalidMimeType ());
+}
+
+// public
+bool kpDocumentSaveOptions::mimeTypeIsInvalid () const
+{
+ return mimeTypeIsInvalid (mimeType ());
+}
+
+
+// public
+int kpDocumentSaveOptions::colorDepth () const
+{
+ return d->m_colorDepth;
+}
+
+// public
+void kpDocumentSaveOptions::setColorDepth (int depth)
+{
+ d->m_colorDepth = depth;
+}
+
+
+// public static
+int kpDocumentSaveOptions::invalidColorDepth ()
+{
+ return -1;
+}
+
+// public static
+bool kpDocumentSaveOptions::colorDepthIsInvalid (int colorDepth)
+{
+ return (colorDepth != 1 && colorDepth != 8 && colorDepth != 32);
+}
+
+// public
+bool kpDocumentSaveOptions::colorDepthIsInvalid () const
+{
+ return colorDepthIsInvalid (colorDepth ());
+}
+
+
+// public
+bool kpDocumentSaveOptions::dither () const
+{
+ return d->m_dither;
+}
+
+// public
+void kpDocumentSaveOptions::setDither (bool dither)
+{
+ d->m_dither = dither;
+}
+
+
+// public static
+int kpDocumentSaveOptions::initialDither ()
+{
+ return false; // to avoid accidental double dithering
+}
+
+
+// public
+int kpDocumentSaveOptions::quality () const
+{
+ return d->m_quality;
+}
+
+// public
+void kpDocumentSaveOptions::setQuality (int quality)
+{
+ d->m_quality = quality;
+}
+
+
+// public static
+int kpDocumentSaveOptions::invalidQuality ()
+{
+ return -2;
+}
+
+// public static
+bool kpDocumentSaveOptions::qualityIsInvalid (int quality)
+{
+ return (quality < -1 || quality > 100);
+}
+
+// public
+bool kpDocumentSaveOptions::qualityIsInvalid () const
+{
+ return qualityIsInvalid (quality ());
+}
+
+
+// public static
+QString kpDocumentSaveOptions::defaultMimeType (KConfigBase *config)
+{
+ return config->readEntry (kpSettingForcedMimeType,
+ QString::fromLatin1 ("image/png"));
+}
+
+// public static
+void kpDocumentSaveOptions::saveDefaultMimeType (KConfigBase *config,
+ const QString &mimeType)
+{
+ config->writeEntry (kpSettingForcedMimeType, mimeType);
+}
+
+
+// public static
+int kpDocumentSaveOptions::defaultColorDepth (KConfigBase *config)
+{
+ int colorDepth =
+ config->readNumEntry (kpSettingForcedColorDepth, -1);
+
+ if (colorDepthIsInvalid (colorDepth))
+ {
+ // (not screen depth, in case of transparency)
+ colorDepth = 32;
+ }
+
+ return colorDepth;
+}
+
+// public static
+void kpDocumentSaveOptions::saveDefaultColorDepth (KConfigBase *config, int colorDepth)
+{
+ config->writeEntry (kpSettingForcedColorDepth, colorDepth);
+}
+
+
+// public static
+int kpDocumentSaveOptions::defaultDither (KConfigBase *config)
+{
+ return config->readBoolEntry (kpSettingForcedDither, initialDither ());
+}
+
+// public static
+void kpDocumentSaveOptions::saveDefaultDither (KConfigBase *config, bool dither)
+{
+ config->writeEntry (kpSettingForcedDither, dither);
+}
+
+
+// public static
+int kpDocumentSaveOptions::defaultQuality (KConfigBase *config)
+{
+ int val = config->readNumEntry (kpSettingForcedQuality, -1);
+ if (qualityIsInvalid (val))
+ val = -1;
+
+ return val;
+}
+
+// public static
+void kpDocumentSaveOptions::saveDefaultQuality (KConfigBase *config, int quality)
+{
+ config->writeEntry (kpSettingForcedQuality, quality);
+}
+
+
+// public static
+kpDocumentSaveOptions kpDocumentSaveOptions::defaultDocumentSaveOptions (KConfigBase *config)
+{
+ kpDocumentSaveOptions saveOptions;
+ saveOptions.setMimeType (defaultMimeType (config));
+ saveOptions.setColorDepth (defaultColorDepth (config));
+ saveOptions.setDither (defaultDither (config));
+ saveOptions.setQuality (defaultQuality (config));
+
+#if DEBUG_KP_DOCUMENT_SAVE_OPTIONS
+ saveOptions.printDebug ("kpDocumentSaveOptions::defaultDocumentSaveOptions()");
+#endif
+
+ return saveOptions;
+}
+
+// public static
+bool kpDocumentSaveOptions::saveDefaultDifferences (KConfigBase *config,
+ const kpDocumentSaveOptions &oldDocInfo,
+ const kpDocumentSaveOptions &newDocInfo)
+{
+ bool savedSomething = false;
+
+#if DEBUG_KP_DOCUMENT_SAVE_OPTIONS
+ kdDebug () << "kpDocumentSaveOptions::saveDefaultDifferences()" << endl;
+ oldDocInfo.printDebug ("\told");
+ newDocInfo.printDebug ("\tnew");
+#endif
+
+ if (newDocInfo.mimeType () != oldDocInfo.mimeType ())
+ {
+ saveDefaultMimeType (config, newDocInfo.mimeType ());
+ savedSomething = true;
+ }
+
+ if (newDocInfo.colorDepth () != oldDocInfo.colorDepth ())
+ {
+ saveDefaultColorDepth (config, newDocInfo.colorDepth ());
+ savedSomething = true;
+ }
+
+ if (newDocInfo.dither () != oldDocInfo.dither ())
+ {
+ saveDefaultDither (config, newDocInfo.dither ());
+ savedSomething = true;
+ }
+
+ if (newDocInfo.quality () != oldDocInfo.quality ())
+ {
+ saveDefaultQuality (config, newDocInfo.quality ());
+ savedSomething = true;
+ }
+
+ return savedSomething;
+}
+
+
+static QStringList mimeTypesSupportingProperty (const QString &property,
+ const QStringList &defaultMimeTypesWithPropertyList)
+{
+ QStringList mimeTypeList;
+
+ KConfigGroupSaver cfgGroupSaver (KGlobal::config (),
+ kpSettingsGroupMimeTypeProperties);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ if (cfg->hasKey (property))
+ {
+ mimeTypeList = cfg->readListEntry (property);
+ }
+ else
+ {
+ mimeTypeList = defaultMimeTypesWithPropertyList;
+
+ cfg->writeEntry (property, mimeTypeList);
+ cfg->sync ();
+ }
+
+ return mimeTypeList;
+}
+
+static bool mimeTypeSupportsProperty (const QString &mimeType,
+ const QString &property, const QStringList &defaultMimeTypesWithPropertyList)
+{
+ const QStringList mimeTypeList = mimeTypesSupportingProperty (
+ property, defaultMimeTypesWithPropertyList);
+
+ return mimeTypeList.contains (mimeType);
+}
+
+
+// SYNC: update mime info
+//
+// Only care about writable mimetypes.
+//
+// Run "branches/kolourpaint/control/scripts/gen_mimetype_line.sh Write" in
+// the version of kdelibs/kimgio/ (e.g. KDE 3.5) KolourPaint is shipped with,
+// to check for any new mimetypes to add info for. In the methods below,
+// you can specify this info (maximum color depth, whether it's lossy etc.).
+//
+// Update the below list also and bump up "kpSettingsGroupMimeTypeProperties"
+// in kpdefs.h.
+//
+// Currently, Depth and Quality settings are mutually exclusive with
+// Depth overriding Quality. I've currently favoured Quality with the
+// below mimetypes (i.e. all lossy mimetypes are only given Quality settings,
+// no Depth settings).
+//
+// Mimetypes done:
+// image/jp2 [UNTESTED]
+// image/jpeg
+// image/png
+// image/x-bmp
+// image/x-eps
+// image/x-pcx
+// image/x-portable-bitmap
+// image/x-portable-greymap
+// image/x-portable-pixmap
+// image/x-rgb
+// image/x-targa
+// image/x-xbm
+// image/x-xpm
+//
+// To test whether depth is configurable, write an image in the new
+// mimetype with all depths and read each one back. See what
+// kpDocument thinks the depth is when it gets QImage to read it.
+
+
+// public static
+int kpDocumentSaveOptions::mimeTypeMaximumColorDepth (const QString &mimeType)
+{
+ QStringList defaultList;
+
+ // SYNC: update mime info here
+
+ // Greyscale actually (unenforced since depth not set to configurable)
+ defaultList << QString::fromLatin1 ("image/x-eps:32");
+
+ defaultList << QString::fromLatin1 ("image/x-portable-bitmap:1");
+
+ // Greyscale actually (unenforced since depth not set to configurable)
+ defaultList << QString::fromLatin1 ("image/x-portable-greymap:8");
+
+ defaultList << QString::fromLatin1 ("image/x-xbm:1");
+
+ const QStringList mimeTypeList = mimeTypesSupportingProperty (
+ kpSettingMimeTypeMaximumColorDepth, defaultList);
+
+ const QString mimeTypeColon = mimeType + QString::fromLatin1 (":");
+ for (QStringList::const_iterator it = mimeTypeList.begin ();
+ it != mimeTypeList.end ();
+ it++)
+ {
+ if ((*it).startsWith (mimeTypeColon))
+ {
+ int number = (*it).mid (mimeTypeColon.length ()).toInt ();
+ if (!colorDepthIsInvalid (number))
+ {
+ return number;
+ }
+ }
+ }
+
+ return 32;
+}
+
+// public
+int kpDocumentSaveOptions::mimeTypeMaximumColorDepth () const
+{
+ return mimeTypeMaximumColorDepth (mimeType ());
+}
+
+
+// public static
+bool kpDocumentSaveOptions::mimeTypeHasConfigurableColorDepth (const QString &mimeType)
+{
+ QStringList defaultMimeTypes;
+
+ // SYNC: update mime info here
+ defaultMimeTypes << QString::fromLatin1 ("image/png");
+ defaultMimeTypes << QString::fromLatin1 ("image/x-bmp");
+ defaultMimeTypes << QString::fromLatin1 ("image/x-pcx");
+
+ // TODO: Only 1, 24 not 8; Qt only sees 32 but "file" cmd realises
+ // it's either 1 or 24.
+ defaultMimeTypes << QString::fromLatin1 ("image/x-rgb");
+
+ // TODO: Only 8 and 24 - no 1.
+ defaultMimeTypes << QString::fromLatin1 ("image/x-xpm");
+
+ return mimeTypeSupportsProperty (mimeType,
+ kpSettingMimeTypeHasConfigurableColorDepth,
+ defaultMimeTypes);
+}
+
+// public
+bool kpDocumentSaveOptions::mimeTypeHasConfigurableColorDepth () const
+{
+ return mimeTypeHasConfigurableColorDepth (mimeType ());
+}
+
+
+// public static
+bool kpDocumentSaveOptions::mimeTypeHasConfigurableQuality (const QString &mimeType)
+{
+ QStringList defaultMimeTypes;
+
+ // SYNC: update mime info here
+ defaultMimeTypes << QString::fromLatin1 ("image/jp2");
+ defaultMimeTypes << QString::fromLatin1 ("image/jpeg");
+
+ return mimeTypeSupportsProperty (mimeType,
+ kpSettingMimeTypeHasConfigurableQuality,
+ defaultMimeTypes);
+}
+
+// public
+bool kpDocumentSaveOptions::mimeTypeHasConfigurableQuality () const
+{
+ return mimeTypeHasConfigurableQuality (mimeType ());
+}
+
+
+// public
+int kpDocumentSaveOptions::isLossyForSaving (const QPixmap &pixmap) const
+{
+ int ret = 0;
+
+ if (mimeTypeMaximumColorDepth () < pixmap.depth ())
+ {
+ ret |= MimeTypeMaximumColorDepthLow;
+ }
+
+ if (mimeTypeHasConfigurableColorDepth () &&
+ !colorDepthIsInvalid () /*TODO: prevent*/ &&
+ (colorDepth () < pixmap.depth () ||
+ colorDepth () < 32 && pixmap.mask ()))
+ {
+ ret |= ColorDepthLow;
+ }
+
+ if (mimeTypeHasConfigurableQuality () &&
+ !qualityIsInvalid ())
+ {
+ ret |= Quality;
+ }
+
+ return ret;
+}
+
diff --git a/kolourpaint/kpdocumentsaveoptions.h b/kolourpaint/kpdocumentsaveoptions.h
new file mode 100644
index 00000000..0d77ec2c
--- /dev/null
+++ b/kolourpaint/kpdocumentsaveoptions.h
@@ -0,0 +1,150 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef KP_DOCUMENT_SAVE_OPTIONS_H
+#define KP_DOCUMENT_SAVE_OPTIONS_H
+
+
+class QPixmap;
+class QString;
+
+class KConfigBase;
+
+
+class kpDocumentSaveOptions
+{
+public:
+ kpDocumentSaveOptions ();
+ kpDocumentSaveOptions (const kpDocumentSaveOptions &rhs);
+ kpDocumentSaveOptions (QString mimeType, int colorDepth, bool dither, int quality);
+ virtual ~kpDocumentSaveOptions ();
+
+ bool operator== (const kpDocumentSaveOptions &rhs) const;
+ bool operator!= (const kpDocumentSaveOptions &rhs) const;
+
+ kpDocumentSaveOptions &operator= (const kpDocumentSaveOptions &rhs);
+
+
+ void printDebug (const QString &prefix) const;
+
+
+ QString mimeType () const;
+ void setMimeType (const QString &mimeType);
+
+ static QString invalidMimeType ();
+ static bool mimeTypeIsInvalid (const QString &mimeType);
+ bool mimeTypeIsInvalid () const;
+
+
+ int colorDepth () const;
+ void setColorDepth (int depth);
+
+ static int invalidColorDepth ();
+ static bool colorDepthIsInvalid (int colorDepth);
+ bool colorDepthIsInvalid () const;
+
+
+ bool dither () const;
+ void setDither (bool dither);
+
+ static int initialDither ();
+
+
+ int quality () const;
+ void setQuality (int quality);
+
+ static int invalidQuality ();
+ static bool qualityIsInvalid (int quality);
+ bool qualityIsInvalid () const;
+
+
+ // (All assume that <config>'s group has been set)
+ // (None of them call KConfigBase::reparseConfig() nor KConfigBase::sync())
+
+ static QString defaultMimeType (KConfigBase *config);
+ static void saveDefaultMimeType (KConfigBase *config, const QString &mimeType);
+
+ static int defaultColorDepth (KConfigBase *config);
+ static void saveDefaultColorDepth (KConfigBase *config, int colorDepth);
+
+ static int defaultDither (KConfigBase *config);
+ static void saveDefaultDither (KConfigBase *config, bool dither);
+
+ static int defaultQuality (KConfigBase *config);
+ static void saveDefaultQuality (KConfigBase *config, int quality);
+
+
+ static kpDocumentSaveOptions defaultDocumentSaveOptions (KConfigBase *config);
+ // (returns true if it encountered a difference (and saved it to <config>))
+ static bool saveDefaultDifferences (KConfigBase *config,
+ const kpDocumentSaveOptions &oldDocInfo,
+ const kpDocumentSaveOptions &newDocInfo);
+
+
+public:
+ // (purely for informational purposes - not enforced by this class)
+ static int mimeTypeMaximumColorDepth (const QString &mimeType);
+ int mimeTypeMaximumColorDepth () const;
+
+
+ static bool mimeTypeHasConfigurableColorDepth (const QString &mimeType);
+ bool mimeTypeHasConfigurableColorDepth () const;
+
+ static bool mimeTypeHasConfigurableQuality (const QString &mimeType);
+ bool mimeTypeHasConfigurableQuality () const;
+
+
+ // TODO: checking for mask loss due to format e.g. BMP
+ enum LossyType
+ {
+ LossLess = 0,
+
+ // mimeTypeMaximumColorDepth() < <pixmap>.depth()
+ MimeTypeMaximumColorDepthLow = 1,
+ // i.e. colorDepth() < <pixmap>.depth() ||
+ // colorDepth() < 32 && <pixmap>.mask()
+ ColorDepthLow = 2,
+ // i.e. mimeTypeHasConfigurableQuality()
+ Quality = 4
+ };
+
+ // Returns whether saving <pixmap> with these options will result in
+ // loss of information. Returned value is the bitwise OR of
+ // LossType enum possiblities.
+ int isLossyForSaving (const QPixmap &pixmap) const;
+
+
+private:
+ // There is no need to maintain binary compatibility at this stage.
+ // The d-pointer is just so that you can experiment without recompiling
+ // the kitchen sink.
+ class kpDocumentSaveOptionsPrivate *d;
+};
+
+
+#endif // KP_DOCUMENT_SAVE_OPTIONS_H
diff --git a/kolourpaint/kpdocumentsaveoptionswidget.cpp b/kolourpaint/kpdocumentsaveoptionswidget.cpp
new file mode 100644
index 00000000..39edf5b8
--- /dev/null
+++ b/kolourpaint/kpdocumentsaveoptionswidget.cpp
@@ -0,0 +1,951 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET 0
+
+
+#include <kpdocumentsaveoptionswidget.h>
+
+#include <qapplication.h>
+#include <qbuffer.h>
+#include <qimage.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qtimer.h>
+
+#include <kcombobox.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kdialog.h>
+#include <kdialogbase.h>
+#include <kglobal.h>
+#include <kimageio.h>
+#include <klocale.h>
+#include <knuminput.h>
+#include <kpushbutton.h>
+
+#include <kpdefs.h>
+#include <kpdocument.h>
+#include <kppixmapfx.h>
+#include <kpresizesignallinglabel.h>
+#include <kpselection.h>
+#include <kptoolpreviewdialog.h>
+#include <kpwidgetmapper.h>
+
+
+// protected static
+const QSize kpDocumentSaveOptionsPreviewDialog::s_pixmapLabelMinimumSize (25, 25);
+
+
+kpDocumentSaveOptionsPreviewDialog::kpDocumentSaveOptionsPreviewDialog (
+ QWidget *parent,
+ const char *name)
+ : QWidget (parent, name,
+ Qt::WType_TopLevel |
+ Qt::WStyle_Customize |
+ Qt::WStyle_DialogBorder |
+ Qt::WStyle_Title),
+#if 0
+KDialogBase (parent, name, false/*non-modal*/,
+ i18n ("Save Preview"),
+ 0/*no buttons*/),
+#endif
+ m_filePixmap (0),
+ m_fileSize (0)
+{
+ setCaption (i18n ("Save Preview"));
+
+ QWidget *baseWidget = this;//new QWidget (this);
+ //setMainWidget (baseWidget);
+
+
+ QGridLayout *lay = new QGridLayout (baseWidget, 2, 1,
+ KDialog::marginHint (), KDialog::spacingHint ());
+
+ m_filePixmapLabel = new kpResizeSignallingLabel (baseWidget);
+ m_fileSizeLabel = new QLabel (baseWidget);
+
+
+ m_filePixmapLabel->setMinimumSize (s_pixmapLabelMinimumSize);
+
+
+ lay->addWidget (m_filePixmapLabel, 0, 0);
+ lay->addWidget (m_fileSizeLabel, 1, 0, Qt::AlignHCenter);
+
+
+ lay->setRowStretch (0, 1);
+
+
+ connect (m_filePixmapLabel, SIGNAL (resized ()),
+ this, SLOT (updatePixmapPreview ()));
+}
+
+kpDocumentSaveOptionsPreviewDialog::~kpDocumentSaveOptionsPreviewDialog ()
+{
+ delete m_filePixmap;
+}
+
+
+// public
+QSize kpDocumentSaveOptionsPreviewDialog::preferredMinimumSize () const
+{
+ const int contentsWidth = 180;
+ const int totalMarginsWidth = 2 * KDialog::marginHint ();
+
+ return QSize (contentsWidth + totalMarginsWidth,
+ contentsWidth * 3 / 4 + totalMarginsWidth);
+}
+
+
+// public slot
+void kpDocumentSaveOptionsPreviewDialog::setFilePixmapAndSize (const QPixmap &pixmap,
+ int fileSize)
+{
+ delete m_filePixmap;
+ m_filePixmap = new QPixmap (pixmap);
+
+ updatePixmapPreview ();
+
+ m_fileSize = fileSize;
+
+ const int pixmapSize = kpPixmapFX::pixmapSize (pixmap);
+ const int percent = pixmapSize ?
+ QMAX (1, fileSize * 100 / pixmapSize) :
+ 0;
+#if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET
+ kdDebug () << "kpDocumentSaveOptionsPreviewDialog::setFilePixmapAndSize()"
+ << " pixmapSize=" << pixmapSize
+ << " fileSize=" << fileSize
+ << " raw fileSize/pixmapSize%="
+ << (pixmapSize ? fileSize * 100 / pixmapSize : 0)
+ << endl;
+#endif
+
+ // HACK: I don't know if the percentage thing will work well and we're
+ // really close to the message freeze so provide alt. texts to choose
+ // from during the message freeze :)
+ const QString alternateText0 = i18n ("%1 bytes");
+ const QString alternateText1 = i18n ("%1 bytes (%2%)");
+ const QString alternateText2 = i18n ("%1 B");
+ const QString alternateText3 = i18n ("%1 B (%2%)");
+ const QString alternateText4 = i18n ("%1 B (approx. %2%)");
+ const QString alternateText5 = i18n ("%1B");
+ const QString alternateText6 = i18n ("%1B (%2%)");
+ const QString alternateText7 = i18n ("%1B (approx. %2%)");
+ m_fileSizeLabel->setText (i18n ("%1 bytes (approx. %2%)")
+ .arg (KGlobal::locale ()->formatLong (m_fileSize))
+ .arg (percent));
+}
+
+// public slot
+void kpDocumentSaveOptionsPreviewDialog::updatePixmapPreview ()
+{
+#if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET
+ kdDebug () << "kpDocumentSaveOptionsPreviewDialog::updatePreviewPixmap()"
+ << " filePixmapLabel.size=" << m_filePixmapLabel->size ()
+ << " filePixmap.size=" << m_filePixmap->size ()
+ << endl;
+#endif
+
+ if (m_filePixmap)
+ {
+ int maxNewWidth = QMIN (m_filePixmap->width (),
+ m_filePixmapLabel->width ()),
+ maxNewHeight = QMIN (m_filePixmap->height (),
+ m_filePixmapLabel->height ());
+
+ double keepsAspect = kpToolPreviewDialog::aspectScale (
+ maxNewWidth, maxNewHeight,
+ m_filePixmap->width (), m_filePixmap->height ());
+ #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET
+ kdDebug () << "\tmaxNewWidth=" << maxNewWidth
+ << " maxNewHeight=" << maxNewHeight
+ << " keepsAspect=" << keepsAspect
+ << endl;
+ #endif
+
+
+ const int newWidth = kpToolPreviewDialog::scaleDimension (
+ m_filePixmap->width (),
+ keepsAspect,
+ 1,
+ maxNewWidth);
+ const int newHeight = kpToolPreviewDialog::scaleDimension (
+ m_filePixmap->height (),
+ keepsAspect,
+ 1,
+ maxNewHeight);
+ #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET
+ kdDebug () << "\tnewWidth=" << newWidth
+ << " newHeight=" << newHeight
+ << endl;
+ #endif
+
+
+ QPixmap transformedPixmap =
+ kpPixmapFX::scale (*m_filePixmap,
+ newWidth, newHeight);
+
+
+ QPixmap labelPixmap (m_filePixmapLabel->width (),
+ m_filePixmapLabel->height ());
+ kpPixmapFX::fill (&labelPixmap, kpColor::transparent);
+ kpPixmapFX::setPixmapAt (&labelPixmap,
+ (labelPixmap.width () - transformedPixmap.width ()) / 2,
+ (labelPixmap.height () - transformedPixmap.height ()) / 2,
+ transformedPixmap);
+
+
+ m_filePixmapLabel->setPixmap (labelPixmap);
+ }
+ else
+ {
+ m_filePixmapLabel->setPixmap (QPixmap ());
+ }
+}
+
+
+// protected virtual [base QWidget]
+void kpDocumentSaveOptionsPreviewDialog::closeEvent (QCloseEvent *e)
+{
+#if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET
+ kdDebug () << "kpDocumentSaveOptionsPreviewDialog::closeEvent()" << endl;
+#endif
+
+ QWidget::closeEvent (e);
+
+ emit finished ();
+}
+
+// protected virtual [base QWidget]
+void kpDocumentSaveOptionsPreviewDialog::moveEvent (QMoveEvent *e)
+{
+#if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET
+ kdDebug () << "kpDocumentSaveOptionsPreviewDialog::moveEvent()" << endl;
+#endif
+
+ QWidget::moveEvent (e);
+
+ emit moved ();
+}
+
+// protected virtual [base QWidget]
+void kpDocumentSaveOptionsPreviewDialog::resizeEvent (QResizeEvent *e)
+{
+#if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET
+ kdDebug () << "kpDocumentSaveOptionsPreviewDialog::resizeEvent()" << endl;
+#endif
+
+ QWidget::resizeEvent (e);
+
+ emit resized ();
+}
+
+
+kpDocumentSaveOptionsWidget::kpDocumentSaveOptionsWidget (
+ const QPixmap &docPixmap,
+ const kpDocumentSaveOptions &saveOptions,
+ const kpDocumentMetaInfo &metaInfo,
+ QWidget *parent, const char *name)
+ : QWidget (parent, name),
+ m_visualParent (parent)
+{
+ init ();
+ setDocumentSaveOptions (saveOptions);
+ setDocumentPixmap (docPixmap);
+ setDocumentMetaInfo (metaInfo);
+}
+
+kpDocumentSaveOptionsWidget::kpDocumentSaveOptionsWidget (
+ QWidget *parent, const char *name)
+ : QWidget (parent, name),
+ m_visualParent (parent)
+{
+ init ();
+}
+
+// private
+void kpDocumentSaveOptionsWidget::init ()
+{
+ m_documentPixmap = 0;
+ m_previewDialog = 0;
+ m_visualParent = 0;
+
+
+ m_colorDepthLabel = new QLabel (i18n ("Convert &to:"), this);
+ m_colorDepthCombo = new KComboBox (this);
+
+ m_colorDepthSpaceWidget = new QWidget (this);
+
+ m_qualityLabel = new QLabel (i18n ("Quali&ty:"), this);
+ m_qualityInput = new KIntNumInput (this);
+ // Note that we set min to 1 not 0 since "0 Quality" is a bit misleading
+ // and 101 quality settings would be weird. So we lose 1 quality setting
+ // according to QImage::save().
+ // TODO: 100 quality is also misleading since that implies perfect quality.
+ m_qualityInput->setRange (1, 100, 1/*step*/, true/*slider*/);
+
+ m_previewButton = new KPushButton (i18n ("&Preview"), this);
+ m_previewButton->setToggleButton (true);
+
+
+ m_colorDepthLabel->setBuddy (m_colorDepthCombo);
+
+ m_qualityLabel->setBuddy (m_qualityInput);
+
+
+ QHBoxLayout *lay = new QHBoxLayout (this, 0/*margin*/, KDialog::spacingHint ());
+
+ lay->addWidget (m_colorDepthLabel, 0/*stretch*/, Qt::AlignLeft);
+ lay->addWidget (m_colorDepthCombo, 0/*stretch*/);
+
+ lay->addWidget (m_colorDepthSpaceWidget, 1/*stretch*/);
+
+ lay->addWidget (m_qualityLabel, 0/*stretch*/, Qt::AlignLeft);
+ lay->addWidget (m_qualityInput, 2/*stretch*/);
+
+ lay->addWidget (m_previewButton, 0/*stretch*/, Qt::AlignRight);
+
+
+ connect (m_colorDepthCombo, SIGNAL (activated (int)),
+ this, SLOT (slotColorDepthSelected ()));
+ connect (m_colorDepthCombo, SIGNAL (activated (int)),
+ this, SLOT (updatePreview ()));
+
+ connect (m_qualityInput, SIGNAL (valueChanged (int)),
+ this, SLOT (updatePreviewDelayed ()));
+
+ connect (m_previewButton, SIGNAL (toggled (bool)),
+ this, SLOT (showPreview (bool)));
+
+
+ m_updatePreviewDelay = 200/*ms*/;
+
+ m_updatePreviewTimer = new QTimer (this);
+ connect (m_updatePreviewTimer, SIGNAL (timeout ()),
+ this, SLOT (updatePreview ()));
+
+ m_updatePreviewDialogLastRelativeGeometryTimer = new QTimer (this);
+ connect (m_updatePreviewDialogLastRelativeGeometryTimer, SIGNAL (timeout ()),
+ this, SLOT (updatePreviewDialogLastRelativeGeometry ()));
+
+
+ setMode (None);
+
+ slotColorDepthSelected ();
+}
+
+kpDocumentSaveOptionsWidget::~kpDocumentSaveOptionsWidget ()
+{
+#if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET
+ kdDebug () << "kpDocumentSaveOptionsWidget::<dtor>()" << endl;
+#endif
+ hidePreview ();
+
+ delete m_documentPixmap;
+}
+
+
+// public
+void kpDocumentSaveOptionsWidget::setVisualParent (QWidget *visualParent)
+{
+#if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET
+ kdDebug () << "kpDocumentSaveOptionsWidget::setVisualParent("
+ << visualParent << ")" << endl;
+#endif
+
+ m_visualParent = visualParent;
+}
+
+
+// protected
+bool kpDocumentSaveOptionsWidget::mimeTypeHasConfigurableColorDepth () const
+{
+ return kpDocumentSaveOptions::mimeTypeHasConfigurableColorDepth (mimeType ());
+}
+
+// protected
+bool kpDocumentSaveOptionsWidget::mimeTypeHasConfigurableQuality () const
+{
+ return kpDocumentSaveOptions::mimeTypeHasConfigurableQuality (mimeType ());
+}
+
+
+// public
+QString kpDocumentSaveOptionsWidget::mimeType () const
+{
+ return m_baseDocumentSaveOptions.mimeType ();
+}
+
+// public slots
+void kpDocumentSaveOptionsWidget::setMimeType (const QString &string)
+{
+#if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET
+ kdDebug () << "kpDocumentSaveOptionsWidget::setMimeType(" << string
+ << ") maxColorDepth="
+ << kpDocumentSaveOptions::mimeTypeMaximumColorDepth (string)
+ << endl;
+#endif
+
+ const int newMimeTypeMaxDepth =
+ kpDocumentSaveOptions::mimeTypeMaximumColorDepth (string);
+
+#if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET
+ kdDebug () << "\toldMimeType=" << mimeType ()
+ << " maxColorDepth="
+ << kpDocumentSaveOptions::mimeTypeMaximumColorDepth (
+ mimeType ())
+ << endl;
+#endif
+
+ if (mimeType ().isEmpty () ||
+ kpDocumentSaveOptions::mimeTypeMaximumColorDepth (mimeType ()) !=
+ newMimeTypeMaxDepth)
+ {
+ m_colorDepthCombo->clear ();
+
+ m_colorDepthCombo->insertItem (i18n ("Monochrome"), 0);
+ m_colorDepthCombo->insertItem (i18n ("Monochrome (Dithered)"), 1);
+
+ if (newMimeTypeMaxDepth >= 8)
+ {
+ m_colorDepthCombo->insertItem (i18n ("256 Color"), 2);
+ m_colorDepthCombo->insertItem (i18n ("256 Color (Dithered)"), 3);
+ }
+
+ if (newMimeTypeMaxDepth >= 24)
+ {
+ m_colorDepthCombo->insertItem (i18n ("24-bit Color"), 4);
+ }
+
+ if (m_colorDepthComboLastSelectedItem >= 0 &&
+ m_colorDepthComboLastSelectedItem < m_colorDepthCombo->count ())
+ {
+ #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET
+ kdDebug () << "\tsetting colorDepthCombo to "
+ << m_colorDepthComboLastSelectedItem << endl;
+ #endif
+
+ m_colorDepthCombo->setCurrentItem (m_colorDepthComboLastSelectedItem);
+ }
+ else
+ {
+ #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET
+ kdDebug () << "\tsetting colorDepthCombo to max item since"
+ << " m_colorDepthComboLastSelectedItem="
+ << m_colorDepthComboLastSelectedItem
+ << " out of range" << endl;
+ #endif
+
+ m_colorDepthCombo->setCurrentItem (m_colorDepthCombo->count () - 1);
+ }
+ }
+
+
+ m_baseDocumentSaveOptions.setMimeType (string);
+
+ if (mimeTypeHasConfigurableColorDepth ())
+ setMode (ColorDepth);
+ else if (mimeTypeHasConfigurableQuality ())
+ setMode (Quality);
+ else
+ setMode (None);
+
+ updatePreview ();
+}
+
+
+// public
+int kpDocumentSaveOptionsWidget::colorDepth () const
+{
+ if (mode () & ColorDepth)
+ {
+ switch (m_colorDepthCombo->currentItem ())
+ {
+ case 0:
+ case 1:
+ return 1;
+
+ case 2:
+ case 3:
+ return 8;
+
+ case 4:
+ return 32;
+
+ default:
+ return kpDocumentSaveOptions::invalidColorDepth ();
+ }
+ }
+ else
+ {
+ return m_baseDocumentSaveOptions.colorDepth ();
+ }
+}
+
+// public
+bool kpDocumentSaveOptionsWidget::dither () const
+{
+ if (mode () & ColorDepth)
+ {
+ return (m_colorDepthCombo->currentItem () == 1 ||
+ m_colorDepthCombo->currentItem () == 3);
+ }
+ else
+ {
+ return m_baseDocumentSaveOptions.dither ();
+ }
+}
+
+// protected static
+int kpDocumentSaveOptionsWidget::colorDepthComboItemFromColorDepthAndDither (
+ int depth, bool dither)
+{
+ if (depth == 1)
+ {
+ if (!dither)
+ {
+ return 0;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+ else if (depth == 8)
+ {
+ if (!dither)
+ {
+ return 2;
+ }
+ else
+ {
+ return 3;
+ }
+ }
+ else if (depth == 32)
+ {
+ return 4;
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+// public slots
+void kpDocumentSaveOptionsWidget::setColorDepthDither (int newDepth, bool newDither)
+{
+#if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET
+ kdDebug () << "kpDocumentSaveOptionsWidget::setColorDepthDither("
+ << "depth=" << newDepth
+ << ",dither=" << newDither
+ << ")" << endl;
+#endif
+
+ m_baseDocumentSaveOptions.setColorDepth (newDepth);
+ m_baseDocumentSaveOptions.setDither (newDither);
+
+
+ const int comboItem = colorDepthComboItemFromColorDepthAndDither (
+ newDepth, newDither);
+ // TODO: Ignoring when comboItem >= m_colorDepthCombo->count() is wrong.
+ // This happens if this mimeType has configurable colour depth
+ // and an incorrect maximum colour depth (less than a QImage of
+ // this mimeType, opened by kpDocument).
+ if (comboItem >= 0 && comboItem < m_colorDepthCombo->count ())
+ m_colorDepthCombo->setCurrentItem (comboItem);
+
+
+ slotColorDepthSelected ();
+}
+
+
+// protected slot
+void kpDocumentSaveOptionsWidget::slotColorDepthSelected ()
+{
+ if (mode () & ColorDepth)
+ {
+ m_colorDepthComboLastSelectedItem = m_colorDepthCombo->currentItem ();
+ }
+ else
+ {
+ m_colorDepthComboLastSelectedItem =
+ colorDepthComboItemFromColorDepthAndDither (
+ m_baseDocumentSaveOptions.colorDepth (),
+ m_baseDocumentSaveOptions.dither ());
+ }
+
+#if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET
+ kdDebug () << "kpDocumentSaveOptionsWidget::slotColorDepthSelected()"
+ << " mode&ColorDepth=" << (mode () & ColorDepth)
+ << " colorDepthComboLastSelectedItem="
+ << m_colorDepthComboLastSelectedItem
+ << endl;
+#endif
+}
+
+
+// public
+int kpDocumentSaveOptionsWidget::quality () const
+{
+ if (mode () & Quality)
+ {
+ return m_qualityInput->value ();
+ }
+ else
+ {
+ return m_baseDocumentSaveOptions.quality ();
+ }
+}
+
+// public
+void kpDocumentSaveOptionsWidget::setQuality (int newQuality)
+{
+#if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET
+ kdDebug () << "kpDocumentSaveOptionsWidget::setQuality("
+ << newQuality << ")" << endl;
+#endif
+
+ m_baseDocumentSaveOptions.setQuality (newQuality);
+ m_qualityInput->setValue (newQuality == -1/*QImage::save() default*/ ?
+ 75 :
+ newQuality);
+}
+
+
+// public
+kpDocumentSaveOptions kpDocumentSaveOptionsWidget::documentSaveOptions () const
+{
+ return kpDocumentSaveOptions (mimeType (), colorDepth (), dither (), quality ());
+}
+
+// public
+void kpDocumentSaveOptionsWidget::setDocumentSaveOptions (
+ const kpDocumentSaveOptions &saveOptions)
+{
+ setMimeType (saveOptions.mimeType ());
+ setColorDepthDither (saveOptions.colorDepth (), saveOptions.dither ());
+ setQuality (saveOptions.quality ());
+}
+
+
+// public
+void kpDocumentSaveOptionsWidget::setDocumentPixmap (const QPixmap &documentPixmap)
+{
+ delete m_documentPixmap;
+ m_documentPixmap = new QPixmap (documentPixmap);
+
+ updatePreview ();
+}
+
+// public
+void kpDocumentSaveOptionsWidget::setDocumentMetaInfo (
+ const kpDocumentMetaInfo &metaInfo)
+{
+ m_documentMetaInfo = metaInfo;
+
+ updatePreview ();
+}
+
+
+// public
+kpDocumentSaveOptionsWidget::Mode kpDocumentSaveOptionsWidget::mode () const
+{
+ return m_mode;
+}
+
+// public
+void kpDocumentSaveOptionsWidget::setMode (Mode mode)
+{
+ m_mode = mode;
+
+
+ // If mode == None, we show still show the Color Depth widgets but disabled
+ m_colorDepthLabel->setShown (mode != Quality);
+ m_colorDepthCombo->setShown (mode != Quality);
+ m_colorDepthSpaceWidget->setShown (mode != Quality);
+
+ m_qualityLabel->setShown (mode == Quality);
+ m_qualityInput->setShown (mode == Quality);
+
+
+ m_colorDepthLabel->setEnabled (mode == ColorDepth);
+ m_colorDepthCombo->setEnabled (mode == ColorDepth);
+
+ m_qualityLabel->setEnabled (mode == Quality);
+ m_qualityInput->setEnabled (mode == Quality);
+
+
+ // SYNC: HACK: When changing between color depth and quality widgets,
+ // we change the height of "this", causing the text on the labels
+ // to move but the first instance of the text doesn't get erased.
+ // Qt bug.
+ QTimer::singleShot (0, this, SLOT (repaintLabels ()));
+}
+
+// protected slot
+void kpDocumentSaveOptionsWidget::repaintLabels ()
+{
+ if (mode () != Quality)
+ m_colorDepthLabel->repaint ();
+ if (mode () == Quality)
+ m_qualityLabel->repaint ();
+}
+
+
+// protected slot
+void kpDocumentSaveOptionsWidget::showPreview (bool yes)
+{
+#if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET
+ kdDebug () << "kpDocumentSaveOptionsWidget::showPreview(" << yes << ")"
+ << " m_previewDialog=" << bool (m_previewDialog)
+ << endl;
+#endif
+
+ if (yes == bool (m_previewDialog))
+ return;
+
+ if (!m_visualParent)
+ return;
+
+ if (yes)
+ {
+ m_previewDialog = new kpDocumentSaveOptionsPreviewDialog (m_visualParent, "previewSaveDialog");
+ updatePreview ();
+
+ connect (m_previewDialog, SIGNAL (finished ()),
+ this, SLOT (hidePreview ()));
+
+
+ KConfigGroupSaver cfgGroupSaver (KGlobal::config (), kpSettingsGroupPreviewSave);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ if (cfg->hasKey (kpSettingPreviewSaveUpdateDelay))
+ {
+ m_updatePreviewDelay = cfg->readNumEntry (kpSettingPreviewSaveUpdateDelay);
+ }
+ else
+ {
+ cfg->writeEntry (kpSettingPreviewSaveUpdateDelay, m_updatePreviewDelay);
+ cfg->sync ();
+ }
+
+ if (m_updatePreviewDelay < 0)
+ m_updatePreviewDelay = 0;
+ #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET
+ kdDebug () << "\tread cfg preview dialog update delay="
+ << m_updatePreviewDelay
+ << endl;
+ #endif
+
+
+ if (m_previewDialogLastRelativeGeometry.isEmpty ())
+ {
+ #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET
+ kdDebug () << "\tread cfg preview dialog last rel geometry" << endl;
+ #endif
+ KConfigGroupSaver cfgGroupSaver (KGlobal::config (), kpSettingsGroupPreviewSave);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ m_previewDialogLastRelativeGeometry = cfg->readRectEntry (
+ kpSettingPreviewSaveGeometry);
+ }
+
+ #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET
+ kdDebug () << "\tpreviewDialogLastRelativeGeometry="
+ << m_previewDialogLastRelativeGeometry
+ << " visualParent->rect()=" << m_visualParent->rect ()
+ << endl;
+ #endif
+
+ QRect relativeGeometry;
+ if (!m_previewDialogLastRelativeGeometry.isEmpty () &&
+ m_visualParent->rect ().intersects (m_previewDialogLastRelativeGeometry))
+ {
+ #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET
+ kdDebug () << "\tok" << endl;
+ #endif
+ relativeGeometry = m_previewDialogLastRelativeGeometry;
+ }
+ else
+ {
+ #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET
+ kdDebug () << "\t\tinvalid" << endl;
+ #endif
+ const int margin = 20;
+
+ relativeGeometry =
+ QRect (m_visualParent->width () -
+ m_previewDialog->preferredMinimumSize ().width () -
+ margin,
+ margin * 2, // Avoid folder combo
+ m_previewDialog->preferredMinimumSize ().width (),
+ m_previewDialog->preferredMinimumSize ().height ());
+ }
+
+
+ const QRect globalGeometry =
+ kpWidgetMapper::toGlobal (m_visualParent,
+ relativeGeometry);
+ #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET
+ kdDebug () << "\trelativeGeometry=" << relativeGeometry
+ << " globalGeometry=" << globalGeometry
+ << endl;
+ #endif
+
+ m_previewDialog->resize (globalGeometry.size ());
+ m_previewDialog->move (globalGeometry.topLeft ());
+
+
+ m_previewDialog->show ();
+
+
+ #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET
+ kdDebug () << "\tgeometry after show="
+ << QRect (m_previewDialog->x (), m_previewDialog->y (),
+ m_previewDialog->width (), m_previewDialog->height ())
+ << endl;
+ #endif
+
+ updatePreviewDialogLastRelativeGeometry ();
+
+ connect (m_previewDialog, SIGNAL (moved ()),
+ this, SLOT (updatePreviewDialogLastRelativeGeometry ()));
+ connect (m_previewDialog, SIGNAL (resized ()),
+ this, SLOT (updatePreviewDialogLastRelativeGeometry ()));
+
+ m_updatePreviewDialogLastRelativeGeometryTimer->start (200/*ms*/);
+ }
+ else
+ {
+ m_updatePreviewDialogLastRelativeGeometryTimer->stop ();
+
+ KConfigGroupSaver cfgGroupSaver (KGlobal::config (), kpSettingsGroupPreviewSave);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ cfg->writeEntry (kpSettingPreviewSaveGeometry, m_previewDialogLastRelativeGeometry);
+ cfg->sync ();
+
+ #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET
+ kdDebug () << "\tsaving preview geometry "
+ << m_previewDialogLastRelativeGeometry
+ << " (Qt would have us believe "
+ << kpWidgetMapper::fromGlobal (m_visualParent,
+ QRect (m_previewDialog->x (), m_previewDialog->y (),
+ m_previewDialog->width (), m_previewDialog->height ()))
+ << ")"
+ << endl;
+ #endif
+
+ m_previewDialog->deleteLater ();
+ m_previewDialog = 0;
+ }
+}
+
+// protected slot
+void kpDocumentSaveOptionsWidget::hidePreview ()
+{
+ if (m_previewButton->isOn ())
+ m_previewButton->toggle ();
+}
+
+
+// protected slot
+void kpDocumentSaveOptionsWidget::updatePreviewDelayed ()
+{
+ m_updatePreviewTimer->start (m_updatePreviewDelay, true/*single shot*/);
+}
+
+// protected slot
+void kpDocumentSaveOptionsWidget::updatePreview ()
+{
+ if (!m_previewDialog || !m_documentPixmap)
+ return;
+
+
+ m_updatePreviewTimer->stop ();
+
+
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+ QByteArray data;
+
+ QBuffer buffer (data);
+ buffer.open (IO_WriteOnly);
+ kpDocument::savePixmapToDevice (*m_documentPixmap,
+ &buffer,
+ documentSaveOptions (),
+ m_documentMetaInfo,
+ false/*no lossy prompt*/,
+ this);
+ buffer.close ();
+
+
+ QImage image;
+ image.loadFromData (data,
+ KImageIO::typeForMime (mimeType ()).latin1 ());
+
+ // TODO: merge with kpDocument::getPixmapFromFile()
+ m_previewDialog->setFilePixmapAndSize (
+ kpPixmapFX::convertToPixmapAsLosslessAsPossible (image),
+ data.size ());
+
+ QApplication::restoreOverrideCursor ();
+}
+
+// protected slot
+void kpDocumentSaveOptionsWidget::updatePreviewDialogLastRelativeGeometry ()
+{
+#if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET
+ kdDebug () << "kpDocumentSaveOptionsWidget::"
+ << "updatePreviewDialogLastRelativeGeometry()"
+ << endl;
+#endif
+
+ if (m_previewDialog && m_previewDialog->isVisible ())
+ {
+ m_previewDialogLastRelativeGeometry =
+ kpWidgetMapper::fromGlobal (m_visualParent,
+ QRect (m_previewDialog->x (), m_previewDialog->y (),
+ m_previewDialog->width (), m_previewDialog->height ()));
+ #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET
+ kdDebug () << "\tcaching pos = "
+ << m_previewDialogLastRelativeGeometry
+ << endl;
+ #endif
+ }
+ else
+ {
+ #if DEBUG_KP_DOCUMENT_SAVE_OPTIONS_WIDGET
+ kdDebug () << "\tnot visible - ignoring geometry" << endl;
+ #endif
+ }
+}
+
+
+#include <kpdocumentsaveoptionswidget.moc>
diff --git a/kolourpaint/kpdocumentsaveoptionswidget.h b/kolourpaint/kpdocumentsaveoptionswidget.h
new file mode 100644
index 00000000..50bd35aa
--- /dev/null
+++ b/kolourpaint/kpdocumentsaveoptionswidget.h
@@ -0,0 +1,200 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef KP_DOCUMENT_SAVE_OPTIONS_WIDGET_H
+#define KP_DOCUMENT_SAVE_OPTIONS_WIDGET_H
+
+
+#include <qsize.h>
+
+#include <qwidget.h>
+
+
+class QPixmap;
+class QLabel;
+
+class kpResizeSignallingLabel;
+
+
+class kpDocumentSaveOptionsPreviewDialog : public QWidget
+{
+Q_OBJECT
+
+public:
+ kpDocumentSaveOptionsPreviewDialog (QWidget *parent, const char *name = 0);
+ virtual ~kpDocumentSaveOptionsPreviewDialog ();
+
+ QSize preferredMinimumSize () const;
+
+protected:
+ static const QSize s_pixmapLabelMinimumSize;
+
+signals:
+ void moved ();
+ void resized ();
+ void finished ();
+
+public slots:
+ void setFilePixmapAndSize (const QPixmap &filePixmap, int fileSize);
+ void updatePixmapPreview ();
+
+protected:
+ virtual void closeEvent (QCloseEvent *e);
+ virtual void moveEvent (QMoveEvent *e);
+ virtual void resizeEvent (QResizeEvent *e);
+
+protected:
+ QPixmap *m_filePixmap;
+ int m_fileSize;
+
+ kpResizeSignallingLabel *m_filePixmapLabel;
+ QLabel *m_fileSizeLabel;
+};
+
+
+#include <qrect.h>
+#include <qwidget.h>
+
+#include <kpdocumentmetainfo.h>
+#include <kpdocumentsaveoptions.h>
+
+
+class QLabel;
+class QTimer;
+
+class KComboBox;
+class KIntNumInput;
+class KPushButton;
+
+
+class kpDocumentSaveOptionsWidget : public QWidget
+{
+Q_OBJECT
+
+public:
+ kpDocumentSaveOptionsWidget (const QPixmap &docPixmap,
+ const kpDocumentSaveOptions &saveOptions,
+ const kpDocumentMetaInfo &metaInfo,
+ QWidget *parent, const char *name = 0);
+ kpDocumentSaveOptionsWidget (QWidget *parent, const char *name = 0);
+private:
+ void init ();
+public:
+ virtual ~kpDocumentSaveOptionsWidget ();
+
+
+ // <visualParent> is usually the filedialog
+ void setVisualParent (QWidget *visualParent);
+
+
+protected:
+ bool mimeTypeHasConfigurableColorDepth () const;
+ bool mimeTypeHasConfigurableQuality () const;
+
+public:
+ QString mimeType () const;
+public slots:
+ void setMimeType (const QString &string);
+
+public:
+ int colorDepth () const;
+ bool dither () const;
+protected:
+ static int colorDepthComboItemFromColorDepthAndDither (int depth, bool dither);
+public slots:
+ void setColorDepthDither (int depth,
+ bool dither = kpDocumentSaveOptions::initialDither ());
+protected slots:
+ void slotColorDepthSelected ();
+
+public:
+ int quality () const;
+public slots:
+ void setQuality (int newQuality);
+
+public:
+ kpDocumentSaveOptions documentSaveOptions () const;
+public slots:
+ void setDocumentSaveOptions (const kpDocumentSaveOptions &saveOptions);
+
+
+public:
+ void setDocumentPixmap (const QPixmap &documentPixmap);
+ void setDocumentMetaInfo (const kpDocumentMetaInfo &metaInfo);
+
+
+protected:
+ enum Mode
+ {
+ // (mutually exclusive)
+ None, ColorDepth, Quality
+ };
+
+ Mode mode () const;
+ void setMode (Mode mode);
+
+protected slots:
+ void repaintLabels ();
+
+
+protected slots:
+ void showPreview (bool yes = true);
+ void hidePreview ();
+ void updatePreviewDelayed ();
+ void updatePreview ();
+ void updatePreviewDialogLastRelativeGeometry ();
+
+
+protected:
+ QWidget *m_visualParent;
+
+ Mode m_mode;
+
+ QPixmap *m_documentPixmap;
+
+ kpDocumentSaveOptions m_baseDocumentSaveOptions;
+ kpDocumentMetaInfo m_documentMetaInfo;
+
+ QLabel *m_colorDepthLabel;
+ KComboBox *m_colorDepthCombo;
+ int m_colorDepthComboLastSelectedItem;
+ QWidget *m_colorDepthSpaceWidget;
+
+ QLabel *m_qualityLabel;
+ KIntNumInput *m_qualityInput;
+
+ KPushButton *m_previewButton;
+ kpDocumentSaveOptionsPreviewDialog *m_previewDialog;
+ QRect m_previewDialogLastRelativeGeometry;
+ QTimer *m_updatePreviewTimer;
+ int m_updatePreviewDelay;
+ QTimer *m_updatePreviewDialogLastRelativeGeometryTimer;
+};
+
+
+#endif // KP_DOCUMENT_SAVE_OPTIONS_WIDGET_H
diff --git a/kolourpaint/kpmainwindow.cpp b/kolourpaint/kpmainwindow.cpp
new file mode 100644
index 00000000..9af3177b
--- /dev/null
+++ b/kolourpaint/kpmainwindow.cpp
@@ -0,0 +1,1061 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <kpmainwindow.h>
+#include <kpmainwindow_p.h>
+
+#include <qdragobject.h>
+#include <qpainter.h>
+#include <qtimer.h>
+
+#include <kactionclasses.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kurldrag.h>
+
+#include <kpcolortoolbar.h>
+#include <kpcommandhistory.h>
+#include <kpdefs.h>
+#include <kpdocument.h>
+#include <kppixmapfx.h>
+#include <kpselection.h>
+#include <kpselectiondrag.h>
+#include <kpsinglekeytriggersaction.h>
+#include <kpthumbnail.h>
+#include <kptool.h>
+#include <kptooltoolbar.h>
+#include <kpviewmanager.h>
+#include <kpviewscrollablecontainer.h>
+#include <kpwidgetmapper.h>
+#include <kpzoomedthumbnailview.h>
+#include <kpzoomedview.h>
+
+#if DEBUG_KP_MAIN_WINDOW
+ #include <qdatetime.h>
+#endif
+
+
+kpMainWindow::kpMainWindow ()
+ : KMainWindow (0/*parent*/, "mainWindow"),
+ m_isFullyConstructed (false)
+{
+ init ();
+ open (KURL (), true/*create an empty doc*/);
+
+ m_isFullyConstructed = true;
+}
+
+kpMainWindow::kpMainWindow (const KURL &url)
+ : KMainWindow (0/*parent*/, "mainWindow"),
+ m_isFullyConstructed (false)
+{
+ init ();
+ open (url, true/*create an empty doc with the same url if url !exist*/);
+
+ m_isFullyConstructed = true;
+}
+
+kpMainWindow::kpMainWindow (kpDocument *newDoc)
+ : KMainWindow (0/*parent*/, "mainWindow"),
+ m_isFullyConstructed (false)
+{
+ init ();
+ setDocument (newDoc);
+
+ m_isFullyConstructed = true;
+}
+
+
+// public
+double kpMainWindow::configColorSimilarity () const
+{
+ return m_configColorSimilarity;
+}
+
+// public
+void kpMainWindow::configSetColorSimilarity (double val)
+{
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupGeneral);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ cfg->writeEntry (kpSettingColorSimilarity, m_configColorSimilarity = val);
+ cfg->sync ();
+}
+
+
+// private
+void kpMainWindow::readGeneralSettings ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tkpMainWindow(" << name () << ")::readGeneralSettings()" << endl;
+#endif
+
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupGeneral);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ m_configFirstTime = cfg->readBoolEntry (kpSettingFirstTime, true);
+ m_configShowGrid = cfg->readBoolEntry (kpSettingShowGrid, false);
+ m_configShowPath = cfg->readBoolEntry (kpSettingShowPath, false);
+ m_configColorSimilarity = cfg->readDoubleNumEntry (kpSettingColorSimilarity, 0);
+ d->m_moreEffectsDialogLastEffect = cfg->readNumEntry (kpSettingMoreEffectsLastEffect);
+ d->m_resizeScaleDialogLastKeepAspect = cfg->readBoolEntry (kpSettingResizeScaleLastKeepAspect, false);
+
+
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tGeneral Settings: firstTime=" << m_configFirstTime
+ << " showGrid=" << m_configShowGrid
+ << " showPath=" << m_configShowPath
+ << " colorSimilarity=" << m_configColorSimilarity
+ << " moreEffectsDialogLastEffect=" << d->m_moreEffectsDialogLastEffect
+ << " resizeScaleDialogLastKeepAspect=" << d->m_resizeScaleDialogLastKeepAspect
+ << endl;
+#endif
+}
+
+// private
+void kpMainWindow::readThumbnailSettings ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tkpMainWindow(" << name () << ")::readThumbnailSettings()" << endl;
+#endif
+
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupThumbnail);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ m_configThumbnailShown = cfg->readBoolEntry (kpSettingThumbnailShown, false);
+ m_configThumbnailGeometry = cfg->readRectEntry (kpSettingThumbnailGeometry);
+ m_configZoomedThumbnail = cfg->readBoolEntry (kpSettingThumbnailZoomed, true);
+ d->m_configThumbnailShowRectangle = cfg->readBoolEntry (kpSettingThumbnailShowRectangle, true);
+
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tThumbnail Settings: shown=" << m_configThumbnailShown
+ << " geometry=" << m_configThumbnailGeometry
+ << " zoomed=" << m_configZoomedThumbnail
+ << " showRectangle=" << d->m_configThumbnailShowRectangle
+ << endl;
+#endif
+}
+
+// private
+void kpMainWindow::init ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow(" << name () << ")::init()" << endl;
+ QTime totalTime; totalTime.start ();
+ QTime time; time.start ();
+#endif
+
+ d = new kpMainWindowPrivate;
+
+ m_scrollView = 0;
+ m_mainView = 0;
+ m_thumbnail = 0;
+ m_thumbnailView = 0;
+ m_document = 0;
+ m_viewManager = 0;
+ m_colorToolBar = 0;
+ m_toolToolBar = 0;
+ m_commandHistory = 0;
+ m_statusBarCreated = false;
+ m_settingSelectionTransparency = 0;
+ m_settingTextStyle = 0;
+
+ m_docResizeToBeCompleted = false;
+
+
+ //
+ // set mainwindow properties
+ //
+
+ setMinimumSize (320, 260);
+ setAcceptDrops (true);
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tTIME: little init = " << time.restart () << "msec" << endl;
+#endif
+
+
+ //
+ // read config
+ //
+
+ // KConfig::readEntry() does not actually reread from disk, hence doesn't
+ // realise what other processes have done e.g. Settings / Show Path
+ kapp->config ()->reparseConfiguration ();
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tTIME: reparseConfig = " << time.restart () << "msec" << endl;
+#endif
+
+ readGeneralSettings ();
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tTIME: readGeneralSettings = " << time.restart () << "msec" << endl;
+#endif
+
+ readThumbnailSettings ();
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tTIME: readThumbnailSettings = " << time.restart () << "msec" << endl;
+#endif
+
+
+ //
+ // create GUI
+ //
+
+ setupActions ();
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tTIME: setupActions = " << time.restart () << "msec" << endl;
+#endif
+
+ createStatusBar ();
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tTIME: createStatusBar = " << time.restart () << "msec" << endl;
+#endif
+
+ createGUI ();
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tTIME: createGUI = " << time.restart () << "msec" << endl;
+#endif
+
+
+ //
+ // create more GUI
+ //
+
+ m_colorToolBar = new kpColorToolBar (i18n ("Color Box"), this, "Color Box");
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tTIME: new kpColorToolBar = " << time.restart () << "msec" << endl;
+#endif
+
+ createToolBox ();
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tTIME: createToolBox = " << time.restart () << "msec" << endl;
+#endif
+
+ m_scrollView = new kpViewScrollableContainer (this, "scrollView");
+ connect (m_scrollView, SIGNAL (beganDocResize ()),
+ this, SLOT (slotBeganDocResize ()));
+ connect (m_scrollView, SIGNAL (continuedDocResize (const QSize &)),
+ this, SLOT (slotContinuedDocResize (const QSize &)));
+ connect (m_scrollView, SIGNAL (cancelledDocResize ()),
+ this, SLOT (slotCancelledDocResize ()));
+ connect (m_scrollView, SIGNAL (endedDocResize (const QSize &)),
+ this, SLOT (slotEndedDocResize (const QSize &)));
+
+ connect (m_scrollView, SIGNAL (statusMessageChanged (const QString &)),
+ this, SLOT (slotDocResizeMessageChanged (const QString &)));
+
+ connect (m_scrollView, SIGNAL (contentsMoving (int, int)),
+ this, SLOT (slotScrollViewAboutToScroll ()));
+ setCentralWidget (m_scrollView);
+ m_scrollView->show ();
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tTIME: m_scrollView = " << time.restart () << "msec" << endl;
+#endif
+
+
+ //
+ // set initial pos/size of GUI
+ //
+
+ setAutoSaveSettings ();
+
+ // Put our non-XMLGUI toolbars in a sane place, the first time around
+ // (have to do this _after_ setAutoSaveSettings as that applies default
+ // (i.e. random) settings to the toolbars)
+ if (m_configFirstTime)
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tfirstTime: positioning toolbars" << endl;
+ #endif
+
+ m_toolToolBar->setBarPos (KToolBar::Left);
+ m_colorToolBar->setBarPos (KToolBar::Bottom);
+
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupGeneral);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ cfg->writeEntry (kpSettingFirstTime, m_configFirstTime = false);
+ cfg->sync ();
+ }
+
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tall done in " << totalTime.elapsed () << "msec" << endl;
+#endif
+}
+
+
+// private virtual [base KMainWindow]
+void kpMainWindow::readProperties (KConfig *cfg)
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow<" << this << ">::readProperties()" << endl;
+#endif
+
+ // No document at all?
+ if (!cfg->hasKey (kpSessionSettingDocumentUrl))
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tno url - no document" << endl;
+ #endif
+ setDocument (0);
+ }
+ // Have a document.
+ else
+ {
+ const KURL url (cfg->readEntry (kpSessionSettingDocumentUrl));
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\turl=" << url << endl;
+ #endif
+
+ const QSize notFromURLDocSize =
+ cfg->readSizeEntry (kpSessionSettingNotFromUrlDocumentSize);
+
+ // Is from URL?
+ if (notFromURLDocSize.isEmpty ())
+ {
+ // If this fails, the empty document that kpMainWindow::kpMainWindow()
+ // created is left untouched.
+ openInternal (url, defaultDocSize (),
+ false/*show error message if url !exist*/);
+ }
+ // Not from URL?
+ else
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tnot from url; doc size=" << notFromURLDocSize << endl;
+ #endif
+ // Either we have an empty URL or we have a "kolourpaint doesnotexist.png"
+ // URL. Regarding the latter case, if a file now actually exists at that
+ // URL, we do open it - ignoring notFromURLDocSize - to avoid putting
+ // the user in a situation where he might accidentally overwrite an
+ // existing file.
+ openInternal (url, notFromURLDocSize,
+ true/*create an empty doc with the same url if url !exist*/);
+ }
+ }
+
+}
+
+// private virtual [base KMainWindow]
+// WARNING: KMainWindow API Doc says "No user interaction is allowed
+// in this function!"
+void kpMainWindow::saveProperties (KConfig *cfg)
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow<" << this << ">::saveProperties()" << endl;
+#endif
+
+ // No document at all?
+ if (!m_document)
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tno url - no document" << endl;
+ #endif
+ }
+ // Have a document.
+ else
+ {
+ // Save URL in all cases:
+ //
+ // a) m_document->isFromURL()
+ // b) !m_document->isFromURL() [save size in this case]
+ // i) No URL
+ // ii) URL (from "kolourpaint doesnotexist.png")
+
+ const KURL url = m_document->url ();
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\turl=" << url << endl;
+ #endif
+ cfg->writeEntry (kpSessionSettingDocumentUrl, url.url ());
+
+ // Not from URL e.g. "kolourpaint doesnotexist.png"?
+ //
+ // Note that "kolourpaint doesexist.png" is considered to be from
+ // a URL even if it was deleted in the background (hence the
+ // "false" arg to isFromURL()). This is because the user expects
+ // it to be from a URL, so when we session restore, we pop up a
+ // "cannot find file" dialog, instead of silently creating a new,
+ // blank document.
+ if (!m_document->isFromURL (false/*don't bother checking exists*/))
+ {
+ // If we don't have a URL either:
+ //
+ // a) it was not modified - so we can use either width() or
+ // constructorWidth() (they'll be equal).
+ // b) the changes were discarded so we use the initial width,
+ // constructorWidth().
+ //
+ // Similarly for height() and constructorHeight().
+ const QSize docSize (m_document->constructorWidth (),
+ m_document->constructorHeight ());
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tnot from url; doc size=" << docSize << endl;
+ #endif
+ cfg->writeEntry (kpSessionSettingNotFromUrlDocumentSize, docSize);
+ }
+
+
+ // Local session save i.e. queryClose() was not called beforehand
+ // (see QApplication::saveState())?
+ #if 0
+ if (m_document->isModified ())
+ {
+ // TODO: Implement by saving the current image to a persistent file.
+ // We do this instead of saving/mutating the backing image file
+ // as no one expects a file save on a session save without a
+ // "do you want to save" dialog first.
+ //
+ // I don't think any KDE application implements local session saving.
+ //
+ // --- The below code does not compile but shows you want to do ---
+
+ // Create unique name for the document in this main window.
+ const KURL tempURL = homeDir +
+ "kolourpaint session " + sessionID +
+ mainWindowPtrToString + ".png";
+ // TODO: Use lossless PNG saving options.
+ kpDocumentSaveOptions pngSaveOptions;
+
+ if (kpDocument::savePixmapToFile (m_document->pixmapWithSelection (),
+ tempURL,
+ pngSaveOptions, *m_document->metaInfo (),
+ false/*no overwrite prompt*/,
+ false/*no lossy prompt*/,
+ this))
+ {
+ // readProperties() will still open kpSessionSettingDocumentUrl
+ // (as that's the expected URL) and will then add commands to:
+ //
+ // 1. Resize the document to the size of image at
+ // kpSessionSettingDocumentUnsavedContentsUrl, if the sizes
+ // differ.
+ // 2. Paste the kpSessionSettingDocumentUnsavedContentsUrl image
+ // (setting the main window's selection mode to opaque beforehand).
+ //
+ // It will then delete the file at
+ // kpSessionSettingDocumentUnsavedContentsUrl.
+ cfg->writeEntry (kpSessionSettingDocumentUnsavedContentsUrl,
+ tempURL.url ());
+ }
+ else
+ {
+ // Not much we can do - we aren't allowed to throw up a dialog.
+ }
+ }
+ #endif
+ }
+}
+
+
+kpMainWindow::~kpMainWindow ()
+{
+ m_isFullyConstructed = false;
+
+ // delete document & views
+ setDocument (0);
+
+ delete m_commandHistory; m_commandHistory = 0;
+ delete m_scrollView; m_scrollView = 0;
+
+ delete d; d = 0;
+}
+
+
+// public
+kpDocument *kpMainWindow::document () const
+{
+ return m_document;
+}
+
+// public
+kpViewManager *kpMainWindow::viewManager () const
+{
+ return m_viewManager;
+}
+
+// public
+kpColorToolBar *kpMainWindow::colorToolBar () const
+{
+ return m_colorToolBar;
+}
+
+// public
+kpToolToolBar *kpMainWindow::toolToolBar () const
+{
+ return m_toolToolBar;
+}
+
+// public
+kpCommandHistory *kpMainWindow::commandHistory () const
+{
+ return m_commandHistory;
+}
+
+
+// private
+void kpMainWindow::setupActions ()
+{
+ setupFileMenuActions ();
+ setupEditMenuActions ();
+ setupViewMenuActions ();
+ setupImageMenuActions ();
+ setupSettingsMenuActions ();
+ setupHelpMenuActions ();
+
+ setupTextToolBarActions ();
+ setupToolActions ();
+}
+
+// private
+void kpMainWindow::enableDocumentActions (bool enable)
+{
+ enableFileMenuDocumentActions (enable);
+ enableEditMenuDocumentActions (enable);
+ enableViewMenuDocumentActions (enable);
+ enableImageMenuDocumentActions (enable);
+ enableSettingsMenuDocumentActions (enable);
+ enableHelpMenuDocumentActions (enable);
+}
+
+
+// public
+bool kpMainWindow::actionsSingleKeyTriggersEnabled () const
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::actionsSingleKeyTriggersEnabled()" << endl;
+ QTime timer; timer.start ();
+#endif
+
+ if (m_toolToolBar)
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\ttime=" << timer.restart () << endl;
+ #endif
+ return m_toolToolBar->toolsSingleKeyTriggersEnabled ();
+ }
+
+ return (m_actionPrevToolOptionGroup1->singleKeyTriggersEnabled () ||
+ m_actionNextToolOptionGroup1->singleKeyTriggersEnabled () ||
+ m_actionPrevToolOptionGroup2->singleKeyTriggersEnabled () ||
+ m_actionNextToolOptionGroup2->singleKeyTriggersEnabled ());
+}
+
+// public
+void kpMainWindow::enableActionsSingleKeyTriggers (bool enable)
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::enableActionsSingleKeyTriggers("
+ << enable << ")" << endl;
+ QTime timer; timer.start ();
+#endif
+
+ if (m_toolToolBar)
+ m_toolToolBar->enableToolsSingleKeyTriggers (enable);
+
+ m_actionPrevToolOptionGroup1->enableSingleKeyTriggers (enable);
+ m_actionNextToolOptionGroup1->enableSingleKeyTriggers (enable);
+ m_actionPrevToolOptionGroup2->enableSingleKeyTriggers (enable);
+ m_actionNextToolOptionGroup2->enableSingleKeyTriggers (enable);
+
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\ttime=" << timer.restart () << endl;
+#endif
+}
+
+
+// private
+void kpMainWindow::setDocument (kpDocument *newDoc)
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::setDocument (" << newDoc << ")" << endl;
+#endif
+
+ // is it a close operation?
+ if (!newDoc)
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tdisabling actions" << endl;
+ #endif
+
+ // sync with the bit marked "sync" below
+
+ if (m_colorToolBar)
+ m_colorToolBar->setEnabled (false);
+ else
+ {
+ kdError () << "kpMainWindow::setDocument() without colorToolBar"
+ << endl;
+ }
+
+ enableTextToolBarActions (false);
+ }
+
+ // Always disable the tools.
+ // If we decide to open a new document/mainView we want
+ // kpTool::begin() to be called again e.g. in case it sets the cursor.
+ // kpViewManager won't do this because we nuke it to avoid stale state.
+ enableToolsDocumentActions (false);
+
+ if (!newDoc)
+ {
+ enableDocumentActions (false);
+ }
+
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tdestroying views" << endl;
+#endif
+
+ delete m_mainView; m_mainView = 0;
+ slotDestroyThumbnail ();
+
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tdestroying viewManager" << endl;
+#endif
+
+ // viewManager will die and so will the selection
+ m_actionCopy->setEnabled (false);
+ m_actionCut->setEnabled (false);
+ m_actionDelete->setEnabled (false);
+ m_actionDeselect->setEnabled (false);
+ m_actionCopyToFile->setEnabled (false);
+
+ delete m_viewManager; m_viewManager = 0;
+
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tdestroying document" << endl;
+ kdDebug () << "\t\tm_document=" << m_document << endl;
+#endif
+ // destroy current document
+ delete m_document;
+ m_document = newDoc;
+
+
+ if (!m_lastCopyToURL.isEmpty ())
+ m_lastCopyToURL.setFileName (QString::null);
+ m_copyToFirstTime = true;
+
+ if (!m_lastExportURL.isEmpty ())
+ m_lastExportURL.setFileName (QString::null);
+ m_exportFirstTime = true;
+
+
+ // not a close operation?
+ if (m_document)
+ {
+ if (m_document->mainWindow () != this)
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tchanging doc's mainWindow from "
+ << m_document->mainWindow ()
+ << " to this="
+ << this
+ << endl;
+ #endif
+ m_document->setMainWindow (this);
+ }
+
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () <<"\tcreating viewManager" << endl;
+ #endif
+ m_viewManager = new kpViewManager (this);
+
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tcreating views" << endl;
+ #endif
+ m_mainView = new kpZoomedView (m_document, m_toolToolBar, m_viewManager,
+ 0/*buddyView*/,
+ m_scrollView,
+ m_scrollView->viewport (), "mainView");
+ if (m_scrollView)
+ {
+ m_scrollView->addChild (m_mainView);
+ }
+ else
+ kdError () << "kpMainWindow::setDocument() without scrollView" << endl;
+ m_viewManager->registerView (m_mainView);
+ m_mainView->show ();
+
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\thooking up document signals" << endl;
+ #endif
+
+ // Copy/Cut/Deselect/Delete
+ connect (m_document, SIGNAL (selectionEnabled (bool)),
+ m_actionCut, SLOT (setEnabled (bool)));
+ connect (m_document, SIGNAL (selectionEnabled (bool)),
+ m_actionCopy, SLOT (setEnabled (bool)));
+ connect (m_document, SIGNAL (selectionEnabled (bool)),
+ m_actionDelete, SLOT (setEnabled (bool)));
+ connect (m_document, SIGNAL (selectionEnabled (bool)),
+ m_actionDeselect, SLOT (setEnabled (bool)));
+ connect (m_document, SIGNAL (selectionEnabled (bool)),
+ m_actionCopyToFile, SLOT (setEnabled (bool)));
+
+ // this code won't actually enable any actions at this stage
+ // (fresh document) but better safe than sorry
+ m_actionCopy->setEnabled (m_document->selection ());
+ m_actionCut->setEnabled (m_document->selection ());
+ m_actionDeselect->setEnabled (m_document->selection ());
+ m_actionDelete->setEnabled (m_document->selection ());
+ m_actionCopyToFile->setEnabled (m_document->selection ());
+
+ connect (m_document, SIGNAL (selectionEnabled (bool)),
+ this, SLOT (slotImageMenuUpdateDueToSelection ()));
+ connect (m_document, SIGNAL (selectionIsTextChanged (bool)),
+ this, SLOT (slotImageMenuUpdateDueToSelection ()));
+
+ // Status bar
+ connect (m_document, SIGNAL (documentOpened ()),
+ this, SLOT (recalculateStatusBar ()));
+
+ connect (m_document, SIGNAL (sizeChanged (const QSize &)),
+ this, SLOT (setStatusBarDocSize (const QSize &)));
+
+ // Caption (url, modified)
+ connect (m_document, SIGNAL (documentModified ()),
+ this, SLOT (slotUpdateCaption ()));
+ connect (m_document, SIGNAL (documentOpened ()),
+ this, SLOT (slotUpdateCaption ()));
+ connect (m_document, SIGNAL (documentSaved ()),
+ this, SLOT (slotUpdateCaption ()));
+
+ // File/Reload action only available with non-empty URL
+ connect (m_document, SIGNAL (documentSaved ()),
+ this, SLOT (slotEnableReload ()));
+
+ connect (m_document, SIGNAL (documentSaved ()),
+ this, SLOT (slotEnableSettingsShowPath ()));
+
+ // Command history
+ if (m_commandHistory)
+ {
+ connect (m_commandHistory, SIGNAL (documentRestored ()),
+ this, SLOT (slotDocumentRestored ())); // caption "!modified"
+ connect (m_document, SIGNAL (documentSaved ()),
+ m_commandHistory, SLOT (documentSaved ()));
+ }
+ else
+ {
+ kdError () << "kpMainWindow::setDocument() without commandHistory"
+ << endl;
+ }
+
+ // Sync document -> views
+ connect (m_document, SIGNAL (contentsChanged (const QRect &)),
+ m_viewManager, SLOT (updateViews (const QRect &)));
+ connect (m_document, SIGNAL (sizeChanged (int, int)),
+ m_viewManager, SLOT (adjustViewsToEnvironment ()));
+
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tenabling actions" << endl;
+ #endif
+
+ // sync with the bit marked "sync" above
+
+ if (m_colorToolBar)
+ m_colorToolBar->setEnabled (true);
+ else
+ {
+ kdError () << "kpMainWindow::setDocument() without colorToolBar"
+ << endl;
+ }
+
+
+ // Hide the text toolbar - it will be shown by kpToolText::begin()
+ enableTextToolBarActions (false);
+
+ enableToolsDocumentActions (true);
+
+ enableDocumentActions (true);
+
+ // TODO: The thumbnail auto zoom doesn't work because it thinks its
+ // width == 1 when !this->isShown(). So for consistency,
+ // never create the thumbnail.
+ #if 0
+ if (m_configThumbnailShown)
+ {
+ if (isShown ())
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tcreating thumbnail immediately" << endl;
+ #endif
+ slotCreateThumbnail ();
+ }
+ // this' geometry is weird ATM
+ else
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tcreating thumbnail LATER" << endl;
+ #endif
+ QTimer::singleShot (0, this, SLOT (slotCreateThumbnail ()));
+ }
+ }
+ #endif
+ }
+
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tupdating mainWindow elements" << endl;
+#endif
+
+ slotImageMenuUpdateDueToSelection ();
+ recalculateStatusBar ();
+ slotUpdateCaption (); // Untitled to start with
+ slotEnableReload ();
+ slotEnableSettingsShowPath ();
+
+ if (m_commandHistory)
+ m_commandHistory->clear ();
+
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tdocument and views ready to go!" << endl;
+#endif
+}
+
+
+// private virtual [base KMainWindow]
+bool kpMainWindow::queryClose ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::queryClose()" << endl;
+#endif
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+ if (!m_document || !m_document->isModified ())
+ return true; // ok to close current doc
+
+ int result = KMessageBox::warningYesNoCancel (this,
+ i18n ("The document \"%1\" has been modified.\n"
+ "Do you want to save it?")
+ .arg (m_document->prettyFilename ()),
+ QString::null/*caption*/,
+ KStdGuiItem::save (), KStdGuiItem::discard ());
+
+ switch (result)
+ {
+ case KMessageBox::Yes:
+ return slotSave (); // close only if save succeeds
+ case KMessageBox::No:
+ return true; // close without saving
+ default:
+ return false; // don't close current doc
+ }
+}
+
+
+// private virtual [base QWidget]
+void kpMainWindow::dragEnterEvent (QDragEnterEvent *e)
+{
+ e->accept (kpSelectionDrag::canDecode (e) ||
+ KURLDrag::canDecode (e) ||
+ QTextDrag::canDecode (e));
+}
+
+// private virtual [base QWidget]
+void kpMainWindow::dropEvent (QDropEvent *e)
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::dropEvent" << e->pos () << endl;
+#endif
+
+ kpSelection sel;
+ KURL::List urls;
+ QString text;
+
+ if (kpSelectionDrag::decode (e, sel/*ref*/, pasteWarnAboutLossInfo ()))
+ {
+ sel.setTransparency (selectionTransparency ());
+ // TODO: drop at point like with QTextDrag below?
+ paste (sel);
+ }
+ else if (KURLDrag::decode (e, urls/*ref*/))
+ {
+ for (KURL::List::ConstIterator it = urls.begin (); it != urls.end (); it++)
+ {
+ open (*it);
+ }
+ }
+ else if (QTextDrag::decode (e, text/*ref*/))
+ {
+ QPoint selTopLeft = KP_INVALID_POINT;
+ const QPoint globalPos = QWidget::mapToGlobal (e->pos ());
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tpos toGlobal=" << globalPos << endl;
+ #endif
+
+ kpView *view = 0;
+
+ if (m_viewManager)
+ {
+ view = m_viewManager->viewUnderCursor ();
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tviewUnderCursor=" << view << endl;
+ #endif
+ if (!view)
+ {
+ // HACK: see kpViewManager::setViewUnderCursor() to see why
+ // it's not reliable
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tattempting to discover view" << endl;
+
+ if (m_mainView && m_scrollView)
+ {
+ kdDebug () << "\t\t\tmainView->globalRect="
+ << kpWidgetMapper::toGlobal (m_mainView, m_mainView->rect ())
+ << " scrollView->globalRect="
+ << kpWidgetMapper::toGlobal (m_scrollView,
+ QRect (0, 0,
+ m_scrollView->visibleWidth (),
+ m_scrollView->visibleHeight ()))
+ << endl;
+ }
+ #endif
+ if (m_thumbnailView &&
+ kpWidgetMapper::toGlobal (m_thumbnailView, m_thumbnailView->rect ())
+ .contains (globalPos))
+ {
+ // TODO: Code will never get executed.
+ // Thumbnail doesn't accept drops.
+ view = m_thumbnailView;
+ }
+ else if (m_mainView &&
+ kpWidgetMapper::toGlobal (m_mainView, m_mainView->rect ())
+ .contains (globalPos) &&
+ m_scrollView &&
+ kpWidgetMapper::toGlobal (m_scrollView,
+ QRect (0, 0,
+ m_scrollView->visibleWidth (),
+ m_scrollView->visibleHeight ()))
+ .contains (globalPos))
+ {
+ view = m_mainView;
+ }
+ }
+ }
+
+ if (view)
+ {
+ const QPoint viewPos = view->mapFromGlobal (globalPos);
+ const QPoint docPoint = view->transformViewToDoc (viewPos);
+
+ // viewUnderCursor() is hacky and can return a view when we aren't
+ // over one thanks to drags.
+ if (m_document && m_document->rect ().contains (docPoint))
+ {
+ selTopLeft = docPoint;
+
+ // TODO: In terms of doc pixels, would be inconsistent behaviour
+ // based on zoomLevel of view.
+ // selTopLeft -= QPoint (-view->selectionResizeHandleAtomicSize (),
+ // -view->selectionResizeHandleAtomicSize ());
+ }
+ }
+
+ pasteText (text, true/*force new text selection*/, selTopLeft);
+ }
+}
+
+
+// private slot
+void kpMainWindow::slotScrollViewAboutToScroll ()
+{
+#if DEBUG_KP_MAIN_WINDOW && 0
+ kdDebug () << "kpMainWindow::slotScrollViewAboutToScroll() tool="
+ << tool () << " viewManager=" << viewManager () << endl;
+ if (viewManager ())
+ {
+ kdDebug () << "\tfastUpdates=" << viewManager ()->fastUpdates ()
+ << " queueUpdates=" << viewManager ()->queueUpdates ()
+ << endl;
+ }
+ else
+ {
+ // We're getting a late signal from the scrollview (thanks to
+ // a timer inside the QScrollView). By now, setDocument() has
+ // already killed the document(), tool() and viewManager().
+ }
+#endif
+
+ QTimer::singleShot (0, this, SLOT (slotScrollViewAfterScroll ()));
+}
+
+// private slot
+void kpMainWindow::slotScrollViewAfterScroll ()
+{
+#if DEBUG_KP_MAIN_WINDOW && 0
+ kdDebug () << "kpMainWindow::slotScrollViewAfterScroll() tool="
+ << tool () << endl;
+#endif
+
+ if (tool ())
+ {
+ tool ()->somethingBelowTheCursorChanged ();
+ }
+}
+
+
+// private virtual [base QWidget]
+void kpMainWindow::moveEvent (QMoveEvent * /*e*/)
+{
+ if (m_thumbnail)
+ {
+ // Disabled because it lags too far behind the mainWindow
+ // m_thumbnail->move (m_thumbnail->pos () + (e->pos () - e->oldPos ()));
+
+ notifyThumbnailGeometryChanged ();
+ }
+}
+
+
+// private slot
+void kpMainWindow::slotUpdateCaption ()
+{
+ if (m_document)
+ {
+ setCaption (m_configShowPath ? m_document->prettyURL ()
+ : m_document->prettyFilename (),
+ m_document->isModified ());
+ }
+ else
+ {
+ setCaption (QString::null, false);
+ }
+}
+
+// private slot
+void kpMainWindow::slotDocumentRestored ()
+{
+ if (m_document)
+ m_document->setModified (false);
+ slotUpdateCaption ();
+}
+
+
+#include <kpmainwindow.moc>
diff --git a/kolourpaint/kpmainwindow.h b/kolourpaint/kpmainwindow.h
new file mode 100644
index 00000000..f5514848
--- /dev/null
+++ b/kolourpaint/kpmainwindow.h
@@ -0,0 +1,739 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef KP_MAIN_WINDOW_H
+#define KP_MAIN_WINDOW_H
+
+
+#define DEBUG_KP_MAIN_WINDOW 0
+
+#include <qpoint.h>
+#include <qptrlist.h>
+#include <qsize.h>
+#include <qvaluevector.h>
+
+#include <kmainwindow.h>
+#include <kurl.h>
+
+#include <kpdefs.h>
+#include <kpdocumentsaveoptions.h>
+#include <kppixmapfx.h>
+
+
+class QPainter;
+class QPoint;
+class QPopupMenu;
+class QRect;
+class QSize;
+class QStringList;
+
+class KAction;
+class KFontAction;
+class KFontSizeAction;
+class KSelectAction;
+class KToggleAction;
+class KToolBar;
+class KPrinter;
+class KRecentFilesAction;
+class KScanDialog;
+class KToggleFullScreenAction;
+
+class kpColor;
+class kpColorToolBar;
+class kpCommand;
+class kpCommandHistory;
+class kpDocument;
+class kpDocumentMetaInfo;
+class kpDocumentSaveOptions;
+class kpViewManager;
+class kpViewScrollableContainer;
+class kpSelection;
+class kpSelectionTransparency;
+class kpSingleKeyTriggersAction;
+class kpSqueezedTextLabel;
+class kpTextStyle;
+class kpThumbnail;
+class kpThumbnailView;
+class kpTool;
+class kpToolText;
+class kpToolToolBar;
+class kpZoomedView;
+
+
+class kpMainWindow : public KMainWindow
+{
+Q_OBJECT
+
+public:
+ // Opens a new window with a blank document.
+ kpMainWindow ();
+
+ // Opens a new window with the document specified by <url>
+ // or creates a blank document if <url> could not be opened.
+ kpMainWindow (const KURL &url);
+
+ // Opens a new window with the document <newDoc>
+ // (<newDoc> can be 0 although this would result in a new
+ // window without a document at all).
+ kpMainWindow (kpDocument *newDoc);
+
+public:
+ double configColorSimilarity () const;
+ void configSetColorSimilarity (double val);
+
+private:
+ bool m_configFirstTime;
+ bool m_configShowGrid;
+ bool m_configShowPath;
+ double m_configColorSimilarity;
+
+ bool m_configThumbnailShown;
+ QRect m_configThumbnailGeometry;
+ bool m_configZoomedThumbnail;
+
+ void readGeneralSettings ();
+ void readThumbnailSettings ();
+ void init ();
+
+ // (only called for restoring a previous session e.g. starting KDE with
+ // a previously saved session; it's not called on normal KolourPaint
+ // startup)
+ virtual void readProperties (KConfig *cfg);
+ // (only called for saving the current session e.g. logging out of KDE
+ // with the KolourPaint window open; it's not called on normal KolourPaint
+ // exit)
+ virtual void saveProperties (KConfig *cfg);
+
+public:
+ ~kpMainWindow ();
+
+private:
+ bool m_isFullyConstructed;
+
+public:
+ kpDocument *document () const;
+ kpViewManager *viewManager () const;
+ kpColorToolBar *colorToolBar () const;
+ kpToolToolBar *toolToolBar () const;
+ kpCommandHistory *commandHistory () const;
+
+private:
+ kpViewScrollableContainer *m_scrollView;
+ kpZoomedView *m_mainView;
+ kpThumbnail *m_thumbnail;
+ kpThumbnailView *m_thumbnailView;
+ kpDocument *m_document;
+ kpViewManager *m_viewManager;
+ kpColorToolBar *m_colorToolBar;
+ kpToolToolBar *m_toolToolBar;
+ kpCommandHistory *m_commandHistory;
+
+private:
+ void setupActions ();
+ void enableDocumentActions (bool enable = true);
+
+public:
+ bool actionsSingleKeyTriggersEnabled () const;
+ void enableActionsSingleKeyTriggers (bool enable = true);
+
+private:
+ void setDocument (kpDocument *newDoc);
+
+ virtual bool queryClose ();
+
+ virtual void dragEnterEvent (QDragEnterEvent *e);
+ virtual void dropEvent (QDropEvent *e);
+
+private slots:
+ void slotScrollViewAboutToScroll ();
+ void slotScrollViewAfterScroll ();
+
+private:
+ virtual void moveEvent (QMoveEvent *e);
+
+private slots:
+ void slotUpdateCaption ();
+ void slotDocumentRestored ();
+
+
+ /*
+ * Tools
+ */
+
+private:
+ void setupToolActions ();
+ void createToolBox ();
+ void enableToolsDocumentActions (bool enable = true);
+
+private slots:
+ void updateToolOptionPrevNextActionsEnabled ();
+
+private:
+ kpTool *m_toolAirSpray, *m_toolBrush, *m_toolColorPicker,
+ *m_toolColorWasher, *m_toolCurve, *m_toolEllipse,
+ *m_toolEllipticalSelection, *m_toolEraser,
+ *m_toolFloodFill, *m_toolFreeFormSelection,
+ *m_toolLine, *m_toolPen, *m_toolPolygon,
+ *m_toolPolyline, *m_toolRectangle, *m_toolRectSelection,
+ *m_toolRoundedRectangle;
+ kpToolText *m_toolText;
+
+ QPtrList <kpTool> m_tools;
+ int m_lastToolNumber;
+
+ bool m_toolActionsEnabled;
+ kpSingleKeyTriggersAction *m_actionPrevToolOptionGroup1,
+ *m_actionNextToolOptionGroup1,
+ *m_actionPrevToolOptionGroup2,
+ *m_actionNextToolOptionGroup2;
+
+ int m_settingSelectionTransparency;
+
+ int m_docResizeWidth, m_docResizeHeight;
+ bool m_docResizeToBeCompleted;
+
+public:
+ kpTool *tool () const;
+ bool toolHasBegunShape () const;
+ bool toolIsASelectionTool (bool includingTextTool = true) const;
+ bool toolIsTextTool () const;
+
+ kpSelectionTransparency selectionTransparency () const;
+ // The drawing background color is set to <transparency>.transparentColor()
+ // if the <transparency> is in Transparent mode or if <forceColorChange>
+ // is true (not the default). [x]
+ //
+ // If <transparency> is in Opaque mode and <forceColorChange> is false,
+ // the background color is not changed because:
+ //
+ // 1. It is ignored by the selection in Opaque mode anyway.
+ // 2. This avoids irritating the user with an unnecessary background
+ // color change.
+ //
+ // The only case where you should set <forceColorChange> to true is in
+ // kpToolSelectionTransparencyCommand to ensure that the state
+ // is identical to when the command was constructed.
+ // Later: I don't think setting it to true is ever necessary since:
+ //
+ // 1. The background color only counts in Transparent mode.
+ //
+ // 2. Any kpToolSelectionTransparencyCommand that switches to
+ // Transparent mode will automatically set the background
+ // color due to the first part of [x] anyway.
+ //
+ // The other fields of <transparency> are copied into the main window
+ // as expected.
+ void setSelectionTransparency (const kpSelectionTransparency &transparency,
+ bool forceColorChange = false);
+ int settingSelectionTransparency () const;
+
+private slots:
+ void slotToolSelected (kpTool *tool);
+
+private:
+ void readLastTool ();
+ int toolNumber () const;
+ void saveLastTool ();
+
+private:
+ bool maybeDragScrollingMainView () const;
+private slots:
+ bool slotDragScroll (const QPoint &docPoint,
+ const QPoint &docLastPoint,
+ int zoomLevel,
+ bool *didSomething);
+ bool slotEndDragScroll ();
+
+private slots:
+ void slotBeganDocResize ();
+ void slotContinuedDocResize (const QSize &size);
+ void slotCancelledDocResize ();
+ void slotEndedDocResize (const QSize &size);
+
+ void slotDocResizeMessageChanged (const QString &string);
+
+private slots:
+ void slotActionPrevToolOptionGroup1 ();
+ void slotActionNextToolOptionGroup1 ();
+ void slotActionPrevToolOptionGroup2 ();
+ void slotActionNextToolOptionGroup2 ();
+
+public slots:
+ void slotToolAirSpray ();
+ void slotToolBrush ();
+ void slotToolColorPicker ();
+ void slotToolColorWasher ();
+ void slotToolCurve ();
+ void slotToolEllipse ();
+ void slotToolEllipticalSelection ();
+ void slotToolEraser ();
+ void slotToolFloodFill ();
+ void slotToolFreeFormSelection ();
+ void slotToolLine ();
+ void slotToolPen ();
+ void slotToolPolygon ();
+ void slotToolPolyline ();
+ void slotToolRectangle ();
+ void slotToolRectSelection ();
+ void slotToolRoundedRectangle ();
+ void slotToolText ();
+
+
+ /*
+ * File Menu
+ */
+
+private:
+ void setupFileMenuActions ();
+ void enableFileMenuDocumentActions (bool enable = true);
+
+ KAction *m_actionNew, *m_actionOpen;
+ KRecentFilesAction *m_actionOpenRecent;
+ KAction *m_actionScan, *m_actionSave, *m_actionSaveAs, *m_actionExport,
+ *m_actionReload,
+ *m_actionPrint, *m_actionPrintPreview,
+ *m_actionMail,
+ *m_actionSetAsWallpaperTiled, *m_actionSetAsWallpaperCentered,
+ *m_actionClose, *m_actionQuit;
+
+ KScanDialog *m_scanDialog;
+
+ KURL m_lastExportURL;
+ kpDocumentSaveOptions m_lastExportSaveOptions;
+ bool m_exportFirstTime;
+
+private:
+ void addRecentURL (const KURL &url);
+
+private slots:
+ void slotNew ();
+
+private:
+ QSize defaultDocSize () const;
+ void saveDefaultDocSize (const QSize &size);
+
+private:
+ bool shouldOpenInNewWindow () const;
+ void setDocumentChoosingWindow (kpDocument *doc);
+
+private:
+ kpDocument *openInternal (const KURL &url,
+ const QSize &fallbackDocSize,
+ bool newDocSameNameIfNotExist);
+ // Same as above except that it:
+ //
+ // 1. Assumes a default fallback document size.
+ // 2. If the URL is successfully opened (with the special exception of
+ // the "kolourpaint doesnotexist.png" case), it is bubbled up to the
+ // top in the Recent Files Action.
+ //
+ // As a result of this behavior, this should only be called in response
+ // to a user open request e.g. File / Open or "kolourpaint doesexist.png".
+ // It should not be used for session restore - in that case, it does not
+ // make sense to bubble the Recent Files list.
+ bool open (const KURL &url, bool newDocSameNameIfNotExist = false);
+
+ KURL::List askForOpenURLs (const QString &caption,
+ const QString &startURL,
+ bool allowMultipleURLs = true);
+
+private slots:
+ void slotOpen ();
+ void slotOpenRecent (const KURL &url);
+
+ void slotScan ();
+ void slotScanned (const QImage &image, int);
+
+ bool save (bool localOnly = false);
+ bool slotSave ();
+
+private:
+ KURL askForSaveURL (const QString &caption,
+ const QString &startURL,
+ const QPixmap &pixmapToBeSaved,
+ const kpDocumentSaveOptions &startSaveOptions,
+ const kpDocumentMetaInfo &docMetaInfo,
+ const QString &forcedSaveOptionsGroup,
+ bool localOnly,
+ kpDocumentSaveOptions *chosenSaveOptions,
+ bool isSavingForFirstTime,
+ bool *allowOverwritePrompt,
+ bool *allowLossyPrompt);
+
+private slots:
+ bool saveAs (bool localOnly = false);
+ bool slotSaveAs ();
+
+ bool slotExport ();
+
+ void slotEnableReload ();
+ bool slotReload ();
+
+private:
+ void sendFilenameToPrinter (KPrinter *printer);
+ void sendPixmapToPrinter (KPrinter *printer, bool showPrinterSetupDialog);
+
+private slots:
+ void slotPrint ();
+ void slotPrintPreview ();
+
+ void slotMail ();
+
+private:
+ void setAsWallpaper (bool centered);
+private slots:
+ void slotSetAsWallpaperCentered ();
+ void slotSetAsWallpaperTiled ();
+
+ void slotClose ();
+ void slotQuit ();
+
+
+ /*
+ * Edit Menu
+ */
+
+private:
+ kpPixmapFX::WarnAboutLossInfo pasteWarnAboutLossInfo ();
+ void setupEditMenuActions ();
+ void enableEditMenuDocumentActions (bool enable = true);
+
+ bool m_editMenuDocumentActionsEnabled;
+
+ KAction *m_actionUndo, *m_actionRedo,
+ *m_actionCut, *m_actionCopy,
+ *m_actionPaste, *m_actionPasteInNewWindow,
+ *m_actionDelete,
+ *m_actionSelectAll, *m_actionDeselect,
+ *m_actionCopyToFile, *m_actionPasteFromFile;
+
+ KURL m_lastPasteFromURL;
+
+ KURL m_lastCopyToURL;
+ kpDocumentSaveOptions m_lastCopyToSaveOptions;
+ bool m_copyToFirstTime;
+
+public:
+ QPopupMenu *selectionToolRMBMenu ();
+
+private slots:
+ void slotCut ();
+ void slotCopy ();
+ void slotEnablePaste ();
+private:
+ QRect calcUsefulPasteRect (int pixmapWidth, int pixmapHeight);
+ void paste (const kpSelection &sel,
+ bool forceTopLeft = false);
+public:
+ // (<forceNewTextSelection> is ignored if <text> is empty)
+ void pasteText (const QString &text,
+ bool forceNewTextSelection = false,
+ const QPoint &newTextSelectionTopLeft = KP_INVALID_POINT);
+ void pasteTextAt (const QString &text, const QPoint &point,
+ // Allow tiny adjustment of <point> so that mouse
+ // pointer is not exactly on top of the topLeft of
+ // any new text selection (so that it doesn't look
+ // weird by being on top of a resize handle just after
+ // a paste).
+ bool allowNewTextSelectionPointShift = false);
+public slots:
+ void slotPaste ();
+private slots:
+ void slotPasteInNewWindow ();
+public slots:
+ void slotDelete ();
+
+ void slotSelectAll ();
+private:
+ void addDeselectFirstCommand (kpCommand *cmd);
+public slots:
+ void slotDeselect ();
+private slots:
+ void slotCopyToFile ();
+ void slotPasteFromFile ();
+
+
+ /*
+ * View Menu
+ */
+
+private:
+ bool m_viewMenuDocumentActionsEnabled;
+
+ void setupViewMenuActions ();
+ bool viewMenuDocumentActionsEnabled () const;
+ void enableViewMenuDocumentActions (bool enable = true);
+ void actionShowGridUpdate ();
+
+ KAction *m_actionFullScreenBIC,
+ *m_actionActualSize,
+ *m_actionFitToPage, *m_actionFitToWidth, *m_actionFitToHeight,
+ *m_actionZoomIn, *m_actionZoomOut;
+ KSelectAction *m_actionZoom;
+ KToggleAction *m_actionShowGrid,
+ *m_actionShowThumbnail, *m_actionZoomedThumbnail;
+
+ QValueVector <int> m_zoomList;
+
+private:
+ void sendZoomListToActionZoom ();
+ int zoomLevelFromString (const QString &string);
+ QString zoomLevelToString (int zoomLevel);
+ void zoomTo (int zoomLevel, bool centerUnderCursor = false);
+
+private slots:
+ void finishZoomTo ();
+
+private slots:
+ void slotActualSize ();
+ void slotFitToPage ();
+ void slotFitToWidth ();
+ void slotFitToHeight ();
+
+public:
+ void zoomIn (bool centerUnderCursor = false);
+ void zoomOut (bool centerUnderCursor = false);
+
+public slots:
+ void slotZoomIn ();
+ void slotZoomOut ();
+
+private:
+ void zoomAccordingToZoomAction (bool centerUnderCursor = false);
+
+private slots:
+ void slotZoom ();
+
+ void slotShowGridToggled ();
+private:
+ void updateMainViewGrid ();
+
+private:
+ QRect mapToGlobal (const QRect &rect) const;
+ QRect mapFromGlobal (const QRect &rect) const;
+
+private slots:
+ void slotDestroyThumbnailIfNotVisible (bool tnIsVisible);
+ void slotDestroyThumbnail ();
+ void slotDestroyThumbnailInitatedByUser ();
+ void slotCreateThumbnail ();
+
+private:
+ QTimer *m_thumbnailSaveConfigTimer;
+
+public:
+ void notifyThumbnailGeometryChanged ();
+
+private slots:
+ void slotSaveThumbnailGeometry ();
+ void slotShowThumbnailToggled ();
+ void updateThumbnailZoomed ();
+ void slotZoomedThumbnailToggled ();
+ void slotThumbnailShowRectangleToggled ();
+
+private:
+ void enableViewZoomedThumbnail (bool enable = true);
+ void enableViewShowThumbnailRectangle (bool enable = true);
+ void enableThumbnailOptionActions (bool enable = true);
+ void createThumbnailView ();
+ void destroyThumbnailView ();
+ void updateThumbnail ();
+
+
+ /*
+ * Image Menu
+ */
+
+private:
+ bool isSelectionActive () const;
+ bool isTextSelection () const;
+
+ QString autoCropText () const;
+
+ void setupImageMenuActions ();
+ void enableImageMenuDocumentActions (bool enable = true);
+
+ bool m_imageMenuDocumentActionsEnabled;
+
+ KAction *m_actionResizeScale,
+ *m_actionCrop, *m_actionAutoCrop,
+ *m_actionFlip, *m_actionRotate, *m_actionSkew,
+ *m_actionConvertToBlackAndWhite, *m_actionConvertToGrayscale,
+ *m_actionMoreEffects,
+ *m_actionInvertColors, *m_actionClear;
+
+private slots:
+ void slotImageMenuUpdateDueToSelection ();
+
+public:
+ kpColor backgroundColor (bool ofSelection = false) const;
+ void addImageOrSelectionCommand (kpCommand *cmd,
+ bool addSelCreateCmdIfSelAvail = true,
+ bool addSelPullCmdIfSelAvail = true);
+
+private slots:
+ void slotResizeScale ();
+public slots:
+ void slotCrop ();
+private slots:
+ void slotAutoCrop ();
+ void slotFlip ();
+ void slotRotate ();
+ void slotSkew ();
+ void slotConvertToBlackAndWhite ();
+ void slotConvertToGrayscale ();
+ void slotInvertColors ();
+ void slotClear ();
+ void slotMoreEffects ();
+
+
+ /*
+ * Settings Menu
+ */
+
+private:
+ void setupSettingsMenuActions ();
+ void enableSettingsMenuDocumentActions (bool enable = true);
+
+ KToggleAction *m_actionShowPath;
+ KAction *m_actionKeyBindings, *m_actionConfigureToolbars, *m_actionConfigure;
+ KToggleFullScreenAction *m_actionFullScreen;
+
+private slots:
+ void slotFullScreen ();
+
+ void slotEnableSettingsShowPath ();
+ void slotShowPathToggled ();
+
+ void slotKeyBindings ();
+
+ void slotConfigureToolBars ();
+ void slotNewToolBarConfig ();
+
+ void slotConfigure ();
+
+
+ /*
+ * Status Bar
+ */
+
+private:
+ bool m_statusBarCreated;
+ kpSqueezedTextLabel *m_statusBarMessageLabel;
+
+ bool m_statusBarShapeLastPointsInitialised;
+ QPoint m_statusBarShapeLastStartPoint, m_statusBarShapeLastEndPoint;
+ bool m_statusBarShapeLastSizeInitialised;
+ QSize m_statusBarShapeLastSize;
+
+ enum
+ {
+ StatusBarItemMessage,
+ StatusBarItemShapePoints,
+ StatusBarItemShapeSize,
+ StatusBarItemDocSize,
+ StatusBarItemDocDepth,
+ StatusBarItemZoom
+ };
+
+ void addPermanentStatusBarItem (int id, int maxTextLen);
+ void createStatusBar ();
+
+private slots:
+ void setStatusBarMessage (const QString &message = QString::null);
+ void setStatusBarShapePoints (const QPoint &startPoint = KP_INVALID_POINT,
+ const QPoint &endPoint = KP_INVALID_POINT);
+ void setStatusBarShapeSize (const QSize &size = KP_INVALID_SIZE);
+ void setStatusBarDocSize (const QSize &size = KP_INVALID_SIZE);
+ void setStatusBarDocDepth (int depth = 0);
+ void setStatusBarZoom (int zoom = 0);
+
+ void recalculateStatusBarMessage ();
+ void recalculateStatusBarShape ();
+
+ void recalculateStatusBar ();
+
+
+ /*
+ * Text ToolBar
+ */
+
+private:
+ void setupTextToolBarActions ();
+ void readAndApplyTextSettings ();
+
+public:
+ void enableTextToolBarActions (bool enable = true);
+
+private slots:
+ void slotTextFontFamilyChanged ();
+ void slotTextFontSizeChanged ();
+ void slotTextBoldChanged ();
+ void slotTextItalicChanged ();
+ void slotTextUnderlineChanged ();
+ void slotTextStrikeThruChanged ();
+
+public:
+ KToolBar *textToolBar ();
+ bool isTextStyleBackgroundOpaque () const;
+ kpTextStyle textStyle () const;
+ void setTextStyle (const kpTextStyle &textStyle_);
+ int settingTextStyle () const;
+
+private:
+ KFontAction *m_actionTextFontFamily;
+ KFontSizeAction *m_actionTextFontSize;
+ KToggleAction *m_actionTextBold, *m_actionTextItalic,
+ *m_actionTextUnderline, *m_actionTextStrikeThru;
+
+ int m_settingTextStyle;
+ QString m_textOldFontFamily;
+ int m_textOldFontSize;
+
+
+ /*
+ * Help Menu
+ */
+private:
+ void setupHelpMenuActions ();
+ void enableHelpMenuDocumentActions (bool enable = true);
+
+private slots:
+ void slotHelpTakingScreenshots ();
+ void slotHelpTakingScreenshotsFollowLink (const QString &link);
+
+
+private:
+ // There is no need to maintain binary compatibility at this stage.
+ // The d-pointer is just so that you can experiment without recompiling
+ // the kitchen sink.
+ class kpMainWindowPrivate *d;
+};
+
+
+#endif // KP_MAIN_WINDOW_H
diff --git a/kolourpaint/kpmainwindow_edit.cpp b/kolourpaint/kpmainwindow_edit.cpp
new file mode 100644
index 00000000..3cf9b4f6
--- /dev/null
+++ b/kolourpaint/kpmainwindow_edit.cpp
@@ -0,0 +1,1069 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <kpmainwindow.h>
+
+#include <qapplication.h>
+#include <qclipboard.h>
+#include <qdatetime.h>
+#include <qfontmetrics.h>
+#include <qimage.h>
+#include <qpixmap.h>
+#include <qvaluevector.h>
+
+#include <kaction.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kstdaction.h>
+
+#include <kpcommandhistory.h>
+#include <kpdocument.h>
+#include <kpdocumentmetainfo.h>
+#include <kpdocumentsaveoptions.h>
+#include <kppixmapfx.h>
+#include <kpselection.h>
+#include <kpselectiondrag.h>
+#include <kpselectiontransparency.h>
+#include <kptool.h>
+#include <kptoolcrop.h>
+#include <kptoolresizescale.h>
+#include <kptoolselection.h>
+#include <kptooltext.h>
+#include <kpviewmanager.h>
+#include <kpviewscrollablecontainer.h>
+#include <kpzoomedview.h>
+
+
+// private
+kpPixmapFX::WarnAboutLossInfo kpMainWindow::pasteWarnAboutLossInfo ()
+{
+ return kpPixmapFX::WarnAboutLossInfo (
+ i18n ("The image to be pasted"
+ " may have more colors than the current screen mode."
+ " In order to display it, some colors may be changed."
+ " Try increasing your screen depth to at least %1bpp."
+
+ "\nIt also"
+
+ " contains translucency which is not fully"
+ " supported. The translucency data will be"
+ " approximated with a 1-bit transparency mask."),
+ i18n ("The image to be pasted"
+ " may have more colors than the current screen mode."
+ " In order to display it, some colors may be changed."
+ " Try increasing your screen depth to at least %1bpp."),
+ i18n ("The image to be pasted"
+ " contains translucency which is not fully"
+ " supported. The translucency data will be"
+ " approximated with a 1-bit transparency mask."),
+ "paste",
+ this);
+}
+
+
+// private
+void kpMainWindow::setupEditMenuActions ()
+{
+ KActionCollection *ac = actionCollection ();
+
+
+ // Undo/Redo
+ // CONFIG: need GUI
+ m_commandHistory = new kpCommandHistory (true/*read config*/, this);
+
+ if (m_configFirstTime)
+ {
+ // (so that cfg-file-editing user can modify in the meantime)
+ m_commandHistory->writeConfig ();
+ }
+
+
+ m_actionCut = KStdAction::cut (this, SLOT (slotCut ()), ac);
+ m_actionCopy = KStdAction::copy (this, SLOT (slotCopy ()), ac);
+ m_actionPaste = KStdAction::paste (this, SLOT (slotPaste ()), ac);
+ m_actionPasteInNewWindow = new KAction (i18n ("Paste in &New Window"),
+ Qt::CTRL + Qt::SHIFT + Qt::Key_V,
+ this, SLOT (slotPasteInNewWindow ()), ac, "edit_paste_in_new_window");
+
+ //m_actionDelete = KStdAction::clear (this, SLOT (slotDelete ()), ac);
+ m_actionDelete = new KAction (i18n ("&Delete Selection"), 0,
+ this, SLOT (slotDelete ()), ac, "edit_clear");
+
+ m_actionSelectAll = KStdAction::selectAll (this, SLOT (slotSelectAll ()), ac);
+ m_actionDeselect = KStdAction::deselect (this, SLOT (slotDeselect ()), ac);
+
+
+ m_actionCopyToFile = new KAction (i18n ("C&opy to File..."), 0,
+ this, SLOT (slotCopyToFile ()), ac, "edit_copy_to_file");
+ m_actionPasteFromFile = new KAction (i18n ("Paste &From File..."), 0,
+ this, SLOT (slotPasteFromFile ()), ac, "edit_paste_from_file");
+
+
+ m_editMenuDocumentActionsEnabled = false;
+ enableEditMenuDocumentActions (false);
+
+ // Paste should always be enabled, as long as there is something paste
+ // (independent of whether we have a document or not)
+ connect (QApplication::clipboard (), SIGNAL (dataChanged ()),
+ this, SLOT (slotEnablePaste ()));
+ slotEnablePaste ();
+}
+
+// private
+void kpMainWindow::enableEditMenuDocumentActions (bool enable)
+{
+ // m_actionCut
+ // m_actionCopy
+ // m_actionPaste
+ // m_actionPasteInNewWindow
+
+ // m_actionDelete
+
+ m_actionSelectAll->setEnabled (enable);
+ // m_actionDeselect
+
+ m_editMenuDocumentActionsEnabled = enable;
+
+ // m_actionCopyToFile
+ // Unlike m_actionPaste, we disable this if there is no document.
+ // This is because "File / Open" would do the same thing, if there is
+ // no document.
+ m_actionPasteFromFile->setEnabled (enable);
+}
+
+
+// public
+QPopupMenu *kpMainWindow::selectionToolRMBMenu ()
+{
+ return (QPopupMenu *) guiFactory ()->container ("selectionToolRMBMenu", this);
+}
+
+
+// private slot
+void kpMainWindow::slotCut ()
+{
+#if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "kpMainWindow::slotCut() CALLED" << endl;
+#endif
+
+ if (!m_document || !m_document->selection ())
+ {
+ kdError () << "kpMainWindow::slotCut () doc=" << m_document
+ << " sel=" << (m_document ? m_document->selection () : 0)
+ << endl;
+ return;
+ }
+
+
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+ slotCopy ();
+ slotDelete ();
+
+ QApplication::restoreOverrideCursor ();
+
+}
+
+// private slot
+void kpMainWindow::slotCopy ()
+{
+#if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "kpMainWindow::slotCopy() CALLED" << endl;
+#endif
+
+ if (!m_document || !m_document->selection ())
+ {
+ kdError () << "kpMainWindow::slotCopy () doc=" << m_document
+ << " sel=" << (m_document ? m_document->selection () : 0)
+ << endl;
+ return;
+ }
+
+
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+ kpSelection sel = *m_document->selection ();
+ // Transparency doesn't get sent across the aether so nuke it now
+ // so that transparency mask doesn't get needlessly recalculated
+ // if we ever call sel.setPixmap().
+ sel.setTransparency (kpSelectionTransparency ());
+
+ if (sel.isText ())
+ {
+ if (!sel.text ().isEmpty ())
+ {
+ QApplication::clipboard ()->setData (new QTextDrag (sel.text ()),
+ QClipboard::Clipboard);
+
+ // SYNC: Normally, users highlight text and press CTRL+C.
+ // Highlighting text copies it to the X11 "middle
+ // mouse button" clipboard. CTRL+C copies it to the
+ // separate, Windows-like "CTRL+V" clipboard.
+ //
+ // However, KolourPaint doesn't support highlighting.
+ // So when they press CTRL+C to copy all text, simulate
+ // the highlighting by copying the text to the "middle
+ // mouse button" clipboard. We don't do this for images
+ // as no one ever middle-mouse-pastes images.
+ //
+ // Note that we don't share the QTextDrag pointer with
+ // the above in case Qt doesn't expect it.
+ //
+ // Once we change KolourPaint to support highlighted text
+ // and CTRL+C to copy only the highlighted text, delete
+ // this code.
+ QApplication::clipboard ()->setData (new QTextDrag (sel.text ()),
+ QClipboard::Selection);
+ }
+ }
+ else
+ {
+ QPixmap rawPixmap;
+
+ if (sel.pixmap ())
+ rawPixmap = *sel.pixmap ();
+ else
+ rawPixmap = m_document->getSelectedPixmap ();
+
+ // Some apps, such as OpenOffice.org 2.0.4, ignore the image mask
+ // when pasting. For transparent pixels, the uninitialized RGB
+ // values are used. Fix this by initializing those values to a
+ // neutral color -- white.
+ //
+ // Strangely enough, OpenOffice.org respects the mask when inserting
+ // an image from a file, as opposed to pasting one from the clipboard.
+ sel.setPixmap (
+ kpPixmapFX::pixmapWithDefinedTransparentPixels (
+ rawPixmap,
+ Qt::white)); // CONFIG
+
+ QApplication::clipboard ()->setData (new kpSelectionDrag (sel),
+ QClipboard::Clipboard);
+ }
+
+ QApplication::restoreOverrideCursor ();
+}
+
+
+static bool HasSomethingToPaste (kpMainWindow *mw)
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow(" << mw->name () << "):HasSomethingToPaste()" << endl;
+ QTime timer;
+ timer.start ();
+#else
+ (void) mw;
+#endif
+
+ bool hasSomething = false;
+
+ QMimeSource *ms = QApplication::clipboard ()->data (QClipboard::Clipboard);
+ if (ms)
+ {
+ // It's faster to test for QTextDrag::canDecode() first due to the
+ // lazy evaluation of the '||' operator.
+ hasSomething = (QTextDrag::canDecode (ms) ||
+ kpSelectionDrag::canDecode (ms));
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t" << mw->name () << "***canDecode=" << timer.restart () << endl;
+ for (int i = 0; ; i++)
+ {
+ const char *fmt = ms->format (i);
+ if (!fmt)
+ break;
+
+ kdDebug () << "\t'" << fmt << "'" << endl;
+ }
+ #endif
+ }
+
+ return hasSomething;
+}
+
+// HACK: SYNC: Non-Qt apps do not cause QApplication::clipboard() to
+// emit dataChanged(). We don't want to have our paste
+// action disabled when we can actually paste something.
+//
+// So we make sure the paste action is always enabled and
+// before any paste, we check if there's actually something
+// to paste (HasSomethingToPasteWithDialogIfNot ()).
+
+#if 1 // Hack code path
+
+
+// Call before any paste only.
+static bool HasSomethingToPasteWithDialogIfNot (kpMainWindow *mw)
+{
+ if (::HasSomethingToPaste (mw))
+ return true;
+
+ // STRING: Unfortunately, we are in a string freeze.
+#if 1
+ (void) mw;
+#else
+ QApplication::setOverrideCursor (Qt::arrowCursor);
+
+ KMessageBox::sorry (mw,
+ STRING_FREEZE_i18n ("<qt><p>There is nothing in the clipboard to paste.</p></qt>"),
+ STRING_FREEZE_i18n ("Cannot Paste"));
+
+ QApplication::restoreOverrideCursor ();
+#endif
+
+ return false;
+}
+
+// private slot
+void kpMainWindow::slotEnablePaste ()
+{
+ const bool shouldEnable = true;
+ m_actionPasteInNewWindow->setEnabled (shouldEnable);
+ m_actionPaste->setEnabled (shouldEnable);
+}
+
+
+#else // No hack
+
+
+// Call before any paste only.
+static bool HasSomethingToPasteWithDialogIfNot (kpMainWindow *)
+{
+ // We will not be called if there's nothing to paste, as the paste
+ // action would have been disabled. But do _not_ assert that
+ // (see the slotPaste() "data unexpectedly disappeared" KMessageBox).
+ return true;
+}
+
+// private slot
+void kpMainWindow::slotEnablePaste ()
+{
+ const bool shouldEnable = ::HasSomethingToPaste (this);
+ m_actionPasteInNewWindow->setEnabled (shouldEnable);
+ m_actionPaste->setEnabled (shouldEnable);
+}
+
+
+#endif
+
+
+// private
+QRect kpMainWindow::calcUsefulPasteRect (int pixmapWidth, int pixmapHeight)
+{
+#if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "kpMainWindow::calcUsefulPasteRect("
+ << pixmapWidth << "," << pixmapHeight
+ << ")"
+ << endl;
+#endif
+ if (!m_document)
+ {
+ kdError () << "kpMainWindow::calcUsefulPasteRect() without doc" << endl;
+ return QRect ();
+ }
+
+ // TODO: 1st choice is to paste sel near but not overlapping last deselect point
+
+ if (m_mainView && m_scrollView)
+ {
+ const QPoint viewTopLeft (m_scrollView->contentsX (),
+ m_scrollView->contentsY ());
+
+ const QPoint docTopLeft = m_mainView->transformViewToDoc (viewTopLeft);
+
+ if ((docTopLeft.x () + pixmapWidth <= m_document->width () &&
+ docTopLeft.y () + pixmapHeight <= m_document->height ()) ||
+ pixmapWidth <= docTopLeft.x () ||
+ pixmapHeight <= docTopLeft.y ())
+ {
+ return QRect (docTopLeft.x (), docTopLeft.y (),
+ pixmapWidth, pixmapHeight);
+ }
+ }
+
+ return QRect (0, 0, pixmapWidth, pixmapHeight);
+}
+
+// private
+void kpMainWindow::paste (const kpSelection &sel, bool forceTopLeft)
+{
+#if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "kpMainWindow::paste(forceTopLeft=" << forceTopLeft << ")"
+ << endl;
+#endif
+
+ if (!sel.pixmap ())
+ {
+ kdError () << "kpMainWindow::paste() with sel without pixmap" << endl;
+ return;
+ }
+
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+
+ //
+ // Make sure we've got a document (esp. with File/Close)
+ //
+
+ if (!m_document)
+ {
+ kpDocument *newDoc = new kpDocument (
+ sel.width (), sel.height (), this);
+
+ // will also create viewManager
+ setDocument (newDoc);
+ }
+
+
+ //
+ // Paste as new selection
+ //
+
+ kpSelection selInUsefulPos = sel;
+ if (!forceTopLeft)
+ selInUsefulPos.moveTo (calcUsefulPasteRect (sel.width (), sel.height ()).topLeft ());
+ addDeselectFirstCommand (new kpToolSelectionCreateCommand (
+ selInUsefulPos.isText () ?
+ i18n ("Text: Create Box") :
+ i18n ("Selection: Create"),
+ selInUsefulPos,
+ this));
+
+
+#if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "sel.size=" << QSize (sel.width (), sel.height ())
+ << " document.size="
+ << QSize (m_document->width (), m_document->height ())
+ << endl;
+#endif
+
+ // If the selection is bigger than the document, automatically
+ // resize the document (with the option of Undo'ing) to fit
+ // the selection.
+ //
+ // No annoying dialog necessary.
+ //
+ if (sel.width () > m_document->width () ||
+ sel.height () > m_document->height ())
+ {
+ m_commandHistory->addCommand (
+ new kpToolResizeScaleCommand (
+ false/*act on doc, not sel*/,
+ QMAX (sel.width (), m_document->width ()),
+ QMAX (sel.height (), m_document->height ()),
+ kpToolResizeScaleCommand::Resize,
+ this));
+ }
+
+
+ QApplication::restoreOverrideCursor ();
+}
+
+// public
+void kpMainWindow::pasteText (const QString &text,
+ bool forceNewTextSelection,
+ const QPoint &newTextSelectionTopLeft)
+{
+#if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "kpMainWindow::pasteText(" << text
+ << ",forceNewTextSelection=" << forceNewTextSelection
+ << ",newTextSelectionTopLeft=" << newTextSelectionTopLeft
+ << ")" << endl;
+#endif
+
+ if (text.isEmpty ())
+ return;
+
+
+ // sync: restoreOverrideCursor() in all exit paths
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+
+ QValueVector <QString> textLines (1, QString::null);
+
+ for (int i = 0; i < (int) text.length (); i++)
+ {
+ if (text [i] == '\n')
+ textLines.push_back (QString::null);
+ else
+ textLines [textLines.size () - 1].append (text [i]);
+ }
+
+
+ if (!forceNewTextSelection &&
+ m_document && m_document->selection () &&
+ m_document->selection ()->isText () &&
+ m_commandHistory && m_viewManager)
+ {
+ #if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "\treusing existing Text Selection" << endl;
+ #endif
+
+ kpMacroCommand *macroCmd = new kpMacroCommand (i18n ("Text: Paste"),
+ this);
+
+ for (int i = 0; i < (int) textLines.size (); i++)
+ {
+ if (i > 0)
+ {
+ macroCmd->addCommand (
+ new kpToolTextEnterCommand (
+ QString::null/*uninteresting child of macroCmd*/,
+ m_viewManager->textCursorRow (),
+ m_viewManager->textCursorCol (),
+ this));
+ }
+
+ macroCmd->addCommand (
+ new kpToolTextInsertCommand (
+ QString::null/*uninteresting child of macroCmd*/,
+ m_viewManager->textCursorRow (),
+ m_viewManager->textCursorCol (),
+ textLines [i],
+ this));
+ }
+
+ m_commandHistory->addCommand (macroCmd, false/*no exec*/);
+ }
+ else
+ {
+ #if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "\tcreating Text Selection" << endl;
+ #endif
+
+ const kpTextStyle ts = textStyle ();
+ const QFontMetrics fontMetrics = ts.fontMetrics ();
+
+ int height = textLines.size () * fontMetrics.height ();
+ if (textLines.size () >= 1)
+ height += (textLines.size () - 1) * fontMetrics.leading ();
+
+ int width = 0;
+ for (QValueVector <QString>::const_iterator it = textLines.begin ();
+ it != textLines.end ();
+ it++)
+ {
+ const int w = fontMetrics.width (*it);
+ if (w > width)
+ width = w;
+ }
+
+
+ const int selWidth = QMAX (kpSelection::minimumWidthForTextStyle (ts),
+ width + kpSelection::textBorderSize () * 2);
+ const int selHeight = QMAX (kpSelection::minimumHeightForTextStyle (ts),
+ height + kpSelection::textBorderSize () * 2);
+ kpSelection sel (QRect (0, 0, selWidth, selHeight),
+ textLines,
+ ts);
+
+ if (newTextSelectionTopLeft != KP_INVALID_POINT)
+ {
+ sel.moveTo (newTextSelectionTopLeft);
+ paste (sel, true/*force topLeft*/);
+ }
+ else
+ {
+ paste (sel);
+ }
+ }
+
+
+ QApplication::restoreOverrideCursor ();
+}
+
+// public
+void kpMainWindow::pasteTextAt (const QString &text, const QPoint &point,
+ bool allowNewTextSelectionPointShift)
+{
+#if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "kpMainWindow::pasteTextAt(" << text
+ << ",point=" << point
+ << ",allowNewTextSelectionPointShift="
+ << allowNewTextSelectionPointShift
+ << ")" << endl;
+#endif
+
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+
+ if (m_document &&
+ m_document->selection () &&
+ m_document->selection ()->isText () &&
+ m_document->selection ()->pointIsInTextArea (point))
+ {
+ kpSelection *sel = m_document->selection ();
+
+ const int row = sel->textRowForPoint (point);
+ const int col = sel->textColForPoint (point);
+
+ m_viewManager->setTextCursorPosition (row, col);
+
+ pasteText (text);
+ }
+ else
+ {
+ QPoint pointToUse = point;
+
+ if (allowNewTextSelectionPointShift)
+ {
+ // TODO: In terms of doc pixels, would be inconsistent behaviour
+ // based on zoomLevel of view.
+ // pointToUse -= QPoint (-view->selectionResizeHandleAtomicSize (),
+ // -view->selectionResizeHandleAtomicSize ());
+ }
+
+ pasteText (text, true/*force new text selection*/, pointToUse);
+ }
+
+ QApplication::restoreOverrideCursor ();
+}
+
+// public slot
+void kpMainWindow::slotPaste ()
+{
+#if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "kpMainWindow::slotPaste() CALLED" << endl;
+#endif
+
+ // sync: restoreOverrideCursor() in all exit paths
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+
+ if (!::HasSomethingToPasteWithDialogIfNot (this))
+ {
+ QApplication::restoreOverrideCursor ();
+ return;
+ }
+
+
+ //
+ // Acquire the pixmap
+ //
+
+ QMimeSource *ms = QApplication::clipboard ()->data (QClipboard::Clipboard);
+ if (!ms)
+ {
+ kdError () << "kpMainWindow::slotPaste() without mimeSource" << endl;
+ QApplication::restoreOverrideCursor ();
+ return;
+ }
+
+ kpSelection sel;
+ QString text;
+ if (kpSelectionDrag::decode (ms, sel/*ref*/, pasteWarnAboutLossInfo ()))
+ {
+ sel.setTransparency (selectionTransparency ());
+ paste (sel);
+ }
+ else if (QTextDrag::decode (ms, text/*ref*/))
+ {
+ pasteText (text);
+ }
+ else
+ {
+ QApplication::restoreOverrideCursor ();
+
+ kdDebug () << "kpMainWindow::slotPaste() could not decode selection" << endl;
+ kdDebug () << "\tFormats supported:" << endl;
+ for (int i = 0; ms->format (i); i++)
+ {
+ kdDebug () << "\t\t" << i << ":" << ms->format (i) << endl;
+ }
+
+ // TODO: fix Klipper
+ KMessageBox::sorry (this,
+ i18n ("<qt><p>KolourPaint cannot paste the contents of"
+ " the clipboard as the data unexpectedly disappeared.</p>"
+
+ "<p>This usually occurs if the application which was"
+ " responsible"
+ " for the clipboard contents has been closed.</p></qt>"),
+ i18n ("Cannot Paste"));
+
+ // TODO: PROPAGATE: interprocess
+ if (KMainWindow::memberList)
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\thave memberList" << endl;
+ #endif
+
+ for (QPtrList <KMainWindow>::const_iterator it = KMainWindow::memberList->begin ();
+ it != KMainWindow::memberList->end ();
+ it++)
+ {
+ kpMainWindow *mw = dynamic_cast <kpMainWindow *> (*it);
+
+ if (!mw)
+ {
+ kdError () << "kpMainWindow::slotPaste() given fake kpMainWindow: " << (*it) << endl;
+ continue;
+ }
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tmw=" << mw << endl;
+ #endif
+
+ mw->slotEnablePaste ();
+ }
+ }
+
+ return;
+ }
+
+ QApplication::restoreOverrideCursor ();
+}
+
+// private slot
+void kpMainWindow::slotPasteInNewWindow ()
+{
+#if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "kpMainWindow::slotPasteInNewWindow() CALLED" << endl;
+#endif
+
+ // sync: restoreOverrideCursor() in all exit paths
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+
+ if (!::HasSomethingToPasteWithDialogIfNot (this))
+ {
+ QApplication::restoreOverrideCursor ();
+ return;
+ }
+
+
+ //
+ // Pasting must ensure that:
+ //
+ // Requirement 1. the document is the same size as the image to be pasted.
+ // Requirement 2. transparent pixels in the image must remain as transparent.
+ //
+
+ kpMainWindow *win = new kpMainWindow (0/*no document*/);
+ win->show ();
+
+ // Make "Edit / Paste in New Window" always paste white pixels as white.
+ // Don't let selection transparency get in the way and paste them as
+ // transparent.
+ kpSelectionTransparency transparency = win->selectionTransparency ();
+ if (transparency.isTransparent ())
+ {
+ #if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "\tchanging selection transparency to opaque" << endl;
+ #endif
+ transparency.setOpaque ();
+ // Since we are setting selection transparency programmatically
+ // -- as opposed to in response to user input -- this will not
+ // affect the selection transparency tool option widget's "last used"
+ // config setting.
+ win->setSelectionTransparency (transparency);
+ }
+
+ // (this handles Requirement 1. above)
+ win->slotPaste ();
+
+ // (this handles Requirement 2. above;
+ // slotDeselect() is not enough unless the document is filled with the
+ // transparent color in advance)
+ win->slotCrop ();
+
+
+ QApplication::restoreOverrideCursor ();
+}
+
+// public slot
+void kpMainWindow::slotDelete ()
+{
+#if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "kpMainWindow::slotDelete() CALLED" << endl;
+#endif
+ if (!m_actionDelete->isEnabled ())
+ {
+ #if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "\taction not enabled - was probably called from kpTool::keyPressEvent()" << endl;
+ #endif
+ return;
+ }
+
+ if (!m_document || !m_document->selection ())
+ {
+ kdError () << "kpMainWindow::slotDelete () doc=" << m_document
+ << " sel=" << (m_document ? m_document->selection () : 0)
+ << endl;
+ return;
+ }
+
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+ addImageOrSelectionCommand (new kpToolSelectionDestroyCommand (
+ m_document->selection ()->isText () ?
+ i18n ("Text: Delete Box") : // not to be confused with i18n ("Text: Delete")
+ i18n ("Selection: Delete"),
+ false/*no push onto doc*/,
+ this));
+}
+
+
+// private slot
+void kpMainWindow::slotSelectAll ()
+{
+#if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "kpMainWindow::slotSelectAll() CALLED" << endl;
+#endif
+ if (!m_document)
+ {
+ kdError () << "kpMainWindow::slotSelectAll() without doc" << endl;
+ return;
+ }
+
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+ if (m_document->selection ())
+ slotDeselect ();
+
+ // just the border - don't actually pull pixmap from doc yet
+ m_document->setSelection (kpSelection (kpSelection::Rectangle, m_document->rect (), selectionTransparency ()));
+
+ if (tool ())
+ tool ()->somethingBelowTheCursorChanged ();
+}
+
+
+// private
+void kpMainWindow::addDeselectFirstCommand (kpCommand *cmd)
+{
+#if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "kpMainWindow::addDeselectFirstCommand("
+ << cmd
+ << ")"
+ << endl;
+#endif
+
+
+ kpSelection *sel = m_document->selection ();
+
+#if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "\tsel=" << sel << endl;
+#endif
+
+ if (sel)
+ {
+ // if you just dragged out something with no action then
+ // forget the drag
+ if (!sel->pixmap ())
+ {
+ #if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "\tjust a fresh border - was nop - delete" << endl;
+ #endif
+ m_document->selectionDelete ();
+ if (tool ())
+ tool ()->somethingBelowTheCursorChanged ();
+
+ if (cmd)
+ m_commandHistory->addCommand (cmd);
+ }
+ else
+ {
+ #if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "\treal selection with pixmap - push onto doc cmd" << endl;
+ #endif
+ kpCommand *deselectCommand = new kpToolSelectionDestroyCommand (
+ sel->isText () ?
+ i18n ("Text: Finish") :
+ i18n ("Selection: Deselect"),
+ true/*push onto document*/,
+ this);
+
+ if (cmd)
+ {
+ kpMacroCommand *macroCmd = new kpMacroCommand (cmd->name (), this);
+ macroCmd->addCommand (deselectCommand);
+ macroCmd->addCommand (cmd);
+ m_commandHistory->addCommand (macroCmd);
+ }
+ else
+ m_commandHistory->addCommand (deselectCommand);
+ }
+ }
+ else
+ {
+ if (cmd)
+ m_commandHistory->addCommand (cmd);
+ }
+}
+
+
+// public slot
+void kpMainWindow::slotDeselect ()
+{
+#if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "kpMainWindow::slotDeselect() CALLED" << endl;
+#endif
+ if (!m_document || !m_document->selection ())
+ {
+ kdError () << "kpMainWindow::slotDeselect() doc=" << m_document
+ << " sel=" << (m_document ? m_document->selection () : 0)
+ << endl;
+ return;
+ }
+
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+ addDeselectFirstCommand (0);
+}
+
+
+// private slot
+void kpMainWindow::slotCopyToFile ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotCopyToFile()" << endl;
+#endif
+
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+
+ if (!m_document->selection ())
+ return;
+
+ kpSelection sel = *m_document->selection ();
+
+ QPixmap pixmapToSave;
+
+ if (!sel.pixmap ())
+ {
+ // Not a floating selection - user has just selected a region;
+ // haven't pulled it off yet so probably don't expect and can't
+ // visualise selection transparency so give opaque, not transparent
+ // pixmap.
+ pixmapToSave = m_document->getSelectedPixmap ();
+ }
+ else
+ pixmapToSave = sel.transparentPixmap ();
+
+
+ kpDocumentSaveOptions chosenSaveOptions;
+ bool allowOverwritePrompt, allowLossyPrompt;
+ KURL chosenURL = askForSaveURL (i18n ("Copy to File"),
+ m_lastCopyToURL.url (),
+ pixmapToSave,
+ m_lastCopyToSaveOptions,
+ kpDocumentMetaInfo (),
+ kpSettingsGroupEditCopyTo,
+ false/*allow remote files*/,
+ &chosenSaveOptions,
+ m_copyToFirstTime,
+ &allowOverwritePrompt,
+ &allowLossyPrompt);
+
+ if (chosenURL.isEmpty ())
+ return;
+
+
+ if (!kpDocument::savePixmapToFile (pixmapToSave,
+ chosenURL,
+ chosenSaveOptions, kpDocumentMetaInfo (),
+ allowOverwritePrompt,
+ allowLossyPrompt,
+ this))
+ {
+ return;
+ }
+
+
+ addRecentURL (chosenURL);
+
+
+ m_lastCopyToURL = chosenURL;
+ m_lastCopyToSaveOptions = chosenSaveOptions;
+
+ m_copyToFirstTime = false;
+}
+
+// private slot
+void kpMainWindow::slotPasteFromFile ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotPasteFromFile()" << endl;
+#endif
+
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+
+ KURL::List urls = askForOpenURLs (i18n ("Paste From File"),
+ m_lastPasteFromURL.url (),
+ false/*only 1 URL*/);
+
+ if (urls.count () != 1)
+ return;
+
+ KURL url = urls.first ();
+ m_lastPasteFromURL = url;
+
+
+ QPixmap pixmap = kpDocument::getPixmapFromFile (url,
+ false/*show error message if doesn't exist*/,
+ this);
+
+
+ if (pixmap.isNull ())
+ return;
+
+
+ addRecentURL (url);
+
+ paste (kpSelection (kpSelection::Rectangle,
+ QRect (0, 0, pixmap.width (), pixmap.height ()),
+ pixmap,
+ selectionTransparency ()));
+}
+
diff --git a/kolourpaint/kpmainwindow_file.cpp b/kolourpaint/kpmainwindow_file.cpp
new file mode 100644
index 00000000..b30b323e
--- /dev/null
+++ b/kolourpaint/kpmainwindow_file.cpp
@@ -0,0 +1,1409 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <kpmainwindow.h>
+
+#include <qcstring.h>
+#include <qdatastream.h>
+#include <qpaintdevicemetrics.h>
+#include <qpainter.h>
+#include <qsize.h>
+
+#include <dcopclient.h>
+#include <kapplication.h>
+#include <kaction.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kfiledialog.h>
+#include <kiconloader.h>
+#include <kimagefilepreview.h>
+#include <kimageio.h>
+#include <kio/netaccess.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kprinter.h>
+#include <kstdaccel.h>
+#include <kstdaction.h>
+#include <kscan.h>
+
+#include <kpdefs.h>
+#include <kpdocument.h>
+#include <kpdocumentsaveoptionswidget.h>
+#include <kptool.h>
+#include <kpview.h>
+#include <kpviewmanager.h>
+
+
+// private
+void kpMainWindow::setupFileMenuActions ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::setupFileMenuActions()" << endl;
+#endif
+ KActionCollection *ac = actionCollection ();
+
+ m_actionNew = KStdAction::openNew (this, SLOT (slotNew ()), ac);
+ m_actionOpen = KStdAction::open (this, SLOT (slotOpen ()), ac);
+
+ m_actionOpenRecent = KStdAction::openRecent (this, SLOT (slotOpenRecent (const KURL &)), ac);
+ m_actionOpenRecent->loadEntries (kapp->config ());
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\trecent URLs=" << m_actionOpenRecent->items () << endl;
+#endif
+
+ m_actionSave = KStdAction::save (this, SLOT (slotSave ()), ac);
+ m_actionSaveAs = KStdAction::saveAs (this, SLOT (slotSaveAs ()), ac);
+
+ m_actionExport = new KAction (i18n ("E&xport..."), 0,
+ this, SLOT (slotExport ()), ac, "file_export");
+
+ m_actionScan = new KAction (i18n ("Scan..."), SmallIcon ("scanner"), 0,
+ this, SLOT (slotScan ()), ac, "file_scan");
+
+ //m_actionRevert = KStdAction::revert (this, SLOT (slotRevert ()), ac);
+ m_actionReload = new KAction (i18n ("Reloa&d"), KStdAccel::reload (),
+ this, SLOT (slotReload ()), ac, "file_revert");
+ slotEnableReload ();
+
+ m_actionPrint = KStdAction::print (this, SLOT (slotPrint ()), ac);
+ m_actionPrintPreview = KStdAction::printPreview (this, SLOT (slotPrintPreview ()), ac);
+
+ m_actionMail = KStdAction::mail (this, SLOT (slotMail ()), ac);
+
+ m_actionSetAsWallpaperCentered = new KAction (i18n ("Set as Wa&llpaper (Centered)"), 0,
+ this, SLOT (slotSetAsWallpaperCentered ()), ac, "file_set_as_wallpaper_centered");
+ m_actionSetAsWallpaperTiled = new KAction (i18n ("Set as Wallpaper (&Tiled)"), 0,
+ this, SLOT (slotSetAsWallpaperTiled ()), ac, "file_set_as_wallpaper_tiled");
+
+ m_actionClose = KStdAction::close (this, SLOT (slotClose ()), ac);
+ m_actionQuit = KStdAction::quit (this, SLOT (slotQuit ()), ac);
+
+ m_scanDialog = 0;
+
+ enableFileMenuDocumentActions (false);
+}
+
+// private
+void kpMainWindow::enableFileMenuDocumentActions (bool enable)
+{
+ // m_actionNew
+ // m_actionOpen
+
+ // m_actionOpenRecent
+
+ m_actionSave->setEnabled (enable);
+ m_actionSaveAs->setEnabled (enable);
+
+ m_actionExport->setEnabled (enable);
+
+ // m_actionReload
+
+ m_actionPrint->setEnabled (enable);
+ m_actionPrintPreview->setEnabled (enable);
+
+ m_actionMail->setEnabled (enable);
+
+ m_actionSetAsWallpaperCentered->setEnabled (enable);
+ m_actionSetAsWallpaperTiled->setEnabled (enable);
+
+ m_actionClose->setEnabled (enable);
+ // m_actionQuit->setEnabled (enable);
+}
+
+
+// private
+void kpMainWindow::addRecentURL (const KURL &url)
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::addRecentURL(" << url << ")" << endl;
+#endif
+ if (url.isEmpty ())
+ return;
+
+
+ KConfig *cfg = kapp->config ();
+
+ // KConfig::readEntry() does not actually reread from disk, hence doesn't
+ // realise what other processes have done e.g. Settings / Show Path
+ cfg->reparseConfiguration ();
+
+ // HACK: Something might have changed interprocess.
+ // If we could PROPAGATE: interprocess, then this wouldn't be required.
+ m_actionOpenRecent->loadEntries (cfg);
+
+ m_actionOpenRecent->addURL (url);
+
+ m_actionOpenRecent->saveEntries (cfg);
+ cfg->sync ();
+
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tnew recent URLs=" << m_actionOpenRecent->items () << endl;
+#endif
+
+
+ // TODO: PROPAGATE: interprocess
+ if (KMainWindow::memberList)
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\thave memberList" << endl;
+ #endif
+
+ for (QPtrList <KMainWindow>::const_iterator it = KMainWindow::memberList->begin ();
+ it != KMainWindow::memberList->end ();
+ it++)
+ {
+ kpMainWindow *mw = dynamic_cast <kpMainWindow *> (*it);
+
+ if (!mw)
+ {
+ kdError () << "kpMainWindow::addRecentURL() given fake kpMainWindow: " << (*it) << endl;
+ continue;
+ }
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tmw=" << mw << endl;
+ #endif
+
+ if (mw != this)
+ {
+ // WARNING: Do not use KRecentFilesAction::setItems()
+ // - it does not work since only its superclass,
+ // KSelectAction, implements setItems() and can't
+ // update KRecentFilesAction's URL list.
+
+ // Avoid URL memory leak in KRecentFilesAction::loadEntries().
+ mw->m_actionOpenRecent->clearURLList ();
+
+ mw->m_actionOpenRecent->loadEntries (cfg);
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\t\tcheck recent URLs="
+ << mw->m_actionOpenRecent->items () << endl;
+ #endif
+ }
+ }
+ }
+}
+
+
+// private slot
+void kpMainWindow::slotNew ()
+{
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+ if (m_document)
+ {
+ kpMainWindow *win = new kpMainWindow ();
+ win->show ();
+ }
+ else
+ {
+ open (KURL (), true/*create an empty doc*/);
+ }
+}
+
+
+// private
+QSize kpMainWindow::defaultDocSize () const
+{
+ // KConfig::readEntry() does not actually reread from disk, hence doesn't
+ // realise what other processes have done e.g. Settings / Show Path
+ kapp->config ()->reparseConfiguration ();
+
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupGeneral);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ QSize docSize = cfg->readSizeEntry (kpSettingLastDocSize);
+
+ if (docSize.isEmpty ())
+ {
+ docSize = QSize (400, 300);
+ }
+ else
+ {
+ // Don't get too big or you'll thrash (or even lock up) the computer
+ // just by opening a window
+ docSize = QSize (QMIN (2048, docSize.width ()),
+ QMIN (2048, docSize.height ()));
+ }
+
+ return docSize;
+}
+
+// private
+void kpMainWindow::saveDefaultDocSize (const QSize &size)
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tCONFIG: saving Last Doc Size = " << size << endl;
+#endif
+
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupGeneral);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ cfg->writeEntry (kpSettingLastDocSize, size);
+ cfg->sync ();
+}
+
+
+// private
+bool kpMainWindow::shouldOpenInNewWindow () const
+{
+ return (m_document && !m_document->isEmpty ());
+}
+
+// private
+void kpMainWindow::setDocumentChoosingWindow (kpDocument *doc)
+{
+ // need new window?
+ if (shouldOpenInNewWindow ())
+ {
+ // send doc to new window
+ kpMainWindow *win = new kpMainWindow (doc);
+ win->show ();
+ }
+ else
+ {
+ // set up views, doc signals
+ setDocument (doc);
+ }
+}
+
+
+// private
+kpDocument *kpMainWindow::openInternal (const KURL &url,
+ const QSize &fallbackDocSize,
+ bool newDocSameNameIfNotExist)
+{
+ // create doc
+ kpDocument *newDoc = new kpDocument (fallbackDocSize.width (),
+ fallbackDocSize.height (),
+ this);
+ if (!newDoc->open (url, newDocSameNameIfNotExist))
+ {
+ delete newDoc;
+ return 0;
+ }
+
+ // Send document to current or new window.
+ setDocumentChoosingWindow (newDoc);
+
+ return newDoc;
+}
+
+// private
+bool kpMainWindow::open (const KURL &url, bool newDocSameNameIfNotExist)
+{
+ kpDocument *newDoc = openInternal (url,
+ defaultDocSize (),
+ newDocSameNameIfNotExist);
+ if (newDoc)
+ {
+ if (newDoc->isFromURL (false/*don't bother checking exists*/))
+ addRecentURL (url);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+// private
+KURL::List kpMainWindow::askForOpenURLs (const QString &caption, const QString &startURL,
+ bool allowMultipleURLs)
+{
+ QStringList mimeTypes = KImageIO::mimeTypes (KImageIO::Reading);
+#if DEBUG_KP_MAIN_WINDOW
+ QStringList sortedMimeTypes = mimeTypes;
+ sortedMimeTypes.sort ();
+ kdDebug () << "kpMainWindow::askForURLs(allowMultiple="
+ << allowMultipleURLs
+ << ")" << endl
+ << "\tmimeTypes=" << mimeTypes << endl
+ << "\tsortedMimeTypes=" << sortedMimeTypes << endl;
+#endif
+ QString filter = mimeTypes.join (" ");
+
+ KFileDialog fd (startURL, filter, this, "fd", true/*modal*/);
+ fd.setCaption (caption);
+ fd.setOperationMode (KFileDialog::Opening);
+ if (allowMultipleURLs)
+ fd.setMode (KFile::Files);
+ fd.setPreviewWidget (new KImageFilePreview (&fd));
+
+ if (fd.exec ())
+ return fd.selectedURLs ();
+ else
+ return KURL::List ();
+}
+
+// private slot
+void kpMainWindow::slotOpen ()
+{
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+
+ const KURL::List urls = askForOpenURLs (i18n ("Open Image"),
+ m_document ? m_document->url ().url () : QString::null);
+
+ for (KURL::List::const_iterator it = urls.begin ();
+ it != urls.end ();
+ it++)
+ {
+ open (*it);
+ }
+}
+
+// private slot
+void kpMainWindow::slotOpenRecent (const KURL &url)
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotOpenRecent(" << url << ")" << endl;
+ kdDebug () << "\titems=" << m_actionOpenRecent->items () << endl;
+#endif
+
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+ open (url);
+
+ // If the open is successful, addRecentURL() would have bubbled up the
+ // URL in the File / Open Recent action. As a side effect, the URL is
+ // deselected.
+ //
+ // If the open fails, we should deselect the URL:
+ //
+ // 1. for consistency
+ //
+ // 2. because it has not been opened.
+ //
+ m_actionOpenRecent->setCurrentItem (-1);
+}
+
+
+// private slot
+void kpMainWindow::slotScan ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotScan() scanDialog=" << m_scanDialog << endl;
+#endif
+
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+
+ if (!m_scanDialog)
+ {
+ // Create scan dialog by looking for plugin.
+ // [takes about 500ms on 350Mhz]
+ m_scanDialog = KScanDialog::getScanDialog (this, "scandialog", true/*modal*/);
+
+ // No scanning support (kdegraphics/libkscan) installed?
+ // [Remove $KDEDIR/share/servicetypes/kscan.desktop and
+ // $KDEDIR/share/services/scanservice.desktop to simulate this]
+ if (!m_scanDialog)
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tcould not create scan dialog" << endl;
+ #endif
+ // Instead, we could try to create the scan dialog in the ctor
+ // and just disable the action in the first place, removing
+ // the need for this dialog.
+ //
+ // But this increases startup time and is a bit risky e.g. if
+ // the scan support hangs, KolourPaint would not be able to be
+ // started at all.
+ //
+ // Also, disabling the action is bad because the scan support
+ // can be installed while KolourPaint is still running.
+ KMessageBox::sorry (this,
+ i18n ("Scanning support is not installed."),
+ i18n ("No Scanning Support"));
+ return;
+ }
+
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tcreated scanDialog=" << m_scanDialog << endl;
+ #endif
+ connect (m_scanDialog, SIGNAL (finalImage (const QImage &, int)),
+ SLOT (slotScanned (const QImage &, int)));
+ }
+
+
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tcalling setup" << endl;
+#endif
+ // Bring up dialog to select scan device.
+ if (m_scanDialog->setup ())
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tOK - showing dialog" << endl;
+ #endif
+ // Called only if scanner configured/available.
+ //
+ // In reality, this seems to be called even if you press "Cancel" in
+ // the KScanDialog::setup() dialog!
+ m_scanDialog->show ();
+ }
+ else
+ {
+ // Have never seen this code path execute even if "Cancel" is pressed.
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tFAIL" << endl;
+ #endif
+ }
+}
+
+// private slot
+void kpMainWindow::slotScanned (const QImage &image, int)
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotScanned() image.rect=" << image.rect () << endl;
+#endif
+
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\thiding dialog" << endl;
+#endif
+ // (KScanDialog does not close itself after a scan is made)
+ //
+ // Close the dialog, first thing:
+ //
+ // 1. This means that any dialogs we bring up won't be nested on top.
+ //
+ // 2. We don't want to return from this method but forget to close
+ // the dialog. So do it before anything else.
+ m_scanDialog->hide ();
+
+ // (just in case there's some drawing between slotScan() exiting and
+ // us being called)
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+
+ // TODO: Maybe this code should be moved into kpdocument.cpp -
+ // since it resembles the responsibilities of kpDocument::open().
+
+ // Convert QImage to kpDocument's image format, gathering meta info
+ // from QImage.
+ kpDocumentSaveOptions saveOptions;
+ kpDocumentMetaInfo metaInfo;
+ const QPixmap pixmap = kpDocument::convertToPixmapAsLosslessAsPossible (
+ image,
+ kpMainWindow::pasteWarnAboutLossInfo (),
+ &saveOptions,
+ &metaInfo);
+
+ if (pixmap.isNull ())
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tcould not convert to pixmap" << endl;
+ #endif
+ KMessageBox::sorry (this,
+ i18n ("Cannot scan - out of graphics memory."),
+ i18n ("Cannot Scan"));
+ return;
+ }
+
+
+ // Create document from image and meta info.
+ kpDocument *doc = new kpDocument (pixmap.width (), pixmap.height (), this);
+ doc->setPixmap (pixmap);
+ doc->setSaveOptions (saveOptions);
+ doc->setMetaInfo (metaInfo);
+
+
+ // Send document to current or new window.
+ setDocumentChoosingWindow (doc);
+}
+
+
+// private slot
+bool kpMainWindow::save (bool localOnly)
+{
+ if (m_document->url ().isEmpty () ||
+ KImageIO::mimeTypes (KImageIO::Writing)
+ .findIndex (m_document->saveOptions ()->mimeType ()) < 0 ||
+ // SYNC: kpDocument::getPixmapFromFile() can't determine quality
+ // from file so it has been set initially to an invalid value.
+ (m_document->saveOptions ()->mimeTypeHasConfigurableQuality () &&
+ m_document->saveOptions ()->qualityIsInvalid ()) ||
+ (localOnly && !m_document->url ().isLocalFile ()))
+ {
+ return saveAs (localOnly);
+ }
+ else
+ {
+ if (m_document->save (false/*no overwrite prompt*/,
+ !m_document->savedAtLeastOnceBefore ()/*lossy prompt*/))
+ {
+ addRecentURL (m_document->url ());
+ return true;
+ }
+ else
+ return false;
+ }
+}
+
+// private slot
+bool kpMainWindow::slotSave ()
+{
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+ return save ();
+}
+
+// private
+KURL kpMainWindow::askForSaveURL (const QString &caption,
+ const QString &startURL,
+ const QPixmap &pixmapToBeSaved,
+ const kpDocumentSaveOptions &startSaveOptions,
+ const kpDocumentMetaInfo &docMetaInfo,
+ const QString &forcedSaveOptionsGroup,
+ bool localOnly,
+ kpDocumentSaveOptions *chosenSaveOptions,
+ bool isSavingForFirstTime,
+ bool *allowOverwritePrompt,
+ bool *allowLossyPrompt)
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::askForURL() startURL=" << startURL << endl;
+ startSaveOptions.printDebug ("\tstartSaveOptions");
+#endif
+
+ bool reparsedConfiguration = false;
+
+ // KConfig::readEntry() does not actually reread from disk, hence doesn't
+ // realise what other processes have done e.g. Settings / Show Path
+ // so reparseConfiguration() must be called
+#define SETUP_READ_CFG() \
+ if (!reparsedConfiguration) \
+ { \
+ kapp->config ()->reparseConfiguration (); \
+ reparsedConfiguration = true; \
+ } \
+ \
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), forcedSaveOptionsGroup); \
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+
+ if (chosenSaveOptions)
+ *chosenSaveOptions = kpDocumentSaveOptions ();
+
+ if (allowOverwritePrompt)
+ *allowOverwritePrompt = true; // play it safe for now
+
+ if (allowLossyPrompt)
+ *allowLossyPrompt = true; // play it safe for now
+
+
+ kpDocumentSaveOptions fdSaveOptions = startSaveOptions;
+
+ QStringList mimeTypes = KImageIO::mimeTypes (KImageIO::Writing);
+#if DEBUG_KP_MAIN_WINDOW
+ QStringList sortedMimeTypes = mimeTypes;
+ sortedMimeTypes.sort ();
+ kdDebug () << "\tmimeTypes=" << mimeTypes << endl
+ << "\tsortedMimeTypes=" << sortedMimeTypes << endl;
+#endif
+ if (mimeTypes.isEmpty ())
+ {
+ kdError () << "No KImageIO output mimetypes!" << endl;
+ return KURL ();
+ }
+
+#define MIME_TYPE_IS_VALID() (!fdSaveOptions.mimeTypeIsInvalid () && \
+ mimeTypes.findIndex (fdSaveOptions.mimeType ()) >= 0)
+ if (!MIME_TYPE_IS_VALID ())
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tmimeType=" << fdSaveOptions.mimeType ()
+ << " not valid, get default" << endl;
+ #endif
+
+ SETUP_READ_CFG ();
+
+ fdSaveOptions.setMimeType (kpDocumentSaveOptions::defaultMimeType (cfg));
+
+
+ if (!MIME_TYPE_IS_VALID ())
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tmimeType=" << fdSaveOptions.mimeType ()
+ << " not valid, get hardcoded" << endl;
+ #endif
+ if (mimeTypes.findIndex ("image/png") > -1)
+ fdSaveOptions.setMimeType ("image/png");
+ else if (mimeTypes.findIndex ("image/x-bmp") > -1)
+ fdSaveOptions.setMimeType ("image/x-bmp");
+ else
+ fdSaveOptions.setMimeType (mimeTypes.first ());
+ }
+ }
+#undef MIME_TYPE_IN_LIST
+
+ if (fdSaveOptions.colorDepthIsInvalid ())
+ {
+ SETUP_READ_CFG ();
+
+ fdSaveOptions.setColorDepth (kpDocumentSaveOptions::defaultColorDepth (cfg));
+ fdSaveOptions.setDither (kpDocumentSaveOptions::defaultDither (cfg));
+ }
+
+ if (fdSaveOptions.qualityIsInvalid ())
+ {
+ SETUP_READ_CFG ();
+
+ fdSaveOptions.setQuality (kpDocumentSaveOptions::defaultQuality (cfg));
+ }
+#if DEBUG_KP_MAIN_WINDOW
+ fdSaveOptions.printDebug ("\tcorrected saveOptions passed to fileDialog");
+#endif
+
+ kpDocumentSaveOptionsWidget *saveOptionsWidget =
+ new kpDocumentSaveOptionsWidget (pixmapToBeSaved,
+ fdSaveOptions,
+ docMetaInfo,
+ this);
+
+ KFileDialog fd (startURL, QString::null, this, "fd", true/*modal*/,
+ saveOptionsWidget);
+ saveOptionsWidget->setVisualParent (&fd);
+ fd.setCaption (caption);
+ fd.setOperationMode (KFileDialog::Saving);
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tmimeTypes=" << mimeTypes << endl;
+#endif
+ fd.setMimeFilter (mimeTypes, fdSaveOptions.mimeType ());
+ if (localOnly)
+ fd.setMode (KFile::File | KFile::LocalOnly);
+
+ connect (&fd, SIGNAL (filterChanged (const QString &)),
+ saveOptionsWidget, SLOT (setMimeType (const QString &)));
+
+
+ if (fd.exec ())
+ {
+ kpDocumentSaveOptions newSaveOptions = saveOptionsWidget->documentSaveOptions ();
+ #if DEBUG_KP_MAIN_WINDOW
+ newSaveOptions.printDebug ("\tnewSaveOptions");
+ #endif
+
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), forcedSaveOptionsGroup);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ // Save options user forced - probably want to use them in future
+ kpDocumentSaveOptions::saveDefaultDifferences (cfg,
+ fdSaveOptions, newSaveOptions);
+ cfg->sync ();
+
+
+ if (chosenSaveOptions)
+ *chosenSaveOptions = newSaveOptions;
+
+
+ bool shouldAllowOverwritePrompt =
+ (fd.selectedURL () != startURL ||
+ newSaveOptions.mimeType () != startSaveOptions.mimeType ());
+ if (allowOverwritePrompt)
+ {
+ *allowOverwritePrompt = shouldAllowOverwritePrompt;
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tallowOverwritePrompt=" << *allowOverwritePrompt << endl;
+ #endif
+ }
+
+ if (allowLossyPrompt)
+ {
+ // SYNC: kpDocumentSaveOptions elements - everything except quality
+ // (one quality setting is "just as lossy" as another so no
+ // need to continually warn due to quality change)
+ *allowLossyPrompt =
+ (isSavingForFirstTime ||
+ shouldAllowOverwritePrompt ||
+ newSaveOptions.mimeType () != startSaveOptions.mimeType () ||
+ newSaveOptions.colorDepth () != startSaveOptions.colorDepth () ||
+ newSaveOptions.dither () != startSaveOptions.dither ());
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tallowLossyPrompt=" << *allowLossyPrompt << endl;
+ #endif
+ }
+
+
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tselectedURL=" << fd.selectedURL () << endl;
+ #endif
+ return fd.selectedURL ();
+ }
+ else
+ return KURL ();
+#undef SETUP_READ_CFG
+}
+
+
+// private slot
+bool kpMainWindow::saveAs (bool localOnly)
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::saveAs URL=" << m_document->url () << endl;
+#endif
+
+ kpDocumentSaveOptions chosenSaveOptions;
+ bool allowOverwritePrompt, allowLossyPrompt;
+ KURL chosenURL = askForSaveURL (i18n ("Save Image As"),
+ m_document->url ().url (),
+ m_document->pixmapWithSelection (),
+ *m_document->saveOptions (),
+ *m_document->metaInfo (),
+ kpSettingsGroupFileSaveAs,
+ localOnly,
+ &chosenSaveOptions,
+ !m_document->savedAtLeastOnceBefore (),
+ &allowOverwritePrompt,
+ &allowLossyPrompt);
+
+
+ if (chosenURL.isEmpty ())
+ return false;
+
+
+ if (!m_document->saveAs (chosenURL, chosenSaveOptions,
+ allowOverwritePrompt,
+ allowLossyPrompt))
+ {
+ return false;
+ }
+
+
+ addRecentURL (chosenURL);
+
+ return true;
+}
+
+// private slot
+bool kpMainWindow::slotSaveAs ()
+{
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+ return saveAs ();
+}
+
+// private slot
+bool kpMainWindow::slotExport ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotExport()" << endl;
+#endif
+
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+
+ kpDocumentSaveOptions chosenSaveOptions;
+ bool allowOverwritePrompt, allowLossyPrompt;
+ KURL chosenURL = askForSaveURL (i18n ("Export"),
+ m_lastExportURL.url (),
+ m_document->pixmapWithSelection (),
+ m_lastExportSaveOptions,
+ *m_document->metaInfo (),
+ kpSettingsGroupFileExport,
+ false/*allow remote files*/,
+ &chosenSaveOptions,
+ m_exportFirstTime,
+ &allowOverwritePrompt,
+ &allowLossyPrompt);
+
+
+ if (chosenURL.isEmpty ())
+ return false;
+
+
+ if (!kpDocument::savePixmapToFile (m_document->pixmapWithSelection (),
+ chosenURL,
+ chosenSaveOptions, *m_document->metaInfo (),
+ allowOverwritePrompt,
+ allowLossyPrompt,
+ this))
+ {
+ return false;
+ }
+
+
+ addRecentURL (chosenURL);
+
+
+ m_lastExportURL = chosenURL;
+ m_lastExportSaveOptions = chosenSaveOptions;
+
+ m_exportFirstTime = false;
+
+ return true;
+}
+
+
+// private slot
+void kpMainWindow::slotEnableReload ()
+{
+ m_actionReload->setEnabled (m_document);
+}
+
+// private slot
+bool kpMainWindow::slotReload ()
+{
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+ if (!m_document)
+ return false;
+
+
+ KURL oldURL = m_document->url ();
+
+
+ if (m_document->isModified ())
+ {
+ int result = KMessageBox::Cancel;
+
+ if (m_document->isFromURL (false/*don't bother checking exists*/) && !oldURL.isEmpty ())
+ {
+ result = KMessageBox::warningContinueCancel (this,
+ i18n ("The document \"%1\" has been modified.\n"
+ "Reloading will lose all changes since you last saved it.\n"
+ "Are you sure?")
+ .arg (m_document->prettyFilename ()),
+ QString::null/*caption*/,
+ i18n ("&Reload"));
+ }
+ else
+ {
+ result = KMessageBox::warningContinueCancel (this,
+ i18n ("The document \"%1\" has been modified.\n"
+ "Reloading will lose all changes.\n"
+ "Are you sure?")
+ .arg (m_document->prettyFilename ()),
+ QString::null/*caption*/,
+ i18n ("&Reload"));
+ }
+
+ if (result != KMessageBox::Continue)
+ return false;
+ }
+
+
+ kpDocument *doc = 0;
+
+ // If it's _supposed to_ come from a URL or it exists
+ if (m_document->isFromURL (false/*don't bother checking exists*/) ||
+ (!oldURL.isEmpty () && KIO::NetAccess::exists (oldURL, true/*open*/, this)))
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotReload() reloading from disk!" << endl;
+ #endif
+
+ doc = new kpDocument (1, 1, this);
+ if (!doc->open (oldURL))
+ {
+ delete doc; doc = 0;
+ return false;
+ }
+
+ addRecentURL (oldURL);
+ }
+ else
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotReload() create doc" << endl;
+ #endif
+
+ doc = new kpDocument (m_document->constructorWidth (),
+ m_document->constructorHeight (),
+ this);
+ doc->setURL (oldURL, false/*not from URL*/);
+ }
+
+
+ setDocument (doc);
+
+ return true;
+}
+
+
+// private
+void kpMainWindow::sendFilenameToPrinter (KPrinter *printer)
+{
+ KURL url = m_document->url ();
+ if (!url.isEmpty ())
+ {
+ int dot;
+
+ QString fileName = url.fileName ();
+ dot = fileName.findRev ('.');
+
+ // file.ext but not .hidden-file?
+ if (dot > 0)
+ fileName.truncate (dot);
+
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::sendFilenameToPrinter() fileName="
+ << fileName
+ << " dir="
+ << url.directory ()
+ << endl;
+ #endif
+ printer->setDocName (fileName);
+ printer->setDocFileName (fileName);
+ printer->setDocDirectory (url.directory ());
+ }
+}
+
+
+static const double InchesPerMeter = 100 / 2.54;
+
+
+// TODO: GUI should allow viewing & changing of DPI.
+
+
+static bool shouldPrintImageCenteredOnPage ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpmainwindow_file.cpp:shouldPrintImageCenteredOnPage()" << endl;
+#endif
+ bool ret;
+
+ KConfigGroupSaver cfgGroupSaver (KGlobal::config (),
+ kpSettingsGroupGeneral);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ if (cfg->hasKey (kpSettingPrintImageCenteredOnPage))
+ {
+ ret = cfg->readBoolEntry (kpSettingPrintImageCenteredOnPage);
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tread: " << ret << endl;
+ #endif
+ }
+ else
+ {
+ ret = true;
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tfirst time - writing default: " << ret << endl;
+#endif
+ cfg->writeEntry (kpSettingPrintImageCenteredOnPage, ret);
+ cfg->sync ();
+ }
+
+ return ret;
+}
+
+
+// private
+void kpMainWindow::sendPixmapToPrinter (KPrinter *printer,
+ bool showPrinterSetupDialog)
+{
+ // Get image to be printed.
+ QPixmap pixmap = m_document->pixmapWithSelection ();
+
+
+ // Get image DPI.
+ double pixmapDotsPerMeterX =
+ double (m_document->metaInfo ()->dotsPerMeterX ());
+ double pixmapDotsPerMeterY =
+ double (m_document->metaInfo ()->dotsPerMeterY ());
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::sendPixmapToPrinter() pixmap:"
+ << " width=" << pixmap.width ()
+ << " height=" << pixmap.height ()
+ << " dotsPerMeterX=" << pixmapDotsPerMeterX
+ << " dotsPerMeterY=" << pixmapDotsPerMeterY
+ << endl;
+#endif
+
+ // Image DPI invalid (e.g. new image, could not read from file
+ // or Qt3 doesn't implement DPI for JPEG)?
+ if (pixmapDotsPerMeterX < 1 || pixmapDotsPerMeterY < 1)
+ {
+ // Even if just one DPI dimension is invalid, mutate both DPI
+ // dimensions as we have no information about the intended
+ // aspect ratio anyway (and other dimension likely to be invalid).
+
+ // When rendering text onto a document, the fonts are rasterised
+ // according to the screen's DPI.
+ // TODO: I think we should use the image's DPI. Technically
+ // possible?
+ //
+ // So no matter what computer you draw text on, you get
+ // the same pixels.
+ //
+ // So we must print at the screen's DPI to get the right text size.
+ //
+ // Unfortunately, this means that moving to a different screen DPI
+ // affects printing. If you edited the image at a different screen
+ // DPI than when you print, you get incorrect results. Furthermore,
+ // this is bogus if you don't have text in your image. Worse still,
+ // what if you have multiple screens connected to the same computer
+ // with different DPIs?
+ // TODO: mysteriously, someone else is setting this to 96dpi always.
+ QPaintDeviceMetrics screenMetrics (&pixmap/*screen element*/);
+ const int dpiX = screenMetrics.logicalDpiX (),
+ dpiY = screenMetrics.logicalDpiY ();
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tusing screen dpi: x=" << dpiX << " y=" << dpiY << endl;
+ #endif
+
+ pixmapDotsPerMeterX = dpiX * InchesPerMeter;
+ pixmapDotsPerMeterY = dpiY * InchesPerMeter;
+ }
+
+
+ // Get page size (excluding margins).
+ // Coordinate (0,0) is the X here:
+ // mmmmm
+ // mX m
+ // m m m = margin
+ // m m
+ // mmmmm
+ QPaintDeviceMetrics printerMetrics (printer);
+ const int printerWidthMM = printerMetrics.widthMM ();
+ const int printerHeightMM = printerMetrics.heightMM ();
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tprinter: widthMM=" << printerWidthMM
+ << " heightMM=" << printerHeightMM
+ << endl;
+#endif
+
+
+ double dpiX = pixmapDotsPerMeterX / InchesPerMeter;
+ double dpiY = pixmapDotsPerMeterY / InchesPerMeter;
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tpixmap: dpiX=" << dpiX << " dpiY=" << dpiY << endl;
+#endif
+
+
+ //
+ // If image doesn't fit on page at intended DPI, change the DPI.
+ //
+
+ const double scaleDpiX = (pixmap.width () / (printerWidthMM / 25.4))
+ / dpiX;
+ const double scaleDpiY = (pixmap.height () / (printerHeightMM / 25.4))
+ / dpiY;
+ const double scaleDpi = QMAX (scaleDpiX, scaleDpiY);
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tscaleDpi: x=" << scaleDpiX << " y=" << scaleDpiY
+ << " --> scale at " << scaleDpi << " to fit?"
+ << endl;
+#endif
+
+ // Need to increase resolution to fit page?
+ if (scaleDpi > 1.0)
+ {
+ dpiX *= scaleDpi;
+ dpiY *= scaleDpi;
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\t\tto fit page, scaled to:"
+ << " dpiX=" << dpiX << " dpiY=" << dpiY << endl;
+ #endif
+ }
+
+
+ // Make sure DPIs are equal as that's all QPrinter::setResolution()
+ // supports. We do this in such a way that we only ever stretch an
+ // image, to avoid losing information. Don't antialias as the printer
+ // will do that to translate our DPI to its physical resolution and
+ // double-antialiasing looks bad.
+ if (dpiX > dpiY)
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tdpiX > dpiY; stretching pixmap height to equalise DPIs to dpiX="
+ << dpiX << endl;
+ #endif
+ kpPixmapFX::scale (&pixmap,
+ pixmap.width (),
+ QMAX (1, qRound (pixmap.height () * dpiX / dpiY)),
+ false/*don't antialias*/);
+
+ dpiY = dpiX;
+ }
+ else if (dpiY > dpiX)
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tdpiY > dpiX; stretching pixmap width to equalise DPIs to dpiY="
+ << dpiY << endl;
+ #endif
+ kpPixmapFX::scale (&pixmap,
+ QMAX (1, qRound (pixmap.width () * dpiY / dpiX)),
+ pixmap.height (),
+ false/*don't antialias*/);
+
+ dpiX = dpiY;
+ }
+
+
+ // ASSERT: dpiX == dpiY
+ // QPrinter::setResolution() has to be called before QPrinter::setup().
+ printer->setResolution (QMAX (1, qRound (dpiX)));
+
+
+ double originX = 0, originY = 0;
+
+ // Centre image on page?
+ if (shouldPrintImageCenteredOnPage ())
+ {
+ originX = (printerWidthMM * dpiX / 25.4 - pixmap.width ()) / 2;
+ originY = (printerHeightMM * dpiY / 25.4 - pixmap.height ()) / 2;
+ }
+
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\torigin: x=" << originX << " y=" << originY << endl;
+#endif
+
+
+ sendFilenameToPrinter (printer);
+
+ if (showPrinterSetupDialog)
+ {
+ // The user can mutate margins at their own risk in this dialog.
+ // It doesn't seem to affect the size of the page as reported
+ // by QPaintDeviceMetrics::{width,height}MM().
+ if (!printer->setup (this))
+ return;
+ }
+
+
+ // Send pixmap to printer.
+ QPainter painter;
+ painter.begin (printer);
+ painter.drawPixmap (qRound (originX), qRound (originY), pixmap);
+ painter.end ();
+}
+
+
+// private slot
+void kpMainWindow::slotPrint ()
+{
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+ KPrinter printer;
+
+ sendPixmapToPrinter (&printer, true/*showPrinterSetupDialog*/);
+}
+
+// private slot
+void kpMainWindow::slotPrintPreview ()
+{
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+ // TODO: get it to reflect default printer's settings
+ KPrinter printer (false/*separate settings from ordinary printer*/);
+
+ // TODO: pass "this" as parent
+ printer.setPreviewOnly (true);
+
+ sendPixmapToPrinter (&printer, false/*don't showPrinterSetupDialog*/);
+}
+
+
+// private slot
+void kpMainWindow::slotMail ()
+{
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+ if (m_document->url ().isEmpty ()/*no name*/ ||
+ !m_document->isFromURL () ||
+ m_document->isModified ()/*needs to be saved*/)
+ {
+ int result = KMessageBox::questionYesNo (this,
+ i18n ("You must save this image before sending it.\n"
+ "Do you want to save it?"),
+ QString::null,
+ KStdGuiItem::save (), KStdGuiItem::cancel ());
+
+ if (result == KMessageBox::Yes)
+ {
+ if (!save ())
+ {
+ // save failed or aborted - don't email
+ return;
+ }
+ }
+ else
+ {
+ // don't want to save - don't email
+ return;
+ }
+ }
+
+ kapp->invokeMailer (
+ QString::null/*to*/,
+ QString::null/*cc*/,
+ QString::null/*bcc*/,
+ m_document->prettyFilename()/*subject*/,
+ QString::null/*body*/,
+ QString::null/*messageFile*/,
+ QStringList (m_document->url ().url ())/*attachments*/);
+}
+
+
+// private
+void kpMainWindow::setAsWallpaper (bool centered)
+{
+ if (m_document->url ().isEmpty ()/*no name*/ ||
+ !m_document->url ().isLocalFile ()/*remote file*/ ||
+ !m_document->isFromURL () ||
+ m_document->isModified ()/*needs to be saved*/)
+ {
+ QString question;
+
+ if (!m_document->url ().isLocalFile ())
+ {
+ question = i18n ("Before this image can be set as the wallpaper, "
+ "you must save it as a local file.\n"
+ "Do you want to save it?");
+ }
+ else
+ {
+ question = i18n ("Before this image can be set as the wallpaper, "
+ "you must save it.\n"
+ "Do you want to save it?");
+ }
+
+ int result = KMessageBox::questionYesNo (this,
+ question, QString::null,
+ KStdGuiItem::save (), KStdGuiItem::cancel ());
+
+ if (result == KMessageBox::Yes)
+ {
+ // save() is smart enough to pop up a filedialog if it's a
+ // remote file that should be saved locally
+ if (!save (true/*localOnly*/))
+ {
+ // save failed or aborted - don't set the wallpaper
+ return;
+ }
+ }
+ else
+ {
+ // don't want to save - don't set wallpaper
+ return;
+ }
+ }
+
+
+ QByteArray data;
+ QDataStream dataStream (data, IO_WriteOnly);
+
+ // write path
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::setAsWallpaper() path="
+ << m_document->url ().path () << endl;
+#endif
+ dataStream << QString (m_document->url ().path ());
+
+ // write position:
+ //
+ // SYNC: kdebase/kcontrol/background/bgsettings.h:
+ // 1 = Centered
+ // 2 = Tiled
+ // 6 = Scaled
+ // 9 = lastWallpaperMode
+ //
+ // Why restrict the user to Centered & Tiled?
+ // Why don't we let the user choose if it should be common to all desktops?
+ // Why don't we rewrite the Background control page?
+ //
+ // Answer: This is supposed to be a quick & convenient feature.
+ //
+ // If you want more options, go to kcontrol for that kind of
+ // flexiblity. We don't want to slow down average users, who see way too
+ // many dialogs already and probably haven't even heard of "Centered Maxpect"...
+ //
+ dataStream << int (centered ? 1 : 2);
+
+
+ // I'm going to all this trouble because the user might not have kdebase
+ // installed so kdebase/kdesktop/KBackgroundIface.h might not be around
+ // to be compiled in (where user == developer :))
+ if (!KApplication::dcopClient ()->send ("kdesktop", "KBackgroundIface",
+ "setWallpaper(QString,int)", data))
+ {
+ KMessageBox::sorry (this, i18n ("Could not change wallpaper."));
+ }
+}
+
+// private slot
+void kpMainWindow::slotSetAsWallpaperCentered ()
+{
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+ setAsWallpaper (true/*centered*/);
+}
+
+// private slot
+void kpMainWindow::slotSetAsWallpaperTiled ()
+{
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+ setAsWallpaper (false/*tiled*/);
+}
+
+
+// private slot
+void kpMainWindow::slotClose ()
+{
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotClose()" << endl;
+#endif
+
+ if (!queryClose ())
+ return;
+
+ setDocument (0);
+}
+
+// private slot
+void kpMainWindow::slotQuit ()
+{
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotQuit()" << endl;
+#endif
+
+ close (); // will call queryClose()
+}
+
diff --git a/kolourpaint/kpmainwindow_help.cpp b/kolourpaint/kpmainwindow_help.cpp
new file mode 100644
index 00000000..fb1fc790
--- /dev/null
+++ b/kolourpaint/kpmainwindow_help.cpp
@@ -0,0 +1,219 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <kpmainwindow.h>
+#include <kpmainwindow_p.h>
+
+#include <dcopclient.h>
+#include <kaction.h>
+#include <kactivelabel.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kdialogbase.h>
+#include <krun.h>
+#include <klocale.h>
+#include <kshortcut.h>
+
+#include <kptool.h>
+
+
+// private
+void kpMainWindow::setupHelpMenuActions ()
+{
+ KActionCollection *ac = actionCollection ();
+
+
+ // Explanation for action name:
+ // "Taking" is like a digital camera when you record the image and is
+ // analogous to pressing PrintScreen. However, "Acquiring" is when
+ // the image is brought into KolourPaint, just as you would acquire
+ // from a digital camera in future versions of KolourPaint. Hence
+ // "Acquiring" is more appropriate.
+ // -- Thurston
+ d->m_actionHelpTakingScreenshots = new KAction (
+ i18n ("Acquiring &Screenshots"), 0,
+ this, SLOT (slotHelpTakingScreenshots ()),
+ ac, "help_taking_screenshots");
+
+
+ enableHelpMenuDocumentActions (false);
+}
+
+// private
+void kpMainWindow::enableHelpMenuDocumentActions (bool /*enable*/)
+{
+}
+
+
+// SYNC: kdebase/kwin/kwinbindings.cpp
+static QString printScreenShortcutString ()
+{
+ KConfigGroupSaver cfgGroupSaver (KGlobal::config (), "Global Shortcuts");
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ // TODO: i18n() entry name? kwinbindings.cpp seems to but it doesn't
+ // make sense.
+ const QString cfgEntryString = cfg->readEntry ("Desktop Screenshot");
+
+
+ // (only use 1st key sequence, if it exists)
+ const QString humanReadableShortcut =
+ KShortcut (cfgEntryString).seq (0).toString ();
+
+ if (!humanReadableShortcut.isEmpty ())
+ {
+ return humanReadableShortcut;
+ }
+ else
+ {
+ // (localised)
+ return KKey (Qt::CTRL + Qt::Key_Print).toString ();
+ }
+}
+
+
+// private slot
+void kpMainWindow::slotHelpTakingScreenshots ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotHelpTakingScreenshots()" << endl;
+#endif
+
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+
+ // TODO: Totally bogus logic if kwin not running under same user as KolourPaint.
+ // SYNC: KWin contains PrintScreen key logic
+ QCStringList dcopApps = KApplication::dcopClient ()->registeredApplications ();
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tdcopApps=" << dcopApps << endl;
+#endif
+ bool isRunningKDE = (dcopApps.findIndex ("kwin") >= 0);
+
+#if 0
+{
+ int i = 0;
+ FILE *fp = fopen ("/home/kdevel/kolourpaint.tmp", "rt");
+ if (fp && fscanf (fp, "Hello: %d", &i) == 1)
+ isRunningKDE = i, fclose (fp);
+}
+#endif
+
+ QString message;
+ if (isRunningKDE)
+ {
+ message = i18n
+ (
+ "<p>"
+ "To acquire a screenshot, press <b>%1</b>."
+ " The screenshot will be placed into the clipboard"
+ " and you will be able to paste it in KolourPaint."
+ "</p>"
+
+ "<p>"
+ "You may configure the <b>Desktop Screenshot</b> shortcut"
+ " in the KDE Control Center"
+ " module <a href=\"configure kde shortcuts\">Keyboard Shortcuts</a>."
+ "</p>"
+
+ "<p>Alternatively, you may try the application"
+ " <a href=\"run ksnapshot\">KSnapshot</a>."
+ "</p>"
+ );
+ }
+ else
+ {
+ message = i18n
+ (
+ "<p>"
+ "You do not appear to be running KDE."
+ "</p>"
+
+ // We tell them this much even though they aren't running KDE
+ // to entice them to use KDE since it's so easy.
+ "<p>"
+ "Once you have loaded KDE:<br>"
+ "<blockquote>"
+ "To acquire a screenshot, press <b>%1</b>."
+ " The screenshot will be placed into the clipboard"
+ " and you will be able to paste it in KolourPaint."
+ "</blockquote>"
+ "</p>"
+
+ "<p>Alternatively, you may try the application"
+ " <a href=\"run ksnapshot\">KSnapshot</a>."
+ "</p>"
+ );
+ }
+
+ // TODO: Totally bogus logic if kwin not running under same user as KolourPaint.
+ message = message.arg (::printScreenShortcutString ());
+
+ // Add extra vertical space
+ message += "<p>&nbsp;</p>";
+
+
+ KDialogBase dlg (this, "helpTakingScreenshotsDialog", true/*modal*/,
+ i18n ("Acquiring Screenshots"),
+ KDialogBase::Close, KDialogBase::Close/*default btn*/,
+ true/*separator line*/);
+
+ KActiveLabel *messageLabel = new KActiveLabel (message, &dlg);
+ disconnect (messageLabel, SIGNAL (linkClicked (const QString &)),
+ messageLabel, SLOT (openLink (const QString &)));
+ connect (messageLabel, SIGNAL (linkClicked (const QString &)),
+ this, SLOT (slotHelpTakingScreenshotsFollowLink (const QString &)));
+
+ dlg.setMainWidget (messageLabel);
+
+ dlg.exec ();
+}
+
+// private
+void kpMainWindow::slotHelpTakingScreenshotsFollowLink (const QString &link)
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotHelpTakingScreenshotsFollowLink("
+ << link << ")" << endl;
+#endif
+
+ if (link == "configure kde shortcuts")
+ {
+ KRun::runCommand ("kcmshell keys");
+ }
+ else if (link == "run ksnapshot")
+ {
+ KRun::runCommand ("ksnapshot");
+ }
+ else
+ {
+ kdError () << "kpMainWindow::slotHelpTakingScreenshotsFollowLink("
+ << link << ")" << endl;
+ }
+}
diff --git a/kolourpaint/kpmainwindow_image.cpp b/kolourpaint/kpmainwindow_image.cpp
new file mode 100644
index 00000000..7f662af7
--- /dev/null
+++ b/kolourpaint/kpmainwindow_image.cpp
@@ -0,0 +1,474 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <kpmainwindow.h>
+#include <kpmainwindow_p.h>
+
+#include <qcolor.h>
+#include <qsize.h>
+
+#include <kaction.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmenubar.h>
+
+#include <kpcolor.h>
+#include <kpdefs.h>
+#include <kpcoloreffect.h>
+#include <kpcolortoolbar.h>
+#include <kpcommandhistory.h>
+#include <kpdocument.h>
+#include <kpeffectinvert.h>
+#include <kpeffectreducecolors.h>
+#include <kpeffectsdialog.h>
+#include <kpselection.h>
+#include <kptool.h>
+#include <kptoolautocrop.h>
+#include <kptoolclear.h>
+#include <kptoolconverttograyscale.h>
+#include <kptoolcrop.h>
+#include <kptoolflip.h>
+#include <kptoolresizescale.h>
+#include <kptoolrotate.h>
+#include <kptoolselection.h>
+#include <kptoolskew.h>
+#include <kpviewmanager.h>
+
+
+// private
+bool kpMainWindow::isSelectionActive () const
+{
+ return (m_document ? bool (m_document->selection ()) : false);
+}
+
+// private
+bool kpMainWindow::isTextSelection () const
+{
+ return (m_document && m_document->selection () &&
+ m_document->selection ()->isText ());
+}
+
+
+// private
+QString kpMainWindow::autoCropText () const
+{
+ return kpToolAutoCropCommand::name (isSelectionActive (),
+ kpToolAutoCropCommand::ShowAccel);
+}
+
+
+// private
+void kpMainWindow::setupImageMenuActions ()
+{
+ KActionCollection *ac = actionCollection ();
+
+ m_actionResizeScale = new KAction (i18n ("R&esize / Scale..."), Qt::CTRL + Qt::Key_E,
+ this, SLOT (slotResizeScale ()), ac, "image_resize_scale");
+
+ m_actionCrop = new KAction (i18n ("Se&t as Image (Crop)"), Qt::CTRL + Qt::Key_T,
+ this, SLOT (slotCrop ()), ac, "image_crop");
+
+ m_actionAutoCrop = new KAction (autoCropText (), Qt::CTRL + Qt::Key_U,
+ this, SLOT (slotAutoCrop ()), ac, "image_auto_crop");
+
+ m_actionFlip = new KAction (i18n ("&Flip..."), Qt::CTRL + Qt::Key_F,
+ this, SLOT (slotFlip ()), ac, "image_flip");
+
+ m_actionRotate = new KAction (i18n ("&Rotate..."), Qt::CTRL + Qt::Key_R,
+ this, SLOT (slotRotate ()), ac, "image_rotate");
+
+ m_actionSkew = new KAction (i18n ("S&kew..."), Qt::CTRL + Qt::Key_K,
+ this, SLOT (slotSkew ()), ac, "image_skew");
+
+ m_actionConvertToBlackAndWhite = new KAction (i18n ("Reduce to Mo&nochrome (Dithered)"), 0,
+ this, SLOT (slotConvertToBlackAndWhite ()), ac, "image_convert_to_black_and_white");
+
+ m_actionConvertToGrayscale = new KAction (i18n ("Reduce to &Grayscale"), 0,
+ this, SLOT (slotConvertToGrayscale ()), ac, "image_convert_to_grayscale");
+
+ m_actionInvertColors = new KAction (i18n ("&Invert Colors"), Qt::CTRL + Qt::Key_I,
+ this, SLOT (slotInvertColors ()), ac, "image_invert_colors");
+
+ m_actionClear = new KAction (i18n ("C&lear"), Qt::CTRL + Qt::SHIFT + Qt::Key_N,
+ this, SLOT (slotClear ()), ac, "image_clear");
+
+ m_actionMoreEffects = new KAction (i18n ("&More Effects..."), Qt::CTRL + Qt::Key_M,
+ this, SLOT (slotMoreEffects ()), ac, "image_more_effects");
+
+ enableImageMenuDocumentActions (false);
+}
+
+// private
+void kpMainWindow::enableImageMenuDocumentActions (bool enable)
+{
+ m_actionResizeScale->setEnabled (enable);
+ m_actionCrop->setEnabled (enable);
+ m_actionAutoCrop->setEnabled (enable);
+ m_actionFlip->setEnabled (enable);
+ m_actionRotate->setEnabled (enable);
+ m_actionSkew->setEnabled (enable);
+ m_actionConvertToBlackAndWhite->setEnabled (enable);
+ m_actionConvertToGrayscale->setEnabled (enable);
+ m_actionInvertColors->setEnabled (enable);
+ m_actionClear->setEnabled (enable);
+ m_actionMoreEffects->setEnabled (enable);
+
+ m_imageMenuDocumentActionsEnabled = enable;
+}
+
+
+// private slot
+void kpMainWindow::slotImageMenuUpdateDueToSelection ()
+{
+ KMenuBar *mBar = menuBar ();
+ if (!mBar) // just in case
+ return;
+
+ int mBarNumItems = (int) mBar->count ();
+ for (int index = 0; index < mBarNumItems; index++)
+ {
+ int id = mBar->idAt (index);
+
+ // SYNC: kolourpaintui.rc
+ QString menuBarItemTextImage = i18n ("&Image");
+ QString menuBarItemTextSelection = i18n ("Select&ion");
+
+ const QString menuBarItemText = mBar->text (id);
+ if (menuBarItemText == menuBarItemTextImage ||
+ menuBarItemText == menuBarItemTextSelection)
+ {
+ if (isSelectionActive ())
+ mBar->changeItem (id, menuBarItemTextSelection);
+ else
+ mBar->changeItem (id, menuBarItemTextImage);
+
+ break;
+ }
+ }
+
+
+ m_actionResizeScale->setEnabled (m_imageMenuDocumentActionsEnabled);
+ m_actionCrop->setEnabled (m_imageMenuDocumentActionsEnabled &&
+ isSelectionActive ());
+
+ const bool enable = (m_imageMenuDocumentActionsEnabled && !isTextSelection ());
+ m_actionAutoCrop->setText (autoCropText ());
+ m_actionAutoCrop->setEnabled (enable);
+ m_actionFlip->setEnabled (enable);
+ m_actionRotate->setEnabled (enable);
+ m_actionSkew->setEnabled (enable);
+ m_actionConvertToBlackAndWhite->setEnabled (enable);
+ m_actionConvertToGrayscale->setEnabled (enable);
+ m_actionInvertColors->setEnabled (enable);
+ m_actionClear->setEnabled (enable);
+ m_actionMoreEffects->setEnabled (enable);
+}
+
+
+// public
+kpColor kpMainWindow::backgroundColor (bool ofSelection) const
+{
+ if (ofSelection)
+ return kpColor::transparent;
+ else
+ {
+ if (m_colorToolBar)
+ return m_colorToolBar->backgroundColor ();
+ else
+ {
+ kdError () << "kpMainWindow::backgroundColor() without colorToolBar" << endl;
+ return kpColor::invalid;
+ }
+ }
+}
+
+
+// public
+void kpMainWindow::addImageOrSelectionCommand (kpCommand *cmd,
+ bool addSelCreateCmdIfSelAvail,
+ bool addSelPullCmdIfSelAvail)
+{
+#if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "kpMainWindow::addImageOrSelectionCommand()"
+ << " addSelCreateCmdIfSelAvail=" << addSelCreateCmdIfSelAvail
+ << " addSelPullCmdIfSelAvail=" << addSelPullCmdIfSelAvail
+ << endl;
+#endif
+
+ if (!m_document)
+ {
+ kdError () << "kpMainWindow::addImageOrSelectionCommand() without doc" << endl;
+ return;
+ }
+
+
+ if (m_viewManager)
+ m_viewManager->setQueueUpdates ();
+
+
+ kpSelection *sel = m_document->selection ();
+#if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "\tsel=" << sel
+ << " sel->pixmap=" << (sel ? sel->pixmap () : 0)
+ << endl;
+#endif
+ if (addSelCreateCmdIfSelAvail && sel && !sel->pixmap ())
+ {
+ // create selection region
+ kpCommand *createCommand = new kpToolSelectionCreateCommand (
+ i18n ("Selection: Create"),
+ *sel,
+ this);
+
+ if (kpToolSelectionCreateCommand::nextUndoCommandIsCreateBorder (commandHistory ()))
+ commandHistory ()->setNextUndoCommand (createCommand);
+ else
+ commandHistory ()->addCommand (createCommand,
+ false/*no exec - user already dragged out sel*/);
+ }
+
+
+ if (addSelPullCmdIfSelAvail && sel && !sel->pixmap ())
+ {
+ kpMacroCommand *macroCmd = new kpMacroCommand (cmd->name (), this);
+
+ macroCmd->addCommand (new kpToolSelectionPullFromDocumentCommand (
+ QString::null/*uninteresting child of macro cmd*/,
+ this));
+
+ macroCmd->addCommand (cmd);
+
+ m_commandHistory->addCommand (macroCmd);
+ }
+ else
+ {
+ m_commandHistory->addCommand (cmd);
+ }
+
+
+ if (m_viewManager)
+ m_viewManager->restoreQueueUpdates ();
+}
+
+// private slot
+void kpMainWindow::slotResizeScale ()
+{
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+ kpToolResizeScaleDialog dialog (this);
+ dialog.setKeepAspectRatio (d->m_resizeScaleDialogLastKeepAspect);
+
+ if (dialog.exec () && !dialog.isNoOp ())
+ {
+ kpToolResizeScaleCommand *cmd = new kpToolResizeScaleCommand (
+ dialog.actOnSelection (),
+ dialog.imageWidth (), dialog.imageHeight (),
+ dialog.type (),
+ this);
+
+ bool addSelCreateCommand = (dialog.actOnSelection () ||
+ cmd->scaleSelectionWithImage ());
+ bool addSelPullCommand = dialog.actOnSelection ();
+
+ addImageOrSelectionCommand (
+ cmd,
+ addSelCreateCommand,
+ addSelPullCommand);
+
+ // Resized document?
+ if (!dialog.actOnSelection () &&
+ dialog.type () == kpToolResizeScaleCommand::Resize)
+ {
+ // TODO: this should be the responsibility of kpDocument
+ saveDefaultDocSize (QSize (dialog.imageWidth (), dialog.imageHeight ()));
+ }
+ }
+
+
+ if (d->m_resizeScaleDialogLastKeepAspect != dialog.keepAspectRatio ())
+ {
+ d->m_resizeScaleDialogLastKeepAspect = dialog.keepAspectRatio ();
+
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupGeneral);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ cfg->writeEntry (kpSettingResizeScaleLastKeepAspect,
+ d->m_resizeScaleDialogLastKeepAspect);
+ cfg->sync ();
+ }
+}
+
+// public slot
+void kpMainWindow::slotCrop ()
+{
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+ if (!m_document || !m_document->selection ())
+ {
+ kdError () << "kpMainWindow::slotCrop() doc=" << m_document
+ << " sel=" << (m_document ? m_document->selection () : 0)
+ << endl;
+ return;
+ }
+
+
+ ::kpToolCrop (this);
+}
+
+// private slot
+void kpMainWindow::slotAutoCrop ()
+{
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+ ::kpToolAutoCrop (this);
+}
+
+// private slot
+void kpMainWindow::slotFlip ()
+{
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+ kpToolFlipDialog dialog ((bool) m_document->selection (), this);
+
+ if (dialog.exec () && !dialog.isNoOp ())
+ {
+ addImageOrSelectionCommand (
+ new kpToolFlipCommand (m_document->selection (),
+ dialog.getHorizontalFlip (), dialog.getVerticalFlip (),
+ this));
+ }
+}
+
+// private slot
+void kpMainWindow::slotRotate ()
+{
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+ kpToolRotateDialog dialog ((bool) m_document->selection (), this);
+
+ if (dialog.exec () && !dialog.isNoOp ())
+ {
+ addImageOrSelectionCommand (
+ new kpToolRotateCommand (m_document->selection (),
+ dialog.angle (),
+ this));
+ }
+}
+
+// private slot
+void kpMainWindow::slotSkew ()
+{
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+ kpToolSkewDialog dialog ((bool) m_document->selection (), this);
+
+ if (dialog.exec () && !dialog.isNoOp ())
+ {
+ addImageOrSelectionCommand (
+ new kpToolSkewCommand (m_document->selection (),
+ dialog.horizontalAngle (), dialog.verticalAngle (),
+ this));
+ }
+}
+
+// private slot
+void kpMainWindow::slotConvertToBlackAndWhite ()
+{
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+ addImageOrSelectionCommand (
+ new kpEffectReduceColorsCommand (1/*depth*/, true/*dither*/,
+ m_document->selection (), this));
+}
+
+// private slot
+void kpMainWindow::slotConvertToGrayscale ()
+{
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+ addImageOrSelectionCommand (
+ new kpToolConvertToGrayscaleCommand (m_document->selection (), this));
+}
+
+// private slot
+void kpMainWindow::slotInvertColors ()
+{
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+ addImageOrSelectionCommand (
+ new kpEffectInvertCommand (m_document->selection (), this));
+}
+
+// private slot
+void kpMainWindow::slotClear ()
+{
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+ addImageOrSelectionCommand (
+ new kpToolClearCommand (m_document->selection (), this));
+}
+
+// private slot
+void kpMainWindow::slotMoreEffects ()
+{
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+ kpEffectsDialog dialog ((bool) m_document->selection (), this);
+ dialog.selectEffect (d->m_moreEffectsDialogLastEffect);
+
+ if (dialog.exec () && !dialog.isNoOp ())
+ {
+ addImageOrSelectionCommand (dialog.createCommand ());
+ }
+
+
+ if (d->m_moreEffectsDialogLastEffect != dialog.selectedEffect ())
+ {
+ d->m_moreEffectsDialogLastEffect = dialog.selectedEffect ();
+
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupGeneral);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ cfg->writeEntry (kpSettingMoreEffectsLastEffect,
+ d->m_moreEffectsDialogLastEffect);
+ cfg->sync ();
+ }
+}
diff --git a/kolourpaint/kpmainwindow_p.h b/kolourpaint/kpmainwindow_p.h
new file mode 100644
index 00000000..9ec94eaa
--- /dev/null
+++ b/kolourpaint/kpmainwindow_p.h
@@ -0,0 +1,49 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef KP_MAIN_WINDOW_P_H
+#define KP_MAIN_WINDOW_P_H
+
+
+class KAction;
+class KToggleAction;
+
+
+struct kpMainWindowPrivate
+{
+ bool m_configThumbnailShowRectangle;
+ KToggleAction *m_actionShowThumbnailRectangle;
+
+ KAction *m_actionHelpTakingScreenshots;
+
+ int m_moreEffectsDialogLastEffect;
+ bool m_resizeScaleDialogLastKeepAspect;
+};
+
+
+#endif // KP_MAIN_WINDOW_P_H
diff --git a/kolourpaint/kpmainwindow_settings.cpp b/kolourpaint/kpmainwindow_settings.cpp
new file mode 100644
index 00000000..609f7dfe
--- /dev/null
+++ b/kolourpaint/kpmainwindow_settings.cpp
@@ -0,0 +1,209 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <kpmainwindow.h>
+
+#include <kactionclasses.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kedittoolbar.h>
+#include <kkeydialog.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kstdaction.h>
+
+#include <kpdefs.h>
+#include <kpdocument.h>
+#include <kptool.h>
+#include <kptooltoolbar.h>
+
+
+// private
+void kpMainWindow::setupSettingsMenuActions ()
+{
+ KActionCollection *ac = actionCollection ();
+
+
+ // Settings/Toolbars |> %s
+ setStandardToolBarMenuEnabled (true);
+
+ // Settings/Show Statusbar
+ createStandardStatusBarAction ();
+
+
+ m_actionFullScreen = KStdAction::fullScreen (this, SLOT (slotFullScreen ()), ac,
+ this/*window*/);
+
+
+ m_actionShowPath = new KToggleAction (i18n ("Show &Path"), 0,
+ this, SLOT (slotShowPathToggled ()), ac, "settings_show_path");
+ m_actionShowPath->setCheckedState (i18n ("Hide &Path"));
+ slotEnableSettingsShowPath ();
+
+
+ m_actionKeyBindings = KStdAction::keyBindings (this, SLOT (slotKeyBindings ()), ac);
+ m_actionConfigureToolbars = KStdAction::configureToolbars (this, SLOT (slotConfigureToolBars ()), ac);
+ // m_actionConfigure = KStdAction::preferences (this, SLOT (slotConfigure ()), ac);
+
+
+ enableSettingsMenuDocumentActions (false);
+}
+
+// private
+void kpMainWindow::enableSettingsMenuDocumentActions (bool /*enable*/)
+{
+}
+
+
+// private slot
+void kpMainWindow::slotFullScreen ()
+{
+ if (m_actionFullScreen->isChecked ())
+ showFullScreen ();
+ else
+ showNormal ();
+}
+
+
+// private slot
+void kpMainWindow::slotEnableSettingsShowPath ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotEnableSettingsShowPath()" << endl;
+#endif
+
+ const bool enable = (m_document && !m_document->url ().isEmpty ());
+
+ m_actionShowPath->setEnabled (enable);
+ m_actionShowPath->setChecked (enable && m_configShowPath);
+}
+
+// private slot
+void kpMainWindow::slotShowPathToggled ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotShowPathToggled()" << endl;
+#endif
+
+ m_configShowPath = m_actionShowPath->isChecked ();
+
+ slotUpdateCaption ();
+
+
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupGeneral);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ cfg->writeEntry (kpSettingShowPath, m_configShowPath);
+ cfg->sync ();
+}
+
+
+// private slot
+void kpMainWindow::slotKeyBindings ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotKeyBindings()" << endl;
+#endif
+
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+
+ bool singleKeyTriggersDisabled = !actionsSingleKeyTriggersEnabled ();
+
+ if (singleKeyTriggersDisabled)
+ enableActionsSingleKeyTriggers (true);
+
+
+ if (KKeyDialog::configure (actionCollection (), this))
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tdialog accepted" << endl;
+ #endif
+ // TODO: PROPAGATE: thru mainWindow's and interprocess
+
+ if (singleKeyTriggersDisabled)
+ enableActionsSingleKeyTriggers (false);
+ }
+}
+
+
+// private slot
+void kpMainWindow::slotConfigureToolBars ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotConfigureToolBars()" << endl;
+#endif
+
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+
+ //saveMainWindowSettings (kapp->config (), autoSaveGroup ());
+
+ KEditToolbar dialog (actionCollection (),
+ QString::null/*default ui.rc file*/,
+ true/*global resource*/,
+ this/*parent*/);
+ // Clicking on OK after Apply brings up the dialog (below) again.
+ // Bug with KEditToolBar.
+ dialog.showButtonApply (false);
+ connect (&dialog, SIGNAL (newToolbarConfig ()),
+ this, SLOT (slotNewToolBarConfig ()));
+
+ dialog.exec ();
+}
+
+// private slot
+void kpMainWindow::slotNewToolBarConfig ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotNewToolBarConfig()" << endl;
+#endif
+
+ // Wouldn't it be nice if createGUI () didn't nuke all the KToolBar's?
+ // (including my non-XMLGUI ones whose states take a _lot_ of effort to
+ // restore).
+ // TODO: this message is probably unacceptable - so restore the state of
+ // my toolbars instead.
+ KMessageBox::information (this,
+ i18n ("You have to restart KolourPaint for these changes to take effect."),
+ i18n ("Toolbar Settings Changed"),
+ QString::fromLatin1 ("ToolBarSettingsChanged"));
+
+ //createGUI();
+ //applyMainWindowSettings (kapp->config (), autoSaveGroup ());
+}
+
+
+// private slot
+void kpMainWindow::slotConfigure ()
+{
+ // TODO
+}
diff --git a/kolourpaint/kpmainwindow_statusbar.cpp b/kolourpaint/kpmainwindow_statusbar.cpp
new file mode 100644
index 00000000..ed854604
--- /dev/null
+++ b/kolourpaint/kpmainwindow_statusbar.cpp
@@ -0,0 +1,417 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG_STATUS_BAR (DEBUG_KP_MAIN_WINDOW && 0)
+
+
+#include <kpmainwindow.h>
+
+#include <qlabel.h>
+#include <qstring.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kstatusbar.h>
+
+#include <kpdefs.h>
+#include <kpdocument.h>
+#include <kpsqueezedtextlabel.h>
+#include <kptool.h>
+#include <kpviewmanager.h>
+#include <kpviewscrollablecontainer.h>
+#include <kpzoomedview.h>
+
+
+// private
+void kpMainWindow::addPermanentStatusBarItem (int id, int maxTextLen)
+{
+ KStatusBar *sb = statusBar ();
+
+ QString textWithMaxLen;
+ textWithMaxLen.fill (QString::number (8/*big fat*/).at (0),
+ maxTextLen); //+ 2/*spaces on either side*/);
+
+ sb->insertFixedItem (textWithMaxLen, id, true/*permanent, place on the right*/);
+ sb->changeItem (QString::null, id);
+}
+
+// private
+void kpMainWindow::createStatusBar ()
+{
+ KStatusBar *sb = statusBar ();
+
+ // 9999 pixels "ought to be enough for anybody"
+ const int maxDimenLength = 4;
+
+ //sb->insertItem (QString::null, StatusBarItemMessage, 1/*stretch*/);
+ //sb->setItemAlignment (StatusBarItemMessage, Qt::AlignLeft | Qt::AlignVCenter);
+
+ m_statusBarMessageLabel = new kpSqueezedTextLabel (sb);
+ //m_statusBarMessageLabel->setShowEllipsis (false);
+ sb->addWidget (m_statusBarMessageLabel, 1/*stretch*/);
+
+ addPermanentStatusBarItem (StatusBarItemShapePoints,
+ (maxDimenLength + 1/*,*/ + maxDimenLength) * 2 + 3/* - */);
+ addPermanentStatusBarItem (StatusBarItemShapeSize,
+ (1/*+/-*/ + maxDimenLength) * 2 + 1/*x*/);
+
+ addPermanentStatusBarItem (StatusBarItemDocSize,
+ maxDimenLength + 1/*x*/ + maxDimenLength);
+ addPermanentStatusBarItem (StatusBarItemDocDepth,
+ 5/*XXbpp*/);
+
+ addPermanentStatusBarItem (StatusBarItemZoom,
+ 5/*1600%*/);
+
+ m_statusBarShapeLastPointsInitialised = false;
+ m_statusBarShapeLastSizeInitialised = false;
+ m_statusBarCreated = true;
+}
+
+
+
+// private slot
+void kpMainWindow::setStatusBarMessage (const QString &message)
+{
+#if DEBUG_STATUS_BAR && 1
+ kdDebug () << "kpMainWindow::setStatusBarMessage("
+ << message
+ << ") ok=" << m_statusBarCreated
+ << endl;
+#endif
+
+ if (!m_statusBarCreated)
+ return;
+
+ //statusBar ()->changeItem (message, StatusBarItemMessage);
+ m_statusBarMessageLabel->setText (message);
+}
+
+// private slot
+void kpMainWindow::setStatusBarShapePoints (const QPoint &startPoint,
+ const QPoint &endPoint)
+{
+#if DEBUG_STATUS_BAR && 0
+ kdDebug () << "kpMainWindow::setStatusBarShapePoints("
+ << startPoint << "," << endPoint
+ << ") ok=" << m_statusBarCreated
+ << endl;
+#endif
+
+ if (!m_statusBarCreated)
+ return;
+
+ if (m_statusBarShapeLastPointsInitialised &&
+ startPoint == m_statusBarShapeLastStartPoint &&
+ endPoint == m_statusBarShapeLastEndPoint)
+ {
+ #if DEBUG_STATUS_BAR && 0
+ kdDebug () << "\tNOP" << endl;
+ #endif
+ return;
+ }
+
+ if (startPoint == KP_INVALID_POINT)
+ {
+ statusBar ()->changeItem (QString::null, StatusBarItemShapePoints);
+ }
+ else if (endPoint == KP_INVALID_POINT)
+ {
+ statusBar ()->changeItem (i18n ("%1,%2")
+ .arg (startPoint.x ())
+ .arg (startPoint.y ()),
+ StatusBarItemShapePoints);
+ }
+ else
+ {
+ statusBar ()->changeItem (i18n ("%1,%2 - %3,%4")
+ .arg (startPoint.x ())
+ .arg (startPoint.y ())
+ .arg (endPoint.x ())
+ .arg (endPoint.y ()),
+ StatusBarItemShapePoints);
+ }
+
+ m_statusBarShapeLastStartPoint = startPoint;
+ m_statusBarShapeLastEndPoint = endPoint;
+ m_statusBarShapeLastPointsInitialised = true;
+}
+
+// private slot
+void kpMainWindow::setStatusBarShapeSize (const QSize &size)
+{
+#if DEBUG_STATUS_BAR && 0
+ kdDebug () << "kpMainWindow::setStatusBarShapeSize("
+ << size
+ << ") ok=" << m_statusBarCreated
+ << endl;
+#endif
+
+ if (!m_statusBarCreated)
+ return;
+
+ if (m_statusBarShapeLastSizeInitialised &&
+ size == m_statusBarShapeLastSize)
+ {
+ #if DEBUG_STATUS_BAR && 0
+ kdDebug () << "\tNOP" << endl;
+ #endif
+ return;
+ }
+
+ if (size == KP_INVALID_SIZE)
+ {
+ statusBar ()->changeItem (QString::null, StatusBarItemShapeSize);
+ }
+ else
+ {
+ statusBar ()->changeItem (i18n ("%1x%2")
+ .arg (size.width ())
+ .arg (size.height ()),
+ StatusBarItemShapeSize);
+ }
+
+ m_statusBarShapeLastSize = size;
+ m_statusBarShapeLastSizeInitialised = true;
+}
+
+// private slot
+void kpMainWindow::setStatusBarDocSize (const QSize &size)
+{
+#if DEBUG_STATUS_BAR && 0
+ kdDebug () << "kpMainWindow::setStatusBarDocSize("
+ << size
+ << ") ok=" << m_statusBarCreated
+ << endl;
+#endif
+
+ if (!m_statusBarCreated)
+ return;
+
+ if (size == KP_INVALID_SIZE)
+ {
+ statusBar ()->changeItem (QString::null, StatusBarItemDocSize);
+ }
+ else
+ {
+ statusBar ()->changeItem (i18n ("%1x%2")
+ .arg (size.width ())
+ .arg (size.height ()),
+ StatusBarItemDocSize);
+ }
+}
+
+// private slot
+void kpMainWindow::setStatusBarDocDepth (int depth)
+{
+#if DEBUG_STATUS_BAR && 0
+ kdDebug () << "kpMainWindow::setStatusBarDocDepth("
+ << depth
+ << ") ok=" << m_statusBarCreated
+ << endl;
+#endif
+
+ if (!m_statusBarCreated)
+ return;
+
+ if (depth <= 0)
+ {
+ statusBar ()->changeItem (QString::null, StatusBarItemDocDepth);
+ }
+ else
+ {
+ statusBar ()->changeItem (i18n ("%1bpp").arg (depth),
+ StatusBarItemDocDepth);
+ }
+}
+
+// private slot
+void kpMainWindow::setStatusBarZoom (int zoom)
+{
+#if DEBUG_STATUS_BAR && 0
+ kdDebug () << "kpMainWindow::setStatusBarZoom("
+ << zoom
+ << ") ok=" << m_statusBarCreated
+ << endl;
+#endif
+
+ if (!m_statusBarCreated)
+ return;
+
+ if (zoom <= 0)
+ {
+ statusBar ()->changeItem (QString::null, StatusBarItemZoom);
+ }
+ else
+ {
+ statusBar ()->changeItem (i18n ("%1%").arg (zoom),
+ StatusBarItemZoom);
+ }
+}
+
+void kpMainWindow::recalculateStatusBarMessage ()
+{
+#if DEBUG_STATUS_BAR && 1
+ kdDebug () << "kpMainWindow::recalculateStatusBarMessage()" << endl;
+#endif
+ QString scrollViewMessage = m_scrollView->statusMessage ();
+#if DEBUG_STATUS_BAR && 1
+ kdDebug () << "\tscrollViewMessage=" << scrollViewMessage << endl;
+ kdDebug () << "\tresizing doc? " << !m_scrollView->newDocSize ().isEmpty ()
+ << endl;
+ kdDebug () << "\tviewUnderCursor? "
+ << (m_viewManager && m_viewManager->viewUnderCursor ())
+ << endl;
+#endif
+
+ // HACK: To work around kpViewScrollableContainer's unreliable
+ // status messages (which in turn is due to Qt not updating
+ // QWidget::hasMouse() on drags and we needing to hack around it)
+ if (!scrollViewMessage.isEmpty () &&
+ m_scrollView->newDocSize ().isEmpty () &&
+ m_viewManager && m_viewManager->viewUnderCursor ())
+ {
+ #if DEBUG_STATUS_BAR && 1
+ kdDebug () << "\t\tnot resizing & viewUnderCursor - message is wrong - clearing"
+ << endl;
+ #endif
+ m_scrollView->blockSignals (true);
+ m_scrollView->clearStatusMessage ();
+ m_scrollView->blockSignals (false);
+
+ scrollViewMessage = QString::null;
+ #if DEBUG_STATUS_BAR && 1
+ kdDebug () << "\t\t\tdone" << endl;
+ #endif
+ }
+
+ if (!scrollViewMessage.isEmpty ())
+ {
+ setStatusBarMessage (scrollViewMessage);
+ }
+ else
+ {
+ const kpTool *t = tool ();
+ if (t)
+ {
+ setStatusBarMessage (t->userMessage ());
+ }
+ else
+ {
+ setStatusBarMessage ();
+ }
+ }
+}
+
+// private slot
+void kpMainWindow::recalculateStatusBarShape ()
+{
+#if DEBUG_STATUS_BAR && 0
+ kdDebug () << "kpMainWindow::recalculateStatusBarShape()" << endl;
+#endif
+
+ QSize docResizeTo = m_scrollView->newDocSize ();
+#if DEBUG_STATUS_BAR && 0
+ kdDebug () << "\tdocResizeTo=" << docResizeTo << endl;
+#endif
+ if (docResizeTo.isValid ())
+ {
+ const QPoint startPoint (m_document->width (), m_document->height ());
+ #if DEBUG_STATUS_BAR && 0
+ kdDebug () << "\thavedMovedFromOrgSize="
+ << m_scrollView->haveMovedFromOriginalDocSize () << endl;
+ #endif
+ if (!m_scrollView->haveMovedFromOriginalDocSize ())
+ {
+ setStatusBarShapePoints (startPoint);
+ setStatusBarShapeSize ();
+ }
+ else
+ {
+ const int newWidth = docResizeTo.width ();
+ const int newHeight = docResizeTo.height ();
+
+ setStatusBarShapePoints (startPoint, QPoint (newWidth, newHeight));
+ const QPoint sizeAsPoint (QPoint (newWidth, newHeight) - startPoint);
+ setStatusBarShapeSize (QSize (sizeAsPoint.x (), sizeAsPoint.y ()));
+ }
+ }
+ else
+ {
+ const kpTool *t = tool ();
+ #if DEBUG_STATUS_BAR && 0
+ kdDebug () << "\ttool=" << t << endl;
+ #endif
+ if (t)
+ {
+ setStatusBarShapePoints (t->userShapeStartPoint (),
+ t->userShapeEndPoint ());
+ setStatusBarShapeSize (t->userShapeSize ());
+ }
+ else
+ {
+ setStatusBarShapePoints ();
+ setStatusBarShapeSize ();
+ }
+ }
+}
+
+// private slot
+void kpMainWindow::recalculateStatusBar ()
+{
+#if DEBUG_STATUS_BAR && 1
+ kdDebug () << "kpMainWindow::recalculateStatusBar() ok="
+ << m_statusBarCreated
+ << endl;
+#endif
+
+ if (!m_statusBarCreated)
+ return;
+
+ recalculateStatusBarMessage ();
+ recalculateStatusBarShape ();
+
+ if (m_document)
+ {
+ setStatusBarDocSize (QSize (m_document->width (), m_document->height ()));
+ setStatusBarDocDepth (m_document->pixmap ()->depth ());
+ }
+ else
+ {
+ setStatusBarDocSize ();
+ setStatusBarDocDepth ();
+ }
+
+ if (m_mainView)
+ {
+ setStatusBarZoom (m_mainView->zoomLevelX ());
+ }
+ else
+ {
+ setStatusBarZoom ();
+ }
+}
diff --git a/kolourpaint/kpmainwindow_text.cpp b/kolourpaint/kpmainwindow_text.cpp
new file mode 100644
index 00000000..d5694dea
--- /dev/null
+++ b/kolourpaint/kpmainwindow_text.cpp
@@ -0,0 +1,395 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <kpmainwindow.h>
+
+#include <kactionclasses.h>
+#include <kapplication.h>
+
+#include <kconfig.h>
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kpcolortoolbar.h>
+#include <kpdefs.h>
+#include <kptextstyle.h>
+#include <kptooltext.h>
+#include <kptooltoolbar.h>
+#include <kptoolwidgetopaqueortransparent.h>
+#include <kpzoomedview.h>
+
+
+// private
+void kpMainWindow::setupTextToolBarActions ()
+{
+ KActionCollection *ac = actionCollection ();
+
+ m_actionTextFontFamily = new KFontAction (i18n ("Font Family"), 0/*shortcut*/,
+ this, SLOT (slotTextFontFamilyChanged ()), ac, "text_font_family");
+ m_actionTextFontSize = new KFontSizeAction (i18n ("Font Size"), 0/*shortcut*/,
+ this, SLOT (slotTextFontSizeChanged ()), ac, "text_font_size");
+
+ m_actionTextBold = new KToggleAction (i18n ("Bold"),
+ "text_bold"/*icon*/, 0/*shortcut*/,
+ this, SLOT (slotTextBoldChanged ()), ac, "text_bold");
+ m_actionTextItalic = new KToggleAction (i18n ("Italic"),
+ "text_italic"/*icon*/, 0/*shortcut*/,
+ this, SLOT (slotTextItalicChanged ()), ac, "text_italic");
+ m_actionTextUnderline = new KToggleAction (i18n ("Underline"),
+ "text_under"/*icon*/, 0/*shortcut*/,
+ this, SLOT (slotTextUnderlineChanged ()), ac, "text_underline");
+ m_actionTextStrikeThru = new KToggleAction (i18n ("Strike Through"),
+ "text_strike"/*icon*/, 0/*shortcut*/,
+ this, SLOT (slotTextStrikeThruChanged ()), ac, "text_strike_thru");
+
+
+ readAndApplyTextSettings ();
+
+
+ enableTextToolBarActions (false);
+}
+
+// private
+void kpMainWindow::readAndApplyTextSettings ()
+{
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupText);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ m_actionTextFontFamily->setFont (cfg->readEntry (kpSettingFontFamily, QString::fromLatin1 ("Times")));
+ m_actionTextFontSize->setFontSize (cfg->readNumEntry (kpSettingFontSize, 14));
+ m_actionTextBold->setChecked (cfg->readBoolEntry (kpSettingBold, false));
+ m_actionTextItalic->setChecked (cfg->readBoolEntry (kpSettingItalic, false));
+ m_actionTextUnderline->setChecked (cfg->readBoolEntry (kpSettingUnderline, false));
+ m_actionTextStrikeThru->setChecked (cfg->readBoolEntry (kpSettingStrikeThru, false));
+
+ m_textOldFontFamily = m_actionTextFontFamily->font ();
+ m_textOldFontSize = m_actionTextFontSize->fontSize ();
+}
+
+
+// public
+void kpMainWindow::enableTextToolBarActions (bool enable)
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::enableTextToolBarActions(" << enable << ")" << endl;
+#endif
+
+ m_actionTextFontFamily->setEnabled (enable);
+ m_actionTextFontSize->setEnabled (enable);
+ m_actionTextBold->setEnabled (enable);
+ m_actionTextItalic->setEnabled (enable);
+ m_actionTextUnderline->setEnabled (enable);
+ m_actionTextStrikeThru->setEnabled (enable);
+
+ if (textToolBar ())
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\thave toolbar - setShown" << endl;
+ #endif
+ textToolBar ()->setShown (enable);
+ }
+}
+
+
+// private slot
+void kpMainWindow::slotTextFontFamilyChanged ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotTextFontFamilyChanged() alive="
+ << m_isFullyConstructed
+ << " fontFamily="
+ << m_actionTextFontFamily->font ()
+ << endl;
+#endif
+
+ if (!m_isFullyConstructed)
+ return;
+
+ if (m_toolText && m_toolText->hasBegun ())
+ {
+ m_toolText->slotFontFamilyChanged (m_actionTextFontFamily->font (),
+ m_textOldFontFamily);
+ }
+
+ // Since editable KSelectAction's steal focus from view, switch back to mainView
+ // TODO: back to the last view
+ if (m_mainView)
+ m_mainView->setFocus ();
+
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupText);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+ cfg->writeEntry (kpSettingFontFamily, m_actionTextFontFamily->font ());
+ cfg->sync ();
+
+ m_textOldFontFamily = m_actionTextFontFamily->font ();
+}
+
+// private slot
+void kpMainWindow::slotTextFontSizeChanged ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotTextFontSizeChanged() alive="
+ << m_isFullyConstructed
+ << " fontSize="
+ << m_actionTextFontSize->fontSize ()
+ << endl;
+#endif
+
+ if (!m_isFullyConstructed)
+ return;
+
+ if (m_toolText && m_toolText->hasBegun ())
+ {
+ m_toolText->slotFontSizeChanged (m_actionTextFontSize->fontSize (),
+ m_textOldFontSize);
+ }
+
+ // Since editable KSelectAction's steal focus from view, switch back to mainView
+ // TODO: back to the last view
+ if (m_mainView)
+ m_mainView->setFocus ();
+
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupText);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+ cfg->writeEntry (kpSettingFontSize, m_actionTextFontSize->fontSize ());
+ cfg->sync ();
+
+ m_textOldFontSize = m_actionTextFontSize->fontSize ();
+}
+
+// private slot
+void kpMainWindow::slotTextBoldChanged ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotTextFontBoldChanged() alive="
+ << m_isFullyConstructed
+ << " bold="
+ << m_actionTextBold->isChecked ()
+ << endl;
+#endif
+
+ if (!m_isFullyConstructed)
+ return;
+
+ if (m_toolText && m_toolText->hasBegun ())
+ m_toolText->slotBoldChanged (m_actionTextBold->isChecked ());
+
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupText);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+ cfg->writeEntry (kpSettingBold, m_actionTextBold->isChecked ());
+ cfg->sync ();
+}
+
+// private slot
+void kpMainWindow::slotTextItalicChanged ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotTextFontItalicChanged() alive="
+ << m_isFullyConstructed
+ << " bold="
+ << m_actionTextItalic->isChecked ()
+ << endl;
+#endif
+
+ if (!m_isFullyConstructed)
+ return;
+
+ if (m_toolText && m_toolText->hasBegun ())
+ m_toolText->slotItalicChanged (m_actionTextItalic->isChecked ());
+
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupText);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+ cfg->writeEntry (kpSettingItalic, m_actionTextItalic->isChecked ());
+ cfg->sync ();
+}
+
+// private slot
+void kpMainWindow::slotTextUnderlineChanged ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotTextFontUnderlineChanged() alive="
+ << m_isFullyConstructed
+ << " underline="
+ << m_actionTextUnderline->isChecked ()
+ << endl;
+#endif
+
+ if (!m_isFullyConstructed)
+ return;
+
+ if (m_toolText && m_toolText->hasBegun ())
+ m_toolText->slotUnderlineChanged (m_actionTextUnderline->isChecked ());
+
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupText);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+ cfg->writeEntry (kpSettingUnderline, m_actionTextUnderline->isChecked ());
+ cfg->sync ();
+}
+
+// private slot
+void kpMainWindow::slotTextStrikeThruChanged ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotTextStrikeThruChanged() alive="
+ << m_isFullyConstructed
+ << " strikeThru="
+ << m_actionTextStrikeThru->isChecked ()
+ << endl;
+#endif
+
+ if (!m_isFullyConstructed)
+ return;
+
+ if (m_toolText && m_toolText->hasBegun ())
+ m_toolText->slotStrikeThruChanged (m_actionTextStrikeThru->isChecked ());
+
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupText);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+ cfg->writeEntry (kpSettingStrikeThru, m_actionTextStrikeThru->isChecked ());
+ cfg->sync ();
+}
+
+
+// public
+KToolBar *kpMainWindow::textToolBar ()
+{
+ return toolBar ("textToolBar");
+}
+
+bool kpMainWindow::isTextStyleBackgroundOpaque () const
+{
+ if (m_toolToolBar)
+ {
+ kpToolWidgetOpaqueOrTransparent *oot =
+ m_toolToolBar->toolWidgetOpaqueOrTransparent ();
+
+ if (oot)
+ {
+ return oot->isOpaque ();
+ }
+ }
+
+ return true;
+}
+
+// public
+kpTextStyle kpMainWindow::textStyle () const
+{
+ return kpTextStyle (m_actionTextFontFamily->font (),
+ m_actionTextFontSize->fontSize (),
+ m_actionTextBold->isChecked (),
+ m_actionTextItalic->isChecked (),
+ m_actionTextUnderline->isChecked (),
+ m_actionTextStrikeThru->isChecked (),
+ m_colorToolBar ? m_colorToolBar->foregroundColor () : kpColor::invalid,
+ m_colorToolBar ? m_colorToolBar->backgroundColor () : kpColor::invalid,
+ isTextStyleBackgroundOpaque ());
+}
+
+// public
+void kpMainWindow::setTextStyle (const kpTextStyle &textStyle_)
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::setTextStyle()" << endl;
+#endif
+
+ m_settingTextStyle++;
+
+
+ if (textStyle_.fontFamily () != m_actionTextFontFamily->font ())
+ {
+ m_actionTextFontFamily->setFont (textStyle_.fontFamily ());
+ slotTextFontFamilyChanged ();
+ }
+
+ if (textStyle_.fontSize () != m_actionTextFontSize->fontSize ())
+ {
+ m_actionTextFontSize->setFontSize (textStyle_.fontSize ());
+ slotTextFontSizeChanged ();
+ }
+
+ if (textStyle_.isBold () != m_actionTextBold->isChecked ())
+ {
+ m_actionTextBold->setChecked (textStyle_.isBold ());
+ slotTextBoldChanged ();
+ }
+
+ if (textStyle_.isItalic () != m_actionTextItalic->isChecked ())
+ {
+ m_actionTextItalic->setChecked (textStyle_.isItalic ());
+ slotTextItalicChanged ();
+ }
+
+ if (textStyle_.isUnderline () != m_actionTextUnderline->isChecked ())
+ {
+ m_actionTextUnderline->setChecked (textStyle_.isUnderline ());
+ slotTextUnderlineChanged ();
+ }
+
+ if (textStyle_.isStrikeThru () != m_actionTextStrikeThru->isChecked ())
+ {
+ m_actionTextStrikeThru->setChecked (textStyle_.isStrikeThru ());
+ slotTextStrikeThruChanged ();
+ }
+
+
+ if (textStyle_.foregroundColor () != m_colorToolBar->foregroundColor ())
+ {
+ m_colorToolBar->setForegroundColor (textStyle_.foregroundColor ());
+ }
+
+ if (textStyle_.backgroundColor () != m_colorToolBar->backgroundColor ())
+ {
+ m_colorToolBar->setBackgroundColor (textStyle_.backgroundColor ());
+ }
+
+
+ if (textStyle_.isBackgroundOpaque () != isTextStyleBackgroundOpaque ())
+ {
+ if (m_toolToolBar)
+ {
+ kpToolWidgetOpaqueOrTransparent *oot =
+ m_toolToolBar->toolWidgetOpaqueOrTransparent ();
+
+ if (oot)
+ {
+ oot->setOpaque (textStyle_.isBackgroundOpaque ());
+ }
+ }
+ }
+
+
+ m_settingTextStyle--;
+}
+
+// public
+int kpMainWindow::settingTextStyle () const
+{
+ return m_settingTextStyle;
+}
+
diff --git a/kolourpaint/kpmainwindow_tools.cpp b/kolourpaint/kpmainwindow_tools.cpp
new file mode 100644
index 00000000..fb86f91f
--- /dev/null
+++ b/kolourpaint/kpmainwindow_tools.cpp
@@ -0,0 +1,646 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <kpmainwindow.h>
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kpcolortoolbar.h>
+#include <kpcommandhistory.h>
+#include <kpdocument.h>
+#include <kpselectiontransparency.h>
+#include <kpsinglekeytriggersaction.h>
+#include <kptool.h>
+#include <kptoolaction.h>
+#include <kptoolairspray.h>
+#include <kptoolbrush.h>
+#include <kptoolcolorpicker.h>
+#include <kptoolcolorwasher.h>
+#include <kptoolcurve.h>
+#include <kptoolellipticalselection.h>
+#include <kptoolellipse.h>
+#include <kptooleraser.h>
+#include <kptoolfloodfill.h>
+#include <kptoolfreeformselection.h>
+#include <kptoolline.h>
+#include <kptoolpen.h>
+#include <kptoolpolygon.h>
+#include <kptoolpolyline.h>
+#include <kptoolrectangle.h>
+#include <kptoolrectselection.h>
+#include <kptoolresizescale.h>
+#include <kptoolroundedrectangle.h>
+#include <kptooltext.h>
+#include <kptooltoolbar.h>
+#include <kptoolwidgetopaqueortransparent.h>
+#include <kpviewscrollablecontainer.h>
+#include <kpzoomedview.h>
+
+
+// private
+void kpMainWindow::setupToolActions ()
+{
+ m_tools.setAutoDelete (true);
+
+ m_tools.append (m_toolFreeFormSelection = new kpToolFreeFormSelection (this));
+ m_tools.append (m_toolRectSelection = new kpToolRectSelection (this));
+
+ m_tools.append (m_toolEllipticalSelection = new kpToolEllipticalSelection (this));
+ m_tools.append (m_toolText = new kpToolText (this));
+
+ m_tools.append (m_toolLine = new kpToolLine (this));
+ m_tools.append (m_toolPen = new kpToolPen (this));
+
+ m_tools.append (m_toolEraser = new kpToolEraser (this));
+ m_tools.append (m_toolBrush = new kpToolBrush (this));
+
+ m_tools.append (m_toolFloodFill = new kpToolFloodFill (this));
+ m_tools.append (m_toolColorPicker = new kpToolColorPicker (this));
+
+ m_tools.append (m_toolColorWasher = new kpToolColorWasher (this));
+ m_tools.append (m_toolAirSpray = new kpToolAirSpray (this));
+
+ m_tools.append (m_toolRoundedRectangle = new kpToolRoundedRectangle (this));
+ m_tools.append (m_toolRectangle = new kpToolRectangle (this));
+
+ m_tools.append (m_toolPolygon = new kpToolPolygon (this));
+ m_tools.append (m_toolEllipse = new kpToolEllipse (this));
+
+ m_tools.append (m_toolPolyline = new kpToolPolyline (this));
+ m_tools.append (m_toolCurve = new kpToolCurve (this));
+
+
+ KActionCollection *ac = actionCollection ();
+
+ m_actionPrevToolOptionGroup1 = new kpSingleKeyTriggersAction (
+ i18n ("Previous Tool Option (Group #1)"),
+ kpTool::shortcutForKey (Qt::Key_1),
+ this, SLOT (slotActionPrevToolOptionGroup1 ()),
+ ac, "prev_tool_option_group_1");
+ m_actionNextToolOptionGroup1 = new kpSingleKeyTriggersAction (
+ i18n ("Next Tool Option (Group #1)"),
+ kpTool::shortcutForKey (Qt::Key_2),
+ this, SLOT (slotActionNextToolOptionGroup1 ()),
+ ac, "next_tool_option_group_1");
+
+ m_actionPrevToolOptionGroup2 = new kpSingleKeyTriggersAction (
+ i18n ("Previous Tool Option (Group #2)"),
+ kpTool::shortcutForKey (Qt::Key_3),
+ this, SLOT (slotActionPrevToolOptionGroup2 ()),
+ ac, "prev_tool_option_group_2");
+ m_actionNextToolOptionGroup2 = new kpSingleKeyTriggersAction (
+ i18n ("Next Tool Option (Group #2)"),
+ kpTool::shortcutForKey (Qt::Key_4),
+ this, SLOT (slotActionNextToolOptionGroup2 ()),
+ ac, "next_tool_option_group_2");
+}
+
+// private
+void kpMainWindow::createToolBox ()
+{
+ m_toolToolBar = new kpToolToolBar (i18n ("Tool Box"), this, 2/*columns/rows*/, "Tool Box");
+ connect (m_toolToolBar, SIGNAL (sigToolSelected (kpTool *)),
+ this, SLOT (slotToolSelected (kpTool *)));
+ connect (m_toolToolBar, SIGNAL (toolWidgetOptionSelected ()),
+ this, SLOT (updateToolOptionPrevNextActionsEnabled ()));
+
+ for (QPtrList <kpTool>::const_iterator it = m_tools.begin ();
+ it != m_tools.end ();
+ it++)
+ {
+ m_toolToolBar->registerTool (*it);
+ }
+
+
+ // (from config file)
+ readLastTool ();
+
+
+ enableToolsDocumentActions (false);
+}
+
+// private
+void kpMainWindow::enableToolsDocumentActions (bool enable)
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::enableToolsDocumentsAction(" << enable << ")" << endl;
+#endif
+
+ m_toolActionsEnabled = enable;
+
+
+ if (enable && !m_toolToolBar->isEnabled ())
+ {
+ kpTool *previousTool = m_toolToolBar->previousTool ();
+
+ // select tool for enabled Tool Box
+
+ if (previousTool)
+ m_toolToolBar->selectPreviousTool ();
+ else
+ {
+ if (m_lastToolNumber >= 0 && m_lastToolNumber < (int) m_tools.count ())
+ m_toolToolBar->selectTool (m_tools.at (m_lastToolNumber));
+ else
+ m_toolToolBar->selectTool (m_toolPen);
+ }
+ }
+ else if (!enable && m_toolToolBar->isEnabled ())
+ {
+ // don't have a disabled Tool Box with an enabled Tool
+ m_toolToolBar->selectTool (0);
+ }
+
+
+ m_toolToolBar->setEnabled (enable);
+
+
+ for (QPtrList <kpTool>::const_iterator it = m_tools.begin ();
+ it != m_tools.end ();
+ it++)
+ {
+ kpToolAction *action = (*it)->action ();
+ if (action)
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tchanging enabled state of " << (*it)->name () << endl;
+ #endif
+
+ if (!enable && action->isChecked ())
+ action->setChecked (false);
+
+ action->setEnabled (enable);
+ }
+ else
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tno action for " << (*it)->name () << endl;
+ #endif
+ }
+ }
+
+
+ updateToolOptionPrevNextActionsEnabled ();
+}
+
+// private slot
+void kpMainWindow::updateToolOptionPrevNextActionsEnabled ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::updateToolOptionPrevNextActionsEnabled()"
+ << " numShownToolWidgets="
+ << m_toolToolBar->numShownToolWidgets ()
+ << endl;
+#endif
+
+ const bool enable = m_toolActionsEnabled;
+
+
+ m_actionPrevToolOptionGroup1->setEnabled (enable &&
+ m_toolToolBar->shownToolWidget (0) &&
+ m_toolToolBar->shownToolWidget (0)->hasPreviousOption ());
+ m_actionNextToolOptionGroup1->setEnabled (enable &&
+ m_toolToolBar->shownToolWidget (0) &&
+ m_toolToolBar->shownToolWidget (0)->hasNextOption ());
+
+ m_actionPrevToolOptionGroup2->setEnabled (enable &&
+ m_toolToolBar->shownToolWidget (1) &&
+ m_toolToolBar->shownToolWidget (1)->hasPreviousOption ());
+ m_actionNextToolOptionGroup2->setEnabled (enable &&
+ m_toolToolBar->shownToolWidget (1) &&
+ m_toolToolBar->shownToolWidget (1)->hasNextOption ());
+}
+
+
+// public
+kpTool *kpMainWindow::tool () const
+{
+ return m_toolToolBar ? m_toolToolBar->tool () : 0;
+}
+
+// public
+bool kpMainWindow::toolHasBegunShape () const
+{
+ kpTool *currentTool = tool ();
+ return (currentTool && currentTool->hasBegunShape ());
+}
+
+// public
+bool kpMainWindow::toolIsASelectionTool (bool includingTextTool) const
+{
+ kpTool *currentTool = tool ();
+
+ return ((currentTool == m_toolFreeFormSelection) ||
+ (currentTool == m_toolRectSelection) ||
+ (currentTool == m_toolEllipticalSelection) ||
+ (currentTool == m_toolText && includingTextTool));
+}
+
+// public
+bool kpMainWindow::toolIsTextTool () const
+{
+ return (tool () == m_toolText);
+}
+
+
+// public
+kpSelectionTransparency kpMainWindow::selectionTransparency () const
+{
+ kpToolWidgetOpaqueOrTransparent *oot = m_toolToolBar->toolWidgetOpaqueOrTransparent ();
+ if (!oot)
+ {
+ kdError () << "kpMainWindow::selectionTransparency() without opaqueOrTransparent widget" << endl;
+ return kpSelectionTransparency ();
+ }
+
+ return kpSelectionTransparency (oot->isOpaque (), backgroundColor (), m_colorToolBar->colorSimilarity ());
+}
+
+// public
+void kpMainWindow::setSelectionTransparency (const kpSelectionTransparency &transparency, bool forceColorChange)
+{
+#if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "kpMainWindow::setSelectionTransparency() isOpaque=" << transparency.isOpaque ()
+ << " color=" << (transparency.transparentColor ().isValid () ? (int *) transparency.transparentColor ().toQRgb () : 0)
+ << " forceColorChange=" << forceColorChange
+ << endl;
+#endif
+
+ kpToolWidgetOpaqueOrTransparent *oot = m_toolToolBar->toolWidgetOpaqueOrTransparent ();
+ if (!oot)
+ {
+ kdError () << "kpMainWindow::setSelectionTransparency() without opaqueOrTransparent widget" << endl;
+ return;
+ }
+
+ m_settingSelectionTransparency++;
+
+ oot->setOpaque (transparency.isOpaque ());
+ if (transparency.isTransparent () || forceColorChange)
+ {
+ m_colorToolBar->setColor (1, transparency.transparentColor ());
+ m_colorToolBar->setColorSimilarity (transparency.colorSimilarity ());
+ }
+
+ m_settingSelectionTransparency--;
+}
+
+// public
+int kpMainWindow::settingSelectionTransparency () const
+{
+ return m_settingSelectionTransparency;
+}
+
+
+// private slot
+void kpMainWindow::slotToolSelected (kpTool *tool)
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotToolSelected (" << tool << ")" << endl;
+#endif
+
+ kpTool *previousTool = m_toolToolBar ? m_toolToolBar->previousTool () : 0;
+
+ if (previousTool)
+ {
+ disconnect (previousTool, SIGNAL (movedAndAboutToDraw (const QPoint &, const QPoint &, int, bool *)),
+ this, SLOT (slotDragScroll (const QPoint &, const QPoint &, int, bool *)));
+ disconnect (previousTool, SIGNAL (endedDraw (const QPoint &)),
+ this, SLOT (slotEndDragScroll ()));
+ disconnect (previousTool, SIGNAL (cancelledShape (const QPoint &)),
+ this, SLOT (slotEndDragScroll ()));
+
+ disconnect (previousTool, SIGNAL (userMessageChanged (const QString &)),
+ this, SLOT (recalculateStatusBarMessage ()));
+ disconnect (previousTool, SIGNAL (userShapePointsChanged (const QPoint &, const QPoint &)),
+ this, SLOT (recalculateStatusBarShape ()));
+ disconnect (previousTool, SIGNAL (userShapeSizeChanged (const QSize &)),
+ this, SLOT (recalculateStatusBarShape ()));
+
+ disconnect (m_colorToolBar, SIGNAL (colorsSwapped (const kpColor &, const kpColor &)),
+ previousTool, SLOT (slotColorsSwappedInternal (const kpColor &, const kpColor &)));
+ disconnect (m_colorToolBar, SIGNAL (foregroundColorChanged (const kpColor &)),
+ previousTool, SLOT (slotForegroundColorChangedInternal (const kpColor &)));
+ disconnect (m_colorToolBar, SIGNAL (backgroundColorChanged (const kpColor &)),
+ previousTool, SLOT (slotBackgroundColorChangedInternal (const kpColor &)));
+ disconnect (m_colorToolBar, SIGNAL (colorSimilarityChanged (double, int)),
+ previousTool, SLOT (slotColorSimilarityChangedInternal (double, int)));
+ }
+
+ if (tool)
+ {
+ connect (tool, SIGNAL (movedAndAboutToDraw (const QPoint &, const QPoint &, int, bool *)),
+ this, SLOT (slotDragScroll (const QPoint &, const QPoint &, int, bool *)));
+ connect (tool, SIGNAL (endedDraw (const QPoint &)),
+ this, SLOT (slotEndDragScroll ()));
+ connect (tool, SIGNAL (cancelledShape (const QPoint &)),
+ this, SLOT (slotEndDragScroll ()));
+
+ connect (tool, SIGNAL (userMessageChanged (const QString &)),
+ this, SLOT (recalculateStatusBarMessage ()));
+ connect (tool, SIGNAL (userShapePointsChanged (const QPoint &, const QPoint &)),
+ this, SLOT (recalculateStatusBarShape ()));
+ connect (tool, SIGNAL (userShapeSizeChanged (const QSize &)),
+ this, SLOT (recalculateStatusBarShape ()));
+ recalculateStatusBar ();
+
+ connect (m_colorToolBar, SIGNAL (colorsSwapped (const kpColor &, const kpColor &)),
+ tool, SLOT (slotColorsSwappedInternal (const kpColor &, const kpColor &)));
+ connect (m_colorToolBar, SIGNAL (foregroundColorChanged (const kpColor &)),
+ tool, SLOT (slotForegroundColorChangedInternal (const kpColor &)));
+ connect (m_colorToolBar, SIGNAL (backgroundColorChanged (const kpColor &)),
+ tool, SLOT (slotBackgroundColorChangedInternal (const kpColor &)));
+ connect (m_colorToolBar, SIGNAL (colorSimilarityChanged (double, int)),
+ tool, SLOT (slotColorSimilarityChangedInternal (double, int)));
+
+
+ saveLastTool ();
+ }
+
+ updateToolOptionPrevNextActionsEnabled ();
+}
+
+
+// private
+void kpMainWindow::readLastTool ()
+{
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupTools);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ m_lastToolNumber = cfg->readNumEntry (kpSettingLastTool, -1);
+}
+
+
+// private
+int kpMainWindow::toolNumber () const
+{
+ int number = 0;
+ for (QPtrList <kpTool>::const_iterator it = m_tools.begin ();
+ it != m_tools.end ();
+ it++)
+ {
+ if (*it == tool ())
+ return number;
+
+ number++;
+ }
+
+ return -1;
+}
+
+// private
+void kpMainWindow::saveLastTool ()
+{
+ int number = toolNumber ();
+ if (number < 0 || number >= (int) m_tools.count ())
+ return;
+
+
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupTools);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ cfg->writeEntry (kpSettingLastTool, number);
+ cfg->sync ();
+}
+
+
+// private
+bool kpMainWindow::maybeDragScrollingMainView () const
+{
+ return (tool () && m_mainView &&
+ tool ()->viewUnderStartPoint () == m_mainView);
+}
+
+// private slot
+bool kpMainWindow::slotDragScroll (const QPoint &docPoint,
+ const QPoint &docLastPoint,
+ int zoomLevel,
+ bool *scrolled)
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotDragScroll() maybeDragScrolling="
+ << maybeDragScrollingMainView ()
+ << endl;
+#endif
+
+ if (maybeDragScrollingMainView ())
+ {
+ return m_scrollView->beginDragScroll (docPoint, docLastPoint, zoomLevel, scrolled);
+ }
+ else
+ {
+ return false;
+ }
+}
+
+// private slot
+bool kpMainWindow::slotEndDragScroll ()
+{
+ // (harmless if haven't started drag scroll)
+ return m_scrollView->endDragScroll ();
+}
+
+
+// private slot
+void kpMainWindow::slotBeganDocResize ()
+{
+ if (toolHasBegunShape ())
+ tool ()->endShapeInternal ();
+
+ recalculateStatusBarShape ();
+}
+
+// private slot
+void kpMainWindow::slotContinuedDocResize (const QSize &)
+{
+ recalculateStatusBarShape ();
+}
+
+// private slot
+void kpMainWindow::slotCancelledDocResize ()
+{
+ recalculateStatusBar ();
+}
+
+// private slot
+void kpMainWindow::slotEndedDocResize (const QSize &size)
+{
+#define DOC_RESIZE_COMPLETED() \
+{ \
+ m_docResizeToBeCompleted = false; \
+ recalculateStatusBar (); \
+}
+
+ // Prevent statusbar updates
+ m_docResizeToBeCompleted = true;
+
+ m_docResizeWidth = (size.width () > 0 ? size.width () : 1),
+ m_docResizeHeight = (size.height () > 0 ? size.height () : 1);
+
+ if (m_docResizeWidth == m_document->width () &&
+ m_docResizeHeight == m_document->height ())
+ {
+ DOC_RESIZE_COMPLETED ();
+ return;
+ }
+
+
+ // Blank status to avoid confusion if dialog comes up
+ setStatusBarMessage ();
+ setStatusBarShapePoints ();
+ setStatusBarShapeSize ();
+
+
+ if (kpTool::warnIfBigImageSize (m_document->width (),
+ m_document->height (),
+ m_docResizeWidth, m_docResizeHeight,
+ i18n ("<qt><p>Resizing the image to"
+ " %1x%2 may take a substantial amount of memory."
+ " This can reduce system"
+ " responsiveness and cause other application resource"
+ " problems.</p>"
+
+ "<p>Are you sure want to resize the"
+ " image?</p></qt>")
+ .arg (m_docResizeWidth)
+ .arg (m_docResizeHeight),
+ i18n ("Resize Image?"),
+ i18n ("R&esize Image"),
+ this))
+ {
+ m_commandHistory->addCommand (
+ new kpToolResizeScaleCommand (
+ false/*doc, not sel*/,
+ m_docResizeWidth, m_docResizeHeight,
+ kpToolResizeScaleCommand::Resize,
+ this));
+
+ saveDefaultDocSize (QSize (m_docResizeWidth, m_docResizeHeight));
+ }
+
+
+ DOC_RESIZE_COMPLETED ();
+
+#undef DOC_RESIZE_COMPLETED
+}
+
+// private slot
+void kpMainWindow::slotDocResizeMessageChanged (const QString &string)
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotDocResizeMessageChanged(" << string
+ << ") docResizeToBeCompleted=" << m_docResizeToBeCompleted
+ << endl;
+#else
+ (void) string;
+#endif
+
+ if (m_docResizeToBeCompleted)
+ return;
+
+ recalculateStatusBarMessage ();
+}
+
+
+// private slot
+void kpMainWindow::slotActionPrevToolOptionGroup1 ()
+{
+ if (!m_toolToolBar->shownToolWidget (0))
+ return;
+
+ m_toolToolBar->shownToolWidget (0)->selectPreviousOption ();
+ updateToolOptionPrevNextActionsEnabled ();
+}
+
+// private slot
+void kpMainWindow::slotActionNextToolOptionGroup1 ()
+{
+ if (!m_toolToolBar->shownToolWidget (0))
+ return;
+
+ m_toolToolBar->shownToolWidget (0)->selectNextOption ();
+ updateToolOptionPrevNextActionsEnabled ();
+}
+
+
+// private slot
+void kpMainWindow::slotActionPrevToolOptionGroup2 ()
+{
+ if (!m_toolToolBar->shownToolWidget (1))
+ return;
+
+ m_toolToolBar->shownToolWidget (1)->selectPreviousOption ();
+ updateToolOptionPrevNextActionsEnabled ();
+}
+
+// private slot
+void kpMainWindow::slotActionNextToolOptionGroup2 ()
+{
+ if (!m_toolToolBar->shownToolWidget (1))
+ return;
+
+ m_toolToolBar->shownToolWidget (1)->selectNextOption ();
+ updateToolOptionPrevNextActionsEnabled ();
+}
+
+
+// public slots
+
+#define SLOT_TOOL(toolName) \
+void kpMainWindow::slotTool##toolName () \
+{ \
+ if (!m_toolToolBar) \
+ return; \
+ \
+ if (tool () == m_tool##toolName) \
+ return; \
+ \
+ m_toolToolBar->selectTool (m_tool##toolName); \
+}
+
+SLOT_TOOL (AirSpray)
+SLOT_TOOL (Brush)
+SLOT_TOOL (ColorPicker)
+SLOT_TOOL (ColorWasher)
+SLOT_TOOL (Curve)
+SLOT_TOOL (Ellipse)
+SLOT_TOOL (EllipticalSelection)
+SLOT_TOOL (Eraser)
+SLOT_TOOL (FloodFill)
+SLOT_TOOL (FreeFormSelection)
+SLOT_TOOL (Line)
+SLOT_TOOL (Pen)
+SLOT_TOOL (Polygon)
+SLOT_TOOL (Polyline)
+SLOT_TOOL (Rectangle)
+SLOT_TOOL (RectSelection)
+SLOT_TOOL (RoundedRectangle)
+SLOT_TOOL (Text)
diff --git a/kolourpaint/kpmainwindow_view.cpp b/kolourpaint/kpmainwindow_view.cpp
new file mode 100644
index 00000000..1aa9b5dc
--- /dev/null
+++ b/kolourpaint/kpmainwindow_view.cpp
@@ -0,0 +1,1151 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG_ZOOM_FLICKER 0
+
+#include <kpmainwindow.h>
+#include <kpmainwindow_p.h>
+
+#include <qdatetime.h>
+#include <qpainter.h>
+#include <qtimer.h>
+
+#include <kactionclasses.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kstdaction.h>
+
+#include <kpdefs.h>
+#include <kpdocument.h>
+#include <kpthumbnail.h>
+#include <kptool.h>
+#include <kptooltoolbar.h>
+#include <kpunzoomedthumbnailview.h>
+#include <kpviewmanager.h>
+#include <kpviewscrollablecontainer.h>
+#include <kpwidgetmapper.h>
+#include <kpzoomedview.h>
+#include <kpzoomedthumbnailview.h>
+
+
+// private
+void kpMainWindow::setupViewMenuActions ()
+{
+ m_viewMenuDocumentActionsEnabled = false;
+ m_thumbnailSaveConfigTimer = 0;
+
+
+ KActionCollection *ac = actionCollection ();
+
+ /*m_actionFullScreen = KStdAction::fullScreen (0, 0, ac);
+ m_actionFullScreen->setEnabled (false);*/
+
+
+ m_actionActualSize = KStdAction::actualSize (this, SLOT (slotActualSize ()), ac);
+ /*m_actionFitToPage = KStdAction::fitToPage (this, SLOT (slotFitToPage ()), ac);
+ m_actionFitToWidth = KStdAction::fitToWidth (this, SLOT (slotFitToWidth ()), ac);
+ m_actionFitToHeight = KStdAction::fitToHeight (this, SLOT (slotFitToHeight ()), ac);*/
+
+
+ m_actionZoomIn = KStdAction::zoomIn (this, SLOT (slotZoomIn ()), ac);
+ m_actionZoomOut = KStdAction::zoomOut (this, SLOT (slotZoomOut ()), ac);
+
+
+ m_actionZoom = new KSelectAction (i18n ("&Zoom"), 0,
+ this, SLOT (slotZoom ()), actionCollection (), "view_zoom_to");
+ m_actionZoom->setEditable (true);
+
+ // create the zoom list for the 1st call to zoomTo() below
+ m_zoomList.append (10); m_zoomList.append (25); m_zoomList.append (33);
+ m_zoomList.append (50); m_zoomList.append (67); m_zoomList.append (75);
+ m_zoomList.append (100);
+ m_zoomList.append (200); m_zoomList.append (300);
+ m_zoomList.append (400); m_zoomList.append (600); m_zoomList.append (800);
+ m_zoomList.append (1000); m_zoomList.append (1200); m_zoomList.append (1600);
+
+
+ m_actionShowGrid = new KToggleAction (i18n ("Show &Grid"), CTRL + Key_G,
+ this, SLOT (slotShowGridToggled ()), actionCollection (), "view_show_grid");
+ m_actionShowGrid->setCheckedState (i18n ("Hide &Grid"));
+
+
+ // TODO: This doesn't work when the thumbnail has focus.
+ // Testcase: Press CTRL+H twice on a fresh KolourPaint.
+ // The second CTRL+H doesn't close the thumbnail.
+ m_actionShowThumbnail = new KToggleAction (i18n ("Show T&humbnail"), CTRL + Key_H,
+ this, SLOT (slotShowThumbnailToggled ()), actionCollection (), "view_show_thumbnail");
+ m_actionShowThumbnail->setCheckedState (i18n ("Hide T&humbnail"));
+
+ // Please do not use setCheckedState() here - it wouldn't make sense
+ m_actionZoomedThumbnail = new KToggleAction (i18n ("Zoo&med Thumbnail Mode"), 0,
+ this, SLOT (slotZoomedThumbnailToggled ()), actionCollection (), "view_zoomed_thumbnail");
+
+ // For consistency with the above action, don't use setCheckedState()
+ //
+ // Also, don't use "Show Thumbnail Rectangle" because if entire doc
+ // can be seen in scrollView, checking option won't "Show" anything
+ // since rect _surrounds_ entire doc (hence, won't be rendered).
+ d->m_actionShowThumbnailRectangle = new KToggleAction (
+ i18n ("Enable Thumbnail &Rectangle"),
+ 0,
+ this, SLOT (slotThumbnailShowRectangleToggled ()),
+ actionCollection (), "view_show_thumbnail_rectangle");
+
+
+ enableViewMenuDocumentActions (false);
+}
+
+// private
+bool kpMainWindow::viewMenuDocumentActionsEnabled () const
+{
+ return m_viewMenuDocumentActionsEnabled;
+}
+
+// private
+void kpMainWindow::enableViewMenuDocumentActions (bool enable)
+{
+ m_viewMenuDocumentActionsEnabled = enable;
+
+
+ m_actionActualSize->setEnabled (enable);
+ /*m_actionFitToPage->setEnabled (enable);
+ m_actionFitToWidth->setEnabled (enable);
+ m_actionFitToHeight->setEnabled (enable);*/
+
+ m_actionZoomIn->setEnabled (enable);
+ m_actionZoomOut->setEnabled (enable);
+
+ m_actionZoom->setEnabled (enable);
+
+ actionShowGridUpdate ();
+
+ m_actionShowThumbnail->setEnabled (enable);
+ enableThumbnailOptionActions (enable);
+
+
+ // TODO: for the time being, assume that we start at zoom 100%
+ // with no grid
+
+ // This function is only called when a new document is created
+ // or an existing document is closed. So the following will
+ // always be correct:
+
+ zoomTo (100);
+}
+
+// private
+void kpMainWindow::actionShowGridUpdate ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::actionShowGridUpdate()" << endl;
+#endif
+ const bool enable = (viewMenuDocumentActionsEnabled () &&
+ m_mainView && m_mainView->canShowGrid ());
+
+ m_actionShowGrid->setEnabled (enable);
+ m_actionShowGrid->setChecked (enable && m_configShowGrid);
+}
+
+
+// private
+void kpMainWindow::sendZoomListToActionZoom ()
+{
+ QStringList items;
+
+ const QValueVector <int>::ConstIterator zoomListEnd (m_zoomList.end ());
+ for (QValueVector <int>::ConstIterator it = m_zoomList.begin ();
+ it != zoomListEnd;
+ it++)
+ {
+ items << zoomLevelToString (*it);
+ }
+
+ // Work around a KDE bug - KSelectAction::setItems() enables the action.
+ // David Faure said it won't be fixed because it's a feature used by
+ // KRecentFilesAction.
+ bool e = m_actionZoom->isEnabled ();
+ m_actionZoom->setItems (items);
+ if (e != m_actionZoom->isEnabled ())
+ m_actionZoom->setEnabled (e);
+}
+
+// private
+int kpMainWindow::zoomLevelFromString (const QString &string)
+{
+ // loop until not digit
+ int i;
+ for (i = 0; i < (int) string.length () && string.at (i).isDigit (); i++)
+ ;
+
+ // convert zoom level to number
+ bool ok = false;
+ int zoomLevel = string.left (i).toInt (&ok);
+
+ if (!ok || zoomLevel <= 0 || zoomLevel > 3200)
+ return 0; // error
+ else
+ return zoomLevel;
+}
+
+// private
+QString kpMainWindow::zoomLevelToString (int zoomLevel)
+{
+ return i18n ("%1%").arg (zoomLevel);
+}
+
+// private
+void kpMainWindow::zoomTo (int zoomLevel, bool centerUnderCursor)
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::zoomTo (" << zoomLevel << ")" << endl;
+#endif
+
+ if (zoomLevel <= 0)
+ zoomLevel = m_mainView ? m_mainView->zoomLevelX () : 100;
+
+// mute point since the thumbnail suffers from this too
+#if 0
+ else if (m_mainView && m_mainView->zoomLevelX () % 100 == 0 && zoomLevel % 100)
+ {
+ if (KMessageBox::warningContinueCancel (this,
+ i18n ("Setting the zoom level to a value that is not a multiple of 100% "
+ "results in imprecise editing and redraw glitches.\n"
+ "Do you really want to set to zoom level to %1%?")
+ .arg (zoomLevel),
+ QString::null/*caption*/,
+ i18n ("Set Zoom Level to %1%").arg (zoomLevel),
+ "DoNotAskAgain_ZoomLevelNotMultipleOf100") != KMessageBox::Continue)
+ {
+ zoomLevel = m_mainView->zoomLevelX ();
+ }
+ }
+#endif
+
+ int index = 0;
+ QValueVector <int>::Iterator it = m_zoomList.begin ();
+
+ while (index < (int) m_zoomList.count () && zoomLevel > *it)
+ it++, index++;
+
+ if (zoomLevel != *it)
+ m_zoomList.insert (it, zoomLevel);
+
+ sendZoomListToActionZoom ();
+ m_actionZoom->setCurrentItem (index);
+
+
+ if (viewMenuDocumentActionsEnabled ())
+ {
+ m_actionActualSize->setEnabled (zoomLevel != 100);
+
+ m_actionZoomIn->setEnabled (m_actionZoom->currentItem () < (int) m_zoomList.count () - 1);
+ m_actionZoomOut->setEnabled (m_actionZoom->currentItem () > 0);
+ }
+
+
+ if (m_viewManager)
+ m_viewManager->setQueueUpdates ();
+
+
+ if (m_scrollView)
+ {
+ m_scrollView->setUpdatesEnabled (false);
+ if (m_scrollView->viewport ())
+ m_scrollView->viewport ()->setUpdatesEnabled (false);
+ }
+
+ if (m_mainView)
+ {
+ m_mainView->setUpdatesEnabled (false);
+
+ if (m_scrollView && m_scrollView->viewport ())
+ {
+ // Ordinary flicker is better than the whole view moving
+ QPainter p (m_mainView);
+ p.fillRect (m_mainView->rect (),
+ m_scrollView->viewport ()->colorGroup ().background ());
+ }
+ }
+
+
+ if (m_scrollView && m_mainView)
+ {
+ #if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "\tscrollView contentsX=" << m_scrollView->contentsX ()
+ << " contentsY=" << m_scrollView->contentsY ()
+ << " contentsWidth=" << m_scrollView->contentsWidth ()
+ << " contentsHeight=" << m_scrollView->contentsHeight ()
+ << " visibleWidth=" << m_scrollView->visibleWidth ()
+ << " visibleHeight=" << m_scrollView->visibleHeight ()
+ << " oldZoomX=" << m_mainView->zoomLevelX ()
+ << " oldZoomY=" << m_mainView->zoomLevelY ()
+ << " newZoom=" << zoomLevel
+ << " mainViewX=" << m_scrollView->childX (m_mainView)
+ << " mainViewY=" << m_scrollView->childY (m_mainView)
+ << endl;
+ #endif
+
+ // TODO: when changing from no scrollbars to scrollbars, Qt lies about
+ // visibleWidth() & visibleHeight() (doesn't take into account the
+ // space taken by the would-be scrollbars) until it updates the
+ // scrollview; hence the centring is off by about 5-10 pixels.
+
+ // TODO: use visibleRect() for greater accuracy?
+
+ int viewX, viewY;
+
+ bool targetDocAvail = false;
+ double targetDocX, targetDocY;
+
+ if (centerUnderCursor &&
+ m_viewManager && m_viewManager->viewUnderCursor ())
+ {
+ kpView *const vuc = m_viewManager->viewUnderCursor ();
+ QPoint viewPoint = vuc->mouseViewPoint ();
+
+ // vuc->transformViewToDoc() returns QPoint which only has int
+ // accuracy so we do X and Y manually.
+ targetDocX = vuc->transformViewToDocX (viewPoint.x ());
+ targetDocY = vuc->transformViewToDocY (viewPoint.y ());
+ targetDocAvail = true;
+
+ if (vuc != m_mainView)
+ viewPoint = vuc->transformViewToOtherView (viewPoint, m_mainView);
+
+ viewX = viewPoint.x ();
+ viewY = viewPoint.y ();
+ }
+ else
+ {
+ viewX = m_scrollView->contentsX () +
+ QMIN (m_mainView->width (),
+ m_scrollView->visibleWidth ()) / 2;
+ viewY = m_scrollView->contentsY () +
+ QMIN (m_mainView->height (),
+ m_scrollView->visibleHeight ()) / 2;
+ }
+
+ int newCenterX = viewX * zoomLevel / m_mainView->zoomLevelX ();
+ int newCenterY = viewY * zoomLevel / m_mainView->zoomLevelY ();
+
+ m_mainView->setZoomLevel (zoomLevel, zoomLevel);
+ #if DEBUG_ZOOM_FLICKER
+ {
+ kdDebug () << "FLICKER: just setZoomLevel" << endl;
+ QTime timer; timer.start ();
+ while (timer.elapsed () < 1000)
+ ;
+ }
+ #endif
+
+ #if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "\tvisibleWidth=" << m_scrollView->visibleWidth ()
+ << " visibleHeight=" << m_scrollView->visibleHeight ()
+ << endl;
+ kdDebug () << "\tnewCenterX=" << newCenterX
+ << " newCenterY=" << newCenterY << endl;
+ #endif
+
+ m_scrollView->center (newCenterX, newCenterY);
+ #if DEBUG_ZOOM_FLICKER
+ {
+ kdDebug () << "FLICKER: just centred" << endl;
+ QTime timer; timer.start ();
+ while (timer.elapsed () < 2000)
+ ;
+ }
+ #endif
+
+ if (centerUnderCursor &&
+ targetDocAvail &&
+ m_viewManager && m_viewManager->viewUnderCursor ())
+ {
+ kpView *const vuc = m_viewManager->viewUnderCursor ();
+
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tcenterUnderCursor: reposition cursor; viewUnderCursor="
+ << vuc->name () << endl;
+ #endif
+
+ const double viewX = vuc->transformDocToViewX (targetDocX);
+ const double viewY = vuc->transformDocToViewY (targetDocY);
+ // Rounding error from zooming in and out :(
+ // TODO: do everything in terms of tool doc points in type "double".
+ const QPoint viewPoint ((int) viewX, (int) viewY);
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tdoc: (" << targetDocX << "," << targetDocY << ")"
+ << " viewUnderCursor: (" << viewX << "," << viewY << ")"
+ << endl;
+ #endif
+
+ if (vuc->clipRegion ().contains (viewPoint))
+ {
+ const QPoint globalPoint =
+ kpWidgetMapper::toGlobal (vuc, viewPoint);
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tglobalPoint=" << globalPoint << endl;
+ #endif
+
+ // TODO: Determine some sane cursor flashing indication -
+ // cursor movement is convenient but not conventional.
+ //
+ // Major problem: if using QApplication::setOverrideCursor()
+ // and in some stage of flash and window quits.
+ //
+ // Or if using kpView::setCursor() and change tool.
+ QCursor::setPos (globalPoint);
+ }
+ // e.g. Zoom to 200%, scroll mainView to bottom-right.
+ // Unzoomed Thumbnail shows top-left portion of bottom-right of
+ // mainView.
+ //
+ // Aim cursor at bottom-right of thumbnail and zoom out with
+ // CTRL+Wheel.
+ //
+ // If mainView is now small enough to largely not need scrollbars,
+ // Unzoomed Thumbnail scrolls to show _top-left_ portion
+ // _of top-left_ of mainView.
+ //
+ // Unzoomed Thumbnail no longer contains the point we zoomed out
+ // on top of.
+ else
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\twon't move cursor - would get outside view"
+ << endl;
+ #endif
+
+ // TODO: Sane cursor flashing indication that indicates
+ // that the normal cursor movement didn't happen.
+ }
+ }
+
+ #if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "\t\tcheck (contentsX=" << m_scrollView->contentsX ()
+ << ",contentsY=" << m_scrollView->contentsY ()
+ << ")" << endl;
+ #endif
+ }
+
+ if (m_mainView)
+ {
+ actionShowGridUpdate ();
+ #if DEBUG_ZOOM_FLICKER
+ {
+ kdDebug () << "FLICKER: updated grid action" << endl;
+ QTime timer; timer.start ();
+ while (timer.elapsed () < 1000)
+ ;
+ }
+ #endif
+
+
+ updateMainViewGrid ();
+ #if DEBUG_ZOOM_FLICKER
+ {
+ kdDebug () << "FLICKER: just updated grid" << endl;
+ QTime timer; timer.start ();
+ while (timer.elapsed () < 1000)
+ ;
+ }
+ #endif
+
+
+ // Since Zoom Level KSelectAction on ToolBar grabs focus after changing
+ // Zoom, switch back to the Main View.
+ // TODO: back to the last view
+ m_mainView->setFocus ();
+ #if DEBUG_ZOOM_FLICKER
+ {
+ kdDebug () << "FLICKER: just set focus to mainview" << endl;
+ QTime timer; timer.start ();
+ while (timer.elapsed () < 1000)
+ ;
+ }
+ #endif
+
+ }
+#if 1
+ // The view magnified and moved beneath the cursor
+ if (tool ())
+ tool ()->somethingBelowTheCursorChanged ();
+ #if DEBUG_ZOOM_FLICKER
+ {
+ kdDebug () << "FLICKER: signalled something below cursor" << endl;
+ QTime timer; timer.start ();
+ while (timer.elapsed () < 1000)
+ ;
+ }
+ #endif
+#endif
+
+ // HACK: make sure all of Qt's update() calls trigger
+ // kpView::paintEvent() _now_ so that they can be queued by us
+ // (until kpViewManager::restoreQueueUpdates()) to reduce flicker
+ // caused mainly by m_scrollView->center()
+ //
+ // TODO: remove flicker completely
+ //QTimer::singleShot (0, this, SLOT (finishZoomTo ()));
+
+ // Later: I don't think there is an update() that needs to be queued
+ // - let's reduce latency instead.
+ finishZoomTo ();
+}
+
+// private slot
+void kpMainWindow::finishZoomTo ()
+{
+#if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "\tkpMainWindow::finishZoomTo enter" << endl;
+#endif
+
+#if DEBUG_ZOOM_FLICKER
+{
+ kdDebug () << "FLICKER: inside finishZoomTo" << endl;
+ QTime timer; timer.start ();
+ while (timer.elapsed () < 2000)
+ ;
+}
+#endif
+
+ // TODO: setUpdatesEnabled() should really return to old value
+ // - not neccessarily "true"
+
+ if (m_mainView)
+ {
+ m_mainView->setUpdatesEnabled (true);
+ m_mainView->update ();
+ }
+
+#if DEBUG_ZOOM_FLICKER
+{
+ kdDebug () << "FLICKER: just updated mainView" << endl;
+ QTime timer; timer.start ();
+ while (timer.elapsed () < 1000)
+ ;
+}
+#endif
+
+ if (m_scrollView)
+ {
+ if (m_scrollView->viewport ())
+ {
+ m_scrollView->viewport ()->setUpdatesEnabled (true);
+ m_scrollView->viewport ()->update ();
+ }
+ #if DEBUG_ZOOM_FLICKER
+ {
+ kdDebug () << "FLICKER: just updated scrollView::viewport()" << endl;
+ QTime timer; timer.start ();
+ while (timer.elapsed () < 1000)
+ ;
+ }
+ #endif
+
+ m_scrollView->setUpdatesEnabled (true);
+ m_scrollView->update ();
+ #if DEBUG_ZOOM_FLICKER
+ {
+ kdDebug () << "FLICKER: just updated scrollView" << endl;
+ QTime timer; timer.start ();
+ while (timer.elapsed () < 1000)
+ ;
+ }
+ #endif
+
+ }
+
+
+ if (m_viewManager && m_viewManager->queueUpdates ()/*just in case*/)
+ m_viewManager->restoreQueueUpdates ();
+#if DEBUG_ZOOM_FLICKER
+{
+ kdDebug () << "FLICKER: restored vm queued updates" << endl;
+ QTime timer; timer.start ();
+ while (timer.elapsed () < 1000)
+ ;
+}
+#endif
+
+ setStatusBarZoom (m_mainView ? m_mainView->zoomLevelX () : 0);
+
+#if DEBUG_KP_MAIN_WINDOW && 1
+ kdDebug () << "\tkpMainWindow::finishZoomTo done" << endl;
+#endif
+
+#if DEBUG_ZOOM_FLICKER
+{
+ kdDebug () << "FLICKER: finishZoomTo done" << endl;
+ QTime timer; timer.start ();
+ while (timer.elapsed () < 1000)
+ ;
+}
+#endif
+}
+
+
+// private slot
+void kpMainWindow::slotActualSize ()
+{
+ zoomTo (100);
+}
+
+// private slot
+void kpMainWindow::slotFitToPage ()
+{
+ if (!m_scrollView || !m_document)
+ return;
+
+ // doc_width * zoom / 100 <= view_width &&
+ // doc_height * zoom / 100 <= view_height &&
+ // 1 <= zoom <= 3200
+
+ zoomTo (QMIN (3200, QMAX (1, QMIN (m_scrollView->visibleWidth () * 100 / m_document->width (),
+ m_scrollView->visibleHeight () * 100 / m_document->height ()))));
+}
+
+// private slot
+void kpMainWindow::slotFitToWidth ()
+{
+ if (!m_scrollView || !m_document)
+ return;
+
+ // doc_width * zoom / 100 <= view_width &&
+ // 1 <= zoom <= 3200
+
+ zoomTo (QMIN (3200, QMAX (1, m_scrollView->visibleWidth () * 100 / m_document->width ())));
+}
+
+// private slot
+void kpMainWindow::slotFitToHeight ()
+{
+ if (!m_scrollView || !m_document)
+ return;
+
+ // doc_height * zoom / 100 <= view_height &&
+ // 1 <= zoom <= 3200
+
+ zoomTo (QMIN (3200, QMAX (1, m_scrollView->visibleHeight () * 100 / m_document->height ())));
+}
+
+
+// public
+void kpMainWindow::zoomIn (bool centerUnderCursor)
+{
+ const int targetItem = m_actionZoom->currentItem () + 1;
+
+ if (targetItem >= (int) m_zoomList.count ())
+ return;
+
+ m_actionZoom->setCurrentItem (targetItem);
+ zoomAccordingToZoomAction (centerUnderCursor);
+}
+
+// public
+void kpMainWindow::zoomOut (bool centerUnderCursor)
+{
+ const int targetItem = m_actionZoom->currentItem () - 1;
+
+ if (targetItem < 0)
+ return;
+
+ m_actionZoom->setCurrentItem (targetItem);
+ zoomAccordingToZoomAction (centerUnderCursor);
+}
+
+
+// public slot
+void kpMainWindow::slotZoomIn ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotZoomIn ()" << endl;
+#endif
+
+ zoomIn (false/*don't center under cursor*/);
+}
+
+// public slot
+void kpMainWindow::slotZoomOut ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotZoomOut ()" << endl;
+#endif
+
+ zoomOut (false/*don't center under cursor*/);
+}
+
+
+// public
+void kpMainWindow::zoomAccordingToZoomAction (bool centerUnderCursor)
+{
+ zoomTo (zoomLevelFromString (m_actionZoom->currentText ()),
+ centerUnderCursor);
+}
+
+// private slot
+void kpMainWindow::slotZoom ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotZoom () index=" << m_actionZoom->currentItem ()
+ << " text='" << m_actionZoom->currentText () << "'" << endl;
+#endif
+ zoomAccordingToZoomAction (false/*don't center under cursor*/);
+}
+
+
+// private slot
+void kpMainWindow::slotShowGridToggled ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotActionShowGridToggled()" << endl;
+#endif
+
+ updateMainViewGrid ();
+
+
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupGeneral);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ cfg->writeEntry (kpSettingShowGrid, m_configShowGrid = m_actionShowGrid->isChecked ());
+ cfg->sync ();
+}
+
+// private
+void kpMainWindow::updateMainViewGrid ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::updateMainViewGrid ()" << endl;
+#endif
+
+ if (m_mainView)
+ m_mainView->showGrid (m_actionShowGrid->isChecked ());
+}
+
+
+// private
+QRect kpMainWindow::mapToGlobal (const QRect &rect) const
+{
+ return kpWidgetMapper::toGlobal (this, rect);
+}
+
+// private
+QRect kpMainWindow::mapFromGlobal (const QRect &rect) const
+{
+ return kpWidgetMapper::fromGlobal (this, rect);
+}
+
+
+// public slot
+void kpMainWindow::slotDestroyThumbnailIfNotVisible (bool tnIsVisible)
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotDestroyThumbnailIfNotVisible(isVisible="
+ << tnIsVisible
+ << ")"
+ << endl;
+#endif
+
+ if (!tnIsVisible)
+ {
+ slotDestroyThumbnailInitatedByUser ();
+ }
+}
+
+// private slot
+void kpMainWindow::slotDestroyThumbnail ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotDestroyThumbnail()" << endl;
+#endif
+
+ m_actionShowThumbnail->setChecked (false);
+ enableThumbnailOptionActions (false);
+ updateThumbnail ();
+}
+
+// private slot
+void kpMainWindow::slotDestroyThumbnailInitatedByUser ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotDestroyThumbnailInitiatedByUser()" << endl;
+#endif
+
+ m_actionShowThumbnail->setChecked (false);
+ slotShowThumbnailToggled ();
+}
+
+// private slot
+void kpMainWindow::slotCreateThumbnail ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotCreateThumbnail()" << endl;
+#endif
+
+ m_actionShowThumbnail->setChecked (true);
+ enableThumbnailOptionActions (true);
+ updateThumbnail ();
+}
+
+// public
+void kpMainWindow::notifyThumbnailGeometryChanged ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::notifyThumbnailGeometryChanged()" << endl;
+#endif
+
+ if (!m_thumbnailSaveConfigTimer)
+ {
+ m_thumbnailSaveConfigTimer = new QTimer (this);
+ connect (m_thumbnailSaveConfigTimer, SIGNAL (timeout ()),
+ this, SLOT (slotSaveThumbnailGeometry ()));
+ }
+
+ m_thumbnailSaveConfigTimer->start (500/*msec*/, true/*single shot*/);
+}
+
+// private slot
+void kpMainWindow::slotSaveThumbnailGeometry ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::saveThumbnailGeometry()" << endl;
+#endif
+
+ if (!m_thumbnail)
+ return;
+
+ QRect rect (m_thumbnail->x (), m_thumbnail->y (),
+ m_thumbnail->width (), m_thumbnail->height ());
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tthumbnail relative geometry=" << rect << endl;
+#endif
+
+ m_configThumbnailGeometry = mapFromGlobal (rect);
+
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tCONFIG: saving thumbnail geometry "
+ << m_configThumbnailGeometry
+ << endl;
+#endif
+
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupThumbnail);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ cfg->writeEntry (kpSettingThumbnailGeometry, m_configThumbnailGeometry);
+ cfg->sync ();
+}
+
+// private slot
+void kpMainWindow::slotShowThumbnailToggled ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotShowThumbnailToggled()" << endl;
+#endif
+
+ m_configThumbnailShown = m_actionShowThumbnail->isChecked ();
+
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupThumbnail);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ cfg->writeEntry (kpSettingThumbnailShown, m_configThumbnailShown);
+ cfg->sync ();
+
+
+ enableThumbnailOptionActions (m_actionShowThumbnail->isChecked ());
+ updateThumbnail ();
+}
+
+// private slot
+void kpMainWindow::updateThumbnailZoomed ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::updateThumbnailZoomed() zoomed="
+ << m_actionZoomedThumbnail->isChecked () << endl;
+#endif
+
+ if (!m_thumbnailView)
+ return;
+
+ destroyThumbnailView ();
+ createThumbnailView ();
+}
+
+// private slot
+void kpMainWindow::slotZoomedThumbnailToggled ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotZoomedThumbnailToggled()" << endl;
+#endif
+
+ m_configZoomedThumbnail = m_actionZoomedThumbnail->isChecked ();
+
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupThumbnail);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ cfg->writeEntry (kpSettingThumbnailZoomed, m_configZoomedThumbnail);
+ cfg->sync ();
+
+
+ updateThumbnailZoomed ();
+}
+
+// private slot
+void kpMainWindow::slotThumbnailShowRectangleToggled ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::slotThumbnailShowRectangleToggled()" << endl;
+#endif
+
+ d->m_configThumbnailShowRectangle = d->m_actionShowThumbnailRectangle->isChecked ();
+
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupThumbnail);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ cfg->writeEntry (kpSettingThumbnailShowRectangle, d->m_configThumbnailShowRectangle);
+ cfg->sync ();
+
+
+ if (m_thumbnailView)
+ {
+ m_thumbnailView->showBuddyViewScrollableContainerRectangle (
+ d->m_actionShowThumbnailRectangle->isChecked ());
+ }
+}
+
+// private
+void kpMainWindow::enableViewZoomedThumbnail (bool enable)
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::enableSettingsViewZoomedThumbnail()" << endl;
+#endif
+
+ m_actionZoomedThumbnail->setEnabled (enable &&
+ m_actionShowThumbnail->isChecked ());
+
+ // Note: Don't uncheck if disabled - being able to see the zoomed state
+ // before turning on the thumbnail can be useful.
+ m_actionZoomedThumbnail->setChecked (m_configZoomedThumbnail);
+}
+
+// private
+void kpMainWindow::enableViewShowThumbnailRectangle (bool enable)
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::enableViewShowThumbnailRectangle()" << endl;
+#endif
+
+ d->m_actionShowThumbnailRectangle->setEnabled (enable &&
+ m_actionShowThumbnail->isChecked ());
+
+ // Note: Don't uncheck if disabled for consistency with
+ // enableViewZoomedThumbnail()
+ d->m_actionShowThumbnailRectangle->setChecked (
+ d->m_configThumbnailShowRectangle);
+}
+
+// private
+void kpMainWindow::enableThumbnailOptionActions (bool enable)
+{
+ enableViewZoomedThumbnail (enable);
+ enableViewShowThumbnailRectangle (enable);
+}
+
+
+// private
+void kpMainWindow::createThumbnailView ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tcreating new kpView:" << endl;
+#endif
+
+ if (m_thumbnailView)
+ {
+ kdDebug () << "kpMainWindow::createThumbnailView() had to destroy view" << endl;
+ destroyThumbnailView ();
+ }
+
+ if (m_actionZoomedThumbnail->isChecked ())
+ {
+ m_thumbnailView = new kpZoomedThumbnailView (
+ m_document, m_toolToolBar, m_viewManager,
+ m_mainView,
+ 0/*scrollableContainer*/,
+ m_thumbnail, "thumbnailView");
+ }
+ else
+ {
+ m_thumbnailView = new kpUnzoomedThumbnailView (
+ m_document, m_toolToolBar, m_viewManager,
+ m_mainView,
+ 0/*scrollableContainer*/,
+ m_thumbnail, "thumbnailView");
+
+ }
+
+ m_thumbnailView->showBuddyViewScrollableContainerRectangle (
+ d->m_actionShowThumbnailRectangle->isChecked ());
+
+
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tgive kpThumbnail the kpView:" << endl;
+#endif
+ if (m_thumbnail)
+ m_thumbnail->setView (m_thumbnailView);
+ else
+ kdError () << "kpMainWindow::createThumbnailView() no thumbnail" << endl;
+
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tregistering the kpView:" << endl;
+#endif
+ if (m_viewManager)
+ m_viewManager->registerView (m_thumbnailView);
+}
+
+// private
+void kpMainWindow::destroyThumbnailView ()
+{
+ if (!m_thumbnailView)
+ return;
+
+ if (m_viewManager)
+ m_viewManager->unregisterView (m_thumbnailView);
+
+ if (m_thumbnail)
+ m_thumbnail->setView (0);
+
+ m_thumbnailView->deleteLater (); m_thumbnailView = 0;
+}
+
+
+// private
+void kpMainWindow::updateThumbnail ()
+{
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "kpMainWindow::updateThumbnail()" << endl;
+#endif
+ bool enable = m_actionShowThumbnail->isChecked ();
+
+#if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tthumbnail="
+ << bool (m_thumbnail)
+ << " action_isChecked="
+ << enable
+ << endl;
+#endif
+
+ if (bool (m_thumbnail) == enable)
+ return;
+
+ if (!m_thumbnail)
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tcreating thumbnail" << endl;
+ #endif
+
+ // Read last saved geometry before creating thumbnail & friends
+ // in case they call notifyThumbnailGeometryChanged()
+ QRect thumbnailGeometry = m_configThumbnailGeometry;
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tlast used geometry=" << thumbnailGeometry << endl;
+ #endif
+
+ m_thumbnail = new kpThumbnail (this, "thumbnail");
+
+ createThumbnailView ();
+
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tmoving thumbnail to right place" << endl;
+ #endif
+ if (!thumbnailGeometry.isEmpty () &&
+ QRect (0, 0, width (), height ()).intersects (thumbnailGeometry))
+ {
+ const QRect geometry = mapToGlobal (thumbnailGeometry);
+ m_thumbnail->resize (geometry.size ());
+ m_thumbnail->move (geometry.topLeft ());
+ }
+ else
+ {
+ if (m_scrollView)
+ {
+ const int margin = 20;
+ const int initialWidth = 160, initialHeight = 120;
+
+ QRect geometryRect (width () - initialWidth - margin * 2,
+ m_scrollView->y () + margin,
+ initialWidth,
+ initialHeight);
+
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tcreating geometry=" << geometryRect << endl;
+ #endif
+
+ geometryRect = mapToGlobal (geometryRect);
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tmap to global=" << geometryRect << endl;
+ #endif
+ m_thumbnail->resize (geometryRect.size ());
+ m_thumbnail->move (geometryRect.topLeft ());
+ }
+ }
+
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tshowing thumbnail" << endl;
+ #endif
+ m_thumbnail->show ();
+
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tconnecting thumbnail::visibilityChange to destroy slot" << endl;
+ #endif
+ connect (m_thumbnail, SIGNAL (visibilityChanged (bool)),
+ this, SLOT (slotDestroyThumbnailIfNotVisible (bool)));
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\t\tDONE" << endl;
+ #endif
+ }
+ else
+ {
+ #if DEBUG_KP_MAIN_WINDOW
+ kdDebug () << "\tdestroying thumbnail" << endl;
+ #endif
+
+ if (m_thumbnailSaveConfigTimer && m_thumbnailSaveConfigTimer->isActive ())
+ {
+ m_thumbnailSaveConfigTimer->stop ();
+ slotSaveThumbnailGeometry ();
+ }
+
+
+ destroyThumbnailView ();
+
+
+ disconnect (m_thumbnail, SIGNAL (visibilityChanged (bool)),
+ this, SLOT (slotDestroyThumbnailIfNotVisible (bool)));
+
+ m_thumbnail->deleteLater (); m_thumbnail = 0;
+ }
+}
diff --git a/kolourpaint/kpselection.cpp b/kolourpaint/kpselection.cpp
new file mode 100644
index 00000000..eb5924cf
--- /dev/null
+++ b/kolourpaint/kpselection.cpp
@@ -0,0 +1,1446 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define DEBUG_KP_SELECTION 0
+
+
+#include <kpselection.h>
+
+#include <qfont.h>
+#include <qimage.h>
+#include <qpainter.h>
+#include <qwmatrix.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kpcolorsimilaritydialog.h>
+#include <kpdefs.h>
+#include <kppixmapfx.h>
+#include <kptool.h>
+
+
+kpSelection::kpSelection (const kpSelectionTransparency &transparency)
+ : QObject (),
+ m_type (kpSelection::Rectangle),
+ m_pixmap (0)
+{
+ setTransparency (transparency);
+}
+
+kpSelection::kpSelection (Type type, const QRect &rect, const QPixmap &pixmap,
+ const kpSelectionTransparency &transparency)
+ : QObject (),
+ m_type (type),
+ m_rect (rect)
+{
+ calculatePoints ();
+ m_pixmap = pixmap.isNull () ? 0 : new QPixmap (pixmap);
+
+ setTransparency (transparency);
+}
+
+kpSelection::kpSelection (Type type, const QRect &rect, const kpSelectionTransparency &transparency)
+ : QObject (),
+ m_type (type),
+ m_rect (rect),
+ m_pixmap (0)
+{
+ calculatePoints ();
+
+ setTransparency (transparency);
+}
+
+kpSelection::kpSelection (const QRect &rect,
+ const QValueVector <QString> &textLines_,
+ const kpTextStyle &textStyle_)
+ : QObject (),
+ m_type (Text),
+ m_rect (rect),
+ m_pixmap (0),
+ m_textStyle (textStyle_)
+{
+ calculatePoints ();
+
+ setTextLines (textLines_);
+}
+
+kpSelection::kpSelection (const QPointArray &points, const QPixmap &pixmap,
+ const kpSelectionTransparency &transparency)
+ : QObject (),
+ m_type (Points),
+ m_rect (points.boundingRect ()),
+ m_points (points)
+{
+ m_pixmap = pixmap.isNull () ? 0 : new QPixmap (pixmap);
+ m_points.detach ();
+
+ setTransparency (transparency);
+}
+
+kpSelection::kpSelection (const QPointArray &points, const kpSelectionTransparency &transparency)
+ : QObject (),
+ m_type (Points),
+ m_rect (points.boundingRect ()),
+ m_points (points),
+ m_pixmap (0)
+{
+ m_points.detach ();
+
+ setTransparency (transparency);
+}
+
+kpSelection::kpSelection (const kpSelection &rhs)
+ : QObject (),
+ m_type (rhs.m_type),
+ m_rect (rhs.m_rect),
+ m_points (rhs.m_points),
+ m_pixmap (rhs.m_pixmap ? new QPixmap (*rhs.m_pixmap) : 0),
+ m_textLines (rhs.m_textLines),
+ m_textStyle (rhs.m_textStyle),
+ m_transparency (rhs.m_transparency),
+ m_transparencyMask (rhs.m_transparencyMask)
+{
+ m_points.detach ();
+}
+
+kpSelection &kpSelection::operator= (const kpSelection &rhs)
+{
+ if (this == &rhs)
+ return *this;
+
+ m_type = rhs.m_type;
+ m_rect = rhs.m_rect;
+ m_points = rhs.m_points;
+ m_points.detach ();
+
+ delete m_pixmap;
+ m_pixmap = rhs.m_pixmap ? new QPixmap (*rhs.m_pixmap) : 0;
+
+ m_textLines = rhs.m_textLines;
+ m_textStyle = rhs.m_textStyle;
+
+ m_transparency = rhs.m_transparency;
+ m_transparencyMask = rhs.m_transparencyMask;
+
+ return *this;
+}
+
+
+// friend
+QDataStream &operator<< (QDataStream &stream, const kpSelection &selection)
+{
+#if DEBUG_KP_SELECTION && 1
+ kdDebug () << "kpSelection::operator<<(sel: rect=" << selection.boundingRect ()
+ << " pixmap rect=" << (selection.pixmap () ? selection.pixmap ()->rect () : QRect ())
+ << endl;
+#endif
+ stream << int (selection.m_type);
+ stream << selection.m_rect;
+ stream << selection.m_points;
+#if DEBUG_KP_SELECTION && 1
+ kdDebug () << "\twrote type=" << int (selection.m_type) << " rect=" << selection.m_rect
+ << " and points" << endl;
+#endif
+
+ // TODO: need for text?
+ // For now we just use QTextDrag for Text Selections so this point is mute.
+ if (selection.m_pixmap)
+ {
+ const QImage image = kpPixmapFX::convertToImage (*selection.m_pixmap);
+ #if DEBUG_KP_SELECTION && 1
+ kdDebug () << "\twrote image rect=" << image.rect () << endl;
+ #endif
+ stream << image;
+ }
+ else
+ {
+ #if DEBUG_KP_SELECTION && 1
+ kdDebug () << "\twrote no image because no pixmap" << endl;
+ #endif
+ stream << QImage ();
+ }
+
+ //stream << selection.m_textLines;
+ //stream << selection.m_textStyle;
+
+ return stream;
+}
+
+// friend
+QDataStream &operator>> (QDataStream &stream, kpSelection &selection)
+{
+ selection.readFromStream (stream);
+ return stream;
+}
+
+// public
+// TODO: KolourPaint has not been tested against invalid or malicious
+// clipboard data [Bug #28].
+void kpSelection::readFromStream (QDataStream &stream,
+ const kpPixmapFX::WarnAboutLossInfo &wali)
+{
+#if DEBUG_KP_SELECTION && 1
+ kdDebug () << "kpSelection::readFromStream()" << endl;
+#endif
+ int typeAsInt;
+ stream >> typeAsInt;
+ m_type = kpSelection::Type (typeAsInt);
+#if DEBUG_KP_SELECTION && 1
+ kdDebug () << "\ttype=" << typeAsInt << endl;
+#endif
+
+ stream >> m_rect;
+ stream >> m_points;
+ m_points.detach ();
+#if DEBUG_KP_SELECTION && 1
+ kdDebug () << "\trect=" << m_rect << endl;
+ //kdDebug () << "\tpoints=" << m_points << endl;
+#endif
+
+ QImage image;
+ stream >> image;
+ delete m_pixmap;
+ if (!image.isNull ())
+ m_pixmap = new QPixmap (kpPixmapFX::convertToPixmap (image, false/*no dither*/, wali));
+ else
+ m_pixmap = 0;
+#if DEBUG_KP_SELECTION && 1
+ kdDebug () << "\timage: w=" << image.width () << " h=" << image.height ()
+ << " depth=" << image.depth () << endl;
+ if (m_pixmap)
+ {
+ kdDebug () << "\tpixmap: w=" << m_pixmap->width () << " h=" << m_pixmap->height ()
+ << endl;
+ }
+ else
+ {
+ kdDebug () << "\tpixmap: none" << endl;
+ }
+#endif
+
+ //stream >> m_textLines;
+ //stream >> m_textStyle;
+}
+
+kpSelection::~kpSelection ()
+{
+ delete m_pixmap; m_pixmap = 0;
+}
+
+
+// private
+void kpSelection::calculatePoints ()
+{
+ if (m_type == kpSelection::Points)
+ return;
+
+ if (m_type == kpSelection::Ellipse)
+ {
+ m_points.makeEllipse (m_rect.x (), m_rect.y (),
+ m_rect.width (), m_rect.height ());
+ return;
+ }
+
+ if (m_type == kpSelection::Rectangle || m_type == kpSelection::Text)
+ {
+ // OPT: not space optimal - redoes corners
+ m_points.resize (m_rect.width () * 2 + m_rect.height () * 2);
+
+ int pointsUpto = 0;
+
+ // top
+ for (int x = 0; x < m_rect.width (); x++)
+ m_points [pointsUpto++] = QPoint (m_rect.x () + x, m_rect.top ());
+
+ // right
+ for (int y = 0; y < m_rect.height (); y++)
+ m_points [pointsUpto++] = QPoint (m_rect.right (), m_rect.y () + y);
+
+ // bottom
+ for (int x = m_rect.width () - 1; x >= 0; x--)
+ m_points [pointsUpto++] = QPoint (m_rect.x () + x, m_rect.bottom ());
+
+ // left
+ for (int y = m_rect.height () - 1; y >= 0; y--)
+ m_points [pointsUpto++] = QPoint (m_rect.left (), m_rect.y () + y);
+
+ return;
+ }
+
+ kdError () << "kpSelection::calculatePoints() with unknown type" << endl;
+ return;
+}
+
+
+// public
+kpSelection::Type kpSelection::type () const
+{
+ return m_type;
+}
+
+// public
+bool kpSelection::isRectangular () const
+{
+ return (m_type == Rectangle || m_type == Text);
+}
+
+// public
+bool kpSelection::isText () const
+{
+ return (m_type == Text);
+}
+
+// public
+QString kpSelection::name () const
+{
+ if (m_type == Text)
+ return i18n ("Text");
+
+ return i18n ("Selection");
+}
+
+
+// public
+int kpSelection::size () const
+{
+ return kpPixmapFX::pointArraySize (m_points) +
+ kpPixmapFX::pixmapSize (m_pixmap) +
+ kpPixmapFX::stringSize (text ()) +
+ kpPixmapFX::pixmapSize (m_transparencyMask);
+}
+
+
+// public
+QBitmap kpSelection::maskForOwnType (bool nullForRectangular) const
+{
+ if (!m_rect.isValid ())
+ {
+ kdError () << "kpSelection::maskForOwnType() boundingRect invalid" << endl;
+ return QBitmap ();
+ }
+
+
+ if (isRectangular ())
+ {
+ if (nullForRectangular)
+ return QBitmap ();
+
+ QBitmap maskBitmap (m_rect.width (), m_rect.height ());
+ maskBitmap.fill (Qt::color1/*opaque*/);
+ return maskBitmap;
+ }
+
+
+ QBitmap maskBitmap (m_rect.width (), m_rect.height ());
+ maskBitmap.fill (Qt::color0/*transparent*/);
+
+ QPainter painter;
+ painter.begin (&maskBitmap);
+ painter.setPen (Qt::color1)/*opaque*/;
+ painter.setBrush (Qt::color1/*opaque*/);
+
+ if (m_type == kpSelection::Ellipse)
+ painter.drawEllipse (0, 0, m_rect.width (), m_rect.height ());
+ else if (m_type == kpSelection::Points)
+ {
+ QPointArray points = m_points;
+ points.detach ();
+ points.translate (-m_rect.x (), -m_rect.y ());
+
+ painter.drawPolygon (points, false/*even-odd algo*/);
+ }
+
+ painter.end ();
+
+
+ return maskBitmap;
+}
+
+
+// public
+QPoint kpSelection::topLeft () const
+{
+ return m_rect.topLeft ();
+}
+
+// public
+QPoint kpSelection::point () const
+{
+ return m_rect.topLeft ();
+}
+
+
+// public
+int kpSelection::x () const
+{
+ return m_rect.x ();
+}
+
+// public
+int kpSelection::y () const
+{
+ return m_rect.y ();
+}
+
+
+// public
+void kpSelection::moveBy (int dx, int dy)
+{
+#if DEBUG_KP_SELECTION && 1
+ kdDebug () << "kpSelection::moveBy(" << dx << "," << dy << ")" << endl;
+#endif
+
+ if (dx == 0 && dy == 0)
+ return;
+
+ QRect oldRect = boundingRect ();
+
+#if DEBUG_KP_SELECTION && 1
+ kdDebug () << "\toldRect=" << oldRect << endl;
+#endif
+
+ m_rect.moveBy (dx, dy);
+ m_points.translate (dx, dy);
+#if DEBUG_KP_SELECTION && 1
+ kdDebug () << "\tnewRect=" << m_rect << endl;
+#endif
+
+ emit changed (oldRect);
+ emit changed (boundingRect ());
+}
+
+// public
+void kpSelection::moveTo (int dx, int dy)
+{
+ moveTo (QPoint (dx, dy));
+}
+
+// public
+void kpSelection::moveTo (const QPoint &topLeftPoint)
+{
+#if DEBUG_KP_SELECTION && 1
+ kdDebug () << "kpSelection::moveTo(" << topLeftPoint << ")" << endl;
+#endif
+ QRect oldBoundingRect = boundingRect ();
+#if DEBUG_KP_SELECTION && 1
+ kdDebug () << "\toldBoundingRect=" << oldBoundingRect << endl;
+#endif
+ if (topLeftPoint == oldBoundingRect.topLeft ())
+ return;
+
+ QPoint delta (topLeftPoint - oldBoundingRect.topLeft ());
+ moveBy (delta.x (), delta.y ());
+}
+
+
+// public
+QPointArray kpSelection::points () const
+{
+ return m_points;
+}
+
+// public
+QPointArray kpSelection::pointArray () const
+{
+ return m_points;
+}
+
+// public
+QRect kpSelection::boundingRect () const
+{
+ return m_rect;
+}
+
+// public
+int kpSelection::width () const
+{
+ return boundingRect ().width ();
+}
+
+// public
+int kpSelection::height () const
+{
+ return boundingRect ().height ();
+}
+
+// public
+bool kpSelection::contains (const QPoint &point) const
+{
+ QRect rect = boundingRect ();
+
+#if DEBUG_KP_SELECTION && 1
+ kdDebug () << "kpSelection::contains(" << point
+ << ") rect==" << rect
+ << " #points=" << m_points.size ()
+ << endl;
+#endif
+
+ if (!rect.contains (point))
+ return false;
+
+ // OPT: QRegion is probably incredibly slow - cache
+ // We can't use the m_pixmap (if avail) and get the transparency of
+ // the pixel at that point as it may be transparent but still within the
+ // border
+ switch (m_type)
+ {
+ case kpSelection::Rectangle:
+ case kpSelection::Text:
+ return true;
+ case kpSelection::Ellipse:
+ return QRegion (m_rect, QRegion::Ellipse).contains (point);
+ case kpSelection::Points:
+ // TODO: make this always include the border
+ // (draw up a rect sel in this mode to see what I mean)
+ return QRegion (m_points, false/*even-odd algo*/).contains (point);
+ default:
+ return false;
+ }
+}
+
+// public
+bool kpSelection::contains (int x, int y)
+{
+ return contains (QPoint (x, y));
+}
+
+
+// public
+QPixmap *kpSelection::pixmap () const
+{
+ return m_pixmap;
+}
+
+// public
+void kpSelection::setPixmap (const QPixmap &pixmap)
+{
+ delete m_pixmap;
+ // TODO: If isText(), setting pixmap() to 0 is unexpected (implies
+ // it's a border, not a text box) but saves memory when using
+ // that kpSelection::setPixmap (QPixmap ()) hack.
+ m_pixmap = pixmap.isNull () ? 0 : new QPixmap (pixmap);
+ QRect changedRect = boundingRect ();
+
+ if (m_pixmap)
+ {
+ const bool changedSize = (m_pixmap->width () != m_rect.width () ||
+ m_pixmap->height () != m_rect.height ());
+ const bool changedFromText = (m_type == Text);
+ if (changedSize || changedFromText)
+ {
+ if (changedSize)
+ {
+ kdError () << "kpSelection::setPixmap() changes the size of the selection!"
+ << " old:"
+ << " w=" << m_rect.width ()
+ << " h=" << m_rect.height ()
+ << " new:"
+ << " w=" << m_pixmap->width ()
+ << " h=" << m_pixmap->height ()
+ << endl;
+ }
+
+ if (changedFromText)
+ {
+ kdError () << "kpSelection::setPixmap() changed from text" << endl;
+ }
+
+ m_type = kpSelection::Rectangle;
+ m_rect = QRect (m_rect.x (), m_rect.y (),
+ m_pixmap->width (), m_pixmap->height ());
+ calculatePoints ();
+
+ m_textLines = QValueVector <QString> ();
+
+ changedRect = changedRect.unite (boundingRect ());
+ }
+ }
+
+ calculateTransparencyMask ();
+
+ emit changed (changedRect);
+}
+
+
+// public
+bool kpSelection::usesBackgroundPixmapToPaint () const
+{
+ // Opaque text with transparent background needs to antialias with
+ // doc pixmap below it.
+ return (isText () &&
+ m_textStyle.foregroundColor ().isOpaque () &&
+ m_textStyle.effectiveBackgroundColor ().isTransparent ());
+}
+
+static int mostContrastingValue (int val)
+{
+ if (val <= 127)
+ return 255;
+ else
+ return 0;
+}
+
+static QRgb mostContrastingRGB (QRgb val)
+{
+ return qRgba (mostContrastingValue (qRed (val)),
+ mostContrastingValue (qGreen (val)),
+ mostContrastingValue (qBlue (val)),
+ mostContrastingValue (qAlpha (val)));
+}
+
+// private
+static void drawTextLines (QPainter *painter, QPainter *maskPainter,
+ const QRect &rect,
+ const QValueVector <QString> &textLines)
+{
+ if (!painter->clipRegion ().isEmpty () || !maskPainter->clipRegion ().isEmpty ())
+ {
+ // TODO: fix esp. before making method public
+ kdError () << "kpselection.cpp:drawTextLines() can't deal with existing painter clip regions" << endl;
+ return;
+ }
+
+
+#define PAINTER_CALL(cmd) \
+{ \
+ if (painter->isActive ()) \
+ painter->cmd; \
+ \
+ if (maskPainter->isActive ()) \
+ maskPainter->cmd; \
+}
+
+
+ // Can't do this because the line heights become
+ // >QFontMetrics::height() if you type Chinese characters (!) and then
+ // the cursor gets out of sync.
+ // PAINTER_CALL (drawText (rect, 0/*flags*/, text ()));
+
+
+#if 0
+ const QFontMetrics fontMetrics (painter->fontMetrics ());
+
+ kdDebug () << "height=" << fontMetrics.height ()
+ << " leading=" << fontMetrics.leading ()
+ << " ascent=" << fontMetrics.ascent ()
+ << " descent=" << fontMetrics.descent ()
+ << " lineSpacing=" << fontMetrics.lineSpacing ()
+ << endl;
+#endif
+
+
+ PAINTER_CALL (setClipRect (rect, QPainter::CoordPainter/*transform*/));
+
+ int baseLine = rect.y () + painter->fontMetrics ().ascent ();
+ for (QValueVector <QString>::const_iterator it = textLines.begin ();
+ it != textLines.end ();
+ it++)
+ {
+ PAINTER_CALL (drawText (rect.x (), baseLine, *it));
+ baseLine += painter->fontMetrics ().lineSpacing ();
+ }
+
+
+#undef PAINTER_CALL
+}
+
+// private
+void kpSelection::paintOpaqueText (QPixmap *destPixmap, const QRect &docRect) const
+{
+ if (!isText () || !m_textStyle.foregroundColor ().isOpaque ())
+ return;
+
+
+ const QRect modifyingRect = docRect.intersect (boundingRect ());
+ if (modifyingRect.isEmpty ())
+ return;
+
+
+ QBitmap destPixmapMask;
+ QPainter destPixmapPainter, destPixmapMaskPainter;
+
+ if (destPixmap->mask ())
+ {
+ if (m_textStyle.effectiveBackgroundColor ().isTransparent ())
+ {
+ QRect modifyingRectRelPixmap = modifyingRect;
+ modifyingRectRelPixmap.moveBy (-docRect.x (), -docRect.y ());
+
+ // Set the RGB of transparent pixels to foreground colour to avoid
+ // anti-aliasing the foreground coloured text with undefined RGBs.
+ kpPixmapFX::setPixmapAt (destPixmap,
+ modifyingRectRelPixmap,
+ kpPixmapFX::pixmapWithDefinedTransparentPixels (
+ kpPixmapFX::getPixmapAt (*destPixmap, modifyingRectRelPixmap),
+ m_textStyle.foregroundColor ().toQColor ()));
+ }
+
+ destPixmapMask = *destPixmap->mask ();
+ destPixmapMaskPainter.begin (&destPixmapMask);
+ destPixmapMaskPainter.translate (-docRect.x (), -docRect.y ());
+ destPixmapMaskPainter.setPen (Qt::color1/*opaque*/);
+ destPixmapMaskPainter.setFont (m_textStyle.font ());
+ }
+
+ destPixmapPainter.begin (destPixmap);
+ destPixmapPainter.translate (-docRect.x (), -docRect.y ());
+ destPixmapPainter.setPen (m_textStyle.foregroundColor ().toQColor ());
+ destPixmapPainter.setFont (m_textStyle.font ());
+
+
+ if (m_textStyle.effectiveBackgroundColor ().isOpaque ())
+ {
+ destPixmapPainter.fillRect (
+ boundingRect (),
+ m_textStyle.effectiveBackgroundColor ().toQColor ());
+
+ if (destPixmapMaskPainter.isActive ())
+ {
+ destPixmapMaskPainter.fillRect (
+ boundingRect (),
+ Qt::color1/*opaque*/);
+ }
+ }
+
+
+ ::drawTextLines (&destPixmapPainter, &destPixmapMaskPainter,
+ textAreaRect (),
+ textLines ());
+
+
+ if (destPixmapPainter.isActive ())
+ destPixmapPainter.end ();
+
+ if (destPixmapMaskPainter.isActive ())
+ destPixmapMaskPainter.end ();
+
+
+ if (!destPixmapMask.isNull ())
+ destPixmap->setMask (destPixmapMask);
+}
+
+// private
+QPixmap kpSelection::transparentForegroundTextPixmap () const
+{
+ if (!isText () || !m_textStyle.foregroundColor ().isTransparent ())
+ return QPixmap ();
+
+
+ QPixmap pixmap (m_rect.width (), m_rect.height ());
+ QBitmap pixmapMask (m_rect.width (), m_rect.height ());
+
+
+ // Iron out stupid case first
+ if (m_textStyle.effectiveBackgroundColor ().isTransparent ())
+ {
+ pixmapMask.fill (Qt::color0/*transparent*/);
+ pixmap.setMask (pixmapMask);
+ return pixmap;
+ }
+
+
+ // -- Foreground transparent, background opaque --
+
+
+ QFont font = m_textStyle.font ();
+ // TODO: why doesn't "font.setStyleStrategy (QFont::NoAntialias);"
+ // let us avoid the hack below?
+
+
+ QPainter pixmapPainter, pixmapMaskPainter;
+
+ pixmap.fill (m_textStyle.effectiveBackgroundColor ().toQColor ());
+ pixmapPainter.begin (&pixmap);
+ // HACK: Transparent foreground colour + antialiased fonts don't
+ // work - they don't seem to be able to draw in
+ // Qt::color0/*transparent*/ (but Qt::color1 seems Ok).
+ // So we draw in a contrasting color to the background so that
+ // we can identify the transparent pixels for manually creating
+ // the mask.
+ pixmapPainter.setPen (
+ QColor (mostContrastingRGB (m_textStyle.effectiveBackgroundColor ().toQRgb () & RGB_MASK)));
+ pixmapPainter.setFont (font);
+
+
+ pixmapMask.fill (Qt::color1/*opaque*/);
+ pixmapMaskPainter.begin (&pixmapMask);
+ pixmapMaskPainter.setPen (Qt::color0/*transparent*/);
+ pixmapMaskPainter.setFont (font);
+
+
+ QRect rect (textAreaRect ());
+ rect.moveBy (-m_rect.x (), -m_rect.y ());
+ ::drawTextLines (&pixmapPainter, &pixmapMaskPainter,
+ rect,
+ textLines ());
+
+
+ if (pixmapPainter.isActive ())
+ pixmapPainter.end ();
+
+ if (pixmapMaskPainter.isActive ())
+ pixmapMaskPainter.end ();
+
+
+#if DEBUG_KP_SELECTION
+ kdDebug () << "\tinvoking foreground transparency hack" << endl;
+#endif
+ QImage image = kpPixmapFX::convertToImage (pixmap);
+ QRgb backgroundRGB = image.pixel (0, 0); // on textBorderSize()
+
+ pixmapMaskPainter.begin (&pixmapMask);
+ for (int y = 0; y < image.height (); y++)
+ {
+ for (int x = 0; x < image.width (); x++)
+ {
+ if (image.pixel (x, y) == backgroundRGB)
+ pixmapMaskPainter.setPen (Qt::color1/*opaque*/);
+ else
+ pixmapMaskPainter.setPen (Qt::color0/*transparent*/);
+
+ pixmapMaskPainter.drawPoint (x, y);
+ }
+ }
+ pixmapMaskPainter.end ();
+
+
+ if (!pixmapMask.isNull ())
+ pixmap.setMask (pixmapMask);
+
+
+ return pixmap;
+}
+
+// public
+void kpSelection::paint (QPixmap *destPixmap, const QRect &docRect) const
+{
+ if (!isText ())
+ {
+ if (pixmap () && !pixmap ()->isNull ())
+ {
+ kpPixmapFX::paintPixmapAt (destPixmap,
+ topLeft () - docRect.topLeft (),
+ transparentPixmap ());
+ }
+ }
+ else
+ {
+ #if DEBUG_KP_SELECTION
+ kdDebug () << "kpSelection::paint() textStyle: fcol="
+ << (int *) m_textStyle.foregroundColor ().toQRgb ()
+ << " bcol="
+ << (int *) m_textStyle.effectiveBackgroundColor ().toQRgb ()
+ << endl;
+ #endif
+
+ if (m_textStyle.foregroundColor ().isOpaque ())
+ {
+ // (may have to antialias with background so don't use m_pixmap)
+ paintOpaqueText (destPixmap, docRect);
+ }
+ else
+ {
+ if (!m_pixmap)
+ {
+ kdError () << "kpSelection::paint() without m_pixmap?" << endl;
+ return;
+ }
+
+ // (transparent foreground slow to render, no antialiasing
+ // so use m_pixmap cache)
+ kpPixmapFX::paintPixmapAt (destPixmap,
+ topLeft () - docRect.topLeft (),
+ *m_pixmap);
+ }
+ }
+}
+
+// private
+void kpSelection::calculateTextPixmap ()
+{
+ if (!isText ())
+ {
+ kdError () << "kpSelection::calculateTextPixmap() not text sel"
+ << endl;
+ return;
+ }
+
+ delete m_pixmap;
+
+ if (m_textStyle.foregroundColor().isOpaque ())
+ {
+ m_pixmap = new QPixmap (m_rect.width (), m_rect.height ());
+
+ if (usesBackgroundPixmapToPaint ())
+ kpPixmapFX::fill (m_pixmap, kpColor::transparent);
+
+ paintOpaqueText (m_pixmap, m_rect);
+ }
+ else
+ {
+ m_pixmap = new QPixmap (transparentForegroundTextPixmap ());
+ }
+}
+
+
+// public static
+QString kpSelection::textForTextLines (const QValueVector <QString> &textLines_)
+{
+ if (textLines_.isEmpty ())
+ return QString::null;
+
+ QString bigString = textLines_ [0];
+
+ for (QValueVector <QString>::const_iterator it = textLines_.begin () + 1;
+ it != textLines_.end ();
+ it++)
+ {
+ bigString += QString::fromLatin1 ("\n");
+ bigString += (*it);
+ }
+
+ return bigString;
+}
+
+// public
+QString kpSelection::text () const
+{
+ if (!isText ())
+ {
+ return QString::null;
+ }
+
+ return textForTextLines (m_textLines);
+}
+
+// public
+QValueVector <QString> kpSelection::textLines () const
+{
+ if (!isText ())
+ {
+ kdError () << "kpSelection::textLines() not a text selection" << endl;
+ return QValueVector <QString> ();
+ }
+
+ return m_textLines;
+}
+
+// public
+void kpSelection::setTextLines (const QValueVector <QString> &textLines_)
+{
+ if (!isText ())
+ {
+ kdError () << "kpSelection::setTextLines() not a text selection" << endl;
+ return;
+ }
+
+ m_textLines = textLines_;
+ if (m_textLines.isEmpty ())
+ {
+ kdError () << "kpSelection::setTextLines() passed no lines" << endl;
+ m_textLines.push_back (QString::null);
+ }
+ calculateTextPixmap ();
+ emit changed (boundingRect ());
+}
+
+// public static
+int kpSelection::textBorderSize ()
+{
+ return 1;
+}
+
+// public
+QRect kpSelection::textAreaRect () const
+{
+ if (!isText ())
+ {
+ kdError () << "kpSelection::textAreaRect() not a text selection" << endl;
+ return QRect ();
+ }
+
+ return QRect (m_rect.x () + textBorderSize (),
+ m_rect.y () + textBorderSize (),
+ m_rect.width () - textBorderSize () * 2,
+ m_rect.height () - textBorderSize () * 2);
+}
+
+// public
+bool kpSelection::pointIsInTextBorderArea (const QPoint &globalPoint) const
+{
+ if (!isText ())
+ {
+ kdError () << "kpSelection::pointIsInTextBorderArea() not a text selection" << endl;
+ return false;
+ }
+
+ return (m_rect.contains (globalPoint) && !pointIsInTextArea (globalPoint));
+}
+
+// public
+bool kpSelection::pointIsInTextArea (const QPoint &globalPoint) const
+{
+ if (!isText ())
+ {
+ kdError () << "kpSelection::pointIsInTextArea() not a text selection" << endl;
+ return false;
+ }
+
+ return textAreaRect ().contains (globalPoint);
+}
+
+
+// public
+void kpSelection::textResize (int width, int height)
+{
+ if (!isText ())
+ {
+ kdError () << "kpSelection::textResize() not a text selection" << endl;
+ return;
+ }
+
+ QRect oldRect = m_rect;
+
+ m_rect = QRect (oldRect.x (), oldRect.y (), width, height);
+
+ calculatePoints ();
+ calculateTextPixmap ();
+
+ emit changed (m_rect.unite (oldRect));
+}
+
+
+// public static
+int kpSelection::minimumWidthForTextStyle (const kpTextStyle &)
+{
+ return (kpSelection::textBorderSize () * 2 + 5);
+}
+
+// public static
+int kpSelection::minimumHeightForTextStyle (const kpTextStyle &)
+{
+ return (kpSelection::textBorderSize () * 2 + 5);
+}
+
+// public static
+QSize kpSelection::minimumSizeForTextStyle (const kpTextStyle &textStyle)
+{
+ return QSize (minimumWidthForTextStyle (textStyle),
+ minimumHeightForTextStyle (textStyle));
+}
+
+
+// public static
+int kpSelection::preferredMinimumWidthForTextStyle (const kpTextStyle &textStyle)
+{
+ const int about15CharsWidth =
+ textStyle.fontMetrics ().width (
+ QString::fromLatin1 ("1234567890abcde"));
+
+ const int preferredMinWidth =
+ QMAX (150,
+ textBorderSize () * 2 + about15CharsWidth);
+
+ return QMAX (minimumWidthForTextStyle (textStyle),
+ QMIN (250, preferredMinWidth));
+}
+
+// public static
+int kpSelection::preferredMinimumHeightForTextStyle (const kpTextStyle &textStyle)
+{
+ const int preferredMinHeight =
+ textBorderSize () * 2 + textStyle.fontMetrics ().height ();
+
+ return QMAX (minimumHeightForTextStyle (textStyle),
+ QMIN (150, preferredMinHeight));
+}
+
+// public static
+QSize kpSelection::preferredMinimumSizeForTextStyle (const kpTextStyle &textStyle)
+{
+ return QSize (preferredMinimumWidthForTextStyle (textStyle),
+ preferredMinimumHeightForTextStyle (textStyle));
+}
+
+
+// public
+int kpSelection::minimumWidth () const
+{
+ if (isText ())
+ return minimumWidthForTextStyle (textStyle ());
+ else
+ return 1;
+}
+
+// public
+int kpSelection::minimumHeight () const
+{
+ if (isText ())
+ return minimumHeightForTextStyle (textStyle ());
+ else
+ return 1;
+}
+
+// public
+QSize kpSelection::minimumSize () const
+{
+ return QSize (minimumWidth (), minimumHeight ());
+}
+
+
+// public
+int kpSelection::textRowForPoint (const QPoint &globalPoint) const
+{
+ if (!isText ())
+ {
+ kdError () << "kpSelection::textRowForPoint() not a text selection" << endl;
+ return -1;
+ }
+
+ if (!pointIsInTextArea (globalPoint))
+ return -1;
+
+ const QFontMetrics fontMetrics (m_textStyle.fontMetrics ());
+
+ int row = (globalPoint.y () - textAreaRect ().y ()) /
+ fontMetrics.lineSpacing ();
+ if (row >= (int) m_textLines.size ())
+ row = m_textLines.size () - 1;
+
+ return row;
+}
+
+// public
+int kpSelection::textColForPoint (const QPoint &globalPoint) const
+{
+ if (!isText ())
+ {
+ kdError () << "kpSelection::textColForPoint() not a text selection" << endl;
+ return -1;
+ }
+
+ int row = textRowForPoint (globalPoint);
+ if (row < 0 || row >= (int) m_textLines.size ())
+ return -1;
+
+ const int localX = globalPoint.x () - textAreaRect ().x ();
+
+ const QFontMetrics fontMetrics (m_textStyle.fontMetrics ());
+
+ // (should be 0 but call just in case)
+ int charLocalLeft = fontMetrics.width (m_textLines [row], 0);
+
+ // OPT: binary search or guess location then move
+ for (int col = 0; col < (int) m_textLines [row].length (); col++)
+ {
+ // OPT: fontMetrics::charWidth() might be faster
+ const int nextCharLocalLeft = fontMetrics.width (m_textLines [row], col + 1);
+ if (localX <= (charLocalLeft + nextCharLocalLeft) / 2)
+ return col;
+
+ charLocalLeft = nextCharLocalLeft;
+ }
+
+ return m_textLines [row].length ()/*past end of line*/;
+}
+
+// public
+QPoint kpSelection::pointForTextRowCol (int row, int col)
+{
+ if (!isText ())
+ {
+ kdError () << "kpSelection::pointForTextRowCol() not a text selection" << endl;
+ return KP_INVALID_POINT;
+ }
+
+ if (row < 0 || row >= (int) m_textLines.size () ||
+ col < 0 || col > (int) m_textLines [row].length ())
+ {
+ #if DEBUG_KP_SELECTION && 1
+ kdDebug () << "kpSelection::pointForTextRowCol("
+ << row << ","
+ << col << ") out of range"
+ << " textLines='"
+ << text ()
+ << "'"
+ << endl;
+ #endif
+ return KP_INVALID_POINT;
+ }
+
+ const QFontMetrics fontMetrics (m_textStyle.fontMetrics ());
+
+ const int x = fontMetrics.width (m_textLines [row], col);
+ const int y = row * fontMetrics.height () +
+ (row >= 1 ? row * fontMetrics.leading () : 0);
+
+ return textAreaRect ().topLeft () + QPoint (x, y);
+}
+
+// public
+kpTextStyle kpSelection::textStyle () const
+{
+ if (!isText ())
+ {
+ kdError () << "kpSelection::textStyle() not a text selection" << endl;
+ }
+
+ return m_textStyle;
+}
+
+// public
+void kpSelection::setTextStyle (const kpTextStyle &textStyle_)
+{
+ if (!isText ())
+ {
+ kdError () << "kpSelection::setTextStyle() not a text selection" << endl;
+ return;
+ }
+
+ m_textStyle = textStyle_;
+ calculateTextPixmap ();
+ emit changed (boundingRect ());
+}
+
+// public
+QPixmap kpSelection::opaquePixmap () const
+{
+ QPixmap *p = pixmap ();
+ if (p)
+ {
+ return *p;
+ }
+ else
+ {
+ return QPixmap ();
+ }
+}
+
+// private
+void kpSelection::calculateTransparencyMask ()
+{
+#if DEBUG_KP_SELECTION
+ kdDebug () << "kpSelection::calculateTransparencyMask()" << endl;
+#endif
+
+ if (isText ())
+ {
+ #if DEBUG_KP_SELECTION
+ kdDebug () << "\ttext - no need for transparency mask" << endl;
+ #endif
+ m_transparencyMask.resize (0, 0);
+ return;
+ }
+
+ if (!m_pixmap)
+ {
+ #if DEBUG_KP_SELECTION
+ kdDebug () << "\tno pixmap - no need for transparency mask" << endl;
+ #endif
+ m_transparencyMask.resize (0, 0);
+ return;
+ }
+
+ if (m_transparency.isOpaque ())
+ {
+ #if DEBUG_KP_SELECTION
+ kdDebug () << "\topaque - no need for transparency mask" << endl;
+ #endif
+ m_transparencyMask.resize (0, 0);
+ return;
+ }
+
+ m_transparencyMask.resize (m_pixmap->width (), m_pixmap->height ());
+
+ QImage image = kpPixmapFX::convertToImage (*m_pixmap);
+ QPainter transparencyMaskPainter (&m_transparencyMask);
+
+ bool hasTransparent = false;
+ for (int y = 0; y < m_pixmap->height (); y++)
+ {
+ for (int x = 0; x < m_pixmap->width (); x++)
+ {
+ if (kpPixmapFX::getColorAtPixel (image, x, y).isSimilarTo (m_transparency.transparentColor (),
+ m_transparency.processedColorSimilarity ()))
+ {
+ transparencyMaskPainter.setPen (Qt::color1/*make it transparent*/);
+ hasTransparent = true;
+ }
+ else
+ {
+ transparencyMaskPainter.setPen (Qt::color0/*keep pixel as is*/);
+ }
+
+ transparencyMaskPainter.drawPoint (x, y);
+ }
+ }
+
+ transparencyMaskPainter.end ();
+
+ if (!hasTransparent)
+ {
+ #if DEBUG_KP_SELECTION
+ kdDebug () << "\tcolour useless - completely opaque" << endl;
+ #endif
+ m_transparencyMask.resize (0, 0);
+ return;
+ }
+}
+
+// public
+QPixmap kpSelection::transparentPixmap () const
+{
+ QPixmap pixmap = opaquePixmap ();
+
+ if (!m_transparencyMask.isNull ())
+ {
+ kpPixmapFX::paintMaskTransparentWithBrush (&pixmap, QPoint (0, 0),
+ m_transparencyMask);
+ }
+
+ return pixmap;
+}
+
+// public
+kpSelectionTransparency kpSelection::transparency () const
+{
+ return m_transparency;
+}
+
+// public
+bool kpSelection::setTransparency (const kpSelectionTransparency &transparency,
+ bool checkTransparentPixmapChanged)
+{
+ if (m_transparency == transparency)
+ return false;
+
+ m_transparency = transparency;
+
+ bool haveChanged = true;
+
+ QBitmap oldTransparencyMask = m_transparencyMask;
+ calculateTransparencyMask ();
+
+
+ if (oldTransparencyMask.width () == m_transparencyMask.width () &&
+ oldTransparencyMask.height () == m_transparencyMask.height ())
+ {
+ if (m_transparencyMask.isNull ())
+ {
+ #if DEBUG_KP_SELECTION
+ kdDebug () << "\tboth old and new pixmaps are null - nothing changed" << endl;
+ #endif
+ haveChanged = false;
+ }
+ else if (checkTransparentPixmapChanged)
+ {
+ QImage oldTransparencyMaskImage = kpPixmapFX::convertToImage (oldTransparencyMask);
+ QImage newTransparencyMaskImage = kpPixmapFX::convertToImage (m_transparencyMask);
+
+ bool changed = false;
+ for (int y = 0; y < oldTransparencyMaskImage.height () && !changed; y++)
+ {
+ for (int x = 0; x < oldTransparencyMaskImage.width () && !changed; x++)
+ {
+ if (kpPixmapFX::getColorAtPixel (oldTransparencyMaskImage, x, y) !=
+ kpPixmapFX::getColorAtPixel (newTransparencyMaskImage, x, y))
+ {
+ #if DEBUG_KP_SELECTION
+ kdDebug () << "\tdiffer at " << QPoint (x, y)
+ << " old=" << (int *) kpPixmapFX::getColorAtPixel (oldTransparencyMaskImage, x, y).toQRgb ()
+ << " new=" << (int *) kpPixmapFX::getColorAtPixel (newTransparencyMaskImage, x, y).toQRgb ()
+ << endl;
+ #endif
+ changed = true;
+ break;
+ }
+ }
+ }
+
+ if (!changed)
+ haveChanged = false;
+ }
+ }
+
+
+ if (haveChanged)
+ emit changed (boundingRect ());
+
+ return haveChanged;
+}
+
+
+// private
+void kpSelection::flipPoints (bool horiz, bool vert)
+{
+ QRect oldRect = boundingRect ();
+
+ m_points.translate (-oldRect.x (), -oldRect.y ());
+
+ const QWMatrix matrix = kpPixmapFX::flipMatrix (oldRect.width (), oldRect.height (),
+ horiz, vert);
+ m_points = matrix.map (m_points);
+
+ m_points.translate (oldRect.x (), oldRect.y ());
+}
+
+
+// public
+void kpSelection::flip (bool horiz, bool vert)
+{
+#if DEBUG_KP_SELECTION && 1
+ kdDebug () << "kpSelection::flip(horiz=" << horiz
+ << ",vert=" << vert << ")" << endl;
+#endif
+
+ flipPoints (horiz, vert);
+
+
+ if (m_pixmap)
+ {
+ #if DEBUG_KP_SELECTION && 1
+ kdDebug () << "\thave pixmap - flipping that" << endl;
+ #endif
+ kpPixmapFX::flip (m_pixmap, horiz, vert);
+ }
+
+ if (!m_transparencyMask.isNull ())
+ {
+ #if DEBUG_KP_SELECTION && 1
+ kdDebug () << "\thave transparency mask - flipping that" << endl;
+ #endif
+ kpPixmapFX::flip (&m_transparencyMask, horiz, vert);
+ }
+
+
+ emit changed (boundingRect ());
+}
+
+#include <kpselection.moc>
+
diff --git a/kolourpaint/kpselection.h b/kolourpaint/kpselection.h
new file mode 100644
index 00000000..69b836b9
--- /dev/null
+++ b/kolourpaint/kpselection.h
@@ -0,0 +1,237 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kpselection_h__
+#define __kpselection_h__
+
+#include <qbitmap.h>
+#include <qdatastream.h>
+#include <qobject.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qpointarray.h>
+#include <qvaluevector.h>
+#include <qrect.h>
+#include <qstring.h>
+
+#include <kpcolor.h>
+#include <kppixmapfx.h>
+#include <kpselectiontransparency.h>
+#include <kptextstyle.h>
+
+
+class QSize;
+
+
+/*
+ * Holds a selection - will also be used for the clipboard
+ * so that we can retain the border.
+ */
+class kpSelection : public QObject
+{
+Q_OBJECT
+
+public:
+ enum Type
+ {
+ Rectangle,
+ Ellipse,
+ Points,
+ Text
+ };
+
+ // (for any)
+ kpSelection (const kpSelectionTransparency &transparency = kpSelectionTransparency ());
+
+ // (for Rectangle & Ellipse)
+ kpSelection (Type type, const QRect &rect, const QPixmap &pixmap = QPixmap (),
+ const kpSelectionTransparency &transparency = kpSelectionTransparency ());
+ kpSelection (Type type, const QRect &rect, const kpSelectionTransparency &transparency);
+
+ // (for Text)
+ kpSelection (const QRect &rect, const QValueVector <QString> &textLines_, const kpTextStyle &textStyle_);
+
+ // (for Points)
+ kpSelection (const QPointArray &points, const QPixmap &pixmap = QPixmap (),
+ const kpSelectionTransparency &transparency = kpSelectionTransparency ());
+ kpSelection (const QPointArray &points, const kpSelectionTransparency &transparency);
+
+ kpSelection (const kpSelection &rhs);
+ kpSelection &operator= (const kpSelection &rhs);
+ friend QDataStream &operator<< (QDataStream &stream, const kpSelection &selection);
+ friend QDataStream &operator>> (QDataStream &stream, kpSelection &selection);
+ void readFromStream (QDataStream &stream,
+ const kpPixmapFX::WarnAboutLossInfo &wali =
+ kpPixmapFX::WarnAboutLossInfo ());
+ ~kpSelection ();
+
+private:
+ void calculatePoints ();
+
+public:
+ Type type () const;
+ bool isRectangular () const;
+ bool isText () const;
+ // returns either i18n ("Selection") or i18n ("Text")
+ QString name () const;
+
+ int size () const;
+
+ QBitmap maskForOwnType (bool nullForRectangular = false) const;
+
+ // synonyms
+ QPoint topLeft () const;
+ QPoint point () const;
+
+ int x () const;
+ int y () const;
+
+ void moveBy (int dx, int dy);
+ void moveTo (int dx, int dy);
+ void moveTo (const QPoint &topLeftPoint);
+
+ // synonyms
+ QPointArray points () const;
+ QPointArray pointArray () const;
+
+ QRect boundingRect () const;
+ int width () const;
+ int height () const;
+
+
+ // (for non-rectangular selections, may return false even if
+ // kpView::onSelectionResizeHandle())
+ bool contains (const QPoint &point) const;
+ bool contains (int x, int y);
+
+
+ // (Avoid using for text selections since text selection may
+ // require a background for antialiasing purposes - use paint()
+ // instead, else no antialising)
+ QPixmap *pixmap () const;
+ void setPixmap (const QPixmap &pixmap);
+
+ bool usesBackgroundPixmapToPaint () const;
+
+private:
+ void paintOpaqueText (QPixmap *destPixmap, const QRect &docRect) const;
+ QPixmap transparentForegroundTextPixmap () const;
+
+public:
+ // (<docRect> is the document rectangle that <*destPixmap> represents)
+ void paint (QPixmap *destPixmap, const QRect &docRect) const;
+
+private:
+ void calculateTextPixmap ();
+
+public:
+ static QString textForTextLines (const QValueVector <QString> &textLines_);
+ QString text () const; // textLines() as one long string
+ QValueVector <QString> textLines () const;
+ void setTextLines (const QValueVector <QString> &textLines_);
+
+ static int textBorderSize ();
+ QRect textAreaRect () const;
+ bool pointIsInTextBorderArea (const QPoint &globalPoint) const;
+ bool pointIsInTextArea (const QPoint &globalPoint) const;
+
+
+ void textResize (int width, int height);
+
+ // TODO: Enforce in kpSelection, not just in kpToolSelection & when pasting
+ // (in kpMainWindow).
+ // Be more robust when external enforcement fails.
+ static int minimumWidthForTextStyle (const kpTextStyle &);
+ static int minimumHeightForTextStyle (const kpTextStyle &);
+ static QSize minimumSizeForTextStyle (const kpTextStyle &);
+
+ static int preferredMinimumWidthForTextStyle (const kpTextStyle &textStyle);
+ static int preferredMinimumHeightForTextStyle (const kpTextStyle &textStyle);
+ static QSize preferredMinimumSizeForTextStyle (const kpTextStyle &textStyle);
+
+ int minimumWidth () const;
+ int minimumHeight () const;
+ QSize minimumSize () const;
+
+ int textRowForPoint (const QPoint &globalPoint) const;
+ int textColForPoint (const QPoint &globalPoint) const;
+ QPoint pointForTextRowCol (int row, int col);
+
+ kpTextStyle textStyle () const;
+ void setTextStyle (const kpTextStyle &textStyle);
+
+ // TODO: ret val inconstent with pixmap()
+ // - fix when merge with kpTempPixmap
+ QPixmap opaquePixmap () const; // same as pixmap()
+
+private:
+ void calculateTransparencyMask ();
+
+public:
+ // Returns opaquePixmap() after applying kpSelectionTransparency
+ QPixmap transparentPixmap () const;
+
+ kpSelectionTransparency transparency () const;
+ // Returns whether or not the selection changed due to setting the
+ // transparency info. If <checkTransparentPixmapChanged> is set,
+ // it will try harder to return false (although the check is
+ // expensive).
+ bool setTransparency (const kpSelectionTransparency &transparency,
+ bool checkTransparentPixmapChanged = false);
+
+private:
+ void flipPoints (bool horiz, bool vert);
+
+public:
+ void flip (bool horiz, bool vert);
+
+signals:
+ void changed (const QRect &docRect);
+
+private:
+ // OPT: use implicit sharing
+
+ Type m_type;
+ QRect m_rect;
+ QPointArray m_points;
+ QPixmap *m_pixmap;
+
+ QValueVector <QString> m_textLines;
+ kpTextStyle m_textStyle;
+
+ kpSelectionTransparency m_transparency;
+ QBitmap m_transparencyMask;
+
+private:
+ // There is no need to maintain binary compatibility at this stage.
+ // The d-pointer is just so that you can experiment without recompiling
+ // the kitchen sink.
+ class kpSelectionPrivate *d;
+};
+
+#endif // __kpselection_h__
diff --git a/kolourpaint/kpselectiondrag.cpp b/kolourpaint/kpselectiondrag.cpp
new file mode 100644
index 00000000..23ab9a4e
--- /dev/null
+++ b/kolourpaint/kpselectiondrag.cpp
@@ -0,0 +1,294 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG_KP_SELECTION_DRAG 0
+
+
+#include <kpselectiondrag.h>
+
+#include <qdatastream.h>
+
+#include <kdebug.h>
+
+#include <kppixmapfx.h>
+#include <kpselection.h>
+
+
+// public static
+const char * const kpSelectionDrag::selectionMimeType = "application/x-kolourpaint-selection";
+
+
+kpSelectionDrag::kpSelectionDrag (QWidget *dragSource, const char *name)
+ : QImageDrag (dragSource, name)
+{
+}
+
+kpSelectionDrag::kpSelectionDrag (const QImage &image, QWidget *dragSource, const char *name)
+ : QImageDrag (image, dragSource, name)
+{
+}
+
+kpSelectionDrag::kpSelectionDrag (const kpSelection &sel, QWidget *dragSource, const char *name)
+ : QImageDrag (dragSource, name)
+{
+ setSelection (sel);
+}
+
+kpSelectionDrag::~kpSelectionDrag ()
+{
+}
+
+
+// public
+void kpSelectionDrag::setSelection (const kpSelection &sel)
+{
+ if (!sel.pixmap ())
+ {
+ kdError () << "kpSelectionDrag::setSelection() without pixmap" << endl;
+ return;
+ }
+
+ m_selection = sel;
+
+ // OPT: an awful waste of memory storing image in both selection and QImage
+
+ // HACK: need to set image else QImageDrag::format() lies
+ setImage (kpPixmapFX::convertToImage (*m_selection.pixmap ()));
+}
+
+// protected
+bool kpSelectionDrag::holdsSelection () const
+{
+ return bool (m_selection.pixmap ());
+}
+
+
+// public virtual [base QMimeSource]
+const char *kpSelectionDrag::format (int which) const
+{
+#if DEBUG_KP_SELECTION_DRAG && 0
+ kdDebug () << "kpSelectionDrag::format(" << which << ")" << endl;
+#endif
+ const char *ret = QImageDrag::format (which);
+ if (ret)
+ {
+ #if DEBUG_KP_SELECTION_DRAG && 0
+ kdDebug () << "\tQImageDrag reports " << ret << endl;
+ #endif
+ return ret;
+ }
+
+ int i;
+ for (i = 0; QImageDrag::format (i); i++)
+ ;
+
+#if DEBUG_KP_SELECTION_DRAG && 0
+ kdDebug () << "\tend of QImageDrag format list at " << i << endl;
+#endif
+
+ if (i == which)
+ {
+ #if DEBUG_KP_SELECTION_DRAG && 0
+ kdDebug () << "\treturning own mimetype" << endl;
+ #endif
+ return kpSelectionDrag::selectionMimeType;
+ }
+ else
+ {
+ #if DEBUG_KP_SELECTION_DRAG && 0
+ kdDebug () << "\treturning non-existent" << endl;
+ #endif
+ return 0;
+ }
+}
+
+// public virtual [base QMimeSource]
+// Don't need to override in Qt 3.2 (the QMimeSource base will work)
+// but we do so just in case QImageDrag later decides to override it.
+bool kpSelectionDrag::provides (const char *mimeType) const
+{
+#if DEBUG_KP_SELECTION_DRAG
+ kdDebug () << "kpSelectionDrag::provides(" << mimeType << ")" << endl;
+#endif
+
+ if (!mimeType)
+ return false;
+
+ return (!strcmp (mimeType, kpSelectionDrag::selectionMimeType) ||
+ QImageDrag::provides (mimeType));
+}
+
+// public virtual [base QMimeSource]
+QByteArray kpSelectionDrag::encodedData (const char *mimeType) const
+{
+#if DEBUG_KP_SELECTION_DRAG
+ kdDebug () << "kpSelectionDrag::encodedData(" << mimeType << ")" << endl;
+#endif
+
+ if (!mimeType)
+ return QByteArray ();
+
+ if (!strcmp (mimeType, kpSelectionDrag::selectionMimeType))
+ {
+ QByteArray ba;
+ QDataStream stream (ba, IO_WriteOnly);
+
+ #if DEBUG_KP_SELECTION_DRAG
+ kdDebug () << "\twant it as kpSelection in QByteArray" << endl;
+ #endif
+
+ if (holdsSelection ())
+ {
+ #if DEBUG_KP_SELECTION_DRAG
+ kdDebug () << "\t\thave selection - return it" << endl;
+ #endif
+ stream << m_selection;
+ }
+ else
+ {
+ #if DEBUG_KP_SELECTION_DRAG
+ kdDebug () << "\t\thave image - call kpSelectionDrag::decode(QImage)" << endl;
+ #endif
+ QImage image;
+ if (kpSelectionDrag::decode (this, image/*ref*/))
+ {
+ #if DEBUG_KP_SELECTION_DRAG
+ kdDebug () << "\t\t\tok - returning sel with image w="
+ << image.width ()
+ << " h="
+ << image.height ()
+ << endl;
+ #endif
+
+ QPixmap pixmap = kpPixmapFX::convertToPixmapAsLosslessAsPossible (image);
+
+ stream << kpSelection (kpSelection::Rectangle,
+ QRect (0, 0, pixmap.width (), pixmap.height ()),
+ pixmap);
+ }
+ else
+ {
+ kdError () << "kpSelectionDrag::encodedData(" << mimeType << ")"
+ << " kpSelectionDrag(QImage) could not decode data into QImage"
+ << endl;
+ stream << kpSelection ();
+ }
+ }
+
+ return ba;
+ }
+ else
+ {
+ #if DEBUG_KP_SELECTION_DRAG
+ kdDebug () << "\twant it as QImage in QByteArray" << endl;
+ #endif
+
+ return QImageDrag::encodedData (mimeType);
+ }
+}
+
+
+// public static
+bool kpSelectionDrag::canDecode (const QMimeSource *e)
+{
+#if DEBUG_KP_SELECTION_DRAG
+ kdDebug () << "kpSelectionDrag::canDecode()" << endl;
+#endif
+
+ if (!e)
+ return false;
+
+ return (e->provides (kpSelectionDrag::selectionMimeType) ||
+ QImageDrag::canDecode (e));
+}
+
+
+// public static
+bool kpSelectionDrag::decode (const QMimeSource *e, QImage &img)
+{
+#if DEBUG_KP_SELECTION_DRAG
+ kdDebug () << "kpSelectionDrag::decode(QImage)" << endl;
+#endif
+ if (!e)
+ return false;
+
+ return (QImageDrag::canDecode (e) && // prevents X errors, jumps based on unitialised values...
+ QImageDrag::decode (e, img/*ref*/));
+}
+
+// public static
+bool kpSelectionDrag::decode (const QMimeSource *e, kpSelection &sel,
+ const kpPixmapFX::WarnAboutLossInfo &wali)
+{
+#if DEBUG_KP_SELECTION_DRAG
+ kdDebug () << "kpSelectionDrag::decode(kpSelection)" << endl;
+#endif
+ if (!e)
+ return false;
+
+ if (e->provides (kpSelectionDrag::selectionMimeType))
+ {
+ #if DEBUG_KP_SELECTION_DRAG
+ kdDebug () << "\tmimeSource provides selection - just return it in QByteArray" << endl;
+ #endif
+ QByteArray data = e->encodedData (kpSelectionDrag::selectionMimeType);
+ QDataStream stream (data, IO_ReadOnly);
+
+ // (no need for wali as kpSelection's by definition only support QPixmap's)
+ stream >> sel;
+ }
+ else
+ {
+ #if DEBUG_KP_SELECTION_DRAG
+ kdDebug () << "\tmimeSource doesn't provide selection - try image" << endl;
+ #endif
+
+ QImage image;
+ if (kpSelectionDrag::decode (e, image/*ref*/))
+ {
+ #if DEBUG_KP_SELECTION_DRAG
+ kdDebug () << "\tok w=" << image.width () << " h=" << image.height () << endl;
+ #endif
+
+ sel = kpSelection (kpSelection::Rectangle,
+ QRect (0, 0, image.width (), image.height ()),
+ kpPixmapFX::convertToPixmapAsLosslessAsPossible (image, wali));
+ }
+ else
+ {
+ #if DEBUG_KP_SELECTION_DRAG
+ kdDebug () << "kpSelectionDrag::decode(kpSelection) mimeSource had no sel "
+ "and could not decode to image" << endl;
+ #endif
+ return false;
+ }
+ }
+
+ return true;
+}
+
+#include <kpselectiondrag.moc>
diff --git a/kolourpaint/kpselectiondrag.h b/kolourpaint/kpselectiondrag.h
new file mode 100644
index 00000000..5aae82d9
--- /dev/null
+++ b/kolourpaint/kpselectiondrag.h
@@ -0,0 +1,71 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kp_selection_drag_h__
+#define __kp_selection_drag_h__
+
+#include <qdragobject.h>
+
+#include <kppixmapfx.h>
+#include <kpselection.h>
+
+
+class kpSelectionDrag : public QImageDrag
+{
+Q_OBJECT
+
+public:
+ kpSelectionDrag (QWidget *dragSource = 0, const char *name = 0);
+ kpSelectionDrag (const QImage &image, QWidget *dragSource = 0, const char *name = 0);
+ kpSelectionDrag (const kpSelection &sel, QWidget *dragSource = 0, const char *name = 0);
+ virtual ~kpSelectionDrag ();
+
+ static const char * const selectionMimeType;
+
+ void setSelection (const kpSelection &sel);
+
+protected:
+ bool holdsSelection () const;
+
+public:
+ virtual const char *format (int which = 0) const;
+ virtual bool provides (const char *mimeType) const;
+ virtual QByteArray encodedData (const char *mimeType) const;
+
+ static bool canDecode (const QMimeSource *e);
+ static bool decode (const QMimeSource *e, QImage &img);
+ static bool decode (const QMimeSource *e, kpSelection &sel,
+ const kpPixmapFX::WarnAboutLossInfo &wali =
+ kpPixmapFX::WarnAboutLossInfo ());
+
+protected:
+ kpSelection m_selection;
+};
+
+
+#endif // __kp_selection_drag_h__
diff --git a/kolourpaint/kpselectiontransparency.cpp b/kolourpaint/kpselectiontransparency.cpp
new file mode 100644
index 00000000..62919be4
--- /dev/null
+++ b/kolourpaint/kpselectiontransparency.cpp
@@ -0,0 +1,178 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define DEBUG_KP_SELECTION_TRANSPARENCY 0
+
+
+#include <kpselectiontransparency.h>
+
+#include <kdebug.h>
+
+#include <kpcolor.h>
+#include <kpcolorsimilaritydialog.h>
+
+
+kpSelectionTransparency::kpSelectionTransparency ()
+ : m_isOpaque (true)
+{
+ setColorSimilarity (0);
+}
+
+kpSelectionTransparency::kpSelectionTransparency (const kpColor &transparentColor, double colorSimilarity)
+ : m_isOpaque (false),
+ m_transparentColor (transparentColor)
+{
+ setColorSimilarity (colorSimilarity);
+}
+
+kpSelectionTransparency::kpSelectionTransparency (bool isOpaque, const kpColor &transparentColor,
+ double colorSimilarity)
+ : m_isOpaque (isOpaque),
+ m_transparentColor (transparentColor)
+{
+ setColorSimilarity (colorSimilarity);
+}
+
+bool kpSelectionTransparency::operator== (const kpSelectionTransparency &rhs) const
+{
+#if DEBUG_KP_SELECTION_TRANSPARENCY && 0
+ kdDebug () << "kpSelectionTransparency::operator==()" << endl;
+#endif
+
+ if (m_isOpaque != rhs.m_isOpaque)
+ {
+ #if DEBUG_KP_SELECTION_TRANSPARENCY && 0
+ kdDebug () << "\tdifferent opacity: lhs=" << m_isOpaque
+ << " rhs=" << rhs.m_isOpaque
+ << endl;
+ #endif
+ return false;
+ }
+
+ if (m_isOpaque)
+ {
+ #if DEBUG_KP_SELECTION_TRANSPARENCY && 0
+ kdDebug () << "\tboth opaque - eq" << endl;
+ #endif
+ return true;
+ }
+
+#if DEBUG_KP_SELECTION_TRANSPARENCY && 0
+ kdDebug () << "\tcolours: lhs=" << (int *) m_transparentColor.toQRgb ()
+ << " rhs=" << (int *) rhs.m_transparentColor.toQRgb ()
+ << endl;
+ kdDebug () << "\tcolour similarity: lhs=" << m_colorSimilarity
+ << " rhs=" << rhs.m_colorSimilarity
+ << endl;
+#endif
+
+ return (m_transparentColor == rhs.m_transparentColor &&
+ m_colorSimilarity == rhs.m_colorSimilarity);
+}
+
+bool kpSelectionTransparency::operator!= (const kpSelectionTransparency &rhs) const
+{
+ return !(*this == rhs);
+}
+
+kpSelectionTransparency::~kpSelectionTransparency ()
+{
+}
+
+
+// public
+bool kpSelectionTransparency::isOpaque () const
+{
+ return m_isOpaque;
+}
+
+// public
+bool kpSelectionTransparency::isTransparent () const
+{
+ return !isOpaque ();
+}
+
+// public
+void kpSelectionTransparency::setOpaque (bool yes)
+{
+ m_isOpaque = yes;
+}
+
+// public
+void kpSelectionTransparency::setTransparent (bool yes)
+{
+ setOpaque (!yes);
+}
+
+
+// public
+kpColor kpSelectionTransparency::transparentColor () const
+{
+ if (m_isOpaque)
+ {
+ // There are legitimate uses for this so no kdError()
+ kdDebug () << "kpSelectionTransparency::transparentColor() "
+ "getting transparent color even though opaque" << endl;
+ }
+
+ return m_transparentColor;
+}
+
+// public
+void kpSelectionTransparency::setTransparentColor (const kpColor &transparentColor)
+{
+ m_transparentColor = transparentColor;
+}
+
+
+// public
+double kpSelectionTransparency::colorSimilarity () const
+{
+ if (m_colorSimilarity < 0 ||
+ m_colorSimilarity > kpColorSimilarityDialog::maximumColorSimilarity)
+ {
+ kdError () << "kpSelectionTransparency::colorSimilarity() invalid colorSimilarity" << endl;
+ return 0;
+ }
+
+ return m_colorSimilarity;
+}
+
+// pubulic
+void kpSelectionTransparency::setColorSimilarity (double colorSimilarity)
+{
+ m_colorSimilarity = colorSimilarity;
+ m_processedColorSimilarity = kpColor::processSimilarity (colorSimilarity);
+}
+
+// public
+int kpSelectionTransparency::processedColorSimilarity () const
+{
+ return m_processedColorSimilarity;
+}
+
diff --git a/kolourpaint/kpselectiontransparency.h b/kolourpaint/kpselectiontransparency.h
new file mode 100644
index 00000000..690c4c1e
--- /dev/null
+++ b/kolourpaint/kpselectiontransparency.h
@@ -0,0 +1,80 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef __kp_selection_transparency_h__
+#define __kp_selection_transparency_h__
+
+#include <kpcolor.h>
+
+
+// This does not apply to the Text Tool. Use kpTextStyle for that.
+class kpSelectionTransparency
+{
+public:
+ // Opaque selection
+ kpSelectionTransparency ();
+ // Selection that's transparent at pixels with <color>
+ kpSelectionTransparency (const kpColor &transparentColor, double colorSimilarity);
+ // If <isOpaque>, <transparentColor> is allowed to be anything
+ // (including invalid) as the color would have no effect.
+ // However, you are encouraged to set it as you would if !<isOpaque>,
+ // because setTransparent(true) might be called later, after which
+ // the <transparentColor> would suddenly become important.
+ //
+ // It is a similar case with <colorSimilarity>, although <colorSimilarity>
+ // must be in-range (see kpColorSimilarityDialog).
+ kpSelectionTransparency (bool isOpaque, const kpColor &transparentColor, double colorSimilarity);
+ // Returns whether they are visually equivalent.
+ // This is the same as a memcmp() except that if they are both opaque,
+ // this function will return true regardless of the transparentColor's.
+ bool operator== (const kpSelectionTransparency &rhs) const;
+ bool operator!= (const kpSelectionTransparency &rhs) const;
+ ~kpSelectionTransparency ();
+
+ bool isOpaque () const;
+ bool isTransparent () const;
+ void setOpaque (bool yes = true);
+ void setTransparent (bool yes = true);
+
+ // If isOpaque(), transparentColor() is generally not called because
+ // the transparent color would have no effect.
+ kpColor transparentColor () const;
+ void setTransparentColor (const kpColor &transparentColor);
+
+ double colorSimilarity () const;
+ void setColorSimilarity (double colorSimilarity);
+ int processedColorSimilarity () const;
+
+private:
+ bool m_isOpaque;
+ kpColor m_transparentColor;
+ double m_colorSimilarity;
+ int m_processedColorSimilarity;
+};
+
+
+#endif // __kp_selection_transparency_h__
diff --git a/kolourpaint/kpsinglekeytriggersaction.cpp b/kolourpaint/kpsinglekeytriggersaction.cpp
new file mode 100644
index 00000000..d3b64002
--- /dev/null
+++ b/kolourpaint/kpsinglekeytriggersaction.cpp
@@ -0,0 +1,155 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG_KP_SINGLE_KEY_TRIGGERS_ACTION 0
+
+
+//
+// kpSingleKeyTriggersActionInterface
+//
+
+
+#include <kpsinglekeytriggersaction.h>
+
+#include <kdebug.h>
+
+#include <kptool.h>
+
+
+kpSingleKeyTriggersActionInterface::kpSingleKeyTriggersActionInterface ()
+{
+ m_singleKeyTriggersEnabled = true;
+}
+
+kpSingleKeyTriggersActionInterface::~kpSingleKeyTriggersActionInterface ()
+{
+}
+
+
+// public
+bool kpSingleKeyTriggersActionInterface::singleKeyTriggersEnabled () const
+{
+ return m_singleKeyTriggersEnabled;
+}
+
+// public
+void kpSingleKeyTriggersActionInterface::enableSingleKeyTriggers (bool enable)
+{
+#if DEBUG_KP_SINGLE_KEY_TRIGGERS_ACTION
+ kdDebug () << "kpSingleKeyTriggersActionInterface(" << /*virtual*/actionName ()
+ << ")::enableSingleKeyTriggers(" << enable << ")"
+ << endl;
+#endif
+
+ if (enable == m_singleKeyTriggersEnabled)
+ {
+ #if DEBUG_KP_SINGLE_KEY_TRIGGERS_ACTION
+ kdDebug () << "\tnop" << endl;
+ #endif
+ return;
+ }
+
+ m_singleKeyTriggersEnabled = enable;
+
+
+ if (enable)
+ {
+ #if DEBUG_KP_SINGLE_KEY_TRIGGERS_ACTION
+ kdDebug () << "\tre-enabling to " << m_fullShortcut.toString () << endl;
+ #endif
+ /*pure virtual*/actionSetShortcut (m_fullShortcut);
+ }
+ else // disable single key triggers
+ {
+ m_fullShortcut = /*pure virtual*/actionShortcut ();
+
+ KShortcut newShortcut;
+ if (kpTool::containsSingleKeyTrigger (m_fullShortcut, &newShortcut))
+ {
+ #if DEBUG_KP_SINGLE_KEY_TRIGGERS_ACTION
+ kdDebug () << "\tchange shortcuts: old="
+ << m_fullShortcut.toString ()
+ << " new="
+ << newShortcut.toString ()
+ << endl;
+ #endif
+ /*pure virtual*/actionSetShortcut (newShortcut);
+ }
+ else
+ {
+ #if DEBUG_KP_SINGLE_KEY_TRIGGERS_ACTION
+ kdDebug () << "\tshortcut is untouched "
+ << m_fullShortcut.toString ()
+ << endl;
+ #endif
+ }
+ }
+}
+
+
+//
+// kpSingleKeyTriggersAction
+//
+
+
+kpSingleKeyTriggersAction::kpSingleKeyTriggersAction (const QString &text,
+ const KShortcut &shortcut,
+ const QObject *receiver, const char *slot,
+ KActionCollection *parent, const char *name)
+ : KAction (text, shortcut, receiver, slot, parent, name)
+{
+}
+
+kpSingleKeyTriggersAction::~kpSingleKeyTriggersAction ()
+{
+}
+
+
+//
+// kpSingleKeyTriggersAction implements kpSingleKeyTriggersActionInterface
+//
+
+// public virtual [base kpSingleKeyTriggersActionInterface]
+const char *kpSingleKeyTriggersAction::actionName () const
+{
+ return name ();
+}
+
+// public virtual [base kpSingleKeyTriggersActionInterface]
+KShortcut kpSingleKeyTriggersAction::actionShortcut () const
+{
+ return shortcut ();
+}
+
+// public virtual [base kpSingleKeyTriggersActionInterface]
+void kpSingleKeyTriggersAction::actionSetShortcut (const KShortcut &shortcut)
+{
+ setShortcut (shortcut);
+}
+
+
+#include <kpsinglekeytriggersaction.moc>
diff --git a/kolourpaint/kpsinglekeytriggersaction.h b/kolourpaint/kpsinglekeytriggersaction.h
new file mode 100644
index 00000000..fc404bd5
--- /dev/null
+++ b/kolourpaint/kpsinglekeytriggersaction.h
@@ -0,0 +1,82 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef KP_SINGLE_KEY_TRIGGERS_ACTION_H
+#define KP_SINGLE_KEY_TRIGGERS_ACTION_H
+
+
+#include <kshortcut.h>
+
+
+class kpSingleKeyTriggersActionInterface
+{
+public:
+ kpSingleKeyTriggersActionInterface ();
+ virtual ~kpSingleKeyTriggersActionInterface ();
+
+ bool singleKeyTriggersEnabled () const;
+ void enableSingleKeyTriggers (bool enable = true);
+
+ // Avoid inheritance diamond by not deriving from KAction
+ // so you'll have to implement these by talking to your base KAction.
+ virtual const char *actionName () const { return 0; }
+ virtual KShortcut actionShortcut () const = 0;
+ virtual void actionSetShortcut (const KShortcut &shortcut) = 0;
+
+protected:
+ bool m_singleKeyTriggersEnabled;
+ KShortcut m_fullShortcut;
+};
+
+
+#include <kaction.h>
+
+
+class kpSingleKeyTriggersAction : public KAction,
+ public kpSingleKeyTriggersActionInterface
+{
+Q_OBJECT
+
+public:
+ kpSingleKeyTriggersAction (const QString &text,
+ const KShortcut &shortcut,
+ const QObject *receiver, const char *slot,
+ KActionCollection *parent, const char *name);
+ virtual ~kpSingleKeyTriggersAction ();
+
+
+ //
+ // kpSingleKeyTriggersActionInterface
+ //
+
+ virtual const char *actionName () const;
+ virtual KShortcut actionShortcut () const;
+ virtual void actionSetShortcut (const KShortcut &shortcut);
+};
+
+
+#endif // KP_SINGLE_KEY_TRIGGERS_ACTION_H
diff --git a/kolourpaint/kptemppixmap.cpp b/kolourpaint/kptemppixmap.cpp
new file mode 100644
index 00000000..55f669ee
--- /dev/null
+++ b/kolourpaint/kptemppixmap.cpp
@@ -0,0 +1,148 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define DEBUG_KP_TEMP_PIXMAP 0
+
+
+#include <kptemppixmap.h>
+
+#include <kppixmapfx.h>
+#include <kpviewmanager.h>
+
+
+kpTempPixmap::kpTempPixmap (bool isBrush, RenderMode renderMode,
+ const QPoint &topLeft, const QPixmap &pixmap)
+ : m_isBrush (isBrush),
+ m_renderMode (renderMode),
+ m_topLeft (topLeft),
+ m_pixmap (pixmap)
+{
+}
+
+kpTempPixmap::kpTempPixmap (const kpTempPixmap &rhs)
+ : m_isBrush (rhs.m_isBrush),
+ m_renderMode (rhs.m_renderMode),
+ m_topLeft (rhs.m_topLeft),
+ m_pixmap (rhs.m_pixmap)
+{
+}
+
+kpTempPixmap &kpTempPixmap::operator= (const kpTempPixmap &rhs)
+{
+ if (this == &rhs)
+ return *this;
+
+ m_isBrush = rhs.m_isBrush;
+ m_renderMode = rhs.m_renderMode;
+ m_topLeft = rhs.m_topLeft;
+ m_pixmap = rhs.m_pixmap;
+
+ return *this;
+}
+
+kpTempPixmap::~kpTempPixmap ()
+{
+}
+
+
+// public
+bool kpTempPixmap::isBrush () const
+{
+ return m_isBrush;
+}
+
+// public
+kpTempPixmap::RenderMode kpTempPixmap::renderMode () const
+{
+ return m_renderMode;
+}
+
+// public
+QPoint kpTempPixmap::topLeft () const
+{
+ return m_topLeft;
+}
+
+// public
+QPixmap kpTempPixmap::pixmap () const
+{
+ return m_pixmap;
+}
+
+
+// public
+bool kpTempPixmap::isVisible (const kpViewManager *vm) const
+{
+ return m_isBrush ? (bool) vm->viewUnderCursor () : true;
+}
+
+// public
+QRect kpTempPixmap::rect () const
+{
+ return QRect (m_topLeft.x (), m_topLeft.y (),
+ m_pixmap.width (), m_pixmap.height ());
+}
+
+// public
+int kpTempPixmap::width () const
+{
+ return m_pixmap.width ();
+}
+
+// public
+int kpTempPixmap::height () const
+{
+ return m_pixmap.height ();
+}
+
+
+// public
+bool kpTempPixmap::mayChangeDocumentMask () const
+{
+ return (m_renderMode == SetPixmap ||
+ m_renderMode == PaintMaskTransparentWithBrush);
+}
+
+// public
+void kpTempPixmap::paint (QPixmap *destPixmap, const QRect &docRect) const
+{
+#define PARAMS destPixmap, m_topLeft - docRect.topLeft (), m_pixmap
+ switch (m_renderMode)
+ {
+ case SetPixmap:
+ kpPixmapFX::setPixmapAt (PARAMS);
+ break;
+ case PaintPixmap:
+ kpPixmapFX::paintPixmapAt (PARAMS);
+ break;
+ case PaintMaskTransparentWithBrush:
+ kpPixmapFX::paintMaskTransparentWithBrush (PARAMS);
+ break;
+ }
+#undef PARAMS
+}
diff --git a/kolourpaint/kptemppixmap.h b/kolourpaint/kptemppixmap.h
new file mode 100644
index 00000000..6ddb9413
--- /dev/null
+++ b/kolourpaint/kptemppixmap.h
@@ -0,0 +1,90 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+// TODO: maybe merge with kpSelection?
+
+
+#ifndef __kp_temp_pixmap_h__
+#define __kp_temp_pixmap_h__
+
+
+#include <qpoint.h>
+#include <qpixmap.h>
+
+class kpViewManager;
+
+
+class kpTempPixmap
+{
+public:
+ enum RenderMode
+ {
+ SetPixmap,
+ PaintPixmap,
+ PaintMaskTransparentWithBrush
+ };
+
+ /*
+ * <isBrush> Specifies that its visibility is dependent on whether or
+ * not the mouse cursor is inside a view. If false, the
+ * pixmap is always displayed.
+ */
+ kpTempPixmap (bool isBrush, RenderMode renderMode, const QPoint &topLeft, const QPixmap &pixmap);
+ kpTempPixmap (const kpTempPixmap &rhs);
+ kpTempPixmap &operator= (const kpTempPixmap &rhs);
+ ~kpTempPixmap ();
+
+ bool isBrush () const;
+ RenderMode renderMode () const;
+ QPoint topLeft () const;
+ QPixmap pixmap () const;
+
+ bool isVisible (const kpViewManager *vm) const;
+ QRect rect () const;
+ int width () const;
+ int height () const;
+
+
+ // Returns whether a call to paint() may change <*destPixmap>'s mask
+ bool mayChangeDocumentMask () const;
+
+ /*
+ * Draws itself onto <*destPixmap>, given that <*destPixmap> represents
+ * the unzoomed <docRect> of the kpDocument. You should check for
+ * visibility before calling this function.
+ */
+ void paint (QPixmap *destPixmap, const QRect &docRect) const;
+
+private:
+ bool m_isBrush;
+ RenderMode m_renderMode;
+ QPoint m_topLeft;
+ QPixmap m_pixmap;
+};
+
+
+#endif // __kp_temp_pixmap_h__
diff --git a/kolourpaint/kptextstyle.cpp b/kolourpaint/kptextstyle.cpp
new file mode 100644
index 00000000..9f4d8fce
--- /dev/null
+++ b/kolourpaint/kptextstyle.cpp
@@ -0,0 +1,279 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <kptextstyle.h>
+
+#include <qdatastream.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+
+
+kpTextStyle::kpTextStyle ()
+ : m_fontSize (0),
+ m_isBold (false), m_isItalic (false),
+ m_isUnderline (false), m_isStrikeThru (false),
+ m_isBackgroundOpaque (true)
+{
+}
+
+kpTextStyle::kpTextStyle (const QString &fontFamily,
+ int fontSize,
+ bool isBold, bool isItalic,
+ bool isUnderline, bool isStrikeThru,
+ const kpColor &fcolor, const kpColor &bcolor,
+ bool isBackgroundOpaque)
+ : m_fontFamily (fontFamily),
+ m_fontSize (fontSize),
+ m_isBold (isBold), m_isItalic (isItalic),
+ m_isUnderline (isUnderline), m_isStrikeThru (isStrikeThru),
+ m_foregroundColor (fcolor), m_backgroundColor (bcolor),
+ m_isBackgroundOpaque (isBackgroundOpaque)
+{
+}
+
+kpTextStyle::~kpTextStyle ()
+{
+}
+
+
+// friend
+QDataStream &operator<< (QDataStream &stream, const kpTextStyle &textStyle)
+{
+ stream << textStyle.m_fontFamily;
+ stream << textStyle.m_fontSize;
+
+ stream << int (textStyle.m_isBold) << int (textStyle.m_isItalic)
+ << int (textStyle.m_isUnderline) << int (textStyle.m_isStrikeThru);
+
+ stream << textStyle.m_foregroundColor << textStyle.m_backgroundColor;
+
+ stream << int (textStyle.m_isBackgroundOpaque);
+
+ return stream;
+}
+
+// friend
+QDataStream &operator>> (QDataStream &stream, kpTextStyle &textStyle)
+{
+ stream >> textStyle.m_fontFamily;
+ stream >> textStyle.m_fontSize;
+
+ int a, b, c, d;
+ stream >> a >> b >> c >> d;
+ textStyle.m_isBold = a;
+ textStyle.m_isItalic = b;
+ textStyle.m_isUnderline = c;
+ textStyle.m_isStrikeThru = d;
+
+ stream >> textStyle.m_foregroundColor >> textStyle.m_backgroundColor;
+
+ int e;
+ stream >> e;
+ textStyle.m_isBackgroundOpaque = e;
+
+ return stream;
+}
+
+// public
+bool kpTextStyle::operator== (const kpTextStyle &rhs) const
+{
+ return (m_fontFamily == rhs.m_fontFamily &&
+ m_fontSize == rhs.m_fontSize &&
+ m_isBold == rhs.m_isBold &&
+ m_isItalic == rhs.m_isItalic &&
+ m_isUnderline == rhs.m_isUnderline &&
+ m_isStrikeThru == rhs.m_isStrikeThru &&
+ m_foregroundColor == rhs.m_foregroundColor &&
+ m_backgroundColor == rhs.m_backgroundColor &&
+ m_isBackgroundOpaque == rhs.m_isBackgroundOpaque);
+}
+
+// public
+bool kpTextStyle::operator!= (const kpTextStyle &rhs) const
+{
+ return !(*this == rhs);
+}
+
+
+// public
+QString kpTextStyle::fontFamily () const
+{
+ return m_fontFamily;
+}
+
+// public
+void kpTextStyle::setFontFamily (const QString &f)
+{
+ m_fontFamily = f;
+}
+
+
+// public
+int kpTextStyle::fontSize () const
+{
+ return m_fontSize;
+}
+
+// public
+void kpTextStyle::setFontSize (int s)
+{
+ m_fontSize = s;
+}
+
+
+// public
+bool kpTextStyle::isBold () const
+{
+ return m_isBold;
+}
+
+// public
+void kpTextStyle::setBold (bool yes)
+{
+ m_isBold = yes;
+}
+
+
+// public
+bool kpTextStyle::isItalic () const
+{
+ return m_isItalic;
+}
+
+// public
+void kpTextStyle::setItalic (bool yes)
+{
+ m_isItalic = yes;
+}
+
+
+// public
+bool kpTextStyle::isUnderline () const
+{
+ return m_isUnderline;
+}
+
+// public
+void kpTextStyle::setUnderline (bool yes)
+{
+ m_isUnderline = yes;
+}
+
+
+// public
+bool kpTextStyle::isStrikeThru () const
+{
+ return m_isStrikeThru;
+}
+
+// public
+void kpTextStyle::setStrikeThru (bool yes)
+{
+ m_isStrikeThru = yes;
+}
+
+
+// public
+kpColor kpTextStyle::foregroundColor () const
+{
+ return m_foregroundColor;
+}
+
+// public
+void kpTextStyle::setForegroundColor (const kpColor &fcolor)
+{
+ m_foregroundColor = fcolor;
+}
+
+
+// public
+kpColor kpTextStyle::backgroundColor () const
+{
+ return m_backgroundColor;
+}
+
+// public
+void kpTextStyle::setBackgroundColor (const kpColor &bcolor)
+{
+ m_backgroundColor = bcolor;
+}
+
+
+// public
+bool kpTextStyle::isBackgroundOpaque () const
+{
+ return m_isBackgroundOpaque;
+}
+
+// public
+void kpTextStyle::setBackgroundOpaque (bool yes)
+{
+ m_isBackgroundOpaque = yes;
+}
+
+
+// public
+bool kpTextStyle::isBackgroundTransparent () const
+{
+ return !m_isBackgroundOpaque;
+}
+
+// public
+void kpTextStyle::setBackgroundTransparent (bool yes)
+{
+ m_isBackgroundOpaque = !yes;
+}
+
+
+// public
+kpColor kpTextStyle::effectiveBackgroundColor () const
+{
+ if (isBackgroundOpaque ())
+ return backgroundColor ();
+ else
+ return kpColor::transparent;
+}
+
+
+// public
+QFont kpTextStyle::font () const
+{
+ QFont fnt (m_fontFamily, m_fontSize);
+ fnt.setBold (m_isBold);
+ fnt.setItalic (m_isItalic);
+ fnt.setUnderline (m_isUnderline);
+ fnt.setStrikeOut (m_isStrikeThru);
+
+ return fnt;
+}
+
+// public
+QFontMetrics kpTextStyle::fontMetrics () const
+{
+ return QFontMetrics (font ());
+}
diff --git a/kolourpaint/kptextstyle.h b/kolourpaint/kptextstyle.h
new file mode 100644
index 00000000..9356ce2c
--- /dev/null
+++ b/kolourpaint/kptextstyle.h
@@ -0,0 +1,108 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef __kp_text_style_h__
+#define __kp_text_style_h__
+
+#include <qstring.h>
+
+#include <kpcolor.h>
+
+class QDataStream;
+class QFont;
+class QFontMetrics;
+
+class kpTextStyle
+{
+public:
+ kpTextStyle ();
+ kpTextStyle (const QString &fontFamily,
+ int fontSize,
+ bool isBold, bool isItalic,
+ bool isUnderline, bool isStrikeThru,
+ const kpColor &fcolor,
+ const kpColor &bcolor,
+ bool isBackgroundOpaque);
+ ~kpTextStyle ();
+
+
+ friend QDataStream &operator<< (QDataStream &stream, const kpTextStyle &textStyle);
+ friend QDataStream &operator>> (QDataStream &stream, kpTextStyle &textStyle);
+ bool operator== (const kpTextStyle &rhs) const;
+ bool operator!= (const kpTextStyle &rhs) const;
+
+
+ QString fontFamily () const;
+ void setFontFamily (const QString &f);
+
+ int fontSize () const;
+ void setFontSize (int s);
+
+ bool isBold () const;
+ void setBold (bool yes = true);
+
+ bool isItalic () const;
+ void setItalic (bool yes = true);
+
+ bool isUnderline () const;
+ void setUnderline (bool yes = true);
+
+ bool isStrikeThru () const;
+ void setStrikeThru (bool yes = true);
+
+ kpColor foregroundColor () const;
+ void setForegroundColor (const kpColor &fcolor);
+
+ // Note: This is the _input_ backgroundColor without applying any
+ // isBackground(Opaque|Transparent)() transformation.
+ // See effectiveBackgroundColor().
+ kpColor backgroundColor () const;
+ void setBackgroundColor (const kpColor &bcolor);
+
+ bool isBackgroundOpaque () const;
+ void setBackgroundOpaque (bool yes = true);
+
+ bool isBackgroundTransparent () const;
+ void setBackgroundTransparent (bool yes = true);
+
+
+ // If isBackgroundOpaque(), returns backgroundColor().
+ // Else, returns kpColor::transparent.
+ kpColor effectiveBackgroundColor () const;
+
+ QFont font () const;
+ QFontMetrics fontMetrics () const;
+
+private:
+ QString m_fontFamily;
+ int m_fontSize;
+ bool m_isBold, m_isItalic, m_isUnderline, m_isStrikeThru;
+ kpColor m_foregroundColor, m_backgroundColor;
+ bool m_isBackgroundOpaque;
+};
+
+#endif // __kp_text_style_h__
diff --git a/kolourpaint/kpthumbnail.cpp b/kolourpaint/kpthumbnail.cpp
new file mode 100644
index 00000000..a45164ac
--- /dev/null
+++ b/kolourpaint/kpthumbnail.cpp
@@ -0,0 +1,213 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define DEBUG_KP_THUMBNAIL 0
+
+#include <kpthumbnail.h>
+
+#include <qdockarea.h>
+#include <qdockwindow.h>
+#include <qtimer.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kpdefs.h>
+#include <kpdocument.h>
+#include <kpmainwindow.h>
+#include <kpthumbnailview.h>
+#include <kptool.h>
+
+
+// TODO: get out of the Alt+Tab list
+kpThumbnail::kpThumbnail (kpMainWindow *parent, const char *name)
+#if KP_IS_QT_3_3
+ : QDockWindow (QDockWindow::OutsideDock, parent, name),
+#else // With Qt <3.3, OutsideDock requires parent = 0,
+ // sync: make sure outside of dock
+ : QDockWindow (QDockWindow::InDock, parent, name),
+ #warning "Using Qt <3.3: the thumbnail will flicker more when appearing"
+#endif
+ m_mainWindow (parent),
+ m_view (0)
+{
+ if (!parent)
+ kdError () << "kpThumbnail::kpThumbnail() requires parent" << endl;
+
+
+#if !KP_IS_QT_3_3
+ if (parent)
+ {
+ hide ();
+
+ // sync: make sure outside of dock
+ parent->moveDockWindow (this, Qt::DockTornOff);
+ }
+#endif
+
+
+ if (parent)
+ {
+ // Prevent thumbnail from docking - it's _really_ irritating otherwise
+ parent->leftDock ()->setAcceptDockWindow (this, false);
+ parent->rightDock ()->setAcceptDockWindow (this, false);
+ parent->topDock ()->setAcceptDockWindow (this, false);
+ parent->bottomDock ()->setAcceptDockWindow (this, false);
+ }
+
+
+ QSize layoutMinimumSize = layout () ? layout ()->minimumSize () : QSize ();
+#if DEBUG_KP_THUMBNAIL
+ kdDebug () << "\tlayout=" << layout ()
+ << " minSize=" << (layout () ? layout ()->minimumSize () : QSize ()) << endl;
+ kdDebug () << "\tboxLayout=" << boxLayout ()
+ << " minSize=" << (boxLayout () ? boxLayout ()->minimumSize () : QSize ())
+ << endl;
+#endif
+ if (layout ())
+ layout ()->setResizeMode (QLayout::FreeResize);
+ setMinimumSize (QMAX (layoutMinimumSize.width (), 64),
+ QMAX (layoutMinimumSize.height (), 64));
+
+
+ // Enable "X" Close Button
+ setCloseMode (QDockWindow::Always);
+
+ setResizeEnabled (true);
+
+ updateCaption ();
+}
+
+kpThumbnail::~kpThumbnail ()
+{
+}
+
+
+// public
+kpThumbnailView *kpThumbnail::view () const
+{
+ return m_view;
+}
+
+// public
+void kpThumbnail::setView (kpThumbnailView *view)
+{
+#if DEBUG_KP_THUMBNAIL
+ kdDebug () << "kpThumbnail::setView(" << view << ")" << endl;
+#endif
+
+ if (m_view == view)
+ return;
+
+
+ if (m_view)
+ {
+ disconnect (m_view, SIGNAL (destroyed ()),
+ this, SLOT (slotViewDestroyed ()));
+ disconnect (m_view, SIGNAL (zoomLevelChanged (int, int)),
+ this, SLOT (updateCaption ()));
+
+ boxLayout ()->remove (m_view);
+ }
+
+ m_view = view;
+
+ if (m_view)
+ {
+ connect (m_view, SIGNAL (destroyed ()),
+ this, SLOT (slotViewDestroyed ()));
+ connect (m_view, SIGNAL (zoomLevelChanged (int, int)),
+ this, SLOT (updateCaption ()));
+ updateCaption ();
+
+ boxLayout ()->addWidget (m_view);
+ m_view->show ();
+ }
+}
+
+
+// public slot
+void kpThumbnail::updateCaption ()
+{
+ setCaption (view () ? view ()->caption () : i18n ("Thumbnail"));
+}
+
+
+// public slot virtual [base QDockWindow]
+void kpThumbnail::dock ()
+{
+#if DEBUG_KP_THUMBNAIL
+ kdDebug () << "kpThumbnail::dock() CALLED - ignoring request" << endl;
+#endif
+
+ // --- ignore all requests to dock ---
+}
+
+
+// protected slot
+void kpThumbnail::slotViewDestroyed ()
+{
+#if DEBUG_KP_THUMBNAIL
+ kdDebug () << "kpThumbnail::slotViewDestroyed()" << endl;
+#endif
+
+ m_view = 0;
+ updateCaption ();
+}
+
+
+// protected virtual [base QWidget]
+void kpThumbnail::resizeEvent (QResizeEvent *e)
+{
+#if DEBUG_KP_THUMBNAIL
+ kdDebug () << "kpThumbnail::resizeEvent(" << width ()
+ << "," << height () << ")" << endl;
+#endif
+
+ QDockWindow::resizeEvent (e);
+
+ // updateVariableZoom (); TODO: is below a good idea since this commented out
+
+ if (m_mainWindow)
+ {
+ m_mainWindow->notifyThumbnailGeometryChanged ();
+
+ if (m_mainWindow->tool ())
+ m_mainWindow->tool ()->somethingBelowTheCursorChanged ();
+ }
+}
+
+// protected virtual [base QWidget]
+void kpThumbnail::moveEvent (QMoveEvent * /*e*/)
+{
+ if (m_mainWindow)
+ m_mainWindow->notifyThumbnailGeometryChanged ();
+}
+
+
+#include <kpthumbnail.moc>
diff --git a/kolourpaint/kpthumbnail.h b/kolourpaint/kpthumbnail.h
new file mode 100644
index 00000000..183dc2f1
--- /dev/null
+++ b/kolourpaint/kpthumbnail.h
@@ -0,0 +1,68 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kp_thumbnail_h__
+#define __kp_thumbnail_h__
+
+#include <qdockwindow.h>
+
+class kpMainWindow;
+class kpThumbnailView;
+
+
+class kpThumbnail : public QDockWindow
+{
+Q_OBJECT
+
+public:
+ kpThumbnail (kpMainWindow *parent, const char *name = 0);
+ virtual ~kpThumbnail ();
+
+public:
+ kpThumbnailView *view () const;
+ void setView (kpThumbnailView *view);
+
+public slots:
+ void updateCaption ();
+
+ virtual void dock ();
+
+protected slots:
+ void slotViewDestroyed ();
+
+protected:
+ virtual void resizeEvent (QResizeEvent *e);
+ virtual void moveEvent (QMoveEvent *e);
+
+private:
+ kpMainWindow *m_mainWindow;
+ kpThumbnailView *m_view;
+};
+
+
+#endif // __kp_thumbnail_h__
diff --git a/kolourpaint/kptool.cpp b/kolourpaint/kptool.cpp
new file mode 100644
index 00000000..8d337c5b
--- /dev/null
+++ b/kolourpaint/kptool.cpp
@@ -0,0 +1,1666 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define DEBUG_KP_TOOL 0
+
+
+#include <kptool.h>
+
+#include <limits.h>
+
+#include <qapplication.h>
+#include <qclipboard.h>
+#include <qcursor.h>
+#include <qevent.h>
+#include <qlayout.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+#include <kpcolor.h>
+#include <kpcolortoolbar.h>
+#include <kpdefs.h>
+#include <kpmainwindow.h>
+#include <kppixmapfx.h>
+#include <kpsinglekeytriggersaction.h>
+#include <kptoolaction.h>
+#include <kptooltoolbar.h>
+#include <kpview.h>
+#include <kpviewmanager.h>
+
+
+//
+// kpTool
+//
+
+struct kpToolPrivate
+{
+};
+
+
+kpTool::kpTool (const QString &text, const QString &description,
+ int key,
+ kpMainWindow *mainWindow, const char *name)
+{
+ init (text, description, key, mainWindow, name);
+}
+
+kpTool::~kpTool ()
+{
+ // before destructing, stop using the tool
+ if (m_began)
+ endInternal ();
+
+ if (m_action)
+ {
+ if (m_mainWindow && m_mainWindow->actionCollection ())
+ m_mainWindow->actionCollection ()->remove (m_action);
+ else
+ delete m_action;
+ }
+
+ delete d;
+}
+
+
+// private
+void kpTool::init (const QString &text, const QString &description,
+ int key,
+ kpMainWindow *mainWindow, const char *name)
+{
+ d = new kpToolPrivate ();
+
+ m_key = key;
+ m_action = 0;
+ m_ignoreColorSignals = 0;
+ m_shiftPressed = false, m_controlPressed = false, m_altPressed = false; // set in beginInternal()
+ m_beganDraw = false;
+ m_text = text, m_description = description, m_name = name;
+ m_mainWindow = mainWindow;
+ m_began = false;
+ m_viewUnderStartPoint = 0;
+ m_userShapeStartPoint = KP_INVALID_POINT;
+ m_userShapeEndPoint = KP_INVALID_POINT;
+ m_userShapeSize = KP_INVALID_SIZE;
+
+ createAction ();
+}
+
+
+// private
+void kpTool::createAction ()
+{
+#if DEBUG_KP_TOOL && 0
+ kdDebug () << "kpTool(" << name () << "::createAction()" << endl;
+#endif
+
+ if (!m_mainWindow)
+ {
+ kdError () << "kpTool::createAction() without mw" << endl;
+ return;
+ }
+
+ KActionCollection *ac = m_mainWindow->actionCollection ();
+ if (!ac)
+ {
+ kdError () << "kpTool::createAction() without ac" << endl;
+ return;
+ }
+
+
+ if (m_action)
+ {
+ // TODO: I don't think this will ever be executed as we are not called
+ // outside of the constructor.
+ #if DEBUG_KP_TOOL
+ kdDebug () << "\tdeleting existing" << endl;
+ #endif
+ ac->remove (m_action);
+ m_action = 0;
+ }
+
+
+ m_action = new kpToolAction (text (), iconName (), shortcutForKey (m_key),
+ this, SLOT (slotActionActivated ()),
+ m_mainWindow->actionCollection (), name ());
+ m_action->setExclusiveGroup (QString::fromLatin1 ("Tool Box Actions"));
+ m_action->setWhatsThis (description ());
+
+ connect (m_action, SIGNAL (toolTipChanged (const QString &)),
+ this, SLOT (slotActionToolTipChanged (const QString &)));
+}
+
+
+// protected slot
+void kpTool::slotActionToolTipChanged (const QString &string)
+{
+ emit actionToolTipChanged (string);
+}
+
+
+// public
+QString kpTool::text () const
+{
+ return m_text;
+}
+
+// public
+void kpTool::setText (const QString &text)
+{
+ m_text = text;
+
+ if (m_action)
+ m_action->setText (m_text);
+ else
+ createAction ();
+}
+
+
+// public static
+QString kpTool::toolTipForTextAndShortcut (const QString &text,
+ const KShortcut &shortcut)
+{
+ for (int i = 0; i < (int) shortcut.count (); i++)
+ {
+ const KKeySequence seq = shortcut.seq (i);
+ if (seq.count () == 1 && containsSingleKeyTrigger (seq))
+ {
+ return i18n ("<Tool Name> (<Single Accel Key>)",
+ "%1 (%2)")
+ .arg (text, seq.toString ());
+ }
+ }
+
+ return text;
+}
+
+// public static
+QString kpTool::toolTip () const
+{
+ return toolTipForTextAndShortcut (text (), shortcut ());
+}
+
+
+// public
+int kpTool::key () const
+{
+ return m_key;
+}
+
+// public
+void kpTool::setKey (int key)
+{
+ m_key = key;
+
+ if (m_action)
+ // TODO: this probably not wise since it nukes the user's settings
+ m_action->setShortcut (shortcutForKey (m_key));
+ else
+ createAction ();
+}
+
+// public static
+KShortcut kpTool::shortcutForKey (int key)
+{
+ KShortcut shortcut;
+
+ if (key)
+ {
+ shortcut.append (KKeySequence (KKey (key)));
+ // (CTRL+<key>, ALT+<key>, CTRL+ALT+<key>, CTRL+SHIFT+<key>
+ // all clash with global KDE shortcuts)
+ shortcut.append (KKeySequence (KKey (Qt::ALT + Qt::SHIFT + key)));
+ }
+
+ return shortcut;
+}
+
+// public
+KShortcut kpTool::shortcut () const
+{
+ return m_action ? m_action->shortcut () : KShortcut ();
+}
+
+
+// public static
+bool kpTool::keyIsText (int key)
+{
+ // TODO: should work like !QKeyEvent::text().isEmpty()
+ return !(key & (Qt::MODIFIER_MASK ^ Qt::SHIFT));
+}
+
+// public static
+bool kpTool::containsSingleKeyTrigger (const KKeySequence &seq)
+{
+ for (int i = 0; i < (int) seq.count (); i++)
+ {
+ const KKey key = seq.key (i);
+ if (keyIsText (key.keyCodeQt ()))
+ return true;
+ }
+
+ return false;
+}
+
+// public static
+bool kpTool::containsSingleKeyTrigger (const KShortcut &shortcut,
+ KShortcut *shortcutWithoutSingleKeyTriggers)
+{
+ if (shortcutWithoutSingleKeyTriggers)
+ *shortcutWithoutSingleKeyTriggers = shortcut;
+
+
+ KShortcut newShortcut;
+ bool needNewShortcut = false;
+
+ for (int i = 0; i < (int) shortcut.count (); i++)
+ {
+ const KKeySequence seq = shortcut.seq (i);
+
+ if (containsSingleKeyTrigger (seq))
+ {
+ needNewShortcut = true;
+ }
+ else
+ {
+ newShortcut.append (seq);
+ }
+ }
+
+
+ if (needNewShortcut && shortcutWithoutSingleKeyTriggers)
+ *shortcutWithoutSingleKeyTriggers = newShortcut;
+
+ return needNewShortcut;
+}
+
+
+// public
+bool kpTool::singleKeyTriggersEnabled () const
+{
+ return (m_action ? m_action->singleKeyTriggersEnabled () : true);
+}
+
+// public
+void kpTool::enableSingleKeyTriggers (bool enable)
+{
+#if DEBUG_KP_TOOL && 0
+ kdDebug () << "kpTool(" << name () << ")::enableSingleKeyTriggers("
+ << enable << ")" << endl;
+#endif
+
+ if (!m_action)
+ {
+ #if DEBUG_KP_TOOL && 0
+ kdDebug () << "\tno action" << endl;
+ #endif
+ return;
+ }
+
+ m_action->enableSingleKeyTriggers (enable);
+}
+
+
+// public
+QString kpTool::description () const
+{
+ return m_description;
+}
+
+// public
+void kpTool::setDescription (const QString &description)
+{
+ m_description = description;
+
+ if (m_action)
+ m_action->setWhatsThis (m_description);
+ else
+ createAction ();
+}
+
+
+// public
+const char *kpTool::name () const
+{
+ return m_name;
+}
+
+
+// static
+QRect kpTool::neededRect (const QRect &rect, int lineWidth)
+{
+ int x1, y1, x2, y2;
+ rect.coords (&x1, &y1, &x2, &y2);
+
+ if (lineWidth < 1)
+ lineWidth = 1;
+
+ return QRect (QPoint (x1 - lineWidth + 1, y1 - lineWidth + 1),
+ QPoint (x2 + lineWidth - 1, y2 + lineWidth - 1));
+}
+
+// static
+QPixmap kpTool::neededPixmap (const QPixmap &pixmap, const QRect &boundingRect)
+{
+ return kpPixmapFX::getPixmapAt (pixmap, boundingRect);
+}
+
+
+// public
+bool kpTool::hasCurrentPoint () const
+{
+ return (viewUnderStartPoint () || viewUnderCursor ());
+}
+
+// public
+QPoint kpTool::currentPoint (bool zoomToDoc) const
+{
+#if DEBUG_KP_TOOL && 0
+ kdDebug () << "kpTool::currentPoint(zoomToDoc=" << zoomToDoc << ")" << endl;
+ kdDebug () << "\tviewUnderStartPoint="
+ << (viewUnderStartPoint () ? viewUnderStartPoint ()->name () : "(none)")
+ << " viewUnderCursor="
+ << (viewUnderCursor () ? viewUnderCursor ()->name () : "(none)")
+ << endl;
+#endif
+
+ kpView *v = viewUnderStartPoint ();
+ if (!v)
+ {
+ v = viewUnderCursor ();
+ if (!v)
+ {
+ #if DEBUG_KP_TOOL && 0
+ kdDebug () << "\tno view - returning sentinel" << endl;
+ #endif
+ return KP_INVALID_POINT;
+ }
+ }
+
+
+ const QPoint globalPos = QCursor::pos ();
+ const QPoint viewPos = v->mapFromGlobal (globalPos);
+#if DEBUG_KP_TOOL && 0
+ kdDebug () << "\tglobalPos=" << globalPos
+ << " viewPos=" << viewPos
+ << endl;
+#endif
+ if (!zoomToDoc)
+ return viewPos;
+
+
+ const QPoint docPos = v->transformViewToDoc (viewPos);
+#if DEBUG_KP_TOOL && 0
+ kdDebug () << "\tdocPos=" << docPos << endl;
+#endif
+ return docPos;
+}
+
+
+// public slot
+void kpTool::somethingBelowTheCursorChanged ()
+{
+ somethingBelowTheCursorChanged (currentPoint (),
+ currentPoint (false/*view point*/));
+}
+
+// private
+// TODO: don't dup code from mouseMoveEvent()
+void kpTool::somethingBelowTheCursorChanged (const QPoint &currentPoint_,
+ const QPoint &currentViewPoint_)
+{
+#if DEBUG_KP_TOOL && 0
+ kdDebug () << "kpTool::somethingBelowTheCursorChanged(docPoint="
+ << currentPoint_
+ << " viewPoint="
+ << currentViewPoint_
+ << ")" << endl;
+ kdDebug () << "\tviewUnderStartPoint="
+ << (viewUnderStartPoint () ? viewUnderStartPoint ()->name () : "(none)")
+ << " viewUnderCursor="
+ << (viewUnderCursor () ? viewUnderCursor ()->name () : "(none)")
+ << endl;
+ kdDebug () << "\tbegan draw=" << m_beganDraw << endl;
+#endif
+
+ m_currentPoint = currentPoint_;
+ m_currentViewPoint = currentViewPoint_;
+
+ if (m_beganDraw)
+ {
+ if (m_currentPoint != KP_INVALID_POINT)
+ {
+ draw (m_currentPoint, m_lastPoint, QRect (m_startPoint, m_currentPoint).normalize ());
+ m_lastPoint = m_currentPoint;
+ }
+ }
+ else
+ {
+ hover (m_currentPoint);
+ }
+}
+
+
+void kpTool::beginInternal ()
+{
+#if DEBUG_KP_TOOL
+ kdDebug () << "kpTool::beginInternal()" << endl;
+#endif
+
+ if (!m_began)
+ {
+ // clear leftover statusbar messages
+ setUserMessage ();
+ m_currentPoint = currentPoint ();
+ m_currentViewPoint = currentPoint (false/*view point*/);
+ setUserShapePoints (m_currentPoint);
+
+ // TODO: Audit all the code in this file - states like "m_began" &
+ // "m_beganDraw" should be set before calling user func.
+ // Also, m_currentPoint should be more frequently initialised.
+
+ // call user virtual func
+ begin ();
+
+ // we've starting using the tool...
+ m_began = true;
+
+ // but we haven't started drawing with it
+ m_beganDraw = false;
+
+
+ uint keyState = KApplication::keyboardModifiers ();
+
+ m_shiftPressed = (keyState & KApplication::ShiftModifier);
+ m_controlPressed = (keyState & KApplication::ControlModifier);
+
+ // TODO: Can't do much about ALT - unless it's always KApplication::Modifier1?
+ // Ditto for everywhere else where I set SHIFT & CTRL but not alt.
+ m_altPressed = false;
+ }
+}
+
+void kpTool::endInternal ()
+{
+ if (m_began)
+ {
+ // before we can stop using the tool, we must stop the current drawing operation (if any)
+ if (hasBegunShape ())
+ endShapeInternal (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ());
+
+ // call user virtual func
+ end ();
+
+ // clear leftover statusbar messages
+ setUserMessage ();
+ setUserShapePoints (currentPoint ());
+
+ // we've stopped using the tool...
+ m_began = false;
+
+ // and so we can't be drawing with it
+ m_beganDraw = false;
+
+ if (m_mainWindow)
+ {
+ kpToolToolBar *tb = m_mainWindow->toolToolBar ();
+ if (tb)
+ {
+ tb->hideAllToolWidgets ();
+ }
+ }
+
+ }
+}
+
+// virtual
+void kpTool::begin ()
+{
+#if DEBUG_KP_TOOL
+ kdDebug () << "kpTool::begin() base implementation" << endl;
+#endif
+}
+
+// virtual
+void kpTool::end ()
+{
+#if DEBUG_KP_TOOL
+ kdDebug () << "kpTool::end() base implementation" << endl;
+#endif
+}
+
+void kpTool::beginDrawInternal ()
+{
+ if (!m_beganDraw)
+ {
+ beginDraw ();
+
+ m_beganDraw = true;
+ emit beganDraw (m_currentPoint);
+ }
+}
+
+// virtual
+void kpTool::beginDraw ()
+{
+}
+
+// virtual
+void kpTool::hover (const QPoint &point)
+{
+#if DEBUG_KP_TOOL
+ kdDebug () << "kpTool::hover" << point
+ << " base implementation"
+ << endl;
+#endif
+
+ setUserShapePoints (point);
+}
+
+// virtual
+void kpTool::globalDraw ()
+{
+}
+
+// virtual
+void kpTool::reselect ()
+{
+#if DEBUG_KP_TOOL
+ kdDebug () << "kpTool::reselect() base implementation" << endl;
+#endif
+}
+
+
+// public
+QIconSet kpTool::iconSet (int forceSize) const
+{
+#if DEBUG_KP_TOOL && 0
+ kdDebug () << "kpTool(" << name () << ")::iconSet(forceSize=" << forceSize << ")" << endl;
+#endif
+ // (robust in case BarIcon() default arg changes)
+ if (forceSize > 0)
+ return BarIconSet (name (), forceSize);
+ else
+ return BarIconSet (name ());
+}
+
+// public
+QString kpTool::iconName () const
+{
+ return name ();
+}
+
+// public
+kpToolAction *kpTool::action ()
+{
+ if (!m_action)
+ createAction ();
+
+ return m_action;
+}
+
+
+// protected slots
+void kpTool::slotActionActivated ()
+{
+ emit actionActivated ();
+}
+
+
+// virtual
+void kpTool::draw (const QPoint &, const QPoint &, const QRect &)
+{
+}
+
+// also called by kpView
+void kpTool::cancelShapeInternal ()
+{
+ if (hasBegunShape ())
+ {
+ m_beganDraw = false;
+ cancelShape ();
+ m_viewUnderStartPoint = 0;
+
+ emit cancelledShape (viewUnderCursor () ? m_currentPoint : KP_INVALID_POINT);
+
+ if (viewUnderCursor ())
+ hover (m_currentPoint);
+ else
+ {
+ m_currentPoint = KP_INVALID_POINT;
+ m_currentViewPoint = KP_INVALID_POINT;
+ hover (m_currentPoint);
+ }
+
+ if (returnToPreviousToolAfterEndDraw ())
+ {
+ kpToolToolBar *tb = mainWindow ()->toolToolBar ();
+
+ // (don't end up with no tool selected)
+ if (tb->previousTool ())
+ {
+ // endInternal() will be called by kpMainWindow (thanks to this line)
+ // so we won't have the view anymore
+ tb->selectPreviousTool ();
+ }
+ }
+ }
+}
+
+// virtual
+void kpTool::cancelShape ()
+{
+ kdWarning () << "Tool cannot cancel operation!" << endl;
+}
+
+void kpTool::releasedAllButtons ()
+{
+}
+
+void kpTool::endDrawInternal (const QPoint &thisPoint, const QRect &normalizedRect,
+ bool wantEndShape)
+{
+#if DEBUG_KP_TOOL && 1
+ kdDebug () << "kpTool::endDrawInternal() wantEndShape=" << wantEndShape << endl;
+#endif
+
+ if (wantEndShape && !hasBegunShape ())
+ return;
+ else if (!wantEndShape && !hasBegunDraw ())
+ return;
+
+ m_beganDraw = false;
+
+ if (wantEndShape)
+ {
+ #if DEBUG_KP_TOOL && 0
+ kdDebug () << "\tcalling endShape()" << endl;
+ #endif
+ endShape (thisPoint, normalizedRect);
+ }
+ else
+ {
+ #if DEBUG_KP_TOOL && 0
+ kdDebug () << "\tcalling endDraw()" << endl;
+ #endif
+ endDraw (thisPoint, normalizedRect);
+ }
+ m_viewUnderStartPoint = 0;
+
+ emit endedDraw (m_currentPoint);
+ if (viewUnderCursor ())
+ hover (m_currentPoint);
+ else
+ {
+ m_currentPoint = KP_INVALID_POINT;
+ m_currentViewPoint = KP_INVALID_POINT;
+ hover (m_currentPoint);
+ }
+
+ if (returnToPreviousToolAfterEndDraw ())
+ {
+ kpToolToolBar *tb = mainWindow ()->toolToolBar ();
+
+ // (don't end up with no tool selected)
+ if (tb->previousTool ())
+ {
+ // endInternal() will be called by kpMainWindow (thanks to this line)
+ // so we won't have the view anymore
+ tb->selectPreviousTool ();
+ }
+ }
+}
+
+// private
+void kpTool::endShapeInternal (const QPoint &thisPoint, const QRect &normalizedRect)
+{
+ endDrawInternal (thisPoint, normalizedRect, true/*end shape*/);
+}
+
+// virtual
+void kpTool::endDraw (const QPoint &, const QRect &)
+{
+}
+
+kpMainWindow *kpTool::mainWindow () const
+{
+ return m_mainWindow;
+}
+
+kpDocument *kpTool::document () const
+{
+ return m_mainWindow ? m_mainWindow->document () : 0;
+}
+
+kpView *kpTool::viewUnderCursor () const
+{
+ kpViewManager *vm = viewManager ();
+ return vm ? vm->viewUnderCursor () : 0;
+}
+
+kpViewManager *kpTool::viewManager () const
+{
+ return m_mainWindow ? m_mainWindow->viewManager () : 0;
+}
+
+kpToolToolBar *kpTool::toolToolBar () const
+{
+ return m_mainWindow ? m_mainWindow->toolToolBar () : 0;
+}
+
+kpColor kpTool::color (int which) const
+{
+ if (m_mainWindow)
+ return m_mainWindow->colorToolBar ()->color (which);
+ else
+ {
+ kdError () << "kpTool::color () without mainWindow" << endl;
+ return kpColor::invalid;
+ }
+}
+
+kpColor kpTool::foregroundColor () const
+{
+ return color (0);
+}
+
+kpColor kpTool::backgroundColor () const
+{
+ return color (1);
+}
+
+
+double kpTool::colorSimilarity () const
+{
+ if (m_mainWindow)
+ return m_mainWindow->colorToolBar ()->colorSimilarity ();
+ else
+ {
+ kdError () << "kpTool::colorSimilarity() without mainWindow" << endl;
+ return 0;
+ }
+}
+
+int kpTool::processedColorSimilarity () const
+{
+ if (m_mainWindow)
+ return m_mainWindow->colorToolBar ()->processedColorSimilarity ();
+ else
+ {
+ kdError () << "kpTool::processedColorSimilarity() without mainWindow" << endl;
+ return kpColor::Exact;
+ }
+}
+
+
+kpColor kpTool::oldForegroundColor () const
+{
+ if (m_mainWindow)
+ return m_mainWindow->colorToolBar ()->oldForegroundColor ();
+ else
+ {
+ kdError () << "kpTool::oldForegroundColor() without mainWindow" << endl;
+ return kpColor::invalid;
+ }
+}
+
+kpColor kpTool::oldBackgroundColor () const
+{
+ if (m_mainWindow)
+ return m_mainWindow->colorToolBar ()->oldBackgroundColor ();
+ else
+ {
+ kdError () << "kpTool::oldBackgroundColor() without mainWindow" << endl;
+ return kpColor::invalid;
+ }
+}
+
+double kpTool::oldColorSimilarity () const
+{
+ if (m_mainWindow)
+ return m_mainWindow->colorToolBar ()->oldColorSimilarity ();
+ else
+ {
+ kdError () << "kpTool::oldColorSimilarity() without mainWindow" << endl;
+ return 0;
+ }
+}
+
+
+void kpTool::slotColorsSwappedInternal (const kpColor &newForegroundColor,
+ const kpColor &newBackgroundColor)
+{
+ if (careAboutColorsSwapped ())
+ {
+ slotColorsSwapped (newForegroundColor, newBackgroundColor);
+ m_ignoreColorSignals = 2;
+ }
+ else
+ m_ignoreColorSignals = 0;
+}
+
+void kpTool::slotForegroundColorChangedInternal (const kpColor &color)
+{
+ if (m_ignoreColorSignals > 0)
+ {
+ #if DEBUG_KP_TOOL && 1
+ kdDebug () << "kpTool::slotForegroundColorChangedInternal() ignoreColorSignals=" << m_ignoreColorSignals << endl;
+ #endif
+ m_ignoreColorSignals--;
+ return;
+ }
+
+ slotForegroundColorChanged (color);
+}
+
+void kpTool::slotBackgroundColorChangedInternal (const kpColor &color)
+{
+ if (m_ignoreColorSignals > 0)
+ {
+ #if DEBUG_KP_TOOL && 1
+ kdDebug () << "kpTool::slotBackgroundColorChangedInternal() ignoreColorSignals=" << m_ignoreColorSignals << endl;
+ #endif
+ m_ignoreColorSignals--;
+ return;
+ }
+
+ slotBackgroundColorChanged (color);
+}
+
+void kpTool::slotColorSimilarityChangedInternal (double similarity, int processedSimilarity)
+{
+ slotColorSimilarityChanged (similarity, processedSimilarity);
+}
+
+bool kpTool::currentPointNextToLast () const
+{
+ if (m_lastPoint == QPoint (-1, -1))
+ return true;
+
+ int dx = kAbs (m_currentPoint.x () - m_lastPoint.x ());
+ int dy = kAbs (m_currentPoint.y () - m_lastPoint.y ());
+
+ return (dx <= 1 && dy <= 1);
+}
+
+bool kpTool::currentPointCardinallyNextToLast () const
+{
+ if (m_lastPoint == QPoint (-1, -1))
+ return true;
+
+ int dx = kAbs (m_currentPoint.x () - m_lastPoint.x ());
+ int dy = kAbs (m_currentPoint.y () - m_lastPoint.y ());
+
+ return (dx + dy <= 1);
+}
+
+kpCommandHistory *kpTool::commandHistory () const
+{
+ return m_mainWindow ? m_mainWindow->commandHistory () : 0;
+}
+
+void kpTool::mousePressEvent (QMouseEvent *e)
+{
+#if DEBUG_KP_TOOL && 1
+ kdDebug () << "kpTool::mousePressEvent pos=" << e->pos ()
+ << " btnStateBefore=" << (int) e->state ()
+ << " btnStateAfter=" << (int) e->stateAfter ()
+ << " button=" << (int) e->button ()
+ << " beganDraw=" << m_beganDraw << endl;
+#endif
+
+ // state of all the buttons - not just the one that triggered the event (button())
+ Qt::ButtonState buttonState = e->stateAfter ();
+
+ if (m_mainWindow && e->button () == Qt::MidButton)
+ {
+ const QString text = QApplication::clipboard ()->text (QClipboard::Selection);
+ #if DEBUG_KP_TOOL && 1
+ kdDebug () << "\tMMB pasteText='" << text << "'" << endl;
+ #endif
+ if (!text.isEmpty ())
+ {
+ if (hasBegunShape ())
+ {
+ #if DEBUG_KP_TOOL && 1
+ kdDebug () << "\t\thasBegunShape - end" << endl;
+ #endif
+ endShapeInternal (m_currentPoint,
+ QRect (m_startPoint, m_currentPoint).normalize ());
+ }
+
+ if (viewUnderCursor ())
+ {
+ m_mainWindow->pasteTextAt (text,
+ viewUnderCursor ()->transformViewToDoc (e->pos ()),
+ true/*adjust topLeft so that cursor isn't
+ on top of resize handle*/);
+ }
+
+ return;
+ }
+ }
+
+ int mb = mouseButton (buttonState);
+#if DEBUG_KP_TOOL && 1
+ kdDebug () << "\tmb=" << mb << " m_beganDraw=" << m_beganDraw << endl;
+#endif
+
+ if (mb == -1 && !m_beganDraw) return; // ignore
+
+ if (m_beganDraw)
+ {
+ if (mb == -1 || mb != m_mouseButton)
+ {
+ #if DEBUG_KP_TOOL && 1
+ kdDebug () << "\tCancelling operation as " << mb << " == -1 or != " << m_mouseButton << endl;
+ #endif
+
+ kpView *view = viewUnderStartPoint ();
+ if (!view)
+ {
+ kdError () << "kpTool::mousePressEvent() cancel without a view under the start point!" << endl;
+ }
+
+ // if we get a mousePressEvent when we're drawing, then the other
+ // mouse button must have been pressed
+ m_currentPoint = view ? view->transformViewToDoc (e->pos ()) : QPoint (-1, -1);
+ m_currentViewPoint = view ? e->pos () : QPoint (-1, -1);
+ cancelShapeInternal ();
+ }
+
+ return;
+ }
+
+ kpView *view = viewUnderCursor ();
+ if (!view)
+ {
+ kdError () << "kpTool::mousePressEvent() without a view under the cursor!" << endl;
+ }
+
+#if DEBUG_KP_TOOL && 1
+ if (view)
+ kdDebug () << "\tview=" << view->name () << endl;
+#endif
+
+
+ // let user know what mouse button is being used for entire draw
+ m_mouseButton = mouseButton (buttonState);
+ m_shiftPressed = (buttonState & Qt::ShiftButton);
+ m_controlPressed = (buttonState & Qt::ControlButton);
+ m_altPressed = (buttonState & Qt::AltButton);
+ m_startPoint = m_currentPoint = view ? view->transformViewToDoc (e->pos ()) : QPoint (-1, -1);
+ m_currentViewPoint = view ? e->pos () : QPoint (-1, -1);
+ m_viewUnderStartPoint = view;
+ m_lastPoint = QPoint (-1, -1);
+
+#if DEBUG_KP_TOOL && 1
+ kdDebug () << "\tBeginning draw @ " << m_currentPoint << endl;
+#endif
+
+ beginDrawInternal ();
+
+ draw (m_currentPoint, m_lastPoint, QRect (m_currentPoint, m_currentPoint));
+ m_lastPoint = m_currentPoint;
+}
+
+void kpTool::mouseMoveEvent (QMouseEvent *e)
+{
+#if DEBUG_KP_TOOL && 0
+ kdDebug () << "kpTool::mouseMoveEvent pos=" << e->pos ()
+ << " btnStateAfter=" << (int) e->stateAfter () << endl;
+ kpView *v0 = viewUnderCursor (),
+ *v1 = viewManager ()->viewUnderCursor (true/*use Qt*/),
+ *v2 = viewUnderStartPoint ();
+ kdDebug () << "\tviewUnderCursor=" << (v0 ? v0->name () : "(none)")
+ << " viewUnderCursorQt=" << (v1 ? v1->name () : "(none)")
+ << " viewUnderStartPoint=" << (v2 ? v2->name () : "(none)")
+ << endl;
+ kdDebug () << "\tfocusWidget=" << kapp->focusWidget () << endl;
+#endif
+
+ Qt::ButtonState buttonState = e->stateAfter ();
+ m_shiftPressed = (buttonState & Qt::ShiftButton);
+ m_controlPressed = (buttonState & Qt::ControlButton);
+ m_altPressed = (buttonState & Qt::AltButton);
+
+ if (m_beganDraw)
+ {
+ kpView *view = viewUnderStartPoint ();
+ if (!view)
+ {
+ kdError () << "kpTool::mouseMoveEvent() without a view under the start point!" << endl;
+ return;
+ }
+
+ m_currentPoint = view->transformViewToDoc (e->pos ());
+ m_currentViewPoint = e->pos ();
+
+ #if DEBUG_KP_TOOL && 0
+ kdDebug () << "\tDraw!" << endl;
+ #endif
+
+ bool dragScrolled = false;
+ movedAndAboutToDraw (m_currentPoint, m_lastPoint, view->zoomLevelX (), &dragScrolled);
+
+ if (dragScrolled)
+ {
+ m_currentPoint = currentPoint ();
+ m_currentViewPoint = currentPoint (false/*view point*/);
+
+ // Scrollview has scrolled contents and has scheduled an update
+ // for the newly exposed region. If draw() schedules an update
+ // as well (instead of immediately updating), the scrollview's
+ // update will be executed first and it'll only update part of
+ // the screen resulting in ugly tearing of the viewManager's
+ // tempPixmap.
+ viewManager ()->setFastUpdates ();
+ }
+
+ draw (m_currentPoint, m_lastPoint, QRect (m_startPoint, m_currentPoint).normalize ());
+
+ if (dragScrolled)
+ viewManager ()->restoreFastUpdates ();
+
+ m_lastPoint = m_currentPoint;
+ }
+ else
+ {
+ kpView *view = viewUnderCursor ();
+ if (!view) // possible if cancelShape()'ed but still holding down initial mousebtn
+ {
+ m_currentPoint = KP_INVALID_POINT;
+ m_currentViewPoint = KP_INVALID_POINT;
+ return;
+ }
+
+ m_currentPoint = view->transformViewToDoc (e->pos ());
+ m_currentViewPoint = e->pos ();
+ hover (m_currentPoint);
+ }
+}
+
+void kpTool::mouseReleaseEvent (QMouseEvent *e)
+{
+#if DEBUG_KP_TOOL && 1
+ kdDebug () << "kpTool::mouseReleaseEvent pos=" << e->pos ()
+ << " btnStateBefore=" << (int) e->state ()
+ << " btnStateAfter=" << (int) e->stateAfter ()
+ << " button=" << (int) e->button () << endl;
+#endif
+
+ if (m_beganDraw) // didn't cancelShape()
+ {
+ kpView *view = viewUnderStartPoint ();
+ if (!view)
+ {
+ kdError () << "kpTool::mouseReleaseEvent() without a view under the start point!" << endl;
+ return;
+ }
+
+ m_currentPoint = view ? view->transformViewToDoc (e->pos ()) : QPoint (-1, -1);
+ m_currentViewPoint = view ? e->pos () : QPoint (-1, -1);
+ endDrawInternal (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ());
+ }
+
+ if ((e->stateAfter () & Qt::MouseButtonMask) == 0)
+ {
+ releasedAllButtons ();
+ }
+}
+
+void kpTool::wheelEvent (QWheelEvent *e)
+{
+#if DEBUG_KP_TOOL
+ kdDebug () << "kpTool::wheelEvent() state=" << e->state ()
+ << " hasBegunDraw=" << hasBegunDraw ()
+ << " delta=" << e->delta ()
+ << endl;
+#endif
+
+ e->ignore ();
+
+ // If CTRL not pressed, bye.
+ if ((e->state () & Qt::ControlButton) == 0)
+ return;
+
+ // If drawing, bye; don't care if a shape in progress though.
+ if (hasBegunDraw ())
+ return;
+
+
+ // Zoom in/out depending on wheel direction.
+
+ // Moved wheel away from user?
+ if (e->delta () > 0)
+ {
+ m_mainWindow->zoomIn (true/*center under cursor*/);
+ e->accept ();
+ }
+ // Moved wheel towards user?
+ else if (e->delta () < 0)
+ {
+ #if 1
+ m_mainWindow->zoomOut (true/*center under cursor - make zoom in/out
+ stay under same doc pos*/);
+ #else
+ m_mainWindow->zoomOut (false/*don't center under cursor - as is
+ confusing behaviour when zooming
+ out*/);
+ #endif
+ e->accept ();
+ }
+}
+
+
+void kpTool::keyPressEvent (QKeyEvent *e)
+{
+#if DEBUG_KP_TOOL && 0
+ kdDebug () << "kpTool::keyPressEvent() e->key=" << e->key () << endl;
+#endif
+
+ int dx = 0, dy = 0;
+
+ e->ignore ();
+
+ switch (e->key ())
+ {
+ case 0:
+ case Qt::Key_unknown:
+ #if DEBUG_KP_TOOL && 0
+ kdDebug () << "kpTool::keyPressEvent() picked up unknown key!" << endl;
+ #endif
+ // --- fall thru and update all modifiers ---
+ case Qt::Key_Alt:
+ case Qt::Key_Shift:
+ case Qt::Key_Control:
+ keyUpdateModifierState (e);
+
+ e->accept ();
+ break;
+
+ case Qt::Key_Delete:
+ m_mainWindow->slotDelete ();
+ break;
+
+ /*
+ * QCursor::setPos conveniently causes mouseMoveEvents :)
+ */
+
+ case Qt::Key_Home: dx = -1, dy = -1; break;
+ case Qt::Key_Up: dy = -1; break;
+ case Qt::Key_PageUp: dx = +1, dy = -1; break;
+
+ case Qt::Key_Left: dx = -1; break;
+ case Qt::Key_Right: dx = +1; break;
+
+ case Qt::Key_End: dx = -1, dy = +1; break;
+ case Qt::Key_Down: dy = +1; break;
+ case Qt::Key_PageDown: dx = +1, dy = +1; break;
+
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ case Qt::Key_Insert:
+ case Qt::Key_Clear/*Numpad 5 Key*/:
+ {
+ kpView *view = viewUnderCursor (); // TODO: wrong for dragging lines outside of view (for e.g.)
+ if (view)
+ {
+ // TODO: what about the modifiers
+ QMouseEvent me (QEvent::MouseButtonPress,
+ view->mapFromGlobal (QCursor::pos ()),
+ Qt::LeftButton,
+ 0);
+ mousePressEvent (&me);
+ e->accept ();
+ }
+
+ break;
+ }}
+
+ kpView *view = viewUnderCursor ();
+ if (view && (dx || dy))
+ {
+ QPoint oldPoint = view->mapFromGlobal (QCursor::pos ());
+ #if DEBUG_KP_TOOL && 0
+ kdDebug () << "\toldPoint=" << oldPoint
+ << " dx=" << dx << " dy=" << dy << endl;
+ #endif
+
+
+ const int viewIncX = (dx ? QMAX (1, view->zoomLevelX () / 100) * dx : 0);
+ const int viewIncY = (dy ? QMAX (1, view->zoomLevelY () / 100) * dy : 0);
+
+ int newViewX = oldPoint.x () + viewIncX;
+ int newViewY = oldPoint.y () + viewIncY;
+
+
+ #if DEBUG_KP_TOOL && 0
+ kdDebug () << "\tnewPoint=" << QPoint (newViewX, newViewY) << endl;
+ #endif
+
+ if (view->transformViewToDoc (QPoint (newViewX, newViewY)) ==
+ view->transformViewToDoc (oldPoint))
+ {
+ newViewX += viewIncX, newViewY += viewIncY;
+
+ #if DEBUG_KP_TOOL && 0
+ kdDebug () << "\tneed adjust for doc - newPoint="
+ << QPoint (newViewX, newViewY) << endl;
+ #endif
+ }
+
+
+ // TODO: visible width/height (e.g. with scrollbars)
+ int x = QMIN (QMAX (newViewX, 0), view->width () - 1);
+ int y = QMIN (QMAX (newViewY, 0), view->height () - 1);
+
+ QCursor::setPos (view->mapToGlobal (QPoint (x, y)));
+ e->accept ();
+ }
+}
+
+void kpTool::keyReleaseEvent (QKeyEvent *e)
+{
+#if DEBUG_KP_TOOL && 0
+ kdDebug () << "kpTool::keyReleaseEvent() e->key=" << e->key () << endl;
+#endif
+
+ e->ignore ();
+
+ switch (e->key ())
+ {
+ case 0:
+ case Qt::Key_unknown:
+ #if DEBUG_KP_TOOL
+ kdDebug () << "kpTool::keyReleaseEvent() picked up unknown key!" << endl;
+ #endif
+ // HACK: around Qt bug: if you hold a modifier before you start the
+ // program and then release it over the view,
+ // Qt reports it as the release of an unknown key
+ // --- fall thru and update all modifiers ---
+ case Qt::Key_Alt:
+ case Qt::Key_Shift:
+ case Qt::Key_Control:
+ keyUpdateModifierState (e);
+
+ e->accept ();
+ break;
+
+ case Qt::Key_Escape:
+ if (hasBegunDraw ())
+ {
+ cancelShapeInternal ();
+ e->accept ();
+ }
+
+ break;
+
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ case Qt::Key_Insert:
+ case Qt::Key_Clear/*Numpad 5 Key*/:
+ {
+ kpView *view = viewUnderCursor ();
+ if (view)
+ {
+ QMouseEvent me (QEvent::MouseButtonRelease,
+ view->mapFromGlobal (QCursor::pos ()),
+ Qt::LeftButton,
+ Qt::LeftButton);
+ mouseReleaseEvent (&me);
+ e->accept ();
+ }
+ break;
+ }}
+}
+
+// private
+void kpTool::keyUpdateModifierState (QKeyEvent *e)
+{
+#if DEBUG_KP_TOOL && 0
+ kdDebug () << "kpTool::updateModifierState() e->key=" << e->key () << endl;
+ kdDebug () << "\tshift="
+ << (e->stateAfter () & Qt::ShiftButton)
+ << " control="
+ << (e->stateAfter () & Qt::ControlButton)
+ << " alt="
+ << (e->stateAfter () & Qt::AltButton)
+ << endl;
+#endif
+ if (e->key () & (Qt::Key_Alt | Qt::Key_Shift | Qt::Key_Control))
+ {
+ #if DEBUG_KP_TOOL && 0
+ kdDebug () << "\t\tmodifier changed - use e's claims" << endl;
+ #endif
+ setShiftPressed (e->stateAfter () & Qt::ShiftButton);
+ setControlPressed (e->stateAfter () & Qt::ControlButton);
+ setAltPressed (e->stateAfter () & Qt::AltButton);
+ }
+ else
+ {
+ #if DEBUG_KP_TOOL && 0
+ kdDebug () << "\t\tmodifiers not changed - figure out the truth" << endl;
+ #endif
+ uint keyState = KApplication::keyboardModifiers ();
+
+ setShiftPressed (keyState & KApplication::ShiftModifier);
+ setControlPressed (keyState & KApplication::ControlModifier);
+
+ // TODO: Can't do much about ALT - unless it's always KApplication::Modifier1?
+ // Ditto for everywhere else where I set SHIFT & CTRL but not alt.
+ setAltPressed (e->stateAfter () & Qt::AltButton);
+ }
+}
+
+
+void kpTool::notifyModifierStateChanged ()
+{
+ if (careAboutModifierState ())
+ {
+ if (m_beganDraw)
+ draw (m_currentPoint, m_lastPoint, QRect (m_startPoint, m_currentPoint).normalize ());
+ else
+ {
+ m_currentPoint = currentPoint ();
+ m_currentViewPoint = currentPoint (false/*view point*/);
+ hover (m_currentPoint);
+ }
+ }
+}
+
+void kpTool::setShiftPressed (bool pressed)
+{
+ if (pressed == m_shiftPressed)
+ return;
+
+ m_shiftPressed = pressed;
+
+ notifyModifierStateChanged ();
+}
+
+void kpTool::setControlPressed (bool pressed)
+{
+ if (pressed == m_controlPressed)
+ return;
+
+ m_controlPressed = pressed;
+
+ notifyModifierStateChanged ();
+}
+
+void kpTool::setAltPressed (bool pressed)
+{
+ if (pressed = m_altPressed)
+ return;
+
+ m_altPressed = pressed;
+
+ notifyModifierStateChanged ();
+}
+
+void kpTool::focusInEvent (QFocusEvent *)
+{
+}
+
+void kpTool::focusOutEvent (QFocusEvent *)
+{
+#if DEBUG_KP_TOOL && 0
+ kdDebug () << "kpTool::focusOutEvent() beganDraw=" << m_beganDraw << endl;
+#endif
+
+ if (m_beganDraw)
+ endDrawInternal (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ());
+}
+
+void kpTool::enterEvent (QEvent *)
+{
+#if DEBUG_KP_TOOL && 1
+ kdDebug () << "kpTool::enterEvent() beganDraw=" << m_beganDraw << endl;
+#endif
+}
+
+void kpTool::leaveEvent (QEvent *)
+{
+#if DEBUG_KP_TOOL && 1
+ kdDebug () << "kpTool::leaveEvent() beganDraw=" << m_beganDraw << endl;
+#endif
+
+ // if we haven't started drawing (e.g. dragging a rectangle)...
+ if (!m_beganDraw)
+ {
+ m_currentPoint = KP_INVALID_POINT;
+ m_currentViewPoint = KP_INVALID_POINT;
+ hover (m_currentPoint);
+ }
+}
+
+// static
+int kpTool::mouseButton (const Qt::ButtonState &buttonState)
+{
+ // we have nothing to do with mid-buttons
+ if (buttonState & Qt::MidButton)
+ return -1;
+
+ // both left & right together is quite meaningless...
+ Qt::ButtonState bothButtons = (Qt::ButtonState) (Qt::LeftButton | Qt::RightButton);
+ if ((buttonState & bothButtons) == bothButtons)
+ return -1;
+
+ if (buttonState & Qt::LeftButton)
+ return 0;
+ else if (buttonState & Qt::RightButton)
+ return 1;
+ else
+ return -1;
+}
+
+
+/*
+ * User Notifications
+ */
+
+
+// public static
+QString kpTool::cancelUserMessage (int mouseButton)
+{
+ if (mouseButton == 0)
+ return i18n ("Right click to cancel.");
+ else
+ return i18n ("Left click to cancel.");
+}
+
+// public
+QString kpTool::cancelUserMessage () const
+{
+ return cancelUserMessage (m_mouseButton);
+}
+
+
+// public
+QString kpTool::userMessage () const
+{
+ return m_userMessage;
+}
+
+// public
+void kpTool::setUserMessage (const QString &userMessage)
+{
+ m_userMessage = userMessage;
+
+ if (m_userMessage.isEmpty ())
+ m_userMessage = text ();
+ else
+ m_userMessage.prepend (i18n ("%1: ").arg (text ()));
+
+ emit userMessageChanged (m_userMessage);
+}
+
+
+// public
+QPoint kpTool::userShapeStartPoint () const
+{
+ return m_userShapeStartPoint;
+}
+
+// public
+QPoint kpTool::userShapeEndPoint () const
+{
+ return m_userShapeEndPoint;
+}
+
+// public static
+int kpTool::calculateLength (int start, int end)
+{
+ if (start <= end)
+ {
+ return end - start + 1;
+ }
+ else
+ {
+ return end - start - 1;
+ }
+}
+
+// public
+void kpTool::setUserShapePoints (const QPoint &startPoint,
+ const QPoint &endPoint,
+ bool setSize)
+{
+ m_userShapeStartPoint = startPoint;
+ m_userShapeEndPoint = endPoint;
+ emit userShapePointsChanged (m_userShapeStartPoint, m_userShapeEndPoint);
+
+ if (setSize)
+ {
+ if (startPoint != KP_INVALID_POINT &&
+ endPoint != KP_INVALID_POINT)
+ {
+ setUserShapeSize (calculateLength (startPoint.x (), endPoint.x ()),
+ calculateLength (startPoint.y (), endPoint.y ()));
+ }
+ else
+ {
+ setUserShapeSize ();
+ }
+ }
+}
+
+
+// public
+QSize kpTool::userShapeSize () const
+{
+ return m_userShapeSize;
+}
+
+// public
+int kpTool::userShapeWidth () const
+{
+ return m_userShapeSize.width ();
+}
+
+// public
+int kpTool::userShapeHeight () const
+{
+ return m_userShapeSize.height ();
+}
+
+// public
+void kpTool::setUserShapeSize (const QSize &size)
+{
+ m_userShapeSize = size;
+
+ emit userShapeSizeChanged (m_userShapeSize);
+ emit userShapeSizeChanged (m_userShapeSize.width (),
+ m_userShapeSize.height ());
+}
+
+// public
+void kpTool::setUserShapeSize (int width, int height)
+{
+ setUserShapeSize (QSize (width, height));
+}
+
+
+// public static
+bool kpTool::warnIfBigImageSize (int oldWidth, int oldHeight,
+ int newWidth, int newHeight,
+ const QString &text,
+ const QString &caption,
+ const QString &continueButtonText,
+ QWidget *parent)
+{
+#if DEBUG_KP_TOOL
+ kdDebug () << "kpTool::warnIfBigImageSize()"
+ << " old: w=" << oldWidth << " h=" << oldWidth
+ << " new: w=" << newWidth << " h=" << newHeight
+ << " pixmapSize="
+ << kpPixmapFX::pixmapSize (newWidth,
+ newHeight,
+ QPixmap::defaultDepth ())
+ << " vs BigImageSize=" << KP_BIG_IMAGE_SIZE
+ << endl;
+#endif
+
+ // Only got smaller or unchanged - don't complain
+ if (!(newWidth > oldWidth || newHeight > oldHeight))
+ {
+ return true;
+ }
+
+ // Was already large - user was warned before, don't annoy him/her again
+ if (kpPixmapFX::pixmapSize (oldWidth, oldHeight, QPixmap::defaultDepth ()) >=
+ KP_BIG_IMAGE_SIZE)
+ {
+ return true;
+ }
+
+ if (kpPixmapFX::pixmapSize (newWidth, newHeight, QPixmap::defaultDepth ()) >=
+ KP_BIG_IMAGE_SIZE)
+ {
+ int accept = KMessageBox::warningContinueCancel (parent,
+ text,
+ caption,
+ continueButtonText,
+ QString::fromLatin1 ("BigImageDontAskAgain"));
+
+ return (accept == KMessageBox::Continue);
+ }
+ else
+ {
+ return true;
+ }
+}
+
+
+#include <kptool.moc>
diff --git a/kolourpaint/kptool.h b/kolourpaint/kptool.h
new file mode 100644
index 00000000..ba7ee75e
--- /dev/null
+++ b/kolourpaint/kptool.h
@@ -0,0 +1,422 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef __kp_tool_h__
+#define __kp_tool_h__
+
+#include <qobject.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qsize.h>
+#include <qstring.h>
+
+#include <kpdefs.h>
+
+
+class QIconSet;
+class QPixmap;
+
+class KKeySequence;
+class KShortcut;
+
+class kpColor;
+class kpColorToolBar;
+class kpCommandHistory;
+class kpDocument;
+class kpView;
+class kpViewManager;
+class kpMainWindow;
+class kpToolAction;
+class kpToolToolBar;
+
+
+// Base class for all tools
+class kpTool : public QObject
+{
+Q_OBJECT
+
+public:
+ kpTool (const QString &text, const QString &description,
+ int key,
+ kpMainWindow *mainWindow, const char *name);
+ virtual ~kpTool ();
+
+private:
+ void init (const QString &text, const QString &description,
+ int key,
+ kpMainWindow *mainWindow, const char *name);
+
+
+protected:
+ void createAction ();
+
+ int m_key;
+ kpToolAction *m_action;
+
+signals:
+ void actionToolTipChanged (const QString &string);
+
+protected slots:
+ void slotActionToolTipChanged (const QString &string);
+
+public:
+ QString text () const;
+ void setText (const QString &text);
+
+ static QString toolTipForTextAndShortcut (const QString &text,
+ const KShortcut &shortcut);
+ QString toolTip () const;
+
+ QString description () const;
+ void setDescription (const QString &description);
+
+ int key () const;
+ void setKey (int key);
+
+ // Given a single <key>, returns a shortcut with <key>
+ // (disabled when the user is editing text) and as an alternate,
+ // <some modifiers>+<key>.
+ static KShortcut shortcutForKey (int key);
+ KShortcut shortcut () const;
+
+ static bool keyIsText (int key);
+ static bool containsSingleKeyTrigger (const KKeySequence &seq);
+ static bool containsSingleKeyTrigger (const KShortcut &shortcut,
+ KShortcut *shortcutWithoutSingleKeyTriggers);
+
+ bool singleKeyTriggersEnabled () const;
+ void enableSingleKeyTriggers (bool enable = true);
+
+ const char *name () const;
+
+
+ static QRect neededRect (const QRect &rect, int lineWidth);
+ static QPixmap neededPixmap (const QPixmap &pixmap, const QRect &boundingRect);
+
+ bool hasCurrentPoint () const;
+ // Returns the position of the cursor relative to the topleft point of
+ // the current view (viewUnderStartPoint() or viewUnderCursor() otherwise).
+ //
+ // If neither viewUnderStartPoint() nor viewUnderCursor()
+ // (i.e. !hasCurrentPoint()), then it returns KP_INVALID_POINT.
+ //
+ // If <zoomToDoc> is set (default), then it returns the position in the
+ // document. This theoretically == m_currentPoint (when m_currentPoint
+ // is defined) but I wouldn't bet on it. This function is useful when
+ // m_currentPoint isn't necessarily defined (outside of beginDraw(),draw()
+ // and hover()).
+ //
+ // If <zoomToDoc> is not set, then it returns an unzoomed view coordinate.
+ //
+ // Keep in mind that if viewUnderStartPoint(), this can return coordinates
+ // outside of the document/view.
+ QPoint currentPoint (bool zoomToDoc = true) const;
+
+public slots:
+ // Call this when something below the mouse cursor may have changed
+ // and/or if the view has moved relative to the cursor (as opposed to
+ // the cursor moving relative to the view, which would trigger a
+ // mouseMoveEvent and all would be well without such hacks)
+ // e.g. when zooming or scrolling the view or when deleting a selection.
+ //
+ // This calls hover() or draw() to let the tool know. The Brush Tool
+ // can then update the position of the Brush Cursor. The Selection
+ // Tool can update the real cursor. The Line Tool can update the current
+ // line. The statubar gets correct coordinates. etc. etc.
+ void somethingBelowTheCursorChanged ();
+
+private:
+ // Same as above except that you claim you know better than currentPoint()
+ void somethingBelowTheCursorChanged (const QPoint &currentPoint_,
+ const QPoint &currentViewPoint_);
+
+public:
+ // called when the tool is selected
+ virtual void begin ();
+
+ // called when the tool is deselected
+ virtual void end ();
+
+ // set after begin() has been called, unset after end() has been called
+ bool hasBegun () const { return m_began; }
+
+ bool hasBegunDraw () const { return m_beganDraw; }
+
+ virtual bool hasBegunShape () const { return hasBegunDraw (); }
+
+ // called when user double-left-clicks on Tool Icon (not view)
+ virtual void globalDraw ();
+
+ // called when the user clicks on the Tool Icon even though it's already
+ // the current tool (used by the selection tools to deselect)
+ virtual void reselect ();
+
+signals:
+ // emitted after beginDraw() has been called
+ void beganDraw (const QPoint &point);
+
+ // Emitted just before draw() is called in mouseMoveEvent(). Slots
+ // connected to this signal should return in <scrolled> whether the
+ // mouse pos may have changed. Used by drag scrolling.
+ void movedAndAboutToDraw (const QPoint &currentPoint, const QPoint &lastPoint,
+ int zoomLevel,
+ bool *scrolled);
+
+ // emitted after endDraw() has been called
+ void endedDraw (const QPoint &point);
+
+ // emitted after cancelShape() has been called
+ void cancelledShape (const QPoint &point);
+
+
+public:
+ QIconSet iconSet (int forceSize = 0) const;
+ QString iconName () const;
+ kpToolAction *action ();
+
+signals:
+ // User clicked on the tool's action - i.e. select this tool
+ void actionActivated ();
+
+protected slots:
+ void slotActionActivated ();
+
+
+protected:
+ virtual bool returnToPreviousToolAfterEndDraw () const { return false; }
+ virtual bool careAboutModifierState () const { return false; }
+ virtual bool careAboutColorsSwapped () const { return false; }
+
+ virtual void beginDraw ();
+
+ // mouse move without button pressed
+ // (only m_currentPoint & m_currentViewPoint is defined)
+ virtual void hover (const QPoint &point);
+
+ // this is useful for "instant" tools like the Pen & Eraser
+ virtual void draw (const QPoint &thisPoint, const QPoint &lastPoint,
+ const QRect &normalizedRect);
+
+ // (m_mouseButton will not change from beginDraw())
+ virtual void cancelShape ();
+ virtual void releasedAllButtons ();
+
+ virtual void endDraw (const QPoint &thisPoint, const QRect &normalizedRect);
+
+ virtual void endShape (const QPoint &thisPoint = QPoint (),
+ const QRect &normalizedRect = QRect ())
+ {
+ endDraw (thisPoint, normalizedRect);
+ }
+
+ kpMainWindow *mainWindow () const;
+ kpDocument *document () const;
+ kpViewManager *viewManager () const;
+ kpToolToolBar *toolToolBar () const;
+ kpView *viewUnderStartPoint () const { return m_viewUnderStartPoint; }
+ kpView *viewUnderCursor () const;
+ kpCommandHistory *commandHistory () const;
+
+ kpColor color (int which) const;
+
+ kpColor foregroundColor () const;
+ kpColor backgroundColor () const;
+
+ double colorSimilarity () const;
+ int processedColorSimilarity () const;
+
+protected:
+ int m_ignoreColorSignals;
+
+protected slots:
+ void slotColorsSwappedInternal (const kpColor &newForegroundColor,
+ const kpColor &newBackgroundColor);
+ void slotForegroundColorChangedInternal (const kpColor &color);
+ void slotBackgroundColorChangedInternal (const kpColor &color);
+ void slotColorSimilarityChangedInternal (double similarity, int processedSimilarity);
+
+protected slots: // TODO: there is no reason why these should be slots
+ virtual void slotColorsSwapped (const kpColor & /*newForegroundColor*/, const kpColor & /*newBackgroundColor*/) {}
+ virtual void slotForegroundColorChanged (const kpColor & /*color*/) {}
+ virtual void slotBackgroundColorChanged (const kpColor & /*color*/) {}
+ virtual void slotColorSimilarityChanged (double /*similarity*/, int /*processedSimilarity*/) {};
+
+protected:
+ // (only valid in slots connected to the respective signals above)
+ kpColor oldForegroundColor () const;
+ kpColor oldBackgroundColor () const;
+ double oldColorSimilarity () const;
+
+protected:
+ // returns true if m_currentPoint <= 1 pixel away from m_lastPoint
+ // or if there was no lastPoint
+ bool currentPointNextToLast () const; // (includes diagonal adjacency)
+ bool currentPointCardinallyNextToLast () const; // (only cardinally adjacent i.e. horiz & vert; no diag)
+
+ int m_mouseButton; /* 0 = left, 1 = right */
+ bool m_shiftPressed, m_controlPressed, m_altPressed; // m_altPressed is unreliable
+ bool m_beganDraw; // set after beginDraw() is called, unset before endDraw() is called
+ QPoint m_startPoint,
+ m_currentPoint, m_currentViewPoint,
+ m_lastPoint;
+
+protected:
+ friend class kpCommandHistory;
+ friend class kpMainWindow;
+ friend class kpToolToolBar;
+ void beginInternal ();
+ void endInternal ();
+
+ void beginDrawInternal ();
+ void endDrawInternal (const QPoint &thisPoint, const QRect &normalizedRect,
+ bool wantEndShape = false);
+ void cancelShapeInternal ();
+ void endShapeInternal (const QPoint &thisPoint = QPoint (),
+ const QRect &normalizedRect = QRect ());
+
+ friend class kpView;
+
+ // If you're reimplementing any of these, you probably don't know what
+ // you're doing - reimplement begin(),beginDraw(),draw(),cancelShape(),
+ // endDraw() etc. instead.
+ virtual void mousePressEvent (QMouseEvent *e);
+ virtual void mouseMoveEvent (QMouseEvent *e);
+ virtual void mouseReleaseEvent (QMouseEvent *e);
+ virtual void wheelEvent (QWheelEvent *e);
+
+ virtual void keyPressEvent (QKeyEvent *e);
+ virtual void keyReleaseEvent (QKeyEvent *e);
+
+ virtual void imStartEvent(QIMEvent *){}
+ virtual void imComposeEvent(QIMEvent *){}
+ virtual void imEndEvent(QIMEvent *){}
+
+private:
+ void keyUpdateModifierState (QKeyEvent *e);
+ void notifyModifierStateChanged ();
+protected:
+ virtual void setShiftPressed (bool pressed);
+ virtual void setControlPressed (bool pressed);
+ virtual void setAltPressed (bool pressed);
+ virtual void focusInEvent (QFocusEvent *e);
+ virtual void focusOutEvent (QFocusEvent *e);
+ virtual void enterEvent (QEvent *e);
+ virtual void leaveEvent (QEvent *e);
+
+ // 0 = left, 1 = right, -1 = other (none, left+right, mid)
+ static int mouseButton (const Qt::ButtonState &buttonState);
+
+ QString m_text, m_description;
+ const char *m_name;
+
+ kpMainWindow *m_mainWindow;
+ bool m_began;
+
+ kpView *m_viewUnderStartPoint;
+
+
+ /*
+ * User Notifications (Status Bar)
+ */
+
+public:
+ // Returns "(Left|Right) click to cancel." where Left or Right is chosen
+ // depending on which one is the _opposite_ of <mouseButton>
+ static QString cancelUserMessage (int mouseButton);
+ QString cancelUserMessage () const;
+
+ QString userMessage () const;
+ void setUserMessage (const QString &userMessage = QString::null);
+
+ QPoint userShapeStartPoint () const;
+ QPoint userShapeEndPoint () const;
+ static int calculateLength (int start, int end);
+ void setUserShapePoints (const QPoint &startPoint = KP_INVALID_POINT,
+ const QPoint &endPoint = KP_INVALID_POINT,
+ bool setSize = true);
+
+ QSize userShapeSize () const;
+ int userShapeWidth () const;
+ int userShapeHeight () const;
+ void setUserShapeSize (const QSize &size = KP_INVALID_SIZE);
+ void setUserShapeSize (int width, int height);
+
+signals:
+ void userMessageChanged (const QString &userMessage);
+ void userShapePointsChanged (const QPoint &startPoint = KP_INVALID_POINT,
+ const QPoint &endPoint = KP_INVALID_POINT);
+ void userShapeSizeChanged (const QSize &size);
+ void userShapeSizeChanged (int width, int height);
+
+protected:
+ QString m_userMessage;
+ QPoint m_userShapeStartPoint, m_userShapeEndPoint;
+ QSize m_userShapeSize;
+
+
+public:
+ // Call this before the user tries to cause the document or selection
+ // to resize from <oldWidth>x<oldHeight> to <newWidth>x<newHeight>.
+ // If at least one dimension increases, the new dimensions will take a
+ // large amount of memory (which causes thrashing, instability) and
+ // the old dimensions did not take a large amount of memory, ask the
+ // user if s/he really wants to perform the operation.
+ //
+ // Returns true if the operation should proceed, false otherwise.
+ //
+ // In order to make the translators' lives possible, this function cannot
+ // generate the <text>,<caption> nor <continueButtonText> (without
+ // concantenating sentences and words with tense). However, it is
+ // recommended that you give them the following values:
+ //
+ // e.g.:
+ // text = i18n ("<qt><p>(Rotating|Skewing) the (image|selection) to"
+ // " %1x%2 may take a substantial amount of memory."
+ // " This can reduce system"
+ // " responsiveness and cause other application resource"
+ // " problems.</p>").arg (newWidth, newHeight)
+ //
+ // "<p>Are you sure want to (rotate|skew) the"
+ // " (image|selection)?</p></qt>");
+ // caption = i18n ("Rotate (Image|Selection)?");
+ // continueButtonText = i18n ("Rotat&e (Image|Selection)");
+ static bool warnIfBigImageSize (int oldWidth, int oldHeight,
+ int newWidth, int newHeight,
+ const QString &text,
+ const QString &caption,
+ const QString &continueButtonText,
+ QWidget *parent);
+
+
+protected:
+ // There is no need to maintain binary compatibility at this stage.
+ // The d-pointer is just so that you can experiment without recompiling
+ // the kitchen sink.
+ class kpToolPrivate *d;
+};
+
+#endif // __kp_tool_h__
diff --git a/kolourpaint/kpview.cpp b/kolourpaint/kpview.cpp
new file mode 100644
index 00000000..1f18c659
--- /dev/null
+++ b/kolourpaint/kpview.cpp
@@ -0,0 +1,1910 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define DEBUG_KP_VIEW 0
+#define DEBUG_KP_VIEW_RENDERER ((DEBUG_KP_VIEW && 0) || 0)
+
+
+#include <kpview.h>
+
+#include <math.h>
+#include <stdlib.h>
+
+#include <qbitmap.h>
+#include <qcursor.h>
+#include <qdragobject.h>
+#include <qguardedptr.h>
+#include <qimage.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qregion.h>
+#include <qmemarray.h>
+
+#if DEBUG_KP_VIEW || DEBUG_KP_VIEW_RENDERER
+ #include <qdatetime.h>
+#endif
+
+#include <kdebug.h>
+
+#include <kpdefs.h>
+#include <kpdocument.h>
+#include <kppixmapfx.h>
+#include <kpselection.h>
+#include <kptemppixmap.h>
+#include <kptool.h>
+#include <kptooltoolbar.h>
+#include <kpviewmanager.h>
+#include <kpviewscrollablecontainer.h>
+
+
+struct kpViewPrivate
+{
+ // sync: kpView::paintEvent()
+ //
+ // Normally, these pointers must be valid while the kpView is alive.
+ // Generally, the objects they point to are deleted only after kpView
+ // is deleted.
+ //
+ // However, sometimes we use deleteLater() for the kpView.
+ // Before the delayed deletion is executed, those objects are deleted
+ // and then our paintEvent() is called. paintEvent() must therefore
+ // have some way of realising that those objects have been deleted so
+ // we use guardded pointers.
+ //
+ // For more details, see SVN commit:
+ // "r385274 | dang | 2005-02-02 22:08:27 +1100 (Wed, 02 Feb 2005) | 21 lines".
+ QGuardedPtr <kpDocument> m_document;
+ QGuardedPtr <kpToolToolBar> m_toolToolBar;
+ QGuardedPtr <kpViewManager> m_viewManager;
+ QGuardedPtr <kpView> m_buddyView;
+ QGuardedPtr <kpViewScrollableContainer> m_scrollableContainer;
+
+ int m_hzoom, m_vzoom;
+ QPoint m_origin;
+ bool m_showGrid;
+ bool m_isBuddyViewScrollableContainerRectangleShown;
+ QRect m_buddyViewScrollableContainerRectangle;
+
+ QRegion m_queuedUpdateArea;
+ QPixmap *m_backBuffer;
+};
+
+
+kpView::kpView (kpDocument *document,
+ kpToolToolBar *toolToolBar,
+ kpViewManager *viewManager,
+ kpView *buddyView,
+ kpViewScrollableContainer *scrollableContainer,
+ QWidget *parent, const char *name)
+
+ : QWidget (parent, name, Qt::WNoAutoErase/*no flicker*/),
+ d (new kpViewPrivate ())
+{
+ d->m_document = document;
+ d->m_toolToolBar = toolToolBar;
+ d->m_viewManager = viewManager;
+ d->m_buddyView = buddyView;
+ d->m_scrollableContainer = scrollableContainer;
+
+ d->m_hzoom = 100, d->m_vzoom = 100;
+ d->m_origin = QPoint (0, 0);
+ d->m_showGrid = false;
+ d->m_isBuddyViewScrollableContainerRectangleShown = false;
+
+ d->m_backBuffer = 0;
+
+
+ setBackgroundMode (Qt::NoBackground); // no flicker
+ setFocusPolicy (QWidget::WheelFocus);
+ setMouseTracking (true); // mouseMoveEvent's even when no mousebtn down
+ setKeyCompression (true);
+ setInputMethodEnabled (true); // ensure using InputMethod
+}
+
+kpView::~kpView ()
+{
+ setHasMouse (false);
+
+ delete d->m_backBuffer;
+ delete d;
+}
+
+
+// public
+kpDocument *kpView::document () const
+{
+ return d->m_document;
+}
+
+// protected
+kpSelection *kpView::selection () const
+{
+ return document () ? document ()->selection () : 0;
+}
+
+// public
+kpToolToolBar *kpView::toolToolBar () const
+{
+ return d->m_toolToolBar;
+}
+
+// protected
+kpTool *kpView::tool () const
+{
+ return toolToolBar () ? toolToolBar ()->tool () : 0;
+}
+
+// public
+kpViewManager *kpView::viewManager () const
+{
+ return d->m_viewManager;
+}
+
+// public
+kpView *kpView::buddyView () const
+{
+ return d->m_buddyView;
+}
+
+// public
+kpViewScrollableContainer *kpView::buddyViewScrollableContainer () const
+{
+ return (buddyView () ? buddyView ()->scrollableContainer () : 0);
+}
+
+// public
+kpViewScrollableContainer *kpView::scrollableContainer () const
+{
+ return d->m_scrollableContainer;
+}
+
+
+// public
+int kpView::zoomLevelX (void) const
+{
+ return d->m_hzoom;
+}
+
+// public
+int kpView::zoomLevelY (void) const
+{
+ return d->m_vzoom;
+}
+
+// public virtual
+void kpView::setZoomLevel (int hzoom, int vzoom)
+{
+ if (hzoom == d->m_hzoom && vzoom == d->m_vzoom)
+ return;
+
+ if (hzoom <= 0 || vzoom <= 0)
+ return;
+
+ d->m_hzoom = hzoom;
+ d->m_vzoom = vzoom;
+
+ if (viewManager ())
+ viewManager ()->updateView (this);
+
+ emit zoomLevelChanged (hzoom, vzoom);
+}
+
+
+// public
+QPoint kpView::origin () const
+{
+ return d->m_origin;
+}
+
+// public virtual
+void kpView::setOrigin (const QPoint &origin)
+{
+#if DEBUG_KP_VIEW
+ kdDebug () << "kpView(" << name () << ")::setOrigin" << origin << endl;
+#endif
+
+ if (origin == d->m_origin)
+ {
+ #if DEBUG_KP_VIEW
+ kdDebug () << "\tNOP" << endl;
+ #endif
+ return;
+ }
+
+ d->m_origin = origin;
+
+ if (viewManager ())
+ viewManager ()->updateView (this);
+
+ emit originChanged (origin);
+}
+
+
+// public
+bool kpView::canShowGrid () const
+{
+ // (minimum zoom level < 400% would probably be reported as a bug by
+ // users who thought that the grid was a part of the image!)
+ return ((zoomLevelX () >= 400 && zoomLevelX () % 100 == 0) &&
+ (zoomLevelY () >= 400 && zoomLevelY () % 100 == 0));
+}
+
+// public
+bool kpView::isGridShown () const
+{
+ return d->m_showGrid;
+}
+
+// public
+void kpView::showGrid (bool yes)
+{
+ if (d->m_showGrid == yes)
+ return;
+
+ if (yes && !canShowGrid ())
+ return;
+
+ d->m_showGrid = yes;
+
+ if (viewManager ())
+ viewManager ()->updateView (this);
+}
+
+
+// public
+bool kpView::isBuddyViewScrollableContainerRectangleShown () const
+{
+ return d->m_isBuddyViewScrollableContainerRectangleShown;
+}
+
+// public
+void kpView::showBuddyViewScrollableContainerRectangle (bool yes)
+{
+ if (yes == d->m_isBuddyViewScrollableContainerRectangleShown)
+ return;
+
+ d->m_isBuddyViewScrollableContainerRectangleShown = yes;
+
+ if (d->m_isBuddyViewScrollableContainerRectangleShown)
+ {
+ // Got these connect statements by analysing deps of
+ // updateBuddyViewScrollableContainerRectangle() rect update code.
+
+ connect (this, SIGNAL (zoomLevelChanged (int, int)),
+ this, SLOT (updateBuddyViewScrollableContainerRectangle ()));
+ connect (this, SIGNAL (originChanged (const QPoint &)),
+ this, SLOT (updateBuddyViewScrollableContainerRectangle ()));
+
+ if (buddyViewScrollableContainer ())
+ {
+ connect (buddyViewScrollableContainer (), SIGNAL (contentsMovingSoon (int, int)),
+ this, SLOT (updateBuddyViewScrollableContainerRectangle ()));
+ connect (buddyViewScrollableContainer (), SIGNAL (resized ()),
+ this, SLOT (updateBuddyViewScrollableContainerRectangle ()));
+ }
+
+ if (buddyView ())
+ {
+ connect (buddyView (), SIGNAL (zoomLevelChanged (int, int)),
+ this, SLOT (updateBuddyViewScrollableContainerRectangle ()));
+ connect (buddyView (), SIGNAL (originChanged (const QPoint &)),
+ this, SLOT (updateBuddyViewScrollableContainerRectangle ()));
+
+ connect (buddyView (), SIGNAL (sizeChanged (int, int)),
+ this, SLOT (updateBuddyViewScrollableContainerRectangle ()));
+ }
+
+ }
+ else
+ {
+ disconnect (this, SIGNAL (zoomLevelChanged (int, int)),
+ this, SLOT (updateBuddyViewScrollableContainerRectangle ()));
+ disconnect (this, SIGNAL (originChanged (const QPoint &)),
+ this, SLOT (updateBuddyViewScrollableContainerRectangle ()));
+
+ if (buddyViewScrollableContainer ())
+ {
+ disconnect (buddyViewScrollableContainer (), SIGNAL (contentsMovingSoon (int, int)),
+ this, SLOT (updateBuddyViewScrollableContainerRectangle ()));
+ disconnect (buddyViewScrollableContainer (), SIGNAL (resized ()),
+ this, SLOT (updateBuddyViewScrollableContainerRectangle ()));
+ }
+
+ if (buddyView ())
+ {
+ disconnect (buddyView (), SIGNAL (zoomLevelChanged (int, int)),
+ this, SLOT (updateBuddyViewScrollableContainerRectangle ()));
+ disconnect (buddyView (), SIGNAL (originChanged (const QPoint &)),
+ this, SLOT (updateBuddyViewScrollableContainerRectangle ()));
+
+ disconnect (buddyView (), SIGNAL (sizeChanged (int, int)),
+ this, SLOT (updateBuddyViewScrollableContainerRectangle ()));
+ }
+
+ }
+
+ updateBuddyViewScrollableContainerRectangle ();
+}
+
+
+// protected
+QRect kpView::buddyViewScrollableContainerRectangle () const
+{
+ return d->m_buddyViewScrollableContainerRectangle;
+}
+
+// protected slot
+void kpView::updateBuddyViewScrollableContainerRectangle ()
+{
+ if (viewManager ())
+ viewManager ()->setQueueUpdates ();
+
+ {
+ if (d->m_buddyViewScrollableContainerRectangle.isValid ())
+ {
+ if (viewManager ())
+ {
+ // Erase last
+ viewManager ()->updateViewRectangleEdges (this,
+ d->m_buddyViewScrollableContainerRectangle);
+ }
+ }
+
+
+ QRect newRect;
+ if (isBuddyViewScrollableContainerRectangleShown () &&
+ buddyViewScrollableContainer () && buddyView ())
+ {
+ QRect docRect = buddyView ()->transformViewToDoc (
+ QRect (buddyViewScrollableContainer ()->contentsXSoon (),
+ buddyViewScrollableContainer ()->contentsYSoon (),
+ QMIN (buddyView ()->width (),
+ buddyViewScrollableContainer ()->visibleWidth ()),
+ QMIN (buddyView ()->height (),
+ buddyViewScrollableContainer ()->visibleHeight ())));
+
+
+ QRect viewRect = this->transformDocToView (docRect);
+
+
+ // (Surround the area of interest by moving outwards by 1 pixel in each
+ // direction - don't overlap area)
+ newRect = QRect (viewRect.x () - 1,
+ viewRect.y () - 1,
+ viewRect.width () + 2,
+ viewRect.height () + 2);
+ }
+ else
+ {
+ newRect = QRect ();
+ }
+
+ if (newRect != d->m_buddyViewScrollableContainerRectangle)
+ {
+ // (must set before updateView() for paintEvent() to see new
+ // rect)
+ d->m_buddyViewScrollableContainerRectangle = newRect;
+
+ if (newRect.isValid ())
+ {
+ if (viewManager ())
+ {
+ viewManager ()->updateViewRectangleEdges (this,
+ d->m_buddyViewScrollableContainerRectangle);
+ }
+ }
+ }
+ }
+
+ if (viewManager ())
+ viewManager ()->restoreQueueUpdates ();
+}
+
+
+// public
+double kpView::transformViewToDocX (double viewX) const
+{
+ return (viewX - origin ().x ()) * 100.0 / zoomLevelX ();
+}
+
+// public
+double kpView::transformViewToDocY (double viewY) const
+{
+ return (viewY - origin ().y ()) * 100.0 / zoomLevelY ();
+}
+
+// public
+QPoint kpView::transformViewToDoc (const QPoint &viewPoint) const
+{
+ return QPoint ((int) transformViewToDocX (viewPoint.x ()),
+ (int) transformViewToDocY (viewPoint.y ()));
+}
+
+// public
+QRect kpView::transformViewToDoc (const QRect &viewRect) const
+{
+ if (zoomLevelX () == 100 && zoomLevelY () == 100)
+ {
+ return QRect (viewRect.x () - origin ().x (),
+ viewRect.y () - origin ().y (),
+ viewRect.width (),
+ viewRect.height ());
+ }
+ else
+ {
+ const QPoint docTopLeft = transformViewToDoc (viewRect.topLeft ());
+
+ // (don't call transformViewToDoc[XY]() - need to round up dimensions)
+ const int docWidth = qRound (double (viewRect.width ()) * 100.0 / double (zoomLevelX ()));
+ const int docHeight = qRound (double (viewRect.height ()) * 100.0 / double (zoomLevelY ()));
+
+ // (like QWMatrix::Areas)
+ return QRect (docTopLeft.x (), docTopLeft.y (), docWidth, docHeight);
+ }
+}
+
+
+// public
+double kpView::transformDocToViewX (double docX) const
+{
+ return (docX * zoomLevelX () / 100.0) + origin ().x ();
+}
+
+// public
+double kpView::transformDocToViewY (double docY) const
+{
+ return (docY * zoomLevelY () / 100.0) + origin ().y ();
+}
+
+// public
+QPoint kpView::transformDocToView (const QPoint &docPoint) const
+{
+ return QPoint ((int) transformDocToViewX (docPoint.x ()),
+ (int) transformDocToViewY (docPoint.y ()));
+}
+
+// public
+QRect kpView::transformDocToView (const QRect &docRect) const
+{
+ if (zoomLevelX () == 100 && zoomLevelY () == 100)
+ {
+ return QRect (docRect.x () + origin ().x (),
+ docRect.y () + origin ().y (),
+ docRect.width (),
+ docRect.height ());
+ }
+ else
+ {
+ const QPoint viewTopLeft = transformDocToView (docRect.topLeft ());
+
+ // (don't call transformDocToView[XY]() - need to round up dimensions)
+ const int viewWidth = qRound (double (docRect.width ()) * double (zoomLevelX ()) / 100.0);
+ const int viewHeight = qRound (double (docRect.height ()) * double (zoomLevelY ()) / 100.0);
+
+ // (like QWMatrix::Areas)
+ return QRect (viewTopLeft.x (), viewTopLeft.y (), viewWidth, viewHeight);
+ }
+}
+
+
+// public
+QPoint kpView::transformViewToOtherView (const QPoint &viewPoint,
+ const kpView *otherView)
+{
+ if (this == otherView)
+ return viewPoint;
+
+ const double docX = transformViewToDocX (viewPoint.x ());
+ const double docY = transformViewToDocY (viewPoint.y ());
+
+ const double otherViewX = otherView->transformDocToViewX (docX);
+ const double otherViewY = otherView->transformDocToViewY (docY);
+
+ return QPoint ((int) otherViewX, (int) otherViewY);
+}
+
+
+// public
+int kpView::zoomedDocWidth () const
+{
+ return document () ? document ()->width () * zoomLevelX () / 100 : 0;
+}
+
+// public
+int kpView::zoomedDocHeight () const
+{
+ return document () ? document ()->height () * zoomLevelY () / 100 : 0;
+}
+
+
+// public
+void kpView::setHasMouse (bool yes)
+{
+ kpViewManager *vm = viewManager ();
+ if (!vm)
+ return;
+
+#if DEBUG_KP_VIEW && 0
+ kdDebug () << "kpView(" << name ()
+ << ")::setHasMouse(" << yes
+ << ") existing viewUnderCursor="
+ << (vm->viewUnderCursor () ? vm->viewUnderCursor ()->name () : "(none)")
+ << endl;
+#endif
+ if (yes && vm->viewUnderCursor () != this)
+ vm->setViewUnderCursor (this);
+ else if (!yes && vm->viewUnderCursor () == this)
+ vm->setViewUnderCursor (0);
+}
+
+
+// public
+void kpView::addToQueuedArea (const QRegion &region)
+{
+#if DEBUG_KP_VIEW && 0
+ kdDebug () << "kpView(" << name ()
+ << ")::addToQueuedArea() already=" << d->m_queuedUpdateArea
+ << " - plus - " << region
+ << endl;
+#endif
+ d->m_queuedUpdateArea += region;
+}
+
+// public
+void kpView::addToQueuedArea (const QRect &rect)
+{
+#if DEBUG_KP_VIEW && 0
+ kdDebug () << "kpView(" << name ()
+ << ")::addToQueuedArea() already=" << d->m_queuedUpdateArea
+ << " - plus - " << rect
+ << endl;
+#endif
+ d->m_queuedUpdateArea += rect;
+}
+
+// public
+void kpView::invalidateQueuedArea ()
+{
+#if DEBUG_KP_VIEW && 0
+ kdDebug () << "kpView::invalidateQueuedArea()" << endl;
+#endif
+
+ d->m_queuedUpdateArea = QRegion ();
+}
+
+// public
+void kpView::updateQueuedArea ()
+{
+ kpViewManager *vm = viewManager ();
+#if DEBUG_KP_VIEW && 0
+ kdDebug () << "kpView(" << name ()
+ << ")::updateQueuedArea() vm=" << (bool) vm
+ << " queueUpdates=" << (vm && vm->queueUpdates ())
+ << " fastUpdates=" << (vm && vm->fastUpdates ())
+ << " area=" << d->m_queuedUpdateArea
+ << endl;
+#endif
+
+ if (!vm)
+ return;
+
+ if (vm->queueUpdates ())
+ return;
+
+ if (!d->m_queuedUpdateArea.isNull ())
+ vm->updateView (this, d->m_queuedUpdateArea);
+
+ invalidateQueuedArea ();
+}
+
+// public
+void kpView::updateMicroFocusHint (const QRect &microFocusHint)
+{
+ int x = microFocusHint.topLeft().x();
+ int y = microFocusHint.topLeft().y();
+ int width = microFocusHint.width();
+ int height = microFocusHint.height();
+ setMicroFocusHint (x, y, width, height);
+}
+
+// public
+QRect kpView::selectionViewRect () const
+{
+ return selection () ?
+ transformDocToView (selection ()->boundingRect ()) :
+ QRect ();
+
+}
+
+// public
+QPoint kpView::mouseViewPoint (const QPoint &returnViewPoint) const
+{
+ if (returnViewPoint != KP_INVALID_POINT)
+ return returnViewPoint;
+ else
+ return mapFromGlobal (QCursor::pos ());
+}
+
+// public
+QPoint kpView::mouseViewPointRelativeToSelection (const QPoint &viewPoint) const
+{
+ if (!selection ())
+ return KP_INVALID_POINT;
+
+ return mouseViewPoint (viewPoint) - transformDocToView (selection ()->topLeft ());
+}
+
+// public
+bool kpView::mouseOnSelection (const QPoint &viewPoint) const
+{
+ const QRect selViewRect = selectionViewRect ();
+ if (!selViewRect.isValid ())
+ return false;
+
+ return selViewRect.contains (mouseViewPoint (viewPoint));
+}
+
+
+// public
+int kpView::textSelectionMoveBorderAtomicSize () const
+{
+ if (!selection () || !selection ()->isText ())
+ return 0;
+
+ return QMAX (4, zoomLevelX () / 100);
+}
+
+// public
+bool kpView::mouseOnSelectionToMove (const QPoint &viewPoint) const
+{
+ if (!mouseOnSelection (viewPoint))
+ return false;
+
+ if (!selection ()->isText ())
+ return true;
+
+ if (mouseOnSelectionResizeHandle (viewPoint))
+ return false;
+
+
+ const QPoint viewPointRelSel = mouseViewPointRelativeToSelection (viewPoint);
+
+ // Middle point should always be selectable
+ const QPoint selCenterDocPoint = selection ()->boundingRect ().center ();
+ if (tool () &&
+ tool ()->currentPoint () == selCenterDocPoint)
+ {
+ return false;
+ }
+
+
+ const int atomicSize = textSelectionMoveBorderAtomicSize ();
+ const QRect selViewRect = selectionViewRect ();
+
+ return (viewPointRelSel.x () < atomicSize ||
+ viewPointRelSel.x () >= selViewRect.width () - atomicSize ||
+ viewPointRelSel.y () < atomicSize ||
+ viewPointRelSel.y () >= selViewRect.height () - atomicSize);
+}
+
+
+// protected
+bool kpView::selectionLargeEnoughToHaveResizeHandlesIfAtomicSize (int atomicSize) const
+{
+ if (!selection ())
+ return false;
+
+ const QRect selViewRect = selectionViewRect ();
+
+ return (selViewRect.width () >= atomicSize * 5 ||
+ selViewRect.height () >= atomicSize * 5);
+}
+
+// public
+int kpView::selectionResizeHandleAtomicSize () const
+{
+ int atomicSize = QMIN (7, QMAX (4, zoomLevelX () / 100));
+ while (atomicSize > 0 &&
+ !selectionLargeEnoughToHaveResizeHandlesIfAtomicSize (atomicSize))
+ {
+ atomicSize--;
+ }
+
+ return atomicSize;
+}
+
+// public
+bool kpView::selectionLargeEnoughToHaveResizeHandles () const
+{
+ return (selectionResizeHandleAtomicSize () > 0);
+}
+
+// public
+QRegion kpView::selectionResizeHandlesViewRegion (bool forRenderer) const
+{
+ QRegion ret;
+
+ const int atomicLength = selectionResizeHandleAtomicSize ();
+ if (atomicLength <= 0)
+ return QRegion ();
+
+
+ // HACK: At low zoom (e.g. 100%), resize handles will probably be too
+ // big and overlap text / cursor / too much of selection.
+ //
+ // So limit the _visual_ size of handles at low zoom. The
+ // handles' grab area remains the same for usability; so yes,
+ // there are a few pixels that don't look grabable but they are.
+ //
+ // The real solution is to be able to partially render the
+ // handles outside of the selection view rect. If not possible,
+ // at least for text boxes, render text on top of handles.
+ int normalAtomicLength = atomicLength;
+ int vertEdgeAtomicLength = atomicLength;
+ if (forRenderer && selection ())
+ {
+ if (zoomLevelX () <= 150)
+ {
+ if (normalAtomicLength > 1)
+ normalAtomicLength--;
+
+ if (vertEdgeAtomicLength > 1)
+ vertEdgeAtomicLength--;
+ }
+
+ // 1 line of text?
+ if (selection ()->isText () && selection ()->textLines ().size () == 1)
+ {
+ if (zoomLevelX () <= 150)
+ vertEdgeAtomicLength = QMIN (vertEdgeAtomicLength, QMAX (2, zoomLevelX () / 100));
+ else if (zoomLevelX () <= 250)
+ vertEdgeAtomicLength = QMIN (vertEdgeAtomicLength, QMAX (3, zoomLevelX () / 100));
+ }
+ }
+
+
+ const QRect selViewRect = selectionViewRect ();
+
+#define ADD_BOX_RELATIVE_TO_SELECTION(type,x,y) \
+ ret += QRect ((x), (y), type##AtomicLength, type##AtomicLength)
+
+ ADD_BOX_RELATIVE_TO_SELECTION (normal,
+ selViewRect.width () - normalAtomicLength,
+ selViewRect.height () - normalAtomicLength);
+ ADD_BOX_RELATIVE_TO_SELECTION (normal,
+ selViewRect.width () - normalAtomicLength,
+ 0);
+ ADD_BOX_RELATIVE_TO_SELECTION (normal,
+ 0,
+ selViewRect.height () - normalAtomicLength);
+ ADD_BOX_RELATIVE_TO_SELECTION (normal,
+ 0,
+ 0);
+
+ ADD_BOX_RELATIVE_TO_SELECTION (vertEdge,
+ selViewRect.width () - vertEdgeAtomicLength,
+ (selViewRect.height () - vertEdgeAtomicLength) / 2);
+ ADD_BOX_RELATIVE_TO_SELECTION (normal,
+ (selViewRect.width () - normalAtomicLength) / 2,
+ selViewRect.height () - normalAtomicLength);
+ ADD_BOX_RELATIVE_TO_SELECTION (normal,
+ (selViewRect.width () - normalAtomicLength) / 2,
+ 0);
+ ADD_BOX_RELATIVE_TO_SELECTION (vertEdge,
+ 0,
+ (selViewRect.height () - vertEdgeAtomicLength) / 2);
+
+#undef ADD_BOX_RELATIVE_TO_SELECTION
+
+ ret.translate (selViewRect.x (), selViewRect.y ());
+ ret = ret.intersect (selViewRect);
+
+ return ret;
+}
+
+// public
+int kpView::mouseOnSelectionResizeHandle (const QPoint &viewPoint) const
+{
+#if DEBUG_KP_VIEW
+ kdDebug () << "kpView::mouseOnSelectionResizeHandle(viewPoint="
+ << viewPoint << ")" << endl;
+#endif
+
+ if (!mouseOnSelection (viewPoint))
+ {
+ #if DEBUG_KP_VIEW
+ kdDebug () << "\tmouse not on sel" << endl;
+ #endif
+ return 0;
+ }
+
+
+ const QRect selViewRect = selectionViewRect ();
+#if DEBUG_KP_VIEW
+ kdDebug () << "\tselViewRect=" << selViewRect << endl;
+#endif
+
+
+ const int atomicLength = selectionResizeHandleAtomicSize ();
+#if DEBUG_KP_VIEW
+ kdDebug () << "\tatomicLength=" << atomicLength << endl;
+#endif
+
+ if (atomicLength <= 0)
+ {
+ #if DEBUG_KP_VIEW
+ kdDebug () << "\tsel not large enough to have resize handles" << endl;
+ #endif
+ // Want to make it possible to move a small selection
+ return 0;
+ }
+
+
+ const QPoint viewPointRelSel = mouseViewPointRelativeToSelection (viewPoint);
+#if DEBUG_KP_VIEW
+ kdDebug () << "\tviewPointRelSel=" << viewPointRelSel << endl;
+#endif
+
+
+#define LOCAL_POINT_IN_BOX_AT(x,y) \
+ QRect ((x), (y), atomicLength, atomicLength).contains (viewPointRelSel)
+
+ // Favour the bottom & right and the corners.
+ if (LOCAL_POINT_IN_BOX_AT (selViewRect.width () - atomicLength,
+ selViewRect.height () - atomicLength))
+ {
+ return Bottom | Right;
+ }
+ else if (LOCAL_POINT_IN_BOX_AT (selViewRect.width () - atomicLength, 0))
+ {
+ return Top | Right;
+ }
+ else if (LOCAL_POINT_IN_BOX_AT (0, selViewRect.height () - atomicLength))
+ {
+ return Bottom | Left;
+ }
+ else if (LOCAL_POINT_IN_BOX_AT (0, 0))
+ {
+ return Top | Left;
+ }
+ else if (LOCAL_POINT_IN_BOX_AT (selViewRect.width () - atomicLength,
+ (selViewRect.height () - atomicLength) / 2))
+ {
+ return Right;
+ }
+ else if (LOCAL_POINT_IN_BOX_AT ((selViewRect.width () - atomicLength) / 2,
+ selViewRect.height () - atomicLength))
+ {
+ return Bottom;
+ }
+ else if (LOCAL_POINT_IN_BOX_AT ((selViewRect.width () - atomicLength) / 2, 0))
+ {
+ return Top;
+ }
+ else if (LOCAL_POINT_IN_BOX_AT (0, (selViewRect.height () - atomicLength) / 2))
+ {
+ return Left;
+ }
+ else
+ {
+ #if DEBUG_KP_VIEW
+ kdDebug () << "\tnot on sel resize handle" << endl;
+ #endif
+ return 0;
+ }
+#undef LOCAL_POINT_IN_BOX_AT
+}
+
+// public
+bool kpView::mouseOnSelectionToSelectText (const QPoint &viewPoint) const
+{
+#if DEBUG_KP_VIEW
+ kdDebug () << "kpView::mouseOnSelectionToSelectText(viewPoint="
+ << viewPoint << ")" << endl;
+#endif
+
+ if (!mouseOnSelection (viewPoint))
+ {
+ #if DEBUG_KP_VIEW
+ kdDebug () << "\tmouse non on sel" << endl;
+ #endif
+ return false;
+ }
+
+ if (!selection ()->isText ())
+ {
+ #if DEBUG_KP_VIEW
+ kdDebug () << "\tsel not text" << endl;
+ #endif
+ return false;
+ }
+
+#if DEBUG_KP_VIEW
+ kdDebug () << "\tmouse on sel: to move=" << mouseOnSelectionToMove ()
+ << " to resize=" << mouseOnSelectionResizeHandle ()
+ << endl;
+#endif
+
+ return (!mouseOnSelectionToMove (viewPoint) &&
+ !mouseOnSelectionResizeHandle (viewPoint));
+}
+
+
+// protected virtual [base QWidget]
+void kpView::mouseMoveEvent (QMouseEvent *e)
+{
+#if DEBUG_KP_VIEW && 0
+ kdDebug () << "kpView(" << name () << ")::mouseMoveEvent ("
+ << e->x () << "," << e->y () << ")"
+ << endl;
+#endif
+
+ // TODO: This is wrong if you leaveEvent the mainView by mouseMoving on the
+ // mainView, landing on top of the thumbnailView cleverly put on top
+ // of the mainView.
+ setHasMouse (rect ().contains (e->pos ()));
+
+ if (tool ())
+ tool ()->mouseMoveEvent (e);
+
+ e->accept ();
+}
+
+// protected virtual [base QWidget]
+void kpView::mousePressEvent (QMouseEvent *e)
+{
+#if DEBUG_KP_VIEW && 0
+ kdDebug () << "kpView(" << name () << ")::mousePressEvent ("
+ << e->x () << "," << e->y () << ")"
+ << endl;
+#endif
+
+ setHasMouse (true);
+
+ if (tool ())
+ tool ()->mousePressEvent (e);
+
+ e->accept ();
+}
+
+// protected virtual [base QWidget]
+void kpView::mouseReleaseEvent (QMouseEvent *e)
+{
+#if DEBUG_KP_VIEW && 0
+ kdDebug () << "kpView(" << name () << ")::mouseReleaseEvent ("
+ << e->x () << "," << e->y () << ")"
+ << endl;
+#endif
+
+ setHasMouse (rect ().contains (e->pos ()));
+
+ if (tool ())
+ tool ()->mouseReleaseEvent (e);
+
+ e->accept ();
+}
+
+// public virtual [base QWidget]
+void kpView::wheelEvent (QWheelEvent *e)
+{
+ if (tool ())
+ tool ()->wheelEvent (e);
+}
+
+
+// protected virtual [base QWidget]
+void kpView::keyPressEvent (QKeyEvent *e)
+{
+#if DEBUG_KP_VIEW && 0
+ kdDebug () << "kpView(" << name () << ")::keyPressEvent()" << endl;
+#endif
+
+ if (tool ())
+ tool ()->keyPressEvent (e);
+
+ e->accept ();
+}
+
+// protected virtual [base QWidget]
+void kpView::keyReleaseEvent (QKeyEvent *e)
+{
+#if DEBUG_KP_VIEW && 0
+ kdDebug () << "kpView(" << name () << ")::keyReleaseEvent()" << endl;
+#endif
+
+ if (tool ())
+ tool ()->keyReleaseEvent (e);
+
+ e->accept ();
+}
+
+
+// protected virtual [base QWidget]
+void kpView::focusInEvent (QFocusEvent *e)
+{
+#if DEBUG_KP_VIEW && 0
+ kdDebug () << "kpView(" << name () << ")::focusInEvent()" << endl;
+#endif
+ if (tool ())
+ tool ()->focusInEvent (e);
+}
+
+// protected virtual [base QWidget]
+void kpView::focusOutEvent (QFocusEvent *e)
+{
+#if DEBUG_KP_VIEW && 0
+ kdDebug () << "kpView(" << name () << ")::focusOutEvent()" << endl;
+#endif
+ if (tool ())
+ tool ()->focusOutEvent (e);
+}
+
+
+// protected virtual [base QWidget]
+void kpView::enterEvent (QEvent *e)
+{
+#if DEBUG_KP_VIEW && 0
+ kdDebug () << "kpView(" << name () << ")::enterEvent()" << endl;
+#endif
+
+ // Don't call setHasMouse(true) as it displays the brush cursor (if
+ // active) when dragging open a menu and then dragging
+ // past the extents of the menu due to Qt sending us an EnterEvent.
+ // We're already covered by MouseMoveEvent anyway.
+ //
+ // But disabling this causes a more serious problem: RMB on a text
+ // box and Esc. We have no other reliable way to determine if the
+ // mouse is still above the view (user could have moved mouse out
+ // while RMB menu was up) and hence the cursor is not updated.
+ setHasMouse (true);
+
+ if (tool ())
+ tool ()->enterEvent (e);
+}
+
+// protected virtual [base QWidget]
+void kpView::leaveEvent (QEvent *e)
+{
+#if DEBUG_KP_VIEW && 0
+ kdDebug () << "kpView(" << name () << ")::leaveEvent()" << endl;
+#endif
+
+ setHasMouse (false);
+ if (tool ())
+ tool ()->leaveEvent (e);
+}
+
+
+// protected virtual [base QWidget]
+void kpView::dragEnterEvent (QDragEnterEvent *)
+{
+#if DEBUG_KP_VIEW && 1
+ kdDebug () << "kpView(" << name () << ")::dragEnterEvent()" << endl;
+#endif
+
+ setHasMouse (true);
+}
+
+// protected virtual [base QWidget]
+void kpView::dragLeaveEvent (QDragLeaveEvent *)
+{
+#if DEBUG_KP_VIEW && 1
+ kdDebug () << "kpView(" << name () << ")::dragLeaveEvent" << endl;
+#endif
+
+ setHasMouse (false);
+}
+
+
+// public virtual [base QWidget]
+void kpView::resize (int w, int h)
+{
+#if DEBUG_KP_VIEW && 1
+ kdDebug () << "kpView(" << name ()
+ << ")::resize(" << w << "," << h << ")"
+ << endl;
+#endif
+
+ QWidget::resize (w, h);
+}
+
+// protected virtual [base QWidget]
+void kpView::resizeEvent (QResizeEvent *e)
+{
+#if DEBUG_KP_VIEW && 1
+ kdDebug () << "kpView(" << name () << ")::resizeEvent("
+ << e->size ()
+ << " vs actual=" << size ()
+ << ") old=" << e->oldSize () << endl;
+#endif
+
+ QWidget::resizeEvent (e);
+
+ emit sizeChanged (width (), height ());
+ emit sizeChanged (size ());
+}
+
+
+// private virtual
+void kpView::imStartEvent (QIMEvent *e)
+{
+#if DEBUG_KP_VIEW && 1
+ kdDebug () << "kpView(" << name () << ")::imStartEvent" << endl;
+#endif
+
+ if (tool ())
+ tool ()->imStartEvent (e);
+ e->accept();
+}
+
+// private virtual
+void kpView::imComposeEvent (QIMEvent *e)
+{
+#if DEBUG_KP_VIEW && 1
+ kdDebug () << "kpView(" << name () << ")::imComposeEvent" << endl;
+#endif
+
+ if (tool ())
+ tool ()->imComposeEvent (e);
+ e->accept();
+}
+
+// private virtual
+void kpView::imEndEvent (QIMEvent *e)
+{
+#if DEBUG_KP_VIEW && 1
+ kdDebug () << "kpView(" << name () << ")::imEndEvent" << endl;
+#endif
+
+ if (tool ())
+ tool ()->imEndEvent (e);
+ e->accept();
+}
+
+
+//
+// Renderer
+//
+
+// protected
+QRect kpView::paintEventGetDocRect (const QRect &viewRect) const
+{
+#if DEBUG_KP_VIEW_RENDERER && 1
+ kdDebug () << "kpView::paintEventGetDocRect(" << viewRect << ")" << endl;
+#endif
+
+ QRect docRect;
+
+ // From the "we aren't sure whether to round up or round down" department:
+
+ if (zoomLevelX () < 100 || zoomLevelY () < 100)
+ docRect = transformViewToDoc (viewRect);
+ else
+ {
+ // think of a grid - you need to fully cover the zoomed-in pixels
+ // when docRect is zoomed back to the view later
+ docRect = QRect (transformViewToDoc (viewRect.topLeft ()), // round down
+ transformViewToDoc (viewRect.bottomRight ())); // round down
+ }
+
+ if (zoomLevelX () % 100 || zoomLevelY () % 100)
+ {
+ // at least round up the bottom-right point and deal with matrix weirdness:
+ // - helpful because it ensures we at least cover the required area
+ // at e.g. 67% or 573%
+ // - harmless since paintEventDrawRect() clips for us anyway
+ docRect.setBottomRight (docRect.bottomRight () + QPoint (2, 2));
+ }
+
+#if DEBUG_KP_VIEW_RENDERER && 1
+ kdDebug () << "\tdocRect=" << docRect << endl;
+#endif
+ kpDocument *doc = document ();
+ if (doc)
+ {
+ docRect = docRect.intersect (doc->rect ());
+ #if DEBUG_KP_VIEW_RENDERER && 1
+ kdDebug () << "\tintersect with doc=" << docRect << endl;
+ #endif
+ }
+
+ return docRect;
+}
+
+// public
+void kpView::drawTransparentBackground (QPainter *painter,
+ int /*viewWidth*/, int /*viewHeight*/,
+ const QRect &rect,
+ bool isPreview)
+{
+ const int cellSize = !isPreview ? 16 : 10;
+
+ int starty = rect.y ();
+ if (starty % cellSize)
+ starty -= (starty % cellSize);
+
+ int startx = rect.x ();
+ if (startx % cellSize)
+ startx -= (startx % cellSize);
+
+ painter->save ();
+ for (int y = starty; y <= rect.bottom (); y += cellSize)
+ {
+ for (int x = startx; x <= rect.right (); x += cellSize)
+ {
+ bool parity = (x / cellSize + y / cellSize) % 2;
+ QColor col;
+
+ if (parity)
+ {
+ if (!isPreview)
+ col = QColor (213, 213, 213);
+ else
+ col = QColor (224, 224, 224);
+ }
+ else
+ col = Qt::white;
+
+ painter->fillRect (x - rect.x (), y - rect.y (), cellSize, cellSize,
+ col);
+ }
+ }
+ painter->restore ();
+}
+
+// protected
+void kpView::paintEventDrawCheckerBoard (QPainter *painter, const QRect &viewRect)
+{
+ kpDocument *doc = document ();
+ if (!doc)
+ return;
+
+ drawTransparentBackground (painter,
+ doc->width () * zoomLevelX () / 100,
+ doc->height () * zoomLevelY () / 100,
+ viewRect);
+}
+
+// protected
+void kpView::paintEventDrawSelection (QPixmap *destPixmap, const QRect &docRect)
+{
+#if DEBUG_KP_VIEW_RENDERER && 1
+ kdDebug () << "kpView::paintEventDrawSelection() docRect=" << docRect << endl;
+#endif
+
+ kpDocument *doc = document ();
+ if (!doc)
+ {
+ #if DEBUG_KP_VIEW_RENDERER && 1
+ kdDebug () << "\tno doc - abort" << endl;
+ #endif
+ return;
+ }
+
+ kpSelection *sel = doc->selection ();
+ if (!sel)
+ {
+ #if DEBUG_KP_VIEW_RENDERER && 1
+ kdDebug () << "\tno sel - abort" << endl;
+ #endif
+ return;
+ }
+
+
+ //
+ // Draw selection pixmap (if there is one)
+ //
+#if DEBUG_KP_VIEW_RENDERER && 1
+ kdDebug () << "\tdraw sel pixmap @ " << sel->topLeft () << endl;
+#endif
+ sel->paint (destPixmap, docRect);
+
+
+ //
+ // Draw selection border
+ //
+
+ kpViewManager *vm = viewManager ();
+#if DEBUG_KP_VIEW_RENDERER && 1
+ kdDebug () << "\tsel border visible="
+ << vm->selectionBorderVisible ()
+ << endl;
+#endif
+ if (vm->selectionBorderVisible ())
+ {
+ QPainter destPixmapPainter (destPixmap);
+ destPixmapPainter.setRasterOp (Qt::XorROP);
+ destPixmapPainter.setPen (QPen (Qt::white, 1, Qt::DotLine));
+
+ destPixmapPainter.setBackgroundMode (QPainter::OpaqueMode);
+ destPixmapPainter.setBackgroundColor (Qt::blue);
+
+ QBitmap maskBitmap;
+ QPainter maskBitmapPainter;
+ if (destPixmap->mask ())
+ {
+ maskBitmap = *destPixmap->mask ();
+ maskBitmapPainter.begin (&maskBitmap);
+ maskBitmapPainter.setPen (Qt::color1/*opaque*/);
+ }
+
+
+ #define PAINTER_CMD(cmd) \
+ { \
+ destPixmapPainter . cmd; \
+ if (maskBitmapPainter.isActive ()) \
+ maskBitmapPainter . cmd; \
+ }
+
+ QRect boundingRect = sel->boundingRect ();
+ #if DEBUG_KP_VIEW_RENDERER && 1
+ kdDebug () << "\tsel boundingRect="
+ << boundingRect
+ << endl;
+ #endif
+
+ if (boundingRect.topLeft () != boundingRect.bottomRight ())
+ {
+ switch (sel->type ())
+ {
+ case kpSelection::Rectangle:
+ case kpSelection::Text:
+ #if DEBUG_KP_VIEW_RENDERER && 1
+ kdDebug () << "\tselection border = rectangle" << endl;
+ kdDebug () << "\t\tx=" << boundingRect.x () - docRect.x ()
+ << " y=" << boundingRect.y () - docRect.y ()
+ << " w=" << boundingRect.width ()
+ << " h=" << boundingRect.height ()
+ << endl;
+ #endif
+ PAINTER_CMD (drawRect (boundingRect.x () - docRect.x (),
+ boundingRect.y () - docRect.y (),
+ boundingRect.width (),
+ boundingRect.height ()));
+ break;
+
+ case kpSelection::Ellipse:
+ #if DEBUG_KP_VIEW_RENDERER && 1
+ kdDebug () << "\tselection border = ellipse" << endl;
+ #endif
+ PAINTER_CMD (drawEllipse (boundingRect.x () - docRect.x (),
+ boundingRect.y () - docRect.y (),
+ boundingRect.width (),
+ boundingRect.height ()));
+ break;
+
+ case kpSelection::Points:
+ {
+ #if DEBUG_KP_VIEW_RENDERER
+ kdDebug () << "\tselection border = freeForm" << endl;
+ #endif
+ QPointArray points = sel->points ();
+ points.detach ();
+ points.translate (-docRect.x (), -docRect.y ());
+ if (vm->selectionBorderFinished ())
+ {
+ PAINTER_CMD (drawPolygon (points));
+ }
+ else
+ {
+ PAINTER_CMD (drawPolyline (points));
+ }
+
+ break;
+ }
+
+ default:
+ kdError () << "kpView::paintEventDrawSelection() unknown sel border type" << endl;
+ break;
+ }
+
+
+ if (vm->selectionBorderFinished () &&
+ (sel->type () == kpSelection::Ellipse ||
+ sel->type () == kpSelection::Points))
+ {
+ destPixmapPainter.save ();
+
+ destPixmapPainter.setRasterOp (Qt::NotROP);
+ PAINTER_CMD (drawRect (boundingRect.x () - docRect.x (),
+ boundingRect.y () - docRect.y (),
+ boundingRect.width (),
+ boundingRect.height ()));
+
+ destPixmapPainter.restore ();
+ }
+ }
+ else
+ {
+ // SYNC: Work around Qt bug: can't draw 1x1 rectangle
+ PAINTER_CMD (drawPoint (boundingRect.topLeft () - docRect.topLeft ()));
+ }
+
+ #undef PAINTER_CMD
+
+ destPixmapPainter.end ();
+ if (maskBitmapPainter.isActive ())
+ maskBitmapPainter.end ();
+
+ destPixmap->setMask (maskBitmap);
+ }
+
+
+ //
+ // Draw text cursor
+ //
+
+ if (sel->isText () &&
+ vm->textCursorEnabled () &&
+ (vm->textCursorBlinkState () ||
+ // For the current main window:
+ // As long as _any_ view has focus, blink _all_ views not just the
+ // one with focus // !this->isActiveWindow ()
+ !vm->activeView ())) // sync: call will break when vm is not held by 1 mainWindow
+ {
+ // TODO: Fix code duplication: kpViewManager::{setTextCursorPosition,updateTextCursor}() & kpView::paintEventDrawSelection()
+ QPoint topLeft = sel->pointForTextRowCol (vm->textCursorRow (), vm->textCursorCol ());
+ if (topLeft != KP_INVALID_POINT)
+ {
+ QRect rect = QRect (topLeft.x (), topLeft.y (),
+ 1, sel->textStyle ().fontMetrics ().height ());
+ rect = rect.intersect (sel->textAreaRect ());
+ if (!rect.isEmpty ())
+ {
+ rect.moveBy (-docRect.x (), -docRect.y ());
+
+ QBitmap maskBitmap;
+ QPainter destPixmapPainter, maskBitmapPainter;
+
+ if (destPixmap->mask ())
+ {
+ maskBitmap = *destPixmap->mask ();
+ maskBitmapPainter.begin (&maskBitmap);
+ maskBitmapPainter.fillRect (rect, Qt::color1/*opaque*/);
+ maskBitmapPainter.end ();
+ }
+
+ destPixmapPainter.begin (destPixmap);
+ destPixmapPainter.setRasterOp (Qt::XorROP);
+ destPixmapPainter.fillRect (rect, Qt::white);
+ destPixmapPainter.end ();
+
+ if (!maskBitmap.isNull ())
+ destPixmap->setMask (maskBitmap);
+ }
+ }
+ }
+}
+
+// protected
+bool kpView::selectionResizeHandleAtomicSizeCloseToZoomLevel () const
+{
+ return (abs (selectionResizeHandleAtomicSize () - zoomLevelX () / 100) < 3);
+}
+
+// protected
+void kpView::paintEventDrawSelectionResizeHandles (QPainter *painter, const QRect &viewRect)
+{
+#if DEBUG_KP_VIEW_RENDERER && 1
+ kdDebug () << "kpView::paintEventDrawSelectionResizeHandles("
+ << viewRect << ")" << endl;
+#endif
+
+ if (!selectionLargeEnoughToHaveResizeHandles ())
+ {
+ #if DEBUG_KP_VIEW_RENDERER && 1
+ kdDebug () << "\tsel not large enough to have resize handles" << endl;
+ #endif
+ return;
+ }
+
+ kpViewManager *vm = viewManager ();
+ if (!vm || !vm->selectionBorderVisible () || !vm->selectionBorderFinished ())
+ {
+ #if DEBUG_KP_VIEW_RENDERER && 1
+ kdDebug () << "\tsel border not visible or not finished" << endl;
+ #endif
+
+ return;
+ }
+
+ const QRect selViewRect = selectionViewRect ();
+#if DEBUG_KP_VIEW_RENDERER && 1
+ kdDebug () << "\tselViewRect=" << selViewRect << endl;
+#endif
+ if (!selViewRect.intersects (viewRect))
+ {
+ #if DEBUG_KP_VIEW_RENDERER && 1
+ kdDebug () << "\tdoesn't intersect viewRect" << endl;
+ #endif
+ return;
+ }
+
+ QRegion selResizeHandlesRegion = selectionResizeHandlesViewRegion (true/*for renderer*/);
+#if DEBUG_KP_VIEW_RENDERER && 1
+ kdDebug () << "\tsel resize handles view region="
+ << selResizeHandlesRegion << endl;
+#endif
+ selResizeHandlesRegion.translate (-viewRect.x (), -viewRect.y ());
+
+ painter->save ();
+
+ QColor fillColor;
+ if (selectionResizeHandleAtomicSizeCloseToZoomLevel ())
+ {
+ fillColor = Qt::blue;
+ painter->setRasterOp (Qt::CopyROP);
+ }
+ else
+ {
+ fillColor = Qt::white;
+ painter->setRasterOp (Qt::XorROP);
+ }
+
+ QMemArray <QRect> rects = selResizeHandlesRegion.rects ();
+ for (QMemArray <QRect>::ConstIterator it = rects.begin ();
+ it != rects.end ();
+ it++)
+ {
+ painter->fillRect (*it, fillColor);
+ }
+
+ painter->restore ();
+}
+
+// protected
+void kpView::paintEventDrawTempPixmap (QPixmap *destPixmap, const QRect &docRect)
+{
+ kpViewManager *vm = viewManager ();
+ if (!vm)
+ return;
+
+ const kpTempPixmap *tpm = vm->tempPixmap ();
+#if DEBUG_KP_VIEW_RENDERER && 1
+ kdDebug () << "kpView::paintEventDrawTempPixmap() tempPixmap="
+ << tpm
+ << " isVisible="
+ << (tpm ? tpm->isVisible (vm) : false)
+ << endl;
+#endif
+
+ if (!tpm || !tpm->isVisible (vm))
+ return;
+
+ tpm->paint (destPixmap, docRect);
+}
+
+// protected
+void kpView::paintEventDrawGridLines (QPainter *painter, const QRect &viewRect)
+{
+ int hzoomMultiple = zoomLevelX () / 100;
+ int vzoomMultiple = zoomLevelY () / 100;
+
+ QPen ordinaryPen (Qt::gray);
+ QPen tileBoundaryPen (Qt::lightGray);
+
+ painter->setPen (ordinaryPen);
+
+ // horizontal lines
+ int starty = viewRect.top ();
+ if (starty % vzoomMultiple)
+ starty = (starty + vzoomMultiple) / vzoomMultiple * vzoomMultiple;
+ int tileHeight = 16 * vzoomMultiple; // CONFIG
+ for (int y = starty - viewRect.y (); y <= viewRect.bottom () - viewRect.y (); y += vzoomMultiple)
+ {
+ if (0 && tileHeight > 0 && y % tileHeight == 0)
+ {
+ painter->setPen (tileBoundaryPen);
+ //painter.setRasterOp (Qt::XorROP);
+ }
+
+ painter->drawLine (0, y, viewRect.right () - viewRect.left (), y);
+
+ if (0 && tileHeight > 0 && y % tileHeight == 0)
+ {
+ painter->setPen (ordinaryPen);
+ //painter.setRasterOp (Qt::CopyROP);
+ }
+ }
+
+ // vertical lines
+ int startx = viewRect.left ();
+ if (startx % hzoomMultiple)
+ startx = (startx + hzoomMultiple) / hzoomMultiple * hzoomMultiple;
+ int tileWidth = 16 * hzoomMultiple; // CONFIG
+ for (int x = startx - viewRect.x (); x <= viewRect.right () - viewRect.x (); x += hzoomMultiple)
+ {
+ if (0 && tileWidth > 0 && x % tileWidth == 0)
+ {
+ painter->setPen (tileBoundaryPen);
+ //painter.setRasterOp (Qt::XorROP);
+ }
+
+ painter->drawLine (x, 0, x, viewRect.bottom () - viewRect.top ());
+
+ if (0 && tileWidth > 0 && x % tileWidth == 0)
+ {
+ painter->setPen (ordinaryPen);
+ //painter.setRasterOp (Qt::CopyROP);
+ }
+ }
+}
+
+
+void kpView::paintEventDrawRect (const QRect &viewRect)
+{
+#if DEBUG_KP_VIEW_RENDERER
+ kdDebug () << "\tkpView::paintEventDrawRect(viewRect=" << viewRect
+ << ")" << endl;
+#endif
+
+ kpViewManager *vm = viewManager ();
+ const kpDocument *doc = document ();
+
+ if (!vm || !doc)
+ return;
+
+
+ if (viewRect.isEmpty ())
+ return;
+
+
+ QRect docRect = paintEventGetDocRect (viewRect);
+
+#if DEBUG_KP_VIEW_RENDERER && 1
+ kdDebug () << "\tdocRect=" << docRect << endl;
+#endif
+
+// uncomment to cause deliberate flicker (identifies needless updates)
+#if DEBUG_KP_VIEW_RENDERER && 0
+ QPainter flickerPainter (this);
+ flickerPainter.fillRect (viewRect, Qt::red);
+ flickerPainter.end ();
+#endif
+
+
+ //
+ // Prepare Back Buffer
+ //
+
+ if (!d->m_backBuffer ||
+ d->m_backBuffer->width () < viewRect.width () ||
+ d->m_backBuffer->height () < viewRect.height () ||
+ d->m_backBuffer->width () > width () ||
+ d->m_backBuffer->height () > height ())
+ {
+ // don't use QPixmap::resize() as that wastes time copying pixels
+ // that will be overwritten anyway
+ //
+ // OPT: Should use doubling trick or at least go up in multiples
+ // to reduce X server pressure.
+ delete d->m_backBuffer;
+ d->m_backBuffer = new QPixmap (viewRect.width (), viewRect.height ());
+ }
+
+// uncomment to catch bits of the view that the renderer forgot to update
+#if 0
+ d->m_backBuffer->fill (Qt::green);
+#endif
+
+ QPainter backBufferPainter;
+ backBufferPainter.begin (d->m_backBuffer);
+
+
+ //
+ // Draw checkboard for transparent images and/or views with borders
+ //
+
+ QPixmap docPixmap;
+
+ bool tempPixmapWillBeRendered = false;
+
+ if (!docRect.isEmpty ())
+ {
+ docPixmap = doc->getPixmapAt (docRect);
+
+ tempPixmapWillBeRendered =
+ (!doc->selection () &&
+ vm->tempPixmap () &&
+ vm->tempPixmap ()->isVisible (vm) &&
+ docRect.intersects (vm->tempPixmap ()->rect ()));
+
+ #if DEBUG_KP_VIEW_RENDERER && 1
+ kdDebug () << "\ttempPixmapWillBeRendered=" << tempPixmapWillBeRendered
+ << " (sel=" << doc->selection ()
+ << " tempPixmap=" << vm->tempPixmap ()
+ << " tempPixmap.isVisible=" << (vm->tempPixmap () ? vm->tempPixmap ()->isVisible (vm) : false)
+ << " docRect.intersects(tempPixmap.rect)=" << (vm->tempPixmap () ? docRect.intersects (vm->tempPixmap ()->rect ()) : false)
+ << ")"
+ << endl;
+ #endif
+ }
+
+ if (docPixmap.mask () ||
+ (tempPixmapWillBeRendered && vm->tempPixmap ()->mayChangeDocumentMask ()))
+ {
+ #if DEBUG_KP_VIEW_RENDERER && 1
+ kdDebug () << "\tmask=" << (bool) docPixmap.mask ()
+ << endl;
+ #endif
+ paintEventDrawCheckerBoard (&backBufferPainter, viewRect);
+ }
+ else
+ {
+ #if DEBUG_KP_VIEW_RENDERER && 1
+ kdDebug () << "\tno mask" << endl;
+ #endif
+ }
+
+
+ if (!docRect.isEmpty ())
+ {
+ //
+ // Draw docPixmap + tempPixmap
+ //
+
+ if (doc->selection ())
+ {
+ paintEventDrawSelection (&docPixmap, docRect);
+ }
+ else if (tempPixmapWillBeRendered)
+ {
+ paintEventDrawTempPixmap (&docPixmap, docRect);
+ }
+
+ #if DEBUG_KP_VIEW_RENDERER && 1
+ kdDebug () << "\torigin=" << origin () << endl;
+ #endif
+ // blit scaled version of docPixmap + tempPixmap onto Back Buffer
+ #if DEBUG_KP_VIEW_RENDERER && 1
+ QTime scaleTimer; scaleTimer.start ();
+ #endif
+ backBufferPainter.translate (origin ().x () - viewRect.x (),
+ origin ().y () - viewRect.y ());
+ backBufferPainter.scale (double (zoomLevelX ()) / 100.0,
+ double (zoomLevelY ()) / 100.0);
+ backBufferPainter.drawPixmap (docRect, docPixmap);
+ backBufferPainter.resetXForm (); // back to 1-1 scaling
+ #if DEBUG_KP_VIEW_RENDERER && 1
+ kdDebug () << "\tscale time=" << scaleTimer.elapsed () << endl;
+ #endif
+
+ } // if (!docRect.isEmpty ()) {
+
+
+ //
+ // Draw Grid Lines
+ //
+
+ if (isGridShown ())
+ {
+ #if DEBUG_KP_VIEW_RENDERER && 1
+ QTime gridTimer; gridTimer.start ();
+ #endif
+ paintEventDrawGridLines (&backBufferPainter, viewRect);
+ #if DEBUG_KP_VIEW_RENDERER && 1
+ kdDebug () << "\tgrid time=" << gridTimer.elapsed () << endl;
+ #endif
+ }
+
+
+ const QRect bvsvRect = buddyViewScrollableContainerRectangle ();
+ if (!bvsvRect.isEmpty ())
+ {
+ backBufferPainter.save ();
+
+ backBufferPainter.setRasterOp (Qt::XorROP);
+ backBufferPainter.setPen (Qt::white);
+ backBufferPainter.translate (-viewRect.x (), -viewRect.y ());
+ backBufferPainter.drawRect (bvsvRect);
+
+ backBufferPainter.restore ();
+ }
+
+
+ if (!docRect.isEmpty ())
+ {
+ if (doc->selection ())
+ {
+ // Draw resize handles on top of possible grid lines
+ paintEventDrawSelectionResizeHandles (&backBufferPainter, viewRect);
+ }
+ }
+
+
+ //
+ // Blit Back Buffer to View
+ //
+
+ backBufferPainter.end ();
+
+ bitBlt (this, viewRect.topLeft (),
+ d->m_backBuffer, QRect (0, 0, viewRect.width (), viewRect.height ()));
+}
+
+
+// protected virtual [base QWidget]
+void kpView::paintEvent (QPaintEvent *e)
+{
+ // sync: kpViewPrivate
+ // WARNING: document(), viewManager() and friends might be 0 in this method.
+ // TODO: I'm not 100% convinced that we always check if their friends are 0.
+
+#if DEBUG_KP_VIEW_RENDERER && 1
+ QTime timer;
+ timer.start ();
+#endif
+
+ kpViewManager *vm = viewManager ();
+
+#if DEBUG_KP_VIEW_RENDERER && 1
+ kdDebug () << "kpView(" << name () << ")::paintEvent() vm=" << (bool) vm
+ << " queueUpdates=" << (vm && vm->queueUpdates ())
+ << " fastUpdates=" << (vm && vm->fastUpdates ())
+ << " viewRect=" << e->rect ()
+ << " erased=" << e->erased ()
+ << " topLeft=" << QPoint (x (), y ())
+ << endl;
+#endif
+
+ if (!vm)
+ return;
+
+ if (vm->queueUpdates ())
+ {
+ // OPT: if this update was due to the document,
+ // use document coordinates (in case of a zoom change in
+ // which view coordinates become out of date)
+ addToQueuedArea (e->region ());
+ return;
+ }
+
+
+ QRegion viewRegion = clipRegion ().intersect (e->region ());
+ QMemArray <QRect> rects = viewRegion.rects ();
+#if DEBUG_KP_VIEW_RENDERER && 1
+ kdDebug () << "\t#rects = " << rects.count () << endl;
+#endif
+
+ for (QMemArray <QRect>::ConstIterator it = rects.begin ();
+ it != rects.end ();
+ it++)
+ {
+ paintEventDrawRect (*it);
+ }
+
+
+#if DEBUG_KP_VIEW_RENDERER && 1
+ kdDebug () << "\tall done in: " << timer.restart () << "ms" << endl;
+#endif
+}
+
+
+#include <kpview.moc>
diff --git a/kolourpaint/kpview.h b/kolourpaint/kpview.h
new file mode 100644
index 00000000..0bf8c495
--- /dev/null
+++ b/kolourpaint/kpview.h
@@ -0,0 +1,535 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef KP_VIEW_H
+#define KP_VIEW_H
+
+
+#include <qwidget.h>
+
+#include <kpdefs.h>
+
+
+class kpDocument;
+class kpSelection;
+class kpTool;
+class kpToolToolBar;
+class kpViewManager;
+class kpViewScrollableContainer;
+
+
+/**
+ * @short Abstract base class for all views.
+ *
+ * This is the abstract base class for all views. A view is a widget that
+ * renders a possibly zoomed onscreen representation of a document.
+ *
+ * 1 view corresponds to 1 document.
+ * 1 document corresponds to 0 or more views.
+ *
+ * @see kpViewManager
+ *
+ * @author Clarence Dang <dang@kde.org>
+ */
+class kpView : public QWidget
+{
+Q_OBJECT
+
+public:
+ /**
+ * Constructs a view.
+ *
+ * @param document The document this view is representing.
+ * @param toolToolBar The tool tool bar.
+ * @param viewManager The view manager.
+ * @param buddyView The view this view watches over (e.g. a thumbnail
+ * view would watch over the main view). May be 0.
+ * See for example, highlightBuddyViewRectangle().
+ * @param scrollableContainer This view's scrollable container.
+ * May be 0.
+ *
+ * You must call adjustEnvironment() at the end of your constructor.
+ */
+ kpView (kpDocument *document,
+ kpToolToolBar *toolToolBar,
+ kpViewManager *viewManager,
+ kpView *buddyView,
+ kpViewScrollableContainer *scrollableContainer,
+ QWidget *parent, const char *name);
+
+ /**
+ * Destructs this view. Informs the viewManager() that the mouse
+ * cursor is no longer above this view.
+ */
+ virtual ~kpView ();
+
+
+ /**
+ * @returns the document.
+ */
+ kpDocument *document () const;
+
+protected:
+ /**
+ * @returns the document's selection.
+ */
+ kpSelection *selection () const;
+
+public:
+ /**
+ * @returns the tool tool bar.
+ */
+ kpToolToolBar *toolToolBar () const;
+
+protected:
+ /**
+ * @returns the currently selected tool.
+ */
+ kpTool *tool () const;
+
+public:
+ /**
+ * @returns the view manager.
+ */
+ kpViewManager *viewManager () const;
+
+ /**
+ * @returns the buddy view.
+ */
+ kpView *buddyView () const;
+
+ /**
+ * @returns the buddyView()'s scrollable container.
+ */
+ kpViewScrollableContainer *buddyViewScrollableContainer () const;
+
+ /**
+ * @returns this view's scrollable container.
+ */
+ kpViewScrollableContainer *scrollableContainer () const;
+
+
+ /**
+ * @returns the horizontal zoom level (100 is unzoomed).
+ */
+ int zoomLevelX (void) const;
+
+ /**
+ * @returns the vertical zoom level (100 is unzoomed).
+ */
+ int zoomLevelY (void) const;
+
+ /**
+ * Sets the horizontal and vertical zoom levels.
+ *
+ * @param hzoom Horizontal zoom level.
+ * @param vzoom Vertical zoom level.
+ *
+ * If reimplementing, you must call this base implementation.
+ */
+ virtual void setZoomLevel (int hzoom, int vzoom);
+
+
+ /**
+ * @returns in views coordinates, where the top-left document() pixel
+ * will be rendered (default: (0,0)).
+ */
+ QPoint origin () const;
+
+ /**
+ * Sets the origin.
+ *
+ * @param origin New origin.
+ *
+ * If reimplementing, you must call this base implementation.
+ */
+ virtual void setOrigin (const QPoint &origin);
+
+
+ /**
+ * @returns whether at this zoom level, the grid can be enabled.
+ * This is based on whether the grid can be sensibly rendered.
+ */
+ bool canShowGrid () const;
+
+ /**
+ * @returns whether the grid is currently shown.
+ */
+ bool isGridShown () const;
+
+ /**
+ * Turns on/off the grid.
+ *
+ * @param yes Whether to enable the grid.
+ */
+ void showGrid (bool yes = true);
+
+
+ /**
+ * @returns whether to draw a rectangle highlighting the area of
+ * buddyView() visible through buddyViewScrollableContainer().
+ */
+ bool isBuddyViewScrollableContainerRectangleShown () const;
+
+ /**
+ * Turns on/off the rectangle highlighting the area of buddyView()
+ * visible through buddyViewScrollableContainer() and redraws.
+ *
+ * @param yes Whether to turn on the rectangle.
+ */
+ void showBuddyViewScrollableContainerRectangle (bool yes = true);
+
+protected:
+ /**
+ * @returns the current rectangle highlighting the area of buddyView()
+ * visible through buddyViewScrollableContainer(), that is being
+ * rendered by this view.
+ */
+ QRect buddyViewScrollableContainerRectangle () const;
+
+protected slots:
+ /**
+ * Updates the buddyViewScrollableContainerRectangle() and redraws
+ * appropriately.
+ *
+ * This is already connected to zoomLevelChanged() and originChanged();
+ * buddyView() and buddyViewScrollableContainer() signals. There is probably no
+ * need to call this function directly.
+ */
+ void updateBuddyViewScrollableContainerRectangle ();
+
+
+public:
+
+ /**
+ * @param viewX Horizontal position in view coordinates.
+ *
+ * @returns viewX transformed to document coordinates, based on the
+ * origin() and zoomLevelX().
+ */
+ double transformViewToDocX (double viewX) const;
+
+ /**
+ * @param viewY Vertical position in view coordinates.
+ *
+ * @returns viewY transformed to document coordinates, based on the
+ * origin() and zoomLevelY().
+ */
+ double transformViewToDocY (double viewY) const;
+
+ /**
+ * @param viewPoint Position in view coordinates.
+ *
+ * @returns viewPoint transformed to document coordinates, based on the
+ * origin(), zoomLevelX() and zoomLevelY().
+ */
+ QPoint transformViewToDoc (const QPoint &viewPoint) const;
+
+ /**
+ * @param viewRect Rectangle in view coordinates.
+ *
+ * @returns viewRect transformed to document coordinates based on the
+ * origin(), zoomLevelX() and zoomLevelY().
+ *
+ * For bounding rectangles, you should use this function instead of
+ * transformViewToDocX(), transformViewToDocY() or
+ * transformViewToDoc(const QPoint &) which act on coordinates only.
+ */
+ QRect transformViewToDoc (const QRect &viewRect) const;
+
+
+ /**
+ * @param docX Horizontal position in document coordinates.
+ *
+ * @returns docX transformed to view coordinates, based on the origin()
+ * and zoomLevelX().
+ */
+ double transformDocToViewX (double docX) const;
+
+ /**
+ * @param docY Vertical position in document coordinates.
+ *
+ * @returns docY transformed to view coordinates, based on the origin()
+ * and zoomLevelY().
+ */
+ double transformDocToViewY (double docY) const;
+
+ /**
+ * @param docPoint Position in document coordinates.
+ *
+ * @returns docPoint transformed to view coordinates, based on the
+ * origin(), zoomLevelX(), zoomLevelY().
+ */
+ QPoint transformDocToView (const QPoint &docPoint) const;
+
+ /**
+ * @param docRect Rectangle in document coordinates.
+ *
+ * @return docRect transformed to view coordinates, based on the
+ * origin(), zoomLevelX() and zoomLevelY().
+ *
+ * For bounding rectangles, you should use this function instead of
+ * transformDocToViewX(), transformDocToViewY() or
+ * transformDocToView(const QPoint &) which act on coordinates only.
+ */
+ QRect transformDocToView (const QRect &docRect) const;
+
+
+ /**
+ * @param viewPoint Position in view coordinates.
+ * @param otherView View whose coordinate system the return value will
+ * be in.
+ *
+ * @returns viewPoint transformed to the coordinate system of
+ * @param otherView based on this and otherView's origin(),
+ * zoomLevelX() and zoomLevelY(). This has less rounding
+ * error than otherView->transformDocToView (transformViewToDoc (viewPoint)).
+ */
+ QPoint transformViewToOtherView (const QPoint &viewPoint,
+ const kpView *otherView);
+
+
+ /**
+ * @returns the approximate view width required to display the entire
+ * document(), based on the zoom level only.
+ */
+ int zoomedDocWidth () const;
+
+ /**
+ * @returns the approximate view height required to display the entire
+ * document(), based on the zoom level only.
+ */
+ int zoomedDocHeight () const;
+
+
+protected:
+ /**
+ * Updates the viewManager() on whether or not the mouse is directly
+ * above this view. Among other things, this ensures the brush cursor
+ * is updated.
+ *
+ * This should be called in event handlers.
+ *
+ * @param yes Whether the mouse is directly above this view.
+ */
+ void setHasMouse (bool yes = true);
+
+
+public:
+ /**
+ * Adds a region (in view coordinates) to the dirty area that is
+ * repainted when the parent @ref kpViewManager is set not to queue
+ * updates.
+ *
+ * @param region Region (in view coordinates) that needs repainting.
+ */
+ void addToQueuedArea (const QRegion &region);
+
+ /**
+ * Convenience function. Same as above.
+ *
+ * Adds a rectangle (in view coordinates) to the dirty area that is
+ * repainted when the parent @ref kpViewManager is set not to queue
+ * updates.
+ *
+ * @param rect Rectangle (in view coordinates) that needs repainting.
+ */
+ void addToQueuedArea (const QRect &rect);
+
+ /**
+ * Removes the dirty region that has been queued for updating.
+ * Does not update the view.
+ */
+ void invalidateQueuedArea ();
+
+ /**
+ * Updates the part of the view described by dirty region and then
+ * calls invalidateQueuedArea(). Does nothing if @ref kpViewManager
+ * is set to queue updates.
+ */
+ void updateQueuedArea ();
+
+ void updateMicroFocusHint (const QRect &microFocusHint);
+
+
+public slots:
+ /**
+ * Call this when the "environment" (e.g. document size) changes. The
+ * environment is defined by the caller and should be based on the type
+ * of view. For instance, an unzoomed thumbnail view would also
+ * include in its environment the contents position of an associated
+ * scrollable container.
+ *
+ * This is never called by the kpView base class.
+ *
+ * Implementors should change whatever state is neccessary for their
+ * type of view. For instance, an unzoomed view would resize itself;
+ * a zoomed thumbnail would change the zoom level.
+ */
+ virtual void adjustToEnvironment () = 0;
+
+
+public:
+ QRect selectionViewRect () const;
+
+ // (if <viewPoint> is KP_INVALID_POINT, it uses QCursor::pos())
+
+ QPoint mouseViewPoint (const QPoint &returnViewPoint = KP_INVALID_POINT) const;
+ QPoint mouseViewPointRelativeToSelection (const QPoint &viewPoint = KP_INVALID_POINT) const;
+ bool mouseOnSelection (const QPoint &viewPoint = KP_INVALID_POINT) const;
+
+ int textSelectionMoveBorderAtomicSize () const;
+ bool mouseOnSelectionToMove (const QPoint &viewPoint = KP_INVALID_POINT) const;
+
+protected:
+ bool selectionLargeEnoughToHaveResizeHandlesIfAtomicSize (int atomicSize) const;
+public:
+ int selectionResizeHandleAtomicSize () const;
+ bool selectionLargeEnoughToHaveResizeHandles () const;
+
+ QRegion selectionResizeHandlesViewRegion (bool forRenderer = false) const;
+
+ enum SelectionResizeType
+ {
+ None = 0,
+ Left = 1,
+ Right = 2,
+ Top = 4,
+ Bottom = 8
+ };
+
+ // Returns a bitwise OR of the SelectionResizeType's
+ int mouseOnSelectionResizeHandle (const QPoint &viewPoint = KP_INVALID_POINT) const;
+
+ bool mouseOnSelectionToSelectText (const QPoint &viewPoint = KP_INVALID_POINT) const;
+
+
+signals:
+ /**
+ * Emitted after all zooming code has been executed.
+ *
+ * @param zoomLevelX New zoomLevelX()
+ * @param zoomLevelY New zoomLevelY()
+ */
+ void zoomLevelChanged (int zoomLevelX, int zoomLevelY);
+
+ /**
+ * Emitted after all resizing code has been executed.
+ *
+ * @param size New view size.
+ */
+ void sizeChanged (const QSize &size);
+
+ /**
+ * Convenience signal - same as above.
+ *
+ * Emitted after all resizing code has been executed.
+ *
+ * @param width New view width.
+ * @param height New view height.
+ */
+ void sizeChanged (int width, int height);
+
+ /**
+ * Emitted after all origin changing code has been executed.
+ *
+ * @param origin The new origin.
+ */
+ void originChanged (const QPoint &origin);
+
+protected:
+ virtual void mouseMoveEvent (QMouseEvent *e);
+ virtual void mousePressEvent (QMouseEvent *e);
+ virtual void mouseReleaseEvent (QMouseEvent *e);
+public:
+ // (needs to be public as it may also get event from
+ // QScrollView::contentsWheelEvent())
+ virtual void wheelEvent (QWheelEvent *e);
+
+protected:
+ virtual void keyPressEvent (QKeyEvent *e);
+ virtual void keyReleaseEvent (QKeyEvent *e);
+
+ virtual void focusInEvent (QFocusEvent *e);
+ virtual void focusOutEvent (QFocusEvent *e);
+
+ virtual void enterEvent (QEvent *e);
+ virtual void leaveEvent (QEvent *e);
+
+ virtual void dragEnterEvent (QDragEnterEvent *);
+ virtual void dragLeaveEvent (QDragLeaveEvent *);
+
+ virtual void imStartEvent (QIMEvent *e);
+ virtual void imComposeEvent (QIMEvent *e);
+ virtual void imEndEvent (QIMEvent *e);
+
+public:
+ virtual void resize (int w, int h);
+protected:
+ virtual void resizeEvent (QResizeEvent *e);
+
+
+protected:
+ QRect paintEventGetDocRect (const QRect &viewRect) const;
+public:
+ /**
+ * Draws an opaque background representing transparency. Currently,
+ * it draws a checkerboard.
+ *
+ * @param painter Painter.
+ * @param viewWidth Total width of the view visible to the user.
+ * @param viewHeight Total height of the view visible to the user.
+ * @param rect Rectangle to paint in relative to the painter. Note
+ * that this function does not clip and may draw slightly
+ * more than the requested rectangle. TODO: why not?
+ * @param isPreview Whether the view is for a preview as opposed to
+ * e.g. editing. If set, this function may render
+ * slightly differently.
+ */
+ static void drawTransparentBackground (QPainter *painter,
+ int viewWidth, int viewHeight,
+ const QRect &rect,
+ bool isPreview = false);
+protected:
+ void paintEventDrawCheckerBoard (QPainter *painter, const QRect &viewRect);
+ void paintEventDrawSelection (QPixmap *destPixmap, const QRect &docRect);
+ bool selectionResizeHandleAtomicSizeCloseToZoomLevel () const;
+ void paintEventDrawSelectionResizeHandles (QPainter *painter, const QRect &viewRect);
+ void paintEventDrawTempPixmap (QPixmap *destPixmap, const QRect &docRect);
+ void paintEventDrawGridLines (QPainter *painter, const QRect &viewRect);
+
+ void paintEventDrawRect (const QRect &viewRect);
+ virtual void paintEvent (QPaintEvent *e);
+
+
+private:
+ struct kpViewPrivate *d;
+};
+
+
+#endif // KP_VIEW_H
diff --git a/kolourpaint/kpviewmanager.cpp b/kolourpaint/kpviewmanager.cpp
new file mode 100644
index 00000000..d649f359
--- /dev/null
+++ b/kolourpaint/kpviewmanager.cpp
@@ -0,0 +1,766 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define DEBUG_KP_VIEW_MANAGER 0
+
+
+#include <kpviewmanager.h>
+
+#include <qapplication.h>
+#include <qtimer.h>
+
+#include <kdebug.h>
+
+#include <kpdefs.h>
+#include <kpdocument.h>
+#include <kpmainwindow.h>
+#include <kpselection.h>
+#include <kptemppixmap.h>
+#include <kptool.h>
+#include <kpview.h>
+
+
+kpViewManager::kpViewManager (kpMainWindow *mainWindow)
+ : m_textCursorBlinkTimer (0),
+ m_textCursorRow (-1),
+ m_textCursorCol (-1),
+ m_textCursorBlinkState (true),
+ m_mainWindow (mainWindow),
+ m_tempPixmap (0),
+ m_viewUnderCursor (0),
+ m_selectionBorderVisible (false),
+ m_selectionBorderFinished (false)
+{
+ m_queueUpdatesCounter = m_fastUpdatesCounter = 0;
+}
+
+// private
+kpDocument *kpViewManager::document () const
+{
+ return m_mainWindow ? m_mainWindow->document () : 0;
+}
+
+kpViewManager::~kpViewManager ()
+{
+ unregisterAllViews ();
+
+ delete m_tempPixmap; m_tempPixmap = 0;
+}
+
+
+void kpViewManager::registerView (kpView *view)
+{
+#if DEBUG_KP_VIEW_MANAGER && 1
+ kdDebug () << "kpViewManager::registerView (" << view << ")" << endl;
+#endif
+ if (view && m_views.findRef (view) < 0)
+ {
+ #if DEBUG_KP_VIEW_MANAGER && 1
+ kdDebug () << "\tadded view" << endl;
+ #endif
+ view->setCursor (m_cursor);
+ m_views.append (view);
+ }
+ else
+ {
+ #if DEBUG_KP_VIEW_MANAGER && 1
+ kdDebug () << "\tignored register view attempt" << endl;
+ #endif
+ }
+}
+
+void kpViewManager::unregisterView (kpView *view)
+{
+ if (view)
+ {
+ if (view == m_viewUnderCursor)
+ m_viewUnderCursor = 0;
+
+ view->unsetCursor ();
+ m_views.removeRef (view);
+ }
+}
+
+void kpViewManager::unregisterAllViews ()
+{
+ // no autoDelete
+ m_views.clear ();
+}
+
+
+// public
+const kpTempPixmap *kpViewManager::tempPixmap () const
+{
+ return m_tempPixmap;
+}
+
+// public
+void kpViewManager::setTempPixmap (const kpTempPixmap &tempPixmap)
+{
+#if DEBUG_KP_VIEW_MANAGER
+ kdDebug () << "kpViewManager::setTempPixmap(isBrush="
+ << tempPixmap.isBrush ()
+ << ",topLeft=" << tempPixmap.topLeft ()
+ << ",pixmap.rect=" << tempPixmap.pixmap ().rect ()
+ << ")" << endl;
+#endif
+
+ QRect oldRect;
+
+ if (m_tempPixmap)
+ {
+ oldRect = m_tempPixmap->rect ();
+ delete m_tempPixmap;
+ m_tempPixmap = 0;
+ }
+
+ m_tempPixmap = new kpTempPixmap (tempPixmap);
+
+
+ setQueueUpdates ();
+
+ if (oldRect.isValid ())
+ updateViews (oldRect);
+ updateViews (m_tempPixmap->rect ());
+
+ restoreQueueUpdates ();
+}
+
+// public
+void kpViewManager::invalidateTempPixmap ()
+{
+ if (!m_tempPixmap)
+ return;
+
+ QRect oldRect = m_tempPixmap->rect ();
+
+ delete m_tempPixmap;
+ m_tempPixmap = 0;
+
+ updateViews (oldRect);
+}
+
+
+// public
+bool kpViewManager::selectionBorderVisible () const
+{
+ return m_selectionBorderVisible;
+}
+
+// public
+void kpViewManager::setSelectionBorderVisible (bool yes)
+{
+ if (m_selectionBorderVisible == yes)
+ return;
+
+ m_selectionBorderVisible = yes;
+
+ if (document () && document ()->selection ())
+ updateViews (document ()->selection ()->boundingRect ());
+}
+
+
+// public
+bool kpViewManager::selectionBorderFinished () const
+{
+ return m_selectionBorderFinished;
+}
+
+// public
+void kpViewManager::setSelectionBorderFinished (bool yes)
+{
+ if (m_selectionBorderFinished == yes)
+ return;
+
+ m_selectionBorderFinished = yes;
+
+ if (document () && document ()->selection ())
+ updateViews (document ()->selection ()->boundingRect ());
+}
+
+
+bool kpViewManager::textCursorEnabled () const
+{
+ return (bool) m_textCursorBlinkTimer;
+}
+
+void kpViewManager::setTextCursorEnabled (bool yes)
+{
+#if DEBUG_KP_VIEW_MANAGER && 1
+ kdDebug () << "kpViewManager::setTextCursorEnabled(" << yes << ")" << endl;
+#endif
+
+ if (yes == textCursorEnabled ())
+ return;
+
+ delete m_textCursorBlinkTimer;
+ m_textCursorBlinkTimer = 0;
+
+ setFastUpdates ();
+ setQueueUpdates ();
+
+ m_textCursorBlinkState = true;
+
+ if (yes)
+ {
+ m_textCursorBlinkTimer = new QTimer (this);
+ connect (m_textCursorBlinkTimer, SIGNAL (timeout ()),
+ this, SLOT (slotTextCursorBlink ()));
+ slotTextCursorBlink ();
+ }
+ // TODO: What if !yes - shouldn't it clear the cursor?
+
+ restoreQueueUpdates ();
+ restoreFastUpdates ();
+}
+
+
+int kpViewManager::textCursorRow () const
+{
+ bool handledErrors = false;
+ if (m_mainWindow)
+ {
+ kpDocument *doc = m_mainWindow->document ();
+ if (doc)
+ {
+ kpSelection *sel = doc->selection ();
+ if (sel && sel->isText ())
+ {
+ if (m_textCursorRow >= (int) sel->textLines ().size ())
+ {
+ #if DEBUG_KP_VIEW_MANAGER && 1
+ kdDebug () << "kpViewManager::textCursorRow() row="
+ << m_textCursorRow
+ << endl;
+ #endif
+ (const_cast <kpViewManager *> (this))->m_textCursorRow =
+ sel->textLines ().size () - 1;
+ }
+
+ handledErrors = true;
+ }
+ }
+ }
+
+ if (!handledErrors)
+ {
+ #if DEBUG_KP_VIEW_MANAGER && 1
+ kdDebug () << "kpViewManager::textCursorRow() no mw, doc or text sel" << endl;
+ #endif
+ (const_cast <kpViewManager *> (this))->m_textCursorRow = -1;
+ }
+
+ return m_textCursorRow;
+}
+
+int kpViewManager::textCursorCol () const
+{
+ int row = textCursorRow ();
+ if (row < 0)
+ return -1;
+
+ bool handledErrors = false;
+ if (m_mainWindow)
+ {
+ kpDocument *doc = m_mainWindow->document ();
+ if (doc)
+ {
+ kpSelection *sel = doc->selection ();
+ if (sel && sel->isText ())
+ {
+ if (m_textCursorCol > (int) sel->textLines () [row].length ())
+ {
+ #if DEBUG_KP_VIEW_MANAGER && 1
+ kdDebug () << "kpViewManager::textCursorRow() col="
+ << m_textCursorCol
+ << endl;
+ #endif
+ (const_cast <kpViewManager *> (this))->m_textCursorCol =
+ sel->textLines () [row].length ();
+ }
+
+ handledErrors = true;
+ }
+ }
+ }
+
+ if (!handledErrors)
+ {
+ #if DEBUG_KP_VIEW_MANAGER && 1
+ kdDebug () << "kpViewManager::textCursorCol() no mw, doc or text sel" << endl;
+ #endif
+ (const_cast <kpViewManager *> (this))->m_textCursorCol = -1;
+ }
+
+ return m_textCursorCol;
+}
+
+void kpViewManager::setTextCursorPosition (int row, int col, bool isUpdateMicroFocusHint)
+{
+ if (row == m_textCursorRow && col == m_textCursorCol)
+ return;
+
+ setFastUpdates ();
+ setQueueUpdates ();
+
+ m_textCursorBlinkState = false;
+ updateTextCursor ();
+
+ m_textCursorRow = row;
+ m_textCursorCol = col;
+
+ m_textCursorBlinkState = true;
+ updateTextCursor ();
+
+ restoreQueueUpdates ();
+ restoreFastUpdates ();
+
+ if (isUpdateMicroFocusHint)
+ {
+ kpDocument *doc = m_mainWindow->document ();
+ if (!doc)
+ return;
+
+ kpSelection *sel = doc->selection ();
+ if (!sel || !sel->isText ())
+ return;
+
+ if (m_viewUnderCursor)
+ {
+ // TODO: Fix code duplication: kpViewManager::{setTextCursorPosition,updateTextCursor}() & kpView::paintEventDrawSelection()
+ QPoint topLeft = sel->pointForTextRowCol (m_textCursorRow, m_textCursorCol);
+ if (topLeft != KP_INVALID_POINT)
+ {
+ // TODO: I think you need to consider zooming e.g. try editing
+ // text at 800% or with focus set to the thumbnail.
+ // kpSelection/kpDocument works fully in unzoomed
+ // coordinates unlike the view (which is zoomed and can
+ // change size).
+ //
+ // To fix it here, I think you should call
+ // m_viewUnderCursor->transformDocToView(QRect). However,
+ // the rest of the InputMethod support still needs to
+ // audited for this.
+ //
+ // [Bug #27]
+ m_viewUnderCursor->updateMicroFocusHint(QRect (topLeft.x (), topLeft.y (), 1, sel->textStyle ().fontMetrics ().height ()));
+ }
+ }
+ }
+}
+
+
+bool kpViewManager::textCursorBlinkState () const
+{
+ return m_textCursorBlinkState;
+}
+
+void kpViewManager::setTextCursorBlinkState (bool on)
+{
+ if (on == m_textCursorBlinkState)
+ return;
+
+ m_textCursorBlinkState = on;
+
+ updateTextCursor ();
+}
+
+
+// protected
+void kpViewManager::updateTextCursor ()
+{
+#if DEBUG_KP_VIEW_MANAGER && 0
+ kdDebug () << "kpViewManager::updateTextCursor()" << endl;
+#endif
+
+ if (!m_mainWindow)
+ return;
+
+ kpDocument *doc = m_mainWindow->document ();
+ if (!doc)
+ return;
+
+ kpSelection *sel = doc->selection ();
+ if (!sel || !sel->isText ())
+ return;
+
+ // TODO: Fix code duplication: kpViewManager::{setTextCursorPosition,updateTextCursor}() & kpView::paintEventDrawSelection()
+ QPoint topLeft = sel->pointForTextRowCol (m_textCursorRow, m_textCursorCol);
+ if (topLeft != KP_INVALID_POINT)
+ {
+ setFastUpdates ();
+ updateViews (QRect (topLeft.x (), topLeft.y (), 1, sel->textStyle ().fontMetrics ().height ()));
+ restoreFastUpdates ();
+ }
+}
+
+// protected slot
+void kpViewManager::slotTextCursorBlink ()
+{
+#if DEBUG_KP_VIEW_MANAGER && 0
+ kdDebug () << "kpViewManager::slotTextCursorBlink() cursorBlinkState="
+ << m_textCursorBlinkState << endl;
+#endif
+
+ if (m_textCursorBlinkTimer)
+ {
+ m_textCursorBlinkTimer->start (QApplication::cursorFlashTime () / 2,
+ true/*single shot*/);
+ }
+
+ updateTextCursor ();
+ m_textCursorBlinkState = !m_textCursorBlinkState;
+}
+
+
+void kpViewManager::setCursor (const QCursor &cursor)
+{
+ for (QPtrList <kpView>::const_iterator it = m_views.begin ();
+ it != m_views.end ();
+ it++)
+ {
+ (*it)->setCursor (cursor);
+ }
+
+ m_cursor = cursor;
+}
+
+void kpViewManager::unsetCursor ()
+{
+ for (QPtrList <kpView>::const_iterator it = m_views.begin ();
+ it != m_views.end ();
+ it++)
+ {
+ (*it)->unsetCursor ();
+ }
+
+ m_cursor = QCursor ();
+}
+
+
+kpView *kpViewManager::viewUnderCursor (bool usingQt) const
+{
+ if (!usingQt)
+ {
+ kpViewManager *nonConstThis = const_cast <kpViewManager *> (this);
+
+ if (m_viewUnderCursor && nonConstThis->m_views.findRef (m_viewUnderCursor) < 0)
+ {
+ kdError () << "kpViewManager::viewUnderCursor(): invalid view" << endl;
+ nonConstThis->m_viewUnderCursor = 0;
+ }
+
+
+ return m_viewUnderCursor;
+ }
+ else
+ {
+ for (QPtrList <kpView>::const_iterator it = m_views.begin ();
+ it != m_views.end ();
+ it++)
+ {
+ if ((*it)->hasMouse ())
+ return (*it);
+ }
+
+ return 0;
+ }
+}
+
+void kpViewManager::setViewUnderCursor (kpView *view)
+{
+#if DEBUG_KP_VIEW_MANAGER && 1
+ kdDebug () << "kpViewManager::setViewUnderCursor ("
+ << (view ? view->name () : "(none)") << ")"
+ << " old=" << (m_viewUnderCursor ? m_viewUnderCursor->name () : "(none)")
+ << endl;
+#endif
+ if (view == m_viewUnderCursor)
+ return;
+
+ m_viewUnderCursor = view;
+
+ if (!m_viewUnderCursor)
+ {
+ // Hide the brush if the mouse cursor just left the view
+ if (m_tempPixmap && m_tempPixmap->isBrush ())
+ {
+ #if DEBUG_KP_VIEW_MANAGER && 1
+ kdDebug () << "\thiding brush pixmap since cursor left view" << endl;
+ #endif
+ updateViews (m_tempPixmap->rect ());
+ }
+ }
+ else
+ {
+ if (m_mainWindow && m_mainWindow->tool ())
+ {
+ #if DEBUG_KP_VIEW_MANAGER && 1
+ kdDebug () << "\tnotify tool that something changed below cursor" << endl;
+ #endif
+ m_mainWindow->tool ()->somethingBelowTheCursorChanged ();
+ }
+ }
+}
+
+
+// public
+kpView *kpViewManager::activeView () const
+{
+ for (QPtrList <kpView>::const_iterator it = m_views.begin ();
+ it != m_views.end ();
+ it++)
+ {
+ if ((*it)->isActiveWindow ())
+ return (*it);
+ }
+
+ return 0;
+}
+
+
+// public
+bool kpViewManager::queueUpdates () const
+{
+ return (m_queueUpdatesCounter > 0);
+}
+
+// public
+void kpViewManager::setQueueUpdates ()
+{
+ m_queueUpdatesCounter++;
+#if DEBUG_KP_VIEW_MANAGER && 1
+ kdDebug () << "kpViewManager::setQueueUpdates() counter="
+ << m_queueUpdatesCounter << endl;
+#endif
+}
+
+// public
+void kpViewManager::restoreQueueUpdates ()
+{
+ m_queueUpdatesCounter--;
+#if DEBUG_KP_VIEW_MANAGER && 1
+ kdDebug () << "kpViewManager::restoreQueueUpdates() counter="
+ << m_queueUpdatesCounter << endl;
+#endif
+ if (m_queueUpdatesCounter < 0)
+ {
+ kdError () << "kpViewManager::restoreQueueUpdates() counter="
+ << m_queueUpdatesCounter;
+ }
+
+ if (m_queueUpdatesCounter <= 0)
+ {
+ for (QPtrList <kpView>::const_iterator it = m_views.begin ();
+ it != m_views.end ();
+ it++)
+ {
+ (*it)->updateQueuedArea ();
+ }
+ }
+}
+
+
+// public
+bool kpViewManager::fastUpdates () const
+{
+ return (m_fastUpdatesCounter > 0);
+}
+
+// public
+void kpViewManager::setFastUpdates ()
+{
+ m_fastUpdatesCounter++;
+#if DEBUG_KP_VIEW_MANAGER && 0
+ kdDebug () << "kpViewManager::setFastUpdates() counter="
+ << m_fastUpdatesCounter << endl;
+#endif
+}
+
+// public
+void kpViewManager::restoreFastUpdates ()
+{
+ m_fastUpdatesCounter--;
+#if DEBUG_KP_VIEW_MANAGER && 0
+ kdDebug () << "kpViewManager::restoreFastUpdates() counter="
+ << m_fastUpdatesCounter << endl;
+#endif
+ if (m_fastUpdatesCounter < 0)
+ {
+ kdError () << "kpViewManager::restoreFastUpdates() counter="
+ << m_fastUpdatesCounter;
+ }
+}
+
+
+void kpViewManager::updateView (kpView *v)
+{
+ updateView (v, QRect (0, 0, v->width (), v->height ()));
+}
+
+void kpViewManager::updateView (kpView *v, const QRect &viewRect)
+{
+ if (!queueUpdates ())
+ {
+ if (fastUpdates ())
+ v->repaint (viewRect, false/*no erase*/);
+ else
+ v->update (viewRect);
+ }
+ else
+ v->addToQueuedArea (viewRect);
+}
+
+void kpViewManager::updateView (kpView *v, int x, int y, int w, int h)
+{
+ updateView (v, QRect (x, y, w, h));
+}
+
+void kpViewManager::updateView (kpView *v, const QRegion &viewRegion)
+{
+ if (!queueUpdates ())
+ {
+ if (fastUpdates ())
+ v->repaint (viewRegion, false/*no erase*/);
+ else
+ v->update (viewRegion.boundingRect ());
+ }
+ else
+ v->addToQueuedArea (viewRegion);
+}
+
+void kpViewManager::updateViewRectangleEdges (kpView *v, const QRect &viewRect)
+{
+ if (viewRect.height () <= 0 || viewRect.width () <= 0)
+ return;
+
+ // Top line
+ updateView (v, QRect (viewRect.x (), viewRect.y (),
+ viewRect.width (), 1));
+
+ if (viewRect.height () >= 2)
+ {
+ // Bottom line
+ updateView (v, QRect (viewRect.x (), viewRect.bottom (),
+ viewRect.width (), 1));
+
+ if (viewRect.height () > 2)
+ {
+ // Left line
+ updateView (v, QRect (viewRect.x (), viewRect.y () + 1,
+ 1, viewRect.height () - 2));
+
+ if (viewRect.width () >= 2)
+ {
+ // Right line
+ updateView (v, QRect (viewRect.right (), viewRect.y () + 1,
+ 1, viewRect.height () - 2));
+ }
+ }
+ }
+}
+
+
+void kpViewManager::updateViews ()
+{
+ kpDocument *doc = document ();
+ if (doc)
+ updateViews (QRect (0, 0, doc->width (), doc->height ()));
+}
+
+void kpViewManager::updateViews (const QRect &docRect)
+{
+#if DEBUG_KP_VIEW_MANAGER && 0
+ kdDebug () << "kpViewManager::updateViews (" << docRect << ")" << endl;
+#endif
+
+ for (QPtrList <kpView>::const_iterator it = m_views.begin ();
+ it != m_views.end ();
+ it++)
+ {
+ kpView *view = *it;
+
+ #if DEBUG_KP_VIEW_MANAGER && 0
+ kdDebug () << "\tupdating view " << view->name () << endl;
+ #endif
+ if (view->zoomLevelX () % 100 == 0 && view->zoomLevelY () % 100 == 0)
+ {
+ #if DEBUG_KP_VIEW_MANAGER && 0
+ kdDebug () << "\t\tviewRect=" << view->transformDocToView (docRect) << endl;
+ #endif
+ updateView (view, view->transformDocToView (docRect));
+ }
+ else
+ {
+ QRect viewRect = view->transformDocToView (docRect);
+
+ int diff = qRound (double (QMAX (view->zoomLevelX (), view->zoomLevelY ())) / 100.0) + 1;
+
+ QRect newRect = QRect (viewRect.x () - diff,
+ viewRect.y () - diff,
+ viewRect.width () + 2 * diff,
+ viewRect.height () + 2 * diff)
+ .intersect (QRect (0, 0, view->width (), view->height ()));
+
+ #if DEBUG_KP_VIEW_MANAGER && 0
+ kdDebug () << "\t\tviewRect (+compensate)=" << newRect << endl;
+ #endif
+ updateView (view, newRect);
+ }
+ }
+}
+
+void kpViewManager::updateViews (int x, int y, int w, int h)
+{
+ updateViews (QRect (x, y, w, h));
+}
+
+
+void kpViewManager::adjustViewsToEnvironment ()
+{
+#if DEBUG_KP_VIEW_MANAGER && 1
+ kdDebug () << "kpViewManager::adjustViewsToEnvironment()"
+ << " numViews=" << m_views.count ()
+ << endl;
+#endif
+ for (QPtrList <kpView>::const_iterator it = m_views.begin ();
+ it != m_views.end ();
+ it++)
+ {
+ kpView *view = *it;
+
+ #if DEBUG_KP_VIEW_MANAGER && 1
+ kdDebug () << "\tview: " << view->name ()
+ << endl;
+ #endif
+ view->adjustToEnvironment ();
+ }
+}
+
+#include <kpviewmanager.moc>
+
diff --git a/kolourpaint/kpviewmanager.h b/kolourpaint/kpviewmanager.h
new file mode 100644
index 00000000..c6ea1ef0
--- /dev/null
+++ b/kolourpaint/kpviewmanager.h
@@ -0,0 +1,220 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kpviewmanager_h__
+#define __kpviewmanager_h__
+
+#include <qcursor.h>
+#include <qobject.h>
+#include <qptrlist.h>
+#include <qrect.h>
+
+
+class QPixmap;
+class QRect;
+class QTimer;
+
+class kpDocument;
+class kpView;
+class kpMainWindow;
+class kpTempPixmap;
+
+class kpViewManager : public QObject
+{
+Q_OBJECT
+
+public:
+ kpViewManager (kpMainWindow *mainWindow);
+ ~kpViewManager ();
+
+
+ //
+ // Registering views
+ //
+
+ void registerView (kpView *view);
+ void unregisterView (kpView *view);
+ void unregisterAllViews ();
+
+
+ //
+ // Temp Pixmap
+ //
+
+ const kpTempPixmap *tempPixmap () const;
+ void setTempPixmap (const kpTempPixmap &tempPixmap);
+ void invalidateTempPixmap ();
+
+
+ //
+ // Selections
+ //
+
+ bool selectionBorderVisible () const;
+ void setSelectionBorderVisible (bool yes = true);
+
+ bool selectionBorderFinished () const;
+ void setSelectionBorderFinished (bool yes = true);
+
+
+ //
+ // Text Cursor
+ //
+
+ bool textCursorEnabled () const;
+ void setTextCursorEnabled (bool yes = true);
+
+ int textCursorRow () const;
+ int textCursorCol () const;
+ void setTextCursorPosition (int row, int col, bool isUpdateMicroFocusHint = false);
+
+ bool textCursorBlinkState () const;
+ void setTextCursorBlinkState (bool on = true);
+
+protected:
+ void updateTextCursor ();
+
+ QTimer *m_textCursorBlinkTimer;
+ int m_textCursorRow, m_textCursorCol;
+ bool m_textCursorBlinkState;
+
+protected slots:
+ void slotTextCursorBlink ();
+
+public:
+
+ //
+ // Cursors
+ //
+
+ void setCursor (const QCursor &cursor);
+ void unsetCursor ();
+
+
+ //
+ // View
+ //
+
+ kpView *viewUnderCursor (bool usingQt = false) const;
+
+ //
+ // QWidget::hasMouse() is unreliable:
+ //
+ // "bool QWidget::hasMouse () const
+ // ... See the "underMouse" property for details.
+ // .
+ // .
+ // .
+ // bool underMouse
+ // ... This value is not updated properly during drag and drop operations."
+ //
+ // i.e. it's possible that hasMouse() returns false in a mousePressEvent()!
+ //
+ // This hack needs to be called from kpView so that viewUnderCursor() works
+ // as a reasonable replacement (although there is at least one case where
+ // it still won't work - just after a fake drag onto the view).
+ //
+ void setViewUnderCursor (kpView *view);
+
+
+ // Returns a pointer to the view that has keyboard focus or else, 0
+ // TODO: rename to "anActiveView()" or "aViewIsActive()" as more than
+ // 1 view can be active at the same time?
+ kpView *activeView () const;
+
+
+ // Specifies whether KolourPaint will queue _all_ paint events
+ // (generated by you or the window system), until the
+ // corresponding call to restoreQueueUpdates(). Use this
+ // before multiple, big, non-interactive changes to the
+ // document to eliminate virtually all flicker.
+ //
+ // This is better than QWidget::setUpdatesEnabled() because
+ // restoreQueueUpdates() automatically restores only the regions
+ // of the views that need to be repainted, per view.
+ bool queueUpdates () const;
+ void setQueueUpdates ();
+ void restoreQueueUpdates ();
+
+ // Controls behaviour of updateViews():
+ //
+ // Slow: Let Qt buffer paint events via QWidget::update().
+ // Results in less flicker. Paint events are probably merged
+ // so long-term efficiency is increased at the expense of
+ // reduced responsiveness (default).
+ // Fast: Force Qt to redraw immediately. No paint events
+ // are merged so there is great potential for flicker,
+ // if used inappropriately. Use this when the redraw
+ // area is small and KolourPaint's responsiveness is
+ // critical. Continual use of this mode can result in
+ // unnecessary redraws and incredibly slugish performance.
+ bool fastUpdates () const;
+ void setFastUpdates ();
+ void restoreFastUpdates ();
+
+private:
+ int m_queueUpdatesCounter, m_fastUpdatesCounter;
+
+public slots:
+ // updating views
+ void updateView (kpView *v);
+ void updateView (kpView *v, const QRect &viewRect);
+ void updateView (kpView *v, int x, int y, int w, int h);
+ void updateView (kpView *v, const QRegion &viewRegion);
+ void updateViewRectangleEdges (kpView *v, const QRect &viewRect);
+
+ void updateViews ();
+ void updateViews (const QRect &docRect);
+ void updateViews (int x, int y, int w, int h);
+
+ void adjustViewsToEnvironment ();
+
+private:
+ // don't use
+ kpViewManager (const kpViewManager &);
+ bool operator= (const kpViewManager &);
+
+ kpDocument *document () const;
+
+ kpMainWindow *m_mainWindow;
+ QPtrList <kpView> m_views;
+ QCursor m_cursor;
+
+ kpTempPixmap *m_tempPixmap;
+ kpView *m_viewUnderCursor;
+
+ bool m_selectionBorderVisible;
+ bool m_selectionBorderFinished;
+
+ // There is no need to maintain binary compatibility at this stage.
+ // The d-pointer is just so that you can experiment without recompiling
+ // the kitchen sink.
+ class kpViewManagerPrivate *d;
+};
+
+#endif // __kpviewmanager_h__
diff --git a/kolourpaint/kpviewscrollablecontainer.cpp b/kolourpaint/kpviewscrollablecontainer.cpp
new file mode 100644
index 00000000..637f71b7
--- /dev/null
+++ b/kolourpaint/kpviewscrollablecontainer.cpp
@@ -0,0 +1,1390 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG_KP_VIEW_SCROLLABLE_CONTAINER 0
+
+#include <kpviewscrollablecontainer.h>
+
+#include <qcursor.h>
+#include <qpainter.h>
+#include <qpen.h>
+#include <qpixmap.h>
+#include <qtimer.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kpdefs.h>
+#include <kppixmapfx.h>
+#include <kpview.h>
+#include <kpwidgetmapper.h>
+
+
+// (Pulled from out of Thurston's hat)
+static const int DragScrollLeftTopMargin = 0;
+static const int DragScrollRightBottomMargin = 16; // scrollbarish
+static const int DragScrollInterval = 1000 / 10;
+static const int DragScrollInitialInterval = DragScrollInterval * 2;
+static const int DragScrollNumPixels = 5;
+static const int DragDistanceFromRectMaxFor1stMultiplier = 50;
+static const int DragDistanceFromRectMaxFor2ndMultiplier = 100;
+
+static const int GripSize = 7;
+static const int GripHandleSize = 7;
+
+
+kpGrip::kpGrip (GripType type,
+ QWidget *parent, const char *name)
+ : QWidget (parent, name),
+ m_type (type),
+ m_startPoint (KP_INVALID_POINT),
+ m_currentPoint (KP_INVALID_POINT),
+ m_shouldReleaseMouseButtons (false)
+{
+ setCursor (cursorForType (m_type));
+
+ setMouseTracking (true); // mouseMoveEvent's even when no mousebtn down
+
+ updatePixmap ();
+}
+
+kpGrip::~kpGrip ()
+{
+}
+
+
+// public
+kpGrip::GripType kpGrip::type () const
+{
+ return m_type;
+}
+
+
+// public static
+const QCursor &kpGrip::cursorForType (GripType type)
+{
+ switch (type)
+ {
+ case Bottom:
+ return Qt::sizeVerCursor;
+ break; // one day you'll forget
+
+ case Right:
+ return Qt::sizeHorCursor;
+ break; // one day you'll forget
+
+ case BottomRight:
+ return Qt::sizeFDiagCursor;
+ break; // one day you'll forget
+ }
+
+ return Qt::arrowCursor;
+}
+
+
+// public
+QRect kpGrip::hotRect (bool toGlobal) const
+{
+ QRect ret;
+
+ switch (m_type)
+ {
+ case Bottom:
+ {
+ const int handleX = (width () - GripHandleSize) / 2;
+ ret = QRect (handleX, 0,
+ GripHandleSize, height ());
+ break;
+ }
+ case Right:
+ {
+ const int handleY = (height () - GripHandleSize) / 2;
+ ret = QRect (0, handleY,
+ width (), GripHandleSize);
+ break;
+ }
+ case BottomRight:
+ // pixmap all opaque
+ ret = rect ();
+ break;
+
+ default:
+ return QRect ();
+ }
+
+ return (toGlobal ? QRect (mapToGlobal (ret.topLeft ()),
+ mapToGlobal (ret.bottomRight ()))
+ : ret);
+}
+
+
+// public
+bool kpGrip::isDrawing () const
+{
+ return (m_startPoint != KP_INVALID_POINT);
+}
+
+
+// public
+QString kpGrip::haventBegunDrawUserMessage () const
+{
+ return i18n ("Left drag the handle to resize the image.");
+}
+
+
+// public
+QString kpGrip::userMessage () const
+{
+ return m_userMessage;
+}
+
+// public
+void kpGrip::setUserMessage (const QString &message)
+{
+ // Don't do NOP checking here since another grip might have changed
+ // the message so an apparent NOP for this grip is not a NOP in the
+ // global sense (kpViewScrollableContainer::slotGripStatusMessageChanged()).
+
+ m_userMessage = message;
+ emit statusMessageChanged (message);
+}
+
+
+// protected
+void kpGrip::updatePixmap ()
+{
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
+ kdDebug () << "kpGrip::updatePixmap() rect=" << rect () << endl;
+#endif
+ if (width () <= 0 || height () <= 0)
+ return;
+
+ QPixmap pixmap (width (), height ());
+ pixmap.fill (colorGroup ().highlight ());
+ kpPixmapFX::ensureTransparentAt (&pixmap, pixmap.rect ());
+ const QRect hr = hotRect ();
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
+ kdDebug () << "\thotRect=" << hr << endl;
+#endif
+ if (hr.isValid ())
+ kpPixmapFX::ensureOpaqueAt (&pixmap, hr);
+
+ setBackgroundPixmap (pixmap);
+ if (pixmap.mask ())
+ setMask (*pixmap.mask ());
+}
+
+
+// protected
+void kpGrip::cancel ()
+{
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
+ kdDebug () << "kpGrip::cancel()" << endl;
+#endif
+ if (m_currentPoint == KP_INVALID_POINT)
+ return;
+
+ m_startPoint = KP_INVALID_POINT;
+ m_currentPoint = KP_INVALID_POINT;
+
+ setUserMessage (i18n ("Resize Image: Let go of all the mouse buttons."));
+ setCursor (Qt::arrowCursor);
+ m_shouldReleaseMouseButtons = true;
+
+ releaseKeyboard ();
+ emit cancelledDraw ();
+}
+
+
+// protected virtual [base QWidget]
+void kpGrip::keyReleaseEvent (QKeyEvent *e)
+{
+ if (m_startPoint != KP_INVALID_POINT &&
+ e->key () == Qt::Key_Escape)
+ {
+ cancel ();
+ }
+}
+
+// protected virtual [base QWidget]
+void kpGrip::mousePressEvent (QMouseEvent *e)
+{
+ if (m_startPoint == KP_INVALID_POINT &&
+ (e->stateAfter () & Qt::MouseButtonMask) == Qt::LeftButton)
+ {
+ m_startPoint = e->pos ();
+ m_currentPoint = e->pos ();
+ emit beganDraw ();
+ grabKeyboard ();
+
+ setUserMessage (i18n ("Resize Image: Right click to cancel."));
+ setCursor (cursorForType (m_type));
+ }
+ else
+ {
+ if (m_startPoint != KP_INVALID_POINT)
+ cancel ();
+ }
+}
+
+// public
+QPoint kpGrip::viewDeltaPoint () const
+{
+ if (m_startPoint == KP_INVALID_POINT)
+ return KP_INVALID_POINT;
+
+ const QPoint point = mapFromGlobal (QCursor::pos ());
+
+ // TODO: this is getting out of sync with m_currentPoint
+
+ return QPoint (((m_type & Right) ? point.x () - m_startPoint.x () : 0),
+ ((m_type & Bottom) ? point.y () - m_startPoint.y () : 0));
+
+}
+
+// public
+void kpGrip::mouseMovedTo (const QPoint &point, bool dueToDragScroll)
+{
+ if (m_startPoint == KP_INVALID_POINT)
+ return;
+
+ m_currentPoint = point;
+
+ emit continuedDraw (((m_type & Right) ? point.x () - m_startPoint.x () : 0),
+ ((m_type & Bottom) ? point.y () - m_startPoint.y () : 0),
+ dueToDragScroll);
+}
+
+// protected virtual [base QWidget]
+void kpGrip::mouseMoveEvent (QMouseEvent *e)
+{
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
+ kdDebug () << "kpGrip::mouseMoveEvent() m_startPoint=" << m_startPoint
+ << " stateAfter=" << e->stateAfter ()
+ << endl;
+#endif
+
+ if (m_startPoint == KP_INVALID_POINT)
+ {
+ if ((e->stateAfter () & Qt::MouseButtonMask) == 0)
+ setUserMessage (haventBegunDrawUserMessage ());
+ return;
+ }
+
+ mouseMovedTo (e->pos (), false/*not due to drag scroll*/);
+}
+
+// protected virtual [base QWidget]
+void kpGrip::mouseReleaseEvent (QMouseEvent *e)
+{
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
+ kdDebug () << "kpGrip::mouseReleaseEvent() m_startPoint=" << m_startPoint
+ << " stateAfter=" << e->stateAfter ()
+ << endl;
+#endif
+
+ if (m_startPoint != KP_INVALID_POINT)
+ {
+ const int dx = m_currentPoint.x () - m_startPoint.x (),
+ dy = m_currentPoint.y () - m_startPoint.y ();
+
+ m_currentPoint = KP_INVALID_POINT;
+ m_startPoint = KP_INVALID_POINT;
+
+ releaseKeyboard ();
+ emit endedDraw ((m_type & Right) ? dx : 0,
+ (m_type & Bottom) ? dy : 0);
+ }
+
+ if ((e->stateAfter () & Qt::MouseButtonMask) == 0)
+ {
+ m_shouldReleaseMouseButtons = false;
+ setUserMessage (QString::null);
+ setCursor (cursorForType (m_type));
+
+ releaseKeyboard ();
+ emit releasedAllButtons ();
+ }
+}
+
+
+// protected virtual [base QWidget]
+void kpGrip::resizeEvent (QResizeEvent *)
+{
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
+ kdDebug () << "kpGrip::resizeEvent()" << endl;
+#endif
+ updatePixmap ();
+}
+
+
+// protected virtual [base QWidget]
+void kpGrip::enterEvent (QEvent * /*e*/)
+{
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
+ kdDebug () << "kpGrip::enterEvent()"
+ << " m_startPoint=" << m_startPoint
+ << " shouldReleaseMouseButtons="
+ << m_shouldReleaseMouseButtons << endl;
+#endif
+
+ if (m_startPoint == KP_INVALID_POINT &&
+ !m_shouldReleaseMouseButtons)
+ {
+ #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
+ kdDebug () << "\tsending message" << endl;
+ #endif
+ setUserMessage (haventBegunDrawUserMessage ());
+ }
+}
+
+// protected virtual [base QWidget]
+void kpGrip::leaveEvent (QEvent * /*e*/)
+{
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
+ kdDebug () << "kpGrip::leaveEvent()"
+ << " m_startPoint=" << m_startPoint
+ << " shouldReleaseMouseButtons="
+ << m_shouldReleaseMouseButtons << endl;
+#endif
+ if (m_startPoint == KP_INVALID_POINT &&
+ !m_shouldReleaseMouseButtons)
+ {
+ setUserMessage (QString::null);
+ }
+}
+
+
+// protected virtual [base QWidget]
+void kpGrip::paintEvent (QPaintEvent *e)
+{
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER && 0
+ kdDebug () << "kpGrip::paintEvent(" << e->rect () << ")" << endl;
+#endif
+ QWidget::paintEvent (e);
+}
+
+
+// TODO: Are we checking for m_view == 0 often enough?
+kpViewScrollableContainer::kpViewScrollableContainer (kpMainWindow *parent,
+ const char *name)
+ : QScrollView ((QWidget *) parent, name, Qt::WStaticContents | Qt::WNoAutoErase),
+ m_mainWindow (parent),
+ m_contentsXSoon (-1), m_contentsYSoon (-1),
+ m_view (0),
+ m_bottomGrip (new kpGrip (kpGrip::Bottom, viewport (), "Bottom Grip")),
+ m_rightGrip (new kpGrip (kpGrip::Right, viewport (), "Right Grip")),
+ m_bottomRightGrip (new kpGrip (kpGrip::BottomRight, viewport (), "BottomRight Grip")),
+ m_docResizingGrip (0),
+ m_dragScrollTimer (new QTimer (this)),
+ m_zoomLevel (100),
+ m_scrollTimerRunOnce (false),
+ m_resizeRoundedLastViewX (-1), m_resizeRoundedLastViewY (-1),
+ m_resizeRoundedLastViewDX (0), m_resizeRoundedLastViewDY (0),
+ m_haveMovedFromOriginalDocSize (false)
+
+{
+ m_bottomGrip->setFixedHeight (GripSize);
+ m_bottomGrip->hide ();
+ addChild (m_bottomGrip);
+ connectGripSignals (m_bottomGrip);
+
+ m_rightGrip->setFixedWidth (GripSize);
+ m_rightGrip->hide ();
+ addChild (m_rightGrip);
+ connectGripSignals (m_rightGrip);
+
+ m_bottomRightGrip->setFixedSize (GripSize, GripSize);
+ m_bottomRightGrip->hide ();
+ addChild (m_bottomRightGrip);
+ connectGripSignals (m_bottomRightGrip);
+
+
+ connect (this, SIGNAL (contentsMoving (int, int)),
+ this, SLOT (slotContentsMoving (int, int)));
+
+ connect (m_dragScrollTimer, SIGNAL (timeout ()),
+ this, SLOT (slotDragScroll ()));
+}
+
+kpViewScrollableContainer::~kpViewScrollableContainer ()
+{
+}
+
+
+// public
+int kpViewScrollableContainer::contentsXSoon ()
+{
+ if (m_contentsXSoon < 0)
+ return contentsX ();
+ else
+ return m_contentsXSoon;
+}
+
+// public
+int kpViewScrollableContainer::contentsYSoon ()
+{
+ if (m_contentsYSoon < 0)
+ return contentsY ();
+ else
+ return m_contentsYSoon;
+}
+
+
+// protected
+void kpViewScrollableContainer::connectGripSignals (kpGrip *grip)
+{
+ connect (grip, SIGNAL (beganDraw ()),
+ this, SLOT (slotGripBeganDraw ()));
+ connect (grip, SIGNAL (continuedDraw (int, int, bool)),
+ this, SLOT (slotGripContinuedDraw (int, int, bool)));
+ connect (grip, SIGNAL (cancelledDraw ()),
+ this, SLOT (slotGripCancelledDraw ()));
+ connect (grip, SIGNAL (endedDraw (int, int)),
+ this, SLOT (slotGripEndedDraw (int, int)));
+
+ connect (grip, SIGNAL (statusMessageChanged (const QString &)),
+ this, SLOT (slotGripStatusMessageChanged (const QString &)));
+
+ connect (grip, SIGNAL (releasedAllButtons ()),
+ this, SLOT (recalculateStatusMessage ()));
+}
+
+
+// public
+QSize kpViewScrollableContainer::newDocSize () const
+{
+ return newDocSize (m_resizeRoundedLastViewDX,
+ m_resizeRoundedLastViewDY);
+}
+
+// public
+bool kpViewScrollableContainer::haveMovedFromOriginalDocSize () const
+{
+ return m_haveMovedFromOriginalDocSize;
+}
+
+// public
+QString kpViewScrollableContainer::statusMessage () const
+{
+ return m_gripStatusMessage;
+}
+
+// public
+void kpViewScrollableContainer::clearStatusMessage ()
+{
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER && 1
+ kdDebug () << "kpViewScrollableContainer::clearStatusMessage()" << endl;
+#endif
+ m_bottomRightGrip->setUserMessage (QString::null);
+ m_bottomGrip->setUserMessage (QString::null);
+ m_rightGrip->setUserMessage (QString::null);
+}
+
+
+// protected
+QSize kpViewScrollableContainer::newDocSize (int viewDX, int viewDY) const
+{
+ if (!m_view)
+ return QSize ();
+
+ if (!docResizingGrip ())
+ return QSize ();
+
+ const int docX = (int) m_view->transformViewToDocX (m_view->width () + viewDX);
+ const int docY = (int) m_view->transformViewToDocY (m_view->height () + viewDY);
+
+ return QSize (QMAX (1, docX), QMAX (1, docY));
+}
+
+
+// protected
+void kpViewScrollableContainer::calculateDocResizingGrip ()
+{
+ if (m_bottomRightGrip->isDrawing ())
+ m_docResizingGrip = m_bottomRightGrip;
+ else if (m_bottomGrip->isDrawing ())
+ m_docResizingGrip = m_bottomGrip;
+ else if (m_rightGrip->isDrawing ())
+ m_docResizingGrip = m_rightGrip;
+ else
+ m_docResizingGrip = 0;
+}
+
+// protected
+kpGrip *kpViewScrollableContainer::docResizingGrip () const
+{
+ return m_docResizingGrip;
+}
+
+
+// protected
+int kpViewScrollableContainer::bottomResizeLineWidth () const
+{
+ if (!docResizingGrip ())
+ return -1;
+
+ if (!m_view)
+ return -1;
+
+ if (docResizingGrip ()->type () & kpGrip::Bottom)
+ return QMAX (m_view->zoomLevelY () / 100, 1);
+ else
+ return 1;
+}
+
+// protected
+int kpViewScrollableContainer::rightResizeLineWidth () const
+{
+ if (!docResizingGrip ())
+ return -1;
+
+ if (!m_view)
+ return -1;
+
+ if (docResizingGrip ()->type () & kpGrip::Right)
+ return QMAX (m_view->zoomLevelX () / 100, 1);
+ else
+ return 1;
+}
+
+
+// protected
+QRect kpViewScrollableContainer::bottomResizeLineRect () const
+{
+ if (m_resizeRoundedLastViewX < 0 || m_resizeRoundedLastViewY < 0)
+ return QRect ();
+
+ return QRect (QPoint (0,
+ m_resizeRoundedLastViewY),
+ QPoint (m_resizeRoundedLastViewX - 1,
+ m_resizeRoundedLastViewY + bottomResizeLineWidth () - 1));
+}
+
+// protected
+QRect kpViewScrollableContainer::rightResizeLineRect () const
+{
+ if (m_resizeRoundedLastViewX < 0 || m_resizeRoundedLastViewY < 0)
+ return QRect ();
+
+ return QRect (QPoint (m_resizeRoundedLastViewX,
+ 0),
+ QPoint (m_resizeRoundedLastViewX + rightResizeLineWidth () - 1,
+ m_resizeRoundedLastViewY - 1));
+}
+
+// protected
+QRect kpViewScrollableContainer::bottomRightResizeLineRect () const
+{
+ if (m_resizeRoundedLastViewX < 0 || m_resizeRoundedLastViewY < 0)
+ return QRect ();
+
+ return QRect (QPoint (m_resizeRoundedLastViewX,
+ m_resizeRoundedLastViewY),
+ QPoint (m_resizeRoundedLastViewX + rightResizeLineWidth () - 1,
+ m_resizeRoundedLastViewY + bottomResizeLineWidth () - 1));
+}
+
+
+// TODO: are these 2 correct? Remember that viewport()->x() == 1, viewport()->y() == 1
+
+// protected
+QPoint kpViewScrollableContainer::mapViewToViewport (const QPoint &viewPoint)
+{
+ return viewPoint - QPoint (contentsX (), contentsY ());
+}
+
+// protected
+QRect kpViewScrollableContainer::mapViewToViewport (const QRect &viewRect)
+{
+ if (!viewRect.isValid ())
+ return QRect ();
+
+ QRect ret = viewRect;
+ ret.moveBy (-contentsX (), -contentsY ());
+ return ret;
+}
+
+
+// protected
+QRect kpViewScrollableContainer::mapViewportToGlobal (const QRect &viewportRect)
+{
+ return kpWidgetMapper::toGlobal (viewport (), viewportRect);
+}
+
+// protected
+QRect kpViewScrollableContainer::mapViewToGlobal (const QRect &viewRect)
+{
+ return mapViewportToGlobal (mapViewToViewport (viewRect));
+}
+
+
+// protected
+void kpViewScrollableContainer::repaintWidgetAtResizeLineViewRect (
+ QWidget *widget, const QRect &resizeLineViewRect)
+{
+ const QRect resizeLineGlobalRect = mapViewToGlobal (resizeLineViewRect);
+ const QRect widgetGlobalRect = kpWidgetMapper::toGlobal (widget,
+ widget->rect ());
+
+ const QRect redrawGlobalRect =
+ resizeLineGlobalRect.intersect (widgetGlobalRect);
+
+ const QRect redrawWidgetRect =
+ kpWidgetMapper::fromGlobal (widget, redrawGlobalRect);
+
+
+ if (redrawWidgetRect.isValid ())
+ {
+ // TODO: should be "!widget->testWFlags (Qt::WRepaintNoErase)"
+ // but for some reason, doesn't work for viewport().
+ const bool erase = !dynamic_cast <kpView *> (widget);
+ widget->repaint (redrawWidgetRect, erase);
+ }
+}
+
+// protected
+void kpViewScrollableContainer::repaintWidgetAtResizeLines (QWidget *widget)
+{
+ repaintWidgetAtResizeLineViewRect (widget, rightResizeLineRect ());
+ repaintWidgetAtResizeLineViewRect (widget, bottomResizeLineRect ());
+ repaintWidgetAtResizeLineViewRect (widget, bottomRightResizeLineRect ());
+}
+
+// protected
+void kpViewScrollableContainer::eraseResizeLines ()
+{
+ if (m_resizeRoundedLastViewX >= 0 && m_resizeRoundedLastViewY >= 0)
+ {
+ repaintWidgetAtResizeLines (viewport ());
+ repaintWidgetAtResizeLines (m_view);
+
+ repaintWidgetAtResizeLines (m_bottomGrip);
+ repaintWidgetAtResizeLines (m_rightGrip);
+ repaintWidgetAtResizeLines (m_bottomRightGrip);
+ }
+}
+
+
+// protected
+void kpViewScrollableContainer::drawResizeLines ()
+{
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER && 0
+ kdDebug () << "kpViewScrollableContainer::drawResizeLines()"
+ << " lastViewX=" << m_resizeRoundedLastViewX
+ << " lastViewY=" << m_resizeRoundedLastViewY
+ << endl;
+#endif
+
+
+ QPainter p (viewport (), true/*unclipped*/);
+ p.setRasterOp (Qt::NotROP);
+
+ const QRect rightRect = rightResizeLineRect ();
+ if (rightRect.isValid ())
+ p.fillRect (mapViewToViewport (rightRect), Qt::white);
+
+ const QRect bottomRect = bottomResizeLineRect ();
+ if (bottomRect.isValid ())
+ p.fillRect (mapViewToViewport (bottomRect), Qt::white);
+
+ const QRect bottomRightRect = bottomRightResizeLineRect ();
+ if (bottomRightRect.isValid ())
+ p.fillRect (mapViewToViewport (bottomRightRect), Qt::white);
+
+ p.end ();
+}
+
+
+// protected
+void kpViewScrollableContainer::updateResizeLines (int viewX, int viewY,
+ int viewDX, int viewDY)
+{
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER && 0
+ kdDebug () << "kpViewScrollableContainer::updateResizeLines("
+ << viewX << "," << viewY << ")"
+ << " oldViewX=" << m_resizeRoundedLastViewX
+ << " oldViewY=" << m_resizeRoundedLastViewY
+ << " viewDX=" << viewDX
+ << " viewDY=" << viewDY
+ << endl;
+#endif
+
+ eraseResizeLines ();
+
+
+ if (viewX >= 0 && viewY >= 0)
+ {
+ m_resizeRoundedLastViewX = (int) m_view->transformDocToViewX ((int) m_view->transformViewToDocX (viewX));
+ m_resizeRoundedLastViewY = (int) m_view->transformDocToViewY ((int) m_view->transformViewToDocY (viewY));
+
+ m_resizeRoundedLastViewDX = viewDX;
+ m_resizeRoundedLastViewDY = viewDY;
+ }
+ else
+ {
+ m_resizeRoundedLastViewX = -1;
+ m_resizeRoundedLastViewY = -1;
+
+ m_resizeRoundedLastViewDX = 0;
+ m_resizeRoundedLastViewDY = 0;
+ }
+
+ // TODO: This is suboptimal since if another window pops up on top of
+ // KolourPaint then disappears, the lines are not redrawn
+ // (although this doesn't happen very frequently since we grab the
+ // keyboard and mouse when resizing):
+ //
+ // e.g. sleep 5 && gedit & sleep 10 && killall gedit
+ //
+ // Should be done in the paintEvent's of every child of the
+ // scrollview.
+ drawResizeLines ();
+}
+
+
+// protected slot
+void kpViewScrollableContainer::slotGripBeganDraw ()
+{
+ if (!m_view)
+ return;
+
+ calculateDocResizingGrip ();
+
+ m_haveMovedFromOriginalDocSize = false;
+
+ updateResizeLines (m_view->width (), m_view->height (),
+ 0/*viewDX*/, 0/*viewDY*/);
+
+ emit beganDocResize ();
+}
+
+// protected slot
+void kpViewScrollableContainer::slotGripContinuedDraw (int inViewDX, int inViewDY,
+ bool dueToDragScroll)
+{
+ int viewDX = inViewDX,
+ viewDY = inViewDY;
+
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
+ kdDebug () << "kpViewScrollableContainer::slotGripContinuedDraw("
+ << viewDX << "," << viewDY << ") size="
+ << newDocSize (viewDX, viewDY)
+ << " dueToDragScroll=" << dueToDragScroll
+ << endl;
+#endif
+
+ if (!m_view)
+ return;
+
+ if (!dueToDragScroll &&
+ beginDragScroll (QPoint (), QPoint (), m_view->zoomLevelX ()))
+ {
+ const QPoint newViewDeltaPoint = docResizingGrip ()->viewDeltaPoint ();
+ viewDX = newViewDeltaPoint.x ();
+ viewDY = newViewDeltaPoint.y ();
+ #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
+ kdDebug () << "\tdrag scrolled - new view delta point="
+ << newViewDeltaPoint
+ << endl;
+ #endif
+ }
+
+ m_haveMovedFromOriginalDocSize = true;
+
+ updateResizeLines (QMAX (1, QMAX (m_view->width () + viewDX, (int) m_view->transformDocToViewX (1))),
+ QMAX (1, QMAX (m_view->height () + viewDY, (int) m_view->transformDocToViewY (1))),
+ viewDX, viewDY);
+
+ emit continuedDocResize (newDocSize ());
+}
+
+// protected slot
+void kpViewScrollableContainer::slotGripCancelledDraw ()
+{
+ m_haveMovedFromOriginalDocSize = false;
+
+ updateResizeLines (-1, -1, 0, 0);
+
+ calculateDocResizingGrip ();
+
+ emit cancelledDocResize ();
+
+ endDragScroll ();
+}
+
+// protected slot
+void kpViewScrollableContainer::slotGripEndedDraw (int viewDX, int viewDY)
+{
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
+ kdDebug () << "kpViewScrollableContainer::slotGripEndedDraw("
+ << viewDX << "," << viewDY << ") size="
+ << newDocSize (viewDX, viewDY)
+ << endl;
+#endif
+
+ if (!m_view)
+ return;
+
+ const QSize newSize = newDocSize (viewDX, viewDY);
+
+ m_haveMovedFromOriginalDocSize = false;
+
+ // must erase lines before view size changes
+ updateResizeLines (-1, -1, 0, 0);
+
+ calculateDocResizingGrip ();
+
+ emit endedDocResize (newSize);
+
+ endDragScroll ();
+}
+
+
+// protected slot
+void kpViewScrollableContainer::slotGripStatusMessageChanged (const QString &string)
+{
+ if (string == m_gripStatusMessage)
+ return;
+
+ m_gripStatusMessage = string;
+ emit statusMessageChanged (string);
+}
+
+
+// public slot
+void kpViewScrollableContainer::recalculateStatusMessage ()
+{
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
+ kdDebug () << "kpViewScrollabelContainer::recalculateStatusMessage()" << endl;
+ kdDebug () << "\tQCursor::pos=" << QCursor::pos ()
+ << " global visibleRect="
+ << kpWidgetMapper::toGlobal (this,
+ QRect (0, 0, visibleWidth (), visibleHeight ()))
+ << " brGrip.hotRect=" << m_bottomRightGrip->hotRect (true)
+ << " bGrip.hotRect=" << m_bottomGrip->hotRect (true)
+ << " rGrip.hotRect=" << m_rightGrip->hotRect (true)
+ << endl;
+#endif
+
+ // HACK: After dragging to a new size, handles move so that they are now
+ // under the mouse pointer but no mouseMoveEvent() is generated for
+ // any grip. This also handles the case of cancelling over any
+ // grip.
+ //
+ if (kpWidgetMapper::toGlobal (this,
+ QRect (0, 0, visibleWidth (), visibleHeight ()))
+ .contains (QCursor::pos ()))
+ {
+ if (m_bottomRightGrip->isShown () &&
+ m_bottomRightGrip->hotRect (true/*to global*/)
+ .contains (QCursor::pos ()))
+ {
+ m_bottomRightGrip->setUserMessage (i18n ("Left drag the handle to resize the image."));
+ }
+ else if (m_bottomGrip->isShown () &&
+ m_bottomGrip->hotRect (true/*to global*/)
+ .contains (QCursor::pos ()))
+ {
+ m_bottomGrip->setUserMessage (i18n ("Left drag the handle to resize the image."));
+ }
+ else if (m_rightGrip->isShown () &&
+ m_rightGrip->hotRect (true/*to global*/)
+ .contains (QCursor::pos ()))
+ {
+ m_rightGrip->setUserMessage (i18n ("Left drag the handle to resize the image."));
+ }
+ else
+ {
+ clearStatusMessage ();
+ }
+ }
+ else
+ {
+ clearStatusMessage ();
+ }
+}
+
+
+// protected slot
+void kpViewScrollableContainer::slotContentsMoving (int x, int y)
+{
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
+ kdDebug () << "kpViewScrollableContainer::slotContentsMoving("
+ << x << "," << y << ")"
+ << " contentsX=" << contentsX ()
+ << " contentsY=" << contentsY () << endl;
+#endif
+
+ m_contentsXSoon = x, m_contentsYSoon = y;
+ emit contentsMovingSoon (m_contentsXSoon, m_contentsYSoon);
+
+ // Reduce flicker - don't let QScrollView scroll to-be-erased lines
+ eraseResizeLines ();
+
+ QTimer::singleShot (0, this, SLOT (slotContentsMoved ()));
+}
+
+// protected slot
+void kpViewScrollableContainer::slotContentsMoved ()
+{
+ m_contentsXSoon = m_contentsYSoon = -1;
+
+ kpGrip *grip = docResizingGrip ();
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
+ kdDebug () << "kpViewScrollableContainer::slotContentsMoved()"
+ << " grip=" << grip
+ << " contentsX=" << contentsX ()
+ << " contentsY=" << contentsY () << endl;
+#endif
+ if (!grip)
+ return;
+
+ grip->mouseMovedTo (grip->mapFromGlobal (QCursor::pos ()),
+ true/*moved due to drag scroll*/);
+}
+
+
+// protected
+void kpViewScrollableContainer::disconnectViewSignals ()
+{
+ disconnect (m_view, SIGNAL (sizeChanged (const QSize &)),
+ this, SLOT (updateGrips ()));
+ disconnect (m_view, SIGNAL (destroyed ()),
+ this, SLOT (slotViewDestroyed ()));
+}
+
+// protected
+void kpViewScrollableContainer::connectViewSignals ()
+{
+ connect (m_view, SIGNAL (sizeChanged (const QSize &)),
+ this, SLOT (updateGrips ()));
+ connect (m_view, SIGNAL (destroyed ()),
+ this, SLOT (slotViewDestroyed ()));
+}
+
+
+// public virtual [base QScrollView]
+void kpViewScrollableContainer::addChild (QWidget *widget, int x, int y)
+{
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
+ kdDebug () << "kpViewScrollableContainer::addChild(" << widget
+ << "," << x << "," << y << endl;
+#endif
+
+ QScrollView::addChild (widget, x, y);
+
+ kpView *view = dynamic_cast <kpView *> (widget);
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
+ kdDebug () << "\tcast to kpView: " << view << endl;
+#endif
+ if (view)
+ {
+ setView (view);
+ }
+}
+
+
+// public
+kpView *kpViewScrollableContainer::view () const
+{
+ return m_view;
+}
+
+// public
+void kpViewScrollableContainer::setView (kpView *view)
+{
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
+ kdDebug () << "kpViewScrollableContainer::setView(" << view << ")" << endl;
+#endif
+
+ if (m_view == view)
+ return;
+
+ if (m_view)
+ {
+ disconnectViewSignals ();
+ }
+
+ m_view = view;
+
+ updateGrips ();
+
+ if (m_view)
+ {
+ connectViewSignals ();
+ }
+}
+
+
+// public slot
+void kpViewScrollableContainer::updateGrips ()
+{
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
+ kdDebug () << "kpViewScrollableContainer::updateGrips() m_view="
+ << m_view << endl;
+#endif
+
+ if (m_view)
+ {
+ m_bottomGrip->setFixedWidth (m_view->width ());
+ moveChild (m_bottomGrip, 0, m_view->height ());
+
+ m_rightGrip->setFixedHeight (m_view->height ());
+ moveChild (m_rightGrip, m_view->width (), 0);
+
+ moveChild (m_bottomRightGrip, m_view->width (), m_view->height ());
+ }
+
+ m_bottomGrip->setShown (bool (m_view));
+ m_rightGrip->setShown (bool (m_view));
+ m_bottomRightGrip->setShown (bool (m_view));
+
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
+ kdDebug () << "\tcontentsRect=" << contentsRect ()
+ << " visibleRect=" << visibleRect ()
+ << " viewportRect=" << viewport ()->rect ()
+ << endl;
+#endif
+
+ if (m_view)
+ {
+ resizeContents (m_view->width () + m_rightGrip->width (),
+ m_view->height () + m_bottomGrip->height ());
+ }
+ else
+ {
+ resizeContents (0, 0);
+ }
+
+ recalculateStatusMessage ();
+}
+
+// protected slot
+void kpViewScrollableContainer::slotViewDestroyed ()
+{
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
+ kdDebug () << "kpViewScrollableContainer::slotViewDestroyed() m_view="
+ << m_view << endl;
+#endif
+
+ m_view = 0;
+ updateGrips ();
+}
+
+
+// public slot
+bool kpViewScrollableContainer::beginDragScroll (const QPoint &/*docPoint*/,
+ const QPoint &/*lastDocPoint*/,
+ int zoomLevel,
+ bool *didSomething)
+{
+ if (didSomething)
+ *didSomething = false;
+
+ m_zoomLevel = zoomLevel;
+
+ const QPoint p = mapFromGlobal (QCursor::pos ());
+
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
+ kdDebug () << "kpViewScrollableContainer::beginDragScroll() p=" << p
+ << " dragScrollTimerRunOnce=" << m_scrollTimerRunOnce
+ << endl;
+#endif
+
+ bool stopDragScroll = true;
+ bool scrolled = false;
+
+ if (!noDragScrollRect ().contains (p))
+ {
+ if (m_dragScrollTimer->isActive ())
+ {
+ if (m_scrollTimerRunOnce)
+ {
+ scrolled = slotDragScroll ();
+ }
+ }
+ else
+ {
+ m_scrollTimerRunOnce = false;
+ m_dragScrollTimer->start (DragScrollInitialInterval);
+ }
+
+ stopDragScroll = false;
+ }
+
+ if (stopDragScroll)
+ m_dragScrollTimer->stop ();
+
+ if (didSomething)
+ *didSomething = scrolled;
+
+ return scrolled;
+}
+
+// public slot
+bool kpViewScrollableContainer::beginDragScroll (const QPoint &docPoint,
+ const QPoint &lastDocPoint,
+ int zoomLevel)
+{
+ return beginDragScroll (docPoint, lastDocPoint, zoomLevel,
+ 0/*don't want scrolled notification*/);
+}
+
+
+// public slot
+bool kpViewScrollableContainer::endDragScroll ()
+{
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
+ kdDebug () << "kpViewScrollableContainer::endDragScroll()" << endl;
+#endif
+
+ if (m_dragScrollTimer->isActive ())
+ {
+ m_dragScrollTimer->stop ();
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+static const int distanceFromRectToMultiplier (int dist)
+{
+ if (dist < 0)
+ return 0;
+ else if (dist < DragDistanceFromRectMaxFor1stMultiplier)
+ return 1;
+ else if (dist < DragDistanceFromRectMaxFor2ndMultiplier)
+ return 2;
+ else
+ return 4;
+}
+
+
+// protected slot
+bool kpViewScrollableContainer::slotDragScroll (bool *didSomething)
+{
+ bool scrolled = false;
+
+ if (didSomething)
+ *didSomething = false;
+
+
+ const QRect rect = noDragScrollRect ();
+ const QPoint pos = mapFromGlobal (QCursor::pos ());
+
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
+ kdDebug () << "kpViewScrollableContainer::slotDragScroll()"
+ << " noDragScrollRect=" << rect
+ << " pos=" << pos
+ << " contentsX=" << contentsX ()
+ << " contentsY=" << contentsY () << endl;
+#endif
+
+ int dx = 0, dy = 0;
+ int dxMultiplier = 0, dyMultiplier = 0;
+
+ if (pos.x () < rect.left ())
+ {
+ dx = -DragScrollNumPixels;
+ dxMultiplier = distanceFromRectToMultiplier (rect.left () - pos.x ());
+ }
+ else if (pos.x () > rect.right ())
+ {
+ dx = +DragScrollNumPixels;
+ dxMultiplier = distanceFromRectToMultiplier (pos.x () - rect.right ());
+ }
+
+ if (pos.y () < rect.top ())
+ {
+ dy = -DragScrollNumPixels;
+ dyMultiplier = distanceFromRectToMultiplier (rect.top () - pos.y ());
+ }
+ else if (pos.y () > rect.bottom ())
+ {
+ dy = +DragScrollNumPixels;
+ dyMultiplier = distanceFromRectToMultiplier (pos.y () - rect.bottom ());
+ }
+
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER && 0
+ kdDebug () << "kpViewScrollableContainer::slotDragScroll()"
+ << " dx=" << dx << " * " << dxMultiplier
+ << " dy=" << dy << " * " << dyMultiplier
+ << " zoomLevel=" << m_zoomLevel
+ << endl;
+#endif
+
+ dx *= dxMultiplier;// * QMAX (1, m_zoomLevel / 100);
+ dy *= dyMultiplier;// * QMAX (1, m_zoomLevel / 100);
+
+ if (dx || dy)
+ {
+ const int oldContentsX = contentsX (),
+ oldContentsY = contentsY ();
+
+ scrollBy (dx, dy);
+
+ #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER && 1
+ kdDebug () << "\tafter scrollBy():"
+ << " contentsX=" << contentsX ()
+ << " contentsY=" << contentsY () << endl;
+ #endif
+
+ scrolled = (oldContentsX != contentsX () ||
+ oldContentsY != contentsY ());
+
+ if (scrolled)
+ {
+ QRegion region = QRect (contentsX (), contentsY (),
+ visibleWidth (), visibleHeight ());
+ region -= QRect (oldContentsX, oldContentsY,
+ visibleWidth (), visibleHeight ());
+
+ // Repaint newly exposed region immediately to reduce tearing
+ // of scrollView.
+ m_view->repaint (region, false/*no erase*/);
+ }
+ }
+
+
+ m_dragScrollTimer->changeInterval (DragScrollInterval);
+ m_scrollTimerRunOnce = true;
+
+
+ if (didSomething)
+ *didSomething = scrolled;
+
+ return scrolled;
+}
+
+// protected virtual [base QScrollView]
+void kpViewScrollableContainer::contentsDragMoveEvent (QDragMoveEvent *e)
+{
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
+ kdDebug () << "kpViewScrollableContainer::contentsDragMoveEvent"
+ << e->pos ()
+ << endl;
+#endif
+
+ QScrollView::contentsDragMoveEvent (e);
+}
+
+// protected slot
+bool kpViewScrollableContainer::slotDragScroll ()
+{
+ return slotDragScroll (0/*don't want scrolled notification*/);
+}
+
+
+// protected virtual [base QScrollView]
+void kpViewScrollableContainer::contentsMouseMoveEvent (QMouseEvent *e)
+{
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
+ kdDebug () << "kpViewScrollableContainer::contentsMouseMoveEvent"
+ << e->pos ()
+ << endl;
+#endif
+
+ QScrollView::contentsMouseMoveEvent (e);
+}
+
+// protected virtual [base QScrollView]
+void kpViewScrollableContainer::mouseMoveEvent (QMouseEvent *e)
+{
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
+ kdDebug () << "kpViewScrollableContainer::mouseMoveEvent"
+ << e->pos ()
+ << endl;
+#endif
+
+ QScrollView::mouseMoveEvent (e);
+}
+
+
+// protected virtual [base QScrollView]
+void kpViewScrollableContainer::contentsWheelEvent (QWheelEvent *e)
+{
+ e->ignore ();
+
+ if (m_view)
+ m_view->wheelEvent (e);
+
+ if (!e->isAccepted ())
+ QScrollView::contentsWheelEvent (e);
+}
+
+
+QRect kpViewScrollableContainer::noDragScrollRect () const
+{
+ return QRect (DragScrollLeftTopMargin, DragScrollLeftTopMargin,
+ width () - DragScrollLeftTopMargin - DragScrollRightBottomMargin,
+ height () - DragScrollLeftTopMargin - DragScrollRightBottomMargin);
+}
+
+// protected virtual [base QScrollView]
+bool kpViewScrollableContainer::eventFilter (QObject *watchedObject, QEvent *event)
+{
+ return QScrollView::eventFilter (watchedObject, event);
+}
+
+// protected virtual [base QScrollView]
+void kpViewScrollableContainer::viewportPaintEvent (QPaintEvent *e)
+{
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
+ kdDebug () << "kpViewScrollableContainer::viewportPaintEvent("
+ << e->rect ()
+ << ")" << endl;
+#endif
+
+ QScrollView::viewportPaintEvent (e);
+}
+
+// protected virtual [base QFrame]
+void kpViewScrollableContainer::paintEvent (QPaintEvent *e)
+{
+#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER && 0
+ kdDebug () << "kpViewScrollableContainer::paintEvent("
+ << e->rect ()
+ << ")" << endl;
+#endif
+
+ QScrollView::paintEvent (e);
+}
+
+// protected virtual [base QScrollView]
+void kpViewScrollableContainer::resizeEvent (QResizeEvent *e)
+{
+ QScrollView::resizeEvent (e);
+
+ emit resized ();
+}
+
+
+#include <kpviewscrollablecontainer.moc>
diff --git a/kolourpaint/kpviewscrollablecontainer.h b/kolourpaint/kpviewscrollablecontainer.h
new file mode 100644
index 00000000..203bbd1f
--- /dev/null
+++ b/kolourpaint/kpviewscrollablecontainer.h
@@ -0,0 +1,255 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef KP_VIEW_SCROLLABLE_CONTAINER_H
+#define KP_VIEW_SCROLLABLE_CONTAINER_H
+
+
+#include <qpoint.h>
+#include <qscrollview.h>
+#include <qsize.h>
+
+
+class QCursor;
+class QRect;
+class QTimer;
+
+class kpGrip;
+class kpView;
+class kpMainWindow;
+
+
+// TODO: refactor by sharing iface's with kpTool
+class kpGrip : public QWidget
+{
+Q_OBJECT
+
+public:
+ enum GripType
+ {
+ Right = 1, Bottom = 2,
+ BottomRight = Right | Bottom
+ };
+
+ kpGrip (GripType type,
+ QWidget *parent, const char *name = 0);
+ virtual ~kpGrip ();
+
+ GripType type () const;
+
+ static const QCursor &cursorForType (GripType type);
+
+ QRect hotRect (bool toGlobal = false) const;
+
+ bool isDrawing () const;
+
+signals:
+ void beganDraw ();
+ void continuedDraw (int viewDX, int viewDY, bool dueToDragScroll);
+ void cancelledDraw ();
+ void endedDraw (int viewDX, int viewDY);
+
+ void statusMessageChanged (const QString &string);
+
+ void releasedAllButtons ();
+
+public:
+ QString haventBegunDrawUserMessage () const;
+
+ QString userMessage () const;
+ void setUserMessage (const QString &message);
+
+protected:
+ void updatePixmap ();
+ void cancel ();
+
+protected:
+ virtual void keyReleaseEvent (QKeyEvent *e);
+ virtual void mousePressEvent (QMouseEvent *e);
+public:
+ QPoint viewDeltaPoint () const;
+ void mouseMovedTo (const QPoint &point, bool dueToDragScroll);
+protected:
+ virtual void mouseMoveEvent (QMouseEvent *e);
+ virtual void mouseReleaseEvent (QMouseEvent *e);
+ virtual void resizeEvent (QResizeEvent *e);
+
+ virtual void enterEvent (QEvent *e);
+ virtual void leaveEvent (QEvent *e);
+
+ virtual void paintEvent (QPaintEvent *e);
+
+protected:
+ GripType m_type;
+ QPoint m_startPoint, m_currentPoint;
+ QString m_userMessage;
+ bool m_shouldReleaseMouseButtons;
+};
+
+
+class kpViewScrollableContainer : public QScrollView
+{
+Q_OBJECT
+
+public:
+ kpViewScrollableContainer (kpMainWindow *parent, const char *name = 0);
+ virtual ~kpViewScrollableContainer ();
+
+ // Same as contentsX() and contentsY() except that after
+ // contentsMovingSoon() is emitted and before the scrollview actually
+ // scrolls, they return the would be values of contentsX() and
+ // contentsY() after scrolling.
+ int contentsXSoon ();
+ int contentsYSoon ();
+
+signals:
+ // connect to this instead of contentsMoving(int,int) so that
+ // contentsXSoon() and contentsYSoon() work
+ void contentsMovingSoon (int contentsX, int contentsY);
+
+ void beganDocResize ();
+ void continuedDocResize (const QSize &size);
+ void cancelledDocResize ();
+ void endedDocResize (const QSize &size);
+
+ // (string.isEmpty() if kpViewScrollableContainer has nothing to say)
+ void statusMessageChanged (const QString &string);
+
+ void resized ();
+
+public:
+ QSize newDocSize () const;
+ bool haveMovedFromOriginalDocSize () const;
+ QString statusMessage () const;
+ void clearStatusMessage ();
+
+protected:
+ void connectGripSignals (kpGrip *grip);
+
+ QSize newDocSize (int viewDX, int viewDY) const;
+
+ void calculateDocResizingGrip ();
+ kpGrip *docResizingGrip () const;
+
+ int bottomResizeLineWidth () const;
+ int rightResizeLineWidth () const;
+
+ QRect bottomResizeLineRect () const;
+ QRect rightResizeLineRect () const;
+ QRect bottomRightResizeLineRect () const;
+
+ QPoint mapViewToViewport (const QPoint &viewPoint);
+ QRect mapViewToViewport (const QRect &viewRect);
+
+ QRect mapViewportToGlobal (const QRect &viewportRect);
+ QRect mapViewToGlobal (const QRect &viewRect);
+
+ void repaintWidgetAtResizeLineViewRect (QWidget *widget,
+ const QRect &resizeLineViewRect);
+ void repaintWidgetAtResizeLines (QWidget *widget);
+ void eraseResizeLines ();
+
+ void drawResizeLines ();
+
+ void updateResizeLines (int viewX, int viewY,
+ int viewDX, int viewDY);
+
+protected slots:
+ void slotGripBeganDraw ();
+ void slotGripContinuedDraw (int viewDX, int viewDY, bool dueToScrollView);
+ void slotGripCancelledDraw ();
+ void slotGripEndedDraw (int viewDX, int viewDY);
+
+ void slotGripStatusMessageChanged (const QString &string);
+
+public slots:
+ void recalculateStatusMessage ();
+
+protected slots:
+ void slotContentsMoving (int x, int y);
+ void slotContentsMoved ();
+
+protected:
+ void disconnectViewSignals ();
+ void connectViewSignals ();
+
+public:
+ // Calls setView(<widget>) after adding <widget> if it's a kpView.
+ virtual void addChild (QWidget *widget, int x = 0, int y = 0);
+
+ kpView *view () const;
+ void setView (kpView *view);
+
+public slots:
+ void updateGrips ();
+protected slots:
+ void slotViewDestroyed ();
+
+public slots:
+ // TODO: Why the QPoint's?
+ // Why the need for view's zoomLevel? We have the view() anyway.
+ bool beginDragScroll (const QPoint &, const QPoint &,
+ int zoomLevel,
+ bool *didSomething);
+ bool beginDragScroll (const QPoint &, const QPoint &,
+ int zoomLevel);
+ bool endDragScroll ();
+
+protected slots:
+ bool slotDragScroll (bool *didSomething);
+ bool slotDragScroll ();
+
+protected:
+ QRect noDragScrollRect () const;
+
+ virtual void contentsDragMoveEvent (QDragMoveEvent *e);
+ virtual void contentsMouseMoveEvent (QMouseEvent *e);
+ virtual void contentsWheelEvent (QWheelEvent *e);
+ virtual void mouseMoveEvent (QMouseEvent *e);
+ virtual bool eventFilter (QObject *watchedObject, QEvent *e);
+ virtual void viewportPaintEvent (QPaintEvent *e);
+ virtual void paintEvent (QPaintEvent *e);
+ virtual void resizeEvent (QResizeEvent *e);
+
+protected:
+ kpMainWindow *m_mainWindow;
+ int m_contentsXSoon, m_contentsYSoon;
+ kpView *m_view;
+ kpGrip *m_bottomGrip, *m_rightGrip, *m_bottomRightGrip;
+ kpGrip *m_docResizingGrip;
+ QTimer *m_dragScrollTimer;
+ int m_zoomLevel;
+ bool m_scrollTimerRunOnce;
+ int m_resizeRoundedLastViewX, m_resizeRoundedLastViewY;
+ int m_resizeRoundedLastViewDX, m_resizeRoundedLastViewDY;
+ bool m_haveMovedFromOriginalDocSize;
+ QString m_gripStatusMessage;
+};
+
+
+#endif // KP_VIEW_SCROLLABLE_CONTAINER_H
diff --git a/kolourpaint/kpwidgetmapper.cpp b/kolourpaint/kpwidgetmapper.cpp
new file mode 100644
index 00000000..beb2624c
--- /dev/null
+++ b/kolourpaint/kpwidgetmapper.cpp
@@ -0,0 +1,76 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <kpwidgetmapper.h>
+
+#include <qpoint.h>
+#include <qrect.h>
+#include <qwidget.h>
+
+
+namespace kpWidgetMapper
+{
+
+
+QPoint fromGlobal (const QWidget *widget, const QPoint &point)
+{
+ if (!widget)
+ return point;
+
+ return widget->mapFromGlobal (point);
+}
+
+QRect fromGlobal (const QWidget *widget, const QRect &rect)
+{
+ if (!widget || !rect.isValid ())
+ return rect;
+
+ QPoint topLeft = fromGlobal (widget, rect.topLeft ());
+ return QRect (topLeft.x (), topLeft.y (), rect.width (), rect.height ());
+}
+
+
+QPoint toGlobal (const QWidget *widget, const QPoint &point)
+{
+ if (!widget)
+ return point;
+
+ return widget->mapToGlobal (point);
+}
+
+QRect toGlobal (const QWidget *widget, const QRect &rect)
+{
+ if (!widget || !rect.isValid ())
+ return rect;
+
+ QPoint topLeft = toGlobal (widget, rect.topLeft ());
+ return QRect (topLeft.x (), topLeft.y (), rect.width (), rect.height ());
+}
+
+
+} // namespace kpWidgetMapper {
diff --git a/kolourpaint/kpwidgetmapper.h b/kolourpaint/kpwidgetmapper.h
new file mode 100644
index 00000000..b5c4c412
--- /dev/null
+++ b/kolourpaint/kpwidgetmapper.h
@@ -0,0 +1,47 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef KP_WIDGET_MAPPER
+#define KP_WIDGET_MAPPER
+
+
+class QWidget;
+class QPoint;
+class QRect;
+
+
+namespace kpWidgetMapper
+{
+ QPoint fromGlobal (const QWidget *widget, const QPoint &point);
+ QRect fromGlobal (const QWidget *widget, const QRect &rect);
+
+ QPoint toGlobal (const QWidget *widget, const QPoint &point);
+ QRect toGlobal (const QWidget *widget, const QRect &rect);
+}
+
+
+#endif // KP_WIDGET_MAPPER
diff --git a/kolourpaint/patches/checkerboard-faster-render.diff b/kolourpaint/patches/checkerboard-faster-render.diff
new file mode 100644
index 00000000..8c9c6402
--- /dev/null
+++ b/kolourpaint/patches/checkerboard-faster-render.diff
@@ -0,0 +1,141 @@
+At 100% zoom: kpMainWindow::drawTransparentBackground() accounts for
+about 75% of kpView::paintEvent()'s time. Bottleneck is
+QPainter::fillRect(). QPainter::drawPixmap() seems much faster. For
+800x600, renderer goes from 10ms to 1ms.
+
+--- kpmainwindow.cpp 2004-08-05 02:10:38.000000000 +1000
++++ kpmainwindow.cpp 2004-09-29 11:24:45.000000000 +1000
+@@ -838,12 +838,116 @@
+ }
+
+
++#if 1
++// (indexed by [isPreview][parity])
++static QPixmap *checkerBoardCache [2][2] = {{0, 0}, {0, 0}};
++
++
++static int checkerBoardCellSize (bool isPreview)
++{
++ return !isPreview ? 16 : 10;
++}
++
++
++// public
++static QPixmap *createCheckerBoardCache (bool isPreview, bool parity)
++{
++ int cellSize = checkerBoardCellSize (isPreview);
++ const int rep = 2; // must be multiple of 2
++
++ QPixmap *newPixmap = new QPixmap (cellSize * rep, cellSize * rep);
++ QPainter painter (newPixmap);
++
++ int parityAsInt = parity ? 1 : 0;
++ for (int y = 0; y < rep; y++)
++ {
++ for (int x = 0; x < rep; x++)
++ {
++ QColor col;
++
++ if ((parityAsInt + x + y) % 2)
++ {
++ if (!isPreview)
++ col = QColor (213, 213, 213);
++ else
++ col = QColor (224, 224, 224);
++ }
++ else
++ col = Qt::white;
++
++ painter.fillRect (x * cellSize, y * cellSize,
++ cellSize, cellSize,
++ col);
++ }
++ }
++
++ painter.end ();
++ return newPixmap;
++}
++
++void kpMainWindow::drawTransparentBackground (QPainter *painter,
++ int /*viewWidth*/, int /*viewHeight*/,
++ const QRect &rect,
++ bool isPreview)
++{
++#if DEBUG_KP_MAIN_WINDOW && 1 || 1
++ kdDebug () << "\tkpMainWindow::drawTransparentBackground(rect="
++ << rect << ")" << endl;
++ QTime totalTimer; totalTimer.start ();
++#endif
++
++ int cellSize = checkerBoardCellSize (isPreview);
++
++
++ int starty = rect.y ();
++ if (starty % cellSize)
++ starty -= (starty % cellSize);
++
++ int startx = rect.x ();
++ if (startx % cellSize)
++ startx -= (startx % cellSize);
++
++
++ int parity = ((startx / cellSize + starty / cellSize) % 2) ? 1 : 0;
++
++ if (!checkerBoardCache [isPreview][parity])
++ {
++ checkerBoardCache [isPreview][parity] = createCheckerBoardCache (isPreview, parity);
++ }
++
++ QPixmap *tilePixmap = checkerBoardCache [isPreview][parity];
++ for (int y = starty; y <= rect.bottom (); y += tilePixmap->height ())
++ {
++ for (int x = startx; x <= rect.right (); x += tilePixmap->width ())
++ {
++ painter->drawPixmap (x - rect.x (), y - rect.y (), *tilePixmap);
++ }
++ }
++
++#if DEBUG_KP_MAIN_WINDOW && 1 || 1
++{
++ const int totalTimerElapsed = totalTimer.elapsed ();
++ kdDebug () << "\t\ttotal=" << totalTimerElapsed << endl;
++}
++#endif
++}
++
++
++#else
++
+ // public
+ void kpMainWindow::drawTransparentBackground (QPainter *painter,
+ int /*viewWidth*/, int /*viewHeight*/,
+ const QRect &rect,
+ bool isPreview)
+ {
++#if DEBUG_KP_MAIN_WINDOW && 1
++ kdDebug () << "\tkpMainWindow::drawTransparentBackground(rect="
++ << rect << ")" << endl;
++ QTime totalTimer; totalTimer.start ();
++#endif
++
++
+ const int cellSize = !isPreview ? 16 : 10;
+
+ int starty = rect.y ();
+@@ -877,8 +982,15 @@
+ }
+ }
+ painter->restore ();
+-}
+
++#if DEBUG_KP_MAIN_WINDOW && 1 || 1
++{
++ const int totalTimerElapsed = totalTimer.elapsed ();
++ kdDebug () << "\t\ttotal=" << totalTimerElapsed << endl;
++}
++#endif
++}
++#endif
+
+ // private slot
+ void kpMainWindow::slotUpdateCaption ()
diff --git a/kolourpaint/patches/color_eraser_speedup.diff b/kolourpaint/patches/color_eraser_speedup.diff
new file mode 100644
index 00000000..5e1ff7b7
--- /dev/null
+++ b/kolourpaint/patches/color_eraser_speedup.diff
@@ -0,0 +1,264 @@
+[probably no longer applies without modification]
+
+Attempts to improve the performance of the Color Eraser & Eraser
+by drawing only _unique_ rectangles across interpolation lines
+and by not drawing pixmaps when the user has a solid rectangular
+brush.
+
+- appears to decrease the performance of the Eraser (QRegion
+ overhead?).
+- reduces code clarity
+- unsure of whether it increases performance of Color Eraser
+ (sometimes it seems faster, sometimes not)
+
+Index: tools/kptoolpen.cpp
+===================================================================
+RCS file: /home/kde/kdenonbeta/kolourpaint/tools/kptoolpen.cpp,v
+retrieving revision 1.9
+diff -u -p -r1.9 kptoolpen.cpp
+--- tools/kptoolpen.cpp 6 Dec 2003 06:53:36 -0000 1.9
++++ tools/kptoolpen.cpp 6 Dec 2003 06:55:46 -0000
+@@ -34,12 +34,13 @@
+ #include <qapplication.h>
+ #include <qbitmap.h>
+ #include <qcursor.h>
+-#include <qimage.h>
+-#include <qpainter.h>
+-#include <qpixmap.h>
+ #if DEBUG_KP_TOOL_PEN
+ #include <qdatetime.h>
+ #endif
++#include <qimage.h>
++#include <qpainter.h>
++#include <qpixmap.h>
++#include <qregion.h>
+
+ #include <kdebug.h>
+ #include <klocale.h>
+@@ -416,31 +417,28 @@ void kpToolPen::draw (const QPoint &this
+ rect = neededRect (rect, m_brushPixmap [m_mouseButton].width ());
+
+ #if DEBUG_KP_TOOL_PEN
+- if (m_mode & WashesPixmaps)
+- {
+- kdDebug () << "Washing pixmap (w=" << rect.width ()
+- << ",h=" << rect.height () << ")" << endl;
+- }
++ kdDebug () << "kpToolPen::draw() interpolate: area (w=" << rect.width ()
++ << ",h=" << rect.height () << ")" << endl;
+ QTime timer;
+ int convAndWashTime;
+ #endif
+
+- QPixmap pixmap = document ()->getPixmapAt (rect);
+- QPainter painter (&pixmap);
++ // optimsation - only render intersections of rectangles once
++ bool delayedDraw = ((m_mode & SquareBrushes) &&
++ ((m_mode & WashesPixmaps) ||
++ (m_mode == Eraser)/*solid rectangular brush*/));
++
++
++ QPixmap pixmap;
++ QPainter painter;
++ pixmap = document ()->getPixmapAt (rect);
++ painter.begin (&pixmap);
+ painter.setPen (color (m_mouseButton));
+
+- QImage image;
+- if (m_mode & WashesPixmaps)
+- {
+ #if DEBUG_KP_TOOL_PEN
++ if (m_mode & WashesPixmaps)
+ timer.start ();
+ #endif
+- image = pixmap.convertToImage ();
+- #if DEBUG_KP_TOOL_PEN
+- convAndWashTime = timer.restart ();
+- kdDebug () << "\tconvert to image: " << convAndWashTime << " ms" << endl;
+- #endif
+- }
+
+ bool didSomething = false;
+
+@@ -453,10 +451,21 @@ void kpToolPen::draw (const QPoint &this
+ else if (m_mode & (DrawsPixmaps | WashesPixmaps))
+ {
+ QRgb colorToReplace;
++ QImage image;
++ QRegion region;
+
+ if (m_mode & WashesPixmaps)
++ {
+ colorToReplace = color (1 - m_mouseButton).rgb ();
+
++ image = pixmap.convertToImage ();
++
++ #if DEBUG_KP_TOOL_PEN
++ convAndWashTime = timer.restart ();
++ kdDebug () << "\tconvert to image: " << convAndWashTime << " ms" << endl;
++ #endif
++ }
++
+ // Sweeps a pixmap along a line (modified Bresenham's line algorithm,
+ // see MODIFIED comment below).
+ //
+@@ -485,19 +494,27 @@ void kpToolPen::draw (const QPoint &this
+ int x = 0;
+ int y = 0;
+
+- if (m_mode & WashesPixmaps)
++ if (delayedDraw)
+ {
+- if (wash (&painter, image,
+- colorToReplace,
+- rect, plotx + rect.left (), ploty + rect.top ()))
+- {
+- didSomething = true;
+- }
++ region = region.unite (hotRect (plotx + rect.left (), ploty + rect.top ()));
+ }
+ else
+ {
+- painter.drawPixmap (hotPoint (plotx, ploty), m_brushPixmap [m_mouseButton]);
+- didSomething = true;
++ if (m_mode & WashesPixmaps)
++ {
++ if (wash (&painter, image,
++ colorToReplace,
++ rect, plotx + rect.left (), ploty + rect.top ()))
++ {
++ didSomething = true;
++ }
++ }
++ else
++ {
++ painter.drawPixmap (hotPoint (plotx, ploty),
++ m_brushPixmap [m_mouseButton]);
++ didSomething = true;
++ }
+ }
+
+ for (int i = 0; i <= inc; i++)
+@@ -541,39 +558,115 @@ void kpToolPen::draw (const QPoint &this
+ // is more than 1 point, of course). This is in contrast to the
+ // ordinary line algorithm which can create diagonal adjacencies.
+
++ if (delayedDraw)
++ {
++ region = region.unite (hotRect (plotx + rect.left (), oldploty + rect.top ()));
++ }
++ else
++ {
++ if (m_mode & WashesPixmaps)
++ {
++ if (wash (&painter, image,
++ colorToReplace,
++ rect, plotx + rect.left (), oldploty + rect.top ()))
++ {
++ didSomething = true;
++ }
++ }
++ else
++ {
++ painter.drawPixmap (hotPoint (plotx, oldploty),
++ m_brushPixmap [m_mouseButton]);
++ didSomething = true;
++ }
++ }
++ }
++
++ if (delayedDraw)
++ {
++ region = region.unite (hotRect (plotx + rect.left (), ploty + rect.top ()));
++ }
++ else
++ {
+ if (m_mode & WashesPixmaps)
+ {
+ if (wash (&painter, image,
+ colorToReplace,
+- rect, plotx + rect.left (), oldploty + rect.top ()))
++ rect, plotx + rect.left (), ploty + rect.top ()))
+ {
+ didSomething = true;
+ }
+ }
+ else
+ {
+- painter.drawPixmap (hotPoint (plotx, oldploty), m_brushPixmap [m_mouseButton]);
++ painter.drawPixmap (hotPoint (plotx, ploty),
++ m_brushPixmap [m_mouseButton]);
+ didSomething = true;
+ }
+ }
+-
++ }
++ }
++
++ if (delayedDraw)
++ {
++ QMemArray <QRect> rects = region.rects ();
++
++ int numRects = rects.count ();
++ #if DEBUG_KP_TOOL_PEN
++ kdDebug () << "\tdelayed draw now happening: numRects="
++ << numRects << endl;
++ int convImageMS = 0;
++ int washMS = 0;
++ int setDocMS = 0;
++ QTime timer;
++ #endif
++ for (int i = 0; i < numRects; i++)
++ {
++ QRect r = rects [i];
++ QPixmap pm = document ()->getPixmapAt (r);
++ #if DEBUG_KP_TOOL_PEN && 0
++ kdDebug () << "\tr=" << r << endl;
++ #endif
++
++ bool drew = false;
++
+ if (m_mode & WashesPixmaps)
+ {
++ timer.start ();
++
+ if (wash (&painter, image,
+ colorToReplace,
+- rect, plotx + rect.left (), ploty + rect.top ()))
++ rect, r))
+ {
+- didSomething = true;
++ drew = true;
+ }
++ washMS += timer.restart ();
+ }
+ else
+ {
+- painter.drawPixmap (hotPoint (plotx, ploty), m_brushPixmap [m_mouseButton]);
++ painter.setBrush (color (m_mouseButton));
++ painter.drawRect (r.x () - rect.x (),
++ r.y () - rect.y (),
++ r.width (),
++ r.height ());
++ drew = true;
++ }
++
++ if (drew)
++ {
++ m_currentCommand->updateBoundingRect (r);
+ didSomething = true;
++ setDocMS += timer.restart ();
+ }
+ }
++
++ #if DEBUG_KP_TOOL_PEN
++ kdDebug () << "convImageMS=" << convImageMS
++ << " washMS=" << washMS
++ << " setDocMS=" << setDocMS
++ << endl;
++ #endif
+ }
+-
+ }
+
+ painter.end ();
diff --git a/kolourpaint/patches/doc_resize_no_flicker.diff b/kolourpaint/patches/doc_resize_no_flicker.diff
new file mode 100644
index 00000000..ae5f9aba
--- /dev/null
+++ b/kolourpaint/patches/doc_resize_no_flicker.diff
@@ -0,0 +1,614 @@
+Eliminates flicker when moving document resize lines / dragging resize
+handles by:
+
+1. Not erasing areas that will be subsequently painted over with resize
+ lines.
+2. Erasing the old areas and painting the new ones atomicly by using
+ clever NOT'ing of pixels.
+
+Additionally, recover the resize lines after a window pops up momentarily
+over KolourPaint (kpViewScrollableContainer::windowActivationChange()).
+
+Critical bugs with this code and scrollbars:
+
+1. Drag scrolling leaves trails of resize lines.
+2. Moving the mouse cursor above the start of the document does not result
+ in a resize line at document y = 1.
+
+Because I'm still debugging, there are a few hacks in the code such as
+"m_resizeLinesDontPaintClever".
+
+Index: kpviewscrollablecontainer.cpp
+===================================================================
+RCS file: /home/kde/kdegraphics/kolourpaint/kpviewscrollablecontainer.cpp,v
+retrieving revision 1.7
+diff -u -p -r1.7 kpviewscrollablecontainer.cpp
+--- kpviewscrollablecontainer.cpp 29 Jul 2004 12:47:15 -0000 1.7
++++ kpviewscrollablecontainer.cpp 30 Jul 2004 11:37:20 -0000
+@@ -1,4 +1,4 @@
+-
++static bool inScroll = false;
+ /*
+ Copyright (c) 2003-2004 Clarence Dang <dang@kde.org>
+ All rights reserved.
+@@ -33,6 +33,7 @@
+ #include <qpainter.h>
+ #include <qpen.h>
+ #include <qpixmap.h>
++#include <qregion.h>
+ #include <qtimer.h>
+
+ #include <kdebug.h>
+@@ -240,7 +241,7 @@ void kpGrip::mousePressEvent (QMouseEven
+ m_startPoint = e->pos ();
+ m_currentPoint = e->pos ();
+ emit beganDraw ();
+- grabKeyboard ();
++ //grabKeyboard (); HACK
+
+ setUserMessage (i18n ("Resize Image: Right click to cancel."));
+ setCursor (cursorForType (m_type));
+@@ -387,6 +388,7 @@ kpViewScrollableContainer::kpViewScrolla
+ m_scrollTimerRunOnce (false),
+ m_resizeRoundedLastViewX (-1), m_resizeRoundedLastViewY (-1),
+ m_resizeRoundedLastViewDX (0), m_resizeRoundedLastViewDY (0),
++ m_resizeLinesDontPaintClever (0),
+ m_haveMovedFromOriginalDocSize (false)
+
+ {
+@@ -561,6 +563,18 @@ QRect kpViewScrollableContainer::bottomR
+ m_resizeRoundedLastViewY + bottomResizeLineWidth () - 1));
+ }
+
++// protected
++QRegion kpViewScrollableContainer::resizeLinesRegion () const
++{
++ QRegion ret;
++
++ ret += rightResizeLineRect ();
++ ret += bottomResizeLineRect ();
++ ret += bottomRightResizeLineRect ();
++
++ return ret;
++}
++
+
+ // TODO: are these 2 correct? Remember that viewport()->x() == 1, viewport()->y() == 1
+
+@@ -581,6 +595,17 @@ QRect kpViewScrollableContainer::mapView
+ return ret;
+ }
+
++// protected
++QRegion kpViewScrollableContainer::mapViewToViewport (const QRegion &viewRegion)
++{
++ if (viewRegion.isEmpty ())
++ return viewRegion;
++
++ QRegion ret = viewRegion;
++ ret.translate (-contentsX (), -contentsY ());
++ return ret;
++}
++
+
+ // protected
+ QRect kpViewScrollableContainer::mapViewportToGlobal (const QRect &viewportRect)
+@@ -589,89 +614,108 @@ QRect kpViewScrollableContainer::mapView
+ }
+
+ // protected
++QRegion kpViewScrollableContainer::mapViewportToGlobal (const QRegion &viewportRegion)
++{
++ return kpWidgetMapper::toGlobal (viewport (), viewportRegion);
++}
++
++
++// protected
+ QRect kpViewScrollableContainer::mapViewToGlobal (const QRect &viewRect)
+ {
+ return mapViewportToGlobal (mapViewToViewport (viewRect));
+ }
+
++// protected
++QRegion kpViewScrollableContainer::mapViewToGlobal (const QRegion &viewRegion)
++{
++ return mapViewportToGlobal (mapViewToViewport (viewRegion));
++}
++
+
+ // protected
+-void kpViewScrollableContainer::repaintWidgetAtResizeLineViewRect (
+- QWidget *widget, const QRect &resizeLineViewRect)
++void kpViewScrollableContainer::repaintWidgetRegion (
++ QWidget *widget,
++ const QRegion &viewRegion)
+ {
+- const QRect resizeLineGlobalRect = mapViewToGlobal (resizeLineViewRect);
++ const QRegion globalRegion = mapViewToGlobal (viewRegion);
++
+ const QRect widgetGlobalRect = kpWidgetMapper::toGlobal (widget,
+ widget->rect ());
+
+- const QRect redrawGlobalRect =
+- resizeLineGlobalRect.intersect (widgetGlobalRect);
+
+- const QRect redrawWidgetRect =
+- kpWidgetMapper::fromGlobal (widget, redrawGlobalRect);
++ const QRegion redrawGlobalRegion =
++ globalRegion.intersect (widgetGlobalRect);
+
++ const QRegion redrawWidgetRegion =
++ kpWidgetMapper::fromGlobal (widget, redrawGlobalRegion);
+
+- if (redrawWidgetRect.isValid ())
++
++ if (!redrawWidgetRegion.isEmpty ())
+ {
+ // TODO: should be "!widget->testWFlags (Qt::WRepaintNoErase)"
+ // but for some reason, doesn't work for viewport().
+ const bool erase = !dynamic_cast <kpView *> (widget);
+- widget->repaint (redrawWidgetRect, erase);
++ widget->repaint (redrawWidgetRegion, erase);
+ }
+ }
+
+ // protected
+-void kpViewScrollableContainer::repaintWidgetAtResizeLines (QWidget *widget)
++void kpViewScrollableContainer::eraseResizeLines (const QRegion &viewRegion)
+ {
+- repaintWidgetAtResizeLineViewRect (widget, rightResizeLineRect ());
+- repaintWidgetAtResizeLineViewRect (widget, bottomResizeLineRect ());
+- repaintWidgetAtResizeLineViewRect (widget, bottomRightResizeLineRect ());
+-}
++ if (viewRegion.isEmpty ())
++ return;
+
+-// protected
+-void kpViewScrollableContainer::eraseResizeLines ()
+-{
+- if (m_resizeRoundedLastViewX >= 0 && m_resizeRoundedLastViewY >= 0)
+- {
+- repaintWidgetAtResizeLines (viewport ());
+- repaintWidgetAtResizeLines (m_view);
+
+- repaintWidgetAtResizeLines (m_bottomGrip);
+- repaintWidgetAtResizeLines (m_rightGrip);
+- repaintWidgetAtResizeLines (m_bottomRightGrip);
+- }
++ repaintWidgetRegion (viewport (), viewRegion);
++ repaintWidgetRegion (m_view, viewRegion);
++
++ repaintWidgetRegion (m_bottomGrip, viewRegion);
++ repaintWidgetRegion (m_rightGrip, viewRegion);
++ repaintWidgetRegion (m_bottomRightGrip, viewRegion);
+ }
+
+
+ // protected
+-void kpViewScrollableContainer::drawResizeLines ()
++void kpViewScrollableContainer::drawResizeLines (const QRegion &viewRegion)
+ {
+ #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
+- kdDebug () << "kpViewScrollableContainer::drawResizeLines()"
++ kdDebug () << "kpViewScrollableContainer::drawResizeLines("
++ << viewRegion <<")"
+ << " lastViewX=" << m_resizeRoundedLastViewX
+ << " lastViewY=" << m_resizeRoundedLastViewY
+ << endl;
+ #endif
+
++ if (viewRegion.isEmpty ())
++ return;
++
+
+ QPainter p (viewport (), true/*unclipped*/);
+ p.setRasterOp (Qt::NotROP);
+
+- const QRect rightRect = rightResizeLineRect ();
+- if (rightRect.isValid ())
+- p.fillRect (mapViewToViewport (rightRect), Qt::white);
+-
+- const QRect bottomRect = bottomResizeLineRect ();
+- if (bottomRect.isValid ())
+- p.fillRect (mapViewToViewport (bottomRect), Qt::white);
+-
+- const QRect bottomRightRect = bottomRightResizeLineRect ();
+- if (bottomRightRect.isValid ())
+- p.fillRect (mapViewToViewport (bottomRightRect), Qt::white);
++ const QMemArray <QRect> rects = mapViewToViewport (viewRegion).rects ();
++ for (QMemArray <QRect>::ConstIterator it = rects.begin ();
++ it != rects.end ();
++ it++)
++ {
++ p.fillRect (*it, Qt::white);
++ }
+
+ p.end ();
+ }
+
+
++template <typename T>
++static inline void swap (T &a, T &b)
++{
++ T temp = a;
++
++ a = b;
++ b = temp;
++}
++
++
+ // protected
+ void kpViewScrollableContainer::updateResizeLines (int viewX, int viewY,
+ int viewDX, int viewDY)
+@@ -686,36 +730,71 @@ void kpViewScrollableContainer::updateRe
+ << endl;
+ #endif
+
+- eraseResizeLines ();
+-
++ int newResizeRoundedLastViewX = -1,
++ newResizeRoundedLastViewY = -1;
++ int newResizeRoundedLastViewDX = 0,
++ newResizeRoundedLastViewDY = 0;
+
+ if (viewX >= 0 && viewY >= 0)
+ {
+- m_resizeRoundedLastViewX = m_view->zoomDocToViewX (m_view->zoomViewToDocX (viewX));
+- m_resizeRoundedLastViewY = m_view->zoomDocToViewY (m_view->zoomViewToDocY (viewY));
++ newResizeRoundedLastViewX = m_view->zoomDocToViewX (m_view->zoomViewToDocX (viewX));
++ newResizeRoundedLastViewY = m_view->zoomDocToViewY (m_view->zoomViewToDocY (viewY));
+
+- m_resizeRoundedLastViewDX = viewDX;
+- m_resizeRoundedLastViewDY = viewDY;
++ newResizeRoundedLastViewDX = viewDX;
++ newResizeRoundedLastViewDY = viewDY;
+ }
+- else
+- {
+- m_resizeRoundedLastViewX = -1;
+- m_resizeRoundedLastViewY = -1;
+
+- m_resizeRoundedLastViewDX = 0;
+- m_resizeRoundedLastViewDY = 0;
+- }
+
+- // TODO: This is suboptimal since if another window pops up on top of
+- // KolourPaint then disappears, the lines are not redrawn
+- // (although this doesn't happen very frequently since we grab the
+- // keyboard and mouse when resizing):
+- //
+- // e.g. sleep 5 && gedit & sleep 10 && killall gedit
++ QRegion oldLinesRegion = resizeLinesRegion ();
++#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
++ kdDebug () << "\toldLinesRegion=" << oldLinesRegion << endl;
++#endif
++
++
++// (macro instead of writing out code to permit experimentation)
++#define SWAP_LAST_VIEW_STATS() \
++{ \
++ swap (m_resizeRoundedLastViewX, newResizeRoundedLastViewX); \
++ swap (m_resizeRoundedLastViewY, newResizeRoundedLastViewY); \
++ \
++ swap (m_resizeRoundedLastViewDX, newResizeRoundedLastViewDX); \
++ swap (m_resizeRoundedLastViewDY, newResizeRoundedLastViewDY); \
++}
++ SWAP_LAST_VIEW_STATS ();
++#undef SWAP_LAST_VIEW_STATS
++
++
++ QRegion newLinesRegion = resizeLinesRegion ();
++#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
++ kdDebug () << "\tnewLinesRegion=" << newLinesRegion << endl;
++#endif
++
++
++ // TODO: This is suboptimal - we will get redraw errors sooner or later.
++ // But I've tried hard to avoid them (e.g. windowActivationChange()).
+ //
+ // Should be done in the paintEvent's of every child of the
+ // scrollview.
+- drawResizeLines ();
++
++ if (m_resizeLinesDontPaintClever)
++ {
++ // (drawResizeLines() NOT's the pixels - so we can erase old and draw
++ // new at the same time)
++ drawResizeLines (newLinesRegion.eor (oldLinesRegion));
++ #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
++ kdDebug () << "\tNOTRregion="
++ << newLinesRegion.eor (oldLinesRegion) << endl;
++ #endif
++ }
++ else
++ {
++ eraseResizeLines (oldLinesRegion);
++ drawResizeLines (newLinesRegion);
++ #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
++ kdDebug () << "\tnot erasing old lines; NOTRregion="
++ << newLinesRegion << endl;
++ #endif
++ }
+ }
+
+
+@@ -729,6 +808,8 @@ void kpViewScrollableContainer::slotGrip
+
+ m_haveMovedFromOriginalDocSize = false;
+
++ m_resizeLinesDontPaintClever = true;
++
+ updateResizeLines (m_view->width (), m_view->height (),
+ 0/*viewDX*/, 0/*viewDY*/);
+
+@@ -750,12 +831,28 @@ void kpViewScrollableContainer::slotGrip
+
+ m_haveMovedFromOriginalDocSize = true;
+
++#if 0
++ if (inScroll != !m_resizeLinesNeedErase)
++ {
++ kdError () << "slotGripContDraw EXCEPTION 0: inScroll=" << inScroll << endl;
++ memset (0, 42, 1048576);
++ }
++#endif
++
+ updateResizeLines (QMAX (1, QMAX (m_view->width () + viewDX, m_view->zoomDocToViewX (1))),
+ QMAX (1, QMAX (m_view->height () + viewDY, m_view->zoomDocToViewY (1))),
+ viewDX, viewDY);
+
+ emit continuedDocResize (newDocSize ());
+
++#if 0
++ if (!m_resizeLinesNeedErase)
++ {
++ kdError () << "slotGripContDraw EXCEPTION 1" << endl;
++ memset (0, 42, 1048576);
++ }
++#endif
++
+ beginDragScroll (QPoint (), QPoint (), m_view->zoomLevelX ());
+ }
+
+@@ -859,8 +956,19 @@ void kpViewScrollableContainer::slotCont
+ << x << "," << y << ")" << endl;
+ #endif
+
++ m_resizeLinesDontPaintClever++;
++
++ if (inScroll && 0)
++ {
++ kdError () << "slotContentsMovING EXCEPTION" << endl;
++ memset (0, 42, 1048576);
++ }
++
++ inScroll = true;
+ // Reduce flicker - don't let QScrollView scroll to-be-erased lines
+- eraseResizeLines ();
++ //eraseResizeLines (resizeLinesRegion ());
++ //m_resizeLinesNeedErase = false;
++
+
+ QTimer::singleShot (0, this, SLOT (slotContentsMoved ()));
+ }
+@@ -874,9 +982,27 @@ void kpViewScrollableContainer::slotCont
+ << " grip=" << grip << endl;
+ #endif
+ if (!grip)
++ {
++ inScroll = false;
+ return;
++ }
+
++ if (!inScroll && 0)
++ {
++ kdError () << "slotContentsMoved EXCEPTION" << endl;
++ memset (0, 42, 1048576);
++ }
+ grip->mouseMovedTo (grip->mapFromGlobal (QCursor::pos ()));
++#if 0
++ if (!m_resizeLinesNeedErase)
++ {
++ kdError () << "slotContentsMoved EXCEPTION 2" << endl;
++ memset (0, 42, 1048576);
++ }
++#endif
++ inScroll = false;
++
++ m_resizeLinesDontPaintClever--;
+ }
+
+
+@@ -1191,7 +1317,7 @@ bool kpViewScrollableContainer::eventFil
+ // protected virtual [base QScrollView]
+ void kpViewScrollableContainer::viewportPaintEvent (QPaintEvent *e)
+ {
+-#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
++#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER && 0
+ kdDebug () << "kpViewScrollableContainer::viewportPaintEvent("
+ << e->rect ()
+ << ")" << endl;
+@@ -1213,4 +1339,42 @@ void kpViewScrollableContainer::paintEve
+ }
+
+
++// protected slot
++void kpViewScrollableContainer::windowActivationChanged ()
++{
++ if (isActiveWindow () &&
++ m_resizeRoundedLastViewX >= 0 && m_resizeRoundedLastViewY >= 0)
++ {
++ // We were obscured by a window that popped up monmentarily and
++ // this clobbered the resize lines (since the scrollView's child
++ // widgets don't draw them). This doesn't happen very frequently
++ // since we grab the keyboard and mouse when resizing but:
++ //
++ // e.g. sleep 5 && gedit & sleep 10 && killall gedit
++ //
++
++ // Repaint child widgets at the resize lines to make sure any
++ // remains of the resize lines are gone.
++ eraseResizeLines (resizeLinesRegion ());
++
++ // Draw the resize lines by NOT-ing the child widget pixels.
++ drawResizeLines (resizeLinesRegion ());
++ }
++}
++
++// protected virtual [base QWidget]
++void kpViewScrollableContainer::windowActivationChange (bool wasActive)
++{
++#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER && 1
++ kdDebug () << "kpViewScrollableContainer::windowActivationChange("
++ << wasActive << ")" << endl;
++#endif
++
++ QScrollView::windowActivationChange (wasActive);
++
++ // Wait for m_view to update
++ QTimer::singleShot (0, this, SLOT (windowActivationChanged ()));
++}
++
++
+ #include <kpviewscrollablecontainer.moc>
+Index: kpviewscrollablecontainer.h
+===================================================================
+RCS file: /home/kde/kdegraphics/kolourpaint/kpviewscrollablecontainer.h,v
+retrieving revision 1.3
+diff -u -p -r1.3 kpviewscrollablecontainer.h
+--- kpviewscrollablecontainer.h 19 Jul 2004 05:00:47 -0000 1.3
++++ kpviewscrollablecontainer.h 30 Jul 2004 11:37:21 -0000
+@@ -31,6 +31,7 @@
+
+
+ #include <qpoint.h>
++#include <qregion.h>
+ #include <qscrollview.h>
+ #include <qsize.h>
+
+@@ -147,19 +148,23 @@ protected:
+ QRect bottomResizeLineRect () const;
+ QRect rightResizeLineRect () const;
+ QRect bottomRightResizeLineRect () const;
++ QRegion resizeLinesRegion () const;
+
+ QPoint mapViewToViewport (const QPoint &viewPoint);
+ QRect mapViewToViewport (const QRect &viewRect);
++ QRegion mapViewToViewport (const QRegion &viewRegion);
+
+ QRect mapViewportToGlobal (const QRect &viewportRect);
++ QRegion mapViewportToGlobal (const QRegion &viewportRegion);
++
+ QRect mapViewToGlobal (const QRect &viewRect);
++ QRegion mapViewToGlobal (const QRegion &viewRegion);
+
+- void repaintWidgetAtResizeLineViewRect (QWidget *widget,
+- const QRect &resizeLineViewRect);
+- void repaintWidgetAtResizeLines (QWidget *widget);
+- void eraseResizeLines ();
++ void repaintWidgetRegion (QWidget *widget,
++ const QRegion &viewRegion);
++ void eraseResizeLines (const QRegion &viewRegion);
+
+- void drawResizeLines ();
++ void drawResizeLines (const QRegion &viewRegion);
+
+ void updateResizeLines (int viewX, int viewY,
+ int viewDX, int viewDY);
+@@ -213,6 +218,12 @@ protected:
+ virtual void viewportPaintEvent (QPaintEvent *e);
+ virtual void paintEvent (QPaintEvent *e);
+
++protected slots:
++ void windowActivationChanged ();
++protected:
++ virtual void windowActivationChange (bool wasActive);
++
++
+ protected:
+ kpMainWindow *m_mainWindow;
+ kpView *m_view;
+@@ -223,6 +234,7 @@ protected:
+ bool m_scrollTimerRunOnce;
+ int m_resizeRoundedLastViewX, m_resizeRoundedLastViewY;
+ int m_resizeRoundedLastViewDX, m_resizeRoundedLastViewDY;
++ int m_resizeLinesDontPaintClever;
+ bool m_haveMovedFromOriginalDocSize;
+ QString m_gripStatusMessage;
+ };
+Index: kpwidgetmapper.cpp
+===================================================================
+RCS file: /home/kde/kdegraphics/kolourpaint/kpwidgetmapper.cpp,v
+retrieving revision 1.1
+diff -u -p -r1.1 kpwidgetmapper.cpp
+--- kpwidgetmapper.cpp 10 Jul 2004 11:38:09 -0000 1.1
++++ kpwidgetmapper.cpp 30 Jul 2004 11:37:21 -0000
+@@ -30,6 +30,7 @@
+
+ #include <qpoint.h>
+ #include <qrect.h>
++#include <qregion.h>
+ #include <qwidget.h>
+
+
+@@ -54,6 +55,17 @@ QRect fromGlobal (const QWidget *widget,
+ return QRect (topLeft.x (), topLeft.y (), rect.width (), rect.height ());
+ }
+
++QRegion fromGlobal (const QWidget *widget, const QRegion &region)
++{
++ if (!widget || region.isEmpty ())
++ return region;
++
++ const QPoint widgetGlobalTopLeft = toGlobal (widget, QPoint (0, 0));
++ QRegion ret = region;
++ ret.translate (-widgetGlobalTopLeft.x (), -widgetGlobalTopLeft.y ());
++ return ret;
++}
++
+
+ QPoint toGlobal (const QWidget *widget, const QPoint &point)
+ {
+@@ -72,5 +84,16 @@ QRect toGlobal (const QWidget *widget, c
+ return QRect (topLeft.x (), topLeft.y (), rect.width (), rect.height ());
+ }
+
++QRegion toGlobal (const QWidget *widget, const QRegion &region)
++{
++ if (!widget || region.isEmpty ())
++ return region;
++
++ const QPoint widgetGlobalTopLeft = toGlobal (widget, QPoint (0, 0));
++ QRegion ret = region;
++ ret.translate (widgetGlobalTopLeft.x (), widgetGlobalTopLeft.y ());
++ return ret;
++}
++
+
+ } // namespace kpWidgetMapper {
+Index: kpwidgetmapper.h
+===================================================================
+RCS file: /home/kde/kdegraphics/kolourpaint/kpwidgetmapper.h,v
+retrieving revision 1.1
+diff -u -p -r1.1 kpwidgetmapper.h
+--- kpwidgetmapper.h 10 Jul 2004 11:38:09 -0000 1.1
++++ kpwidgetmapper.h 30 Jul 2004 11:37:21 -0000
+@@ -32,15 +32,18 @@
+ class QWidget;
+ class QPoint;
+ class QRect;
++class QRegion;
+
+
+ namespace kpWidgetMapper
+ {
+ QPoint fromGlobal (const QWidget *widget, const QPoint &point);
+ QRect fromGlobal (const QWidget *widget, const QRect &rect);
++ QRegion fromGlobal (const QWidget *widget, const QRegion &region);
+
+ QPoint toGlobal (const QWidget *widget, const QPoint &point);
+ QRect toGlobal (const QWidget *widget, const QRect &rect);
++ QRegion toGlobal (const QWidget *widget, const QRegion &region);
+ }
+
+
diff --git a/kolourpaint/pics/Makefile.am b/kolourpaint/pics/Makefile.am
new file mode 100644
index 00000000..2e4aed53
--- /dev/null
+++ b/kolourpaint/pics/Makefile.am
@@ -0,0 +1,13 @@
+SUBDIRS = custom
+
+KDE_ICON = kolourpaint
+
+actionicondir = $(kde_datadir)/kolourpaint/icons
+actionicon_ICON = AUTO
+
+# Change the following line every time you add an icon
+# to force Makefile regeneration:
+#
+# 4
+#
+
diff --git a/kolourpaint/pics/cr16-action-tool_brush.png b/kolourpaint/pics/cr16-action-tool_brush.png
new file mode 100644
index 00000000..32a23881
--- /dev/null
+++ b/kolourpaint/pics/cr16-action-tool_brush.png
Binary files differ
diff --git a/kolourpaint/pics/cr16-action-tool_color_picker.png b/kolourpaint/pics/cr16-action-tool_color_picker.png
new file mode 100644
index 00000000..569171e6
--- /dev/null
+++ b/kolourpaint/pics/cr16-action-tool_color_picker.png
Binary files differ
diff --git a/kolourpaint/pics/cr16-action-tool_color_washer.png b/kolourpaint/pics/cr16-action-tool_color_washer.png
new file mode 100644
index 00000000..97193458
--- /dev/null
+++ b/kolourpaint/pics/cr16-action-tool_color_washer.png
Binary files differ
diff --git a/kolourpaint/pics/cr16-action-tool_curve.png b/kolourpaint/pics/cr16-action-tool_curve.png
new file mode 100644
index 00000000..b86c96fb
--- /dev/null
+++ b/kolourpaint/pics/cr16-action-tool_curve.png
Binary files differ
diff --git a/kolourpaint/pics/cr16-action-tool_ellipse.png b/kolourpaint/pics/cr16-action-tool_ellipse.png
new file mode 100644
index 00000000..608d40b7
--- /dev/null
+++ b/kolourpaint/pics/cr16-action-tool_ellipse.png
Binary files differ
diff --git a/kolourpaint/pics/cr16-action-tool_elliptical_selection.png b/kolourpaint/pics/cr16-action-tool_elliptical_selection.png
new file mode 100644
index 00000000..70edc438
--- /dev/null
+++ b/kolourpaint/pics/cr16-action-tool_elliptical_selection.png
Binary files differ
diff --git a/kolourpaint/pics/cr16-action-tool_eraser.png b/kolourpaint/pics/cr16-action-tool_eraser.png
new file mode 100644
index 00000000..459d28a2
--- /dev/null
+++ b/kolourpaint/pics/cr16-action-tool_eraser.png
Binary files differ
diff --git a/kolourpaint/pics/cr16-action-tool_flood_fill.png b/kolourpaint/pics/cr16-action-tool_flood_fill.png
new file mode 100644
index 00000000..746ede5b
--- /dev/null
+++ b/kolourpaint/pics/cr16-action-tool_flood_fill.png
Binary files differ
diff --git a/kolourpaint/pics/cr16-action-tool_free_form_selection.png b/kolourpaint/pics/cr16-action-tool_free_form_selection.png
new file mode 100644
index 00000000..ed03ba39
--- /dev/null
+++ b/kolourpaint/pics/cr16-action-tool_free_form_selection.png
Binary files differ
diff --git a/kolourpaint/pics/cr16-action-tool_line.png b/kolourpaint/pics/cr16-action-tool_line.png
new file mode 100644
index 00000000..ce282923
--- /dev/null
+++ b/kolourpaint/pics/cr16-action-tool_line.png
Binary files differ
diff --git a/kolourpaint/pics/cr16-action-tool_pen.png b/kolourpaint/pics/cr16-action-tool_pen.png
new file mode 100644
index 00000000..ae64f5aa
--- /dev/null
+++ b/kolourpaint/pics/cr16-action-tool_pen.png
Binary files differ
diff --git a/kolourpaint/pics/cr16-action-tool_polygon.png b/kolourpaint/pics/cr16-action-tool_polygon.png
new file mode 100644
index 00000000..a5500d94
--- /dev/null
+++ b/kolourpaint/pics/cr16-action-tool_polygon.png
Binary files differ
diff --git a/kolourpaint/pics/cr16-action-tool_polyline.png b/kolourpaint/pics/cr16-action-tool_polyline.png
new file mode 100644
index 00000000..1e23ccd9
--- /dev/null
+++ b/kolourpaint/pics/cr16-action-tool_polyline.png
Binary files differ
diff --git a/kolourpaint/pics/cr16-action-tool_rect_selection.png b/kolourpaint/pics/cr16-action-tool_rect_selection.png
new file mode 100644
index 00000000..a85ef3f8
--- /dev/null
+++ b/kolourpaint/pics/cr16-action-tool_rect_selection.png
Binary files differ
diff --git a/kolourpaint/pics/cr16-action-tool_rectangle.png b/kolourpaint/pics/cr16-action-tool_rectangle.png
new file mode 100644
index 00000000..a8455de0
--- /dev/null
+++ b/kolourpaint/pics/cr16-action-tool_rectangle.png
Binary files differ
diff --git a/kolourpaint/pics/cr16-action-tool_rounded_rectangle.png b/kolourpaint/pics/cr16-action-tool_rounded_rectangle.png
new file mode 100644
index 00000000..4b5a0617
--- /dev/null
+++ b/kolourpaint/pics/cr16-action-tool_rounded_rectangle.png
Binary files differ
diff --git a/kolourpaint/pics/cr16-action-tool_spraycan.png b/kolourpaint/pics/cr16-action-tool_spraycan.png
new file mode 100644
index 00000000..75b7f748
--- /dev/null
+++ b/kolourpaint/pics/cr16-action-tool_spraycan.png
Binary files differ
diff --git a/kolourpaint/pics/cr16-action-tool_text.png b/kolourpaint/pics/cr16-action-tool_text.png
new file mode 100644
index 00000000..ffaab637
--- /dev/null
+++ b/kolourpaint/pics/cr16-action-tool_text.png
Binary files differ
diff --git a/kolourpaint/pics/cr22-action-tool_brush.png b/kolourpaint/pics/cr22-action-tool_brush.png
new file mode 100644
index 00000000..f2ad76cf
--- /dev/null
+++ b/kolourpaint/pics/cr22-action-tool_brush.png
Binary files differ
diff --git a/kolourpaint/pics/cr22-action-tool_color_picker.png b/kolourpaint/pics/cr22-action-tool_color_picker.png
new file mode 100644
index 00000000..3b7ed752
--- /dev/null
+++ b/kolourpaint/pics/cr22-action-tool_color_picker.png
Binary files differ
diff --git a/kolourpaint/pics/cr22-action-tool_color_washer.png b/kolourpaint/pics/cr22-action-tool_color_washer.png
new file mode 100644
index 00000000..35fcbac1
--- /dev/null
+++ b/kolourpaint/pics/cr22-action-tool_color_washer.png
Binary files differ
diff --git a/kolourpaint/pics/cr22-action-tool_curve.png b/kolourpaint/pics/cr22-action-tool_curve.png
new file mode 100644
index 00000000..9723e209
--- /dev/null
+++ b/kolourpaint/pics/cr22-action-tool_curve.png
Binary files differ
diff --git a/kolourpaint/pics/cr22-action-tool_ellipse.png b/kolourpaint/pics/cr22-action-tool_ellipse.png
new file mode 100644
index 00000000..cc57b2f8
--- /dev/null
+++ b/kolourpaint/pics/cr22-action-tool_ellipse.png
Binary files differ
diff --git a/kolourpaint/pics/cr22-action-tool_elliptical_selection.png b/kolourpaint/pics/cr22-action-tool_elliptical_selection.png
new file mode 100644
index 00000000..a6b23e11
--- /dev/null
+++ b/kolourpaint/pics/cr22-action-tool_elliptical_selection.png
Binary files differ
diff --git a/kolourpaint/pics/cr22-action-tool_eraser.png b/kolourpaint/pics/cr22-action-tool_eraser.png
new file mode 100644
index 00000000..78632a55
--- /dev/null
+++ b/kolourpaint/pics/cr22-action-tool_eraser.png
Binary files differ
diff --git a/kolourpaint/pics/cr22-action-tool_flood_fill.png b/kolourpaint/pics/cr22-action-tool_flood_fill.png
new file mode 100644
index 00000000..17cfefba
--- /dev/null
+++ b/kolourpaint/pics/cr22-action-tool_flood_fill.png
Binary files differ
diff --git a/kolourpaint/pics/cr22-action-tool_free_form_selection.png b/kolourpaint/pics/cr22-action-tool_free_form_selection.png
new file mode 100644
index 00000000..cc397f09
--- /dev/null
+++ b/kolourpaint/pics/cr22-action-tool_free_form_selection.png
Binary files differ
diff --git a/kolourpaint/pics/cr22-action-tool_line.png b/kolourpaint/pics/cr22-action-tool_line.png
new file mode 100644
index 00000000..7ab3c3d9
--- /dev/null
+++ b/kolourpaint/pics/cr22-action-tool_line.png
Binary files differ
diff --git a/kolourpaint/pics/cr22-action-tool_pen.png b/kolourpaint/pics/cr22-action-tool_pen.png
new file mode 100644
index 00000000..f80fa363
--- /dev/null
+++ b/kolourpaint/pics/cr22-action-tool_pen.png
Binary files differ
diff --git a/kolourpaint/pics/cr22-action-tool_polygon.png b/kolourpaint/pics/cr22-action-tool_polygon.png
new file mode 100644
index 00000000..1d05ec57
--- /dev/null
+++ b/kolourpaint/pics/cr22-action-tool_polygon.png
Binary files differ
diff --git a/kolourpaint/pics/cr22-action-tool_polyline.png b/kolourpaint/pics/cr22-action-tool_polyline.png
new file mode 100644
index 00000000..36c089c9
--- /dev/null
+++ b/kolourpaint/pics/cr22-action-tool_polyline.png
Binary files differ
diff --git a/kolourpaint/pics/cr22-action-tool_rect_selection.png b/kolourpaint/pics/cr22-action-tool_rect_selection.png
new file mode 100644
index 00000000..3dc8c75a
--- /dev/null
+++ b/kolourpaint/pics/cr22-action-tool_rect_selection.png
Binary files differ
diff --git a/kolourpaint/pics/cr22-action-tool_rectangle.png b/kolourpaint/pics/cr22-action-tool_rectangle.png
new file mode 100644
index 00000000..9954c17e
--- /dev/null
+++ b/kolourpaint/pics/cr22-action-tool_rectangle.png
Binary files differ
diff --git a/kolourpaint/pics/cr22-action-tool_rounded_rectangle.png b/kolourpaint/pics/cr22-action-tool_rounded_rectangle.png
new file mode 100644
index 00000000..b736a6cc
--- /dev/null
+++ b/kolourpaint/pics/cr22-action-tool_rounded_rectangle.png
Binary files differ
diff --git a/kolourpaint/pics/cr22-action-tool_spraycan.png b/kolourpaint/pics/cr22-action-tool_spraycan.png
new file mode 100644
index 00000000..bbd35078
--- /dev/null
+++ b/kolourpaint/pics/cr22-action-tool_spraycan.png
Binary files differ
diff --git a/kolourpaint/pics/cr22-action-tool_text.png b/kolourpaint/pics/cr22-action-tool_text.png
new file mode 100644
index 00000000..52b3ccf7
--- /dev/null
+++ b/kolourpaint/pics/cr22-action-tool_text.png
Binary files differ
diff --git a/kolourpaint/pics/cr32-action-tool_brush.png b/kolourpaint/pics/cr32-action-tool_brush.png
new file mode 100644
index 00000000..18bcab66
--- /dev/null
+++ b/kolourpaint/pics/cr32-action-tool_brush.png
Binary files differ
diff --git a/kolourpaint/pics/cr32-action-tool_color_picker.png b/kolourpaint/pics/cr32-action-tool_color_picker.png
new file mode 100644
index 00000000..38197e44
--- /dev/null
+++ b/kolourpaint/pics/cr32-action-tool_color_picker.png
Binary files differ
diff --git a/kolourpaint/pics/cr32-action-tool_color_washer.png b/kolourpaint/pics/cr32-action-tool_color_washer.png
new file mode 100644
index 00000000..b49c03ca
--- /dev/null
+++ b/kolourpaint/pics/cr32-action-tool_color_washer.png
Binary files differ
diff --git a/kolourpaint/pics/cr32-action-tool_curve.png b/kolourpaint/pics/cr32-action-tool_curve.png
new file mode 100644
index 00000000..db900434
--- /dev/null
+++ b/kolourpaint/pics/cr32-action-tool_curve.png
Binary files differ
diff --git a/kolourpaint/pics/cr32-action-tool_ellipse.png b/kolourpaint/pics/cr32-action-tool_ellipse.png
new file mode 100644
index 00000000..f6315fdc
--- /dev/null
+++ b/kolourpaint/pics/cr32-action-tool_ellipse.png
Binary files differ
diff --git a/kolourpaint/pics/cr32-action-tool_elliptical_selection.png b/kolourpaint/pics/cr32-action-tool_elliptical_selection.png
new file mode 100644
index 00000000..72c5fc7c
--- /dev/null
+++ b/kolourpaint/pics/cr32-action-tool_elliptical_selection.png
Binary files differ
diff --git a/kolourpaint/pics/cr32-action-tool_eraser.png b/kolourpaint/pics/cr32-action-tool_eraser.png
new file mode 100644
index 00000000..92efe970
--- /dev/null
+++ b/kolourpaint/pics/cr32-action-tool_eraser.png
Binary files differ
diff --git a/kolourpaint/pics/cr32-action-tool_flood_fill.png b/kolourpaint/pics/cr32-action-tool_flood_fill.png
new file mode 100644
index 00000000..6c116a71
--- /dev/null
+++ b/kolourpaint/pics/cr32-action-tool_flood_fill.png
Binary files differ
diff --git a/kolourpaint/pics/cr32-action-tool_free_form_selection.png b/kolourpaint/pics/cr32-action-tool_free_form_selection.png
new file mode 100644
index 00000000..b27f17d2
--- /dev/null
+++ b/kolourpaint/pics/cr32-action-tool_free_form_selection.png
Binary files differ
diff --git a/kolourpaint/pics/cr32-action-tool_line.png b/kolourpaint/pics/cr32-action-tool_line.png
new file mode 100644
index 00000000..b42db17f
--- /dev/null
+++ b/kolourpaint/pics/cr32-action-tool_line.png
Binary files differ
diff --git a/kolourpaint/pics/cr32-action-tool_pen.png b/kolourpaint/pics/cr32-action-tool_pen.png
new file mode 100644
index 00000000..a5881690
--- /dev/null
+++ b/kolourpaint/pics/cr32-action-tool_pen.png
Binary files differ
diff --git a/kolourpaint/pics/cr32-action-tool_polygon.png b/kolourpaint/pics/cr32-action-tool_polygon.png
new file mode 100644
index 00000000..5f643e3c
--- /dev/null
+++ b/kolourpaint/pics/cr32-action-tool_polygon.png
Binary files differ
diff --git a/kolourpaint/pics/cr32-action-tool_polyline.png b/kolourpaint/pics/cr32-action-tool_polyline.png
new file mode 100644
index 00000000..fabc5a48
--- /dev/null
+++ b/kolourpaint/pics/cr32-action-tool_polyline.png
Binary files differ
diff --git a/kolourpaint/pics/cr32-action-tool_rect_selection.png b/kolourpaint/pics/cr32-action-tool_rect_selection.png
new file mode 100644
index 00000000..c13c43b9
--- /dev/null
+++ b/kolourpaint/pics/cr32-action-tool_rect_selection.png
Binary files differ
diff --git a/kolourpaint/pics/cr32-action-tool_rectangle.png b/kolourpaint/pics/cr32-action-tool_rectangle.png
new file mode 100644
index 00000000..a271b7e5
--- /dev/null
+++ b/kolourpaint/pics/cr32-action-tool_rectangle.png
Binary files differ
diff --git a/kolourpaint/pics/cr32-action-tool_rounded_rectangle.png b/kolourpaint/pics/cr32-action-tool_rounded_rectangle.png
new file mode 100644
index 00000000..ed572dda
--- /dev/null
+++ b/kolourpaint/pics/cr32-action-tool_rounded_rectangle.png
Binary files differ
diff --git a/kolourpaint/pics/cr32-action-tool_spraycan.png b/kolourpaint/pics/cr32-action-tool_spraycan.png
new file mode 100644
index 00000000..b908810c
--- /dev/null
+++ b/kolourpaint/pics/cr32-action-tool_spraycan.png
Binary files differ
diff --git a/kolourpaint/pics/cr32-action-tool_text.png b/kolourpaint/pics/cr32-action-tool_text.png
new file mode 100644
index 00000000..a80b07a6
--- /dev/null
+++ b/kolourpaint/pics/cr32-action-tool_text.png
Binary files differ
diff --git a/kolourpaint/pics/cr48-action-tool_brush.png b/kolourpaint/pics/cr48-action-tool_brush.png
new file mode 100644
index 00000000..55758f0d
--- /dev/null
+++ b/kolourpaint/pics/cr48-action-tool_brush.png
Binary files differ
diff --git a/kolourpaint/pics/cr48-action-tool_color_picker.png b/kolourpaint/pics/cr48-action-tool_color_picker.png
new file mode 100644
index 00000000..e5414d6e
--- /dev/null
+++ b/kolourpaint/pics/cr48-action-tool_color_picker.png
Binary files differ
diff --git a/kolourpaint/pics/cr48-action-tool_color_washer.png b/kolourpaint/pics/cr48-action-tool_color_washer.png
new file mode 100644
index 00000000..2966d680
--- /dev/null
+++ b/kolourpaint/pics/cr48-action-tool_color_washer.png
Binary files differ
diff --git a/kolourpaint/pics/cr48-action-tool_curve.png b/kolourpaint/pics/cr48-action-tool_curve.png
new file mode 100644
index 00000000..c046a3ab
--- /dev/null
+++ b/kolourpaint/pics/cr48-action-tool_curve.png
Binary files differ
diff --git a/kolourpaint/pics/cr48-action-tool_ellipse.png b/kolourpaint/pics/cr48-action-tool_ellipse.png
new file mode 100644
index 00000000..a17095b1
--- /dev/null
+++ b/kolourpaint/pics/cr48-action-tool_ellipse.png
Binary files differ
diff --git a/kolourpaint/pics/cr48-action-tool_elliptical_selection.png b/kolourpaint/pics/cr48-action-tool_elliptical_selection.png
new file mode 100644
index 00000000..637b9603
--- /dev/null
+++ b/kolourpaint/pics/cr48-action-tool_elliptical_selection.png
Binary files differ
diff --git a/kolourpaint/pics/cr48-action-tool_eraser.png b/kolourpaint/pics/cr48-action-tool_eraser.png
new file mode 100644
index 00000000..69e5b77a
--- /dev/null
+++ b/kolourpaint/pics/cr48-action-tool_eraser.png
Binary files differ
diff --git a/kolourpaint/pics/cr48-action-tool_flood_fill.png b/kolourpaint/pics/cr48-action-tool_flood_fill.png
new file mode 100644
index 00000000..a2937a95
--- /dev/null
+++ b/kolourpaint/pics/cr48-action-tool_flood_fill.png
Binary files differ
diff --git a/kolourpaint/pics/cr48-action-tool_free_form_selection.png b/kolourpaint/pics/cr48-action-tool_free_form_selection.png
new file mode 100644
index 00000000..b0dd0ae9
--- /dev/null
+++ b/kolourpaint/pics/cr48-action-tool_free_form_selection.png
Binary files differ
diff --git a/kolourpaint/pics/cr48-action-tool_line.png b/kolourpaint/pics/cr48-action-tool_line.png
new file mode 100644
index 00000000..6d28915b
--- /dev/null
+++ b/kolourpaint/pics/cr48-action-tool_line.png
Binary files differ
diff --git a/kolourpaint/pics/cr48-action-tool_pen.png b/kolourpaint/pics/cr48-action-tool_pen.png
new file mode 100644
index 00000000..16d0f2f3
--- /dev/null
+++ b/kolourpaint/pics/cr48-action-tool_pen.png
Binary files differ
diff --git a/kolourpaint/pics/cr48-action-tool_polygon.png b/kolourpaint/pics/cr48-action-tool_polygon.png
new file mode 100644
index 00000000..d06b5b64
--- /dev/null
+++ b/kolourpaint/pics/cr48-action-tool_polygon.png
Binary files differ
diff --git a/kolourpaint/pics/cr48-action-tool_polyline.png b/kolourpaint/pics/cr48-action-tool_polyline.png
new file mode 100644
index 00000000..2d859d96
--- /dev/null
+++ b/kolourpaint/pics/cr48-action-tool_polyline.png
Binary files differ
diff --git a/kolourpaint/pics/cr48-action-tool_rect_selection.png b/kolourpaint/pics/cr48-action-tool_rect_selection.png
new file mode 100644
index 00000000..71b59563
--- /dev/null
+++ b/kolourpaint/pics/cr48-action-tool_rect_selection.png
Binary files differ
diff --git a/kolourpaint/pics/cr48-action-tool_rectangle.png b/kolourpaint/pics/cr48-action-tool_rectangle.png
new file mode 100644
index 00000000..9320c2d4
--- /dev/null
+++ b/kolourpaint/pics/cr48-action-tool_rectangle.png
Binary files differ
diff --git a/kolourpaint/pics/cr48-action-tool_rounded_rectangle.png b/kolourpaint/pics/cr48-action-tool_rounded_rectangle.png
new file mode 100644
index 00000000..91b8a185
--- /dev/null
+++ b/kolourpaint/pics/cr48-action-tool_rounded_rectangle.png
Binary files differ
diff --git a/kolourpaint/pics/cr48-action-tool_spraycan.png b/kolourpaint/pics/cr48-action-tool_spraycan.png
new file mode 100644
index 00000000..cb653e54
--- /dev/null
+++ b/kolourpaint/pics/cr48-action-tool_spraycan.png
Binary files differ
diff --git a/kolourpaint/pics/cr48-action-tool_text.png b/kolourpaint/pics/cr48-action-tool_text.png
new file mode 100644
index 00000000..1d007c02
--- /dev/null
+++ b/kolourpaint/pics/cr48-action-tool_text.png
Binary files differ
diff --git a/kolourpaint/pics/crsc-action-tool_brush.svgz b/kolourpaint/pics/crsc-action-tool_brush.svgz
new file mode 100644
index 00000000..5559a91b
--- /dev/null
+++ b/kolourpaint/pics/crsc-action-tool_brush.svgz
Binary files differ
diff --git a/kolourpaint/pics/crsc-action-tool_color_picker.svgz b/kolourpaint/pics/crsc-action-tool_color_picker.svgz
new file mode 100644
index 00000000..810cfe6b
--- /dev/null
+++ b/kolourpaint/pics/crsc-action-tool_color_picker.svgz
Binary files differ
diff --git a/kolourpaint/pics/crsc-action-tool_color_washer.svgz b/kolourpaint/pics/crsc-action-tool_color_washer.svgz
new file mode 100644
index 00000000..00f72a44
--- /dev/null
+++ b/kolourpaint/pics/crsc-action-tool_color_washer.svgz
Binary files differ
diff --git a/kolourpaint/pics/crsc-action-tool_curve.svgz b/kolourpaint/pics/crsc-action-tool_curve.svgz
new file mode 100644
index 00000000..fa995555
--- /dev/null
+++ b/kolourpaint/pics/crsc-action-tool_curve.svgz
Binary files differ
diff --git a/kolourpaint/pics/crsc-action-tool_ellipse.svgz b/kolourpaint/pics/crsc-action-tool_ellipse.svgz
new file mode 100644
index 00000000..42ca1349
--- /dev/null
+++ b/kolourpaint/pics/crsc-action-tool_ellipse.svgz
Binary files differ
diff --git a/kolourpaint/pics/crsc-action-tool_elliptical_selection.svgz b/kolourpaint/pics/crsc-action-tool_elliptical_selection.svgz
new file mode 100644
index 00000000..6b7c23b2
--- /dev/null
+++ b/kolourpaint/pics/crsc-action-tool_elliptical_selection.svgz
Binary files differ
diff --git a/kolourpaint/pics/crsc-action-tool_eraser.svgz b/kolourpaint/pics/crsc-action-tool_eraser.svgz
new file mode 100644
index 00000000..1a0278cc
--- /dev/null
+++ b/kolourpaint/pics/crsc-action-tool_eraser.svgz
Binary files differ
diff --git a/kolourpaint/pics/crsc-action-tool_flood_fill.svgz b/kolourpaint/pics/crsc-action-tool_flood_fill.svgz
new file mode 100644
index 00000000..24725599
--- /dev/null
+++ b/kolourpaint/pics/crsc-action-tool_flood_fill.svgz
Binary files differ
diff --git a/kolourpaint/pics/crsc-action-tool_free_form_selection.svgz b/kolourpaint/pics/crsc-action-tool_free_form_selection.svgz
new file mode 100644
index 00000000..2f304923
--- /dev/null
+++ b/kolourpaint/pics/crsc-action-tool_free_form_selection.svgz
Binary files differ
diff --git a/kolourpaint/pics/crsc-action-tool_line.svgz b/kolourpaint/pics/crsc-action-tool_line.svgz
new file mode 100644
index 00000000..f2cb1822
--- /dev/null
+++ b/kolourpaint/pics/crsc-action-tool_line.svgz
Binary files differ
diff --git a/kolourpaint/pics/crsc-action-tool_pen.svgz b/kolourpaint/pics/crsc-action-tool_pen.svgz
new file mode 100644
index 00000000..da783fda
--- /dev/null
+++ b/kolourpaint/pics/crsc-action-tool_pen.svgz
Binary files differ
diff --git a/kolourpaint/pics/crsc-action-tool_polygon.svgz b/kolourpaint/pics/crsc-action-tool_polygon.svgz
new file mode 100644
index 00000000..2e8e9066
--- /dev/null
+++ b/kolourpaint/pics/crsc-action-tool_polygon.svgz
Binary files differ
diff --git a/kolourpaint/pics/crsc-action-tool_polyline.svgz b/kolourpaint/pics/crsc-action-tool_polyline.svgz
new file mode 100644
index 00000000..f5f2e881
--- /dev/null
+++ b/kolourpaint/pics/crsc-action-tool_polyline.svgz
Binary files differ
diff --git a/kolourpaint/pics/crsc-action-tool_rect_selection.svgz b/kolourpaint/pics/crsc-action-tool_rect_selection.svgz
new file mode 100644
index 00000000..ac7ae6cf
--- /dev/null
+++ b/kolourpaint/pics/crsc-action-tool_rect_selection.svgz
Binary files differ
diff --git a/kolourpaint/pics/crsc-action-tool_rectangle.svgz b/kolourpaint/pics/crsc-action-tool_rectangle.svgz
new file mode 100644
index 00000000..c12ebf7c
--- /dev/null
+++ b/kolourpaint/pics/crsc-action-tool_rectangle.svgz
Binary files differ
diff --git a/kolourpaint/pics/crsc-action-tool_rounded_rectangle.svgz b/kolourpaint/pics/crsc-action-tool_rounded_rectangle.svgz
new file mode 100644
index 00000000..37625f3f
--- /dev/null
+++ b/kolourpaint/pics/crsc-action-tool_rounded_rectangle.svgz
Binary files differ
diff --git a/kolourpaint/pics/crsc-action-tool_spraycan.svgz b/kolourpaint/pics/crsc-action-tool_spraycan.svgz
new file mode 100644
index 00000000..6095c388
--- /dev/null
+++ b/kolourpaint/pics/crsc-action-tool_spraycan.svgz
Binary files differ
diff --git a/kolourpaint/pics/crsc-action-tool_text.svgz b/kolourpaint/pics/crsc-action-tool_text.svgz
new file mode 100644
index 00000000..c6be2b8f
--- /dev/null
+++ b/kolourpaint/pics/crsc-action-tool_text.svgz
Binary files differ
diff --git a/kolourpaint/pics/custom/Makefile.am b/kolourpaint/pics/custom/Makefile.am
new file mode 100644
index 00000000..e796e21a
--- /dev/null
+++ b/kolourpaint/pics/custom/Makefile.am
@@ -0,0 +1,10 @@
+customicondir = $(kde_datadir)/kolourpaint/pics
+customicon_DATA = tool_spraycan_9x9.png tool_spraycan_17x17.png tool_spraycan_29x29.png \
+ color_transparent_26x26.png colorbutton_swap_16x16.png \
+ option_opaque.png option_transparent.png \
+ resize.png scale.png smooth_scale.png \
+ image_skew_horizontal.png image_skew_vertical.png \
+ image_rotate_anticlockwise.png image_rotate_clockwise.png
+
+EXTRA_DIST = $(customicon_DATA)
+
diff --git a/kolourpaint/pics/custom/color_transparent_26x26.png b/kolourpaint/pics/custom/color_transparent_26x26.png
new file mode 100644
index 00000000..3ba3d39e
--- /dev/null
+++ b/kolourpaint/pics/custom/color_transparent_26x26.png
Binary files differ
diff --git a/kolourpaint/pics/custom/colorbutton_swap_16x16.png b/kolourpaint/pics/custom/colorbutton_swap_16x16.png
new file mode 100644
index 00000000..cebe9ed5
--- /dev/null
+++ b/kolourpaint/pics/custom/colorbutton_swap_16x16.png
Binary files differ
diff --git a/kolourpaint/pics/custom/image_rotate_anticlockwise.png b/kolourpaint/pics/custom/image_rotate_anticlockwise.png
new file mode 100644
index 00000000..8ef21bf2
--- /dev/null
+++ b/kolourpaint/pics/custom/image_rotate_anticlockwise.png
Binary files differ
diff --git a/kolourpaint/pics/custom/image_rotate_clockwise.png b/kolourpaint/pics/custom/image_rotate_clockwise.png
new file mode 100644
index 00000000..dee00f4a
--- /dev/null
+++ b/kolourpaint/pics/custom/image_rotate_clockwise.png
Binary files differ
diff --git a/kolourpaint/pics/custom/image_skew_horizontal.png b/kolourpaint/pics/custom/image_skew_horizontal.png
new file mode 100644
index 00000000..c27c8223
--- /dev/null
+++ b/kolourpaint/pics/custom/image_skew_horizontal.png
Binary files differ
diff --git a/kolourpaint/pics/custom/image_skew_vertical.png b/kolourpaint/pics/custom/image_skew_vertical.png
new file mode 100644
index 00000000..e84ac257
--- /dev/null
+++ b/kolourpaint/pics/custom/image_skew_vertical.png
Binary files differ
diff --git a/kolourpaint/pics/custom/option_opaque.png b/kolourpaint/pics/custom/option_opaque.png
new file mode 100644
index 00000000..ab442ecb
--- /dev/null
+++ b/kolourpaint/pics/custom/option_opaque.png
Binary files differ
diff --git a/kolourpaint/pics/custom/option_transparent.png b/kolourpaint/pics/custom/option_transparent.png
new file mode 100644
index 00000000..e751d7e9
--- /dev/null
+++ b/kolourpaint/pics/custom/option_transparent.png
Binary files differ
diff --git a/kolourpaint/pics/custom/resize.png b/kolourpaint/pics/custom/resize.png
new file mode 100644
index 00000000..0046cbcf
--- /dev/null
+++ b/kolourpaint/pics/custom/resize.png
Binary files differ
diff --git a/kolourpaint/pics/custom/scale.png b/kolourpaint/pics/custom/scale.png
new file mode 100644
index 00000000..9f0904dd
--- /dev/null
+++ b/kolourpaint/pics/custom/scale.png
Binary files differ
diff --git a/kolourpaint/pics/custom/smooth_scale.png b/kolourpaint/pics/custom/smooth_scale.png
new file mode 100644
index 00000000..5c48a6e2
--- /dev/null
+++ b/kolourpaint/pics/custom/smooth_scale.png
Binary files differ
diff --git a/kolourpaint/pics/custom/tool_spraycan_17x17.png b/kolourpaint/pics/custom/tool_spraycan_17x17.png
new file mode 100644
index 00000000..c5d228b0
--- /dev/null
+++ b/kolourpaint/pics/custom/tool_spraycan_17x17.png
Binary files differ
diff --git a/kolourpaint/pics/custom/tool_spraycan_29x29.png b/kolourpaint/pics/custom/tool_spraycan_29x29.png
new file mode 100644
index 00000000..47cfce16
--- /dev/null
+++ b/kolourpaint/pics/custom/tool_spraycan_29x29.png
Binary files differ
diff --git a/kolourpaint/pics/custom/tool_spraycan_9x9.png b/kolourpaint/pics/custom/tool_spraycan_9x9.png
new file mode 100644
index 00000000..85dde5a8
--- /dev/null
+++ b/kolourpaint/pics/custom/tool_spraycan_9x9.png
Binary files differ
diff --git a/kolourpaint/pics/hi16-app-kolourpaint.png b/kolourpaint/pics/hi16-app-kolourpaint.png
new file mode 100644
index 00000000..7802218f
--- /dev/null
+++ b/kolourpaint/pics/hi16-app-kolourpaint.png
Binary files differ
diff --git a/kolourpaint/pics/hi22-app-kolourpaint.png b/kolourpaint/pics/hi22-app-kolourpaint.png
new file mode 100644
index 00000000..9f411e5c
--- /dev/null
+++ b/kolourpaint/pics/hi22-app-kolourpaint.png
Binary files differ
diff --git a/kolourpaint/pics/hi32-app-kolourpaint.png b/kolourpaint/pics/hi32-app-kolourpaint.png
new file mode 100644
index 00000000..33e3e6f2
--- /dev/null
+++ b/kolourpaint/pics/hi32-app-kolourpaint.png
Binary files differ
diff --git a/kolourpaint/pics/hi48-app-kolourpaint.png b/kolourpaint/pics/hi48-app-kolourpaint.png
new file mode 100644
index 00000000..6878b110
--- /dev/null
+++ b/kolourpaint/pics/hi48-app-kolourpaint.png
Binary files differ
diff --git a/kolourpaint/pics/hisc-app-kolourpaint.svgz b/kolourpaint/pics/hisc-app-kolourpaint.svgz
new file mode 100644
index 00000000..9823cf97
--- /dev/null
+++ b/kolourpaint/pics/hisc-app-kolourpaint.svgz
Binary files differ
diff --git a/kolourpaint/pixmapfx/Makefile.am b/kolourpaint/pixmapfx/Makefile.am
new file mode 100644
index 00000000..dfa1d697
--- /dev/null
+++ b/kolourpaint/pixmapfx/Makefile.am
@@ -0,0 +1,19 @@
+INCLUDES = -I$(srcdir)/.. -I$(srcdir)/../cursors -I$(srcdir)/../interfaces \
+ -I$(srcdir)/../pixmapfx \
+ -I$(srcdir)/../tools \
+ -I$(srcdir)/../views \
+ -I$(srcdir)/../widgets $(all_includes)
+
+noinst_LTLIBRARIES = libkolourpaintpixmapfx.la
+libkolourpaintpixmapfx_la_SOURCES = kpcoloreffect.cpp \
+ kpeffectbalance.cpp \
+ kpeffectblursharpen.cpp \
+ kpeffectemboss.cpp \
+ kpeffectflatten.cpp \
+ kpeffectinvert.cpp \
+ kpeffectreducecolors.cpp \
+ kpeffectsdialog.cpp \
+ kpfloodfill.cpp \
+ kppixmapfx.cpp
+
+METASOURCES = AUTO
diff --git a/kolourpaint/pixmapfx/kpcoloreffect.cpp b/kolourpaint/pixmapfx/kpcoloreffect.cpp
new file mode 100644
index 00000000..1660c1fa
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpcoloreffect.cpp
@@ -0,0 +1,168 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <kpcoloreffect.h>
+
+#include <qapplication.h>
+#include <qpixmap.h>
+
+#include <kdialog.h>
+#include <klocale.h>
+
+#include <kpdefs.h>
+#include <kpdocument.h>
+#include <kpmainwindow.h>
+#include <kpselection.h>
+
+
+kpColorEffectCommand::kpColorEffectCommand (const QString &name,
+ bool actOnSelection,
+ kpMainWindow *mainWindow)
+ : kpCommand (mainWindow),
+ m_name (name),
+ m_actOnSelection (actOnSelection),
+ m_oldPixmapPtr (0)
+{
+}
+
+kpColorEffectCommand::~kpColorEffectCommand ()
+{
+ delete m_oldPixmapPtr; m_oldPixmapPtr = 0;
+}
+
+
+// public virtual [base kpCommand]
+QString kpColorEffectCommand::name () const
+{
+ if (m_actOnSelection)
+ return i18n ("Selection: %1").arg (m_name);
+ else
+ return m_name;
+}
+
+
+// public virtual [base kpCommand]
+int kpColorEffectCommand::size () const
+{
+ return kpPixmapFX::pixmapSize (m_oldPixmapPtr);
+}
+
+
+// public virtual [base kpCommand]
+void kpColorEffectCommand::execute ()
+{
+ kpDocument *doc = document ();
+ if (!doc)
+ return;
+
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+
+ const QPixmap oldPixmap = *doc->pixmap (m_actOnSelection);
+
+ if (!isInvertible ())
+ {
+ m_oldPixmapPtr = new QPixmap ();
+ *m_oldPixmapPtr = oldPixmap;
+ }
+
+
+ QPixmap newPixmap = /*pure virtual*/applyColorEffect (oldPixmap);
+
+ doc->setPixmap (m_actOnSelection, newPixmap);
+
+
+ QApplication::restoreOverrideCursor ();
+}
+
+// public virtual [base kpCommand]
+void kpColorEffectCommand::unexecute ()
+{
+ kpDocument *doc = document ();
+ if (!doc)
+ return;
+
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+
+ QPixmap newPixmap;
+
+ if (!isInvertible ())
+ {
+ newPixmap = *m_oldPixmapPtr;
+ }
+ else
+ {
+ newPixmap = /*pure virtual*/applyColorEffect (*doc->pixmap (m_actOnSelection));
+ }
+
+ doc->setPixmap (m_actOnSelection, newPixmap);
+
+
+ delete m_oldPixmapPtr; m_oldPixmapPtr = 0;
+
+
+ QApplication::restoreOverrideCursor ();
+}
+
+
+kpColorEffectWidget::kpColorEffectWidget (bool actOnSelection,
+ kpMainWindow *mainWindow,
+ QWidget *parent, const char *name)
+ : QWidget (parent, name),
+ m_actOnSelection (actOnSelection),
+ m_mainWindow (mainWindow)
+{
+}
+
+kpColorEffectWidget::~kpColorEffectWidget ()
+{
+}
+
+
+// public
+QString kpColorEffectWidget::caption () const
+{
+ return QString::null;
+}
+
+
+// protected
+int kpColorEffectWidget::marginHint () const
+{
+ return 0;
+}
+
+// protected
+int kpColorEffectWidget::spacingHint () const
+{
+ return KDialog::spacingHint ();
+}
+
+
+#include <kpcoloreffect.moc>
diff --git a/kolourpaint/pixmapfx/kpcoloreffect.h b/kolourpaint/pixmapfx/kpcoloreffect.h
new file mode 100644
index 00000000..8b3dfd09
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpcoloreffect.h
@@ -0,0 +1,111 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef KP_COLOR_EFFECT_H
+#define KP_COLOR_EFFECT_H
+
+#include <qstring.h>
+#include <qwidget.h>
+
+#include <kpcommandhistory.h>
+
+class QPixmap;
+
+class kpDocument;
+class kpMainWindow;
+
+
+class kpColorEffectCommand : public kpCommand
+{
+public:
+ kpColorEffectCommand (const QString &name,
+ bool actOnSelection,
+ kpMainWindow *mainWindow);
+ virtual ~kpColorEffectCommand ();
+
+ virtual QString name () const;
+ virtual int size () const;
+
+public:
+ virtual void execute ();
+ virtual void unexecute ();
+
+public:
+ // Return true if applyColorEffect(applyColorEffect(pixmap)) == pixmap
+ // to avoid storing the old pixmap, saving memory.
+ virtual bool isInvertible () const { return false; }
+
+protected:
+ virtual QPixmap applyColorEffect (const QPixmap &pixmap) = 0;
+
+private:
+ QString m_name;
+ bool m_actOnSelection;
+
+ QPixmap *m_oldPixmapPtr;
+};
+
+
+class kpColorEffectWidget : public QWidget
+{
+Q_OBJECT
+
+public:
+ kpColorEffectWidget (bool actOnSelection,
+ kpMainWindow *mainWindow,
+ QWidget *parent, const char *name = 0);
+ virtual ~kpColorEffectWidget ();
+
+signals:
+ void settingsChangedNoWaitCursor ();
+
+ void settingsChanged ();
+
+ // (same as settingsChanged() but preview doesn't update until there
+ // has been no activity for a while - used for sliders in slow effects)
+ void settingsChangedDelayed ();
+
+public:
+ virtual QString caption () const;
+
+ virtual bool isNoOp () const = 0;
+ virtual QPixmap applyColorEffect (const QPixmap &pixmap) = 0;
+
+ virtual kpColorEffectCommand *createCommand () const = 0;
+
+protected:
+ int marginHint () const;
+ int spacingHint () const;
+
+protected:
+ bool m_actOnSelection;
+ kpMainWindow *m_mainWindow;
+};
+
+
+#endif // KP_COLOR_EFFECT_H
diff --git a/kolourpaint/pixmapfx/kpeffectbalance.cpp b/kolourpaint/pixmapfx/kpeffectbalance.cpp
new file mode 100644
index 00000000..f4494d29
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpeffectbalance.cpp
@@ -0,0 +1,517 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG_KP_EFFECT_BALANCE 0
+
+
+#include <kpeffectbalance.h>
+
+#include <math.h>
+
+#include <qfontmetrics.h>
+#include <qimage.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpixmap.h>
+#include <qpushbutton.h>
+
+#include <kcombobox.h>
+#include <kdebug.h>
+#include <kimageeffect.h>
+#include <klocale.h>
+#include <knuminput.h>
+
+#include <kppixmapfx.h>
+
+
+#if DEBUG_KP_EFFECT_BALANCE
+ #include <qdatetime.h>
+#endif
+
+
+kpEffectBalanceCommand::kpEffectBalanceCommand (int channels,
+ int brightness, int contrast, int gamma,
+ bool actOnSelection,
+ kpMainWindow *mainWindow)
+ : kpColorEffectCommand (i18n ("Balance"), actOnSelection, mainWindow),
+ m_channels (channels),
+ m_brightness (brightness), m_contrast (contrast), m_gamma (gamma)
+{
+}
+
+kpEffectBalanceCommand::~kpEffectBalanceCommand ()
+{
+}
+
+
+static inline int between0And255 (int val)
+{
+ if (val < 0)
+ return 0;
+ else if (val > 255)
+ return 255;
+ else
+ return val;
+}
+
+
+static inline int brightness (int base, int strength)
+{
+ return between0And255 (base + strength * 255 / 50);
+}
+
+static inline int contrast (int base, int strength)
+{
+ return between0And255 ((base - 127) * (strength + 50) / 50 + 127);
+}
+
+static inline int gamma (int base, int strength)
+{
+ return between0And255 (qRound (255.0 * pow (base / 255.0, 1.0 / pow (10, strength / 50.0))));
+}
+
+
+static inline int brightnessContrastGamma (int base,
+ int newBrightness,
+ int newContrast,
+ int newGamma)
+{
+ return gamma (contrast (brightness (base, newBrightness),
+ newContrast),
+ newGamma);
+}
+
+static inline QRgb brightnessContrastGammaForRGB (QRgb rgb,
+ int channels,
+ int brightness, int contrast, int gamma)
+{
+ int red = qRed (rgb);
+ int green = qGreen (rgb);
+ int blue = qBlue (rgb);
+
+
+ if (channels & kpEffectBalanceCommand::Red)
+ red = brightnessContrastGamma (red, brightness, contrast, gamma);
+ if (channels & kpEffectBalanceCommand::Green)
+ green = brightnessContrastGamma (green, brightness, contrast, gamma);
+ if (channels & kpEffectBalanceCommand::Blue)
+ blue = brightnessContrastGamma (blue, brightness, contrast, gamma);
+
+
+ return qRgba (red, green, blue, qAlpha (rgb));
+}
+
+
+// public static
+QPixmap kpEffectBalanceCommand::applyColorEffect (const QPixmap &pixmap,
+ int channels,
+ int brightness, int contrast, int gamma)
+{
+#if DEBUG_KP_EFFECT_BALANCE
+ kdDebug () << "kpEffectBalanceCommand::applyColorEffect("
+ << "channels=" << channels
+ << ",brightness=" << brightness
+ << ",contrast=" << contrast
+ << ",gamma=" << gamma
+ << ")" << endl;
+ QTime timer; timer.start ();
+#endif
+
+ QImage image = kpPixmapFX::convertToImage (pixmap);
+#if DEBUG_KP_EFFECT_BALANCE
+ kdDebug () << "\tconvertToImage=" << timer.restart () << endl;
+#endif
+
+
+ Q_UINT8 transformRed [256],
+ transformGreen [256],
+ transformBlue [256];
+
+ for (int i = 0; i < 256; i++)
+ {
+ Q_UINT8 applied = (Q_UINT8) brightnessContrastGamma (i, brightness, contrast, gamma);
+
+ if (channels & kpEffectBalanceCommand::Red)
+ transformRed [i] = applied;
+ else
+ transformRed [i] = i;
+
+ if (channels & kpEffectBalanceCommand::Green)
+ transformGreen [i] = applied;
+ else
+ transformGreen [i] = i;
+
+ if (channels & kpEffectBalanceCommand::Blue)
+ transformBlue [i] = applied;
+ else
+ transformBlue [i] = i;
+ }
+
+#if DEBUG_KP_EFFECT_BALANCE
+ kdDebug () << "\tbuild lookup=" << timer.restart () << endl;
+#endif
+
+
+ if (image.depth () > 8)
+ {
+ for (int y = 0; y < image.height (); y++)
+ {
+ for (int x = 0; x < image.width (); x++)
+ {
+ const QRgb rgb = image.pixel (x, y);
+
+ const Q_UINT8 red = (Q_UINT8) qRed (rgb);
+ const Q_UINT8 green = (Q_UINT8) qGreen (rgb);
+ const Q_UINT8 blue = (Q_UINT8) qBlue (rgb);
+ const Q_UINT8 alpha = (Q_UINT8) qAlpha (rgb);
+
+ image.setPixel (x, y,
+ qRgba (transformRed [red],
+ transformGreen [green],
+ transformBlue [blue],
+ alpha));
+
+ #if 0
+ image.setPixel (x, y,
+ brightnessContrastGammaForRGB (image.pixel (x, y),
+ channels,
+ brightness, contrast, gamma));
+ #endif
+ }
+ }
+ }
+ else
+ {
+ for (int i = 0; i < image.numColors (); i++)
+ {
+ const QRgb rgb = image.color (i);
+
+ const Q_UINT8 red = (Q_UINT8) qRed (rgb);
+ const Q_UINT8 green = (Q_UINT8) qGreen (rgb);
+ const Q_UINT8 blue = (Q_UINT8) qBlue (rgb);
+ const Q_UINT8 alpha = (Q_UINT8) qAlpha (rgb);
+
+ image.setColor (i,
+ qRgba (transformRed [red],
+ transformGreen [green],
+ transformBlue [blue],
+ alpha));
+
+ #if 0
+ image.setColor (i,
+ brightnessContrastGammaForRGB (image.color (i),
+ channels,
+ brightness, contrast, gamma));
+ #endif
+ }
+
+ }
+#if DEBUG_KP_EFFECT_BALANCE
+ kdDebug () << "\teffect=" << timer.restart () << endl;
+#endif
+
+ const QPixmap retPixmap = kpPixmapFX::convertToPixmap (image);
+#if DEBUG_KP_EFFECT_BALANCE
+ kdDebug () << "\tconvertToPixmap=" << timer.restart () << endl;
+#endif
+
+ return retPixmap;
+}
+
+// protected virtual [base kpColorEffectCommand]
+QPixmap kpEffectBalanceCommand::applyColorEffect (const QPixmap &pixmap)
+{
+ return applyColorEffect (pixmap, m_channels,
+ m_brightness, m_contrast, m_gamma);
+}
+
+
+
+kpEffectBalanceWidget::kpEffectBalanceWidget (bool actOnSelection,
+ kpMainWindow *mainWindow,
+ QWidget *parent, const char *name)
+ : kpColorEffectWidget (actOnSelection, mainWindow, parent, name)
+{
+ QGridLayout *lay = new QGridLayout (this, 5, 5, marginHint (), spacingHint ());
+
+
+ QLabel *brightnessLabel = new QLabel (i18n ("&Brightness:"), this);
+ m_brightnessInput = new KIntNumInput (0/*value*/, this);
+ m_brightnessInput->setRange (-50, 50, 1/*step*/, true/*slider*/);
+ QPushButton *brightnessResetPushButton = new QPushButton (i18n ("Re&set"), this);
+
+ QLabel *contrastLabel = new QLabel (i18n ("Co&ntrast:"), this);
+ m_contrastInput = new KIntNumInput (0/*value*/, this);
+ m_contrastInput->setRange (-50, 50, 1/*step*/, true/*slider*/);
+ QPushButton *contrastResetPushButton = new QPushButton (i18n ("&Reset"), this);
+
+ QLabel *gammaLabel = new QLabel (i18n ("&Gamma:"), this);
+ m_gammaInput = new KIntNumInput (0/*value*/, this);
+ m_gammaInput->setRange (-50, 50, 1/*step*/, true/*slider*/);
+ // TODO: This is what should be shown in the m_gammaInput spinbox
+ m_gammaLabel = new QLabel (this);
+ // TODO: This doesn't seem to be wide enough with some fonts so the
+ // whole layout moves when we drag the gamma slider.
+ m_gammaLabel->setMinimumWidth (m_gammaLabel->fontMetrics ().width (" 10.00 "));
+ m_gammaLabel->setAlignment (m_gammaLabel->alignment () | Qt::AlignRight);
+ QPushButton *gammaResetPushButton = new QPushButton (i18n ("Rese&t"), this);
+
+
+ QWidget *spaceWidget = new QLabel (this);
+ spaceWidget->setFixedSize (1, spacingHint ());
+
+
+ QLabel *channelLabel = new QLabel (i18n ("C&hannels:"), this);
+ m_channelsComboBox = new KComboBox (this);
+ m_channelsComboBox->insertItem (i18n ("All"));
+ m_channelsComboBox->insertItem (i18n ("Red"));
+ m_channelsComboBox->insertItem (i18n ("Green"));
+ m_channelsComboBox->insertItem (i18n ("Blue"));
+
+
+ QPushButton *resetPushButton = new QPushButton (i18n ("Reset &All Values"), this);
+
+
+ brightnessLabel->setBuddy (m_brightnessInput);
+ contrastLabel->setBuddy (m_contrastInput);
+ gammaLabel->setBuddy (m_gammaInput);
+
+ channelLabel->setBuddy (m_channelsComboBox);
+
+
+ lay->addWidget (brightnessLabel, 0, 0);
+ lay->addMultiCellWidget (m_brightnessInput, 0, 0, 1, 2);
+ lay->addWidget (brightnessResetPushButton, 0, 4);
+
+ lay->addWidget (contrastLabel, 1, 0);
+ lay->addMultiCellWidget (m_contrastInput, 1, 1, 1, 2);
+ lay->addWidget (contrastResetPushButton, 1, 4);
+
+ lay->addWidget (gammaLabel, 2, 0);
+ lay->addMultiCellWidget (m_gammaInput, 2, 2, 1, 2);
+ lay->addWidget (m_gammaLabel, 2, 3);
+ lay->addWidget (gammaResetPushButton, 2, 4);
+
+ lay->addMultiCellWidget (spaceWidget, 3, 3, 0, 4);
+ lay->addMultiCellWidget (resetPushButton, 4, 4, 2, 4, Qt::AlignRight);
+
+ lay->addWidget (channelLabel, 4, 0);
+ lay->addWidget (m_channelsComboBox, 4, 1, Qt::AlignLeft);
+ //lay->addWidget (resetPushButton, 4, 2, Qt::AlignRight);
+
+ lay->setColStretch (1, 1);
+
+
+ // (no need for settingsChangedDelayed() since BCG effect is so fast :))
+ connect (m_brightnessInput, SIGNAL (valueChanged (int)),
+ this, SIGNAL (settingsChangedNoWaitCursor ()));
+ connect (m_contrastInput, SIGNAL (valueChanged (int)),
+ this, SIGNAL (settingsChangedNoWaitCursor ()));
+
+ connect (m_gammaInput, SIGNAL (valueChanged (int)),
+ this, SLOT (recalculateGammaLabel ()));
+ connect (m_gammaInput, SIGNAL (valueChanged (int)),
+ this, SIGNAL (settingsChangedNoWaitCursor ()));
+
+ connect (m_channelsComboBox, SIGNAL (activated (int)),
+ this, SIGNAL (settingsChanged ()));
+
+ connect (brightnessResetPushButton, SIGNAL (clicked ()),
+ this, SLOT (resetBrightness ()));
+ connect (contrastResetPushButton, SIGNAL (clicked ()),
+ this, SLOT (resetContrast ()));
+ connect (gammaResetPushButton, SIGNAL (clicked ()),
+ this, SLOT (resetGamma ()));
+
+ connect (resetPushButton, SIGNAL (clicked ()),
+ this, SLOT (resetAll ()));
+
+
+ recalculateGammaLabel ();
+}
+
+kpEffectBalanceWidget::~kpEffectBalanceWidget ()
+{
+}
+
+
+// public virtual [base kpColorEffectWidget]
+QString kpEffectBalanceWidget::caption () const
+{
+ return i18n ("Settings");
+}
+
+
+// public virtual [base kpColorEffectWidget]
+bool kpEffectBalanceWidget::isNoOp () const
+{
+ return (brightness () == 0 && contrast () == 0 && gamma () == 0);
+}
+
+// public virtual [base kpColorEffectWidget]
+QPixmap kpEffectBalanceWidget::applyColorEffect (const QPixmap &pixmap)
+{
+ return kpEffectBalanceCommand::applyColorEffect (pixmap,
+ channels (), brightness (), contrast (), gamma ());
+}
+
+// public virtual [base kpColorEffectWidget]
+kpColorEffectCommand *kpEffectBalanceWidget::createCommand () const
+{
+ return new kpEffectBalanceCommand (channels (),
+ brightness (), contrast (), gamma (),
+ m_actOnSelection,
+ m_mainWindow);
+}
+
+
+// protected
+int kpEffectBalanceWidget::channels () const
+{
+ switch (m_channelsComboBox->currentItem ())
+ {
+ default:
+ case 0:
+ return kpEffectBalanceCommand::RGB;
+
+ case 1:
+ return kpEffectBalanceCommand::Red;
+
+ case 2:
+ return kpEffectBalanceCommand::Green;
+
+ case 3:
+ return kpEffectBalanceCommand::Blue;
+ }
+}
+
+
+// protected
+int kpEffectBalanceWidget::brightness () const
+{
+ return m_brightnessInput->value ();
+}
+
+// protected
+int kpEffectBalanceWidget::contrast () const
+{
+ return m_contrastInput->value ();
+}
+
+// protected
+int kpEffectBalanceWidget::gamma () const
+{
+ return m_gammaInput->value ();
+}
+
+
+// protected slot
+void kpEffectBalanceWidget::recalculateGammaLabel ()
+{
+ m_gammaLabel->setText (
+ " " +
+ QString::number (pow (10, gamma () / 50.0),
+ 'f'/*[-]9.9*/,
+ 2/*precision*/) +
+ " ");
+ m_gammaLabel->repaint ();
+}
+
+
+// protected slot
+void kpEffectBalanceWidget::resetBrightness ()
+{
+ if (brightness () == 0)
+ return;
+
+ bool sb = signalsBlocked ();
+
+ if (!sb) blockSignals (true);
+ m_brightnessInput->setValue (0);
+ if (!sb) blockSignals (false);
+
+ // Immediate update (if signals aren't blocked)
+ emit settingsChanged ();
+}
+
+// protected slot
+void kpEffectBalanceWidget::resetContrast ()
+{
+ if (contrast () == 0)
+ return;
+
+ bool sb = signalsBlocked ();
+
+ if (!sb) blockSignals (true);
+ m_contrastInput->setValue (0);
+ if (!sb) blockSignals (false);
+
+ // Immediate update (if signals aren't blocked)
+ emit settingsChanged ();
+}
+
+// protected slot
+void kpEffectBalanceWidget::resetGamma ()
+{
+ if (gamma () == 0)
+ return;
+
+ bool sb = signalsBlocked ();
+
+ if (!sb) blockSignals (true);
+ m_gammaInput->setValue (0);
+ recalculateGammaLabel ();
+ if (!sb) blockSignals (false);
+
+ // Immediate update (if signals aren't blocked)
+ emit settingsChanged ();
+}
+
+
+// protected slot
+void kpEffectBalanceWidget::resetAll ()
+{
+ if (isNoOp ())
+ return;
+
+ // Prevent multiple settingsChanged() which would normally result in
+ // redundant, expensive preview repaints
+ blockSignals (true);
+
+ resetBrightness ();
+ resetContrast ();
+ resetGamma ();
+
+ recalculateGammaLabel ();
+
+ blockSignals (false);
+
+ emit settingsChanged ();
+}
+
+
+#include <kpeffectbalance.moc>
diff --git a/kolourpaint/pixmapfx/kpeffectbalance.h b/kolourpaint/pixmapfx/kpeffectbalance.h
new file mode 100644
index 00000000..b045159f
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpeffectbalance.h
@@ -0,0 +1,116 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef KP_EFFECT_BALANCE_H
+#define KP_EFFECT_BALANCE_H
+
+#include <kpcoloreffect.h>
+
+
+class QLabel;
+
+class KComboBox;
+class KIntNumInput;
+
+class kpMainWindowe;
+
+
+class kpEffectBalanceCommand : public kpColorEffectCommand
+{
+public:
+ enum Channel
+ {
+ None = 0,
+ Red = 1, Green = 2, Blue = 4,
+ RGB = Red | Green | Blue
+ };
+
+ // <brightness>, <contrast> & <gamma> are from -50 to 50
+
+ kpEffectBalanceCommand (int channels,
+ int brightness, int contrast, int gamma,
+ bool actOnSelection,
+ kpMainWindow *mainWindow);
+ virtual ~kpEffectBalanceCommand ();
+
+ static QPixmap applyColorEffect (const QPixmap &pixmap,
+ int channels,
+ int brightness, int contrast, int gamma);
+
+protected:
+ virtual QPixmap applyColorEffect (const QPixmap &pixmap);
+
+protected:
+ int m_channels;
+ int m_brightness, m_contrast, m_gamma;
+};
+
+
+class kpEffectBalanceWidget : public kpColorEffectWidget
+{
+Q_OBJECT
+
+public:
+ kpEffectBalanceWidget (bool actOnSelection,
+ kpMainWindow *mainWindow,
+ QWidget *parent, const char *name = 0);
+ virtual ~kpEffectBalanceWidget ();
+
+ virtual QString caption () const;
+
+ virtual bool isNoOp () const;
+ virtual QPixmap applyColorEffect (const QPixmap &pixmap);
+
+ virtual kpColorEffectCommand *createCommand () const;
+
+protected:
+ int channels () const;
+
+ int brightness () const;
+ int contrast () const;
+ int gamma () const;
+
+protected slots:
+ void recalculateGammaLabel ();
+
+ void resetBrightness ();
+ void resetContrast ();
+ void resetGamma ();
+
+ void resetAll ();
+
+protected:
+ KIntNumInput *m_brightnessInput,
+ *m_contrastInput,
+ *m_gammaInput;
+ QLabel *m_gammaLabel;
+ KComboBox *m_channelsComboBox;
+};
+
+
+#endif // KP_EFFECT_BALANCE_H
diff --git a/kolourpaint/pixmapfx/kpeffectblursharpen.cpp b/kolourpaint/pixmapfx/kpeffectblursharpen.cpp
new file mode 100644
index 00000000..50c0b27d
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpeffectblursharpen.cpp
@@ -0,0 +1,291 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG_KP_EFFECT_BLUR_SHARPEN 0
+
+
+#include <kpeffectblursharpen.h>
+
+#include <qimage.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpixmap.h>
+#include <qpushbutton.h>
+
+#include <kdebug.h>
+#include <kimageeffect.h>
+#include <klocale.h>
+#include <knuminput.h>
+
+#include <kpmainwindow.h>
+#include <kppixmapfx.h>
+
+
+static QString nameForType (kpEffectBlurSharpenCommand::Type type)
+{
+ if (type == kpEffectBlurSharpenCommand::Blur)
+ return i18n ("Soften");
+ else if (type == kpEffectBlurSharpenCommand::Sharpen)
+ return i18n ("Sharpen");
+ else
+ return QString::null;
+}
+
+
+kpEffectBlurSharpenCommand::kpEffectBlurSharpenCommand (Type type,
+ double radius, double sigma,
+ int repeat,
+ bool actOnSelection,
+ kpMainWindow *mainWindow)
+ : kpColorEffectCommand (::nameForType (type), actOnSelection, mainWindow),
+ m_type (type),
+ m_radius (radius), m_sigma (sigma),
+ m_repeat (repeat)
+{
+}
+
+kpEffectBlurSharpenCommand::~kpEffectBlurSharpenCommand ()
+{
+}
+
+
+// public static
+QPixmap kpEffectBlurSharpenCommand::apply (const QPixmap &pixmap,
+ Type type, double radius, double sigma,
+ int repeat)
+{
+#if DEBUG_KP_EFFECT_BLUR_SHARPEN
+ kdDebug () << "kpEffectBlurSharpenCommand::apply(type="
+ << int (type)
+ << " radius=" << radius
+ << " sigma=" << sigma
+ << " repeat=" << repeat
+ << ")"
+ << endl;
+#endif
+
+ // (KImageEffect::(blur|sharpen)() ignores mask)
+ QPixmap usePixmap = kpPixmapFX::pixmapWithDefinedTransparentPixels (
+ pixmap,
+ Qt::white/*arbitrarily chosen*/);
+
+
+ QImage image = kpPixmapFX::convertToImage (usePixmap);
+
+ for (int i = 0; i < repeat; i++)
+ {
+ if (type == Blur)
+ image = KImageEffect::blur (image, radius, sigma);
+ else if (type == Sharpen)
+ image = KImageEffect::sharpen (image, radius, sigma);
+ }
+
+ QPixmap retPixmap = kpPixmapFX::convertToPixmap (image);
+
+
+ // KImageEffect::(blur|sharpen)() nukes mask - restore it
+ if (usePixmap.mask ())
+ retPixmap.setMask (*usePixmap.mask ());
+
+
+ return retPixmap;
+}
+
+// protected virtual [base kpColorEffectCommand]
+QPixmap kpEffectBlurSharpenCommand::applyColorEffect (const QPixmap &pixmap)
+{
+ return apply (pixmap, m_type, m_radius, m_sigma, m_repeat);
+}
+
+
+
+kpEffectBlurSharpenWidget::kpEffectBlurSharpenWidget (bool actOnSelection,
+ kpMainWindow *mainWindow,
+ QWidget *parent, const char *name)
+ : kpColorEffectWidget (actOnSelection, mainWindow, parent, name)
+{
+ QGridLayout *lay = new QGridLayout (this, 4, 2, marginHint (), spacingHint ());
+
+
+ QLabel *amountLabel = new QLabel (i18n ("&Amount:"), this);
+ m_amountInput = new KIntNumInput (this);
+ m_amountInput->setRange (-10, 10, 1/*step*/, true/*slider*/);
+
+ m_typeLabel = new QLabel (this);
+
+
+ amountLabel->setBuddy (m_amountInput);
+
+
+ lay->addWidget (amountLabel, 0, 0);
+ lay->addWidget (m_amountInput, 0, 1);
+
+ lay->addMultiCellWidget (m_typeLabel, 1, 1, 0, 1, Qt::AlignCenter);
+
+ lay->setColStretch (1, 1);
+
+
+ connect (m_amountInput, SIGNAL (valueChanged (int)),
+ this, SIGNAL (settingsChangedDelayed ()));
+
+ connect (m_amountInput, SIGNAL (valueChanged (int)),
+ this, SLOT (slotUpdateTypeLabel ()));
+}
+
+kpEffectBlurSharpenWidget::~kpEffectBlurSharpenWidget ()
+{
+}
+
+
+// public virtual [base kpColorEffectWidget]
+QString kpEffectBlurSharpenWidget::caption () const
+{
+ return QString::null;
+}
+
+
+// public virtual [base kpColorEffectWidget]
+bool kpEffectBlurSharpenWidget::isNoOp () const
+{
+ return (type () == kpEffectBlurSharpenCommand::None);
+}
+
+// public virtual [base kpColorEffectWidget]
+QPixmap kpEffectBlurSharpenWidget::applyColorEffect (const QPixmap &pixmap)
+{
+ return kpEffectBlurSharpenCommand::apply (pixmap,
+ type (), radius (), sigma (), repeat ());
+}
+
+// public virtual [base kpColorEffectWidget]
+kpColorEffectCommand *kpEffectBlurSharpenWidget::createCommand () const
+{
+ return new kpEffectBlurSharpenCommand (type (), radius (), sigma (), repeat (),
+ m_actOnSelection,
+ m_mainWindow);
+}
+
+
+// protected slot
+void kpEffectBlurSharpenWidget::slotUpdateTypeLabel ()
+{
+ QString text = ::nameForType (type ());
+
+#if DEBUG_KP_EFFECT_BLUR_SHARPEN
+ kdDebug () << "kpEffectBlurSharpenWidget::slotUpdateTypeLabel() text="
+ << text << endl;
+#endif
+ m_typeLabel->setText (text);
+}
+
+
+// protected
+kpEffectBlurSharpenCommand::Type kpEffectBlurSharpenWidget::type () const
+{
+ if (m_amountInput->value () == 0)
+ return kpEffectBlurSharpenCommand::None;
+ else if (m_amountInput->value () < 0)
+ return kpEffectBlurSharpenCommand::Blur;
+ else
+ return kpEffectBlurSharpenCommand::Sharpen;
+}
+
+// The numbers that follow were picked by experimentation.
+// I still have no idea what "radius" and "sigma" mean
+// (even after reading the API).
+
+// protected
+double kpEffectBlurSharpenWidget::radius () const
+{
+ if (m_amountInput->value () == 0)
+ return 0;
+
+ if (m_amountInput->value () < 0)
+ {
+ return 8;
+ }
+ else
+ {
+ const double SharpenMin = .1;
+ const double SharpenMax = 2.5;
+
+ return SharpenMin +
+ (m_amountInput->value () - 1) *
+ (SharpenMax - SharpenMin) /
+ (m_amountInput->maxValue () - 1);
+ }
+}
+
+// protected
+double kpEffectBlurSharpenWidget::sigma () const
+{
+ if (m_amountInput->value () == 0)
+ return 0;
+
+ if (m_amountInput->value () < 0)
+ {
+ const double BlurMin = .5;
+ const double BlurMax = 4;
+
+ return BlurMin +
+ (-m_amountInput->value () - 1) *
+ (BlurMax - BlurMin) /
+ (-m_amountInput->minValue () - 1);
+ }
+ else
+ {
+ const double SharpenMin = .5;
+ const double SharpenMax = 3.0;
+
+ return SharpenMin +
+ (m_amountInput->value () - 1) *
+ (SharpenMax - SharpenMin) /
+ (m_amountInput->maxValue () - 1);
+ }
+}
+
+// protected
+int kpEffectBlurSharpenWidget::repeat () const
+{
+ if (m_amountInput->value () == 0)
+ return 0;
+
+ if (m_amountInput->value () < 0)
+ return 1;
+ else
+ {
+ const double SharpenMin = 1;
+ const double SharpenMax = 2;
+
+ return qRound (SharpenMin +
+ (m_amountInput->value () - 1) *
+ (SharpenMax - SharpenMin) /
+ (m_amountInput->maxValue () - 1));
+ }
+}
+
+#include <kpeffectblursharpen.moc>
diff --git a/kolourpaint/pixmapfx/kpeffectblursharpen.h b/kolourpaint/pixmapfx/kpeffectblursharpen.h
new file mode 100644
index 00000000..3b12def1
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpeffectblursharpen.h
@@ -0,0 +1,105 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef KP_EFFECT_BLUR_SHARPEN_H
+#define KP_EFFECT_BLUR_SHARPEN_H
+
+
+#include <kpcolor.h>
+
+#include <kpcoloreffect.h>
+
+
+class QLabel;
+
+class KIntNumInput;
+
+class kpMainWindow;
+
+
+class kpEffectBlurSharpenCommand : public kpColorEffectCommand
+{
+public:
+ enum Type
+ {
+ None = 0, Blur, Sharpen
+ };
+
+ kpEffectBlurSharpenCommand (Type type,
+ double radius, double sigma,
+ int repeat,
+ bool actOnSelection,
+ kpMainWindow *mainWindow);
+ virtual ~kpEffectBlurSharpenCommand ();
+
+ static QPixmap apply (const QPixmap &pixmap,
+ Type type, double radius, double sigma,
+ int repeat);
+
+protected:
+ virtual QPixmap applyColorEffect (const QPixmap &pixmap);
+
+protected:
+ Type m_type;
+ double m_radius, m_sigma;
+ int m_repeat;
+};
+
+
+class kpEffectBlurSharpenWidget : public kpColorEffectWidget
+{
+Q_OBJECT
+
+public:
+ kpEffectBlurSharpenWidget (bool actOnSelection,
+ kpMainWindow *mainWindow,
+ QWidget *parent, const char *name = 0);
+ virtual ~kpEffectBlurSharpenWidget ();
+
+ virtual QString caption () const;
+
+ virtual bool isNoOp () const;
+ virtual QPixmap applyColorEffect (const QPixmap &pixmap);
+
+ virtual kpColorEffectCommand *createCommand () const;
+
+protected slots:
+ void slotUpdateTypeLabel ();
+
+protected:
+ kpEffectBlurSharpenCommand::Type type () const;
+ double radius () const;
+ double sigma () const;
+ int repeat () const;
+
+ KIntNumInput *m_amountInput;
+ QLabel *m_typeLabel;
+};
+
+
+#endif // KP_EFFECT_BLUR_SHARPEN_H
diff --git a/kolourpaint/pixmapfx/kpeffectemboss.cpp b/kolourpaint/pixmapfx/kpeffectemboss.cpp
new file mode 100644
index 00000000..e33f3a42
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpeffectemboss.cpp
@@ -0,0 +1,228 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG_KP_EFFECT_EMBOSS 0
+
+
+#include <kpeffectemboss.h>
+
+#include <qcheckbox.h>
+#include <qimage.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpixmap.h>
+#include <qpushbutton.h>
+
+#include <kdebug.h>
+#include <kimageeffect.h>
+#include <klocale.h>
+#include <knuminput.h>
+
+#include <kpmainwindow.h>
+#include <kppixmapfx.h>
+
+
+kpEffectEmbossCommand::kpEffectEmbossCommand (double radius, double sigma,
+ int repeat,
+ bool actOnSelection,
+ kpMainWindow *mainWindow)
+ : kpColorEffectCommand (i18n ("Emboss"), actOnSelection, mainWindow),
+ m_radius (radius), m_sigma (sigma),
+ m_repeat (repeat)
+{
+}
+
+kpEffectEmbossCommand::~kpEffectEmbossCommand ()
+{
+}
+
+
+// public static
+QPixmap kpEffectEmbossCommand::apply (const QPixmap &pixmap,
+ double radius, double sigma,
+ int repeat)
+{
+#if DEBUG_KP_EFFECT_EMBOSS
+ kdDebug () << "kpEffectEmbossCommand::apply()"
+ << " radius=" << radius
+ << " sigma=" << sigma
+ << " repeat=" << repeat
+ << ")"
+ << endl;
+#endif
+
+ // (KImageEffect::emboss() ignores mask)
+ QPixmap usePixmap = kpPixmapFX::pixmapWithDefinedTransparentPixels (
+ pixmap,
+ Qt::white/*arbitrarily chosen*/);
+
+
+ QImage image = kpPixmapFX::convertToImage (usePixmap);
+
+ for (int i = 0; i < repeat; i++)
+ {
+ image = KImageEffect::emboss (image, radius, sigma);
+ }
+
+ QPixmap retPixmap = kpPixmapFX::convertToPixmap (image);
+
+
+ // KImageEffect::emboss() nukes mask - restore it
+ if (usePixmap.mask ())
+ retPixmap.setMask (*usePixmap.mask ());
+
+
+ return retPixmap;
+}
+
+// protected virtual [base kpColorEffectCommand]
+QPixmap kpEffectEmbossCommand::applyColorEffect (const QPixmap &pixmap)
+{
+ return apply (pixmap, m_radius, m_sigma, m_repeat);
+}
+
+
+
+kpEffectEmbossWidget::kpEffectEmbossWidget (bool actOnSelection,
+ kpMainWindow *mainWindow,
+ QWidget *parent, const char *name)
+ : kpColorEffectWidget (actOnSelection, mainWindow, parent, name)
+{
+ QGridLayout *lay = new QGridLayout (this, 4, 2, marginHint (), spacingHint ());
+
+
+#if 0
+ QLabel *amountLabel = new QLabel (i18n ("&Amount:"), this);
+ m_amountInput = new KIntNumInput (this);
+ m_amountInput->setRange (0, 10, 1/*step*/, true/*slider*/);
+ m_amountInput->setSpecialValueText (i18n ("None"));
+
+
+ amountLabel->setBuddy (m_amountInput);
+
+
+ lay->addWidget (amountLabel, 0, 0);
+ lay->addWidget (m_amountInput, 0, 1);
+
+ lay->setColStretch (1, 1);
+
+
+ connect (m_amountInput, SIGNAL (valueChanged (int)),
+ this, SIGNAL (settingsChanged ()));
+#endif
+
+ m_enableCheckBox = new QCheckBox (i18n ("E&nable"), this);
+
+
+ lay->addMultiCellWidget (m_enableCheckBox, 0, 0, 0, 1, Qt::AlignCenter);
+
+
+ // (settingsChangedDelayed() instead of settingsChanged() so that the
+ // user can quickly press OK to apply effect to document directly and
+ // not have to wait for the also slow preview)
+ connect (m_enableCheckBox, SIGNAL (toggled (bool)),
+ this, SIGNAL (settingsChangedDelayed ()));
+}
+
+kpEffectEmbossWidget::~kpEffectEmbossWidget ()
+{
+}
+
+
+// public virtual [base kpColorEffectWidget]
+QString kpEffectEmbossWidget::caption () const
+{
+ return QString::null;
+}
+
+
+// public virtual [base kpColorEffectWidget]
+bool kpEffectEmbossWidget::isNoOp () const
+{
+ //return (m_amountInput->value () == 0);
+ return !m_enableCheckBox->isChecked ();
+}
+
+// public virtual [base kpColorEffectWidget]
+QPixmap kpEffectEmbossWidget::applyColorEffect (const QPixmap &pixmap)
+{
+ if (isNoOp ())
+ return pixmap;
+
+ return kpEffectEmbossCommand::apply (pixmap, radius (), sigma (), repeat ());
+}
+
+// public virtual [base kpColorEffectWidget]
+kpColorEffectCommand *kpEffectEmbossWidget::createCommand () const
+{
+ return new kpEffectEmbossCommand (radius (), sigma (), repeat (),
+ m_actOnSelection,
+ m_mainWindow);
+}
+
+
+// The numbers that follow were picked by experimentation.
+// I still have no idea what "radius" and "sigma" mean
+// (even after reading the API).
+
+// protected
+double kpEffectEmbossWidget::radius () const
+{
+ //if (m_amountInput->value () == 0)
+ // return 0;
+
+ return 0;
+}
+
+
+// protected
+double kpEffectEmbossWidget::sigma () const
+{
+#if 0
+ if (m_amountInput->value () == 0)
+ return 0;
+
+ const double Min = 1;
+ const double Max = 1.2;
+
+ return Min +
+ (m_amountInput->maxValue () - m_amountInput->value ()) *
+ (Max - Min) /
+ (m_amountInput->maxValue () - 1);
+#endif
+
+ return 1;
+}
+
+// protected
+int kpEffectEmbossWidget::repeat () const
+{
+ return 1;
+}
+
+
+#include <kpeffectemboss.moc>
diff --git a/kolourpaint/pixmapfx/kpeffectemboss.h b/kolourpaint/pixmapfx/kpeffectemboss.h
new file mode 100644
index 00000000..0234627f
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpeffectemboss.h
@@ -0,0 +1,93 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef KP_EFFECT_EMBOSS_H
+#define KP_EFFECT_EMBOSS_H
+
+
+#include <kpcolor.h>
+
+#include <kpcoloreffect.h>
+
+
+class QCheckBox;
+class KIntNumInput;
+
+class kpMainWindow;
+
+
+class kpEffectEmbossCommand : public kpColorEffectCommand
+{
+public:
+ kpEffectEmbossCommand (double radius, double sigma,
+ int repeat,
+ bool actOnSelection,
+ kpMainWindow *mainWindow);
+ virtual ~kpEffectEmbossCommand ();
+
+ static QPixmap apply (const QPixmap &pixmap,
+ double radius, double sigma,
+ int repeat);
+
+protected:
+ virtual QPixmap applyColorEffect (const QPixmap &pixmap);
+
+protected:
+ double m_radius, m_sigma;
+ int m_repeat;
+};
+
+
+class kpEffectEmbossWidget : public kpColorEffectWidget
+{
+Q_OBJECT
+
+public:
+ kpEffectEmbossWidget (bool actOnSelection,
+ kpMainWindow *mainWindow,
+ QWidget *parent, const char *name = 0);
+ virtual ~kpEffectEmbossWidget ();
+
+ virtual QString caption () const;
+
+ virtual bool isNoOp () const;
+ virtual QPixmap applyColorEffect (const QPixmap &pixmap);
+
+ virtual kpColorEffectCommand *createCommand () const;
+
+protected:
+ double radius () const;
+ double sigma () const;
+ int repeat () const;
+
+ //KIntNumInput *m_amountInput;
+ QCheckBox *m_enableCheckBox;
+};
+
+
+#endif // KP_EFFECT_EMBOSS_H
diff --git a/kolourpaint/pixmapfx/kpeffectflatten.cpp b/kolourpaint/pixmapfx/kpeffectflatten.cpp
new file mode 100644
index 00000000..6a81bca0
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpeffectflatten.cpp
@@ -0,0 +1,266 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG_KP_EFFECT_FLATTEN 0
+
+
+#include <kpeffectflatten.h>
+
+#include <qcheckbox.h>
+#include <qimage.h>
+#include <qlayout.h>
+#include <qpixmap.h>
+#include <qvbox.h>
+
+#include <kcolorbutton.h>
+#include <kconfig.h>
+#include <kdialog.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kimageeffect.h>
+#include <klocale.h>
+
+#include <kpdefs.h>
+#include <kppixmapfx.h>
+
+
+//
+// kpEffectFlattenCommand
+//
+
+kpEffectFlattenCommand::kpEffectFlattenCommand (const QColor &color1,
+ const QColor &color2,
+ bool actOnSelection,
+ kpMainWindow *mainWindow)
+ : kpColorEffectCommand (i18n ("Flatten"), actOnSelection, mainWindow),
+ m_color1 (color1), m_color2 (color2)
+{
+}
+
+kpEffectFlattenCommand::~kpEffectFlattenCommand ()
+{
+}
+
+
+// public static
+void kpEffectFlattenCommand::apply (QPixmap *destPixmapPtr,
+ const QColor &color1, const QColor &color2)
+{
+ if (!destPixmapPtr)
+ return;
+
+ QImage image = kpPixmapFX::convertToImage (*destPixmapPtr);
+ apply (&image, color1, color2);
+ *destPixmapPtr = kpPixmapFX::convertToPixmap (image);
+}
+
+// public static
+QPixmap kpEffectFlattenCommand::apply (const QPixmap &pm,
+ const QColor &color1, const QColor &color2)
+{
+ QImage image = kpPixmapFX::convertToImage (pm);
+ apply (&image, color1, color2);
+ return kpPixmapFX::convertToPixmap (image);
+}
+
+// public static
+void kpEffectFlattenCommand::apply (QImage *destImagePtr,
+ const QColor &color1, const QColor &color2)
+{
+ if (!destImagePtr)
+ return;
+
+ KImageEffect::flatten (*destImagePtr/*ref*/, color1, color2);
+}
+
+// public static
+QImage kpEffectFlattenCommand::apply (const QImage &img,
+ const QColor &color1, const QColor &color2)
+{
+ QImage retImage = img;
+ apply (&retImage, color1, color2);
+ return retImage;
+}
+
+
+//
+// kpEffectFlattenCommand implements kpColorEffectCommand interface
+//
+
+// protected virtual [base kpColorEffectCommand]
+QPixmap kpEffectFlattenCommand::applyColorEffect (const QPixmap &pixmap)
+{
+ return apply (pixmap, m_color1, m_color2);
+}
+
+
+//
+// kpEffectFlattenWidget
+//
+
+// public static
+// Don't initialise globally when we probably don't have a colour
+// allocation context. This way, the colours aren't sometimes invalid
+// (e.g. at 8-bit).
+QColor kpEffectFlattenWidget::s_lastColor1;
+QColor kpEffectFlattenWidget::s_lastColor2;
+
+kpEffectFlattenWidget::kpEffectFlattenWidget (bool actOnSelection,
+ kpMainWindow *mainWindow,
+ QWidget *parent,
+ const char *name)
+ : kpColorEffectWidget (actOnSelection, mainWindow, parent, name)
+{
+ if (!s_lastColor1.isValid () || !s_lastColor2.isValid ())
+ {
+ KConfigGroupSaver cfgGroupSaver (KGlobal::config (), kpSettingsGroupFlattenEffect);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ s_lastColor1 = cfg->readColorEntry (kpSettingFlattenEffectColor1);
+ if (!s_lastColor1.isValid ())
+ s_lastColor1 = Qt::red;
+
+ s_lastColor2 = cfg->readColorEntry (kpSettingFlattenEffectColor2);
+ if (!s_lastColor2.isValid ())
+ s_lastColor2 = Qt::blue;
+ }
+
+
+ m_enableCheckBox = new QCheckBox (i18n ("E&nable"), this);
+
+ QVBox *colorButtonContainer = new QVBox (this);
+ colorButtonContainer->setMargin (KDialog::marginHint () / 2);
+ colorButtonContainer->setSpacing (spacingHint ());
+ m_color1Button = new KColorButton (s_lastColor1, colorButtonContainer);
+ m_color2Button = new KColorButton (s_lastColor2, colorButtonContainer);
+
+
+ m_color1Button->setEnabled (false);
+ m_color2Button->setEnabled (false);
+
+
+ QVBoxLayout *lay = new QVBoxLayout (this, marginHint (), spacingHint ());
+ lay->addWidget (m_enableCheckBox);
+ lay->addWidget (colorButtonContainer);
+
+
+ connect (m_enableCheckBox, SIGNAL (toggled (bool)),
+ this, SLOT (slotEnableChanged (bool)));
+
+ connect (m_color1Button, SIGNAL (changed (const QColor &)),
+ this, SIGNAL (settingsChanged ()));
+ connect (m_color2Button, SIGNAL (changed (const QColor &)),
+ this, SIGNAL (settingsChanged ()));
+}
+
+kpEffectFlattenWidget::~kpEffectFlattenWidget ()
+{
+ s_lastColor1 = color1 ();
+ s_lastColor2 = color2 ();
+
+
+ KConfigGroupSaver cfgGroupSaver (KGlobal::config (), kpSettingsGroupFlattenEffect);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ cfg->writeEntry (kpSettingFlattenEffectColor1, s_lastColor1);
+ cfg->writeEntry (kpSettingFlattenEffectColor2, s_lastColor2);
+ cfg->sync ();
+}
+
+
+// public
+QColor kpEffectFlattenWidget::color1 () const
+{
+ return m_color1Button->color ();
+}
+
+// public
+QColor kpEffectFlattenWidget::color2 () const
+{
+ return m_color2Button->color ();
+}
+
+
+//
+// kpEffectFlattenWidget implements kpColorEffectWidget interface
+//
+
+// public virtual [base kpColorEffectWidget]
+QString kpEffectFlattenWidget::caption () const
+{
+ return i18n ("Colors");
+}
+
+
+// public virtual [base kpColorEffectWidget]
+bool kpEffectFlattenWidget::isNoOp () const
+{
+ return !m_enableCheckBox->isChecked ();
+}
+
+// public virtual [base kpColorEffectWidget]
+QPixmap kpEffectFlattenWidget::applyColorEffect (const QPixmap &pixmap)
+{
+#if DEBUG_KP_EFFECT_FLATTEN
+ kdDebug () << "kpEffectFlattenWidget::applyColorEffect() nop="
+ << isNoOp () << endl;
+#endif
+
+ if (isNoOp ())
+ return pixmap;
+
+ return kpEffectFlattenCommand::apply (pixmap, color1 (), color2 ());
+}
+
+
+// public virtual [base kpColorEffectWidget]
+kpColorEffectCommand *kpEffectFlattenWidget::createCommand () const
+{
+ return new kpEffectFlattenCommand (color1 (), color2 (),
+ m_actOnSelection,
+ m_mainWindow);
+}
+
+
+// protected slot:
+void kpEffectFlattenWidget::slotEnableChanged (bool enable)
+{
+#if DEBUG_KP_EFFECT_FLATTEN
+ kdDebug () << "kpEffectFlattenWidget::slotEnableChanged(" << enable
+ << ") enableButton=" << m_enableCheckBox->isChecked ()
+ << endl;
+#endif
+
+ m_color1Button->setEnabled (enable);
+ m_color2Button->setEnabled (enable);
+
+ emit settingsChanged ();
+}
+
+
+#include <kpeffectflatten.moc>
+
diff --git a/kolourpaint/pixmapfx/kpeffectflatten.h b/kolourpaint/pixmapfx/kpeffectflatten.h
new file mode 100644
index 00000000..79c9bbaf
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpeffectflatten.h
@@ -0,0 +1,115 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef KP_EFFECT_FLATTEN_H
+#define KP_EFFECT_FLATTEN_H
+
+
+#include <qcolor.h>
+
+#include <kpcoloreffect.h>
+
+
+class QCheckBox;
+class QImage;
+
+class KColorButton;
+
+class kpMainWindow;
+
+
+class kpEffectFlattenCommand : public kpColorEffectCommand
+{
+public:
+ kpEffectFlattenCommand (const QColor &color1, const QColor &color2,
+ bool actOnSelection,
+ kpMainWindow *mainWindow);
+ virtual ~kpEffectFlattenCommand ();
+
+
+ static void apply (QPixmap *destPixmapPtr,
+ const QColor &color1, const QColor &color2);
+ static QPixmap apply (const QPixmap &pm,
+ const QColor &color1, const QColor &color2);
+ static void apply (QImage *destImagePtr,
+ const QColor &color1, const QColor &color2);
+ static QImage apply (const QImage &img,
+ const QColor &color1, const QColor &color2);
+
+
+ //
+ // kpColorEffectCommand interface
+ //
+
+protected:
+ virtual QPixmap applyColorEffect (const QPixmap &pixmap);
+
+ QColor m_color1, m_color2;
+};
+
+
+class kpEffectFlattenWidget : public kpColorEffectWidget
+{
+Q_OBJECT
+
+public:
+ kpEffectFlattenWidget (bool actOnSelection,
+ kpMainWindow *mainWindow,
+ QWidget *parent, const char *name = 0);
+ virtual ~kpEffectFlattenWidget ();
+
+
+ static QColor s_lastColor1, s_lastColor2;
+
+
+ QColor color1 () const;
+ QColor color2 () const;
+
+
+ //
+ // kpColorEffectWidget interface
+ //
+
+ virtual QString caption () const;
+
+ virtual bool isNoOp () const;
+ virtual QPixmap applyColorEffect (const QPixmap &pixmap);
+
+ virtual kpColorEffectCommand *createCommand () const;
+
+protected slots:
+ void slotEnableChanged (bool enable);
+
+protected:
+ QCheckBox *m_enableCheckBox;
+ KColorButton *m_color1Button, *m_color2Button;
+};
+
+
+
+#endif // KP_EFFECT_FLATTEN_H
diff --git a/kolourpaint/pixmapfx/kpeffectinvert.cpp b/kolourpaint/pixmapfx/kpeffectinvert.cpp
new file mode 100644
index 00000000..b9bb00a8
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpeffectinvert.cpp
@@ -0,0 +1,315 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG_KP_EFFECT_INVERT 0
+
+
+#include <kpeffectinvert.h>
+
+#include <qcheckbox.h>
+#include <qimage.h>
+#include <qlayout.h>
+#include <qpixmap.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kppixmapfx.h>
+
+
+//
+// kpEffectInvertCommand
+//
+
+kpEffectInvertCommand::kpEffectInvertCommand (int channels,
+ bool actOnSelection,
+ kpMainWindow *mainWindow)
+ : kpColorEffectCommand (channels == RGB ?
+ i18n ("Invert Colors") : i18n ("Invert"),
+ actOnSelection, mainWindow),
+ m_channels (channels)
+{
+}
+
+kpEffectInvertCommand::kpEffectInvertCommand (bool actOnSelection,
+ kpMainWindow *mainWindow)
+ : kpColorEffectCommand (i18n ("Invert Colors"), actOnSelection, mainWindow),
+ m_channels (RGB)
+{
+}
+
+kpEffectInvertCommand::~kpEffectInvertCommand ()
+{
+}
+
+
+// public static
+void kpEffectInvertCommand::apply (QPixmap *destPixmapPtr, int channels)
+{
+ QImage image = kpPixmapFX::convertToImage (*destPixmapPtr);
+ apply (&image, channels);
+ *destPixmapPtr = kpPixmapFX::convertToPixmap (image);
+}
+
+// public static
+QPixmap kpEffectInvertCommand::apply (const QPixmap &pm, int channels)
+{
+ QImage image = kpPixmapFX::convertToImage (pm);
+ apply (&image, channels);
+ return kpPixmapFX::convertToPixmap (image);
+}
+
+// public static
+void kpEffectInvertCommand::apply (QImage *destImagePtr, int channels)
+{
+ QRgb mask = qRgba ((channels & Red) ? 0xFF : 0,
+ (channels & Green) ? 0xFF : 0,
+ (channels & Blue) ? 0xFF : 0,
+ 0/*don't invert alpha*/);
+#if DEBUG_KP_EFFECT_INVERT
+ kdDebug () << "kpEffectInvertCommand::apply(channels=" << channels
+ << ") mask=" << (int *) mask
+ << endl;
+#endif
+
+ if (destImagePtr->depth () > 8)
+ {
+ #if 0
+ // SYNC: TODO: Qt BUG - invertAlpha argument is inverted!!!
+ destImagePtr->invertPixels (true/*no invert alpha (Qt 3.2)*/);
+ #else
+ // Above version works for Qt 3.2 at least.
+ // But this version will always work (slower, though) and supports
+ // inverting particular channels.
+ for (int y = 0; y < destImagePtr->height (); y++)
+ {
+ for (int x = 0; x < destImagePtr->width (); x++)
+ {
+ destImagePtr->setPixel (x, y, destImagePtr->pixel (x, y) ^ mask);
+ }
+ }
+ #endif
+ }
+ else
+ {
+ for (int i = 0; i < destImagePtr->numColors (); i++)
+ {
+ destImagePtr->setColor (i, destImagePtr->color (i) ^ mask);
+ }
+ }
+}
+
+// public static
+QImage kpEffectInvertCommand::apply (const QImage &img, int channels)
+{
+ QImage retImage = img;
+ apply (&retImage, channels);
+ return retImage;
+}
+
+
+//
+// kpEffectInvertCommand implements kpColorEffectCommand interface
+//
+
+// protected virtual [base kpColorEffectCommand]
+QPixmap kpEffectInvertCommand::applyColorEffect (const QPixmap &pixmap)
+{
+ return apply (pixmap, m_channels);
+}
+
+
+//
+// kpEffectInvertWidget
+//
+
+kpEffectInvertWidget::kpEffectInvertWidget (bool actOnSelection,
+ kpMainWindow *mainWindow,
+ QWidget *parent,
+ const char *name)
+ : kpColorEffectWidget (actOnSelection, mainWindow, parent, name)
+{
+ QVBoxLayout *topLevelLay = new QVBoxLayout (this, marginHint (), spacingHint ());
+
+
+ QWidget *centerWidget = new QWidget (this);
+ topLevelLay->addWidget (centerWidget, 0/*stretch*/, Qt::AlignCenter);
+
+
+ QVBoxLayout *centerWidgetLay = new QVBoxLayout (centerWidget,
+ 0/*margin*/,
+ spacingHint ());
+
+
+ m_redCheckBox = new QCheckBox (i18n ("&Red"), centerWidget);
+ m_greenCheckBox = new QCheckBox (i18n ("&Green"), centerWidget);
+ m_blueCheckBox = new QCheckBox (i18n ("&Blue"), centerWidget);
+
+ QWidget *spaceWidget = new QWidget (centerWidget);
+ spaceWidget->setFixedSize (1, spacingHint ());
+
+ m_allCheckBox = new QCheckBox (i18n ("&All"), centerWidget);
+
+
+ m_redCheckBox->setChecked (false);
+ m_greenCheckBox->setChecked (false);
+ m_blueCheckBox->setChecked (false);
+
+ m_allCheckBox->setChecked (false);
+
+
+ centerWidgetLay->addWidget (m_redCheckBox);
+ centerWidgetLay->addWidget (m_greenCheckBox);
+ centerWidgetLay->addWidget (m_blueCheckBox);
+
+ centerWidgetLay->addWidget (spaceWidget);
+
+ centerWidgetLay->addWidget (m_allCheckBox);
+
+
+ m_inSignalHandler = false;
+ connect (m_redCheckBox, SIGNAL (toggled (bool)),
+ this, SLOT (slotRGBCheckBoxToggled ()));
+ connect (m_greenCheckBox, SIGNAL (toggled (bool)),
+ this, SLOT (slotRGBCheckBoxToggled ()));
+ connect (m_blueCheckBox, SIGNAL (toggled (bool)),
+ this, SLOT (slotRGBCheckBoxToggled ()));
+
+ connect (m_allCheckBox, SIGNAL (toggled (bool)),
+ this, SLOT (slotAllCheckBoxToggled ()));
+}
+
+kpEffectInvertWidget::~kpEffectInvertWidget ()
+{
+}
+
+
+// public
+int kpEffectInvertWidget::channels () const
+{
+#if DEBUG_KP_EFFECT_INVERT
+ kdDebug () << "kpEffectInvertWidget::channels()"
+ << " isChecked: r=" << m_redCheckBox->isChecked ()
+ << " g=" << m_greenCheckBox->isChecked ()
+ << " b=" << m_blueCheckBox->isChecked ()
+ << endl;
+#endif
+
+ int channels = 0;
+
+
+ if (m_redCheckBox->isChecked ())
+ channels |= kpEffectInvertCommand::Red;
+
+ if (m_greenCheckBox->isChecked ())
+ channels |= kpEffectInvertCommand::Green;
+
+ if (m_blueCheckBox->isChecked ())
+ channels |= kpEffectInvertCommand::Blue;
+
+
+#if DEBUG_KP_EFFECT_INVERT
+ kdDebug () << "\treturning channels=" << (int *) channels << endl;
+#endif
+ return channels;
+}
+
+
+//
+// kpEffectInvertWidget implements kpColorEffectWidget interface
+//
+
+// public virtual [base kpColorEffectWidget]
+QString kpEffectInvertWidget::caption () const
+{
+ return i18n ("Channels");
+}
+
+
+// public virtual [base kpColorEffectWidget]
+bool kpEffectInvertWidget::isNoOp () const
+{
+ return (channels () == kpEffectInvertCommand::None);
+}
+
+// public virtual [base kpColorEffectWidget]
+QPixmap kpEffectInvertWidget::applyColorEffect (const QPixmap &pixmap)
+{
+ return kpEffectInvertCommand::apply (pixmap, channels ());
+}
+
+
+// public virtual [base kpColorEffectWidget]
+kpColorEffectCommand *kpEffectInvertWidget::createCommand () const
+{
+ return new kpEffectInvertCommand (channels (),
+ m_actOnSelection,
+ m_mainWindow);
+}
+
+
+// protected slots
+void kpEffectInvertWidget::slotRGBCheckBoxToggled ()
+{
+ if (m_inSignalHandler)
+ return;
+
+ m_inSignalHandler = true;
+
+ //blockSignals (true);
+ m_allCheckBox->setChecked (m_redCheckBox->isChecked () &&
+ m_blueCheckBox->isChecked () &&
+ m_greenCheckBox->isChecked ());
+ //blockSignals (false);
+
+ emit settingsChanged ();
+
+ m_inSignalHandler = false;
+}
+
+// protected slot
+void kpEffectInvertWidget::slotAllCheckBoxToggled ()
+{
+ if (m_inSignalHandler)
+ return;
+
+ m_inSignalHandler = true;
+
+ //blockSignals (true);
+ m_redCheckBox->setChecked (m_allCheckBox->isChecked ());
+ m_greenCheckBox->setChecked (m_allCheckBox->isChecked ());
+ m_blueCheckBox->setChecked (m_allCheckBox->isChecked ());
+ //blockSignals (false);
+
+ emit settingsChanged ();
+
+ m_inSignalHandler = false;
+}
+
+
+#include <kpeffectinvert.moc>
+
diff --git a/kolourpaint/pixmapfx/kpeffectinvert.h b/kolourpaint/pixmapfx/kpeffectinvert.h
new file mode 100644
index 00000000..61d6cfda
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpeffectinvert.h
@@ -0,0 +1,130 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef KP_EFFECT_INVERT_H
+#define KP_EFFECT_INVERT_H
+
+
+#include <kpcoloreffect.h>
+
+
+class QCheckBox;
+class QImage;
+
+class kpMainWindow;
+
+
+class kpEffectInvertCommand : public kpColorEffectCommand
+{
+public:
+ enum Channel
+ {
+ None = 0,
+ Red = 1, Green = 2, Blue = 4,
+ RGB = Red | Green | Blue
+ };
+
+ kpEffectInvertCommand (int channels,
+ bool actOnSelection,
+ kpMainWindow *mainWindow);
+ kpEffectInvertCommand (bool actOnSelection,
+ kpMainWindow *mainWindow);
+ virtual ~kpEffectInvertCommand ();
+
+
+ //
+ // Inverts the colours of each pixel in the given image.
+ // These functions differ from QImage::invertPixels() in the following ways:
+ //
+ // 1. for 8-bit images, it inverts the colours of the Colour Table
+ // (this means that you would get visually similar results to inversion
+ // at higher bit depths - rather than a "random-looking" inversion
+ // depending on the contents of the Colour Table)
+ // 2. never inverts the Alpha Buffer
+ //
+
+ static void apply (QPixmap *destPixmapPtr, int channels = RGB);
+ static QPixmap apply (const QPixmap &pm, int channels = RGB);
+ static void apply (QImage *destImagePtr, int channels = RGB);
+ static QImage apply (const QImage &img, int channels = RGB);
+
+
+ //
+ // kpColorEffectCommand interface
+ //
+
+public:
+ virtual bool isInvertible () const { return true; }
+
+protected:
+ virtual QPixmap applyColorEffect (const QPixmap &pixmap);
+
+ int m_channels;
+};
+
+
+class kpEffectInvertWidget : public kpColorEffectWidget
+{
+Q_OBJECT
+
+public:
+ kpEffectInvertWidget (bool actOnSelection,
+ kpMainWindow *mainWindow,
+ QWidget *parent, const char *name = 0);
+ virtual ~kpEffectInvertWidget ();
+
+
+ int channels () const;
+
+
+ //
+ // kpColorEffectWidget interface
+ //
+
+ virtual QString caption () const;
+
+ virtual bool isNoOp () const;
+ virtual QPixmap applyColorEffect (const QPixmap &pixmap);
+
+ virtual kpColorEffectCommand *createCommand () const;
+
+protected slots:
+ void slotRGBCheckBoxToggled ();
+ void slotAllCheckBoxToggled ();
+
+protected:
+ QCheckBox *m_redCheckBox, *m_greenCheckBox, *m_blueCheckBox,
+ *m_allCheckBox;
+
+ // blockSignals() didn't seem to work
+ bool m_inSignalHandler;
+};
+
+
+
+#endif // KP_EFFECT_INVERT_H
diff --git a/kolourpaint/pixmapfx/kpeffectreducecolors.cpp b/kolourpaint/pixmapfx/kpeffectreducecolors.cpp
new file mode 100644
index 00000000..b6eb7a42
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpeffectreducecolors.cpp
@@ -0,0 +1,446 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG_KP_EFFECT_REDUCE_COLORS 0
+
+
+#include <kpeffectreducecolors.h>
+
+#include <qbuttongroup.h>
+#include <qcheckbox.h>
+#include <qimage.h>
+#include <qlayout.h>
+#include <qpixmap.h>
+#include <qradiobutton.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kppixmapfx.h>
+
+
+QImage convertImageDepth (const QImage &image, int depth, bool dither)
+{
+#if DEBUG_KP_EFFECT_REDUCE_COLORS
+ kdDebug () << "::convertImageDepth() changing image (w=" << image.width ()
+ << ",h=" << image.height ()
+ << ") depth from " << image.depth ()
+ << " to " << depth
+ << " (dither=" << dither << ")"
+ << endl;
+#endif
+
+ if (image.isNull ())
+ return image;
+
+ if (depth == image.depth ())
+ return image;
+
+
+#if DEBUG_KP_EFFECT_REDUCE_COLORS && 0
+ for (int y = 0; y < image.height (); y++)
+ {
+ for (int x = 0; x < image.width (); x++)
+ {
+ fprintf (stderr, " %08X", image.pixel (x, y));
+ }
+ fprintf (stderr, "\n");
+ }
+#endif
+
+
+ // Hack around Qt's braindead QImage::convertDepth(1, ...) (with
+ // dithering off) which produces pathetic results with an image that
+ // only has 2 colours - sometimes it just gives a completely black
+ // result. Instead, we simply preserve the 2 colours. One use case
+ // is resaving a "colour monochrome" image (<= 2 colours but not
+ // necessarily black & white).
+ if (depth == 1 && !dither)
+ {
+ #if DEBUG_KP_EFFECT_REDUCE_COLORS
+ kdDebug () << "\tinvoking convert-to-depth 1 hack" << endl;
+ #endif
+ QRgb color0, color1;
+ bool color0Valid = false, color1Valid = false;
+
+ bool moreThan2Colors = false;
+
+ QImage monoImage (image.width (), image.height (),
+ 1/*depth*/, 2/*numColors*/, QImage::LittleEndian);
+ #if DEBUG_KP_EFFECT_REDUCE_COLORS
+ kdDebug () << "\t\tinitialising output image w=" << monoImage.width ()
+ << ",h=" << monoImage.height ()
+ << ",d=" << monoImage.depth ()
+ << endl;
+ #endif
+ for (int y = 0; y < image.height (); y++)
+ {
+ for (int x = 0; x < image.width (); x++)
+ {
+ QRgb imagePixel = image.pixel (x, y);
+
+ if (color0Valid && imagePixel == color0)
+ monoImage.setPixel (x, y, 0);
+ else if (color1Valid && imagePixel == color1)
+ monoImage.setPixel (x, y, 1);
+ else if (!color0Valid)
+ {
+ color0 = imagePixel;
+ color0Valid = true;
+ monoImage.setPixel (x, y, 0);
+ #if DEBUG_KP_EFFECT_REDUCE_COLORS
+ kdDebug () << "\t\t\tcolor0=" << (int *) color0
+ << " at x=" << x << ",y=" << y << endl;
+ #endif
+ }
+ else if (!color1Valid)
+ {
+ color1 = imagePixel;
+ color1Valid = true;
+ monoImage.setPixel (x, y, 1);
+ #if DEBUG_KP_EFFECT_REDUCE_COLORS
+ kdDebug () << "\t\t\tcolor1=" << (int *) color1
+ << " at x=" << x << ",y=" << y << endl;
+ #endif
+ }
+ else
+ {
+ #if DEBUG_KP_EFFECT_REDUCE_COLORS
+ kdDebug () << "\t\t\timagePixel=" << (int *) imagePixel
+ << " at x=" << x << ",y=" << y
+ << " moreThan2Colors - abort hack" << endl;
+ #endif
+ moreThan2Colors = true;
+
+ // Dijkstra, this is clearer than double break'ing or
+ // a check in both loops
+ goto exit_loop;
+ }
+ }
+ }
+ exit_loop:
+
+ if (!moreThan2Colors)
+ {
+ monoImage.setColor (0, color0Valid ? color0 : 0xFFFFFF);
+ monoImage.setColor (1, color1Valid ? color1 : 0x000000);
+ return monoImage;
+ }
+ }
+
+
+ QImage retImage = image.convertDepth (depth,
+ Qt::AutoColor |
+ (dither ? Qt::DiffuseDither : Qt::ThresholdDither) |
+ Qt::ThresholdAlphaDither |
+ (dither ? Qt::PreferDither : Qt::AvoidDither));
+
+#if DEBUG_KP_EFFECT_REDUCE_COLORS && 0
+ kdDebug () << "After colour reduction:" << endl;
+ for (int y = 0; y < image.height (); y++)
+ {
+ for (int x = 0; x < image.width (); x++)
+ {
+ fprintf (stderr, " %08X", image.pixel (x, y));
+ }
+ fprintf (stderr, "\n");
+ }
+#endif
+
+ return retImage;
+}
+
+
+//
+// kpEffectReduceColorsCommand
+//
+
+kpEffectReduceColorsCommand::kpEffectReduceColorsCommand (int depth, bool dither,
+ bool actOnSelection,
+ kpMainWindow *mainWindow)
+ : kpColorEffectCommand (commandName (depth, dither), actOnSelection, mainWindow),
+ m_depth (depth), m_dither (dither)
+{
+}
+
+kpEffectReduceColorsCommand::~kpEffectReduceColorsCommand ()
+{
+}
+
+
+// public
+QString kpEffectReduceColorsCommand::commandName (int depth, int dither) const
+{
+ if (depth == 1)
+ {
+ if (dither)
+ return i18n ("Reduce to Monochrome (Dithered)");
+ else
+ return i18n ("Reduce to Monochrome");
+ }
+ else if (depth == 8)
+ {
+ if (dither)
+ return i18n ("Reduce to 256 Color (Dithered)");
+ else
+ return i18n ("Reduce to 256 Color");
+ }
+ else
+ {
+ return QString::null;
+ }
+}
+
+
+// public static
+void kpEffectReduceColorsCommand::apply (QPixmap *destPixmapPtr, int depth, bool dither)
+{
+ if (!destPixmapPtr)
+ return;
+
+ if (depth != 1 && depth != 8)
+ return;
+
+
+ QImage image = kpPixmapFX::convertToImage (*destPixmapPtr);
+
+
+ image = ::convertImageDepth (image, depth, dither);
+
+ if (image.isNull ())
+ return;
+
+
+ QPixmap pixmap = kpPixmapFX::convertToPixmap (image, false/*no dither*/);
+
+
+ // HACK: The above "image.convertDepth()" erases the Alpha Channel
+ // (at least for monochrome).
+ // qpixmap.html says "alpha masks on monochrome images are ignored."
+ //
+ // Put the mask back.
+ //
+ if (destPixmapPtr->mask ())
+ pixmap.setMask (*destPixmapPtr->mask ());
+
+ *destPixmapPtr = pixmap;
+}
+
+// public static
+QPixmap kpEffectReduceColorsCommand::apply (const QPixmap &pm, int depth, bool dither)
+{
+ QPixmap ret = pm;
+ apply (&ret, depth, dither);
+ return ret;
+}
+
+
+//
+// kpEffectReduceColorsCommand implements kpColorEffectCommand interface
+//
+
+// protected virtual [base kpColorEffectCommand]
+QPixmap kpEffectReduceColorsCommand::applyColorEffect (const QPixmap &pixmap)
+{
+ return apply (pixmap, m_depth, m_dither);
+}
+
+
+//
+// kpEffectReduceColorsWidget
+//
+
+kpEffectReduceColorsWidget::kpEffectReduceColorsWidget (bool actOnSelection,
+ kpMainWindow *mainWindow,
+ QWidget *parent,
+ const char *name)
+ : kpColorEffectWidget (actOnSelection, mainWindow, parent, name)
+{
+ QVBoxLayout *lay = new QVBoxLayout (this, marginHint (), spacingHint ());
+
+
+ m_blackAndWhiteRadioButton =
+ new QRadioButton (i18n ("&Monochrome"), this);
+
+ m_blackAndWhiteDitheredRadioButton =
+ new QRadioButton (i18n ("Mo&nochrome (dithered)"), this);
+
+ m_8BitRadioButton = new QRadioButton (i18n ("256 co&lor"), this);
+
+ m_8BitDitheredRadioButton = new QRadioButton (i18n ("256 colo&r (dithered)"), this);
+
+ m_24BitRadioButton = new QRadioButton (i18n ("24-&bit color"), this);
+
+
+ QButtonGroup *buttonGroup = new QButtonGroup (this);
+ buttonGroup->hide ();
+
+ buttonGroup->insert (m_blackAndWhiteRadioButton);
+ buttonGroup->insert (m_blackAndWhiteDitheredRadioButton);
+ buttonGroup->insert (m_8BitRadioButton);
+ buttonGroup->insert (m_8BitDitheredRadioButton);
+ buttonGroup->insert (m_24BitRadioButton);
+
+
+ const int screenDepth = QPixmap::defaultDepth ();
+#if DEBUG_KP_EFFECT_REDUCE_COLORS
+ kdDebug () << "kpEffectReduceColorsWidget::<ctor> screenDepth="
+ << screenDepth
+ << endl;
+#endif
+
+ // Note that everything is disabled for a 1-bit screen since there
+ // would be no effect. I won't support 2-bit or 4-bit screens either :)
+ m_blackAndWhiteRadioButton->setEnabled (screenDepth >= 8);
+ m_blackAndWhiteDitheredRadioButton->setEnabled (screenDepth >= 8);
+ m_8BitRadioButton->setEnabled (screenDepth >= 8);
+ // (not enabled if screenDepth==8 as m_8BitRadioButton already serves
+ // as NOP default)
+ m_8BitDitheredRadioButton->setEnabled (screenDepth > 8);
+ // (not "screenDepth >= 24" as we need a NOP default for 15/16-bit
+ // screens)
+ m_24BitRadioButton->setEnabled (screenDepth > 8);
+
+
+ m_defaultRadioButton = 0;
+
+ if (m_24BitRadioButton->isEnabled ())
+ {
+ #if DEBUG_KP_EFFECT_REDUCE_COLORS
+ kdDebug () << "\tdefault is 24-bit button" << endl;
+ #endif
+ m_defaultRadioButton = m_24BitRadioButton;
+ }
+ else if (m_8BitRadioButton->isEnabled ())
+ {
+ #if DEBUG_KP_EFFECT_REDUCE_COLORS
+ kdDebug () << "\tdefault is 8-bit button" << endl;
+ #endif
+ m_defaultRadioButton = m_8BitRadioButton;
+ }
+ else
+ {
+ #if DEBUG_KP_EFFECT_REDUCE_COLORS
+ kdDebug () << "\tuser must have a 1-bit screen - no default" << endl;
+ #endif
+ }
+
+
+ if (m_defaultRadioButton)
+ m_defaultRadioButton->setChecked (true);
+
+
+ lay->addWidget (m_blackAndWhiteRadioButton);
+ lay->addWidget (m_blackAndWhiteDitheredRadioButton);
+ lay->addWidget (m_8BitRadioButton);
+ lay->addWidget (m_8BitDitheredRadioButton);
+ lay->addWidget (m_24BitRadioButton);
+
+
+ connect (m_blackAndWhiteRadioButton, SIGNAL (toggled (bool)),
+ this, SIGNAL (settingsChanged ()));
+ connect (m_blackAndWhiteDitheredRadioButton, SIGNAL (toggled (bool)),
+ this, SIGNAL (settingsChanged ()));
+ connect (m_8BitRadioButton, SIGNAL (toggled (bool)),
+ this, SIGNAL (settingsChanged ()));
+ connect (m_8BitDitheredRadioButton, SIGNAL (toggled (bool)),
+ this, SIGNAL (settingsChanged ()));
+ connect (m_24BitRadioButton, SIGNAL (toggled (bool)),
+ this, SIGNAL (settingsChanged ()));
+}
+
+kpEffectReduceColorsWidget::~kpEffectReduceColorsWidget ()
+{
+}
+
+
+// public
+int kpEffectReduceColorsWidget::depth () const
+{
+ if (m_blackAndWhiteRadioButton->isChecked () ||
+ m_blackAndWhiteDitheredRadioButton->isChecked ())
+ {
+ return 1;
+ }
+ else if (m_8BitRadioButton->isChecked () ||
+ m_8BitDitheredRadioButton->isChecked ())
+ {
+ return 8;
+ }
+ else if (m_24BitRadioButton->isChecked ())
+ {
+ return 24;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+// public
+bool kpEffectReduceColorsWidget::dither () const
+{
+ return (m_blackAndWhiteDitheredRadioButton->isChecked () ||
+ m_8BitDitheredRadioButton->isChecked ());
+}
+
+
+//
+// kpEffectReduceColorsWidget implements kpColorEffectWidget interface
+//
+
+// public virtual [base kpColorEffectWidget]
+QString kpEffectReduceColorsWidget::caption () const
+{
+ return i18n ("Reduce To");
+}
+
+
+// public virtual [base kpColorEffectWidget]
+bool kpEffectReduceColorsWidget::isNoOp () const
+{
+ return (!m_defaultRadioButton || m_defaultRadioButton->isChecked ());
+}
+
+// public virtual [base kpColorEffectWidget]
+QPixmap kpEffectReduceColorsWidget::applyColorEffect (const QPixmap &pixmap)
+{
+ return kpEffectReduceColorsCommand::apply (pixmap, depth (), dither ());
+}
+
+
+// public virtual [base kpColorEffectWidget]
+kpColorEffectCommand *kpEffectReduceColorsWidget::createCommand () const
+{
+ return new kpEffectReduceColorsCommand (depth (), dither (),
+ m_actOnSelection,
+ m_mainWindow);
+}
+
+
+#include <kpeffectreducecolors.moc>
+
diff --git a/kolourpaint/pixmapfx/kpeffectreducecolors.h b/kolourpaint/pixmapfx/kpeffectreducecolors.h
new file mode 100644
index 00000000..a14cffc7
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpeffectreducecolors.h
@@ -0,0 +1,110 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef KP_EFFECT_REDUCE_COLORS_H
+#define KP_EFFECT_REDUCE_COLORS_H
+
+
+#include <kpcoloreffect.h>
+
+
+class QRadioButton;
+class QImage;
+
+class kpMainWindow;
+
+
+QImage convertImageDepth (const QImage &image, int depth, bool dither);
+
+
+class kpEffectReduceColorsCommand : public kpColorEffectCommand
+{
+public:
+ // depth must be 1 or 8
+ kpEffectReduceColorsCommand (int depth, bool dither,
+ bool actOnSelection,
+ kpMainWindow *mainWindow);
+ virtual ~kpEffectReduceColorsCommand ();
+
+ QString commandName (int depth, int dither) const;
+
+ // (always preserves mask)
+ static void apply (QPixmap *destPixmapPtr, int depth, bool dither);
+ static QPixmap apply (const QPixmap &pm, int depth, bool dither);
+
+
+ //
+ // kpColorEffectCommand interface
+ //
+
+protected:
+ virtual QPixmap applyColorEffect (const QPixmap &pixmap);
+
+ int m_depth;
+ bool m_dither;
+};
+
+
+class kpEffectReduceColorsWidget : public kpColorEffectWidget
+{
+Q_OBJECT
+
+public:
+ kpEffectReduceColorsWidget (bool actOnSelection,
+ kpMainWindow *mainWindow,
+ QWidget *parent, const char *name = 0);
+ virtual ~kpEffectReduceColorsWidget ();
+
+
+ int depth () const;
+ bool dither () const;
+
+
+ //
+ // kpColorEffectWidget interface
+ //
+
+ virtual QString caption () const;
+
+ virtual bool isNoOp () const;
+ virtual QPixmap applyColorEffect (const QPixmap &pixmap);
+
+ virtual kpColorEffectCommand *createCommand () const;
+
+protected:
+ QRadioButton *m_blackAndWhiteRadioButton,
+ *m_blackAndWhiteDitheredRadioButton,
+ *m_8BitRadioButton,
+ *m_8BitDitheredRadioButton,
+ *m_24BitRadioButton;
+ QRadioButton *m_defaultRadioButton;
+};
+
+
+
+#endif // KP_EFFECT_REDUCE_COLORS_H
diff --git a/kolourpaint/pixmapfx/kpeffectsdialog.cpp b/kolourpaint/pixmapfx/kpeffectsdialog.cpp
new file mode 100644
index 00000000..666f81cf
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpeffectsdialog.cpp
@@ -0,0 +1,369 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG_KP_EFFECTS_DIALOG 0
+
+
+#include <kpeffectsdialog.h>
+
+#include <qgroupbox.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qtimer.h>
+
+#include <kapplication.h>
+#include <kcombobox.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kpdefs.h>
+#include <kpdocument.h>
+#include <kpeffectbalance.h>
+#include <kpeffectblursharpen.h>
+#include <kpeffectemboss.h>
+#include <kpeffectflatten.h>
+#include <kpeffectinvert.h>
+#include <kpeffectreducecolors.h>
+#include <kppixmapfx.h>
+
+
+// protected static
+int kpEffectsDialog::s_lastWidth = 640;
+int kpEffectsDialog::s_lastHeight = 620;
+
+
+kpEffectsDialog::kpEffectsDialog (bool actOnSelection,
+ kpMainWindow *parent,
+ const char *name)
+ : kpToolPreviewDialog (kpToolPreviewDialog::Preview,
+ true/*reserve top row*/,
+ QString::null/*caption*/,
+ QString::null/*afterActionText (no Dimensions Group Box)*/,
+ actOnSelection,
+ parent,
+ name),
+ m_delayedUpdateTimer (new QTimer (this)),
+ m_effectsComboBox (0),
+ m_settingsGroupBox (0),
+ m_settingsLayout (0),
+ m_colorEffectWidget (0)
+{
+#if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "kpEffectsDialog::kpEffectsDialog()" << endl;
+#endif
+
+ if (actOnSelection)
+ setCaption (i18n ("More Image Effects (Selection)"));
+ else
+ setCaption (i18n ("More Image Effects"));
+
+
+ connect (m_delayedUpdateTimer, SIGNAL (timeout ()),
+ this, SLOT (slotUpdateWithWaitCursor ()));
+
+
+ QHBox *effectContainer = new QHBox (mainWidget ());
+ effectContainer->setSpacing (spacingHint () * 4
+ /*need more space for QGroupBox titles*/);
+ effectContainer->setMargin (0);
+
+ QLabel *label = new QLabel (i18n ("&Effect:"), effectContainer);
+
+ m_effectsComboBox = new KComboBox (effectContainer);
+ m_effectsComboBox->insertItem (i18n ("Balance"));
+ m_effectsComboBox->insertItem (i18n ("Emboss"));
+ m_effectsComboBox->insertItem (i18n ("Flatten"));
+ m_effectsComboBox->insertItem (i18n ("Invert"));
+ m_effectsComboBox->insertItem (i18n ("Reduce Colors"));
+ m_effectsComboBox->insertItem (i18n ("Soften & Sharpen"));
+
+ label->setBuddy (m_effectsComboBox);
+ effectContainer->setStretchFactor (m_effectsComboBox, 1);
+
+ addCustomWidgetToFront (effectContainer);
+
+
+ m_settingsGroupBox = new QGroupBox (mainWidget ());
+ m_settingsLayout = new QVBoxLayout (m_settingsGroupBox,
+ marginHint () * 2,
+ spacingHint ());
+ addCustomWidgetToBack (m_settingsGroupBox);
+
+
+ connect (m_effectsComboBox, SIGNAL (activated (int)),
+ this, SLOT (selectEffect (int)));
+ selectEffect (0);
+
+
+ resize (s_lastWidth, s_lastHeight);
+
+
+#if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "\tabout to slotUpdate()" << endl;
+#endif
+ slotUpdate ();
+}
+
+kpEffectsDialog::~kpEffectsDialog ()
+{
+ s_lastWidth = width ();
+ s_lastHeight = height ();
+}
+
+
+// public virtual [base kpToolPreviewDialog]
+bool kpEffectsDialog::isNoOp () const
+{
+ if (!m_colorEffectWidget)
+ return true;
+
+ return m_colorEffectWidget->isNoOp ();
+}
+
+// public
+kpColorEffectCommand *kpEffectsDialog::createCommand () const
+{
+ if (!m_colorEffectWidget)
+ return 0;
+
+ return m_colorEffectWidget->createCommand ();
+}
+
+
+// protected virtual [base kpToolPreviewDialog]
+QSize kpEffectsDialog::newDimensions () const
+{
+ kpDocument *doc = document ();
+ if (!doc)
+ return QSize ();
+
+ return QSize (doc->width (m_actOnSelection),
+ doc->height (m_actOnSelection));
+}
+
+// protected virtual [base kpToolPreviewDialog]
+QPixmap kpEffectsDialog::transformPixmap (const QPixmap &pixmap,
+ int targetWidth, int targetHeight) const
+{
+ QPixmap pixmapWithEffect;
+
+ if (m_colorEffectWidget)
+ pixmapWithEffect = m_colorEffectWidget->applyColorEffect (pixmap);
+ else
+ pixmapWithEffect = pixmap;
+
+ return kpPixmapFX::scale (pixmapWithEffect, targetWidth, targetHeight);
+}
+
+
+// public
+int kpEffectsDialog::selectedEffect () const
+{
+ return m_effectsComboBox->currentItem ();
+}
+
+// public slot
+void kpEffectsDialog::selectEffect (int which)
+{
+#if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "kpEffectsDialog::selectEffect(" << which << ")" << endl;
+#endif
+
+ if (which < 0 ||
+ which >= m_effectsComboBox->count ())
+ {
+ return;
+ }
+
+ if (which != m_effectsComboBox->currentItem ())
+ m_effectsComboBox->setCurrentItem (which);
+
+
+ delete m_colorEffectWidget;
+ m_colorEffectWidget = 0;
+
+
+ m_settingsGroupBox->setCaption (QString::null);
+
+#define CREATE_EFFECT_WIDGET(name) \
+ m_colorEffectWidget = new name (m_actOnSelection, \
+ m_mainWindow, \
+ m_settingsGroupBox)
+ switch (which)
+ {
+ case 0:
+ CREATE_EFFECT_WIDGET (kpEffectBalanceWidget);
+ break;
+
+ case 1:
+ CREATE_EFFECT_WIDGET (kpEffectEmbossWidget);
+ break;
+
+ case 2:
+ CREATE_EFFECT_WIDGET (kpEffectFlattenWidget);
+ break;
+
+ case 3:
+ CREATE_EFFECT_WIDGET (kpEffectInvertWidget);
+ break;
+
+ case 4:
+ CREATE_EFFECT_WIDGET (kpEffectReduceColorsWidget);
+ break;
+
+ case 5:
+ CREATE_EFFECT_WIDGET (kpEffectBlurSharpenWidget);
+ break;
+ }
+#undef CREATE_EFFECT_WIDGET
+
+
+ if (m_colorEffectWidget)
+ {
+ #if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "\twidget exists for effect #" << endl;
+ #endif
+ m_settingsGroupBox->setTitle (m_colorEffectWidget->caption ());
+
+
+ // Don't resize the preview when showing the widget:
+ // TODO: actually work
+
+ QSize previewGroupBoxMinSize = m_previewGroupBox->minimumSize ();
+ QSize previewGroupBoxMaxSize = m_previewGroupBox->maximumSize ();
+ QLayout::ResizeMode previewGroupBoxResizeMode =
+ m_previewGroupBox->layout () ?
+ m_previewGroupBox->layout ()->resizeMode () :
+ QLayout::Auto;
+ #if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "\tpreviewGroupBox: minSize=" << previewGroupBoxMinSize
+ << " maxSize=" << previewGroupBoxMaxSize
+ << " size=" << m_previewGroupBox->size ()
+ << " layout=" << m_previewGroupBox->layout ()
+ << " resizeMode=" << previewGroupBoxResizeMode
+ << endl;
+ #endif
+
+ if (m_previewGroupBox->layout ())
+ m_previewGroupBox->layout ()->setResizeMode (QLayout::FreeResize);
+ #if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "\tafter set resizeMode, previewGroupBox.size="
+ << m_previewGroupBox->size () << endl;
+ #endif
+ m_previewGroupBox->setFixedSize (m_previewGroupBox->size ());
+ #if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "\tafter set fixedSize, previewGroupBox.size="
+ << m_previewGroupBox->size () << endl;
+ #endif
+
+ // Show widget
+ m_settingsLayout->addWidget (m_colorEffectWidget);
+ #if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "\tafter addWidget, previewGroupBox.size="
+ << m_previewGroupBox->size () << endl;
+ #endif
+ m_colorEffectWidget->show ();
+ #if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "\tafter addWidget show, previewGroupBox.size="
+ << m_previewGroupBox->size () << endl;
+ #endif
+
+ m_previewGroupBox->setMinimumSize (previewGroupBoxMinSize);
+ m_previewGroupBox->setMaximumSize (previewGroupBoxMaxSize);
+ #if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "\tafter set fixedSize, previewGroupBox.size="
+ << m_previewGroupBox->size () << endl;
+ #endif
+ if (m_previewGroupBox->layout ())
+ m_previewGroupBox->layout ()->setResizeMode (previewGroupBoxResizeMode);
+ #if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "\tafter restore resizeMode, previewGroupBox.size="
+ << m_previewGroupBox->size () << endl;
+ #endif
+
+
+ connect (m_colorEffectWidget, SIGNAL (settingsChangedNoWaitCursor ()),
+ this, SLOT (slotUpdate ()));
+ connect (m_colorEffectWidget, SIGNAL (settingsChanged ()),
+ this, SLOT (slotUpdateWithWaitCursor ()));
+ connect (m_colorEffectWidget, SIGNAL (settingsChangedDelayed ()),
+ this, SLOT (slotDelayedUpdate ()));
+ slotUpdateWithWaitCursor ();
+ #if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "\tafter slotUpdateWithWaitCursor, previewGroupBox.size="
+ << m_previewGroupBox->size () << endl;
+ #endif
+ }
+}
+
+
+// protected slot virtual [base kpToolPreviewDialog]
+void kpEffectsDialog::slotUpdate ()
+{
+#if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "kpEffectsDialog::slotUpdate()"
+ << " timerActive=" << m_delayedUpdateTimer->isActive ()
+ << endl;
+#endif
+
+ m_delayedUpdateTimer->stop ();
+
+ kpToolPreviewDialog::slotUpdate ();
+}
+
+// protected slot virtual [base kpToolPreviewDialog]
+void kpEffectsDialog::slotUpdateWithWaitCursor ()
+{
+#if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "kpEffectsDialog::slotUpdateWithWaitCursor()"
+ << " timerActive=" << m_delayedUpdateTimer->isActive ()
+ << endl;
+#endif
+
+ m_delayedUpdateTimer->stop ();
+
+ kpToolPreviewDialog::slotUpdateWithWaitCursor ();
+}
+
+
+// protected slot
+void kpEffectsDialog::slotDelayedUpdate ()
+{
+#if DEBUG_KP_EFFECTS_DIALOG
+ kdDebug () << "kpEffectsDialog::slotDelayedUpdate()"
+ << " timerActive=" << m_delayedUpdateTimer->isActive ()
+ << endl;
+#endif
+ m_delayedUpdateTimer->stop ();
+
+ m_delayedUpdateTimer->start (400/*ms*/, true/*single shot*/);
+}
+
+
+#include <kpeffectsdialog.moc>
diff --git a/kolourpaint/pixmapfx/kpeffectsdialog.h b/kolourpaint/pixmapfx/kpeffectsdialog.h
new file mode 100644
index 00000000..fe7265cc
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpeffectsdialog.h
@@ -0,0 +1,90 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef KP_EFFECTS_DIALOG_H
+#define KP_EFFECTS_DIALOG_H
+
+
+#include <kptoolpreviewdialog.h>
+
+
+class QGroupBox;
+class QStringList;
+class QTimer;
+class QVBoxLayout;
+
+class KComboBox;
+
+class kpColorEffectCommand;
+class kpColorEffectWidget;
+class kpMainWindow;
+
+
+class kpEffectsDialog : public kpToolPreviewDialog
+{
+Q_OBJECT
+
+public:
+ kpEffectsDialog (bool actOnSelection,
+ kpMainWindow *parent,
+ const char *name = 0);
+ virtual ~kpEffectsDialog ();
+
+ virtual bool isNoOp () const;
+ kpColorEffectCommand *createCommand () const;
+
+protected:
+ virtual QSize newDimensions () const;
+ virtual QPixmap transformPixmap (const QPixmap &pixmap,
+ int targetWidth, int targetHeight) const;
+
+public:
+ int selectedEffect () const;
+public slots:
+ void selectEffect (int which);
+
+protected slots:
+ virtual void slotUpdate ();
+ virtual void slotUpdateWithWaitCursor ();
+
+ void slotDelayedUpdate ();
+
+protected:
+ static int s_lastWidth, s_lastHeight;
+
+ QTimer *m_delayedUpdateTimer;
+
+ KComboBox *m_effectsComboBox;
+ QGroupBox *m_settingsGroupBox;
+ QVBoxLayout *m_settingsLayout;
+
+ kpColorEffectWidget *m_colorEffectWidget;
+};
+
+
+#endif // KP_EFFECTS_DIALOG_H
diff --git a/kolourpaint/pixmapfx/kpfloodfill.cpp b/kolourpaint/pixmapfx/kpfloodfill.cpp
new file mode 100644
index 00000000..602e8acf
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpfloodfill.cpp
@@ -0,0 +1,362 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define DEBUG_KP_FLOOD_FILL 0
+
+
+#include <kpfloodfill.h>
+
+#include <qapplication.h>
+#include <qbitmap.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+
+#include <kdebug.h>
+#include <kpdefs.h>
+
+#include <kppixmapfx.h>
+#include <kptool.h>
+
+
+kpFloodFill::kpFloodFill (QPixmap *pixmap, int x, int y,
+ const kpColor &color, int processedColorSimilarity)
+ : m_pixmapPtr (pixmap), m_x (x), m_y (y),
+ m_color (color), m_processedColorSimilarity (processedColorSimilarity),
+ m_initState (0)
+{
+}
+
+kpFloodFill::~kpFloodFill ()
+{
+}
+
+
+// private
+int kpFloodFill::fillLinesListSize (const QValueList <kpFloodFill::FillLine> &fillLines) const
+{
+ return (fillLines.size () * kpFloodFill::FillLine::size ());
+}
+
+// public
+int kpFloodFill::size () const
+{
+ int fillLinesCacheSize = 0;
+ for (QValueVector < QValueList <kpFloodFill::FillLine > >::const_iterator it = m_fillLinesCache.begin ();
+ it != m_fillLinesCache.end ();
+ it++)
+ {
+ fillLinesCacheSize += fillLinesListSize (*it);
+ }
+
+ return fillLinesListSize (m_fillLines) +
+ kpPixmapFX::imageSize (m_image) +
+ fillLinesCacheSize;
+}
+
+
+QRect kpFloodFill::boundingRect () const
+{
+ return m_boundingRect;
+}
+
+bool kpFloodFill::fill ()
+{
+ if (m_initState < 2 && !prepare ())
+ {
+ kdError () << "kpFloodFill:fill() could not prepare()!" << endl;
+ return false;
+ }
+
+ // not trying to do a NOP fill
+ if (m_boundingRect.isValid ())
+ {
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+ QPainter painter, maskPainter;
+ QBitmap maskBitmap;
+
+ if (m_pixmapPtr->mask () || m_color.isTransparent ())
+ {
+ maskBitmap = kpPixmapFX::getNonNullMask (*m_pixmapPtr);
+ maskPainter.begin (&maskBitmap);
+ maskPainter.setPen (m_color.maskColor ());
+ }
+
+ if (m_color.isOpaque ())
+ {
+ painter.begin (m_pixmapPtr);
+ painter.setPen (m_color.toQColor ());
+ }
+
+ const QValueList <FillLine>::ConstIterator fillLinesEnd = m_fillLines.end ();
+ for (QValueList <FillLine>::ConstIterator it = m_fillLines.begin ();
+ it != fillLinesEnd;
+ it++)
+ {
+ QPoint p1 = QPoint ((*it).m_x1, (*it).m_y);
+ QPoint p2 = QPoint ((*it).m_x2, (*it).m_y);
+
+ if (painter.isActive ())
+ painter.drawLine (p1, p2);
+
+ if (maskPainter.isActive ())
+ maskPainter.drawLine (p1, p2);
+ }
+
+ if (painter.isActive ())
+ painter.end ();
+
+ if (maskPainter.isActive ())
+ maskPainter.end ();
+
+ if (!maskBitmap.isNull ())
+ m_pixmapPtr->setMask (maskBitmap);
+
+ QApplication::restoreOverrideCursor ();
+ }
+ else
+ {
+ #if DEBUG_KP_FLOOD_FILL && 1
+ kdDebug () << "kpFloodFill::fill() performing NOP fill" << endl;
+ #endif
+ }
+
+ return true;
+}
+
+bool kpFloodFill::prepareColorToChange ()
+{
+#if DEBUG_KP_FLOOD_FILL && 1
+ kdDebug () << "kpFloodFill::prepareColorToChange" << endl;
+#endif
+
+ m_colorToChange = kpPixmapFX::getColorAtPixel (*m_pixmapPtr, QPoint (m_x, m_y));
+
+ if (m_colorToChange.isOpaque ())
+ {
+ #if DEBUG_KP_FLOOD_FILL && 1
+ kdDebug () << "\tcolorToChange: r=" << m_colorToChange.red ()
+ << ", b=" << m_colorToChange.blue ()
+ << ", g=" << m_colorToChange.green ()
+ << endl;
+ #endif
+ }
+ else
+ {
+ #if DEBUG_KP_FLOOD_FILL && 1
+ kdDebug () << "\tcolorToChange: transparent" << endl;
+ #endif
+ }
+
+ m_initState = 1;
+ return true;
+}
+
+// Derived from the zSprite2 Graphics Engine
+
+bool kpFloodFill::prepare ()
+{
+#if DEBUG_KP_FLOOD_FILL && 1
+ kdDebug () << "kpFloodFill::prepare()" << endl;
+#endif
+ m_boundingRect = QRect ();
+
+ if (m_initState < 1 && !prepareColorToChange ())
+ {
+ kdError () << "kpFloodFill:prepare() could not prepareColorToChange()!" << endl;
+ return false;
+ }
+
+#if DEBUG_KP_FLOOD_FILL && 1
+ kdDebug () << "\tperforming NOP check" << endl;
+#endif
+
+ // get the color we need to replace
+ if (m_processedColorSimilarity == 0 && m_color == m_colorToChange)
+ {
+ // need to do absolutely nothing (this is a significant optimisation
+ // for people who randomly click a lot over already-filled areas)
+ m_initState = 2; // sync with all "return true"'s
+ return true;
+ }
+
+#if DEBUG_KP_FLOOD_FILL && 1
+ kdDebug () << "\tconverting to image" << endl;
+#endif
+
+ // is this the only way to read pixels?
+ m_image = kpPixmapFX::convertToImage (*m_pixmapPtr);
+ if (m_image.isNull ())
+ {
+ kdError () << "kpFloodFill::prepare() could not convert to QImage" << endl;
+ return false;
+ }
+
+#if DEBUG_KP_FLOOD_FILL && 1
+ kdDebug () << "\tcreating fillLinesCache" << endl;
+#endif
+
+ // ready cache
+ m_fillLinesCache.resize (m_pixmapPtr->height ());
+
+#if DEBUG_KP_FLOOD_FILL && 1
+ kdDebug () << "\tcreating fill lines" << endl;
+#endif
+
+ // draw initial line
+ addLine (m_y, findMinX (m_y, m_x), findMaxX (m_y, m_x));
+
+ for (QValueList <FillLine>::ConstIterator it = m_fillLines.begin ();
+ it != m_fillLines.end ();
+ it++)
+ {
+ #if DEBUG_KP_FLOOD_FILL && 0
+ kdDebug () << "Expanding from y=" << (*it).m_y
+ << " x1=" << (*it).m_x1
+ << " x2=" << (*it).m_x2
+ << endl;
+ #endif
+
+ // make more lines above and below current line
+ findAndAddLines (*it, -1);
+ findAndAddLines (*it, +1);
+ }
+
+#if DEBUG_KP_FLOOD_FILL && 1
+ kdDebug () << "\tfinalising memory usage" << endl;
+#endif
+
+ // finalize memory usage
+ m_image.reset ();
+ m_fillLinesCache.clear ();
+
+ m_initState = 2; // sync with all "return true"'s
+ return true;
+}
+
+void kpFloodFill::addLine (int y, int x1, int x2)
+{
+#if DEBUG_KP_FLOOD_FILL && 0
+ kdDebug () << "kpFillCommand::fillAddLine (" << y << "," << x1 << "," << x2 << ")" << endl;
+#endif
+
+ m_fillLines.append (FillLine (y, x1, x2));
+ m_fillLinesCache [y].append (FillLine (y /* OPT */, x1, x2));
+ m_boundingRect = m_boundingRect.unite (QRect (QPoint (x1, y), QPoint (x2, y)));
+}
+
+kpColor kpFloodFill::pixelColor (int x, int y, bool *beenHere) const
+{
+ if (beenHere)
+ *beenHere = false;
+
+ if (y >= (int) m_fillLinesCache.count ())
+ {
+ kdError () << "kpFloodFill::pixelColor("
+ << x << ","
+ << y << ") y out of range=" << m_pixmapPtr->height () << endl;
+ return kpColor::invalid;
+ }
+
+ const QValueList <FillLine>::ConstIterator theEnd = m_fillLinesCache [y].end ();
+ for (QValueList <FillLine>::ConstIterator it = m_fillLinesCache [y].begin ();
+ it != theEnd;
+ it++)
+ {
+ if (x >= (*it).m_x1 && x <= (*it).m_x2)
+ {
+ if (beenHere)
+ *beenHere = true;
+ return m_color;
+ }
+ }
+
+ return kpPixmapFX::getColorAtPixel (m_image, QPoint (x, y));
+}
+
+bool kpFloodFill::shouldGoTo (int x, int y) const
+{
+ bool beenThere;
+ const kpColor col = pixelColor (x, y, &beenThere);
+
+ return (!beenThere && col.isSimilarTo (m_colorToChange, m_processedColorSimilarity));
+}
+
+void kpFloodFill::findAndAddLines (const FillLine &fillLine, int dy)
+{
+ // out of bounds?
+ if (fillLine.m_y + dy < 0 || fillLine.m_y + dy >= m_pixmapPtr->height ())
+ return;
+
+ for (int xnow = fillLine.m_x1; xnow <= fillLine.m_x2; xnow++)
+ {
+ // At current position, right colour?
+ if (shouldGoTo (xnow, fillLine.m_y + dy))
+ {
+ // Find minimum and maximum x values
+ int minxnow = findMinX (fillLine.m_y + dy, xnow);
+ int maxxnow = findMaxX (fillLine.m_y + dy, xnow);
+
+ // Draw line
+ addLine (fillLine.m_y + dy, minxnow, maxxnow);
+
+ // Move x pointer
+ xnow = maxxnow;
+ }
+ }
+}
+
+// finds the minimum x value at a certain line to be filled
+int kpFloodFill::findMinX (int y, int x) const
+{
+ for (;;)
+ {
+ if (x < 0)
+ return 0;
+
+ if (shouldGoTo (x, y))
+ x--;
+ else
+ return x + 1;
+ }
+}
+
+// finds the maximum x value at a certain line to be filled
+int kpFloodFill::findMaxX (int y, int x) const
+{
+ for (;;)
+ {
+ if (x > m_pixmapPtr->width () - 1)
+ return m_pixmapPtr->width () - 1;
+
+ if (shouldGoTo (x, y))
+ x++;
+ else
+ return x - 1;
+ }
+}
diff --git a/kolourpaint/pixmapfx/kpfloodfill.h b/kolourpaint/pixmapfx/kpfloodfill.h
new file mode 100644
index 00000000..5c0d8001
--- /dev/null
+++ b/kolourpaint/pixmapfx/kpfloodfill.h
@@ -0,0 +1,106 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kpfloodfill_h__
+#define __kpfloodfill_h__
+
+#include <qimage.h>
+#include <qvaluelist.h>
+#include <qvaluevector.h>
+
+#include <kpcolor.h>
+
+class QPixmap;
+
+class kpFloodFill
+{
+public:
+ kpFloodFill (QPixmap *pixmap, int x, int y,
+ const kpColor &color,
+ int processedColorSimilarity);
+ ~kpFloodFill ();
+
+ int size () const;
+
+ kpColor color () const { return m_color; }
+ int processedColorSimilarity () const { return m_processedColorSimilarity; }
+
+ // you should call [prepareColorToChange(),[prepare(),[fill()]]]
+ bool prepareColorToChange ();
+
+ // (only valid after prepareColorToChange())
+ kpColor colorToChange () const { return m_colorToChange; };
+
+ bool prepare ();
+ QRect boundingRect () const; // only valid after prepare()
+
+ bool fill ();
+
+private:
+ QPixmap *m_pixmapPtr;
+ int m_x, m_y;
+ kpColor m_color;
+ int m_processedColorSimilarity;
+
+ int m_initState;
+
+ QRect m_boundingRect;
+
+ struct FillLine
+ {
+ FillLine (int y = -1, int x1 = -1, int x2 = -1)
+ : m_y (y), m_x1 (x1), m_x2 (x2)
+ {
+ }
+
+ static int size ()
+ {
+ return sizeof (FillLine);
+ }
+
+ int m_y, m_x1, m_x2;
+ };
+
+ int fillLinesListSize (const QValueList <kpFloodFill::FillLine> &fillLines) const;
+
+ void addLine (int y, int x1, int x2);
+ kpColor pixelColor (int x, int y, bool *beenHere = 0) const;
+ bool shouldGoTo (int x, int y) const;
+ void findAndAddLines (const FillLine &fillLine, int dy);
+ int findMinX (int y, int x) const;
+ int findMaxX (int y, int x) const;
+
+ QValueList <FillLine> m_fillLines;
+
+ // Init info
+ QImage m_image;
+ QValueVector < QValueList <FillLine> > m_fillLinesCache;
+ kpColor m_colorToChange;
+};
+
+#endif // __kpfloodfill_h__
diff --git a/kolourpaint/pixmapfx/kppixmapfx.cpp b/kolourpaint/pixmapfx/kppixmapfx.cpp
new file mode 100644
index 00000000..1bd0b173
--- /dev/null
+++ b/kolourpaint/pixmapfx/kppixmapfx.cpp
@@ -0,0 +1,1677 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define DEBUG_KP_PIXMAP_FX 0
+
+
+#include <kppixmapfx.h>
+
+#include <math.h>
+
+#include <qapplication.h>
+#include <qbitmap.h>
+#include <qdatetime.h>
+#include <qimage.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qpointarray.h>
+#include <qrect.h>
+
+#include <kconfig.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+#include <kpcolor.h>
+#include <kpdefs.h>
+#include <kpselection.h>
+#include <kptool.h>
+
+
+//
+// Overflow Resistant Arithmetic:
+//
+
+// public static
+int kpPixmapFX::addDimensions (int lhs, int rhs)
+{
+ if (lhs < 0 || rhs < 0 ||
+ lhs > INT_MAX - rhs)
+ {
+ return INT_MAX;
+ }
+
+ return lhs + rhs;
+}
+
+// public static
+int kpPixmapFX::multiplyDimensions (int lhs, int rhs)
+{
+ if (rhs == 0)
+ return 0;
+
+ if (lhs < 0 || rhs < 0 ||
+ lhs > INT_MAX / rhs)
+ {
+ return INT_MAX;
+ }
+
+ return lhs * rhs;
+}
+
+
+//
+// QPixmap Statistics
+//
+
+// public static
+int kpPixmapFX::pixmapArea (const QPixmap &pixmap)
+{
+ return kpPixmapFX::pixmapArea (pixmap.width (), pixmap.height ());
+}
+
+// public static
+int kpPixmapFX::pixmapArea (const QPixmap *pixmap)
+{
+ return (pixmap ? kpPixmapFX::pixmapArea (*pixmap) : 0);
+}
+
+// public static
+int kpPixmapFX::pixmapArea (int width, int height)
+{
+ return multiplyDimensions (width, height);
+}
+
+
+// public static
+int kpPixmapFX::pixmapSize (const QPixmap &pixmap)
+{
+ return kpPixmapFX::pixmapSize (pixmap.width (), pixmap.height (),
+ pixmap.depth ());
+}
+
+// public static
+int kpPixmapFX::pixmapSize (const QPixmap *pixmap)
+{
+ return (pixmap ? kpPixmapFX::pixmapSize (*pixmap) : 0);
+}
+
+// public static
+int kpPixmapFX::pixmapSize (int width, int height, int depth)
+{
+ // handle 15bpp
+ int roundedDepth = (depth > 8 ? (depth + 7) / 8 * 8 : depth);
+
+#if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "kpPixmapFX::pixmapSize() w=" << width
+ << " h=" << height
+ << " d=" << depth
+ << " roundedDepth=" << roundedDepth
+ << " ret="
+ << multiplyDimensions (kpPixmapFX::pixmapArea (width, height), roundedDepth) / 8
+ << endl;
+#endif
+ return multiplyDimensions (kpPixmapFX::pixmapArea (width, height), roundedDepth) / 8;
+}
+
+
+// public static
+int kpPixmapFX::imageSize (const QImage &image)
+{
+ return kpPixmapFX::imageSize (image.width (), image.height (), image.depth ());
+}
+
+// public static
+int kpPixmapFX::imageSize (const QImage *image)
+{
+ return (image ? kpPixmapFX::imageSize (*image) : 0);
+}
+
+// public static
+int kpPixmapFX::imageSize (int width, int height, int depth)
+{
+ // handle 15bpp
+ int roundedDepth = (depth > 8 ? (depth + 7) / 8 * 8 : depth);
+
+#if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "kpPixmapFX::imageSize() w=" << width
+ << " h=" << height
+ << " d=" << depth
+ << " roundedDepth=" << roundedDepth
+ << " ret="
+ << multiplyDimensions (multiplyDimensions (width, height), roundedDepth) / 8
+ << endl;
+#endif
+
+ return multiplyDimensions (multiplyDimensions (width, height), roundedDepth) / 8;
+}
+
+
+// public static
+int kpPixmapFX::selectionSize (const kpSelection &sel)
+{
+ return sel.size ();
+}
+
+// public static
+int kpPixmapFX::selectionSize (const kpSelection *sel)
+{
+ return (sel ? sel->size () : 0);
+}
+
+
+// public static
+int kpPixmapFX::stringSize (const QString &string)
+{
+#if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "kpPixmapFX::stringSize(" << string << ")"
+ << " len=" << string.length ()
+ << " sizeof(QChar)=" << sizeof (QChar)
+ << endl;
+#endif
+ return string.length () * sizeof (QChar);
+}
+
+
+// public static
+int kpPixmapFX::pointArraySize (const QPointArray &points)
+{
+#if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "kpPixmapFX::pointArraySize() points.size="
+ << points.size ()
+ << " sizeof(QPoint)=" << sizeof (QPoint)
+ << endl;
+#endif
+
+ return (points.size () * sizeof (QPoint));
+}
+
+
+//
+// QPixmap/QImage Conversion Functions
+//
+
+// public static
+QImage kpPixmapFX::convertToImage (const QPixmap &pixmap)
+{
+ if (pixmap.isNull ())
+ return QImage ();
+
+ return pixmap.convertToImage ();
+}
+
+
+// Returns true if <image> contains translucency (rather than just transparency)
+// QPixmap::hasAlphaChannel() appears to give incorrect results
+static bool imageHasAlphaChannel (const QImage &image)
+{
+ if (image.depth () < 32)
+ return false;
+
+ for (int y = 0; y < image.height (); y++)
+ {
+ for (int x = 0; x < image.width (); x++)
+ {
+ const QRgb rgb = image.pixel (x, y);
+
+ if (qAlpha (rgb) > 0 && qAlpha (rgb) < 255)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static int imageNumColorsUpTo (const QImage &image, int max)
+{
+ QMap <QRgb, bool> rgbMap;
+
+ if (image.depth () <= 8)
+ {
+ for (int i = 0; i < image.numColors () && (int) rgbMap.size () < max; i++)
+ {
+ rgbMap.insert (image.color (i), true);
+ }
+ }
+ else
+ {
+ for (int y = 0; y < image.height () && (int) rgbMap.size () < max; y++)
+ {
+ for (int x = 0; x < image.width () && (int) rgbMap.size () < max; x++)
+ {
+ rgbMap.insert (image.pixel (x, y), true);
+ }
+ }
+ }
+
+ return rgbMap.size ();
+}
+
+static void convertToPixmapWarnAboutLoss (const QImage &image,
+ const kpPixmapFX::WarnAboutLossInfo &wali)
+{
+ if (!wali.isValid ())
+ return;
+
+
+ const QString colorDepthTranslucencyDontAskAgain =
+ wali.m_dontAskAgainPrefix + "_ColorDepthTranslucency";
+ const QString colorDepthDontAskAgain =
+ wali.m_dontAskAgainPrefix + "_ColorDepth";
+ const QString translucencyDontAskAgain =
+ wali.m_dontAskAgainPrefix + "_Translucency";
+
+#if DEBUG_KP_PIXMAP_FX && 1
+ QTime timer;
+ timer.start ();
+#endif
+
+ bool hasAlphaChannel =
+ (KMessageBox::shouldBeShownContinue (translucencyDontAskAgain) &&
+ imageHasAlphaChannel (image));
+
+#if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\twarnAboutLoss - check hasAlphaChannel took "
+ << timer.restart () << "msec" << endl;
+#endif
+
+ bool moreColorsThanDisplay =
+ (KMessageBox::shouldBeShownContinue (colorDepthDontAskAgain) &&
+ image.depth () > QColor::numBitPlanes () &&
+ QColor::numBitPlanes () < 24); // 32 indicates alpha channel
+
+ int screenDepthNeeded = 0;
+
+ if (moreColorsThanDisplay)
+ screenDepthNeeded = QMIN (24, image.depth ());
+
+#if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\ttranslucencyShouldBeShown="
+ << KMessageBox::shouldBeShownContinue (translucencyDontAskAgain)
+ << endl
+ << "\thasAlphaChannel=" << hasAlphaChannel
+ << endl
+ << "\tcolorDepthShownBeShown="
+ << KMessageBox::shouldBeShownContinue (colorDepthDontAskAgain)
+ << endl
+ << "\timage.depth()=" << image.depth ()
+ << endl
+ << "\tscreenDepth=" << QColor::numBitPlanes ()
+ << endl
+ << "\tmoreColorsThanDisplay=" << moreColorsThanDisplay
+ << endl
+ << "\tneedDepth=" << screenDepthNeeded
+ << endl;
+#endif
+
+
+ QApplication::setOverrideCursor (Qt::arrowCursor);
+
+ if (moreColorsThanDisplay && hasAlphaChannel)
+ {
+ KMessageBox::information (wali.m_parent,
+ wali.m_moreColorsThanDisplayAndHasAlphaChannelMessage
+ .arg (screenDepthNeeded),
+ QString::null, // or would you prefer "Low Screen Depth and Image Contains Transparency"? :)
+ colorDepthTranslucencyDontAskAgain);
+
+ if (!KMessageBox::shouldBeShownContinue (colorDepthTranslucencyDontAskAgain))
+ {
+ KMessageBox::saveDontShowAgainContinue (colorDepthDontAskAgain);
+ KMessageBox::saveDontShowAgainContinue (translucencyDontAskAgain);
+ }
+ }
+ else if (moreColorsThanDisplay)
+ {
+ KMessageBox::information (wali.m_parent,
+ wali.m_moreColorsThanDisplayMessage
+ .arg (screenDepthNeeded),
+ i18n ("Low Screen Depth"),
+ colorDepthDontAskAgain);
+ }
+ else if (hasAlphaChannel)
+ {
+ KMessageBox::information (wali.m_parent,
+ wali.m_hasAlphaChannelMessage,
+ i18n ("Image Contains Translucency"),
+ translucencyDontAskAgain);
+ }
+
+ QApplication::restoreOverrideCursor ();
+}
+
+// public static
+QPixmap kpPixmapFX::convertToPixmap (const QImage &image, bool pretty,
+ const WarnAboutLossInfo &wali)
+{
+#if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "kpPixmapFX::convertToPixmap(image,pretty=" << pretty
+ << ",warnAboutLossInfo.isValid=" << wali.isValid ()
+ << ")" << endl;
+ QTime timer;
+ timer.start ();
+#endif
+
+ if (image.isNull ())
+ return QPixmap ();
+
+
+ QPixmap destPixmap;
+
+ if (!pretty)
+ {
+ destPixmap.convertFromImage (image,
+ Qt::ColorOnly/*always display depth*/ |
+ Qt::ThresholdDither/*no dither*/ |
+ Qt::ThresholdAlphaDither/*no dither alpha*/|
+ Qt::AvoidDither);
+ }
+ else
+ {
+ destPixmap.convertFromImage (image,
+ Qt::ColorOnly/*always display depth*/ |
+ Qt::DiffuseDither/*hi quality dither*/ |
+ Qt::ThresholdAlphaDither/*no dither alpha*/ |
+ Qt::PreferDither/*(dither even if <256 colours)*/);
+ }
+
+#if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\tconversion took " << timer.elapsed () << "msec" << endl;
+#endif
+
+ kpPixmapFX::ensureNoAlphaChannel (&destPixmap);
+
+
+ if (wali.isValid ())
+ convertToPixmapWarnAboutLoss (image, wali);
+
+
+ return destPixmap;
+}
+
+// TODO: don't dup convertToPixmap() code
+// public static
+QPixmap kpPixmapFX::convertToPixmapAsLosslessAsPossible (const QImage &image,
+ const WarnAboutLossInfo &wali)
+{
+#if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "kpPixmapFX::convertToPixmapAsLosslessAsPossible(image depth="
+ << image.depth ()
+ << ",warnAboutLossInfo.isValid=" << wali.isValid ()
+ << ") screenDepth=" << QPixmap::defaultDepth ()
+ << " imageNumColorsUpTo257=" << imageNumColorsUpTo (image, 257)
+ << endl;
+ QTime timer;
+ timer.start ();
+#endif
+
+ if (image.isNull ())
+ return QPixmap ();
+
+
+ const int screenDepth = (QPixmap::defaultDepth () >= 24 ?
+ 32 :
+ QPixmap::defaultDepth ());
+
+ QPixmap destPixmap;
+ int ditherFlags = 0;
+
+ if (image.depth () <= screenDepth)
+ {
+ #if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\timage depth <= screen depth - don't dither"
+ << " (AvoidDither | ThresholdDither)" << endl;
+ #endif
+
+ ditherFlags = (Qt::AvoidDither | Qt::ThresholdDither);
+ }
+ // PRE: image.depth() > screenDepth
+ // ASSERT: screenDepth < 32
+ else if (screenDepth <= 8)
+ {
+ const int screenNumColors = (1 << screenDepth);
+
+ #if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\tscreen depth <= 8; imageNumColorsUpTo"
+ << (screenNumColors + 1)
+ << "=" << imageNumColorsUpTo (image, screenNumColors + 1)
+ << endl;
+ #endif
+
+ if (imageNumColorsUpTo (image, screenNumColors + 1) <= screenNumColors)
+ {
+ #if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\t\tcolors fit on screen - don't dither"
+ << " (AvoidDither | ThresholdDither)" << endl;
+ #endif
+ ditherFlags = (Qt::AvoidDither | Qt::ThresholdDither);
+ }
+ else
+ {
+ #if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\t\tcolors don't fit on screen - dither"
+ << " (PreferDither | DiffuseDither)" << endl;
+ #endif
+ ditherFlags = (Qt::PreferDither | Qt::DiffuseDither);
+ }
+ }
+ // PRE: image.depth() > screenDepth &&
+ // screenDepth > 8
+ // ASSERT: screenDepth < 32
+ else
+ {
+ #if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\tscreen depth > 8 - read config" << endl;
+ #endif
+
+ int configDitherIfNumColorsGreaterThan = 323;
+
+ KConfigGroupSaver cfgGroupSaver (KGlobal::config (),
+ kpSettingsGroupGeneral);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ if (cfg->hasKey (kpSettingDitherOnOpen))
+ {
+ configDitherIfNumColorsGreaterThan = cfg->readNumEntry (kpSettingDitherOnOpen);
+ }
+ else
+ {
+ cfg->writeEntry (kpSettingDitherOnOpen, configDitherIfNumColorsGreaterThan);
+ cfg->sync ();
+ }
+
+ #if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\t\tcfg=" << configDitherIfNumColorsGreaterThan
+ << " image=" << imageNumColorsUpTo (image, configDitherIfNumColorsGreaterThan + 1)
+ << endl;
+ #endif
+
+ if (imageNumColorsUpTo (image, configDitherIfNumColorsGreaterThan + 1) >
+ configDitherIfNumColorsGreaterThan)
+ {
+ #if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\t\t\talways dither (PreferDither | DiffuseDither)"
+ << endl;
+ #endif
+ ditherFlags = (Qt::PreferDither | Qt::DiffuseDither);
+ }
+ else
+ {
+ #if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\t\t\tdon't dither (AvoidDither | ThresholdDither)"
+ << endl;
+ #endif
+ ditherFlags = (Qt::AvoidDither | Qt::ThresholdDither);
+ }
+ }
+
+
+ destPixmap.convertFromImage (image,
+ Qt::ColorOnly/*always display depth*/ |
+ Qt::ThresholdAlphaDither/*no dither alpha*/ |
+ ditherFlags);
+
+#if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\tconversion took " << timer.elapsed () << "msec" << endl;
+#endif
+
+ kpPixmapFX::ensureNoAlphaChannel (&destPixmap);
+
+
+ if (wali.isValid ())
+ convertToPixmapWarnAboutLoss (image, wali);
+
+
+ return destPixmap;
+}
+
+
+// public static
+QPixmap kpPixmapFX::pixmapWithDefinedTransparentPixels (const QPixmap &pixmap,
+ const QColor &transparentColor)
+{
+ if (!pixmap.mask ())
+ return pixmap;
+
+ QPixmap retPixmap (pixmap.width (), pixmap.height ());
+ retPixmap.fill (transparentColor);
+
+ QPainter p (&retPixmap);
+ p.drawPixmap (QPoint (0, 0), pixmap);
+ p.end ();
+
+ retPixmap.setMask (*pixmap.mask ());
+ return retPixmap;
+}
+
+
+//
+// Get/Set Parts of Pixmap
+//
+
+
+// public static
+QPixmap kpPixmapFX::getPixmapAt (const QPixmap &pm, const QRect &rect)
+{
+ QPixmap retPixmap (rect.width (), rect.height ());
+
+#if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "kpPixmapFX::getPixmapAt(pm.hasMask="
+ << (pm.mask () ? 1 : 0)
+ << ",rect="
+ << rect
+ << ")"
+ << endl;
+#endif
+
+ const QRect validSrcRect = pm.rect ().intersect (rect);
+ const bool wouldHaveUndefinedPixels = (validSrcRect != rect);
+
+ if (wouldHaveUndefinedPixels)
+ {
+ #if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "\tret would contain undefined pixels - setting them to transparent" << endl;
+ #endif
+ QBitmap transparentMask (rect.width (), rect.height ());
+ transparentMask.fill (Qt::color0/*transparent*/);
+ retPixmap.setMask (transparentMask);
+ }
+
+ if (validSrcRect.isEmpty ())
+ {
+ #if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "\tsilly case - completely invalid rect - ret transparent pixmap" << endl;
+ #endif
+ return retPixmap;
+ }
+
+
+ const QPoint destTopLeft = validSrcRect.topLeft () - rect.topLeft ();
+
+ // copy data _and_ mask (if avail)
+ copyBlt (&retPixmap, /* dest */
+ destTopLeft.x (), destTopLeft.y (), /* dest pt */
+ &pm, /* src */
+ validSrcRect.x (), validSrcRect.y (), /* src pt */
+ validSrcRect.width (), validSrcRect.height ());
+
+ if (wouldHaveUndefinedPixels && retPixmap.mask () && !pm.mask ())
+ {
+ #if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "\tensure opaque in valid region" << endl;
+ #endif
+ kpPixmapFX::ensureOpaqueAt (&retPixmap,
+ QRect (destTopLeft.x (), destTopLeft.y (),
+ validSrcRect.width (), validSrcRect.height ()));
+ }
+
+#if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "\tretPixmap.hasMask="
+ << (retPixmap.mask () ? 1 : 0)
+ << endl;
+#endif
+
+ return retPixmap;
+}
+
+
+// public static
+void kpPixmapFX::setPixmapAt (QPixmap *destPixmapPtr, const QRect &destRect,
+ const QPixmap &srcPixmap)
+{
+ if (!destPixmapPtr)
+ return;
+
+#if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "kpPixmapFX::setPixmapAt(destPixmap->rect="
+ << destPixmapPtr->rect ()
+ << ",destPixmap->hasMask="
+ << (destPixmapPtr->mask () ? 1 : 0)
+ << ",destRect="
+ << destRect
+ << ",srcPixmap.rect="
+ << srcPixmap.rect ()
+ << ",srcPixmap.hasMask="
+ << (srcPixmap.mask () ? 1 : 0)
+ << ")"
+ << endl;
+#endif
+
+#if DEBUG_KP_PIXMAP_FX && 0
+ if (destPixmapPtr->mask ())
+ {
+ QImage image = kpPixmapFX::convertToImage (*destPixmapPtr);
+ int numTrans = 0;
+
+ for (int y = 0; y < image.height (); y++)
+ {
+ for (int x = 0; x < image.width (); x++)
+ {
+ if (qAlpha (image.pixel (x, y)) == 0)
+ numTrans++;
+ }
+ }
+
+ kdDebug () << "\tdestPixmapPtr numTrans=" << numTrans << endl;
+ }
+#endif
+
+#if 0
+ // TODO: why does undo'ing a single pen dot on a transparent pixel,
+ // result in a opaque image, except for that single transparent pixel???
+ // Qt bug on boundary case?
+
+ // copy data _and_ mask
+ copyBlt (destPixmapPtr,
+ destAt.x (), destAt.y (),
+ &srcPixmap,
+ 0, 0,
+ destRect.width (), destRect.height ());
+#else
+ bitBlt (destPixmapPtr,
+ destRect.x (), destRect.y (),
+ &srcPixmap,
+ 0, 0,
+ destRect.width (), destRect.height (),
+ Qt::CopyROP,
+ true/*ignore mask*/);
+
+ if (srcPixmap.mask ())
+ {
+ QBitmap mask = getNonNullMask (*destPixmapPtr);
+ bitBlt (&mask,
+ destRect.x (), destRect.y (),
+ srcPixmap.mask (),
+ 0, 0,
+ destRect.width (), destRect.height (),
+ Qt::CopyROP,
+ true/*ignore mask*/);
+ destPixmapPtr->setMask (mask);
+ }
+#endif
+
+ if (destPixmapPtr->mask () && !srcPixmap.mask ())
+ {
+ #if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "\t\topaque'ing dest rect" << endl;
+ #endif
+ kpPixmapFX::ensureOpaqueAt (destPixmapPtr, destRect);
+ }
+
+#if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "\tdestPixmap->hasMask="
+ << (destPixmapPtr->mask () ? 1 : 0)
+ << endl;
+ if (destPixmapPtr->mask ())
+ {
+ QImage image = kpPixmapFX::convertToImage (*destPixmapPtr);
+ int numTrans = 0;
+
+ for (int y = 0; y < image.height (); y++)
+ {
+ for (int x = 0; x < image.width (); x++)
+ {
+ if (qAlpha (image.pixel (x, y)) == 0)
+ numTrans++;
+ }
+ }
+
+ kdDebug () << "\tdestPixmapPtr numTrans=" << numTrans << endl;
+ }
+#endif
+}
+
+// public static
+void kpPixmapFX::setPixmapAt (QPixmap *destPixmapPtr, const QPoint &destAt,
+ const QPixmap &srcPixmap)
+{
+ kpPixmapFX::setPixmapAt (destPixmapPtr,
+ QRect (destAt.x (), destAt.y (),
+ srcPixmap.width (), srcPixmap.height ()),
+ srcPixmap);
+}
+
+// public static
+void kpPixmapFX::setPixmapAt (QPixmap *destPixmapPtr, int destX, int destY,
+ const QPixmap &srcPixmap)
+{
+ kpPixmapFX::setPixmapAt (destPixmapPtr, QPoint (destX, destY), srcPixmap);
+}
+
+
+// public static
+void kpPixmapFX::paintPixmapAt (QPixmap *destPixmapPtr, const QPoint &destAt,
+ const QPixmap &srcPixmap)
+{
+ if (!destPixmapPtr)
+ return;
+
+ // Copy src (masked by src's mask) on top of dest.
+ bitBlt (destPixmapPtr, /* dest */
+ destAt.x (), destAt.y (), /* dest pt */
+ &srcPixmap, /* src */
+ 0, 0 /* src pt */);
+
+ kpPixmapFX::ensureOpaqueAt (destPixmapPtr, destAt, srcPixmap);
+}
+
+// public static
+void kpPixmapFX::paintPixmapAt (QPixmap *destPixmapPtr, int destX, int destY,
+ const QPixmap &srcPixmap)
+{
+ kpPixmapFX::paintPixmapAt (destPixmapPtr, QPoint (destX, destY), srcPixmap);
+}
+
+
+// public static
+kpColor kpPixmapFX::getColorAtPixel (const QPixmap &pm, const QPoint &at)
+{
+#if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "kpToolColorPicker::colorAtPixel" << p << endl;
+#endif
+
+ if (at.x () < 0 || at.x () >= pm.width () ||
+ at.y () < 0 || at.y () >= pm.height ())
+ {
+ return kpColor::invalid;
+ }
+
+ QPixmap pixmap = getPixmapAt (pm, QRect (at, at));
+ QImage image = kpPixmapFX::convertToImage (pixmap);
+ if (image.isNull ())
+ {
+ kdError () << "kpPixmapFX::getColorAtPixel(QPixmap) could not convert to QImage" << endl;
+ return kpColor::invalid;
+ }
+
+ return getColorAtPixel (image, QPoint (0, 0));
+}
+
+// public static
+kpColor kpPixmapFX::getColorAtPixel (const QPixmap &pm, int x, int y)
+{
+ return kpPixmapFX::getColorAtPixel (pm, QPoint (x, y));
+}
+
+// public static
+kpColor kpPixmapFX::getColorAtPixel (const QImage &img, const QPoint &at)
+{
+ if (!img.valid (at.x (), at.y ()))
+ return kpColor::invalid;
+
+ QRgb rgba = img.pixel (at.x (), at.y ());
+ return kpColor (rgba);
+}
+
+// public static
+kpColor kpPixmapFX::getColorAtPixel (const QImage &img, int x, int y)
+{
+ return kpPixmapFX::getColorAtPixel (img, QPoint (x, y));
+}
+
+
+//
+// Mask Operations
+//
+
+
+// public static
+void kpPixmapFX::ensureNoAlphaChannel (QPixmap *destPixmapPtr)
+{
+ if (destPixmapPtr->hasAlphaChannel ())
+ destPixmapPtr->setMask (kpPixmapFX::getNonNullMask/*just in case*/ (*destPixmapPtr));
+}
+
+
+// public static
+QBitmap kpPixmapFX::getNonNullMask (const QPixmap &pm)
+{
+ if (pm.mask ())
+ return *pm.mask ();
+ else
+ {
+ QBitmap maskBitmap (pm.width (), pm.height ());
+ maskBitmap.fill (Qt::color1/*opaque*/);
+
+ return maskBitmap;
+ }
+}
+
+
+// public static
+void kpPixmapFX::ensureTransparentAt (QPixmap *destPixmapPtr, const QRect &destRect)
+{
+ if (!destPixmapPtr)
+ return;
+
+ QBitmap maskBitmap = getNonNullMask (*destPixmapPtr);
+
+ QPainter p (&maskBitmap);
+
+ p.setPen (Qt::color0/*transparent*/);
+ p.setBrush (Qt::color0/*transparent*/);
+
+ p.drawRect (destRect);
+
+ p.end ();
+
+ destPixmapPtr->setMask (maskBitmap);
+}
+
+
+// public static
+void kpPixmapFX::paintMaskTransparentWithBrush (QPixmap *destPixmapPtr, const QPoint &destAt,
+ const QPixmap &brushBitmap)
+{
+ if (!destPixmapPtr)
+ return;
+
+ if (brushBitmap.depth () > 1)
+ {
+ kdError () << "kpPixmapFX::paintMaskTransparentWidthBrush() passed brushPixmap with depth > 1" << endl;
+ return;
+ }
+
+ QBitmap destMaskBitmap = kpPixmapFX::getNonNullMask (*destPixmapPtr);
+
+ // Src
+ // Dest Mask Brush Bitmap = Result
+ // -------------------------------------
+ // 0 0 0
+ // 0 1 0
+ // 1 0 1
+ // 1 1 0
+ //
+ // Brush Bitmap value of 1 means "make transparent"
+ // 0 means "leave it as it is"
+
+ bitBlt (&destMaskBitmap,
+ destAt.x (), destAt.y (),
+ &brushBitmap,
+ 0, 0,
+ brushBitmap.width (), brushBitmap.height (),
+ Qt::NotAndROP);
+
+ destPixmapPtr->setMask (destMaskBitmap);
+}
+
+// public static
+void kpPixmapFX::paintMaskTransparentWithBrush (QPixmap *destPixmapPtr, int destX, int destY,
+ const QPixmap &brushBitmap)
+{
+ kpPixmapFX::paintMaskTransparentWithBrush (destPixmapPtr,
+ QPoint (destX, destY),
+ brushBitmap);
+}
+
+
+// public static
+void kpPixmapFX::ensureOpaqueAt (QPixmap *destPixmapPtr, const QRect &destRect)
+{
+ if (!destPixmapPtr || !destPixmapPtr->mask ()/*already opaque*/)
+ return;
+
+ QBitmap maskBitmap = *destPixmapPtr->mask ();
+
+ QPainter p (&maskBitmap);
+
+ p.setPen (Qt::color1/*opaque*/);
+ p.setBrush (Qt::color1/*opaque*/);
+
+ p.drawRect (destRect);
+
+ p.end ();
+
+ destPixmapPtr->setMask (maskBitmap);
+}
+
+// public static
+void kpPixmapFX::ensureOpaqueAt (QPixmap *destPixmapPtr, const QPoint &destAt,
+ const QPixmap &srcPixmap)
+{
+ if (!destPixmapPtr || !destPixmapPtr->mask ()/*already opaque*/)
+ return;
+
+ QBitmap destMask = *destPixmapPtr->mask ();
+
+ if (srcPixmap.mask ())
+ {
+ bitBlt (&destMask, /* dest */
+ destAt, /* dest pt */
+ srcPixmap.mask (), /* src */
+ QRect (0, 0, srcPixmap.width (), srcPixmap.height ()), /* src rect */
+ Qt::OrROP/*if either is opaque, it's opaque*/);
+ }
+ else
+ {
+ QPainter p (&destMask);
+
+ p.setPen (Qt::color1/*opaque*/);
+ p.setBrush (Qt::color1/*opaque*/);
+
+ p.drawRect (destAt.x (), destAt.y (),
+ srcPixmap.width (), srcPixmap.height ());
+
+ p.end ();
+ }
+
+ destPixmapPtr->setMask (destMask);
+}
+
+// public static
+void kpPixmapFX::ensureOpaqueAt (QPixmap *destPixmapPtr, int destX, int destY,
+ const QPixmap &srcPixmap)
+{
+ kpPixmapFX::ensureOpaqueAt (destPixmapPtr, QPoint (destX, destY), srcPixmap);
+}
+
+
+//
+// Effects
+//
+
+// public static
+void kpPixmapFX::convertToGrayscale (QPixmap *destPixmapPtr)
+{
+ QImage image = kpPixmapFX::convertToImage (*destPixmapPtr);
+ kpPixmapFX::convertToGrayscale (&image);
+ *destPixmapPtr = kpPixmapFX::convertToPixmap (image);
+}
+
+// public static
+QPixmap kpPixmapFX::convertToGrayscale (const QPixmap &pm)
+{
+ QImage image = kpPixmapFX::convertToImage (pm);
+ kpPixmapFX::convertToGrayscale (&image);
+ return kpPixmapFX::convertToPixmap (image);
+}
+
+static QRgb toGray (QRgb rgb)
+{
+ // naive way that doesn't preserve brightness
+ // int gray = (qRed (rgb) + qGreen (rgb) + qBlue (rgb)) / 3;
+
+ // over-exaggerates red & blue
+ // int gray = qGray (rgb);
+
+ int gray = (212671 * qRed (rgb) + 715160 * qGreen (rgb) + 72169 * qBlue (rgb)) / 1000000;
+ return qRgba (gray, gray, gray, qAlpha (rgb));
+}
+
+// public static
+void kpPixmapFX::convertToGrayscale (QImage *destImagePtr)
+{
+ if (destImagePtr->depth () > 8)
+ {
+ // hmm, why not just write to the pixmap directly???
+
+ for (int y = 0; y < destImagePtr->height (); y++)
+ {
+ for (int x = 0; x < destImagePtr->width (); x++)
+ {
+ destImagePtr->setPixel (x, y, toGray (destImagePtr->pixel (x, y)));
+ }
+ }
+ }
+ else
+ {
+ // 1- & 8- bit images use a color table
+ for (int i = 0; i < destImagePtr->numColors (); i++)
+ destImagePtr->setColor (i, toGray (destImagePtr->color (i)));
+ }
+}
+
+// public static
+QImage kpPixmapFX::convertToGrayscale (const QImage &img)
+{
+ QImage retImage = img;
+ kpPixmapFX::convertToGrayscale (&retImage);
+ return retImage;
+}
+
+
+// public static
+void kpPixmapFX::fill (QPixmap *destPixmapPtr, const kpColor &color)
+{
+ if (!destPixmapPtr)
+ return;
+
+ if (color.isOpaque ())
+ {
+ destPixmapPtr->setMask (QBitmap ()); // no mask = opaque
+ destPixmapPtr->fill (color.toQColor ());
+ }
+ else
+ {
+ kpPixmapFX::ensureTransparentAt (destPixmapPtr, destPixmapPtr->rect ());
+ }
+}
+
+// public static
+QPixmap kpPixmapFX::fill (const QPixmap &pm, const kpColor &color)
+{
+ QPixmap ret = pm;
+ kpPixmapFX::fill (&ret, color);
+ return ret;
+}
+
+
+// public static
+void kpPixmapFX::resize (QPixmap *destPixmapPtr, int w, int h,
+ const kpColor &backgroundColor, bool fillNewAreas)
+{
+#if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "kpPixmapFX::resize()" << endl;
+#endif
+
+ if (!destPixmapPtr)
+ return;
+
+ int oldWidth = destPixmapPtr->width ();
+ int oldHeight = destPixmapPtr->height ();
+
+ if (w == oldWidth && h == oldHeight)
+ return;
+
+
+ destPixmapPtr->resize (w, h);
+
+ if (fillNewAreas && (w > oldWidth || h > oldHeight))
+ {
+ #if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\tfilling in new areas" << endl;
+ #endif
+ QBitmap maskBitmap;
+ QPainter painter, maskPainter;
+
+ if (backgroundColor.isOpaque ())
+ {
+ painter.begin (destPixmapPtr);
+ painter.setPen (backgroundColor.toQColor ());
+ painter.setBrush (backgroundColor.toQColor ());
+ }
+
+ if (backgroundColor.isTransparent () || destPixmapPtr->mask ())
+ {
+ maskBitmap = kpPixmapFX::getNonNullMask (*destPixmapPtr);
+ maskPainter.begin (&maskBitmap);
+ maskPainter.setPen (backgroundColor.maskColor ());
+ maskPainter.setBrush (backgroundColor.maskColor ());
+ }
+
+ #define PAINTER_CALL(cmd) \
+ { \
+ if (painter.isActive ()) \
+ painter . cmd ; \
+ \
+ if (maskPainter.isActive ()) \
+ maskPainter . cmd ; \
+ }
+ if (w > oldWidth)
+ PAINTER_CALL (drawRect (oldWidth, 0, w - oldWidth, oldHeight));
+
+ if (h > oldHeight)
+ PAINTER_CALL (drawRect (0, oldHeight, w, h - oldHeight));
+ #undef PAINTER_CALL
+
+ if (maskPainter.isActive ())
+ maskPainter.end ();
+
+ if (painter.isActive ())
+ painter.end ();
+
+ if (!maskBitmap.isNull ())
+ destPixmapPtr->setMask (maskBitmap);
+ }
+}
+
+// public static
+QPixmap kpPixmapFX::resize (const QPixmap &pm, int w, int h,
+ const kpColor &backgroundColor, bool fillNewAreas)
+{
+ QPixmap ret = pm;
+ kpPixmapFX::resize (&ret, w, h, backgroundColor, fillNewAreas);
+ return ret;
+}
+
+
+// public static
+void kpPixmapFX::scale (QPixmap *destPixmapPtr, int w, int h, bool pretty)
+{
+ if (!destPixmapPtr)
+ return;
+
+ *destPixmapPtr = kpPixmapFX::scale (*destPixmapPtr, w, h, pretty);
+}
+
+// public static
+QPixmap kpPixmapFX::scale (const QPixmap &pm, int w, int h, bool pretty)
+{
+#if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "kpPixmapFX::scale(oldRect=" << pm.rect ()
+ << ",w=" << w
+ << ",h=" << h
+ << ",pretty=" << pretty
+ << ")"
+ << endl;
+#endif
+
+ if (w == pm.width () && h == pm.height ())
+ return pm;
+
+ if (pretty)
+ {
+ QImage image = kpPixmapFX::convertToImage (pm);
+
+ #if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "\tBefore smooth scale:" << endl;
+ for (int y = 0; y < image.height (); y++)
+ {
+ for (int x = 0; x < image.width (); x++)
+ {
+ fprintf (stderr, " %08X", image.pixel (x, y));
+ }
+ fprintf (stderr, "\n");
+ }
+ #endif
+
+ image = image.smoothScale (w, h);
+
+ #if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "\tAfter smooth scale:" << endl;
+ for (int y = 0; y < image.height (); y++)
+ {
+ for (int x = 0; x < image.width (); x++)
+ {
+ fprintf (stderr, " %08X", image.pixel (x, y));
+ }
+ fprintf (stderr, "\n");
+ }
+ #endif
+
+ return kpPixmapFX::convertToPixmap (image, false/*let's not smooth it again*/);
+ }
+ else
+ {
+ QWMatrix matrix;
+
+ matrix.scale (double (w) / double (pm.width ()),
+ double (h) / double (pm.height ()));
+
+ return pm.xForm (matrix);
+ }
+}
+
+
+// public static
+double kpPixmapFX::AngleInDegreesEpsilon =
+ KP_RADIANS_TO_DEGREES (atan (1.0 / 10000.0))
+ / (2.0/*max error allowed*/ * 2.0/*for good measure*/);
+
+
+static QWMatrix matrixWithZeroOrigin (const QWMatrix &matrix, int width, int height)
+{
+#if DEBUG_KP_PIXMAP_FX
+ kdDebug () << "matrixWithZeroOrigin(w=" << width << ",h=" << height << ")" << endl;
+ kdDebug () << "\tmatrix: m11=" << matrix.m11 ()
+ << " m12=" << matrix.m12 ()
+ << " m21=" << matrix.m21 ()
+ << " m22=" << matrix.m22 ()
+ << " dx=" << matrix.dx ()
+ << " dy=" << matrix.dy ()
+ << endl;
+#endif
+ // TODO: Should we be using QWMatrix::Areas?
+ QRect newRect = matrix.mapRect (QRect (0, 0, width, height));
+#if DEBUG_KP_PIXMAP_FX
+ kdDebug () << "\tnewRect=" << newRect << endl;
+#endif
+
+ QWMatrix translatedMatrix (matrix.m11 (), matrix.m12 (), matrix.m21 (), matrix.m22 (),
+ matrix.dx () - newRect.left (), matrix.dy () - newRect.top ());
+
+ return translatedMatrix;
+}
+
+static QPixmap xForm (const QPixmap &pm, const QWMatrix &transformMatrix_,
+ const kpColor &backgroundColor,
+ int targetWidth, int targetHeight)
+{
+ QWMatrix transformMatrix = transformMatrix_;
+
+#if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "kppixmapfx.cpp: xForm(pm.size=" << pm.size ()
+ << ",targetWidth=" << targetWidth
+ << ",targetHeight=" << targetHeight
+ << ")"
+ << endl;
+#endif
+ // TODO: Should we be using QWMatrix::Areas?
+ QRect newRect = transformMatrix.map (pm.rect ());
+#if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\tmappedRect=" << newRect << endl;
+
+#endif
+
+ QWMatrix scaleMatrix;
+ if (targetWidth > 0 && targetWidth != newRect.width ())
+ {
+ #if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\tadjusting for targetWidth" << endl;
+ #endif
+ scaleMatrix.scale (double (targetWidth) / double (newRect.width ()), 1);
+ }
+
+ if (targetHeight > 0 && targetHeight != newRect.height ())
+ {
+ #if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\tadjusting for targetHeight" << endl;
+ #endif
+ scaleMatrix.scale (1, double (targetHeight) / double (newRect.height ()));
+ }
+
+ if (!scaleMatrix.isIdentity ())
+ {
+ #if DEBUG_KP_PIXMAP_FX && 1
+ // TODO: What is going on here??? Why isn't matrix * working properly?
+ QWMatrix wrongMatrix = transformMatrix * scaleMatrix;
+ QWMatrix oldHat = transformMatrix;
+ if (targetWidth > 0 && targetWidth != newRect.width ())
+ oldHat.scale (double (targetWidth) / double (newRect.width ()), 1);
+ if (targetHeight > 0 && targetHeight != newRect.height ())
+ oldHat.scale (1, double (targetHeight) / double (newRect.height ()));
+ QWMatrix altHat = transformMatrix;
+ altHat.scale ((targetWidth > 0 && targetWidth != newRect.width ()) ? double (targetWidth) / double (newRect.width ()) : 1,
+ (targetHeight > 0 && targetHeight != newRect.height ()) ? double (targetHeight) / double (newRect.height ()) : 1);
+ QWMatrix correctMatrix = scaleMatrix * transformMatrix;
+
+ kdDebug () << "\tsupposedlyWrongMatrix: m11=" << wrongMatrix.m11 () // <<<---- this is the correct matrix???
+ << " m12=" << wrongMatrix.m12 ()
+ << " m21=" << wrongMatrix.m21 ()
+ << " m22=" << wrongMatrix.m22 ()
+ << " dx=" << wrongMatrix.dx ()
+ << " dy=" << wrongMatrix.dy ()
+ << " rect=" << wrongMatrix.map (pm.rect ())
+ << endl
+ << "\ti_used_to_use_thisMatrix: m11=" << oldHat.m11 ()
+ << " m12=" << oldHat.m12 ()
+ << " m21=" << oldHat.m21 ()
+ << " m22=" << oldHat.m22 ()
+ << " dx=" << oldHat.dx ()
+ << " dy=" << oldHat.dy ()
+ << " rect=" << oldHat.map (pm.rect ())
+ << endl
+ << "\tabove but scaled at the same time: m11=" << altHat.m11 ()
+ << " m12=" << altHat.m12 ()
+ << " m21=" << altHat.m21 ()
+ << " m22=" << altHat.m22 ()
+ << " dx=" << altHat.dx ()
+ << " dy=" << altHat.dy ()
+ << " rect=" << altHat.map (pm.rect ())
+ << endl
+ << "\tsupposedlyCorrectMatrix: m11=" << correctMatrix.m11 ()
+ << " m12=" << correctMatrix.m12 ()
+ << " m21=" << correctMatrix.m21 ()
+ << " m22=" << correctMatrix.m22 ()
+ << " dx=" << correctMatrix.dx ()
+ << " dy=" << correctMatrix.dy ()
+ << " rect=" << correctMatrix.map (pm.rect ())
+ << endl;
+ #endif
+
+ transformMatrix = transformMatrix * scaleMatrix;
+
+ // TODO: Should we be using QWMatrix::Areas?
+ newRect = transformMatrix.map (pm.rect ());
+ #if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\tnewRect after targetWidth,targetHeight adjust=" << newRect << endl;
+ #endif
+ }
+
+
+ QPixmap newPixmap (targetWidth > 0 ? targetWidth : newRect.width (),
+ targetHeight > 0 ? targetHeight : newRect.height ());
+ if ((targetWidth > 0 && targetWidth != newRect.width ()) ||
+ (targetHeight > 0 && targetHeight != newRect.height ()))
+ {
+ #if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "kppixmapfx.cpp: xForm(pm.size=" << pm.size ()
+ << ",targetWidth=" << targetWidth
+ << ",targetHeight=" << targetHeight
+ << ") newRect=" << newRect
+ << " (you are a victim of rounding error)"
+ << endl;
+ #endif
+ }
+
+ QBitmap newBitmapMask;
+
+ if (backgroundColor.isOpaque ())
+ newPixmap.fill (backgroundColor.toQColor ());
+
+ if (backgroundColor.isTransparent () || pm.mask ())
+ {
+ newBitmapMask.resize (newPixmap.width (), newPixmap.height ());
+ newBitmapMask.fill (backgroundColor.maskColor ());
+ }
+
+ QPainter painter (&newPixmap);
+#if DEBUG_KP_PIXMAP_FX && 1
+ kdDebug () << "\tmatrix: m11=" << transformMatrix.m11 ()
+ << " m12=" << transformMatrix.m12 ()
+ << " m21=" << transformMatrix.m21 ()
+ << " m22=" << transformMatrix.m22 ()
+ << " dx=" << transformMatrix.dx ()
+ << " dy=" << transformMatrix.dy ()
+ << endl;
+ const QWMatrix trueMatrix = QPixmap::trueMatrix (transformMatrix,
+ pm.width (), pm.height ());
+ kdDebug () << "\ttrue matrix: m11=" << trueMatrix.m11 ()
+ << " m12=" << trueMatrix.m12 ()
+ << " m21=" << trueMatrix.m21 ()
+ << " m22=" << trueMatrix.m22 ()
+ << " dx=" << trueMatrix.dx ()
+ << " dy=" << trueMatrix.dy ()
+ << endl;
+#endif
+ painter.setWorldMatrix (transformMatrix);
+#if DEBUG_KP_PIXMAP_FX && 0
+ kdDebug () << "\ttranslate top=" << painter.xForm (QPoint (0, 0)) << endl;
+ kdDebug () << "\tmatrix: m11=" << painter.worldMatrix ().m11 ()
+ << " m12=" << painter.worldMatrix ().m12 ()
+ << " m21=" << painter.worldMatrix ().m21 ()
+ << " m22=" << painter.worldMatrix ().m22 ()
+ << " dx=" << painter.worldMatrix ().dx ()
+ << " dy=" << painter.worldMatrix ().dy ()
+ << endl;
+#endif
+ painter.drawPixmap (QPoint (0, 0), pm);
+ painter.end ();
+
+ if (!newBitmapMask.isNull ())
+ {
+ QPainter maskPainter (&newBitmapMask);
+ maskPainter.setWorldMatrix (transformMatrix);
+ maskPainter.drawPixmap (QPoint (0, 0), kpPixmapFX::getNonNullMask (pm));
+ maskPainter.end ();
+ newPixmap.setMask (newBitmapMask);
+ }
+
+ return newPixmap;
+}
+
+// public static
+QWMatrix kpPixmapFX::skewMatrix (int width, int height, double hangle, double vangle)
+{
+ if (fabs (hangle - 0) < kpPixmapFX::AngleInDegreesEpsilon &&
+ fabs (vangle - 0) < kpPixmapFX::AngleInDegreesEpsilon)
+ {
+ return QWMatrix ();
+ }
+
+
+ /* Diagram for completeness :)
+ *
+ * |---------- w ----------|
+ * (0,0)
+ * _ _______________________ (w,0)
+ * | |\~_ va |
+ * | | \ ~_ |
+ * | |ha\ ~__ |
+ * | \ ~__ | dy
+ * h | \ ~___ |
+ * | \ ~___ |
+ * | | \ ~___| (w,w*tan(va)=dy)
+ * | | \ * \
+ * _ |________\________|_____|\ vertical shear factor
+ * (0,h) dx ^~_ | \ |
+ * | ~_ \________\________ General Point (x,y) V
+ * | ~__ \ Skewed Point (x + y*tan(ha),y + x*tan(va))
+ * (h*tan(ha)=dx,h) ~__ \ ^
+ * ~___ \ |
+ * ~___ \ horizontal shear factor
+ * Key: ~___\
+ * ha = hangle (w + h*tan(ha)=w+dx,h + w*tan(va)=w+dy)
+ * va = vangle
+ *
+ * Skewing really just twists a rectangle into a parallelogram.
+ *
+ */
+
+ //QWMatrix matrix (1, tan (KP_DEGREES_TO_RADIANS (vangle)), tan (KP_DEGREES_TO_RADIANS (hangle)), 1, 0, 0);
+ // I think this is clearer than above :)
+ QWMatrix matrix;
+ matrix.shear (tan (KP_DEGREES_TO_RADIANS (hangle)),
+ tan (KP_DEGREES_TO_RADIANS (vangle)));
+
+ return matrixWithZeroOrigin (matrix, width, height);
+}
+
+// public static
+QWMatrix kpPixmapFX::skewMatrix (const QPixmap &pixmap, double hangle, double vangle)
+{
+ return kpPixmapFX::skewMatrix (pixmap.width (), pixmap.height (), hangle, vangle);
+}
+
+
+// public static
+void kpPixmapFX::skew (QPixmap *destPixmapPtr, double hangle, double vangle,
+ const kpColor &backgroundColor,
+ int targetWidth, int targetHeight)
+{
+ if (!destPixmapPtr)
+ return;
+
+ *destPixmapPtr = kpPixmapFX::skew (*destPixmapPtr, hangle, vangle,
+ backgroundColor,
+ targetWidth, targetHeight);
+}
+
+// public static
+QPixmap kpPixmapFX::skew (const QPixmap &pm, double hangle, double vangle,
+ const kpColor &backgroundColor,
+ int targetWidth, int targetHeight)
+{
+#if DEBUG_KP_PIXMAP_FX
+ kdDebug () << "kpPixmapFX::skew() pm.width=" << pm.width ()
+ << " pm.height=" << pm.height ()
+ << " hangle=" << hangle
+ << " vangle=" << vangle
+ << " targetWidth=" << targetWidth
+ << " targetHeight=" << targetHeight
+ << endl;
+#endif
+
+ if (fabs (hangle - 0) < kpPixmapFX::AngleInDegreesEpsilon &&
+ fabs (vangle - 0) < kpPixmapFX::AngleInDegreesEpsilon &&
+ (targetWidth <= 0 && targetHeight <= 0)/*don't want to scale?*/)
+ {
+ return pm;
+ }
+
+ if (fabs (hangle) > 90 - kpPixmapFX::AngleInDegreesEpsilon ||
+ fabs (vangle) > 90 - kpPixmapFX::AngleInDegreesEpsilon)
+ {
+ kdError () << "kpPixmapFX::skew() passed hangle and/or vangle out of range (-90 < x < 90)" << endl;
+ return pm;
+ }
+
+
+ QWMatrix matrix = skewMatrix (pm, hangle, vangle);
+
+ return ::xForm (pm, matrix, backgroundColor, targetWidth, targetHeight);
+}
+
+
+// public static
+QWMatrix kpPixmapFX::rotateMatrix (int width, int height, double angle)
+{
+ if (fabs (angle - 0) < kpPixmapFX::AngleInDegreesEpsilon)
+ {
+ return QWMatrix ();
+ }
+
+ QWMatrix matrix;
+ matrix.translate (width / 2, height / 2);
+ matrix.rotate (angle);
+
+ return matrixWithZeroOrigin (matrix, width, height);
+}
+
+// public static
+QWMatrix kpPixmapFX::rotateMatrix (const QPixmap &pixmap, double angle)
+{
+ return kpPixmapFX::rotateMatrix (pixmap.width (), pixmap.height (), angle);
+}
+
+
+// public static
+bool kpPixmapFX::isLosslessRotation (double angle)
+{
+ const double angleIn = angle;
+
+ // Reflect angle into positive if negative
+ if (angle < 0)
+ angle = -angle;
+
+ // Remove multiples of 90 to make sure 0 <= angle <= 90
+ angle -= ((int) angle) / 90 * 90;
+
+ // "Impossible" situation?
+ if (angle < 0 || angle > 90)
+ {
+ kdError () << "kpPixmapFX::isLosslessRotation(" << angleIn
+ << ") result=" << angle
+ << endl;
+ return false; // better safe than sorry
+ }
+
+ const bool ret = (angle < kpPixmapFX::AngleInDegreesEpsilon ||
+ 90 - angle < kpPixmapFX::AngleInDegreesEpsilon);
+#if DEBUG_KP_PIXMAP_FX
+ kdDebug () << "kpPixmapFX::isLosslessRotation(" << angleIn << ")"
+ << " residual angle=" << angle
+ << " returning " << ret
+ << endl;
+#endif
+ return ret;
+}
+
+
+// public static
+void kpPixmapFX::rotate (QPixmap *destPixmapPtr, double angle,
+ const kpColor &backgroundColor,
+ int targetWidth, int targetHeight)
+{
+ if (!destPixmapPtr)
+ return;
+
+ *destPixmapPtr = kpPixmapFX::rotate (*destPixmapPtr, angle,
+ backgroundColor,
+ targetWidth, targetHeight);
+}
+
+// public static
+QPixmap kpPixmapFX::rotate (const QPixmap &pm, double angle,
+ const kpColor &backgroundColor,
+ int targetWidth, int targetHeight)
+{
+ if (fabs (angle - 0) < kpPixmapFX::AngleInDegreesEpsilon &&
+ (targetWidth <= 0 && targetHeight <= 0)/*don't want to scale?*/)
+ {
+ return pm;
+ }
+
+
+ QWMatrix matrix = rotateMatrix (pm, angle);
+
+ return ::xForm (pm, matrix, backgroundColor, targetWidth, targetHeight);
+}
+
+
+// public static
+QWMatrix kpPixmapFX::flipMatrix (int width, int height, bool horz, bool vert)
+{
+ if (width <= 0 || height <= 0)
+ {
+ kdError () << "kpPixmapFX::flipMatrix() passed invalid dimensions" << endl;
+ return QWMatrix ();
+ }
+
+ return QWMatrix (horz ? -1 : +1, // m11
+ 0, // m12
+ 0, // m21
+ vert ? -1 : +1, // m22
+ horz ? (width - 1) : 0, // dx
+ vert ? (height - 1) : 0); // dy
+}
+
+// public static
+QWMatrix kpPixmapFX::flipMatrix (const QPixmap &pixmap, bool horz, bool vert)
+{
+ return kpPixmapFX::flipMatrix (pixmap.width (), pixmap.height (),
+ horz, vert);
+}
+
+
+// public static
+void kpPixmapFX::flip (QPixmap *destPixmapPtr, bool horz, bool vert)
+{
+ if (!horz && !vert)
+ return;
+
+ *destPixmapPtr = kpPixmapFX::flip (*destPixmapPtr, horz, vert);
+}
+
+// public static
+QPixmap kpPixmapFX::flip (const QPixmap &pm, bool horz, bool vert)
+{
+ if (!horz && !vert)
+ return pm;
+
+ return pm.xForm (flipMatrix (pm, horz, vert));
+}
+
+// public static
+void kpPixmapFX::flip (QImage *destImagePtr, bool horz, bool vert)
+{
+ if (!horz && !vert)
+ return;
+
+ *destImagePtr = kpPixmapFX::flip (*destImagePtr, horz, vert);
+}
+
+// public static
+QImage kpPixmapFX::flip (const QImage &img, bool horz, bool vert)
+{
+ if (!horz && !vert)
+ return img;
+
+ return img.mirror (horz, vert);
+}
diff --git a/kolourpaint/pixmapfx/kppixmapfx.h b/kolourpaint/pixmapfx/kppixmapfx.h
new file mode 100644
index 00000000..c083ee43
--- /dev/null
+++ b/kolourpaint/pixmapfx/kppixmapfx.h
@@ -0,0 +1,450 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef KP_PIXMAP_FX_H
+#define KP_PIXMAP_FX_H
+
+
+#include <qstring.h>
+
+
+class QBitmap;
+class QColor;
+class QImage;
+class QPointArray;
+class QPixmap;
+class QPoint;
+class QRect;
+class QString;
+class QWidget;
+class QWMatrix;
+
+class kpColor;
+class kpSelection;
+
+
+class kpPixmapFX
+{
+public:
+
+ //
+ // Overflow Resistant Arithmetic:
+ //
+ // Returns INT_MAX if <lhs> or <rhs> < 0 or if would overflow.
+ static int addDimensions (int lhs, int rhs);
+ static int multiplyDimensions (int lhs, int rhs);
+
+
+ //
+ // QPixmap Statistics
+ //
+
+ // Returns the width * height.
+ static int pixmapArea (const QPixmap &pixmap);
+ static int pixmapArea (const QPixmap *pixmap);
+ static int pixmapArea (int width, int height);
+
+ // Returns the estimated size of <pixmap> in pixmap memory.
+ static int pixmapSize (const QPixmap &pixmap);
+ static int pixmapSize (const QPixmap *pixmap);
+ static int pixmapSize (int width, int height, int depth);
+
+ static int imageSize (const QImage &image);
+ static int imageSize (const QImage *image);
+ static int imageSize (int width, int height, int depth);
+
+ static int selectionSize (const kpSelection &sel);
+ static int selectionSize (const kpSelection *sel);
+
+ static int stringSize (const QString &string);
+
+ static int pointArraySize (const QPointArray &points);
+
+
+ //
+ // QPixmap/QImage Conversion Functions
+ //
+
+ //
+ // Converts <pixmap> to a QImage and returns it.
+ //
+ // WARNING: On an 8-bit screen:
+ //
+ // QPixmap result = convertToPixmap (convertToImage (pixmap));
+ //
+ // <result> is slightly differently colored to <pixmap>.
+ //
+ // KolourPaint needs to convert to QImage occasionally as
+ // QImage allows KolourPaint to read pixels and because the QImage
+ // methods give reliable results and pixel-identical results on
+ // all platforms. The QPixmap paint engine has no such guarantee
+ // and even depends on the quality of the video driver.
+ //
+ // As a result, KolourPaint should not be used on an 8-bit screen.
+ // HITODO: Add warning on startup, like in KolourPaint/KDE4.
+ //
+ // This bug will be fixed when KolourPaint gets a proper image library,
+ // where QPixmap -> QImage -> QPixmap transitions will be not be needed.
+ static QImage convertToImage (const QPixmap &pixmap);
+
+ //
+ // Dialog info for warning about data loss with convertToPixmap().
+ //
+ struct WarnAboutLossInfo
+ {
+ // <moreColorsThanDisplayAndHasAlphaChannelMessage>:
+ //
+ // i18n ("The (image \"example.jpg\"|image from the clipboard)"
+ // " may have more colors than the current screen mode."
+ // " In order to display it, some colors may be changed."
+ // " Try increasing your screen depth to at least %1bpp."
+ //
+ // "\nIt also"
+ //
+ // " contains translucency which is not fully"
+ // " supported. The translucency data will be"
+ // " approximated with a 1-bit transparency mask.")
+ //
+ // <moreColorsThanDisplayMessage>:
+ // i18n ("The (image \"example.jpg\"|image from the clipboard)"
+ // " may have more colors than the current screen mode."
+ // " In order to display it, some colors may be changed."
+ // " Try increasing your screen depth to at least %1bpp.")
+ //
+ // <hasAlphaChannelMessage>:
+ // i18n ("The (image \"example.jpg\"|image from the clipboard)"
+ // " contains translucency which is not fully"
+ // " supported. The translucency data will be"
+ // " approximated with a 1-bit transparency mask.")
+ //
+ // <dontAskAgainPrefix>:
+ //
+ // Don'tAskAgain ID for dialog.
+ //
+ // <parent>:
+ //
+ // Dialog parent
+ //
+ WarnAboutLossInfo (const QString &moreColorsThanDisplayAndHasAlphaChannelMessage,
+ const QString &moreColorsThanDisplayMessage,
+ const QString &hasAlphaChannelMessage,
+ const QString &dontAskAgainPrefix,
+ QWidget *parent)
+ :
+ m_moreColorsThanDisplayAndHasAlphaChannelMessage (
+ moreColorsThanDisplayAndHasAlphaChannelMessage),
+ m_moreColorsThanDisplayMessage (
+ moreColorsThanDisplayMessage),
+ m_hasAlphaChannelMessage (
+ hasAlphaChannelMessage),
+ m_dontAskAgainPrefix (
+ dontAskAgainPrefix),
+ m_parent (parent),
+ m_isValid (true)
+ {
+ }
+
+ WarnAboutLossInfo ()
+ : m_parent (0),
+ m_isValid (false)
+ {
+ }
+
+ ~WarnAboutLossInfo ()
+ {
+ }
+
+
+ bool isValid () const { return m_isValid; }
+
+
+ QString m_moreColorsThanDisplayAndHasAlphaChannelMessage,
+ m_moreColorsThanDisplayMessage,
+ m_hasAlphaChannelMessage;
+ QString m_dontAskAgainPrefix;
+ QWidget *m_parent;
+ bool m_isValid;
+ };
+
+ //
+ // Converts <image> to a QPixmap of the current display's depth and
+ // returns it.
+ //
+ // If the flag <pretty> is set, it will dither the image making the
+ // returned pixmap look better but if the image has few colours
+ // (less than the screen can handle), this will be at the expense of
+ // exactness of conversion.
+ //
+ // This will automatically call ensureNoAlphaChannel().
+ //
+ // Never use a foreign QPixmap that is offered to you - always get the
+ // foreign QImage and use this function to convert it to a sane QPixmap.
+ //
+ // <wali>, if specified, describes parameters for the dialog that comes
+ // up warning the user of data loss if the <image> contains translucency
+ // and/or more colors than the current display.
+ //
+ static QPixmap convertToPixmap (const QImage &image, bool pretty = false,
+ const WarnAboutLossInfo &wali = WarnAboutLossInfo ());
+
+ // Same as convertToPixmap() but tries as hard as possible to make the
+ // pixmap look like the original <image> - when in doubt, reads the
+ // config to see whether or not to dither (default: on).
+ //
+ // If you know for sure that <image> can be displayed losslessly on
+ // the screen, you should call convertToPixmap() with <pretty> = false
+ // instead. If you know for sure that <image> cannot be displayed
+ // losslessly, then call convertToPixmap() with <pretty> = true.
+ //
+ static QPixmap convertToPixmapAsLosslessAsPossible (const QImage &image,
+ const WarnAboutLossInfo &wali = WarnAboutLossInfo ());
+
+
+ // Sets the RGB values of the pixels where <pixmap> is transparent to
+ // <transparentColor>. This has visually no effect on the <pixmap>
+ // unless the mask is lost.
+ static QPixmap pixmapWithDefinedTransparentPixels (const QPixmap &pixmap,
+ const QColor &transparentColor);
+
+
+ //
+ // Get/Set Parts of Pixmap
+ //
+
+
+ //
+ // Returns the pixel and mask data found at the <rect> in <pm>.
+ //
+ static QPixmap getPixmapAt (const QPixmap &pm, const QRect &rect);
+
+ //
+ // Sets the pixel and mask data at <destRect> in <*destPixmapPtr>
+ // to <srcPixmap>.
+ //
+ static void setPixmapAt (QPixmap *destPixmapPtr, const QRect &destRect,
+ const QPixmap &srcPixmap);
+
+ //
+ // Sets the pixel and mask data at the rectangle in <*destPixmapPtr>,
+ // with the top-left <destAt> and dimensions <srcPixmap.rect()>,
+ // to <srcPixmap>.
+ //
+ static void setPixmapAt (QPixmap *destPixmapPtr, const QPoint &destAt,
+ const QPixmap &srcPixmap);
+ static void setPixmapAt (QPixmap *destPixmapPtr, int destX, int destY,
+ const QPixmap &srcPixmap);
+
+ //
+ // Draws <srcPixmap> on top of <*destPixmapPtr> at <destAt>.
+ // The mask of <*destPixmapPtr> is adjusted so that all opaque
+ // pixels in <srcPixmap> will be opaque in <*destPixmapPtr>.
+ //
+ static void paintPixmapAt (QPixmap *destPixmapPtr, const QPoint &destAt,
+ const QPixmap &srcPixmap);
+ static void paintPixmapAt (QPixmap *destPixmapPtr, int destX, int destY,
+ const QPixmap &srcPixmap);
+
+ //
+ // Returns the colour of the pixel at <at> in <pm>.
+ // If the pixel is transparent, a value is returned such that
+ // kpTool::isColorTransparent(<return_value>) will return true.
+ //
+ static kpColor getColorAtPixel (const QPixmap &pm, const QPoint &at);
+ static kpColor getColorAtPixel (const QPixmap &pm, int x, int y);
+
+ //
+ // Returns the color of the pixel at <at> in <img>.
+ // If the pixel is transparent, a value is returned such that
+ // kpTool::isColorTransparent(<return_value>) will return true.
+ //
+ static kpColor getColorAtPixel (const QImage &img, const QPoint &at);
+ static kpColor getColorAtPixel (const QImage &img, int x, int y);
+
+
+ //
+ // Mask Operations
+ //
+
+
+ //
+ // Removes <*destPixmapPtr>'s Alpha Channel and attempts to convert it
+ // to a mask. KolourPaint - and QPixmap to a great extent - does not
+ // support Alpha Channels - only masks. Call this whenever you get
+ // a pixmap from a foreign source; else all KolourPaint code will
+ // exhibit "undefined behaviour".
+ //
+ static void ensureNoAlphaChannel (QPixmap *destPixmapPtr);
+
+ //
+ // Returns <pm>'s mask or a fully opaque mask (with <pm>'s dimensions)
+ // if <pm> does not have a mask.
+ //
+ static QBitmap getNonNullMask (const QPixmap &pm);
+
+ //
+ // Ensures that <*destPixmapPtr> is transparent at <rect>.
+ //
+ static void ensureTransparentAt (QPixmap *destPixmapPtr, const QRect &destRect);
+
+ //
+ // Sets the mask of <*destPixmapPtr> at the rectangle, with the
+ // top-left <destAt> and dimensions <srcMaskBitmap.rect()>,
+ // to transparent where <brushBitmap> is opaque.
+ //
+ // <brushPixmap> must be a QPixmap of depth 1 (or a QBitmap).
+ //
+ static void paintMaskTransparentWithBrush (QPixmap *destPixmapPtr, const QPoint &destAt,
+ const QPixmap &brushBitmap);
+ static void paintMaskTransparentWithBrush (QPixmap *destPixmapPtr, int destX, int destY,
+ const QPixmap &brushBitmap);
+
+ //
+ // Ensures that <*destPixmapPtr> is opaque at <rect>.
+ //
+ static void ensureOpaqueAt (QPixmap *destPixmapPtr, const QRect &destRect);
+
+ //
+ // Ensures that <srcPixmap>'s opaque pixels will be opaque if
+ // painted onto <*destPixmapPtr> at <destAt>.
+ //
+ static void ensureOpaqueAt (QPixmap *destPixmapPtr, const QPoint &destAt,
+ const QPixmap &srcPixmap);
+ static void ensureOpaqueAt (QPixmap *destPixmapPtr, int destX, int destY,
+ const QPixmap &srcPixmap);
+
+
+ //
+ // Effects
+ //
+
+
+ //
+ // Converts the image to grayscale.
+ //
+ static void convertToGrayscale (QPixmap *destPixmapPtr);
+ static QPixmap convertToGrayscale (const QPixmap &pm);
+ static void convertToGrayscale (QImage *destImagePtr);
+ static QImage convertToGrayscale (const QImage &img);
+
+ //
+ // Fills an image in the given color.
+ //
+ static void fill (QPixmap *destPixmapPtr, const kpColor &color);
+ static QPixmap fill (const QPixmap &pm, const kpColor &color);
+
+ //
+ // Resizes an image to the given width and height,
+ // filling any new areas with <backgroundColor> if <fillNewAreas> is set.
+ //
+ static void resize (QPixmap *destPixmapPtr, int w, int h,
+ const kpColor &backgroundColor, bool fillNewAreas = true);
+ static QPixmap resize (const QPixmap &pm, int w, int h,
+ const kpColor &backgroundColor, bool fillNewAreas = true);
+
+ //
+ // Scales an image to the given width and height.
+ // If <pretty> is true, a smooth scale will be used.
+ //
+ static void scale (QPixmap *destPixmapPtr, int w, int h, bool pretty = false);
+ static QPixmap scale (const QPixmap &pm, int w, int h, bool pretty = false);
+
+
+ // The minimum difference between 2 angles (in degrees) such that they are
+ // considered different. This gives you at least enough precision to
+ // rotate an image whose width <= 10000 such that its height increases
+ // by just 1 (and similarly with height <= 10000 and width).
+ //
+ // Currently used for skew & rotate operations.
+ static double AngleInDegreesEpsilon;
+
+
+ //
+ // Skews an image.
+ //
+ // <hangle> horizontal angle clockwise (-90 < x < 90)
+ // <vangle> vertical angle clockwise (-90 < x < 90)
+ // <backgroundColor> color to fill new areas with
+ // <targetWidth> if > 0, the desired width of the resultant pixmap
+ // <targetHeight> if > 0, the desired height of the resultant pixmap
+ //
+ // Using <targetWidth> & <targetHeight> to generate preview pixmaps is
+ // significantly more efficient than skewing and then scaling yourself.
+ //
+ static QWMatrix skewMatrix (int width, int height, double hangle, double vangle);
+ static QWMatrix skewMatrix (const QPixmap &pixmap, double hangle, double vangle);
+
+ static void skew (QPixmap *destPixmapPtr, double hangle, double vangle,
+ const kpColor &backgroundColor,
+ int targetWidth = -1, int targetHeight = -1);
+ static QPixmap skew (const QPixmap &pm, double hangle, double vangle,
+ const kpColor &backgroundColor,
+ int targetWidth = -1, int targetHeight = -1);
+
+ //
+ // Rotates an image.
+ //
+ // <angle> clockwise angle to rotate by
+ // <backgroundColor> color to fill new areas with
+ // <targetWidth> if > 0, the desired width of the resultant pixmap
+ // <targetHeight> if > 0, the desired height of the resultant pixmap
+ //
+ // Using <targetWidth> & <targetHeight> to generate preview pixmaps is
+ // significantly more efficient than rotating and then scaling yourself.
+ //
+ static QWMatrix rotateMatrix (int width, int height, double angle);
+ static QWMatrix rotateMatrix (const QPixmap &pixmap, double angle);
+
+ static bool isLosslessRotation (double angle);
+
+ static void rotate (QPixmap *destPixmapPtr, double angle,
+ const kpColor &backgroundColor,
+ int targetWidth = -1, int targetHeight = -1);
+ static QPixmap rotate (const QPixmap &pm, double angle,
+ const kpColor &backgroundColor,
+ int targetWidth = -1, int targetHeight = -1);
+
+
+ //
+ // Flips an image in the given directions.
+ //
+ static QWMatrix flipMatrix (int width, int height, bool horz, bool vert);
+ static QWMatrix flipMatrix (const QPixmap &pixmap, bool horz, bool vert);
+
+ // TODO: this kind of overloading is error prone
+ // e.g. QPixmap pixmap;
+ // kpPixmapFX::flip (pixmap, false, true);
+ // looks like it will flip vertically but does absolutely nothing!
+ // (should be &pixmap)
+ static void flip (QPixmap *destPixmapPtr, bool horz, bool vert);
+ static QPixmap flip (const QPixmap &pm, bool horz, bool vert);
+ static void flip (QImage *destImagePtr, bool horz, bool vert);
+ static QImage flip (const QImage &img, bool horz, bool vert);
+};
+
+
+#endif // KP_PIXMAP_FX_H
diff --git a/kolourpaint/tests/45deg_line.png b/kolourpaint/tests/45deg_line.png
new file mode 100644
index 00000000..5af95109
--- /dev/null
+++ b/kolourpaint/tests/45deg_line.png
Binary files differ
diff --git a/kolourpaint/tests/4x4-transparent.png b/kolourpaint/tests/4x4-transparent.png
new file mode 100644
index 00000000..58b0668e
--- /dev/null
+++ b/kolourpaint/tests/4x4-transparent.png
Binary files differ
diff --git a/kolourpaint/tests/5x5.png b/kolourpaint/tests/5x5.png
new file mode 100644
index 00000000..850766c7
--- /dev/null
+++ b/kolourpaint/tests/5x5.png
Binary files differ
diff --git a/kolourpaint/tests/depth1.bmp b/kolourpaint/tests/depth1.bmp
new file mode 100644
index 00000000..326c665a
--- /dev/null
+++ b/kolourpaint/tests/depth1.bmp
Binary files differ
diff --git a/kolourpaint/tests/dither.png b/kolourpaint/tests/dither.png
new file mode 100644
index 00000000..443ed07c
--- /dev/null
+++ b/kolourpaint/tests/dither.png
Binary files differ
diff --git a/kolourpaint/tests/rotate.png b/kolourpaint/tests/rotate.png
new file mode 100644
index 00000000..f6f028b4
--- /dev/null
+++ b/kolourpaint/tests/rotate.png
Binary files differ
diff --git a/kolourpaint/tests/small16x16.png b/kolourpaint/tests/small16x16.png
new file mode 100644
index 00000000..ed61d4d6
--- /dev/null
+++ b/kolourpaint/tests/small16x16.png
Binary files differ
diff --git a/kolourpaint/tests/tool_fill_xlimit.png b/kolourpaint/tests/tool_fill_xlimit.png
new file mode 100644
index 00000000..b499879a
--- /dev/null
+++ b/kolourpaint/tests/tool_fill_xlimit.png
Binary files differ
diff --git a/kolourpaint/tests/transparent.png b/kolourpaint/tests/transparent.png
new file mode 100644
index 00000000..c05a92ef
--- /dev/null
+++ b/kolourpaint/tests/transparent.png
Binary files differ
diff --git a/kolourpaint/tests/transparent_selection.png b/kolourpaint/tests/transparent_selection.png
new file mode 100644
index 00000000..8db8b7e5
--- /dev/null
+++ b/kolourpaint/tests/transparent_selection.png
Binary files differ
diff --git a/kolourpaint/tools/Makefile.am b/kolourpaint/tools/Makefile.am
new file mode 100644
index 00000000..9c665cb1
--- /dev/null
+++ b/kolourpaint/tools/Makefile.am
@@ -0,0 +1,53 @@
+INCLUDES = -I$(srcdir)/.. -I$(srcdir)/../cursors -I$(srcdir)/../interfaces \
+ -I$(srcdir)/../pixmapfx \
+ -I$(srcdir)/../tools \
+ -I$(srcdir)/../views \
+ -I$(srcdir)/../widgets $(all_includes)
+
+noinst_LTLIBRARIES = libkolourpainttools.la
+libkolourpainttools_la_SOURCES = kptoolaction.cpp \
+ kptoolairspray.cpp \
+ kptoolautocrop.cpp \
+ kptoolbrush.cpp kptoolclear.cpp \
+ kptoolcolorpicker.cpp kptoolcolorwasher.cpp \
+ kptoolconverttograyscale.cpp \
+ kptoolcrop.cpp \
+ kptoolcurve.cpp \
+ kptoolellipse.cpp \
+ kptoolellipticalselection.cpp kptooleraser.cpp \
+ kptoolflip.cpp kptoolfloodfill.cpp \
+ kptoolfreeformselection.cpp \
+ kptoolline.cpp kptoolpen.cpp \
+ kptoolpolygon.cpp kptoolpolyline.cpp \
+ kptoolpreviewdialog.cpp \
+ kptoolrectangle.cpp kptoolrectselection.cpp \
+ kptoolresizescale.cpp kptoolrotate.cpp \
+ kptoolroundedrectangle.cpp kptoolselection.cpp \
+ kptoolskew.cpp kptooltext.cpp
+
+# TODO: Why is this needed? Isn't linking at the toplevel enough?
+libkolourpainttools_la_LIBADD = ../pixmapfx/libkolourpaintpixmapfx.la ../cursors/libkolourpaintcursors.la
+
+METASOURCES = kptoolaction.moc \
+ kptoolairspray.moc \
+ kptoolbrush.moc \
+ kptoolcolorpicker.moc \
+ kptoolcolorwasher.moc \
+ kptoolcurve.moc \
+ kptoolellipse.moc \
+ kptooleraser.moc \
+ kptoolflip.moc \
+ kptoolfloodfill.moc \
+ kptoolline.moc \
+ kptoolpen.moc \
+ kptoolpolygon.moc \
+ kptoolpolyline.moc \
+ kptoolpreviewdialog.moc \
+ kptoolrectangle.moc \
+ kptoolresizescale.moc \
+ kptoolrotate.moc \
+ kptoolroundedrectangle.moc \
+ kptoolselection.moc \
+ kptoolskew.moc \
+ kptooltext.moc
+
diff --git a/kolourpaint/tools/kptoolaction.cpp b/kolourpaint/tools/kptoolaction.cpp
new file mode 100644
index 00000000..ef5c8510
--- /dev/null
+++ b/kolourpaint/tools/kptoolaction.cpp
@@ -0,0 +1,107 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <kptoolaction.h>
+
+#include <kptool.h>
+
+
+kpToolAction::kpToolAction (const QString &text,
+ const QString &pic, const KShortcut &shortcut,
+ const QObject *receiver, const char *slot,
+ QObject *parent, const char *name)
+ : KToggleAction (text,
+ pic, shortcut,
+ receiver, slot,
+ parent, name)
+{
+ updateToolTip ();
+}
+
+kpToolAction::~kpToolAction ()
+{
+}
+
+
+// protected
+void kpToolAction::updateToolTip ()
+{
+ const QString newToolTip =
+ kpTool::toolTipForTextAndShortcut (text (), shortcut ());
+ if (newToolTip == toolTip ())
+ return;
+
+ setToolTip (newToolTip);
+ emit toolTipChanged (newToolTip);
+}
+
+
+//
+// KToggleAction interface
+//
+
+// public slot virtual [base KAction]
+void kpToolAction::setText (const QString &text)
+{
+ KToggleAction::setText (text);
+ updateToolTip ();
+}
+
+// public slot virtual [base KAction]
+bool kpToolAction::setShortcut (const KShortcut &shortcut)
+{
+ bool ret = KToggleAction::setShortcut (shortcut);
+ updateToolTip ();
+ return ret;
+}
+
+
+//
+// KToggleAction implements kpSingleKeyTriggersActionInterface
+//
+
+// public virtual [base kpSingleKeyTriggersActionInterface]
+const char *kpToolAction::actionName () const
+{
+ return name ();
+}
+
+// public virtual [base kpSingleKeyTriggersActionInterface]
+KShortcut kpToolAction::actionShortcut () const
+{
+ return shortcut ();
+}
+
+// public virtual [base kpSingleKeyTriggersActionInterface]
+void kpToolAction::actionSetShortcut (const KShortcut &shortcut)
+{
+ setShortcut (shortcut);
+}
+
+
+#include <kptoolaction.moc>
diff --git a/kolourpaint/tools/kptoolaction.h b/kolourpaint/tools/kptoolaction.h
new file mode 100644
index 00000000..df4e407e
--- /dev/null
+++ b/kolourpaint/tools/kptoolaction.h
@@ -0,0 +1,78 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef KP_TOOL_ACTION_H
+#define KP_TOOL_ACTION_H
+
+#include <kactionclasses.h>
+
+#include <kpsinglekeytriggersaction.h>
+
+
+// Same as KToggleAction but shows the first single key trigger in the tooltip.
+class kpToolAction : public KToggleAction,
+ public kpSingleKeyTriggersActionInterface
+{
+Q_OBJECT
+
+public:
+ kpToolAction (const QString &text,
+ const QString &pic, const KShortcut &shortcut,
+ const QObject *receiver, const char *slot,
+ QObject *parent, const char *name);
+ virtual ~kpToolAction ();
+
+
+signals:
+ // Not emitted when toolTip is manually overriden by setToolTip()
+ void toolTipChanged (const QString &string);
+
+protected:
+ void updateToolTip ();
+
+
+ //
+ // KToggleAction interface
+ //
+
+public slots:
+ virtual void setText (const QString &text);
+ virtual bool setShortcut (const KShortcut &shortcut);
+
+
+ //
+ // kpSingleKeyTriggersActionInterface
+ //
+
+public:
+ virtual const char *actionName () const;
+ virtual KShortcut actionShortcut () const;
+ virtual void actionSetShortcut (const KShortcut &shortcut);
+};
+
+
+#endif // KP_TOOL_ACTION_H
diff --git a/kolourpaint/tools/kptoolairspray.cpp b/kolourpaint/tools/kptoolairspray.cpp
new file mode 100644
index 00000000..43f8bef3
--- /dev/null
+++ b/kolourpaint/tools/kptoolairspray.cpp
@@ -0,0 +1,376 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define DEBUG_KP_TOOL_SPRAYCAN 0
+
+#include <stdlib.h>
+
+#include <qbitmap.h>
+#include <qpainter.h>
+#include <qpen.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qpointarray.h>
+#include <qrect.h>
+#include <qtimer.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kpcommandhistory.h>
+#include <kpdefs.h>
+#include <kpdocument.h>
+#include <kpmainwindow.h>
+#include <kppixmapfx.h>
+#include <kptoolairspray.h>
+#include <kptooltoolbar.h>
+#include <kptoolwidgetspraycansize.h>
+#include <kpview.h>
+#include <kpviewmanager.h>
+
+
+/*
+ * kpToolAirSpray
+ */
+
+kpToolAirSpray::kpToolAirSpray (kpMainWindow *mainWindow)
+ : kpTool (i18n ("Spraycan"), i18n ("Sprays graffiti"),
+ Qt::Key_Y,
+ mainWindow, "tool_spraycan"),
+ m_currentCommand (0)
+{
+ m_timer = new QTimer (this);
+ connect (m_timer, SIGNAL (timeout ()), this, SLOT (actuallyDraw ()));
+}
+
+kpToolAirSpray::~kpToolAirSpray ()
+{
+ delete m_currentCommand;
+}
+
+
+// private
+QString kpToolAirSpray::haventBegunDrawUserMessage () const
+{
+ return i18n ("Click or drag to spray graffiti.");
+}
+
+// public virtual
+void kpToolAirSpray::begin ()
+{
+ kpToolToolBar *tb = toolToolBar ();
+
+ m_toolWidgetSpraycanSize = 0;
+ m_size = 10;
+
+ if (tb)
+ {
+ m_toolWidgetSpraycanSize = tb->toolWidgetSpraycanSize ();
+
+ if (m_toolWidgetSpraycanSize)
+ {
+ m_size = m_toolWidgetSpraycanSize->spraycanSize ();
+ connect (m_toolWidgetSpraycanSize, SIGNAL (spraycanSizeChanged (int)),
+ this, SLOT (slotSpraycanSizeChanged (int)));
+
+ m_toolWidgetSpraycanSize->show ();
+ }
+ }
+
+ setUserMessage (haventBegunDrawUserMessage ());
+}
+
+// public virtual
+void kpToolAirSpray::end ()
+{
+ if (m_toolWidgetSpraycanSize)
+ {
+ disconnect (m_toolWidgetSpraycanSize, SIGNAL (spraycanSizeChanged (int)),
+ this, SLOT (slotSpraycanSizeChanged (int)));
+ m_toolWidgetSpraycanSize = 0;
+ }
+
+ setUserMessage (haventBegunDrawUserMessage ());
+}
+
+// private slot
+void kpToolAirSpray::slotSpraycanSizeChanged (int size)
+{
+ m_size = size;
+}
+
+
+void kpToolAirSpray::beginDraw ()
+{
+ m_currentCommand = new kpToolAirSprayCommand (
+ color (m_mouseButton),
+ m_size,
+ mainWindow ());
+
+ // without delay
+ actuallyDraw ();
+
+ // use a timer instead of reimplementing draw() (we don't draw all the time)
+ m_timer->start (25);
+
+ setUserMessage (cancelUserMessage ());
+}
+
+void kpToolAirSpray::draw (const QPoint &thisPoint, const QPoint &, const QRect &)
+{
+ // if the user is moving the spray, make the spray line continuous
+ if (thisPoint != m_lastPoint)
+ {
+ // without delay
+ actuallyDraw ();
+ }
+
+ setUserShapePoints (thisPoint);
+}
+
+void kpToolAirSpray::actuallyDraw ()
+{
+ QPointArray pArray (10);
+ int numPoints = 0;
+
+ QPoint p = m_currentPoint;
+
+#if DEBUG_KP_TOOL_SPRAYCAN
+ kdDebug () << "kpToolAirSpray::actuallyDraw() currentPoint=" << p
+ << " size=" << m_size
+ << endl;
+#endif
+
+ int radius = m_size / 2;
+
+ for (int i = 0; i < 10; i++)
+ {
+ int dx, dy;
+
+ dx = (rand () % m_size) - radius;
+ dy = (rand () % m_size) - radius;
+
+ // make it look circular
+ // OPT: can be done better
+ if (dx * dx + dy * dy <= radius * radius)
+ pArray [numPoints++] = QPoint (p.x () + dx, p.y () + dy);
+ }
+
+ pArray.resize (numPoints);
+
+ if (numPoints > 0)
+ {
+ // leave the command to draw
+ m_currentCommand->addPoints (pArray);
+ }
+}
+
+// virtual
+void kpToolAirSpray::cancelShape ()
+{
+#if 0
+ endDraw (QPoint (), QRect ());
+ mainWindow ()->commandHistory ()->undo ();
+#else
+ m_timer->stop ();
+
+ m_currentCommand->finalize ();
+ m_currentCommand->cancel ();
+
+ delete m_currentCommand;
+ m_currentCommand = 0;
+#endif
+
+ setUserMessage (i18n ("Let go of all the mouse buttons."));
+}
+
+void kpToolAirSpray::releasedAllButtons ()
+{
+ setUserMessage (haventBegunDrawUserMessage ());
+}
+
+// virtual
+void kpToolAirSpray::endDraw (const QPoint &, const QRect &)
+{
+ m_timer->stop ();
+
+ m_currentCommand->finalize ();
+ mainWindow ()->commandHistory ()->addCommand (m_currentCommand, false /* don't exec */);
+
+ // don't delete - it's up to the commandHistory
+ m_currentCommand = 0;
+
+ setUserMessage (haventBegunDrawUserMessage ());
+}
+
+
+/*
+ * kpToolAirSprayCommand
+ */
+
+kpToolAirSprayCommand::kpToolAirSprayCommand (const kpColor &color, int size,
+ kpMainWindow *mainWindow)
+ : kpCommand (mainWindow),
+ m_color (color),
+ m_size (size),
+ m_newPixmapPtr (0)
+{
+ m_oldPixmap = *document ()->pixmap ();
+}
+
+kpToolAirSprayCommand::~kpToolAirSprayCommand ()
+{
+ delete m_newPixmapPtr;
+}
+
+
+// public virtual [base kpCommand]
+QString kpToolAirSprayCommand::name () const
+{
+ return i18n ("Spraycan");
+}
+
+
+// public virtual [base kpCommand]
+int kpToolAirSprayCommand::size () const
+{
+ return kpPixmapFX::pixmapSize (m_newPixmapPtr) +
+ kpPixmapFX::pixmapSize (m_oldPixmap);
+}
+
+
+// Redo:
+//
+// must not call before unexecute() as m_newPixmapPtr is null
+// (one reason why we told addCommand() not to execute,
+// the other being that the dots have already been draw onto the doc)
+void kpToolAirSprayCommand::execute ()
+{
+ if (m_newPixmapPtr)
+ {
+ document ()->setPixmapAt (*m_newPixmapPtr, m_boundingRect.topLeft ());
+
+ // (will be regenerated in unexecute() if required)
+ delete m_newPixmapPtr;
+ m_newPixmapPtr = 0;
+ }
+ else
+ kdError () << "kpToolAirSprayCommand::execute() has null m_newPixmapPtr" << endl;
+}
+
+// Undo:
+void kpToolAirSprayCommand::unexecute ()
+{
+ if (!m_newPixmapPtr)
+ {
+ // the ultimate in laziness - figure out Redo info only if we Undo
+ m_newPixmapPtr = new QPixmap (m_boundingRect.width (), m_boundingRect.height ());
+ *m_newPixmapPtr = document ()->getPixmapAt (m_boundingRect);
+ }
+ else
+ kdError () << "kpToolAirSprayCommand::unexecute() has non-null newPixmapPtr" << endl;
+
+ document ()->setPixmapAt (m_oldPixmap, m_boundingRect.topLeft ());
+}
+
+
+// public
+void kpToolAirSprayCommand::addPoints (const QPointArray &points)
+{
+ QRect docRect = points.boundingRect ();
+
+#if DEBUG_KP_TOOL_SPRAYCAN
+ kdDebug () << "kpToolAirSprayCommand::addPoints() docRect=" << docRect
+ << " numPoints=" << points.count () << endl;
+ for (int i = 0; i < (int) points.count (); i++)
+ kdDebug () << "\t" << i << ": " << points [i] << endl;
+#endif
+
+ QPixmap pixmap = document ()->getPixmapAt (docRect);
+ QBitmap mask;
+
+ QPainter painter, maskPainter;
+
+ if (m_color.isOpaque ())
+ {
+ painter.begin (&pixmap);
+ painter.setPen (m_color.toQColor ());
+ }
+
+ if (pixmap.mask () || m_color.isTransparent ())
+ {
+ mask = kpPixmapFX::getNonNullMask (pixmap);
+ maskPainter.begin (&mask);
+ maskPainter.setPen (m_color.maskColor ());
+ }
+
+ for (int i = 0; i < (int) points.count (); i++)
+ {
+ QPoint pt (points [i].x () - docRect.x (),
+ points [i].y () - docRect.y ());
+
+ if (painter.isActive ())
+ painter.drawPoint (pt);
+
+ if (maskPainter.isActive ())
+ maskPainter.drawPoint (pt);
+ }
+
+ if (maskPainter.isActive ())
+ maskPainter.end ();
+
+ if (painter.isActive ())
+ painter.end ();
+
+ if (!mask.isNull ())
+ pixmap.setMask (mask);
+
+ viewManager ()->setFastUpdates ();
+ document ()->setPixmapAt (pixmap, docRect.topLeft ());
+ viewManager ()->restoreFastUpdates ();
+
+ m_boundingRect = m_boundingRect.unite (docRect);
+}
+
+void kpToolAirSprayCommand::finalize ()
+{
+ // store only needed part of doc pixmap
+ m_oldPixmap = kpTool::neededPixmap (m_oldPixmap, m_boundingRect);
+}
+
+void kpToolAirSprayCommand::cancel ()
+{
+ if (m_boundingRect.isValid ())
+ {
+ viewManager ()->setFastUpdates ();
+ document ()->setPixmapAt (m_oldPixmap, m_boundingRect.topLeft ());
+ viewManager ()->restoreFastUpdates ();
+ }
+}
+
+#include <kptoolairspray.moc>
diff --git a/kolourpaint/tools/kptoolairspray.h b/kolourpaint/tools/kptoolairspray.h
new file mode 100644
index 00000000..24f02787
--- /dev/null
+++ b/kolourpaint/tools/kptoolairspray.h
@@ -0,0 +1,110 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kptoolairspray_h__
+#define __kptoolairspray_h__
+
+#include <kpcommandhistory.h>
+#include <kpcolor.h>
+#include <kptool.h>
+
+class QPixmap;
+class QPoint;
+class QRect;
+class QString;
+class QTimer;
+
+class kpMainWindow;
+class kpToolAirSprayCommand;
+class kpToolWidgetSpraycanSize;
+class kpViewManager;
+
+class kpToolAirSpray : public kpTool
+{
+Q_OBJECT
+
+public:
+ kpToolAirSpray (kpMainWindow *);
+ virtual ~kpToolAirSpray ();
+
+private:
+ QString haventBegunDrawUserMessage () const;
+
+public:
+ virtual void begin ();
+ virtual void end ();
+
+private slots:
+ void slotSpraycanSizeChanged (int size);
+
+public:
+ virtual void beginDraw ();
+ virtual void draw (const QPoint &thisPoint, const QPoint &, const QRect &);
+ virtual void cancelShape ();
+ virtual void releasedAllButtons ();
+ virtual void endDraw (const QPoint &, const QRect &);
+
+public slots:
+ void actuallyDraw ();
+
+private:
+ kpToolWidgetSpraycanSize *m_toolWidgetSpraycanSize;
+ kpToolAirSprayCommand *m_currentCommand;
+ QTimer *m_timer;
+ int m_size;
+};
+
+class kpToolAirSprayCommand : public kpCommand
+{
+public:
+ kpToolAirSprayCommand (const kpColor &color, int size,
+ kpMainWindow *mainWindow);
+ virtual ~kpToolAirSprayCommand ();
+
+ virtual QString name () const;
+
+ virtual int size () const;
+
+ virtual void execute ();
+ virtual void unexecute ();
+
+ // interface for KToolAirSpray
+ void addPoints (const QPointArray &points);
+ void finalize ();
+ void cancel ();
+
+private:
+ kpColor m_color;
+ int m_size;
+
+ QPixmap *m_newPixmapPtr;
+ QPixmap m_oldPixmap;
+ QRect m_boundingRect;
+};
+
+#endif // __kptoolairspray_h__
diff --git a/kolourpaint/tools/kptoolautocrop.cpp b/kolourpaint/tools/kptoolautocrop.cpp
new file mode 100644
index 00000000..244c192d
--- /dev/null
+++ b/kolourpaint/tools/kptoolautocrop.cpp
@@ -0,0 +1,780 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+// TODO: Color Similarity is obviously useful in Autocrop but it isn't
+// obvious as to how to implement it. The current heuristic,
+// for each side, chooses an arbitrary reference color for which
+// all other candidate pixels in that side are tested against
+// for similarity. But if the reference color happens to be at
+// one extreme of the range of colors in that side, then pixels
+// at the other extreme would not be deemed similar enough. The
+// key is to find the median color as the reference but how do
+// you do this if you don't know which pixels to sample in the first
+// place (that's what you're trying to find)? Chicken and egg situation.
+//
+// The other heuristic that is in doubt is the use of the average
+// color in determining the similarity of sides (it is possible
+// to get vastly differently colors in both sides yet they will be
+// considered similar).
+
+#define DEBUG_KP_TOOL_AUTO_CROP 0
+
+
+#include <kptoolautocrop.h>
+
+#include <qapplication.h>
+#include <qbitmap.h>
+#include <qimage.h>
+#include <qpainter.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+#include <kpcolortoolbar.h>
+#include <kpcommandhistory.h>
+#include <kpdocument.h>
+#include <kpmainwindow.h>
+#include <kppixmapfx.h>
+#include <kpselection.h>
+#include <kptool.h>
+#include <kpviewmanager.h>
+
+
+kpToolAutoCropBorder::kpToolAutoCropBorder (const QPixmap *pixmapPtr,
+ int processedColorSimilarity)
+ : m_pixmapPtr (pixmapPtr),
+ m_processedColorSimilarity (processedColorSimilarity)
+{
+ invalidate ();
+}
+
+
+// public
+int kpToolAutoCropBorder::size () const
+{
+ return sizeof (kpToolAutoCropBorder);
+}
+
+
+// public
+const QPixmap *kpToolAutoCropBorder::pixmap () const
+{
+ return m_pixmapPtr;
+}
+
+// public
+int kpToolAutoCropBorder::processedColorSimilarity () const
+{
+ return m_processedColorSimilarity;
+}
+
+// public
+QRect kpToolAutoCropBorder::rect () const
+{
+ return m_rect;
+}
+
+// public
+int kpToolAutoCropBorder::left () const
+{
+ return m_rect.left ();
+}
+
+// public
+int kpToolAutoCropBorder::right () const
+{
+ return m_rect.right ();
+}
+
+// public
+int kpToolAutoCropBorder::top () const
+{
+ return m_rect.top ();
+}
+
+// public
+int kpToolAutoCropBorder::bottom () const
+{
+ return m_rect.bottom ();
+}
+
+// public
+kpColor kpToolAutoCropBorder::referenceColor () const
+{
+ return m_referenceColor;
+}
+
+// public
+kpColor kpToolAutoCropBorder::averageColor () const
+{
+ if (!m_rect.isValid ())
+ return kpColor::invalid;
+
+ if (m_referenceColor.isTransparent ())
+ return kpColor::transparent;
+ else if (m_processedColorSimilarity == 0)
+ return m_referenceColor;
+ else
+ {
+ int numPixels = (m_rect.width () * m_rect.height ());
+ if (numPixels <= 0)
+ {
+ kdError () << "kpToolAutoCropBorder::averageColor() rect=" << m_rect << endl;
+ return kpColor::invalid;
+ }
+
+ return kpColor (m_redSum / numPixels,
+ m_greenSum / numPixels,
+ m_blueSum / numPixels);
+ }
+}
+
+bool kpToolAutoCropBorder::isSingleColor () const
+{
+ return m_isSingleColor;
+}
+
+
+// public
+bool kpToolAutoCropBorder::calculate (int isX, int dir)
+{
+#if DEBUG_KP_TOOL_AUTO_CROP && 1
+ kdDebug () << "kpToolAutoCropBorder::calculate() CALLED!" << endl;
+#endif
+ int maxX = m_pixmapPtr->width () - 1;
+ int maxY = m_pixmapPtr->height () - 1;
+
+ QImage image = kpPixmapFX::convertToImage (*m_pixmapPtr);
+ if (image.isNull ())
+ {
+ kdError () << "Border::calculate() could not convert to QImage" << endl;
+ return false;
+ }
+
+ // (sync both branches)
+ if (isX)
+ {
+ int numCols = 0;
+ int startX = (dir > 0) ? 0 : maxX;
+
+ kpColor col = kpPixmapFX::getColorAtPixel (image, startX, 0);
+ for (int x = startX;
+ x >= 0 && x <= maxX;
+ x += dir)
+ {
+ int y;
+ for (y = 0; y <= maxY; y++)
+ {
+ if (!kpPixmapFX::getColorAtPixel (image, x, y).isSimilarTo (col, m_processedColorSimilarity))
+ break;
+ }
+
+ if (y <= maxY)
+ break;
+ else
+ numCols++;
+ }
+
+ if (numCols)
+ {
+ m_rect = QRect (QPoint (startX, 0),
+ QPoint (startX + (numCols - 1) * dir, maxY)).normalize ();
+ m_referenceColor = col;
+ }
+ }
+ else
+ {
+ int numRows = 0;
+ int startY = (dir > 0) ? 0 : maxY;
+
+ kpColor col = kpPixmapFX::getColorAtPixel (image, 0, startY);
+ for (int y = startY;
+ y >= 0 && y <= maxY;
+ y += dir)
+ {
+ int x;
+ for (x = 0; x <= maxX; x++)
+ {
+ if (!kpPixmapFX::getColorAtPixel (image, x, y).isSimilarTo (col, m_processedColorSimilarity))
+ break;
+ }
+
+ if (x <= maxX)
+ break;
+ else
+ numRows++;
+ }
+
+ if (numRows)
+ {
+ m_rect = QRect (QPoint (0, startY),
+ QPoint (maxX, startY + (numRows - 1) * dir)).normalize ();
+ m_referenceColor = col;
+ }
+ }
+
+
+ if (m_rect.isValid ())
+ {
+ m_isSingleColor = true;
+
+ if (m_referenceColor.isOpaque () && m_processedColorSimilarity != 0)
+ {
+ for (int y = m_rect.top (); y <= m_rect.bottom (); y++)
+ {
+ for (int x = m_rect.left (); x <= m_rect.right (); x++)
+ {
+ kpColor colAtPixel = kpPixmapFX::getColorAtPixel (image, x, y);
+
+ if (m_isSingleColor && colAtPixel != m_referenceColor)
+ m_isSingleColor = false;
+
+ m_redSum += colAtPixel.red ();
+ m_greenSum += colAtPixel.green ();
+ m_blueSum += colAtPixel.blue ();
+ }
+ }
+ }
+ }
+
+
+ return true;
+}
+
+// public
+bool kpToolAutoCropBorder::fillsEntirePixmap () const
+{
+ return (m_rect == m_pixmapPtr->rect ());
+}
+
+// public
+bool kpToolAutoCropBorder::exists () const
+{
+ // (will use in an addition so make sure returns 1 or 0)
+ return (m_rect.isValid () ? 1 : 0);
+}
+
+// public
+void kpToolAutoCropBorder::invalidate ()
+{
+ m_rect = QRect ();
+ m_referenceColor = kpColor::invalid;
+ m_redSum = m_greenSum = m_blueSum = 0;
+ m_isSingleColor = false;
+}
+
+
+class kpSetOverrideCursorSaver
+{
+public:
+ kpSetOverrideCursorSaver (const QCursor &cursor)
+ {
+ QApplication::setOverrideCursor (cursor);
+ }
+
+ ~kpSetOverrideCursorSaver ()
+ {
+ QApplication::restoreOverrideCursor ();
+ }
+};
+
+
+void showNothingToAutocropMessage (kpMainWindow *mainWindow, bool actOnSelection)
+{
+ kpSetOverrideCursorSaver cursorSaver (Qt::arrowCursor);
+
+ if (actOnSelection)
+ {
+ KMessageBox::information (mainWindow,
+ i18n ("KolourPaint cannot remove the selection's internal border as it"
+ " could not be located."),
+ i18n ("Cannot Remove Internal Border"),
+ "NothingToAutoCrop");
+ }
+ else
+ {
+ KMessageBox::information (mainWindow,
+ i18n ("KolourPaint cannot automatically crop the image as its"
+ " border could not be located."),
+ i18n ("Cannot Autocrop"),
+ "NothingToAutoCrop");
+ }
+}
+
+bool kpToolAutoCrop (kpMainWindow *mainWindow)
+{
+#if DEBUG_KP_TOOL_AUTO_CROP
+ kdDebug () << "kpToolAutoCrop() CALLED!" << endl;
+#endif
+
+ if (!mainWindow)
+ {
+ kdError () << "kpToolAutoCrop() passed NULL mainWindow" << endl;
+ return false;
+ }
+
+ kpDocument *doc = mainWindow->document ();
+ if (!doc)
+ {
+ kdError () << "kpToolAutoCrop() passed NULL document" << endl;
+ return false;
+ }
+
+ // OPT: if already pulled selection pixmap, no need to do it again here
+ QPixmap pixmap = doc->selection () ? doc->getSelectedPixmap () : *doc->pixmap ();
+ if (pixmap.isNull ())
+ {
+ kdError () << "kptoolAutoCrop() pased NULL pixmap" << endl;
+ return false;
+ }
+
+ kpViewManager *vm = mainWindow->viewManager ();
+ if (!vm)
+ {
+ kdError () << "kpToolAutoCrop() passed NULL vm" << endl;
+ return false;
+ }
+
+ int processedColorSimilarity = mainWindow->colorToolBar ()->processedColorSimilarity ();
+ kpToolAutoCropBorder leftBorder (&pixmap, processedColorSimilarity),
+ rightBorder (&pixmap, processedColorSimilarity),
+ topBorder (&pixmap, processedColorSimilarity),
+ botBorder (&pixmap, processedColorSimilarity);
+
+
+ kpSetOverrideCursorSaver cursorSaver (Qt::waitCursor);
+
+ // TODO: With Colour Similarity, a lot of weird (and wonderful) things can
+ // happen resulting in a huge number of code paths. Needs refactoring
+ // and regression testing.
+ //
+ // TODO: e.g. When the top fills entire rect but bot doesn't we could
+ // invalidate top and continue autocrop.
+ int numRegions = 0;
+ if (!leftBorder.calculate (true/*x*/, +1/*going right*/) ||
+ leftBorder.fillsEntirePixmap () ||
+ !rightBorder.calculate (true/*x*/, -1/*going left*/) ||
+ rightBorder.fillsEntirePixmap () ||
+ !topBorder.calculate (false/*y*/, +1/*going down*/) ||
+ topBorder.fillsEntirePixmap () ||
+ !botBorder.calculate (false/*y*/, -1/*going up*/) ||
+ botBorder.fillsEntirePixmap () ||
+ ((numRegions = leftBorder.exists () +
+ rightBorder.exists () +
+ topBorder.exists () +
+ botBorder.exists ()) == 0))
+ {
+ #if DEBUG_KP_TOOL_AUTO_CROP
+ kdDebug () << "\tcan't find border; leftBorder.rect=" << leftBorder.rect ()
+ << " rightBorder.rect=" << rightBorder.rect ()
+ << " topBorder.rect=" << topBorder.rect ()
+ << " botBorder.rect=" << botBorder.rect ()
+ << endl;
+ #endif
+ ::showNothingToAutocropMessage (mainWindow, (bool) doc->selection ());
+ return false;
+ }
+
+#if DEBUG_KP_TOOL_AUTO_CROP
+ kdDebug () << "\tnumRegions=" << numRegions << endl;
+ kdDebug () << "\t\tleft=" << leftBorder.rect ()
+ << " refCol=" << (leftBorder.exists () ? (int *) leftBorder.referenceColor ().toQRgb () : 0)
+ << " avgCol=" << (leftBorder.exists () ? (int *) leftBorder.averageColor ().toQRgb () : 0)
+ << endl;
+ kdDebug () << "\t\tright=" << rightBorder.rect ()
+ << " refCol=" << (rightBorder.exists () ? (int *) rightBorder.referenceColor ().toQRgb () : 0)
+ << " avgCol=" << (rightBorder.exists () ? (int *) rightBorder.averageColor ().toQRgb () : 0)
+ << endl;
+ kdDebug () << "\t\ttop=" << topBorder.rect ()
+ << " refCol=" << (topBorder.exists () ? (int *) topBorder.referenceColor ().toQRgb () : 0)
+ << " avgCol=" << (topBorder.exists () ? (int *) topBorder.averageColor ().toQRgb () : 0)
+ << endl;
+ kdDebug () << "\t\tbot=" << botBorder.rect ()
+ << " refCol=" << (botBorder.exists () ? (int *) botBorder.referenceColor ().toQRgb () : 0)
+ << " avgCol=" << (botBorder.exists () ? (int *) botBorder.averageColor ().toQRgb () : 0)
+ << endl;
+#endif
+
+
+ // In case e.g. the user pastes a solid, coloured-in rectangle,
+ // we favour killing the bottom and right regions
+ // (these regions probably contain the unwanted whitespace due
+ // to the doc being bigger than the pasted selection to start with).
+ //
+ // We also kill if they kiss or even overlap.
+
+ if (leftBorder.exists () && rightBorder.exists ())
+ {
+ const kpColor leftCol = leftBorder.averageColor ();
+ const kpColor rightCol = rightBorder.averageColor ();
+
+ if ((numRegions == 2 && !leftCol.isSimilarTo (rightCol, processedColorSimilarity)) ||
+ leftBorder.right () >= rightBorder.left () - 1) // kissing or overlapping
+ {
+ #if DEBUG_KP_TOOL_AUTO_CROP
+ kdDebug () << "\tignoring left border" << endl;
+ #endif
+ leftBorder.invalidate ();
+ }
+ }
+
+ if (topBorder.exists () && botBorder.exists ())
+ {
+ const kpColor topCol = topBorder.averageColor ();
+ const kpColor botCol = botBorder.averageColor ();
+
+ if ((numRegions == 2 && !topCol.isSimilarTo (botCol, processedColorSimilarity)) ||
+ topBorder.bottom () >= botBorder.top () - 1) // kissing or overlapping
+ {
+ #if DEBUG_KP_TOOL_AUTO_CROP
+ kdDebug () << "\tignoring top border" << endl;
+ #endif
+ topBorder.invalidate ();
+ }
+ }
+
+
+ mainWindow->addImageOrSelectionCommand (
+ new kpToolAutoCropCommand (
+ (bool) doc->selection (),
+ leftBorder, rightBorder,
+ topBorder, botBorder,
+ mainWindow));
+
+
+ return true;
+}
+
+
+kpToolAutoCropCommand::kpToolAutoCropCommand (bool actOnSelection,
+ const kpToolAutoCropBorder &leftBorder,
+ const kpToolAutoCropBorder &rightBorder,
+ const kpToolAutoCropBorder &topBorder,
+ const kpToolAutoCropBorder &botBorder,
+ kpMainWindow *mainWindow)
+ : kpNamedCommand (name (actOnSelection, DontShowAccel), mainWindow),
+ m_actOnSelection (actOnSelection),
+ m_leftBorder (leftBorder),
+ m_rightBorder (rightBorder),
+ m_topBorder (topBorder),
+ m_botBorder (botBorder),
+ m_leftPixmap (0),
+ m_rightPixmap (0),
+ m_topPixmap (0),
+ m_botPixmap (0)
+{
+ kpDocument *doc = document ();
+ if (!doc)
+ {
+ kdError () << "kpToolAutoCropCommand::<ctor>() without doc" << endl;
+ m_oldWidth = 0;
+ m_oldHeight = 0;
+ return;
+ }
+
+ m_oldWidth = doc->width (m_actOnSelection);
+ m_oldHeight = doc->height (m_actOnSelection);
+}
+
+kpToolAutoCropCommand::~kpToolAutoCropCommand ()
+{
+ deleteUndoPixmaps ();
+}
+
+
+// public static
+QString kpToolAutoCropCommand::name (bool actOnSelection, int options)
+{
+ if (actOnSelection)
+ {
+ if (options & ShowAccel)
+ return i18n ("Remove Internal B&order");
+ else
+ return i18n ("Remove Internal Border");
+ }
+ else
+ {
+ if (options & ShowAccel)
+ return i18n ("Autocr&op");
+ else
+ return i18n ("Autocrop");
+ }
+}
+
+
+// public virtual [base kpCommand]
+int kpToolAutoCropCommand::size () const
+{
+ return m_leftBorder.size () +
+ m_rightBorder.size () +
+ m_topBorder.size () +
+ m_botBorder.size () +
+ kpPixmapFX::pixmapSize (m_leftPixmap) +
+ kpPixmapFX::pixmapSize (m_rightPixmap) +
+ kpPixmapFX::pixmapSize (m_topPixmap) +
+ kpPixmapFX::pixmapSize (m_botPixmap) +
+ m_oldSelection.size ();
+}
+
+
+// private
+void kpToolAutoCropCommand::getUndoPixmap (const kpToolAutoCropBorder &border, QPixmap **pixmap)
+{
+ kpDocument *doc = document ();
+
+#if DEBUG_KP_TOOL_AUTO_CROP && 1
+ kdDebug () << "kpToolAutoCropCommand::getUndoPixmap()" << endl;
+ kdDebug () << "\tpixmap=" << pixmap
+ << " border: rect=" << border.rect ()
+ << " isSingleColor=" << border.isSingleColor ()
+ << endl;
+#endif
+
+ if (!doc)
+ return;
+
+ if (pixmap && border.exists () && !border.isSingleColor ())
+ {
+ if (*pixmap)
+ {
+ #if DEBUG_KP_TOOL_AUTO_CROP && 1
+ kdDebug () << "\talready have *pixmap - delete it" << endl;
+ #endif
+ delete *pixmap;
+ }
+
+ *pixmap = new QPixmap (
+ kpPixmapFX::getPixmapAt (*doc->pixmap (m_actOnSelection),
+ border.rect ()));
+ }
+}
+
+
+// private
+void kpToolAutoCropCommand::getUndoPixmaps ()
+{
+ getUndoPixmap (m_leftBorder, &m_leftPixmap);
+ getUndoPixmap (m_rightBorder, &m_rightPixmap);
+ getUndoPixmap (m_topBorder, &m_topPixmap);
+ getUndoPixmap (m_botBorder, &m_botPixmap);
+}
+
+// private
+void kpToolAutoCropCommand::deleteUndoPixmaps ()
+{
+#if DEBUG_KP_TOOL_AUTO_CROP && 1
+ kdDebug () << "kpToolAutoCropCommand::deleteUndoPixmaps()" << endl;
+#endif
+
+ delete m_leftPixmap; m_leftPixmap = 0;
+ delete m_rightPixmap; m_rightPixmap = 0;
+ delete m_topPixmap; m_topPixmap = 0;
+ delete m_botPixmap; m_botPixmap = 0;
+}
+
+
+// public virtual [base kpCommand]
+void kpToolAutoCropCommand::execute ()
+{
+ if (!m_contentsRect.isValid ())
+ m_contentsRect = contentsRect ();
+
+
+ getUndoPixmaps ();
+
+
+ kpDocument *doc = document ();
+ if (!doc)
+ return;
+
+
+ QPixmap pixmapWithoutBorder =
+ kpTool::neededPixmap (*doc->pixmap (m_actOnSelection),
+ m_contentsRect);
+
+
+ if (!m_actOnSelection)
+ doc->setPixmap (pixmapWithoutBorder);
+ else
+ {
+ m_oldSelection = *doc->selection ();
+ m_oldSelection.setPixmap (QPixmap ());
+
+ // m_contentsRect is relative to the top of the sel
+ // while sel is relative to the top of the doc
+ QRect rect = m_contentsRect;
+ rect.moveBy (m_oldSelection.x (), m_oldSelection.y ());
+
+ kpSelection sel (kpSelection::Rectangle,
+ rect,
+ pixmapWithoutBorder,
+ m_oldSelection.transparency ());
+
+ doc->setSelection (sel);
+
+ if (m_mainWindow->tool ())
+ m_mainWindow->tool ()->somethingBelowTheCursorChanged ();
+ }
+}
+
+// public virtual [base kpCommand]
+void kpToolAutoCropCommand::unexecute ()
+{
+#if DEBUG_KP_TOOL_AUTO_CROP && 1
+ kdDebug () << "kpToolAutoCropCommand::unexecute()" << endl;
+#endif
+
+ kpDocument *doc = document ();
+ if (!doc)
+ return;
+
+ QPixmap pixmap (m_oldWidth, m_oldHeight);
+ QBitmap maskBitmap;
+
+ // restore the position of the centre image
+ kpPixmapFX::setPixmapAt (&pixmap, m_contentsRect,
+ *doc->pixmap (m_actOnSelection));
+
+ // draw the borders
+
+ QPainter painter (&pixmap);
+ QPainter maskPainter;
+
+ const kpToolAutoCropBorder *borders [] =
+ {
+ &m_leftBorder, &m_rightBorder,
+ &m_topBorder, &m_botBorder,
+ 0
+ };
+
+ const QPixmap *pixmaps [] =
+ {
+ m_leftPixmap, m_rightPixmap,
+ m_topPixmap, m_botPixmap,
+ 0
+ };
+
+ const QPixmap **p = pixmaps;
+ for (const kpToolAutoCropBorder **b = borders; *b; b++, p++)
+ {
+ if (!(*b)->exists ())
+ continue;
+
+ if ((*b)->isSingleColor ())
+ {
+ kpColor col = (*b)->referenceColor ();
+ #if DEBUG_KP_TOOL_AUTO_CROP && 1
+ kdDebug () << "\tdrawing border " << (*b)->rect ()
+ << " rgb=" << (int *) col.toQRgb () /* %X hack */ << endl;
+ #endif
+
+ if (col.isOpaque ())
+ {
+ painter.fillRect ((*b)->rect (), col.toQColor ());
+ }
+ else
+ {
+ if (maskBitmap.isNull ())
+ {
+ // TODO: dangerous when a painter is active on pixmap?
+ maskBitmap = kpPixmapFX::getNonNullMask (pixmap);
+ maskPainter.begin (&maskBitmap);
+ }
+
+ maskPainter.fillRect ((*b)->rect (), Qt::color0/*transparent*/);
+ }
+ }
+ else
+ {
+ #if DEBUG_KP_TOOL_AUTO_CROP && 1
+ kdDebug () << "\trestoring border pixmap " << (*b)->rect () << endl;
+ #endif
+ // **p cannot contain a single transparent pixel because
+ // if it did, all other pixels must be transparent (only
+ // transparent pixels are similar to transparent pixels)
+ // and the other branch would execute.
+ if (*p)
+ {
+ // TODO: We should really edit the mask here. Due to good
+ // luck (if "maskBitmap" is initialized above, this region
+ // will be marked as opaque in the mask; if it's not
+ // initialized, we will be opaque by default), we
+ // don't actually have to edit the mask but this is
+ // highly error-prone.
+ painter.drawPixmap ((*b)->rect (), **p);
+ }
+ }
+ }
+
+ if (maskPainter.isActive ())
+ maskPainter.end ();
+
+ painter.end ();
+
+ if (!maskBitmap.isNull ())
+ pixmap.setMask (maskBitmap);
+
+
+ if (!m_actOnSelection)
+ doc->setPixmap (pixmap);
+ else
+ {
+ kpSelection sel = m_oldSelection;
+ sel.setPixmap (pixmap);
+
+ doc->setSelection (sel);
+
+ if (m_mainWindow->tool ())
+ m_mainWindow->tool ()->somethingBelowTheCursorChanged ();
+ }
+
+
+ deleteUndoPixmaps ();
+}
+
+
+// private
+QRect kpToolAutoCropCommand::contentsRect () const
+{
+ const QPixmap *pixmap = document ()->pixmap (m_actOnSelection);
+
+ QPoint topLeft (m_leftBorder.exists () ?
+ m_leftBorder.rect ().right () + 1 :
+ 0,
+ m_topBorder.exists () ?
+ m_topBorder.rect ().bottom () + 1 :
+ 0);
+ QPoint botRight (m_rightBorder.exists () ?
+ m_rightBorder.rect ().left () - 1 :
+ pixmap->width () - 1,
+ m_botBorder.exists () ?
+ m_botBorder.rect ().top () - 1 :
+ pixmap->height () - 1);
+
+ return QRect (topLeft, botRight);
+}
diff --git a/kolourpaint/tools/kptoolautocrop.h b/kolourpaint/tools/kptoolautocrop.h
new file mode 100644
index 00000000..4d016a1d
--- /dev/null
+++ b/kolourpaint/tools/kptoolautocrop.h
@@ -0,0 +1,127 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kptoolautocrop_h__
+#define __kptoolautocrop_h__
+
+#include <qrect.h>
+
+#include <kpcommandhistory.h>
+
+#include <kpcolor.h>
+#include <kpselection.h>
+
+class QPixmap;
+class kpDocument;
+class kpMainWindow;
+class kpViewManager;
+
+
+// (returns true on success (even if it did nothing) or false on error)
+bool kpToolAutoCrop (kpMainWindow *mainWindow);
+
+
+class kpToolAutoCropBorder
+{
+public:
+ kpToolAutoCropBorder (const QPixmap *pixmapPtr, int processedColorSimilarity);
+
+ int size () const;
+
+ const QPixmap *pixmap () const;
+ int processedColorSimilarity () const;
+ QRect rect () const;
+ int left () const;
+ int right () const;
+ int top () const;
+ int bottom () const;
+ kpColor referenceColor () const;
+ kpColor averageColor () const;
+ bool isSingleColor () const;
+
+ // (returns true on success (even if no rect) or false on error)
+ bool calculate (int isX, int dir);
+
+ bool fillsEntirePixmap () const;
+ bool exists () const;
+ void invalidate ();
+
+private:
+ const QPixmap *m_pixmapPtr;
+ int m_processedColorSimilarity;
+
+ QRect m_rect;
+ kpColor m_referenceColor;
+ int m_redSum, m_greenSum, m_blueSum;
+ bool m_isSingleColor;
+};
+
+
+class kpToolAutoCropCommand : public kpNamedCommand
+{
+public:
+ kpToolAutoCropCommand (bool actOnSelection,
+ const kpToolAutoCropBorder &leftBorder,
+ const kpToolAutoCropBorder &rightBorder,
+ const kpToolAutoCropBorder &topBorder,
+ const kpToolAutoCropBorder &botBorder,
+ kpMainWindow *mainWindow);
+ virtual ~kpToolAutoCropCommand ();
+
+ enum NameOptions
+ {
+ DontShowAccel = 0,
+ ShowAccel = 1
+ };
+
+ static QString name (bool actOnSelection, int options);
+
+ virtual int size () const;
+
+private:
+ void getUndoPixmap (const kpToolAutoCropBorder &border, QPixmap **pixmap);
+ void getUndoPixmaps ();
+ void deleteUndoPixmaps ();
+
+public:
+ virtual void execute ();
+ virtual void unexecute ();
+
+private:
+ QRect contentsRect () const;
+
+ bool m_actOnSelection;
+ kpToolAutoCropBorder m_leftBorder, m_rightBorder, m_topBorder, m_botBorder;
+ QPixmap *m_leftPixmap, *m_rightPixmap, *m_topPixmap, *m_botPixmap;
+
+ QRect m_contentsRect;
+ int m_oldWidth, m_oldHeight;
+ kpSelection m_oldSelection;
+};
+
+#endif // __kptoolautocrop_h__
diff --git a/kolourpaint/tools/kptoolbrush.cpp b/kolourpaint/tools/kptoolbrush.cpp
new file mode 100644
index 00000000..6e684ed9
--- /dev/null
+++ b/kolourpaint/tools/kptoolbrush.cpp
@@ -0,0 +1,45 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <klocale.h>
+#include <kptoolbrush.h>
+
+kpToolBrush::kpToolBrush (kpMainWindow *mainWindow)
+ : kpToolPen (kpToolPen::Brush,
+ i18n ("Brush"),
+ i18n ("Draw using brushes of different shapes and sizes"),
+ Qt::Key_B,
+ mainWindow, "tool_brush")
+{
+}
+
+kpToolBrush::~kpToolBrush ()
+{
+}
+
+#include <kptoolbrush.moc>
diff --git a/kolourpaint/tools/kptoolbrush.h b/kolourpaint/tools/kptoolbrush.h
new file mode 100644
index 00000000..69498495
--- /dev/null
+++ b/kolourpaint/tools/kptoolbrush.h
@@ -0,0 +1,43 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kptoolbrush_h__
+#define __kptoolbrush_h__
+
+#include <kptoolpen.h>
+
+class kpToolBrush : public kpToolPen
+{
+Q_OBJECT
+
+public:
+ kpToolBrush (kpMainWindow *mainWindow);
+ virtual ~kpToolBrush ();
+};
+
+#endif // __kptoolbrush_h__
diff --git a/kolourpaint/tools/kptoolclear.cpp b/kolourpaint/tools/kptoolclear.cpp
new file mode 100644
index 00000000..230e54a3
--- /dev/null
+++ b/kolourpaint/tools/kptoolclear.cpp
@@ -0,0 +1,135 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <kptoolclear.h>
+
+#include <qpixmap.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kpdefs.h>
+#include <kpdocument.h>
+#include <kpmainwindow.h>
+#include <kppixmapfx.h>
+#include <kpselection.h>
+
+
+kpToolClearCommand::kpToolClearCommand (bool actOnSelection,
+ const kpColor &newColor,
+ kpMainWindow *mainWindow)
+ : kpCommand (mainWindow),
+ m_actOnSelection (actOnSelection),
+ m_newColor (newColor),
+ m_oldPixmapPtr (0)
+{
+}
+
+kpToolClearCommand::kpToolClearCommand (bool actOnSelection,
+ kpMainWindow *mainWindow)
+ : kpCommand (mainWindow),
+ m_actOnSelection (actOnSelection),
+ m_newColor (mainWindow ? mainWindow->backgroundColor () : kpColor::invalid),
+ m_oldPixmapPtr (0)
+{
+}
+
+kpToolClearCommand::~kpToolClearCommand ()
+{
+ delete m_oldPixmapPtr;
+}
+
+
+// public virtual [base kpCommand]
+QString kpToolClearCommand::name () const
+{
+ QString opName = i18n ("Clear");
+
+ if (m_actOnSelection)
+ return i18n ("Selection: %1").arg (opName);
+ else
+ return opName;
+}
+
+
+// public virtual [base kpCommand]
+int kpToolClearCommand::size () const
+{
+ return kpPixmapFX::pixmapSize (m_oldPixmapPtr);
+}
+
+
+// public virtual [base kpCommand]
+void kpToolClearCommand::execute ()
+{
+ kpDocument *doc = document ();
+ if (!doc)
+ {
+ kdError () << "kpToolClearCommand::execute() without doc" << endl;
+ return;
+ }
+
+
+ m_oldPixmapPtr = new QPixmap ();
+ *m_oldPixmapPtr = *doc->pixmap (m_actOnSelection);
+
+
+ if (m_actOnSelection)
+ {
+ // OPT: could just edit pixmap directly and signal change
+ kpSelection *sel = doc->selection ();
+
+ QPixmap newPixmap (sel->width (), sel->height ());
+ kpPixmapFX::fill (&newPixmap, m_newColor);
+ // TODO: maybe disable Image/Clear if transparent colour
+ if (m_newColor.isOpaque ())
+ newPixmap.setMask (sel->maskForOwnType ());
+
+ sel->setPixmap (newPixmap);
+ }
+ else
+ doc->fill (m_newColor);
+}
+
+// public virtual [base kpCommand]
+void kpToolClearCommand::unexecute ()
+{
+ kpDocument *doc = document ();
+ if (!doc)
+ {
+ kdError () << "kpToolClearCommand::execute() without doc" << endl;
+ return;
+ }
+
+
+ doc->setPixmap (m_actOnSelection, *m_oldPixmapPtr);
+
+
+ delete m_oldPixmapPtr;
+ m_oldPixmapPtr = 0;
+}
diff --git a/kolourpaint/tools/kptoolclear.h b/kolourpaint/tools/kptoolclear.h
new file mode 100644
index 00000000..ccf3697f
--- /dev/null
+++ b/kolourpaint/tools/kptoolclear.h
@@ -0,0 +1,68 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kptoolclear_h__
+#define __kptoolclear_h__
+
+#include <kpcommandhistory.h>
+
+#include <kpcolor.h>
+
+class QPixmap;
+class QString;
+
+class kpDocument;
+class kpMainWindow;
+
+
+class kpToolClearCommand : public kpCommand
+{
+public:
+ kpToolClearCommand (bool actOnSelection,
+ const kpColor &newColor,
+ kpMainWindow *mainWindow);
+ kpToolClearCommand (bool actOnSelection,
+ kpMainWindow *mainWindow);
+ virtual ~kpToolClearCommand ();
+
+ virtual QString name () const;
+
+ virtual int size () const;
+
+ virtual void execute ();
+ virtual void unexecute ();
+
+private:
+ bool m_actOnSelection;
+
+ kpColor m_newColor;
+ QPixmap *m_oldPixmapPtr;
+};
+
+
+#endif // __kptoolclear_h__
diff --git a/kolourpaint/tools/kptoolcolorpicker.cpp b/kolourpaint/tools/kptoolcolorpicker.cpp
new file mode 100644
index 00000000..1050b1cf
--- /dev/null
+++ b/kolourpaint/tools/kptoolcolorpicker.cpp
@@ -0,0 +1,197 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define DEBUG_KP_TOOL_COLOR_PICKER 0
+
+
+#include <kptoolcolorpicker.h>
+
+#include <qimage.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kpcolortoolbar.h>
+#include <kpcommandhistory.h>
+#include <kpdefs.h>
+#include <kpdocument.h>
+#include <kpmainwindow.h>
+#include <kppixmapfx.h>
+
+
+/*
+ * kpToolColorPicker
+ */
+
+kpToolColorPicker::kpToolColorPicker (kpMainWindow *mainWindow)
+ : kpTool (i18n ("Color Picker"), i18n ("Lets you select a color from the image"),
+ Qt::Key_C,
+ mainWindow, "tool_color_picker")
+{
+}
+
+kpToolColorPicker::~kpToolColorPicker ()
+{
+}
+
+kpColor kpToolColorPicker::colorAtPixel (const QPoint &p)
+{
+#if DEBUG_KP_TOOL_COLOR_PICKER && 0
+ kdDebug () << "kpToolColorPicker::colorAtPixel" << p << endl;
+#endif
+
+ return kpPixmapFX::getColorAtPixel (*document ()->pixmap (), p);
+}
+
+
+QString kpToolColorPicker::haventBegunDrawUserMessage () const
+{
+ return i18n ("Click to select a color.");
+}
+
+void kpToolColorPicker::begin ()
+{
+ setUserMessage (haventBegunDrawUserMessage ());
+}
+
+// virtual
+void kpToolColorPicker::beginDraw ()
+{
+ m_oldColor = color (m_mouseButton);
+
+ setUserMessage (cancelUserMessage ());
+}
+
+// virtual
+void kpToolColorPicker::draw (const QPoint &thisPoint, const QPoint &, const QRect &)
+{
+ const kpColor color = colorAtPixel (thisPoint);
+
+ if (color.isValid ())
+ {
+ mainWindow ()->colorToolBar ()->setColor (m_mouseButton, color);
+ setUserShapePoints (thisPoint);
+ }
+ else
+ {
+ mainWindow ()->colorToolBar ()->setColor (m_mouseButton, m_oldColor);
+ setUserShapePoints ();
+ }
+}
+
+// virtual
+void kpToolColorPicker::cancelShape ()
+{
+ mainWindow ()->colorToolBar ()->setColor (m_mouseButton, m_oldColor);
+
+ setUserMessage (i18n ("Let go of all the mouse buttons."));
+}
+
+void kpToolColorPicker::releasedAllButtons ()
+{
+ setUserMessage (haventBegunDrawUserMessage ());
+
+}
+
+// virtual
+void kpToolColorPicker::endDraw (const QPoint &thisPoint, const QRect &)
+{
+ const kpColor color = colorAtPixel (thisPoint);
+
+ if (color.isValid ())
+ {
+ kpToolColorPickerCommand *cmd = new kpToolColorPickerCommand (
+ m_mouseButton,
+ color, m_oldColor,
+ mainWindow ());
+
+ mainWindow ()->commandHistory ()->addCommand (cmd, false /* no exec */);
+ setUserMessage (haventBegunDrawUserMessage ());
+ }
+ else
+ {
+ cancelShape ();
+ }
+}
+
+/*
+ * kpToolColorPickerCommand
+ */
+
+kpToolColorPickerCommand::kpToolColorPickerCommand (int mouseButton,
+ const kpColor &newColor,
+ const kpColor &oldColor,
+ kpMainWindow *mainWindow)
+ : kpCommand (mainWindow),
+ m_mouseButton (mouseButton),
+ m_newColor (newColor),
+ m_oldColor (oldColor)
+{
+}
+
+kpToolColorPickerCommand::~kpToolColorPickerCommand ()
+{
+}
+
+
+// public virtual [base kpCommand]
+QString kpToolColorPickerCommand::name () const
+{
+ return i18n ("Color Picker");
+}
+
+
+// public virtual [base kpCommand]
+int kpToolColorPickerCommand::size () const
+{
+ return 0;
+}
+
+
+// public virtual [base kpCommand]
+void kpToolColorPickerCommand::execute ()
+{
+ colorToolBar ()->setColor (m_mouseButton, m_newColor);
+}
+
+// public virtual [base kpCommand]
+void kpToolColorPickerCommand::unexecute ()
+{
+ colorToolBar ()->setColor (m_mouseButton, m_oldColor);
+}
+
+
+// private
+kpColorToolBar *kpToolColorPickerCommand::colorToolBar () const
+{
+ return m_mainWindow ? m_mainWindow->colorToolBar () : 0;
+}
+
+#include <kptoolcolorpicker.moc>
diff --git a/kolourpaint/tools/kptoolcolorpicker.h b/kolourpaint/tools/kptoolcolorpicker.h
new file mode 100644
index 00000000..46fc94be
--- /dev/null
+++ b/kolourpaint/tools/kptoolcolorpicker.h
@@ -0,0 +1,95 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kptoolcolorpicker_h__
+#define __kptoolcolorpicker_h__
+
+#include <kpcommandhistory.h>
+
+#include <kpcolor.h>
+#include <kptool.h>
+
+class QPoint;
+class QRect;
+
+class kpColorToolBar;
+
+class kpToolColorPicker : public kpTool
+{
+Q_OBJECT
+
+public:
+ kpToolColorPicker (kpMainWindow *);
+ virtual ~kpToolColorPicker ();
+
+ // generally the user goes to pick a color but wants to return to using
+ // his/her previous drawing tool
+ virtual bool returnToPreviousToolAfterEndDraw () const { return true; }
+
+private:
+ QString haventBegunDrawUserMessage () const;
+
+public:
+ virtual void begin ();
+ virtual void beginDraw ();
+ virtual void draw (const QPoint &thisPoint, const QPoint &, const QRect &);
+ virtual void cancelShape ();
+ virtual void releasedAllButtons ();
+ virtual void endDraw (const QPoint &thisPoint, const QRect &);
+
+private:
+ kpColor colorAtPixel (const QPoint &p);
+
+ kpColor m_oldColor;
+};
+
+class kpToolColorPickerCommand : public kpCommand
+{
+public:
+ kpToolColorPickerCommand (int mouseButton,
+ const kpColor &newColor, const kpColor &oldColor,
+ kpMainWindow *mainWindow);
+ virtual ~kpToolColorPickerCommand ();
+
+ virtual QString name () const;
+
+ virtual int size () const;
+
+ virtual void execute ();
+ virtual void unexecute ();
+
+private:
+ kpColorToolBar *colorToolBar () const;
+
+private:
+ int m_mouseButton;
+ kpColor m_newColor;
+ kpColor m_oldColor;
+};
+
+#endif // __kptoolcolorpicker_h__
diff --git a/kolourpaint/tools/kptoolcolorwasher.cpp b/kolourpaint/tools/kptoolcolorwasher.cpp
new file mode 100644
index 00000000..6c2d091f
--- /dev/null
+++ b/kolourpaint/tools/kptoolcolorwasher.cpp
@@ -0,0 +1,45 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <klocale.h>
+#include <kptoolcolorwasher.h>
+
+kpToolColorWasher::kpToolColorWasher (kpMainWindow *mainWindow)
+ : kpToolPen (kpToolPen::ColorWasher,
+ i18n ("Color Eraser"),
+ i18n ("Replaces pixels of the foreground color with the background color"),
+ Qt::Key_O,
+ mainWindow, "tool_color_washer")
+{
+}
+
+kpToolColorWasher::~kpToolColorWasher ()
+{
+}
+
+#include <kptoolcolorwasher.moc>
diff --git a/kolourpaint/tools/kptoolcolorwasher.h b/kolourpaint/tools/kptoolcolorwasher.h
new file mode 100644
index 00000000..1a707c3e
--- /dev/null
+++ b/kolourpaint/tools/kptoolcolorwasher.h
@@ -0,0 +1,43 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kptoolcolorwasher_h__
+#define __kptoolcolorwasher_h__
+
+#include <kptoolpen.h>
+
+class kpToolColorWasher : public kpToolPen
+{
+Q_OBJECT
+
+public:
+ kpToolColorWasher (kpMainWindow *mainWindow);
+ virtual ~kpToolColorWasher ();
+};
+
+#endif // __kptoolcolorwasher_h__
diff --git a/kolourpaint/tools/kptoolconverttograyscale.cpp b/kolourpaint/tools/kptoolconverttograyscale.cpp
new file mode 100644
index 00000000..a80ef8fa
--- /dev/null
+++ b/kolourpaint/tools/kptoolconverttograyscale.cpp
@@ -0,0 +1,106 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <qapplication.h>
+#include <qpixmap.h>
+
+#include <klocale.h>
+
+#include <kpdefs.h>
+#include <kpdocument.h>
+#include <kpmainwindow.h>
+#include <kppixmapfx.h>
+#include <kpselection.h>
+#include <kptoolconverttograyscale.h>
+
+
+kpToolConvertToGrayscaleCommand::kpToolConvertToGrayscaleCommand (bool actOnSelection,
+ kpMainWindow *mainWindow)
+ : kpCommand (mainWindow),
+ m_actOnSelection (actOnSelection),
+ m_oldPixmapPtr (0)
+{
+}
+
+kpToolConvertToGrayscaleCommand::~kpToolConvertToGrayscaleCommand ()
+{
+ delete m_oldPixmapPtr;
+}
+
+
+// public virtual [base kpCommand]
+QString kpToolConvertToGrayscaleCommand::name () const
+{
+ QString opName = i18n ("Reduce to Grayscale");
+
+ if (m_actOnSelection)
+ return i18n ("Selection: %1").arg (opName);
+ else
+ return opName;
+}
+
+
+// public virtual [base kpCommand]
+int kpToolConvertToGrayscaleCommand::size () const
+{
+ return kpPixmapFX::pixmapSize (m_oldPixmapPtr);
+}
+
+
+// public virtual [base kpCommand]
+void kpToolConvertToGrayscaleCommand::execute ()
+{
+ kpDocument *doc = document ();
+ if (!doc)
+ return;
+
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+ m_oldPixmapPtr = new QPixmap ();
+ *m_oldPixmapPtr = *doc->pixmap (m_actOnSelection);
+
+ QPixmap newPixmap = kpPixmapFX::convertToGrayscale (*doc->pixmap (m_actOnSelection));
+
+ doc->setPixmap (m_actOnSelection, newPixmap);
+
+ QApplication::restoreOverrideCursor ();
+}
+
+// public virtual [base kpCommand]
+void kpToolConvertToGrayscaleCommand::unexecute ()
+{
+ kpDocument *doc = document ();
+ if (!doc)
+ return;
+
+ doc->setPixmap (m_actOnSelection, *m_oldPixmapPtr);
+
+ delete m_oldPixmapPtr;
+ m_oldPixmapPtr = 0;
+}
+
diff --git a/kolourpaint/tools/kptoolconverttograyscale.h b/kolourpaint/tools/kptoolconverttograyscale.h
new file mode 100644
index 00000000..6ea5e515
--- /dev/null
+++ b/kolourpaint/tools/kptoolconverttograyscale.h
@@ -0,0 +1,57 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kptoolconverttograyscale_h__
+#define __kptoolconverttograyscale_h__
+
+#include <kpcommandhistory.h>
+
+class QPixmap;
+class QString;
+
+class kpMainWindow;
+
+class kpToolConvertToGrayscaleCommand : public kpCommand
+{
+public:
+ kpToolConvertToGrayscaleCommand (bool actOnSelection,
+ kpMainWindow *mainWindow);
+ virtual ~kpToolConvertToGrayscaleCommand ();
+
+ virtual QString name () const;
+ virtual int size () const;
+
+ virtual void execute ();
+ virtual void unexecute ();
+
+private:
+ bool m_actOnSelection;
+ QPixmap *m_oldPixmapPtr;
+};
+
+#endif // __kptoolconverttograyscale_h__
diff --git a/kolourpaint/tools/kptoolcrop.cpp b/kolourpaint/tools/kptoolcrop.cpp
new file mode 100644
index 00000000..8cc6e880
--- /dev/null
+++ b/kolourpaint/tools/kptoolcrop.cpp
@@ -0,0 +1,335 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG_KP_TOOL_CROP 0
+
+
+#include <kptoolcrop.h>
+
+#include <qpixmap.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kpcolor.h>
+#include <kpcommandhistory.h>
+#include <kpdocument.h>
+#include <kpmainwindow.h>
+#include <kpselection.h>
+#include <kptoolclear.h>
+#include <kptoolresizescale.h>
+#include <kptoolselection.h>
+#include <kpviewmanager.h>
+
+
+kpSelection selectionBorderAndMovedTo0_0 (const kpSelection &sel)
+{
+ kpSelection borderSel = sel;
+
+ borderSel.setPixmap (QPixmap ()); // only interested in border
+ borderSel.moveTo (QPoint (0, 0));
+
+ return borderSel;
+}
+
+
+//
+// kpToolCropSetImageCommand
+//
+
+class kpToolCropSetImageCommand : public kpCommand
+{
+public:
+ kpToolCropSetImageCommand (kpMainWindow *mainWindow);
+ virtual ~kpToolCropSetImageCommand ();
+
+ /* (uninteresting child of macro cmd) */
+ virtual QString name () const { return QString::null; }
+
+ virtual int size () const
+ {
+ return kpPixmapFX::pixmapSize (m_oldPixmap) +
+ kpPixmapFX::selectionSize (m_fromSelection) +
+ kpPixmapFX::pixmapSize (m_pixmapIfFromSelectionDoesntHaveOne);
+ }
+
+ virtual void execute ();
+ virtual void unexecute ();
+
+protected:
+ kpColor m_backgroundColor;
+ QPixmap m_oldPixmap;
+ kpSelection m_fromSelection;
+ QPixmap m_pixmapIfFromSelectionDoesntHaveOne;
+};
+
+
+kpToolCropSetImageCommand::kpToolCropSetImageCommand (kpMainWindow *mainWindow)
+ : kpCommand (mainWindow),
+ m_backgroundColor (mainWindow ? mainWindow->backgroundColor () : kpColor::invalid),
+ m_fromSelection (*mainWindow->document ()->selection ()),
+ m_pixmapIfFromSelectionDoesntHaveOne (
+ m_fromSelection.pixmap () ?
+ QPixmap () :
+ mainWindow->document ()->getSelectedPixmap ())
+{
+}
+
+kpToolCropSetImageCommand::~kpToolCropSetImageCommand ()
+{
+}
+
+
+// public virtual [base kpCommand]
+void kpToolCropSetImageCommand::execute ()
+{
+#if DEBUG_KP_TOOL_CROP
+ kdDebug () << "kpToolCropSetImageCommand::execute()" << endl;
+#endif
+
+ viewManager ()->setQueueUpdates ();
+ {
+ m_oldPixmap = kpPixmapFX::getPixmapAt (*document ()->pixmap (),
+ QRect (0, 0, m_fromSelection.width (), m_fromSelection.height ()));
+
+
+ //
+ // e.g. original elliptical selection:
+ //
+ // t/---\ T = original transparent selection pixel
+ // | TT | t = outside the selection region
+ // t\__/t [every other character] = original opaque selection pixel
+ //
+ // Afterwards, the _document_ image becomes:
+ //
+ // b/---\ T = [unchanged]
+ // | TT | b = background color
+ // b\__/b [every other character] = [unchanged]
+ //
+ // The selection is deleted.
+ //
+ // TODO: Do not introduce a mask if the result will not contain
+ // any transparent pixels.
+ //
+
+ QPixmap newDocPixmap (m_fromSelection.width (), m_fromSelection.height ());
+ kpPixmapFX::fill (&newDocPixmap, m_backgroundColor);
+
+ #if DEBUG_KP_TOOL_CROP
+ kdDebug () << "\tsel: rect=" << m_fromSelection.boundingRect ()
+ << " pm=" << m_fromSelection.pixmap ()
+ << endl;
+ #endif
+ QPixmap selTransparentPixmap;
+
+ if (m_fromSelection.pixmap ())
+ {
+ selTransparentPixmap = m_fromSelection.transparentPixmap ();
+ #if DEBUG_KP_TOOL_CROP
+ kdDebug () << "\thave pixmap; rect="
+ << selTransparentPixmap.rect ()
+ << endl;
+ #endif
+ }
+ else
+ {
+ selTransparentPixmap = m_pixmapIfFromSelectionDoesntHaveOne;
+ #if DEBUG_KP_TOOL_CROP
+ kdDebug () << "\tno pixmap in sel - get it; rect="
+ << selTransparentPixmap.rect ()
+ << endl;
+ #endif
+ }
+
+ kpPixmapFX::paintMaskTransparentWithBrush (&newDocPixmap,
+ QPoint (0, 0),
+ m_fromSelection.maskForOwnType ());
+
+ kpPixmapFX::paintPixmapAt (&newDocPixmap,
+ QPoint (0, 0),
+ selTransparentPixmap);
+
+
+ document ()->setPixmapAt (newDocPixmap, QPoint (0, 0));
+ document ()->selectionDelete ();
+
+
+ if (mainWindow ()->tool ())
+ m_mainWindow->tool ()->somethingBelowTheCursorChanged ();
+ }
+ viewManager ()->restoreQueueUpdates ();
+}
+
+// public virtual [base kpCommand]
+void kpToolCropSetImageCommand::unexecute ()
+{
+#if DEBUG_KP_TOOL_CROP
+ kdDebug () << "kpToolCropSetImageCommand::unexecute()" << endl;
+#endif
+
+ viewManager ()->setQueueUpdates ();
+ {
+ document ()->setPixmapAt (m_oldPixmap, QPoint (0, 0));
+ m_oldPixmap.resize (0, 0);
+
+ #if DEBUG_KP_TOOL_CROP
+ kdDebug () << "\tsel: rect=" << m_fromSelection.boundingRect ()
+ << " pm=" << m_fromSelection.pixmap ()
+ << endl;
+ #endif
+ document ()->setSelection (m_fromSelection);
+
+ if (mainWindow ()->tool ())
+ m_mainWindow->tool ()->somethingBelowTheCursorChanged ();
+ }
+ viewManager ()->restoreQueueUpdates ();
+}
+
+
+//
+// kpToolCropCommand
+//
+
+
+class kpToolCropCommand : public kpMacroCommand
+{
+public:
+ kpToolCropCommand (kpMainWindow *mainWindow);
+ virtual ~kpToolCropCommand ();
+};
+
+
+kpToolCropCommand::kpToolCropCommand (kpMainWindow *mainWindow)
+ : kpMacroCommand (i18n ("Set as Image"), mainWindow)
+{
+#if DEBUG_KP_TOOL_CROP
+ kdDebug () << "kpToolCropCommand::<ctor>()" << endl;
+#endif
+
+ if (!mainWindow ||
+ !mainWindow->document () ||
+ !mainWindow->document ()->selection ())
+ {
+ kdError () << "kpToolCropCommand::kpToolCropCommand() without sel" << endl;
+ return;
+ }
+
+ kpSelection *sel = mainWindow->document ()->selection ();
+
+
+#if DEBUG_KP_TOOL_CROP
+ kdDebug () << "\tsel: w=" << sel->width ()
+ << " h=" << sel->height ()
+ << " <- resizing doc to these dimen" << endl;
+#endif
+
+ // (must resize doc _before_ kpToolCropSetImageCommand in case doc
+ // needs to gets bigger - else pasted down pixmap may not fit)
+ addCommand (
+ new kpToolResizeScaleCommand (
+ false/*act on doc, not sel*/,
+ sel->width (), sel->height (),
+ kpToolResizeScaleCommand::Resize,
+ mainWindow));
+
+
+ if (sel->isText ())
+ {
+ #if DEBUG_KP_TOOL_CROP
+ kdDebug () << "\tisText" << endl;
+ kdDebug () << "\tclearing doc with trans cmd" << endl;
+ #endif
+ addCommand (
+ new kpToolClearCommand (
+ false/*act on doc*/,
+ kpColor::transparent,
+ mainWindow));
+
+ #if DEBUG_KP_TOOL_CROP
+ kdDebug () << "\tmoving sel to (0,0) cmd" << endl;
+ #endif
+ kpToolSelectionMoveCommand *moveCmd =
+ new kpToolSelectionMoveCommand (
+ QString::null/*uninteresting child of macro cmd*/,
+ mainWindow);
+ moveCmd->moveTo (QPoint (0, 0), true/*move on exec, not now*/);
+ moveCmd->finalize ();
+ addCommand (moveCmd);
+ }
+ else
+ {
+ #if DEBUG_KP_TOOL_CROP
+ kdDebug () << "\tis pixmap sel" << endl;
+ kdDebug () << "\tcreating SetImage cmd" << endl;
+ #endif
+ addCommand (new kpToolCropSetImageCommand (mainWindow));
+
+ #if 0
+ addCommand (
+ new kpToolSelectionCreateCommand (
+ QString::null/*uninteresting child of macro cmd*/,
+ selectionBorderAndMovedTo0_0 (*sel),
+ mainWindow));
+ #endif
+ }
+}
+
+kpToolCropCommand::~kpToolCropCommand ()
+{
+}
+
+
+void kpToolCrop (kpMainWindow *mainWindow)
+{
+ kpDocument *doc = mainWindow->document ();
+ if (!doc)
+ return;
+
+ kpSelection *sel = doc ? doc->selection () : 0;
+ if (!sel)
+ return;
+
+
+ bool selWasText = sel->isText ();
+ kpSelection borderSel = selectionBorderAndMovedTo0_0 (*sel);
+
+
+ mainWindow->addImageOrSelectionCommand (
+ new kpToolCropCommand (mainWindow),
+ true/*add create cmd*/,
+ false/*don't add pull cmd*/);
+
+
+ if (!selWasText)
+ {
+ mainWindow->commandHistory ()->addCommand (
+ new kpToolSelectionCreateCommand (
+ i18n ("Selection: Create"),
+ borderSel,
+ mainWindow));
+ }
+}
diff --git a/kolourpaint/tools/kptoolcrop.h b/kolourpaint/tools/kptoolcrop.h
new file mode 100644
index 00000000..c710a041
--- /dev/null
+++ b/kolourpaint/tools/kptoolcrop.h
@@ -0,0 +1,39 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef KP_TOOL_CROP_H
+#define KP_TOOL_CROP_H
+
+
+class kpMainWindow;
+
+
+void kpToolCrop (kpMainWindow *mainWindow);
+
+
+#endif // KP_TOOL_CROP_H
diff --git a/kolourpaint/tools/kptoolcurve.cpp b/kolourpaint/tools/kptoolcurve.cpp
new file mode 100644
index 00000000..f889c1ba
--- /dev/null
+++ b/kolourpaint/tools/kptoolcurve.cpp
@@ -0,0 +1,47 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <kptoolcurve.h>
+
+#include <klocale.h>
+
+
+kpToolCurve::kpToolCurve (kpMainWindow *mainWindow)
+ : kpToolPolygon (Curve,
+ i18n ("Curve"),
+ i18n ("Draws curves"),
+ Qt::Key_V,
+ mainWindow, "tool_curve")
+{
+}
+
+kpToolCurve::~kpToolCurve ()
+{
+}
+
+#include <kptoolcurve.moc>
diff --git a/kolourpaint/tools/kptoolcurve.h b/kolourpaint/tools/kptoolcurve.h
new file mode 100644
index 00000000..489ce1fb
--- /dev/null
+++ b/kolourpaint/tools/kptoolcurve.h
@@ -0,0 +1,45 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kptoolcurve_h__
+#define __kptoolcurve_h__
+
+#include <kptoolpolygon.h>
+
+class kpMainWindow;
+
+class kpToolCurve : public kpToolPolygon
+{
+Q_OBJECT
+
+public:
+ kpToolCurve (kpMainWindow *mainWindow);
+ virtual ~kpToolCurve ();
+};
+
+#endif // __kptoolcurve_h__
diff --git a/kolourpaint/tools/kptoolellipse.cpp b/kolourpaint/tools/kptoolellipse.cpp
new file mode 100644
index 00000000..f3b31dbb
--- /dev/null
+++ b/kolourpaint/tools/kptoolellipse.cpp
@@ -0,0 +1,45 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <klocale.h>
+#include <kptoolellipse.h>
+
+kpToolEllipse::kpToolEllipse (kpMainWindow *mainWindow)
+ : kpToolRectangle (Ellipse,
+ i18n ("Ellipse"),
+ i18n ("Draws ellipses and circles"),
+ Qt::Key_E,
+ mainWindow, "tool_ellipse")
+{
+}
+
+kpToolEllipse::~kpToolEllipse ()
+{
+}
+
+#include <kptoolellipse.moc>
diff --git a/kolourpaint/tools/kptoolellipse.h b/kolourpaint/tools/kptoolellipse.h
new file mode 100644
index 00000000..fc9bf798
--- /dev/null
+++ b/kolourpaint/tools/kptoolellipse.h
@@ -0,0 +1,45 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kptoolellipse_h__
+#define __kptoolellipse_h__
+
+#include <kptoolrectangle.h>
+
+class kpMainWindow;
+
+class kpToolEllipse : public kpToolRectangle
+{
+Q_OBJECT
+
+public:
+ kpToolEllipse (kpMainWindow *);
+ virtual ~kpToolEllipse ();
+};
+
+#endif // __kptoolellipse_h__
diff --git a/kolourpaint/tools/kptoolellipticalselection.cpp b/kolourpaint/tools/kptoolellipticalselection.cpp
new file mode 100644
index 00000000..13daf799
--- /dev/null
+++ b/kolourpaint/tools/kptoolellipticalselection.cpp
@@ -0,0 +1,46 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <kptoolellipticalselection.h>
+
+#include <klocale.h>
+
+
+kpToolEllipticalSelection::kpToolEllipticalSelection (kpMainWindow *mainWindow)
+ : kpToolSelection (Ellipse,
+ i18n ("Selection (Elliptical)"),
+ i18n ("Makes an elliptical or circular selection"),
+ Qt::Key_I,
+ mainWindow, "tool_elliptical_selection")
+{
+}
+
+kpToolEllipticalSelection::~kpToolEllipticalSelection ()
+{
+}
+
diff --git a/kolourpaint/tools/kptoolellipticalselection.h b/kolourpaint/tools/kptoolellipticalselection.h
new file mode 100644
index 00000000..9dbd643e
--- /dev/null
+++ b/kolourpaint/tools/kptoolellipticalselection.h
@@ -0,0 +1,43 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kptoolellipticalselection_h__
+#define __kptoolellipticalselection_h__
+
+#include <kptoolselection.h>
+
+class kpMainWindow;
+
+class kpToolEllipticalSelection : public kpToolSelection
+{
+public:
+ kpToolEllipticalSelection (kpMainWindow *);
+ virtual ~kpToolEllipticalSelection ();
+};
+
+#endif // __kptoolellipticalselection_h__
diff --git a/kolourpaint/tools/kptooleraser.cpp b/kolourpaint/tools/kptooleraser.cpp
new file mode 100644
index 00000000..1acbf66e
--- /dev/null
+++ b/kolourpaint/tools/kptooleraser.cpp
@@ -0,0 +1,44 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <klocale.h>
+#include <kptooleraser.h>
+
+kpToolEraser::kpToolEraser (kpMainWindow *mainWindow)
+ : kpToolPen (kpToolPen::Eraser,
+ i18n ("Eraser"), i18n ("Lets you rub out mistakes"),
+ Qt::Key_A,
+ mainWindow, "tool_eraser")
+{
+}
+
+kpToolEraser::~kpToolEraser ()
+{
+}
+
+#include <kptooleraser.moc>
diff --git a/kolourpaint/tools/kptooleraser.h b/kolourpaint/tools/kptooleraser.h
new file mode 100644
index 00000000..4dd7704a
--- /dev/null
+++ b/kolourpaint/tools/kptooleraser.h
@@ -0,0 +1,43 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kptooleraser_h__
+#define __kptooleraser_h__
+
+#include <kptoolpen.h>
+
+class kpToolEraser : public kpToolPen
+{
+Q_OBJECT
+
+public:
+ kpToolEraser (kpMainWindow *mainWindow);
+ virtual ~kpToolEraser ();
+};
+
+#endif // __kptooleraser_h__
diff --git a/kolourpaint/tools/kptoolflip.cpp b/kolourpaint/tools/kptoolflip.cpp
new file mode 100644
index 00000000..58eeb66d
--- /dev/null
+++ b/kolourpaint/tools/kptoolflip.cpp
@@ -0,0 +1,213 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <kptoolflip.h>
+
+#include <qapplication.h>
+#include <qradiobutton.h>
+#include <qvbox.h>
+#include <qvbuttongroup.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kpdefs.h>
+#include <kpdocument.h>
+#include <kppixmapfx.h>
+#include <kpselection.h>
+#include <kptool.h>
+#include <kpmainwindow.h>
+
+
+/*
+ * kpToolFlipCommand
+ */
+
+kpToolFlipCommand::kpToolFlipCommand (bool actOnSelection,
+ bool horiz, bool vert,
+ kpMainWindow *mainWindow)
+ : kpCommand (mainWindow),
+ m_actOnSelection (actOnSelection),
+ m_horiz (horiz), m_vert (vert)
+{
+}
+
+kpToolFlipCommand::~kpToolFlipCommand ()
+{
+}
+
+
+// public virtual [base kpCommand]
+QString kpToolFlipCommand::name () const
+{
+ QString opName;
+
+
+#if 1
+ opName = i18n ("Flip");
+#else // re-enable when giving full descriptions for all actions
+ if (m_horiz && m_vert)
+ opName = i18n ("Flip horizontally and vertically");
+ else if (m_horiz)
+ opName = i18n ("Flip horizontally");
+ else if (m_vert)
+ opName = i18n ("Flip vertically");
+ else
+ {
+ kdError () << "kpToolFlipCommand::name() not asked to flip" << endl;
+ return QString::null;
+ }
+#endif
+
+
+ if (m_actOnSelection)
+ return i18n ("Selection: %1").arg (opName);
+ else
+ return opName;
+}
+
+
+// public virtual [base kpCommand]
+int kpToolFlipCommand::size () const
+{
+ return 0;
+}
+
+
+// public virtual [base kpCommand]
+void kpToolFlipCommand::execute ()
+{
+ flip ();
+}
+
+// public virtual [base kpCommand]
+void kpToolFlipCommand::unexecute ()
+{
+ flip ();
+}
+
+
+// private
+void kpToolFlipCommand::flip ()
+{
+ kpDocument *doc = document ();
+ if (!doc)
+ return;
+
+
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+
+ if (m_actOnSelection)
+ {
+ doc->selection ()->flip (m_horiz, m_vert);
+ if (m_mainWindow->tool ())
+ m_mainWindow->tool ()->somethingBelowTheCursorChanged ();
+ }
+ else
+ {
+ QPixmap newPixmap = kpPixmapFX::flip (*doc->pixmap (), m_horiz, m_vert);
+
+ doc->setPixmap (newPixmap);
+ }
+
+
+ QApplication::restoreOverrideCursor ();
+}
+
+
+/*
+ * kpToolFlipDialog
+ */
+
+// private static
+bool kpToolFlipDialog::s_lastIsVerticalFlip = true;
+
+
+kpToolFlipDialog::kpToolFlipDialog (bool actOnSelection, QWidget *parent)
+ : KDialogBase (parent, 0/*name*/, true/*modal*/,
+ actOnSelection ? i18n ("Flip Selection") : i18n ("Flip Image"),
+ KDialogBase::Ok | KDialogBase::Cancel)
+{
+ QVBox *vbox = makeVBoxMainWidget ();
+
+ if (!vbox)
+ {
+ kdError () << "kpToolFlipDialog::kpToolFlipDialog() received NULL vbox" << endl;
+ }
+ else
+ {
+ QVButtonGroup *buttonGroup = new QVButtonGroup (i18n ("Direction"), vbox);
+
+ // I'm sure vert flipping is much more common than horiz flipping so make it come first
+ m_verticalFlipRadioButton = new QRadioButton (i18n ("&Vertical (upside-down)"), buttonGroup);
+ m_horizontalFlipRadioButton = new QRadioButton (i18n ("&Horizontal"), buttonGroup);
+
+ m_verticalFlipRadioButton->setChecked (s_lastIsVerticalFlip);
+ m_horizontalFlipRadioButton->setChecked (!s_lastIsVerticalFlip);
+
+ connect (m_verticalFlipRadioButton, SIGNAL (toggled (bool)),
+ this, SLOT (slotIsVerticalFlipChanged ()));
+ connect (m_horizontalFlipRadioButton, SIGNAL (toggled (bool)),
+ this, SLOT (slotIsVerticalFlipChanged ()));
+ }
+}
+
+kpToolFlipDialog::~kpToolFlipDialog ()
+{
+}
+
+
+// public slot
+void kpToolFlipDialog::slotIsVerticalFlipChanged ()
+{
+ s_lastIsVerticalFlip = m_verticalFlipRadioButton->isChecked ();
+}
+
+
+// public
+bool kpToolFlipDialog::getHorizontalFlip () const
+{
+ return m_horizontalFlipRadioButton->isChecked ();
+}
+
+// public
+bool kpToolFlipDialog::getVerticalFlip () const
+{
+ return m_verticalFlipRadioButton->isChecked ();
+}
+
+// public
+bool kpToolFlipDialog::isNoOp () const
+{
+ return !getHorizontalFlip () && !getVerticalFlip ();
+}
+
+
+#include <kptoolflip.moc>
+
diff --git a/kolourpaint/tools/kptoolflip.h b/kolourpaint/tools/kptoolflip.h
new file mode 100644
index 00000000..c287c320
--- /dev/null
+++ b/kolourpaint/tools/kptoolflip.h
@@ -0,0 +1,88 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kptoolflip_h__
+#define __kptoolflip_h__
+
+#include <kpcommandhistory.h>
+#include <kdialogbase.h>
+
+class QRadioButton;
+class QString;
+
+class kpDocument;
+class kpMainWindow;
+
+
+class kpToolFlipCommand : public kpCommand
+{
+public:
+ kpToolFlipCommand (bool actOnSelection,
+ bool horiz, bool vert,
+ kpMainWindow *mainWindow);
+ virtual ~kpToolFlipCommand ();
+
+ virtual QString name () const;
+
+ virtual int size () const;
+
+ virtual void execute ();
+ virtual void unexecute ();
+
+private:
+ void flip ();
+
+ bool m_actOnSelection;
+ bool m_horiz, m_vert;
+};
+
+
+class kpToolFlipDialog : public KDialogBase
+{
+Q_OBJECT
+
+public:
+ kpToolFlipDialog (bool actOnSelection, QWidget *parent);
+ ~kpToolFlipDialog ();
+
+private:
+ static bool s_lastIsVerticalFlip;
+
+public slots:
+ void slotIsVerticalFlipChanged ();
+
+public:
+ bool getHorizontalFlip () const;
+ bool getVerticalFlip () const;
+ bool isNoOp () const;
+
+private:
+ QRadioButton *m_horizontalFlipRadioButton, *m_verticalFlipRadioButton;
+};
+
+#endif // __kptoolflip_h__
diff --git a/kolourpaint/tools/kptoolfloodfill.cpp b/kolourpaint/tools/kptoolfloodfill.cpp
new file mode 100644
index 00000000..bb17d701
--- /dev/null
+++ b/kolourpaint/tools/kptoolfloodfill.cpp
@@ -0,0 +1,261 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define DEBUG_KP_TOOL_FLOOD_FILL 0
+
+
+#include <kptoolfloodfill.h>
+
+#include <qapplication.h>
+#include <qcursor.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kpcommandhistory.h>
+#include <kpdefs.h>
+#include <kpdocument.h>
+#include <kpmainwindow.h>
+#include <kpview.h>
+#include <kpviewmanager.h>
+
+
+/*
+ * kpToolFloodFill
+ */
+
+kpToolFloodFill::kpToolFloodFill (kpMainWindow *mainWindow)
+ : kpTool (i18n ("Flood Fill"), i18n ("Fills regions in the image"),
+ Qt::Key_F,
+ mainWindow, "tool_flood_fill"),
+ m_currentCommand (0)
+{
+}
+
+kpToolFloodFill::~kpToolFloodFill ()
+{
+}
+
+QString kpToolFloodFill::haventBegunDrawUserMessage () const
+{
+ return i18n ("Click to fill a region.");
+}
+
+void kpToolFloodFill::begin ()
+{
+ setUserMessage (haventBegunDrawUserMessage ());
+}
+
+// virtual
+void kpToolFloodFill::beginDraw ()
+{
+#if DEBUG_KP_TOOL_FLOOD_FILL && 1
+ kdDebug () << "kpToolFloodFill::beginDraw()" << endl;
+#endif
+
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+ // Flood Fill is an expensive CPU operation so we only fill at a
+ // mouse click (beginDraw ()), not on mouse move (virtually draw())
+ m_currentCommand = new kpToolFloodFillCommand (m_currentPoint.x (), m_currentPoint.y (),
+ color (m_mouseButton), processedColorSimilarity (),
+ mainWindow ());
+
+ if (m_currentCommand->prepareColorToChange ())
+ {
+ #if DEBUG_KP_TOOL_FLOOD_FILL && 1
+ kdDebug () << "\tperforming new-doc-corner-case check" << endl;
+ #endif
+ if (document ()->url ().isEmpty () && !document ()->isModified ())
+ {
+ m_currentCommand->setFillEntirePixmap ();
+ m_currentCommand->execute ();
+ }
+ else if (m_currentCommand->prepare ())
+ {
+ m_currentCommand->execute ();
+ }
+ else
+ {
+ kdError () << "kpToolFloodFill::beginDraw() could not fill!" << endl;
+ }
+ }
+ else
+ {
+ kdError () << "kpToolFloodFill::beginDraw() could not prepareColorToChange!" << endl;
+ }
+
+ QApplication::restoreOverrideCursor ();
+
+ setUserMessage (cancelUserMessage ());
+}
+
+// virtual
+void kpToolFloodFill::draw (const QPoint &thisPoint, const QPoint &, const QRect &)
+{
+ setUserShapePoints (thisPoint);
+}
+
+// virtual
+void kpToolFloodFill::cancelShape ()
+{
+#if 0
+ endDraw (QPoint (), QRect ());
+ mainWindow ()->commandHistory ()->undo ();
+#else
+ m_currentCommand->unexecute ();
+
+ delete m_currentCommand;
+ m_currentCommand = 0;
+#endif
+
+ setUserMessage (i18n ("Let go of all the mouse buttons."));
+}
+
+void kpToolFloodFill::releasedAllButtons ()
+{
+ setUserMessage (haventBegunDrawUserMessage ());
+}
+
+// virtual
+void kpToolFloodFill::endDraw (const QPoint &, const QRect &)
+{
+ mainWindow ()->commandHistory ()->addCommand (m_currentCommand,
+ false /* no exec - we already did it up there */);
+
+ // don't delete
+ m_currentCommand = 0;
+ setUserMessage (haventBegunDrawUserMessage ());
+}
+
+
+/*
+ * kpToolFloodFillCommand
+ */
+
+kpToolFloodFillCommand::kpToolFloodFillCommand (int x, int y,
+ const kpColor &color, int processedColorSimilarity,
+ kpMainWindow *mainWindow)
+ : kpCommand (mainWindow),
+ kpFloodFill (document ()->pixmap (), x, y, color, processedColorSimilarity),
+ m_fillEntirePixmap (false)
+{
+}
+
+kpToolFloodFillCommand::~kpToolFloodFillCommand ()
+{
+}
+
+
+// public virtual [base kpCommand]
+QString kpToolFloodFillCommand::name () const
+{
+ return i18n ("Flood Fill");
+}
+
+// public virtual [base kpCommand]
+int kpToolFloodFillCommand::size () const
+{
+ return kpFloodFill::size () + kpPixmapFX::pixmapSize (m_oldPixmap);
+}
+
+
+void kpToolFloodFillCommand::setFillEntirePixmap (bool yes)
+{
+ m_fillEntirePixmap = yes;
+}
+
+
+// virtual
+void kpToolFloodFillCommand::execute ()
+{
+#if DEBUG_KP_TOOL_FLOOD_FILL && 1
+ kdDebug () << "kpToolFloodFillCommand::execute() m_fillEntirePixmap=" << m_fillEntirePixmap << endl;
+#endif
+
+ kpDocument *doc = document ();
+ if (!doc)
+ return;
+
+
+ if (m_fillEntirePixmap)
+ {
+ doc->fill (kpFloodFill::color ());
+ }
+ else
+ {
+ QRect rect = kpFloodFill::boundingRect ();
+ if (rect.isValid ())
+ {
+ QApplication::setOverrideCursor (QCursor::waitCursor);
+
+ m_oldPixmap = doc->getPixmapAt (rect);
+
+ kpFloodFill::fill ();
+ doc->slotContentsChanged (rect);
+
+ QApplication::restoreOverrideCursor ();
+ }
+ else
+ {
+ #if DEBUG_KP_TOOL_FLOOD_FILL && 1
+ kdDebug () << "\tinvalid boundingRect - must be NOP case" << endl;
+ #endif
+ }
+ }
+}
+
+// virtual
+void kpToolFloodFillCommand::unexecute ()
+{
+ kpDocument *doc = document ();
+ if (!doc)
+ return;
+
+
+ if (m_fillEntirePixmap)
+ {
+ doc->fill (kpFloodFill::colorToChange ());
+ }
+ else
+ {
+ QRect rect = kpFloodFill::boundingRect ();
+ if (rect.isValid ())
+ {
+ doc->setPixmapAt (m_oldPixmap, rect.topLeft ());
+
+ m_oldPixmap.resize (0, 0);
+
+ doc->slotContentsChanged (rect);
+ }
+ }
+}
+
+#include <kptoolfloodfill.moc>
diff --git a/kolourpaint/tools/kptoolfloodfill.h b/kolourpaint/tools/kptoolfloodfill.h
new file mode 100644
index 00000000..a2eeaa5a
--- /dev/null
+++ b/kolourpaint/tools/kptoolfloodfill.h
@@ -0,0 +1,94 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kptoolfloodfill_h__
+#define __kptoolfloodfill_h__
+
+#include <qpixmap.h>
+
+#include <kpcommandhistory.h>
+
+#include <kpfloodfill.h>
+#include <kptool.h>
+
+
+class QString;
+
+class kpColor;
+
+class kpMainWindow;
+class kpToolFloodFillCommand;
+
+
+class kpToolFloodFill : public kpTool
+{
+Q_OBJECT
+
+public:
+ kpToolFloodFill (kpMainWindow *);
+ virtual ~kpToolFloodFill ();
+
+private:
+ QString haventBegunDrawUserMessage () const;
+
+public:
+ virtual void begin ();
+ virtual void beginDraw ();
+ virtual void draw (const QPoint &thisPoint, const QPoint &, const QRect &);
+ virtual void cancelShape ();
+ virtual void releasedAllButtons ();
+ virtual void endDraw (const QPoint &, const QRect &);
+
+private:
+ kpToolFloodFillCommand *m_currentCommand;
+};
+
+
+class kpToolFloodFillCommand : public kpCommand, public kpFloodFill
+{
+public:
+ kpToolFloodFillCommand (int x, int y,
+ const kpColor &color, int processedColorSimilarity,
+ kpMainWindow *mainWindow);
+ virtual ~kpToolFloodFillCommand ();
+
+ virtual QString name () const;
+
+ virtual int size () const;
+
+ void setFillEntirePixmap (bool yes = true);
+
+ virtual void execute ();
+ virtual void unexecute ();
+
+private:
+ QPixmap m_oldPixmap;
+ bool m_fillEntirePixmap;
+};
+
+#endif // __kptoolfloodfill_h__
diff --git a/kolourpaint/tools/kptoolfreeformselection.cpp b/kolourpaint/tools/kptoolfreeformselection.cpp
new file mode 100644
index 00000000..7c736728
--- /dev/null
+++ b/kolourpaint/tools/kptoolfreeformselection.cpp
@@ -0,0 +1,46 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <kptoolfreeformselection.h>
+
+#include <klocale.h>
+
+
+kpToolFreeFormSelection::kpToolFreeFormSelection (kpMainWindow *mainWindow)
+ : kpToolSelection (kpToolSelection::FreeForm,
+ i18n ("Selection (Free-Form)"),
+ i18n ("Makes a free-form selection"),
+ Qt::Key_M,
+ mainWindow, "tool_free_form_selection")
+{
+}
+
+kpToolFreeFormSelection::~kpToolFreeFormSelection ()
+{
+}
+
diff --git a/kolourpaint/tools/kptoolfreeformselection.h b/kolourpaint/tools/kptoolfreeformselection.h
new file mode 100644
index 00000000..28f1e5ec
--- /dev/null
+++ b/kolourpaint/tools/kptoolfreeformselection.h
@@ -0,0 +1,43 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kptoolfreeformselection_h__
+#define __kptoolfreeformselection_h__
+
+#include <kptoolselection.h>
+
+class kpMainWindow;
+
+class kpToolFreeFormSelection : public kpToolSelection
+{
+public:
+ kpToolFreeFormSelection (kpMainWindow *);
+ virtual ~kpToolFreeFormSelection ();
+};
+
+#endif // __kptoolfreeformselection_h__
diff --git a/kolourpaint/tools/kptoolline.cpp b/kolourpaint/tools/kptoolline.cpp
new file mode 100644
index 00000000..809824d9
--- /dev/null
+++ b/kolourpaint/tools/kptoolline.cpp
@@ -0,0 +1,47 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <kptoolline.h>
+
+#include <klocale.h>
+
+
+kpToolLine::kpToolLine (kpMainWindow *mainWindow)
+ : kpToolPolygon (Line,
+ i18n ("Line"),
+ i18n ("Draws lines"),
+ Qt::Key_L,
+ mainWindow, "tool_line")
+{
+}
+
+kpToolLine::~kpToolLine ()
+{
+}
+
+#include <kptoolline.moc>
diff --git a/kolourpaint/tools/kptoolline.h b/kolourpaint/tools/kptoolline.h
new file mode 100644
index 00000000..7a956245
--- /dev/null
+++ b/kolourpaint/tools/kptoolline.h
@@ -0,0 +1,45 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kptoolline_h__
+#define __kptoolline_h__
+
+#include <kptoolpolygon.h>
+
+class kpMainWindow;
+
+class kpToolLine : public kpToolPolygon
+{
+Q_OBJECT
+
+public:
+ kpToolLine (kpMainWindow *);
+ virtual ~kpToolLine ();
+};
+
+#endif // __kptoolline_h__
diff --git a/kolourpaint/tools/kptoolpen.cpp b/kolourpaint/tools/kptoolpen.cpp
new file mode 100644
index 00000000..eb731ceb
--- /dev/null
+++ b/kolourpaint/tools/kptoolpen.cpp
@@ -0,0 +1,1145 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define DEBUG_KP_TOOL_PEN 0
+
+#include <qapplication.h>
+#include <qbitmap.h>
+#include <qcursor.h>
+#include <qimage.h>
+#include <qpainter.h>
+#if DEBUG_KP_TOOL_PEN
+ #include <qdatetime.h>
+#endif
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kpcolor.h>
+#include <kpcommandhistory.h>
+#include <kpcursorprovider.h>
+#include <kptoolpen.h>
+#include <kpdefs.h>
+#include <kpdocument.h>
+#include <kpmainwindow.h>
+#include <kppixmapfx.h>
+#include <kptemppixmap.h>
+#include <kptoolclear.h>
+#include <kptooltoolbar.h>
+#include <kptoolwidgetbrush.h>
+#include <kptoolwidgeterasersize.h>
+#include <kpviewmanager.h>
+
+/*
+ * kpToolPen
+ */
+
+kpToolPen::kpToolPen (Mode mode,
+ const QString &text, const QString &description,
+ int key,
+ kpMainWindow *mainWindow, const char *name)
+ : kpTool (text, description, key, mainWindow, name),
+ m_mode (mode),
+ m_toolWidgetBrush (0),
+ m_toolWidgetEraserSize (0),
+ m_currentCommand (0)
+{
+}
+
+kpToolPen::kpToolPen (kpMainWindow *mainWindow)
+ : kpTool (i18n ("Pen"), i18n ("Draws dots and freehand strokes"),
+ Qt::Key_P,
+ mainWindow, "tool_pen"),
+ m_mode (Pen),
+ m_toolWidgetBrush (0),
+ m_toolWidgetEraserSize (0),
+ m_currentCommand (0)
+{
+}
+
+void kpToolPen::setMode (Mode mode)
+{
+ int usesPixmaps = (mode & (DrawsPixmaps | WashesPixmaps));
+ int usesBrushes = (mode & (SquareBrushes | DiverseBrushes));
+
+ if ((usesPixmaps && !usesBrushes) ||
+ (usesBrushes && !usesPixmaps))
+ {
+ kdError () << "kpToolPen::setMode() passed invalid mode" << endl;
+ return;
+ }
+
+ m_mode = mode;
+}
+
+kpToolPen::~kpToolPen ()
+{
+}
+
+
+// private
+QString kpToolPen::haventBegunDrawUserMessage () const
+{
+ switch (m_mode)
+ {
+ case Pen:
+ case Brush:
+ return i18n ("Click to draw dots or drag to draw strokes.");
+ return i18n ("Click to draw dots or drag to draw strokes.");
+ case Eraser:
+ return i18n ("Click or drag to erase.");
+ case ColorWasher:
+ return i18n ("Click or drag to erase pixels of the foreground color.");
+ default:
+ return QString::null;
+ }
+}
+
+// virtual
+void kpToolPen::begin ()
+{
+ m_toolWidgetBrush = 0;
+ m_brushIsDiagonalLine = false;
+
+ kpToolToolBar *tb = toolToolBar ();
+ if (!tb)
+ return;
+
+ if (m_mode & SquareBrushes)
+ {
+ m_toolWidgetEraserSize = tb->toolWidgetEraserSize ();
+ connect (m_toolWidgetEraserSize, SIGNAL (eraserSizeChanged (int)),
+ this, SLOT (slotEraserSizeChanged (int)));
+ m_toolWidgetEraserSize->show ();
+
+ slotEraserSizeChanged (m_toolWidgetEraserSize->eraserSize ());
+
+ viewManager ()->setCursor (kpCursorProvider::lightCross ());
+ }
+
+ if (m_mode & DiverseBrushes)
+ {
+ m_toolWidgetBrush = tb->toolWidgetBrush ();
+ connect (m_toolWidgetBrush, SIGNAL (brushChanged (const QPixmap &, bool)),
+ this, SLOT (slotBrushChanged (const QPixmap &, bool)));
+ m_toolWidgetBrush->show ();
+
+ slotBrushChanged (m_toolWidgetBrush->brush (),
+ m_toolWidgetBrush->brushIsDiagonalLine ());
+
+ viewManager ()->setCursor (kpCursorProvider::lightCross ());
+ }
+
+ setUserMessage (haventBegunDrawUserMessage ());
+}
+
+// virtual
+void kpToolPen::end ()
+{
+ if (m_toolWidgetEraserSize)
+ {
+ disconnect (m_toolWidgetEraserSize, SIGNAL (eraserSizeChanged (int)),
+ this, SLOT (slotEraserSizeChanged (int)));
+ m_toolWidgetEraserSize = 0;
+ }
+
+ if (m_toolWidgetBrush)
+ {
+ disconnect (m_toolWidgetBrush, SIGNAL (brushChanged (const QPixmap &, bool)),
+ this, SLOT (slotBrushChanged (const QPixmap &, bool)));
+ m_toolWidgetBrush = 0;
+ }
+
+ kpViewManager *vm = viewManager ();
+ if (vm)
+ {
+ if (vm->tempPixmap () && vm->tempPixmap ()->isBrush ())
+ vm->invalidateTempPixmap ();
+
+ if (m_mode & (SquareBrushes | DiverseBrushes))
+ vm->unsetCursor ();
+ }
+
+ // save memory
+ for (int i = 0; i < 2; i++)
+ m_brushPixmap [i].resize (0, 0);
+ m_cursorPixmap.resize (0, 0);
+}
+
+// virtual
+void kpToolPen::beginDraw ()
+{
+ switch (m_mode)
+ {
+ case Pen:
+ m_currentCommand = new kpToolPenCommand (i18n ("Pen"), mainWindow ());
+ break;
+ case Brush:
+ m_currentCommand = new kpToolPenCommand (i18n ("Brush"), mainWindow ());
+ break;
+ case Eraser:
+ m_currentCommand = new kpToolPenCommand (i18n ("Eraser"), mainWindow ());
+ break;
+ case ColorWasher:
+ m_currentCommand = new kpToolPenCommand (i18n ("Color Eraser"), mainWindow ());
+ break;
+
+ default:
+ m_currentCommand = new kpToolPenCommand (i18n ("Custom Pen or Brush"), mainWindow ());
+ break;
+ }
+
+ // we normally show the Brush pix in the foreground colour but if the
+ // user starts drawing in the background color, we don't want to leave
+ // the cursor in the foreground colour -- just hide it in all cases
+ // to avoid confusion
+ viewManager ()->invalidateTempPixmap ();
+
+ setUserMessage (cancelUserMessage ());
+}
+
+// virtual
+void kpToolPen::hover (const QPoint &point)
+{
+#if DEBUG_KP_TOOL_PEN && 0
+ kdDebug () << "kpToolPen::hover(" << point << ")"
+ << " hasBegun=" << hasBegun ()
+ << " hasBegunDraw=" << hasBegunDraw ()
+ << " cursorPixmap.isNull=" << m_cursorPixmap.isNull ()
+ << endl;
+#endif
+ if (point != KP_INVALID_POINT && !m_cursorPixmap.isNull ())
+ {
+ // (for hotPoint() as m_mouseButton is not normally defined in hover())
+ m_mouseButton = 0;
+
+ kpTempPixmap::RenderMode renderMode;
+ QPixmap cursorPixmapForTempPixmap = m_cursorPixmap;
+
+ if (m_mode & SquareBrushes)
+ renderMode = kpTempPixmap::SetPixmap;
+ else if (m_mode & DiverseBrushes)
+ {
+ if (color (0).isOpaque ())
+ renderMode = kpTempPixmap::PaintPixmap;
+ else
+ {
+ renderMode = kpTempPixmap::PaintMaskTransparentWithBrush;
+ cursorPixmapForTempPixmap = kpPixmapFX::getNonNullMask (m_cursorPixmap);
+ }
+ }
+
+ viewManager ()->setFastUpdates ();
+
+ viewManager ()->setTempPixmap (
+ kpTempPixmap (true/*brush*/,
+ renderMode,
+ hotPoint (),
+ cursorPixmapForTempPixmap));
+
+ viewManager ()->restoreFastUpdates ();
+ }
+
+#if DEBUG_KP_TOOL_PEN && 0
+ if (document ()->rect ().contains (point))
+ {
+ QImage image = kpPixmapFX::convertToImage (*document ()->pixmap ());
+
+ QRgb v = image.pixel (point.x (), point.y ());
+ kdDebug () << "(" << point << "): r=" << qRed (v)
+ << " g=" << qGreen (v)
+ << " b=" << qBlue (v)
+ << " a=" << qAlpha (v)
+ << endl;
+ }
+#endif
+
+ setUserShapePoints (point);
+}
+
+bool kpToolPen::wash (QPainter *painter, QPainter *maskPainter,
+ const QImage &image,
+ const kpColor &colorToReplace,
+ const QRect &imageRect, int plotx, int ploty)
+{
+ return wash (painter, maskPainter, image, colorToReplace, imageRect, hotRect (plotx, ploty));
+}
+
+bool kpToolPen::wash (QPainter *painter, QPainter *maskPainter,
+ const QImage &image,
+ const kpColor &colorToReplace,
+ const QRect &imageRect, const QRect &drawRect)
+{
+ bool didSomething = false;
+
+#if DEBUG_KP_TOOL_PEN && 0
+ kdDebug () << "kpToolPen::wash(imageRect=" << imageRect
+ << ",drawRect=" << drawRect
+ << ")" << endl;
+#endif
+
+// make use of scanline coherence
+#define FLUSH_LINE() \
+{ \
+ if (painter && painter->isActive ()) \
+ painter->drawLine (startDrawX, y, x - 1, y); \
+ if (maskPainter && maskPainter->isActive ()) \
+ maskPainter->drawLine (startDrawX, y, x - 1, y); \
+ didSomething = true; \
+ startDrawX = -1; \
+}
+
+ const int maxY = drawRect.bottom () - imageRect.top ();
+
+ const int minX = drawRect.left () - imageRect.left ();
+ const int maxX = drawRect.right () - imageRect.left ();
+
+ for (int y = drawRect.top () - imageRect.top ();
+ y <= maxY;
+ y++)
+ {
+ int startDrawX = -1;
+
+ int x; // for FLUSH_LINE()
+ for (x = minX; x <= maxX; x++)
+ {
+ #if DEBUG_KP_TOOL_PEN && 0
+ fprintf (stderr, "y=%i x=%i colorAtPixel=%08X colorToReplace=%08X ... ",
+ y, x,
+ kpPixmapFX::getColorAtPixel (image, QPoint (x, y)).toQRgb (),
+ colorToReplace.toQRgb ());
+ #endif
+ if (kpPixmapFX::getColorAtPixel (image, QPoint (x, y)).isSimilarTo (colorToReplace, processedColorSimilarity ()))
+ {
+ #if DEBUG_KP_TOOL_PEN && 0
+ fprintf (stderr, "similar\n");
+ #endif
+ if (startDrawX < 0)
+ startDrawX = x;
+ }
+ else
+ {
+ #if DEBUG_KP_TOOL_PEN && 0
+ fprintf (stderr, "different\n");
+ #endif
+ if (startDrawX >= 0)
+ FLUSH_LINE ();
+ }
+ }
+
+ if (startDrawX >= 0)
+ FLUSH_LINE ();
+ }
+
+#undef FLUSH_LINE
+
+ return didSomething;
+}
+
+// virtual
+void kpToolPen::globalDraw ()
+{
+ // it's easiest to reimplement globalDraw() here rather than in
+ // all the relevant subclasses
+
+ if (m_mode == Eraser)
+ {
+ #if DEBUG_KP_TOOL_PEN
+ kdDebug () << "kpToolPen::globalDraw() eraser" << endl;
+ #endif
+ mainWindow ()->commandHistory ()->addCommand (
+ new kpToolClearCommand (false/*act on doc, not sel*/, mainWindow ()));
+ }
+ else if (m_mode == ColorWasher)
+ {
+ #if DEBUG_KP_TOOL_PEN
+ kdDebug () << "kpToolPen::globalDraw() colour eraser" << endl;
+ #endif
+ if (foregroundColor () == backgroundColor () && processedColorSimilarity () == 0)
+ return;
+
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+ kpToolPenCommand *cmd = new kpToolPenCommand (
+ i18n ("Color Eraser"), mainWindow ());
+
+ QPainter painter, maskPainter;
+ QBitmap maskBitmap;
+
+ if (backgroundColor ().isOpaque ())
+ {
+ painter.begin (document ()->pixmap ());
+ painter.setPen (backgroundColor ().toQColor ());
+ }
+
+ if (backgroundColor ().isTransparent () ||
+ document ()->pixmap ()->mask ())
+ {
+ maskBitmap = kpPixmapFX::getNonNullMask (*document ()->pixmap ());
+ maskPainter.begin (&maskBitmap);
+
+ maskPainter.setPen (backgroundColor ().maskColor ());
+ }
+
+ const QImage image = kpPixmapFX::convertToImage (*document ()->pixmap ());
+ QRect rect = document ()->rect ();
+
+ const bool didSomething = wash (&painter, &maskPainter, image,
+ foregroundColor ()/*replace foreground*/,
+ rect, rect);
+
+ // flush
+ if (painter.isActive ())
+ painter.end ();
+
+ if (maskPainter.isActive ())
+ maskPainter.end ();
+
+ if (didSomething)
+ {
+ if (!maskBitmap.isNull ())
+ document ()->pixmap ()->setMask (maskBitmap);
+
+
+ document ()->slotContentsChanged (rect);
+
+
+ cmd->updateBoundingRect (rect);
+ cmd->finalize ();
+
+ mainWindow ()->commandHistory ()->addCommand (cmd, false /* don't exec */);
+
+ // don't delete - it's up to the commandHistory
+ cmd = 0;
+ }
+ else
+ {
+ #if DEBUG_KP_TOOL_PEN
+ kdDebug () << "\tisNOP" << endl;
+ #endif
+ delete cmd;
+ cmd = 0;
+ }
+
+ QApplication::restoreOverrideCursor ();
+ }
+}
+
+// virtual
+// TODO: refactor!
+void kpToolPen::draw (const QPoint &thisPoint, const QPoint &lastPoint, const QRect &)
+{
+ if ((m_mode & WashesPixmaps) && (foregroundColor () == backgroundColor ()) && processedColorSimilarity () == 0)
+ return;
+
+ // sync: remember to restoreFastUpdates() in all exit paths
+ viewManager ()->setFastUpdates ();
+
+ if (m_brushIsDiagonalLine ? currentPointCardinallyNextToLast () : currentPointNextToLast ())
+ {
+ if (m_mode & DrawsPixels)
+ {
+ QPixmap pixmap (1, 1);
+
+ const kpColor c = color (m_mouseButton);
+
+ // OPT: this seems hopelessly inefficient
+ if (c.isOpaque ())
+ {
+ pixmap.fill (c.toQColor ());
+ }
+ else
+ {
+ QBitmap mask (1, 1);
+ mask.fill (Qt::color0/*transparent*/);
+
+ pixmap.setMask (mask);
+ }
+
+ // draw onto doc
+ document ()->setPixmapAt (pixmap, thisPoint);
+
+ m_currentCommand->updateBoundingRect (thisPoint);
+ }
+ // Brush & Eraser
+ else if (m_mode & DrawsPixmaps)
+ {
+ if (color (m_mouseButton).isOpaque ())
+ document ()->paintPixmapAt (m_brushPixmap [m_mouseButton], hotPoint ());
+ else
+ {
+ kpPixmapFX::paintMaskTransparentWithBrush (document ()->pixmap (),
+ hotPoint (),
+ kpPixmapFX::getNonNullMask (m_brushPixmap [m_mouseButton]));
+ document ()->slotContentsChanged (hotRect ());
+ }
+
+ m_currentCommand->updateBoundingRect (hotRect ());
+ }
+ else if (m_mode & WashesPixmaps)
+ {
+ #if DEBUG_KP_TOOL_PEN
+ kdDebug () << "Washing pixmap (immediate)" << endl;
+ QTime timer;
+ #endif
+ QRect rect = hotRect ();
+ #if DEBUG_KP_TOOL_PEN
+ timer.start ();
+ #endif
+ QPixmap pixmap = document ()->getPixmapAt (rect);
+ #if DEBUG_KP_TOOL_PEN
+ kdDebug () << "\tget from doc: " << timer.restart () << "ms" << endl;
+ #endif
+ const QImage image = kpPixmapFX::convertToImage (pixmap);
+ #if DEBUG_KP_TOOL_PEN
+ kdDebug () << "\tconvert to image: " << timer.restart () << "ms" << endl;
+ #endif
+ QPainter painter, maskPainter;
+ QBitmap maskBitmap;
+
+ if (color (m_mouseButton).isOpaque ())
+ {
+ painter.begin (&pixmap);
+ painter.setPen (color (m_mouseButton).toQColor ());
+ }
+
+ if (color (m_mouseButton).isTransparent () ||
+ pixmap.mask ())
+ {
+ maskBitmap = kpPixmapFX::getNonNullMask (pixmap);
+ maskPainter.begin (&maskBitmap);
+ maskPainter.setPen (color (m_mouseButton).maskColor ());
+ }
+
+ bool didSomething = wash (&painter, &maskPainter,
+ image,
+ color (1 - m_mouseButton)/*color to replace*/,
+ rect, rect);
+
+ if (painter.isActive ())
+ painter.end ();
+
+ if (maskPainter.isActive ())
+ maskPainter.end ();
+
+ if (didSomething)
+ {
+ if (!maskBitmap.isNull ())
+ pixmap.setMask (maskBitmap);
+
+ #if DEBUG_KP_TOOL_PEN
+ kdDebug () << "\twashed: " << timer.restart () << "ms" << endl;
+ #endif
+ document ()->setPixmapAt (pixmap, hotPoint ());
+ #if DEBUG_KP_TOOL_PEN
+ kdDebug () << "\tset doc: " << timer.restart () << "ms" << endl;
+ #endif
+ m_currentCommand->updateBoundingRect (hotRect ());
+ #if DEBUG_KP_TOOL_PEN
+ kdDebug () << "\tupdate boundingRect: " << timer.restart () << "ms" << endl;
+ kdDebug () << "\tdone" << endl;
+ #endif
+ }
+
+ #if DEBUG_KP_TOOL_PEN && 1
+ kdDebug () << endl;
+ #endif
+ }
+ }
+ // in reality, the system is too slow to give us all the MouseMove events
+ // so we "interpolate" the missing points :)
+ else
+ {
+ // find bounding rectangle
+ QRect rect = QRect (thisPoint, lastPoint).normalize ();
+ if (m_mode != DrawsPixels)
+ rect = neededRect (rect, m_brushPixmap [m_mouseButton].width ());
+
+ #if DEBUG_KP_TOOL_PEN
+ if (m_mode & WashesPixmaps)
+ {
+ kdDebug () << "Washing pixmap (w=" << rect.width ()
+ << ",h=" << rect.height () << ")" << endl;
+ }
+ QTime timer;
+ int convAndWashTime;
+ #endif
+
+ const kpColor c = color (m_mouseButton);
+ bool transparent = c.isTransparent ();
+
+ QPixmap pixmap = document ()->getPixmapAt (rect);
+ QBitmap maskBitmap;
+
+ QPainter painter, maskPainter;
+
+ if (m_mode & (DrawsPixels | WashesPixmaps))
+ {
+ if (!transparent)
+ {
+ painter.begin (&pixmap);
+ painter.setPen (c.toQColor ());
+ }
+
+ if (transparent || pixmap.mask ())
+ {
+ maskBitmap = kpPixmapFX::getNonNullMask (pixmap);
+ maskPainter.begin (&maskBitmap);
+ maskPainter.setPen (c.maskColor ());
+ }
+ }
+
+ QImage image;
+ if (m_mode & WashesPixmaps)
+ {
+ #if DEBUG_KP_TOOL_PEN
+ timer.start ();
+ #endif
+ image = kpPixmapFX::convertToImage (pixmap);
+ #if DEBUG_KP_TOOL_PEN
+ convAndWashTime = timer.restart ();
+ kdDebug () << "\tconvert to image: " << convAndWashTime << " ms" << endl;
+ #endif
+ }
+
+ bool didSomething = false;
+
+ if (m_mode & DrawsPixels)
+ {
+ QPoint sp = lastPoint - rect.topLeft (), ep = thisPoint - rect.topLeft ();
+ if (painter.isActive ())
+ painter.drawLine (sp, ep);
+
+ if (maskPainter.isActive ())
+ maskPainter.drawLine (sp, ep);
+
+ didSomething = true;
+ }
+ // Brush & Eraser
+ else if (m_mode & (DrawsPixmaps | WashesPixmaps))
+ {
+ kpColor colorToReplace;
+
+ if (m_mode & WashesPixmaps)
+ colorToReplace = color (1 - m_mouseButton);
+
+ // Sweeps a pixmap along a line (modified Bresenham's line algorithm,
+ // see MODIFIED comment below).
+ //
+ // Derived from the zSprite2 Graphics Engine
+
+ const int x1 = (thisPoint - rect.topLeft ()).x (),
+ y1 = (thisPoint - rect.topLeft ()).y (),
+ x2 = (lastPoint - rect.topLeft ()).x (),
+ y2 = (lastPoint - rect.topLeft ()).y ();
+
+ // Difference of x and y values
+ int dx = x2 - x1;
+ int dy = y2 - y1;
+
+ // Absolute values of differences
+ int ix = kAbs (dx);
+ int iy = kAbs (dy);
+
+ // Larger of the x and y differences
+ int inc = ix > iy ? ix : iy;
+
+ // Plot location
+ int plotx = x1;
+ int ploty = y1;
+
+ int x = 0;
+ int y = 0;
+
+ if (m_mode & WashesPixmaps)
+ {
+ if (wash (&painter, &maskPainter, image,
+ colorToReplace,
+ rect, plotx + rect.left (), ploty + rect.top ()))
+ {
+ didSomething = true;
+ }
+ }
+ else
+ {
+ if (!transparent)
+ {
+ kpPixmapFX::paintPixmapAt (&pixmap,
+ hotPoint (plotx, ploty),
+ m_brushPixmap [m_mouseButton]);
+ }
+ else
+ {
+ kpPixmapFX::paintMaskTransparentWithBrush (&pixmap,
+ hotPoint (plotx, ploty),
+ kpPixmapFX::getNonNullMask (m_brushPixmap [m_mouseButton]));
+ }
+
+ didSomething = true;
+ }
+
+ for (int i = 0; i <= inc; i++)
+ {
+ // oldplotx is equally as valid but would look different
+ // (but nobody will notice which one it is)
+ int oldploty = ploty;
+ int plot = 0;
+
+ x += ix;
+ y += iy;
+
+ if (x > inc)
+ {
+ plot++;
+ x -= inc;
+
+ if (dx < 0)
+ plotx--;
+ else
+ plotx++;
+ }
+
+ if (y > inc)
+ {
+ plot++;
+ y -= inc;
+
+ if (dy < 0)
+ ploty--;
+ else
+ ploty++;
+ }
+
+ if (plot)
+ {
+ if (m_brushIsDiagonalLine && plot == 2)
+ {
+ // MODIFIED: every point is
+ // horizontally or vertically adjacent to another point (if there
+ // is more than 1 point, of course). This is in contrast to the
+ // ordinary line algorithm which can create diagonal adjacencies.
+
+ if (m_mode & WashesPixmaps)
+ {
+ if (wash (&painter, &maskPainter, image,
+ colorToReplace,
+ rect, plotx + rect.left (), oldploty + rect.top ()))
+ {
+ didSomething = true;
+ }
+ }
+ else
+ {
+ if (!transparent)
+ {
+ kpPixmapFX::paintPixmapAt (&pixmap,
+ hotPoint (plotx, oldploty),
+ m_brushPixmap [m_mouseButton]);
+ }
+ else
+ {
+ kpPixmapFX::paintMaskTransparentWithBrush (&pixmap,
+ hotPoint (plotx, oldploty),
+ kpPixmapFX::getNonNullMask (m_brushPixmap [m_mouseButton]));
+ }
+
+ didSomething = true;
+ }
+ }
+
+ if (m_mode & WashesPixmaps)
+ {
+ if (wash (&painter, &maskPainter, image,
+ colorToReplace,
+ rect, plotx + rect.left (), ploty + rect.top ()))
+ {
+ didSomething = true;
+ }
+ }
+ else
+ {
+ if (!transparent)
+ {
+ kpPixmapFX::paintPixmapAt (&pixmap,
+ hotPoint (plotx, ploty),
+ m_brushPixmap [m_mouseButton]);
+ }
+ else
+ {
+ kpPixmapFX::paintMaskTransparentWithBrush (&pixmap,
+ hotPoint (plotx, ploty),
+ kpPixmapFX::getNonNullMask (m_brushPixmap [m_mouseButton]));
+ }
+
+ didSomething = true;
+ }
+ }
+ }
+
+ }
+
+ if (painter.isActive ())
+ painter.end ();
+
+ if (maskPainter.isActive ())
+ maskPainter.end ();
+
+ #if DEBUG_KP_TOOL_PEN
+ if (m_mode & WashesPixmaps)
+ {
+ int ms = timer.restart ();
+ kdDebug () << "\ttried to wash: " << ms << "ms"
+ << " (" << (ms ? (rect.width () * rect.height () / ms) : -1234)
+ << " pixels/ms)"
+ << endl;
+ convAndWashTime += ms;
+ }
+ #endif
+
+
+ if (didSomething)
+ {
+ if (!maskBitmap.isNull ())
+ pixmap.setMask (maskBitmap);
+
+ // draw onto doc
+ document ()->setPixmapAt (pixmap, rect.topLeft ());
+
+ #if DEBUG_KP_TOOL_PEN
+ if (m_mode & WashesPixmaps)
+ {
+ int ms = timer.restart ();
+ kdDebug () << "\tset doc: " << ms << "ms" << endl;
+ convAndWashTime += ms;
+ }
+ #endif
+
+ m_currentCommand->updateBoundingRect (rect);
+
+ #if DEBUG_KP_TOOL_PEN
+ if (m_mode & WashesPixmaps)
+ {
+ int ms = timer.restart ();
+ kdDebug () << "\tupdate boundingRect: " << ms << "ms" << endl;
+ convAndWashTime += ms;
+ kdDebug () << "\tdone (" << (convAndWashTime ? (rect.width () * rect.height () / convAndWashTime) : -1234)
+ << " pixels/ms)"
+ << endl;
+ }
+ #endif
+ }
+
+ #if DEBUG_KP_TOOL_PEN
+ if (m_mode & WashesPixmaps)
+ kdDebug () << endl;
+ #endif
+ }
+
+ viewManager ()->restoreFastUpdates ();
+ setUserShapePoints (thisPoint);
+}
+
+// virtual
+void kpToolPen::cancelShape ()
+{
+ m_currentCommand->finalize ();
+ m_currentCommand->cancel ();
+
+ delete m_currentCommand;
+ m_currentCommand = 0;
+
+ updateBrushCursor (false/*no recalc*/);
+
+ setUserMessage (i18n ("Let go of all the mouse buttons."));
+}
+
+void kpToolPen::releasedAllButtons ()
+{
+ setUserMessage (haventBegunDrawUserMessage ());
+}
+
+// virtual
+void kpToolPen::endDraw (const QPoint &, const QRect &)
+{
+ m_currentCommand->finalize ();
+ mainWindow ()->commandHistory ()->addCommand (m_currentCommand, false /* don't exec */);
+
+ // don't delete - it's up to the commandHistory
+ m_currentCommand = 0;
+
+ updateBrushCursor (false/*no recalc*/);
+
+ setUserMessage (haventBegunDrawUserMessage ());
+}
+
+
+// TODO: maybe the base should be virtual?
+kpColor kpToolPen::color (int which)
+{
+#if DEBUG_KP_TOOL_PEN && 0
+ kdDebug () << "kpToolPen::color (" << which << ")" << endl;
+#endif
+
+ // Pen & Brush
+ if ((m_mode & SwappedColors) == 0)
+ return kpTool::color (which);
+ // only the (Color) Eraser uses the opposite color
+ else
+ return kpTool::color (which ? 0 : 1); // don't trust !0 == 1
+}
+
+// virtual private slot
+void kpToolPen::slotForegroundColorChanged (const kpColor &col)
+{
+#if DEBUG_KP_TOOL_PEN
+ kdDebug () << "kpToolPen::slotForegroundColorChanged()" << endl;
+#endif
+ if (col.isOpaque ())
+ m_brushPixmap [(m_mode & SwappedColors) ? 1 : 0].fill (col.toQColor ());
+
+ updateBrushCursor ();
+}
+
+// virtual private slot
+void kpToolPen::slotBackgroundColorChanged (const kpColor &col)
+{
+#if DEBUG_KP_TOOL_PEN
+ kdDebug () << "kpToolPen::slotBackgroundColorChanged()" << endl;
+#endif
+
+ if (col.isOpaque ())
+ m_brushPixmap [(m_mode & SwappedColors) ? 0 : 1].fill (col.toQColor ());
+
+ updateBrushCursor ();
+}
+
+// private slot
+void kpToolPen::slotBrushChanged (const QPixmap &pixmap, bool isDiagonalLine)
+{
+#if DEBUG_KP_TOOL_PEN
+ kdDebug () << "kpToolPen::slotBrushChanged()" << endl;
+#endif
+ for (int i = 0; i < 2; i++)
+ {
+ m_brushPixmap [i] = pixmap;
+ if (color (i).isOpaque ())
+ m_brushPixmap [i].fill (color (i).toQColor ());
+ }
+
+ m_brushIsDiagonalLine = isDiagonalLine;
+
+ updateBrushCursor ();
+}
+
+// private slot
+void kpToolPen::slotEraserSizeChanged (int size)
+{
+#if DEBUG_KP_TOOL_PEN
+ kdDebug () << "KpToolPen::slotEraserSizeChanged(size=" << size << ")" << endl;
+#endif
+
+ for (int i = 0; i < 2; i++)
+ {
+ // Note: No matter what, the eraser's brush pixmap is never given
+ // a mask.
+ //
+ // With a transparent color, since we don't fill anything, the
+ // resize by itself will leave us with garbage pixels. This
+ // doesn't matter because:
+ //
+ // 1. The hover cursor will ask kpToolWidgetEraserSize for a proper
+ // cursor pixmap.
+ // 2. We will draw using kpPixmapFX::paintMaskTransparentWithBrush()
+ // which only cares about the opaqueness.
+ m_brushPixmap [i].resize (size, size);
+ if (color (i).isOpaque ())
+ m_brushPixmap [i].fill (color (i).toQColor ());
+ }
+
+ updateBrushCursor ();
+}
+
+QPoint kpToolPen::hotPoint () const
+{
+ return hotPoint (m_currentPoint);
+}
+
+QPoint kpToolPen::hotPoint (int x, int y) const
+{
+ return hotPoint (QPoint (x, y));
+}
+
+QPoint kpToolPen::hotPoint (const QPoint &point) const
+{
+ /*
+ * e.g.
+ * Width 5:
+ * 0 1 2 3 4
+ * ^
+ * |
+ * Center
+ */
+ return point -
+ QPoint (m_brushPixmap [m_mouseButton].width () / 2,
+ m_brushPixmap [m_mouseButton].height () / 2);
+}
+
+QRect kpToolPen::hotRect () const
+{
+ return hotRect (m_currentPoint);
+}
+
+QRect kpToolPen::hotRect (int x, int y) const
+{
+ return hotRect (QPoint (x, y));
+}
+
+QRect kpToolPen::hotRect (const QPoint &point) const
+{
+ QPoint topLeft = hotPoint (point);
+ return QRect (topLeft.x (),
+ topLeft.y (),
+ m_brushPixmap [m_mouseButton].width (),
+ m_brushPixmap [m_mouseButton].height ());
+}
+
+// private
+void kpToolPen::updateBrushCursor (bool recalc)
+{
+#if DEBUG_KP_TOOL_PEN && 1
+ kdDebug () << "kpToolPen::updateBrushCursor(recalc=" << recalc << ")" << endl;
+#endif
+
+ if (recalc)
+ {
+ if (m_mode & SquareBrushes)
+ m_cursorPixmap = m_toolWidgetEraserSize->cursorPixmap (color (0));
+ else if (m_mode & DiverseBrushes)
+ m_cursorPixmap = m_brushPixmap [0];
+ }
+
+ hover (hasBegun () ? m_currentPoint : currentPoint ());
+}
+
+
+/*
+ * kpToolPenCommand
+ */
+
+kpToolPenCommand::kpToolPenCommand (const QString &name, kpMainWindow *mainWindow)
+ : kpNamedCommand (name, mainWindow),
+ m_pixmap (*document ()->pixmap ())
+{
+}
+
+kpToolPenCommand::~kpToolPenCommand ()
+{
+}
+
+
+// public virtual [base kpCommand]
+int kpToolPenCommand::size () const
+{
+ return kpPixmapFX::pixmapSize (m_pixmap);
+}
+
+
+// public virtual [base kpCommand]
+void kpToolPenCommand::execute ()
+{
+ swapOldAndNew ();
+}
+
+// public virtual [base kpCommand]
+void kpToolPenCommand::unexecute ()
+{
+ swapOldAndNew ();
+}
+
+
+// private
+void kpToolPenCommand::swapOldAndNew ()
+{
+ if (m_boundingRect.isValid ())
+ {
+ QPixmap oldPixmap = document ()->getPixmapAt (m_boundingRect);
+
+ document ()->setPixmapAt (m_pixmap, m_boundingRect.topLeft ());
+
+ m_pixmap = oldPixmap;
+ }
+}
+
+// public
+void kpToolPenCommand::updateBoundingRect (const QPoint &point)
+{
+ updateBoundingRect (QRect (point, point));
+}
+
+// public
+void kpToolPenCommand::updateBoundingRect (const QRect &rect)
+{
+#if DEBUG_KP_TOOL_PEN & 0
+ kdDebug () << "kpToolPenCommand::updateBoundingRect() existing="
+ << m_boundingRect
+ << " plus="
+ << rect
+ << endl;
+#endif
+ m_boundingRect = m_boundingRect.unite (rect);
+#if DEBUG_KP_TOOL_PEN & 0
+ kdDebug () << "\tresult=" << m_boundingRect << endl;
+#endif
+}
+
+// public
+void kpToolPenCommand::finalize ()
+{
+ if (m_boundingRect.isValid ())
+ {
+ // store only needed part of doc pixmap
+ m_pixmap = kpTool::neededPixmap (m_pixmap, m_boundingRect);
+ }
+ else
+ {
+ m_pixmap.resize (0, 0);
+ }
+}
+
+// public
+void kpToolPenCommand::cancel ()
+{
+ if (m_boundingRect.isValid ())
+ {
+ viewManager ()->setFastUpdates ();
+ document ()->setPixmapAt (m_pixmap, m_boundingRect.topLeft ());
+ viewManager ()->restoreFastUpdates ();
+ }
+}
+
+#include <kptoolpen.moc>
diff --git a/kolourpaint/tools/kptoolpen.h b/kolourpaint/tools/kptoolpen.h
new file mode 100644
index 00000000..f57eb367
--- /dev/null
+++ b/kolourpaint/tools/kptoolpen.h
@@ -0,0 +1,160 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kptoolpen_h__
+#define __kptoolpen_h__
+
+#include <qpixmap.h>
+#include <qrect.h>
+
+#include <kpcommandhistory.h>
+#include <kptool.h>
+
+class QPoint;
+class QString;
+
+class kpColor;
+class kpMainWindow;
+class kpToolPenCommand;
+class kpToolWidgetBrush;
+class kpToolWidgetEraserSize;
+class kpViewManager;
+
+class kpToolPen : public kpTool
+{
+Q_OBJECT
+
+public:
+ enum Mode
+ {
+ // tool properties
+ DrawsPixels = (1 << 0), DrawsPixmaps = (1 << 1), WashesPixmaps = (1 << 2),
+ NoBrushes = 0, SquareBrushes = (1 << 3), DiverseBrushes = (1 << 4),
+ NormalColors = 0, SwappedColors = (1 << 5),
+
+ // tools:
+ //
+ // Pen = draws pixels, "interpolates" by "sweeping" pixels along a line (no brushes)
+ // Brush = draws pixmaps, "interpolates" by "sweeping" pixmaps along a line (interesting brushes)
+ // Eraser = Brush but with foreground & background colors swapped (a few square brushes)
+ // Color Washer = Brush that replaces/washes the foreground color with the background color
+ //
+ // (note the capitalization of "brush" here :))
+ Pen = DrawsPixels | NoBrushes | NormalColors,
+ Brush = DrawsPixmaps | DiverseBrushes | NormalColors,
+ Eraser = DrawsPixmaps | SquareBrushes | SwappedColors,
+ ColorWasher = WashesPixmaps | SquareBrushes | SwappedColors
+ };
+
+ kpToolPen (Mode mode, const QString &text, const QString &description,
+ int key,
+ kpMainWindow *mainWindow, const char *name);
+ kpToolPen (kpMainWindow *mainWindow);
+ virtual ~kpToolPen ();
+
+ void setMode (Mode mode);
+
+private:
+ QString haventBegunDrawUserMessage () const;
+
+public:
+ virtual void begin ();
+ virtual void end ();
+
+ virtual void beginDraw ();
+ virtual void hover (const QPoint &point);
+ virtual void globalDraw ();
+ virtual void draw (const QPoint &thisPoint, const QPoint &lastPoint, const QRect &);
+ virtual void cancelShape ();
+ virtual void releasedAllButtons ();
+ virtual void endDraw (const QPoint &, const QRect &);
+
+private slots:
+ virtual void slotForegroundColorChanged (const kpColor &col);
+ virtual void slotBackgroundColorChanged (const kpColor &col);
+
+ void slotBrushChanged (const QPixmap &pixmap, bool isDiagonalLine);
+ void slotEraserSizeChanged (int size);
+
+private:
+ bool wash (QPainter *painter, QPainter *maskPainter,
+ const QImage &image,
+ const kpColor &colorToReplace,
+ const QRect &imageRect, int plotx, int ploty);
+ bool wash (QPainter *painter, QPainter *maskPainter,
+ const QImage &image,
+ const kpColor &colorToReplace,
+ const QRect &imageRect, const QRect &drawRect);
+
+ kpColor color (int which);
+
+ QPoint hotPoint () const;
+ QPoint hotPoint (int x, int y) const;
+ QPoint hotPoint (const QPoint &point) const;
+ QRect hotRect () const;
+ QRect hotRect (int x, int y) const;
+ QRect hotRect (const QPoint &point) const;
+
+ Mode m_mode;
+
+ void updateBrushCursor (bool recalc = true);
+
+ kpToolWidgetBrush *m_toolWidgetBrush;
+ kpToolWidgetEraserSize *m_toolWidgetEraserSize;
+ QPixmap m_brushPixmap [2];
+ QPixmap m_cursorPixmap;
+ bool m_brushIsDiagonalLine;
+
+ kpToolPenCommand *m_currentCommand;
+};
+
+class kpToolPenCommand : public kpNamedCommand
+{
+public:
+ kpToolPenCommand (const QString &name, kpMainWindow *mainWindow);
+ virtual ~kpToolPenCommand ();
+
+ virtual int size () const;
+
+ virtual void execute ();
+ virtual void unexecute ();
+
+ // interface for KToolPen
+ void updateBoundingRect (const QPoint &point);
+ void updateBoundingRect (const QRect &rect);
+ void finalize ();
+ void cancel ();
+
+private:
+ void swapOldAndNew ();
+
+ QPixmap m_pixmap;
+ QRect m_boundingRect;
+};
+
+#endif // __kptoolpen_h__
diff --git a/kolourpaint/tools/kptoolpolygon.cpp b/kolourpaint/tools/kptoolpolygon.cpp
new file mode 100644
index 00000000..fb68745c
--- /dev/null
+++ b/kolourpaint/tools/kptoolpolygon.cpp
@@ -0,0 +1,895 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define DEBUG_KP_TOOL_POLYGON 0
+
+#include <kptoolpolygon.h>
+
+#include <float.h>
+#include <math.h>
+
+#include <qbitmap.h>
+#include <qcursor.h>
+#include <qlayout.h>
+#include <qpainter.h>
+#include <qpoint.h>
+#include <qpushbutton.h>
+#include <qrect.h>
+#include <qtooltip.h>
+#include <qvbuttongroup.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kpcommandhistory.h>
+#include <kpdocument.h>
+#include <kpdefs.h>
+#include <kpmainwindow.h>
+#include <kppixmapfx.h>
+#include <kptemppixmap.h>
+#include <kptooltoolbar.h>
+#include <kptoolwidgetlinewidth.h>
+#include <kpviewmanager.h>
+
+
+#if DEBUG_KP_TOOL_POLYGON
+static const char *pointArrayToString (const QPointArray &pointArray)
+{
+ static char string [1000];
+ string [0] = '\0';
+
+ for (QPointArray::ConstIterator it = pointArray.begin ();
+ it != pointArray.end ();
+ it++)
+ {
+ QString ps = QString (" (%1, %2)").arg ((*it).x ()).arg ((*it).y ());
+ const char *pss = ps.latin1 ();
+ if (strlen (string) + strlen (pss) + 1 > sizeof (string) / sizeof (string [0]))
+ break;
+ strcat (string, pss);
+ }
+
+ return string;
+}
+#endif
+
+
+static QPen makeMaskPen (const kpColor &color, int lineWidth, Qt::PenStyle lineStyle)
+{
+ return QPen (color.maskColor (),
+ lineWidth == 1 ? 0/*closer to looking width 1*/ : lineWidth, lineStyle,
+ Qt::RoundCap, Qt::RoundJoin);
+}
+
+static QPen makePen (const kpColor &color, int lineWidth, Qt::PenStyle lineStyle)
+{
+ if (color.isOpaque ())
+ {
+ return QPen (color.toQColor (),
+ lineWidth == 1 ? 0/*closer to looking width 1*/ : lineWidth, lineStyle,
+ Qt::RoundCap, Qt::RoundJoin);
+ }
+ else
+ return Qt::NoPen;
+}
+
+static QBrush makeMaskBrush (const kpColor &foregroundColor,
+ const kpColor &backgroundColor,
+ kpToolWidgetFillStyle *toolWidgetFillStyle)
+{
+ if (toolWidgetFillStyle)
+ return toolWidgetFillStyle->maskBrush (foregroundColor, backgroundColor);
+ else
+ return Qt::NoBrush;
+}
+
+static QBrush makeBrush (const kpColor &foregroundColor,
+ const kpColor &backgroundColor,
+ kpToolWidgetFillStyle *toolWidgetFillStyle)
+{
+ if (toolWidgetFillStyle)
+ return toolWidgetFillStyle->brush (foregroundColor, backgroundColor);
+ else
+ return Qt::NoBrush;
+}
+
+static bool only1PixelInPointArray (const QPointArray &points)
+{
+ if (points.count () == 0)
+ return false;
+
+ for (int i = 1; i < (int) points.count (); i++)
+ {
+ if (points [i] != points [0])
+ return false;
+ }
+
+ return true;
+}
+
+static QPixmap pixmap (const QPixmap &oldPixmap,
+ const QPointArray &points, const QRect &rect,
+ const kpColor &foregroundColor, kpColor backgroundColor,
+ int lineWidth, Qt::PenStyle lineStyle,
+ kpToolWidgetFillStyle *toolWidgetFillStyle,
+ enum kpToolPolygon::Mode mode, bool final = true)
+{
+ //
+ // figure out points to draw relative to topLeft of oldPixmap
+
+ QPointArray pointsInRect = points;
+ pointsInRect.detach ();
+ pointsInRect.translate (-rect.x (), -rect.y ());
+
+#if DEBUG_KP_TOOL_POLYGON && 0
+ kdDebug () << "kptoolpolygon.cpp: pixmap(): points=" << pointArrayToString (points) << endl;
+#endif
+
+
+ //
+ // draw
+
+ QPen pen = makePen (foregroundColor, lineWidth, lineStyle),
+ maskPen = makeMaskPen (foregroundColor, lineWidth, lineStyle);
+ QBrush brush = makeBrush (foregroundColor, backgroundColor, toolWidgetFillStyle),
+ maskBrush = makeMaskBrush (foregroundColor, backgroundColor, toolWidgetFillStyle);
+
+ QPixmap pixmap = oldPixmap;
+ QBitmap maskBitmap;
+
+ QPainter painter, maskPainter;
+
+ if (pixmap.mask () ||
+ (maskPen.style () != Qt::NoPen &&
+ maskPen.color () == Qt::color0/*transparent*/) ||
+ (maskBrush.style () != Qt::NoBrush &&
+ maskBrush.color () == Qt::color0/*transparent*/))
+ {
+ maskBitmap = kpPixmapFX::getNonNullMask (pixmap);
+ maskPainter.begin (&maskBitmap);
+ maskPainter.setPen (maskPen);
+ maskPainter.setBrush (maskBrush);
+
+ #if DEBUG_KP_TOOL_POLYGON && 0
+ kdDebug () << "\tmaskPainter begin because:" << endl
+ << "\t\tpixmap.mask=" << pixmap.mask () << endl
+ << "\t\t(maskPenStyle!=NoPen)=" << (maskPen.style () != Qt::NoPen) << endl
+ << "\t\t(maskPenColor==trans)=" << (maskPen.color () == Qt::color0) << endl
+ << "\t\t(maskBrushStyle!=NoBrush)=" << (maskBrush.style () != Qt::NoBrush) << endl
+ << "\t\t(maskBrushColor==trans)=" << (maskBrush.color () == Qt::color0) << endl;
+ #endif
+ }
+
+ if (pen.style () != Qt::NoPen ||
+ brush.style () != Qt::NoBrush)
+ {
+ painter.begin (&pixmap);
+ painter.setPen (pen);
+ painter.setBrush (brush);
+
+ #if DEBUG_KP_TOOL_POLYGON && 0
+ kdDebug () << "\tpainter begin pen.rgb="
+ << (int *) painter.pen ().color ().rgb ()
+ << endl;
+ #endif
+ }
+
+#define PAINTER_CALL(cmd) \
+{ \
+ if (painter.isActive ()) \
+ painter . cmd ; \
+ \
+ if (maskPainter.isActive ()) \
+ maskPainter . cmd ; \
+}
+
+ // SYNC: Qt bug
+ if (only1PixelInPointArray (pointsInRect))
+ {
+ PAINTER_CALL (drawPoint (pointsInRect [0]));
+ }
+ else
+ {
+ switch (mode)
+ {
+ case kpToolPolygon::Line:
+ case kpToolPolygon::Polyline:
+ PAINTER_CALL (drawPolyline (pointsInRect));
+ break;
+ case kpToolPolygon::Polygon:
+ // TODO: why aren't the ends rounded?
+ PAINTER_CALL (drawPolygon (pointsInRect));
+
+ if (!final && 0/*HACK for TODO*/)
+ {
+ int count = pointsInRect.count ();
+
+ if (count > 2)
+ {
+ if (painter.isActive ())
+ {
+ QPen XORpen = painter.pen ();
+ XORpen.setColor (Qt::white);
+
+ painter.setPen (XORpen);
+ painter.setRasterOp (Qt::XorROP);
+ }
+
+ if (maskPainter.isActive ())
+ {
+ QPen XORpen = maskPainter.pen ();
+
+ // TODO???
+ #if 0
+ if (kpTool::isColorTransparent (foregroundColor))
+ XORpen.setColor (Qt::color1/*opaque*/);
+ else
+ XORpen.setColor (Qt::color0/*transparent*/);
+ #endif
+
+ maskPainter.setPen (XORpen);
+ }
+
+ PAINTER_CALL (drawLine (pointsInRect [0], pointsInRect [count - 1]));
+ }
+ }
+ break;
+ case kpToolPolygon::Curve:
+ int numPoints = pointsInRect.count ();
+ QPointArray pa (4);
+
+ pa [0] = pointsInRect [0];
+ pa [3] = pointsInRect [1];
+
+ switch (numPoints)
+ {
+ case 2:
+ pa [1] = pointsInRect [0];
+ pa [2] = pointsInRect [1];
+ break;
+ case 3:
+ pa [1] = pa [2] = pointsInRect [2];
+ break;
+ case 4:
+ pa [1] = pointsInRect [2];
+ pa [2] = pointsInRect [3];
+ }
+
+ PAINTER_CALL (drawCubicBezier (pa));
+ }
+ }
+#undef PAINTER_CALL
+
+ if (painter.isActive ())
+ painter.end ();
+
+ if (maskPainter.isActive ())
+ maskPainter.end ();
+
+ if (!maskBitmap.isNull ())
+ pixmap.setMask (maskBitmap);
+
+ return pixmap;
+}
+
+
+/*
+ * kpToolPolygon
+ */
+
+kpToolPolygon::kpToolPolygon (Mode mode,
+ const QString &text, const QString &description,
+ int key,
+ kpMainWindow *mainWindow, const char *name)
+ : kpTool (text, description, key, mainWindow, name),
+ m_mode (mode),
+ m_toolWidgetFillStyle (0),
+ m_toolWidgetLineWidth (0)
+{
+}
+
+kpToolPolygon::kpToolPolygon (kpMainWindow *mainWindow)
+ : kpTool (i18n ("Polygon"), i18n ("Draws polygons"),
+ Qt::Key_G,
+ mainWindow, "tool_polygon"),
+ m_mode (Polygon),
+ m_toolWidgetFillStyle (0),
+ m_toolWidgetLineWidth (0)
+{
+}
+
+kpToolPolygon::~kpToolPolygon ()
+{
+}
+
+void kpToolPolygon::setMode (Mode m)
+{
+ m_mode = m;
+}
+
+
+// private
+QString kpToolPolygon::haventBegunShapeUserMessage () const
+{
+ switch (m_mode)
+ {
+ case Line:
+ return i18n ("Drag to draw.");
+ case Polygon:
+ case Polyline:
+ return i18n ("Drag to draw the first line.");
+ case Curve:
+ return i18n ("Drag out the start and end points.");
+ default:
+ return QString::null;
+ }
+}
+
+// virtual
+void kpToolPolygon::begin ()
+{
+ kpToolToolBar *tb = toolToolBar ();
+
+#if DEBUG_KP_TOOL_POLYGON
+ kdDebug () << "kpToolPolygon::begin() tb=" << tb << endl;
+#endif
+
+ if (tb)
+ {
+ if (m_mode == Polygon)
+ m_toolWidgetFillStyle = tb->toolWidgetFillStyle ();
+ else
+ m_toolWidgetFillStyle = 0;
+
+ m_toolWidgetLineWidth = tb->toolWidgetLineWidth ();
+
+ if (m_toolWidgetFillStyle)
+ {
+ connect (m_toolWidgetFillStyle, SIGNAL (fillStyleChanged (kpToolWidgetFillStyle::FillStyle)),
+ this, SLOT (slotFillStyleChanged (kpToolWidgetFillStyle::FillStyle)));
+ }
+ connect (m_toolWidgetLineWidth, SIGNAL (lineWidthChanged (int)),
+ this, SLOT (slotLineWidthChanged (int)));
+
+ if (m_toolWidgetFillStyle)
+ m_toolWidgetFillStyle->show ();
+ m_toolWidgetLineWidth->show ();
+
+ m_lineWidth = m_toolWidgetLineWidth->lineWidth ();
+ }
+ else
+ {
+ m_toolWidgetFillStyle = 0;
+ m_toolWidgetLineWidth = 0;
+
+ m_lineWidth = 1;
+ }
+
+ viewManager ()->setCursor (QCursor (CrossCursor));
+
+ m_originatingMouseButton = -1;
+
+ setUserMessage (haventBegunShapeUserMessage ());
+}
+
+// virtual
+void kpToolPolygon::end ()
+{
+ endShape ();
+
+ if (m_toolWidgetFillStyle)
+ {
+ disconnect (m_toolWidgetFillStyle, SIGNAL (fillStyleChanged (kpToolWidgetFillStyle::FillStyle)),
+ this, SLOT (slotFillStyleChanged (kpToolWidgetFillStyle::FillStyle)));
+ m_toolWidgetFillStyle = 0;
+ }
+
+ if (m_toolWidgetLineWidth)
+ {
+ disconnect (m_toolWidgetLineWidth, SIGNAL (lineWidthChanged (int)),
+ this, SLOT (slotLineWidthChanged (int)));
+ m_toolWidgetLineWidth = 0;
+ }
+
+ viewManager ()->unsetCursor ();
+}
+
+
+void kpToolPolygon::beginDraw ()
+{
+#if DEBUG_KP_TOOL_POLYGON
+ kdDebug () << "kpToolPolygon::beginDraw() m_points=" << pointArrayToString (m_points)
+ << ", startPoint=" << m_startPoint << endl;
+#endif
+
+ bool endedShape = false;
+
+ // starting with a line...
+ if (m_points.count () == 0)
+ {
+ m_originatingMouseButton = m_mouseButton;
+ m_points.putPoints (m_points.count (), 2,
+ m_startPoint.x (), m_startPoint.y (),
+ m_startPoint.x (), m_startPoint.y ());
+ }
+ // continuing poly*
+ else
+ {
+ if (m_mouseButton != m_originatingMouseButton)
+ {
+ m_mouseButton = m_originatingMouseButton;
+ endShape ();
+ endedShape = true;
+ }
+ else
+ {
+ int count = m_points.count ();
+ m_points.putPoints (count, 1,
+ m_startPoint.x (), m_startPoint.y ());
+
+ // start point = last end point;
+ // _not_ the new/current start point
+ // (which is disregarded in a poly* as only the end points count
+ // after the initial line)
+ //
+ // Curve Tool ignores m_startPoint (doesn't call applyModifiers())
+ // after the initial has been defined.
+ m_startPoint = m_points [count - 1];
+ }
+ }
+
+#if DEBUG_KP_TOOL_POLYGON
+ kdDebug () << "\tafterwards, m_points=" << pointArrayToString (m_points) << endl;
+#endif
+
+ if (!endedShape)
+ {
+ switch (m_mode)
+ {
+ case Line:
+ case Curve:
+ case Polygon:
+ case Polyline:
+ setUserMessage (cancelUserMessage ());
+ break;
+
+ default:
+ kdError () << "kpToolPolygon::beginDraw() shape" << endl;
+ break;
+ }
+ }
+}
+
+// private
+void kpToolPolygon::applyModifiers ()
+{
+ int count = m_points.count ();
+
+ m_toolLineStartPoint = m_startPoint; /* also correct for poly* tool (see beginDraw()) */
+ m_toolLineEndPoint = m_currentPoint;
+
+#if DEBUG_KP_TOOL_POLYGON && 1
+ kdDebug () << "kpToolPolygon::applyModifiers() #pts=" << count
+ << " line: startPt=" << m_toolLineStartPoint
+ << " endPt=" << m_toolLineEndPoint
+ << " modifiers: shift=" << m_shiftPressed
+ << " alt=" << m_altPressed
+ << " ctrl=" << m_controlPressed
+ << endl;
+#endif
+
+ // angles
+ if (m_shiftPressed || m_controlPressed)
+ {
+ int diffx = m_toolLineEndPoint.x () - m_toolLineStartPoint.x ();
+ int diffy = m_toolLineEndPoint.y () - m_toolLineStartPoint.y ();
+
+ double ratio;
+ if (diffx == 0)
+ ratio = DBL_MAX;
+ else
+ ratio = fabs (double (diffy) / double (diffx));
+ #if DEBUG_KP_TOOL_POLYGON && 1
+ kdDebug () << "\tdiffx=" << diffx << " diffy=" << diffy
+ << " ratio=" << ratio
+ << endl;
+ #endif
+
+ // Shift = 0, 45, 90
+ // Alt = 0, 30, 60, 90
+ // Shift + Alt = 0, 30, 45, 60, 90
+ double angles [10]; // "ought to be enough for anybody"
+ int numAngles = 0;
+ angles [numAngles++] = 0;
+ if (m_controlPressed)
+ angles [numAngles++] = KP_PI / 6;
+ if (m_shiftPressed)
+ angles [numAngles++] = KP_PI / 4;
+ if (m_controlPressed)
+ angles [numAngles++] = KP_PI / 3;
+ angles [numAngles++] = KP_PI / 2;
+
+ double angle = angles [numAngles - 1];
+ for (int i = 0; i < numAngles - 1; i++)
+ {
+ double acceptingRatio = tan ((angles [i] + angles [i + 1]) / 2.0);
+ if (ratio < acceptingRatio)
+ {
+ angle = angles [i];
+ break;
+ }
+ }
+
+ // horizontal (dist from start !maintained)
+ if (fabs (KP_RADIANS_TO_DEGREES (angle) - 0)
+ < kpPixmapFX::AngleInDegreesEpsilon)
+ {
+ m_toolLineEndPoint = QPoint (m_toolLineEndPoint.x (), m_toolLineStartPoint.y ());
+ }
+ // vertical (dist from start !maintained)
+ else if (fabs (KP_RADIANS_TO_DEGREES (angle) - 90)
+ < kpPixmapFX::AngleInDegreesEpsilon)
+ {
+ m_toolLineEndPoint = QPoint (m_toolLineStartPoint.x (), m_toolLineEndPoint.y ());
+ }
+ // diagonal (dist from start maintained)
+ else
+ {
+ const double dist = sqrt (diffx * diffx + diffy * diffy);
+
+ #define sgn(a) ((a)<0?-1:1)
+ // Round distances _before_ adding to any coordinate
+ // (ensures consistent rounding behaviour in x & y directions)
+ const int newdx = qRound (dist * cos (angle) * sgn (diffx));
+ const int newdy = qRound (dist * sin (angle) * sgn (diffy));
+ #undef sgn
+
+ m_toolLineEndPoint = QPoint (m_toolLineStartPoint.x () + newdx,
+ m_toolLineStartPoint.y () + newdy);
+
+ #if DEBUG_KP_TOOL_POLYGON && 1
+ kdDebug () << "\t\tdiagonal line: dist=" << dist
+ << " angle=" << (angle * 180 / KP_PI)
+ << " endPoint=" << m_toolLineEndPoint
+ << endl;
+ #endif
+ }
+ } // if (m_shiftPressed || m_controlPressed) {
+
+ // centring
+ if (m_altPressed && 0/*ALT is unreliable*/)
+ {
+ // start = start - diff
+ // = start - (end - start)
+ // = start - end + start
+ // = 2 * start - end
+ if (count == 2)
+ m_toolLineStartPoint += (m_toolLineStartPoint - m_toolLineEndPoint);
+ else
+ m_toolLineEndPoint += (m_toolLineEndPoint - m_toolLineStartPoint);
+ } // if (m_altPressed) {
+
+ m_points [count - 2] = m_toolLineStartPoint;
+ m_points [count - 1] = m_toolLineEndPoint;
+
+ m_toolLineRect = kpTool::neededRect (QRect (m_toolLineStartPoint, m_toolLineEndPoint).normalize (),
+ m_lineWidth);
+}
+
+// virtual
+void kpToolPolygon::draw (const QPoint &, const QPoint &, const QRect &)
+{
+ if (m_points.count () == 0)
+ return;
+
+#if DEBUG_KP_TOOL_POLYGON
+ kdDebug () << "kpToolPolygon::draw() m_points=" << pointArrayToString (m_points)
+ << ", endPoint=" << m_currentPoint << endl;
+#endif
+
+ bool drawingALine = (m_mode != Curve) ||
+ (m_mode == Curve && m_points.count () == 2);
+
+ if (drawingALine)
+ applyModifiers ();
+ else
+ m_points [m_points.count () - 1] = m_currentPoint;
+
+#if DEBUG_KP_TOOL_POLYGON
+ kdDebug () << "\tafterwards, m_points=" << pointArrayToString (m_points) << endl;
+#endif
+
+ updateShape ();
+
+ if (drawingALine)
+ setUserShapePoints (m_toolLineStartPoint, m_toolLineEndPoint);
+ else
+ setUserShapePoints (m_currentPoint);
+}
+
+// private slot
+void kpToolPolygon::updateShape ()
+{
+ if (m_points.count () == 0)
+ return;
+
+ QRect boundingRect = kpTool::neededRect (m_points.boundingRect (), m_lineWidth);
+
+#if DEBUG_KP_TOOL_POLYGON
+ kdDebug () << "kpToolPolygon::updateShape() boundingRect="
+ << boundingRect
+ << " lineWidth="
+ << m_lineWidth
+ << endl;
+#endif
+
+ QPixmap oldPixmap = document ()->getPixmapAt (boundingRect);
+ QPixmap newPixmap = pixmap (oldPixmap,
+ m_points, boundingRect,
+ color (m_mouseButton), color (1 - m_mouseButton),
+ m_lineWidth, Qt::SolidLine,
+ m_toolWidgetFillStyle,
+ m_mode, false/*not final*/);
+
+ viewManager ()->setFastUpdates ();
+ viewManager ()->setTempPixmap (kpTempPixmap (false/*always display*/,
+ kpTempPixmap::SetPixmap/*render mode*/,
+ boundingRect.topLeft (),
+ newPixmap));
+ viewManager ()->restoreFastUpdates ();
+}
+
+// virtual
+void kpToolPolygon::cancelShape ()
+{
+#if 0
+ endDraw (QPoint (), QRect ());
+ commandHistory ()->undo ();
+#else
+ viewManager ()->invalidateTempPixmap ();
+#endif
+ m_points.resize (0);
+
+ setUserMessage (i18n ("Let go of all the mouse buttons."));
+}
+
+void kpToolPolygon::releasedAllButtons ()
+{
+ if (!hasBegunShape ())
+ setUserMessage (haventBegunShapeUserMessage ());
+
+ // --- else case already handled by endDraw() ---
+}
+
+// virtual
+void kpToolPolygon::endDraw (const QPoint &, const QRect &)
+{
+#if DEBUG_KP_TOOL_POLYGON
+ kdDebug () << "kpToolPolygon::endDraw() m_points=" << pointArrayToString (m_points) << endl;
+#endif
+
+ if (m_points.count () == 0)
+ return;
+
+ if (m_mode == Line ||
+ (m_mode == Curve && m_points.count () >= 4) ||
+ m_points.count () >= 50)
+ {
+ endShape ();
+ }
+ else
+ {
+ switch (m_mode)
+ {
+ case Line:
+ kdError () << "kpToolPolygon::endDraw() - line not ended" << endl;
+ setUserMessage ();
+ break;
+
+ case Polygon:
+ case Polyline:
+ if (m_points.isEmpty ())
+ {
+ kdError () << "kpToolPolygon::endDraw() exception - poly without points" << endl;
+ setUserMessage ();
+ }
+ else
+ {
+ if (m_mouseButton == 0)
+ {
+ setUserMessage (i18n ("Left drag another line or right click to finish."));
+ }
+ else
+ {
+ setUserMessage (i18n ("Right drag another line or left click to finish."));
+ }
+ }
+
+ break;
+
+ case Curve:
+ if (m_points.size () == 2)
+ {
+ if (m_mouseButton == 0)
+ {
+ setUserMessage (i18n ("Left drag to set the first control point or right click to finish."));
+ }
+ else
+ {
+ setUserMessage (i18n ("Right drag to set the first control point or left click to finish."));
+ }
+ }
+ else if (m_points.size () == 3)
+ {
+ if (m_mouseButton == 0)
+ {
+ setUserMessage (i18n ("Left drag to set the last control point or right click to finish."));
+ }
+ else
+ {
+ setUserMessage (i18n ("Right drag to set the last control point or left click to finish."));
+ }
+ }
+ else
+ {
+ kdError () << "kpToolPolygon::endDraw() exception - points" << endl;
+ setUserMessage ();
+ }
+
+ break;
+
+ default:
+ kdError () << "kpToolPolygon::endDraw() - clueless" << endl;
+ setUserMessage ();
+ break;
+ }
+ }
+}
+
+// public virtual
+void kpToolPolygon::endShape (const QPoint &, const QRect &)
+{
+#if DEBUG_KP_TOOL_POLYGON
+ kdDebug () << "kpToolPolygon::endShape() m_points=" << pointArrayToString (m_points) << endl;
+#endif
+
+ if (!hasBegunShape ())
+ return;
+
+ viewManager ()->invalidateTempPixmap ();
+
+ QRect boundingRect = kpTool::neededRect (m_points.boundingRect (), m_lineWidth);
+
+ kpToolPolygonCommand *lineCommand =
+ new kpToolPolygonCommand
+ (text (),
+ m_points, boundingRect,
+ color (m_mouseButton), color (1 - m_mouseButton),
+ m_lineWidth, Qt::SolidLine,
+ m_toolWidgetFillStyle,
+ document ()->getPixmapAt (boundingRect),
+ m_mode,
+ mainWindow ());
+
+ commandHistory ()->addCommand (lineCommand);
+
+ m_points.resize (0);
+ setUserMessage (haventBegunShapeUserMessage ());
+
+}
+
+// public virtual
+bool kpToolPolygon::hasBegunShape () const
+{
+ return (m_points.count () > 0);
+}
+
+
+// public slot
+void kpToolPolygon::slotLineWidthChanged (int width)
+{
+ m_lineWidth = width;
+ updateShape ();
+}
+
+// public slot
+void kpToolPolygon::slotFillStyleChanged (kpToolWidgetFillStyle::FillStyle /*fillStyle*/)
+{
+ updateShape ();
+}
+
+// virtual protected slot
+void kpToolPolygon::slotForegroundColorChanged (const kpColor &)
+{
+ updateShape ();
+}
+
+// virtual protected slot
+void kpToolPolygon::slotBackgroundColorChanged (const kpColor &)
+{
+ updateShape ();
+}
+
+
+/*
+ * kpToolPolygonCommand
+ */
+
+kpToolPolygonCommand::kpToolPolygonCommand (const QString &name,
+ const QPointArray &points,
+ const QRect &normalizedRect,
+ const kpColor &foregroundColor, const kpColor &backgroundColor,
+ int lineWidth, Qt::PenStyle lineStyle,
+ kpToolWidgetFillStyle *toolWidgetFillStyle,
+ const QPixmap &originalArea,
+ enum kpToolPolygon::Mode mode,
+ kpMainWindow *mainWindow)
+ : kpNamedCommand (name, mainWindow),
+ m_points (points),
+ m_normalizedRect (normalizedRect),
+ m_foregroundColor (foregroundColor), m_backgroundColor (backgroundColor),
+ m_lineWidth (lineWidth), m_lineStyle (lineStyle),
+ m_toolWidgetFillStyle (toolWidgetFillStyle),
+ m_originalArea (originalArea),
+ m_mode (mode)
+{
+ m_points.detach ();
+}
+
+kpToolPolygonCommand::~kpToolPolygonCommand ()
+{
+}
+
+
+// public virtual [base kpCommand]
+int kpToolPolygonCommand::size () const
+{
+ return kpPixmapFX::pointArraySize (m_points) +
+ kpPixmapFX::pixmapSize (m_originalArea);
+}
+
+
+// public virtual [base kpCommand]
+void kpToolPolygonCommand::execute ()
+{
+ QPixmap p = pixmap (m_originalArea,
+ m_points, m_normalizedRect,
+ m_foregroundColor, m_backgroundColor,
+ m_lineWidth, m_lineStyle,
+ m_toolWidgetFillStyle,
+ m_mode);
+ document ()->setPixmapAt (p, m_normalizedRect.topLeft ());
+}
+
+// public virtual [base kpCommand]
+void kpToolPolygonCommand::unexecute ()
+{
+ document ()->setPixmapAt (m_originalArea, m_normalizedRect.topLeft ());
+}
+
+#include <kptoolpolygon.moc>
diff --git a/kolourpaint/tools/kptoolpolygon.h b/kolourpaint/tools/kptoolpolygon.h
new file mode 100644
index 00000000..456dc4c0
--- /dev/null
+++ b/kolourpaint/tools/kptoolpolygon.h
@@ -0,0 +1,157 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kptoolpolygon_h__
+#define __kptoolpolygon_h__
+
+#include <qbrush.h>
+#include <qpen.h>
+#include <qobject.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qpointarray.h>
+#include <qrect.h>
+
+#include <kpcommandhistory.h>
+
+#include <kpcolor.h>
+#include <kptool.h>
+#include <kptoolwidgetfillstyle.h>
+
+class QMouseEvent;
+class QPen;
+class QPoint;
+class QRect;
+class QString;
+
+class kpView;
+class kpDocument;
+class kpMainWindow;
+
+class kpToolWidgetFillStyle;
+class kpToolWidgetLineWidth;
+class kpViewManager;
+
+class kpToolPolygon : public kpTool
+{
+Q_OBJECT
+
+public:
+ enum Mode
+ {
+ Polygon, Polyline, Line, Curve
+ };
+
+ kpToolPolygon (Mode mode, const QString &text, const QString &description,
+ int key,
+ kpMainWindow *mainWindow, const char *name);
+ kpToolPolygon (kpMainWindow *mainWindow);
+ virtual ~kpToolPolygon ();
+
+ void setMode (Mode mode);
+
+ virtual bool careAboutModifierState () const { return true; }
+
+private:
+ QString haventBegunShapeUserMessage () const;
+
+public:
+ virtual void begin ();
+ virtual void end ();
+
+ virtual void beginDraw ();
+ virtual void draw (const QPoint &, const QPoint &, const QRect &);
+ virtual void cancelShape ();
+ virtual void releasedAllButtons ();
+ virtual void endDraw (const QPoint &, const QRect &);
+ virtual void endShape (const QPoint & = QPoint (), const QRect & = QRect ());
+
+ virtual bool hasBegunShape () const;
+
+public slots:
+ void slotLineWidthChanged (int width);
+ void slotFillStyleChanged (kpToolWidgetFillStyle::FillStyle fillStyle);
+
+protected slots:
+ virtual void slotForegroundColorChanged (const kpColor &);
+ virtual void slotBackgroundColorChanged (const kpColor &);
+
+private slots:
+ void updateShape ();
+
+private:
+ Mode m_mode;
+
+ kpToolWidgetFillStyle *m_toolWidgetFillStyle;
+
+ int m_lineWidth;
+ kpToolWidgetLineWidth *m_toolWidgetLineWidth;
+
+ int m_originatingMouseButton;
+
+ void applyModifiers ();
+
+ QPoint m_toolLineStartPoint, m_toolLineEndPoint;
+ QRect m_toolLineRect;
+
+ QPointArray m_points;
+};
+
+class kpToolPolygonCommand : public kpNamedCommand
+{
+public:
+ kpToolPolygonCommand (const QString &name,
+ const QPointArray &points,
+ const QRect &normalizedRect,
+ const kpColor &foregroundColor, const kpColor &backgroundColor,
+ int lineWidth, Qt::PenStyle lineStyle,
+ kpToolWidgetFillStyle *toolWidgetFillStyle,
+ const QPixmap &originalArea,
+ kpToolPolygon::Mode mode,
+ kpMainWindow *mainWindow);
+ virtual ~kpToolPolygonCommand ();
+
+ virtual int size () const;
+
+ virtual void execute ();
+ virtual void unexecute ();
+
+private:
+ QPointArray m_points;
+ QRect m_normalizedRect;
+
+ kpColor m_foregroundColor, m_backgroundColor;
+ int m_lineWidth;
+ Qt::PenStyle m_lineStyle;
+ kpToolWidgetFillStyle *m_toolWidgetFillStyle;
+
+ QPixmap m_originalArea;
+ kpToolPolygon::Mode m_mode;
+};
+
+#endif // __kptoolpolygon_h__
diff --git a/kolourpaint/tools/kptoolpolyline.cpp b/kolourpaint/tools/kptoolpolyline.cpp
new file mode 100644
index 00000000..6299b5b7
--- /dev/null
+++ b/kolourpaint/tools/kptoolpolyline.cpp
@@ -0,0 +1,47 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <kptoolpolyline.h>
+
+#include <klocale.h>
+
+
+kpToolPolyline::kpToolPolyline (kpMainWindow *mainWindow)
+ : kpToolPolygon (Polyline,
+ i18n ("Connected Lines"),
+ i18n ("Draws connected lines"),
+ Qt::Key_N,
+ mainWindow, "tool_polyline")
+{
+}
+
+kpToolPolyline::~kpToolPolyline ()
+{
+}
+
+#include <kptoolpolyline.moc>
diff --git a/kolourpaint/tools/kptoolpolyline.h b/kolourpaint/tools/kptoolpolyline.h
new file mode 100644
index 00000000..f76a3959
--- /dev/null
+++ b/kolourpaint/tools/kptoolpolyline.h
@@ -0,0 +1,46 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kptoolpolyline_h__
+#define __kptoolpolyline_h__
+
+#include <kptoolpolygon.h>
+
+class kpMainWindow;
+
+class kpToolPolyline : public kpToolPolygon
+{
+Q_OBJECT
+
+public:
+ kpToolPolyline (kpMainWindow *);
+ virtual ~kpToolPolyline ();
+};
+
+#endif // __kptoolpolyline_h__
+
diff --git a/kolourpaint/tools/kptoolpreviewdialog.cpp b/kolourpaint/tools/kptoolpreviewdialog.cpp
new file mode 100644
index 00000000..23149232
--- /dev/null
+++ b/kolourpaint/tools/kptoolpreviewdialog.cpp
@@ -0,0 +1,431 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG_KP_TOOL_PREVIEW_DIALOG 0
+
+#include <kptoolpreviewdialog.h>
+
+#include <qapplication.h>
+#include <qlayout.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kpcolor.h>
+#include <kpdocument.h>
+#include <kpmainwindow.h>
+#include <kppixmapfx.h>
+#include <kpresizesignallinglabel.h>
+#include <kpselection.h>
+
+
+kpToolPreviewDialog::kpToolPreviewDialog (Features features,
+ bool reserveTopRow,
+ const QString &caption,
+ const QString &afterActionText,
+ bool actOnSelection,
+ kpMainWindow *parent,
+ const char *name)
+ : KDialogBase (parent, name, true/*modal*/,
+ caption,
+ KDialogBase::Ok | KDialogBase::Cancel),
+ m_afterActionText (afterActionText),
+ m_actOnSelection (actOnSelection),
+ m_mainWindow (parent),
+ m_dimensionsGroupBox (0),
+ m_afterTransformDimensionsLabel (0),
+ m_previewGroupBox (0),
+ m_previewPixmapLabel (0),
+ m_gridLayout (0)
+{
+ QWidget *baseWidget = new QWidget (this);
+ setMainWidget (baseWidget);
+
+
+ if (document ())
+ {
+ m_oldWidth = document ()->width (actOnSelection);
+ m_oldHeight = document ()->height (actOnSelection);
+ }
+ else
+ {
+ m_oldWidth = m_oldHeight = 1;
+ }
+
+
+ if (features & Dimensions)
+ createDimensionsGroupBox ();
+
+ if (features & Preview)
+ createPreviewGroupBox ();
+
+
+ m_gridLayout = new QGridLayout (baseWidget, 4, 2,
+ 0/*margin*/, spacingHint ());
+ m_gridNumRows = reserveTopRow ? 1 : 0;
+ if (m_dimensionsGroupBox || m_previewGroupBox)
+ {
+ if (m_dimensionsGroupBox && m_previewGroupBox)
+ {
+ m_gridLayout->addWidget (m_dimensionsGroupBox, m_gridNumRows, 0);
+ m_gridLayout->addWidget (m_previewGroupBox, m_gridNumRows, 1);
+
+ m_gridLayout->setColStretch (1, 1);
+ }
+ else if (m_dimensionsGroupBox)
+ {
+ m_gridLayout->addMultiCellWidget (m_dimensionsGroupBox,
+ m_gridNumRows, m_gridNumRows, 0, 1);
+ }
+ else if (m_previewGroupBox)
+ {
+ m_gridLayout->addMultiCellWidget (m_previewGroupBox,
+ m_gridNumRows, m_gridNumRows, 0, 1);
+ }
+
+ m_gridLayout->setRowStretch (m_gridNumRows, 1);
+ m_gridNumRows++;;
+ }
+}
+
+kpToolPreviewDialog::~kpToolPreviewDialog ()
+{
+}
+
+
+// private
+void kpToolPreviewDialog::createDimensionsGroupBox ()
+{
+ m_dimensionsGroupBox = new QGroupBox (i18n ("Dimensions"), mainWidget ());
+
+ QLabel *originalLabel = new QLabel (i18n ("Original:"), m_dimensionsGroupBox);
+ QString originalDimensions;
+ if (document ())
+ {
+ originalDimensions = i18n ("%1 x %2")
+ .arg (m_oldWidth)
+ .arg (m_oldHeight);
+
+ // Stop the Dimensions Group Box from resizing so often
+ const QString minimumLengthString ("100000 x 100000");
+ const int padLength = minimumLengthString.length ();
+ for (int i = originalDimensions.length (); i < padLength; i++)
+ originalDimensions += " ";
+ }
+ QLabel *originalDimensionsLabel = new QLabel (originalDimensions, m_dimensionsGroupBox);
+
+ QLabel *afterTransformLabel = new QLabel (m_afterActionText, m_dimensionsGroupBox);
+ m_afterTransformDimensionsLabel = new QLabel (m_dimensionsGroupBox);
+
+
+ QGridLayout *dimensionsLayout = new QGridLayout (m_dimensionsGroupBox,
+ 2, 2,
+ marginHint () * 2, spacingHint ());
+
+ dimensionsLayout->addWidget (originalLabel, 0, 0, Qt::AlignBottom);
+ dimensionsLayout->addWidget (originalDimensionsLabel, 0, 1, Qt::AlignBottom);
+ dimensionsLayout->addWidget (afterTransformLabel, 1, 0, Qt::AlignTop);
+ dimensionsLayout->addWidget (m_afterTransformDimensionsLabel, 1, 1, Qt::AlignTop);
+}
+
+// private
+void kpToolPreviewDialog::createPreviewGroupBox ()
+{
+ m_previewGroupBox = new QGroupBox (i18n ("Preview"), mainWidget ());
+
+ m_previewPixmapLabel = new kpResizeSignallingLabel (m_previewGroupBox);
+ m_previewPixmapLabel->setMinimumSize (150, 110);
+ connect (m_previewPixmapLabel, SIGNAL (resized ()),
+ this, SLOT (updatePreview ()));
+
+ QPushButton *updatePushButton = new QPushButton (i18n ("&Update"),
+ m_previewGroupBox);
+ connect (updatePushButton, SIGNAL (clicked ()),
+ this, SLOT (slotUpdateWithWaitCursor ()));
+
+
+ QVBoxLayout *previewLayout = new QVBoxLayout (m_previewGroupBox,
+ marginHint () * 2,
+ QMAX (1, spacingHint () / 2));
+
+ previewLayout->addWidget (m_previewPixmapLabel, 1/*stretch*/);
+ previewLayout->addWidget (updatePushButton, 0/*stretch*/, Qt::AlignHCenter);
+}
+
+
+// protected
+kpDocument *kpToolPreviewDialog::document () const
+{
+ return m_mainWindow ? m_mainWindow->document () : 0;
+}
+
+
+// protected
+void kpToolPreviewDialog::addCustomWidgetToFront (QWidget *w)
+{
+ m_gridLayout->addMultiCellWidget (w, 0, 0, 0, 1);
+}
+
+// protected
+void kpToolPreviewDialog::addCustomWidget (QWidget *w)
+{
+ m_gridLayout->addMultiCellWidget (w, m_gridNumRows, m_gridNumRows, 0, 1);
+ m_gridNumRows++;
+}
+
+
+// private
+void kpToolPreviewDialog::updateDimensions ()
+{
+ if (!m_dimensionsGroupBox)
+ return;
+
+ kpDocument *doc = document ();
+ if (!doc)
+ return;
+
+ QSize newDim = newDimensions ();
+#if DEBUG_KP_TOOL_PREVIEW_DIALOG
+ kdDebug () << "kpToolPreviewDialog::updateDimensions(): newDim=" << newDim << endl;
+#endif
+
+ QString newDimString = i18n ("%1 x %2")
+ .arg (newDim.width ())
+ .arg (newDim.height ());
+ m_afterTransformDimensionsLabel->setText (newDimString);
+}
+
+
+// public static
+double kpToolPreviewDialog::aspectScale (int newWidth, int newHeight,
+ int oldWidth, int oldHeight)
+{
+ double widthScale = double (newWidth) / double (oldWidth);
+ double heightScale = double (newHeight) / double (oldHeight);
+
+ // Keeps aspect ratio
+ return QMIN (widthScale, heightScale);
+}
+
+// public static
+int kpToolPreviewDialog::scaleDimension (int dimension, double scale, int min, int max)
+{
+ return QMAX (min,
+ QMIN (max,
+ qRound (dimension * scale)));
+}
+
+
+// private
+void kpToolPreviewDialog::updateShrukenDocumentPixmap ()
+{
+#if DEBUG_KP_TOOL_PREVIEW_DIALOG
+ kdDebug () << "kpToolPreviewDialog::updateShrukenDocumentPixmap()"
+ << " shrunkenDocPixmap.size="
+ << m_shrunkenDocumentPixmap.size ()
+ << " previewPixmapLabelSizeWhenUpdatedPixmap="
+ << m_previewPixmapLabelSizeWhenUpdatedPixmap
+ << " previewPixmapLabel.size="
+ << m_previewPixmapLabel->size ()
+ << endl;
+#endif
+
+ if (!m_previewGroupBox)
+ return;
+
+
+ kpDocument *doc = document ();
+ if (!doc || !doc->pixmap ())
+ {
+ kdError () << "kpToolPreviewDialog::updateShrunkenDocumentPixmap() doc="
+ << doc << endl;
+ return;
+ }
+
+ if (m_shrunkenDocumentPixmap.isNull () ||
+ m_previewPixmapLabel->size () != m_previewPixmapLabelSizeWhenUpdatedPixmap)
+ {
+ #if DEBUG_KP_TOOL_PREVIEW_DIALOG
+ kdDebug () << "\tupdating shrunkenDocPixmap" << endl;
+ #endif
+
+ // TODO: Why the need to keep aspect ratio here?
+ // Isn't scaling the skewed result maintaining aspect enough?
+ double keepsAspectScale = aspectScale (m_previewPixmapLabel->width (),
+ m_previewPixmapLabel->height (),
+ m_oldWidth,
+ m_oldHeight);
+
+ QPixmap pixmap;
+
+ if (m_actOnSelection)
+ {
+ kpSelection sel = *doc->selection ();
+ if (!sel.pixmap ())
+ sel.setPixmap (doc->getSelectedPixmap ());
+
+ pixmap = sel.transparentPixmap ();
+ }
+ else
+ {
+ pixmap = *doc->pixmap ();
+ }
+
+ m_shrunkenDocumentPixmap = kpPixmapFX::scale (
+ pixmap,
+ scaleDimension (m_oldWidth,
+ keepsAspectScale,
+ 1, m_previewPixmapLabel->width ()),
+ scaleDimension (m_oldHeight,
+ keepsAspectScale,
+ 1, m_previewPixmapLabel->height ()));
+ #if 0
+ m_shrunkenDocumentPixmap = kpPixmapFX::scale (
+ m_actOnSelection ? doc->getSelectedPixmap () : *doc->pixmap (),
+ m_previewPixmapLabel->width (),
+ m_previewPixmapLabel->height ());
+ #endif
+
+ m_previewPixmapLabelSizeWhenUpdatedPixmap = m_previewPixmapLabel->size ();
+ }
+}
+
+
+// private
+void kpToolPreviewDialog::updatePreview ()
+{
+#if DEBUG_KP_TOOL_PREVIEW_DIALOG
+ kdDebug () << "kpToolPreviewDialog::updatePreview()" << endl;
+#endif
+
+ if (!m_previewGroupBox)
+ return;
+
+
+ kpDocument *doc = document ();
+ if (!doc)
+ return;
+
+ updateShrukenDocumentPixmap ();
+
+ if (!m_shrunkenDocumentPixmap.isNull ())
+ {
+ QSize newDim = newDimensions ();
+ double keepsAspectScale = aspectScale (m_previewPixmapLabel->width (),
+ m_previewPixmapLabel->height (),
+ newDim.width (),
+ newDim.height ());
+
+ int targetWidth = scaleDimension (newDim.width (),
+ keepsAspectScale,
+ 1, // min
+ m_previewPixmapLabel->width ()); // max
+ int targetHeight = scaleDimension (newDim.height (),
+ keepsAspectScale,
+ 1, // min
+ m_previewPixmapLabel->height ()); // max
+
+ // TODO: Some effects work directly on QImage; so could cache the
+ // QImage so that transformPixmap() is faster
+ QPixmap transformedShrunkenDocumentPixmap =
+ transformPixmap (m_shrunkenDocumentPixmap, targetWidth, targetHeight);
+
+ QPixmap previewPixmap (m_previewPixmapLabel->width (),
+ m_previewPixmapLabel->height ());
+ kpPixmapFX::fill (&previewPixmap, kpColor::transparent);
+ kpPixmapFX::setPixmapAt (&previewPixmap,
+ (previewPixmap.width () - transformedShrunkenDocumentPixmap.width ()) / 2,
+ (previewPixmap.height () - transformedShrunkenDocumentPixmap.height ()) / 2,
+ transformedShrunkenDocumentPixmap);
+
+#if DEBUG_KP_TOOL_PREVIEW_DIALOG
+ kdDebug () << "kpToolPreviewDialog::updatePreview ():"
+ << " shrunkenDocumentPixmap: w="
+ << m_shrunkenDocumentPixmap.width ()
+ << " h="
+ << m_shrunkenDocumentPixmap.height ()
+ << " previewPixmapLabel: w="
+ << m_previewPixmapLabel->width ()
+ << " h="
+ << m_previewPixmapLabel->height ()
+ << " transformedShrunkenDocumentPixmap: w="
+ << transformedShrunkenDocumentPixmap.width ()
+ << " h="
+ << transformedShrunkenDocumentPixmap.height ()
+ << " previewPixmap: w="
+ << previewPixmap.width ()
+ << " h="
+ << previewPixmap.height ()
+ << endl;
+#endif
+
+ m_previewPixmapLabel->setPixmap (previewPixmap);
+
+ // immediate update esp. for expensive previews
+ m_previewPixmapLabel->repaint (false/*no erase*/);
+
+#if DEBUG_KP_TOOL_PREVIEW_DIALOG
+ kdDebug () << "\tafter QLabel::setPixmap() previewPixmapLabel: w="
+ << m_previewPixmapLabel->width ()
+ << " h="
+ << m_previewPixmapLabel->height ()
+ << endl;
+#endif
+ }
+}
+
+
+// protected slot virtual
+void kpToolPreviewDialog::slotUpdate ()
+{
+#if DEBUG_KP_TOOL_PREVIEW_DIALOG
+ kdDebug () << "kpToolPreviewDialog::slotUpdate()" << endl;
+#endif
+ updateDimensions ();
+ updatePreview ();
+}
+
+// protected slot virtual
+void kpToolPreviewDialog::slotUpdateWithWaitCursor ()
+{
+#if DEBUG_KP_TOOL_PREVIEW_DIALOG
+ kdDebug () << "kpToolPreviewDialog::slotUpdateWithWaitCursor()"
+ << endl;
+#endif
+
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+ slotUpdate ();
+
+ QApplication::restoreOverrideCursor ();
+}
+
+
+#include <kptoolpreviewdialog.moc>
diff --git a/kolourpaint/tools/kptoolpreviewdialog.h b/kolourpaint/tools/kptoolpreviewdialog.h
new file mode 100644
index 00000000..35efdc38
--- /dev/null
+++ b/kolourpaint/tools/kptoolpreviewdialog.h
@@ -0,0 +1,131 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef __kp_tool_preview_dialog_h__
+#define __kp_tool_preview_dialog_h__
+
+
+#include <qpixmap.h>
+
+#include <kdialogbase.h>
+
+
+class QLabel;
+class QGridLayout;
+class QGroupBox;
+
+class kpDocument;
+class kpMainWindow;
+class kpResizeSignallingLabel;
+
+
+class kpToolPreviewDialog : public KDialogBase
+{
+Q_OBJECT
+
+public:
+ enum Features
+ {
+ Dimensions = 1, Preview = 2,
+ AllFeatures = Dimensions | Preview
+ };
+
+ // You must call slotUpdate() in your constructor
+ kpToolPreviewDialog (Features features,
+ bool reserveTopRow,
+ // e.g. "Skew (Image|Selection)"
+ const QString &caption,
+ // (in the Dimensions Group Box) e.g. "After Skew:"
+ const QString &afterActionText,
+ bool actOnSelection,
+ kpMainWindow *parent,
+ const char *name = 0);
+ virtual ~kpToolPreviewDialog ();
+
+private:
+ void createDimensionsGroupBox ();
+ void createPreviewGroupBox ();
+
+public:
+ virtual bool isNoOp () const = 0;
+
+protected:
+ kpDocument *document () const;
+
+ // All widgets must have mainWidget() as their parent
+ void addCustomWidgetToFront (QWidget *w); // see <reserveTopRow> in ctor
+ void addCustomWidget (QWidget *w);
+ void addCustomWidgetToBack (QWidget *w)
+ {
+ addCustomWidget (w);
+ }
+
+ virtual QSize newDimensions () const = 0;
+ virtual QPixmap transformPixmap (const QPixmap &pixmap,
+ int targetWidth, int targetHeight) const = 0;
+
+private:
+ void updateDimensions ();
+
+public:
+ static double aspectScale (int newWidth, int newHeight,
+ int oldWidth, int oldHeight);
+ static int scaleDimension (int dimension, double scale, int min, int max);
+
+private:
+ void updateShrukenDocumentPixmap ();
+
+protected slots:
+ void updatePreview ();
+
+ // Call this whenever a value (e.g. an angle) changes
+ // and the Dimensions & Preview need to be updated
+ virtual void slotUpdate ();
+
+ virtual void slotUpdateWithWaitCursor ();
+
+protected:
+ QString m_afterActionText;
+ bool m_actOnSelection;
+ kpMainWindow *m_mainWindow;
+
+ int m_oldWidth, m_oldHeight;
+
+ QGroupBox *m_dimensionsGroupBox;
+ QLabel *m_afterTransformDimensionsLabel;
+
+ QGroupBox *m_previewGroupBox;
+ kpResizeSignallingLabel *m_previewPixmapLabel;
+ QSize m_previewPixmapLabelSizeWhenUpdatedPixmap;
+ QPixmap m_shrunkenDocumentPixmap;
+
+ QGridLayout *m_gridLayout;
+ int m_gridNumRows;
+};
+
+
+#endif // __kp_tool_preview_dialog_h__
diff --git a/kolourpaint/tools/kptoolrectangle.cpp b/kolourpaint/tools/kptoolrectangle.cpp
new file mode 100644
index 00000000..275db667
--- /dev/null
+++ b/kolourpaint/tools/kptoolrectangle.cpp
@@ -0,0 +1,638 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define DEBUG_KP_TOOL_RECTANGLE 0
+
+#include <qbitmap.h>
+#include <qcursor.h>
+#include <qevent.h>
+#include <qpainter.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kpcolor.h>
+#include <kpcommandhistory.h>
+#include <kpdefs.h>
+#include <kpdocument.h>
+#include <kpmainwindow.h>
+#include <kppixmapfx.h>
+#include <kptemppixmap.h>
+#include <kptoolrectangle.h>
+#include <kptooltoolbar.h>
+#include <kptoolwidgetfillstyle.h>
+#include <kptoolwidgetlinewidth.h>
+#include <kpview.h>
+#include <kpviewmanager.h>
+
+static QPixmap pixmap (const kpToolRectangle::Mode mode,
+ kpDocument *document, const QRect &rect,
+ const QPoint &startPoint, const QPoint &endPoint,
+ const QPen &pen, const QPen &maskPen,
+ const QBrush &brush, const QBrush &maskBrush)
+{
+ QPixmap pixmap = document->getPixmapAt (rect);
+ QBitmap maskBitmap;
+
+ QPainter painter, maskPainter;
+
+#if DEBUG_KP_TOOL_RECTANGLE && 1
+ kdDebug () << "pixmap: rect=" << rect
+ << " startPoint=" << startPoint
+ << " endPoint=" << endPoint
+ << endl;
+ kdDebug () << "\tm: p=" << (maskPen.style () != Qt::NoPen)
+ << " b=" << (maskBrush.style () != Qt::NoBrush)
+ << " o: p=" << (pen.style () != Qt::NoPen)
+ << " b=" << (brush.style () != Qt::NoBrush)
+ << endl;
+ kdDebug () << "\tmaskPen.color()=" << (int *) maskPen.color ().rgb ()
+ << " transparent=" << (int *) Qt::color0.rgb ()/*transparent*/
+ << endl;
+#endif
+
+ if (pixmap.mask () ||
+ (maskPen.style () != Qt::NoPen &&
+ maskPen.color () == Qt::color0/*transparent*/) ||
+ (maskBrush.style () != Qt::NoBrush &&
+ maskBrush.color () == Qt::color0/*transparent*/))
+ {
+ maskBitmap = kpPixmapFX::getNonNullMask (pixmap);
+ maskPainter.begin (&maskBitmap);
+ maskPainter.setPen (maskPen);
+ maskPainter.setBrush (maskBrush);
+ }
+
+ if (pen.style () != Qt::NoPen ||
+ brush.style () != Qt::NoBrush)
+ {
+ painter.begin (&pixmap);
+ painter.setPen (pen);
+ painter.setBrush (brush);
+ }
+
+#define PAINTER_CALL(cmd) \
+{ \
+ if (painter.isActive ()) \
+ painter . cmd ; \
+ \
+ if (maskPainter.isActive ()) \
+ maskPainter . cmd ; \
+}
+
+ if (startPoint != endPoint)
+ {
+ #if DEBUG_KP_TOOL_RECTANGLE && 1
+ kdDebug () << "\tdraw shape" << endl;
+ #endif
+
+ // TODO: Rectangle of pen width 1, height 1 and width X is rendered
+ // as width X - 1.
+ switch (mode)
+ {
+ case kpToolRectangle::Rectangle:
+ PAINTER_CALL (drawRect (QRect (startPoint - rect.topLeft (), endPoint - rect.topLeft ())));
+ break;
+ case kpToolRectangle::RoundedRectangle:
+ PAINTER_CALL (drawRoundRect (QRect (startPoint - rect.topLeft (), endPoint - rect.topLeft ())));
+ break;
+ case kpToolRectangle::Ellipse:
+ PAINTER_CALL (drawEllipse (QRect (startPoint - rect.topLeft (), endPoint - rect.topLeft ())));
+ break;
+ default:
+ kdError () << "kptoolrectangle.cpp::pixmap() passed unknown mode: " << int (mode) << endl;
+ break;
+ }
+ }
+ else
+ {
+ #if DEBUG_KP_TOOL_RECTANGLE && 1
+ kdDebug () << "\tstartPoint == endPoint" << endl;
+ #endif
+ // SYNC: Work around Qt bug: can't draw 1x1 rectangle
+ // Not strictly correct for border width > 1
+ // but better than not drawing at all
+ PAINTER_CALL (drawPoint (startPoint - rect.topLeft ()));
+ }
+#undef PAINTER_CALL
+
+ if (painter.isActive ())
+ painter.end ();
+
+ if (maskPainter.isActive ())
+ maskPainter.end ();
+
+ if (!maskBitmap.isNull ())
+ pixmap.setMask (maskBitmap);
+
+ return pixmap;
+}
+
+
+/*
+ * kpToolRectangle
+ */
+
+kpToolRectangle::kpToolRectangle (Mode mode,
+ const QString &text,
+ const QString &description,
+ int key,
+ kpMainWindow *mainWindow,
+ const char *name)
+ : kpTool (text, description, key, mainWindow, name),
+ m_mode (mode),
+ m_toolWidgetLineWidth (0),
+ m_toolWidgetFillStyle (0)
+{
+}
+
+kpToolRectangle::kpToolRectangle (kpMainWindow *mainWindow)
+ : kpTool (i18n ("Rectangle"), i18n ("Draws rectangles and squares"),
+ Qt::Key_R,
+ mainWindow, "tool_rectangle"),
+ m_mode (Rectangle),
+ m_toolWidgetLineWidth (0),
+ m_toolWidgetFillStyle (0)
+{
+}
+
+kpToolRectangle::~kpToolRectangle ()
+{
+}
+
+void kpToolRectangle::setMode (Mode mode)
+{
+ m_mode = mode;
+}
+
+
+// private
+void kpToolRectangle::updatePens ()
+{
+ for (int i = 0; i < 2; i++)
+ updatePen (i);
+}
+
+// private
+void kpToolRectangle::updateBrushes ()
+{
+ for (int i = 0; i < 2; i++)
+ updateBrush (i);
+}
+
+// virtual private slot
+void kpToolRectangle::slotForegroundColorChanged (const kpColor &)
+{
+#if DEBUG_KP_TOOL_RECTANGLE
+ kdDebug () << "kpToolRectangle::slotForegroundColorChanged()" << endl;
+#endif
+ updatePen (0);
+ updateBrush (0); // brush may be in foreground color
+ updateBrush (1);
+}
+
+// virtual private slot
+void kpToolRectangle::slotBackgroundColorChanged (const kpColor &)
+{
+#if DEBUG_KP_TOOL_RECTANGLE
+ kdDebug () << "kpToolRectangle::slotBackgroundColorChanged()" << endl;
+ kdDebug () << "\tm_toolWidgetFillStyle=" << m_toolWidgetFillStyle << endl;
+#endif
+ updatePen (1);
+ updateBrush (0);
+ updateBrush (1); // brush may be in background color
+}
+
+// private
+void kpToolRectangle::updatePen (int mouseButton)
+{
+ QColor maskPenColor = color (mouseButton).maskColor ();
+
+ if (!m_toolWidgetLineWidth)
+ {
+ if (color (mouseButton).isOpaque ())
+ m_pen [mouseButton] = QPen (color (mouseButton).toQColor ());
+ else
+ m_pen [mouseButton] = Qt::NoPen;
+ m_maskPen [mouseButton] = QPen (maskPenColor);
+ }
+ else
+ {
+ if (color (mouseButton).isOpaque ())
+ {
+ m_pen [mouseButton] = QPen (color (mouseButton).toQColor (),
+ m_toolWidgetLineWidth->lineWidth (),
+ Qt::SolidLine);
+ }
+ else
+ m_pen [mouseButton] = Qt::NoPen;
+ m_maskPen [mouseButton] = QPen (maskPenColor,
+ m_toolWidgetLineWidth->lineWidth (),
+ Qt::SolidLine);
+ }
+}
+
+void kpToolRectangle::updateBrush (int mouseButton)
+{
+#if DEBUG_KP_TOOL_RECTANGLE
+ kdDebug () << "kpToolRectangle::brush () mouseButton=" << mouseButton
+ << " m_toolWidgetFillStyle=" << m_toolWidgetFillStyle
+ << endl;
+#endif
+ if (m_toolWidgetFillStyle)
+ {
+ m_brush [mouseButton] = m_toolWidgetFillStyle->brush (
+ color (mouseButton)/*foreground colour*/,
+ color (1 - mouseButton)/*background colour*/);
+
+ m_maskBrush [mouseButton] = m_toolWidgetFillStyle->maskBrush (
+ color (mouseButton)/*foreground colour*/,
+ color (1 - mouseButton)/*background colour*/);
+ }
+ else
+ {
+ m_brush [mouseButton] = Qt::NoBrush;
+ m_maskBrush [mouseButton] = Qt::NoBrush;
+ }
+}
+
+
+// private slot virtual
+void kpToolRectangle::slotLineWidthChanged ()
+{
+ updatePens ();
+
+ if (hasBegunDraw ())
+ updateShape ();
+}
+
+// private slot virtual
+void kpToolRectangle::slotFillStyleChanged ()
+{
+ updateBrushes ();
+
+ if (hasBegunDraw ())
+ updateShape ();
+}
+
+
+// private
+QString kpToolRectangle::haventBegunDrawUserMessage () const
+{
+ return i18n ("Drag to draw.");
+}
+
+// virtual
+void kpToolRectangle::begin ()
+{
+#if DEBUG_KP_TOOL_RECTANGLE
+ kdDebug () << "kpToolRectangle::begin ()" << endl;
+#endif
+
+ kpToolToolBar *tb = toolToolBar ();
+
+#if DEBUG_KP_TOOL_RECTANGLE
+ kdDebug () << "\ttoolToolBar=" << tb << endl;
+#endif
+
+ if (tb)
+ {
+ m_toolWidgetLineWidth = tb->toolWidgetLineWidth ();
+ connect (m_toolWidgetLineWidth, SIGNAL (lineWidthChanged (int)),
+ this, SLOT (slotLineWidthChanged ()));
+ m_toolWidgetLineWidth->show ();
+
+ updatePens ();
+
+
+ m_toolWidgetFillStyle = tb->toolWidgetFillStyle ();
+ connect (m_toolWidgetFillStyle, SIGNAL (fillStyleChanged (kpToolWidgetFillStyle::FillStyle)),
+ this, SLOT (slotFillStyleChanged ()));
+ m_toolWidgetFillStyle->show ();
+
+ updateBrushes ();
+ }
+
+#if DEBUG_KP_TOOL_RECTANGLE
+ kdDebug () << "\t\tm_toolWidgetFillStyle=" << m_toolWidgetFillStyle << endl;
+#endif
+
+ viewManager ()->setCursor (QCursor (CrossCursor));
+
+ setUserMessage (haventBegunDrawUserMessage ());
+}
+
+// virtual
+void kpToolRectangle::end ()
+{
+#if DEBUG_KP_TOOL_RECTANGLE
+ kdDebug () << "kpToolRectangle::end ()" << endl;
+#endif
+
+ if (m_toolWidgetLineWidth)
+ {
+ disconnect (m_toolWidgetLineWidth, SIGNAL (lineWidthChanged (int)),
+ this, SLOT (slotLineWidthChanged ()));
+ m_toolWidgetLineWidth = 0;
+ }
+
+ if (m_toolWidgetFillStyle)
+ {
+ disconnect (m_toolWidgetFillStyle, SIGNAL (fillStyleChanged (kpToolWidgetFillStyle::FillStyle)),
+ this, SLOT (slotFillStyleChanged ()));
+ m_toolWidgetFillStyle = 0;
+ }
+
+ viewManager ()->unsetCursor ();
+}
+
+void kpToolRectangle::applyModifiers ()
+{
+ QRect rect = QRect (m_startPoint, m_currentPoint).normalize ();
+
+#if DEBUG_KP_TOOL_RECTANGLE
+ kdDebug () << "kpToolRectangle::applyModifiers(" << rect
+ << ") shift=" << m_shiftPressed
+ << " ctrl=" << m_controlPressed
+ << endl;
+#endif
+
+ // user wants to m_startPoint == centre
+ if (m_controlPressed)
+ {
+ int xdiff = kAbs (m_startPoint.x () - m_currentPoint.x ());
+ int ydiff = kAbs (m_startPoint.y () - m_currentPoint.y ());
+ rect = QRect (m_startPoint.x () - xdiff, m_startPoint.y () - ydiff,
+ xdiff * 2 + 1, ydiff * 2 + 1);
+ }
+
+ // user wants major axis == minor axis:
+ // rectangle --> square
+ // rounded rectangle --> rounded square
+ // ellipse --> circle
+ if (m_shiftPressed)
+ {
+ if (!m_controlPressed)
+ {
+ if (rect.width () < rect.height ())
+ {
+ if (m_startPoint.y () == rect.y ())
+ rect.setHeight (rect.width ());
+ else
+ rect.setY (rect.bottom () - rect.width () + 1);
+ }
+ else
+ {
+ if (m_startPoint.x () == rect.x ())
+ rect.setWidth (rect.height ());
+ else
+ rect.setX (rect.right () - rect.height () + 1);
+ }
+ }
+ // have to maintain the centre
+ else
+ {
+ if (rect.width () < rect.height ())
+ {
+ QPoint center = rect.center ();
+ rect.setHeight (rect.width ());
+ rect.moveCenter (center);
+ }
+ else
+ {
+ QPoint center = rect.center ();
+ rect.setWidth (rect.height ());
+ rect.moveCenter (center);
+ }
+ }
+ }
+
+ m_toolRectangleStartPoint = rect.topLeft ();
+ m_toolRectangleEndPoint = rect.bottomRight ();
+
+ m_toolRectangleRectWithoutLineWidth = rect;
+ m_toolRectangleRect = kpTool::neededRect (rect, QMAX (m_pen [m_mouseButton].width (),
+ m_maskPen [m_mouseButton].width ()));
+}
+
+void kpToolRectangle::beginDraw ()
+{
+ setUserMessage (cancelUserMessage ());
+}
+
+void kpToolRectangle::updateShape ()
+{
+ viewManager ()->setFastUpdates ();
+
+ QPixmap newPixmap = pixmap (m_mode, document (), m_toolRectangleRect,
+ m_toolRectangleStartPoint, m_toolRectangleEndPoint,
+ m_pen [m_mouseButton], m_maskPen [m_mouseButton],
+ m_brush [m_mouseButton], m_maskBrush [m_mouseButton]);
+ kpTempPixmap newTempPixmap (false/*always display*/,
+ kpTempPixmap::SetPixmap/*render mode*/,
+ m_toolRectangleRect.topLeft (),
+ newPixmap);
+ viewManager ()->setTempPixmap (newTempPixmap);
+
+ viewManager ()->restoreFastUpdates ();
+}
+
+void kpToolRectangle::draw (const QPoint &, const QPoint &, const QRect &)
+{
+ applyModifiers ();
+
+
+ updateShape ();
+
+
+ // Recover the start and end points from the transformed & normalized m_toolRectangleRect
+
+ // S. or S or SC or S == C
+ // .C C
+ if (m_currentPoint.x () >= m_startPoint.x () &&
+ m_currentPoint.y () >= m_startPoint.y ())
+ {
+ setUserShapePoints (m_toolRectangleRectWithoutLineWidth.topLeft (),
+ m_toolRectangleRectWithoutLineWidth.bottomRight ());
+ }
+ // .C or C
+ // S. S
+ else if (m_currentPoint.x () >= m_startPoint.x () &&
+ m_currentPoint.y () < m_startPoint.y ())
+ {
+ setUserShapePoints (m_toolRectangleRectWithoutLineWidth.bottomLeft (),
+ m_toolRectangleRectWithoutLineWidth.topRight ());
+ }
+ // .S or CS
+ // C.
+ else if (m_currentPoint.x () < m_startPoint.x () &&
+ m_currentPoint.y () >= m_startPoint.y ())
+ {
+ setUserShapePoints (m_toolRectangleRectWithoutLineWidth.topRight (),
+ m_toolRectangleRectWithoutLineWidth.bottomLeft ());
+ }
+ // C.
+ // .S
+ else
+ {
+ setUserShapePoints (m_toolRectangleRectWithoutLineWidth.bottomRight (),
+ m_toolRectangleRectWithoutLineWidth.topLeft ());
+ }
+}
+
+void kpToolRectangle::cancelShape ()
+{
+#if 0
+ endDraw (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ());
+ mainWindow ()->commandHistory ()->undo ();
+#else
+ viewManager ()->invalidateTempPixmap ();
+#endif
+
+ setUserMessage (i18n ("Let go of all the mouse buttons."));
+}
+
+void kpToolRectangle::releasedAllButtons ()
+{
+ setUserMessage (haventBegunDrawUserMessage ());
+}
+
+void kpToolRectangle::endDraw (const QPoint &, const QRect &)
+{
+ applyModifiers ();
+
+ // TODO: flicker
+ viewManager ()->invalidateTempPixmap ();
+
+ mainWindow ()->commandHistory ()->addCommand (
+ new kpToolRectangleCommand
+ (m_mode,
+ m_pen [m_mouseButton], m_maskPen [m_mouseButton],
+ m_brush [m_mouseButton], m_maskBrush [m_mouseButton],
+ m_toolRectangleRect, m_toolRectangleStartPoint, m_toolRectangleEndPoint,
+ mainWindow ()));
+
+ setUserMessage (haventBegunDrawUserMessage ());
+}
+
+
+/*
+ * kpToolRectangleCommand
+ */
+
+kpToolRectangleCommand::kpToolRectangleCommand (kpToolRectangle::Mode mode,
+ const QPen &pen, const QPen &maskPen,
+ const QBrush &brush, const QBrush &maskBrush,
+ const QRect &rect,
+ const QPoint &startPoint, const QPoint &endPoint,
+ kpMainWindow *mainWindow)
+ : kpCommand (mainWindow),
+ m_mode (mode),
+ m_pen (pen), m_maskPen (maskPen),
+ m_brush (brush), m_maskBrush (maskBrush),
+ m_rect (rect),
+ m_startPoint (startPoint),
+ m_endPoint (endPoint),
+ m_oldPixmapPtr (0)
+{
+}
+
+kpToolRectangleCommand::~kpToolRectangleCommand ()
+{
+ delete m_oldPixmapPtr;
+}
+
+
+// public virtual [base kpCommand]
+QString kpToolRectangleCommand::name () const
+{
+ switch (m_mode)
+ {
+ case kpToolRectangle::Rectangle:
+ return i18n ("Rectangle");
+ case kpToolRectangle::RoundedRectangle:
+ return i18n ("Rounded Rectangle");
+ case kpToolRectangle::Ellipse:
+ return i18n ("Ellipse");
+ default:
+ kdError () << "kpToolRectangleCommand::name() passed unknown mode: " << int (m_mode) << endl;
+ return QString::null;
+ }
+}
+
+
+// public virtual [base kpCommand]
+int kpToolRectangleCommand::size () const
+{
+ return kpPixmapFX::pixmapSize (m_oldPixmapPtr);
+}
+
+
+// public virtual [base kpCommand]
+void kpToolRectangleCommand::execute ()
+{
+ kpDocument *doc = document ();
+ if (!doc)
+ return;
+
+ // store Undo info
+ if (!m_oldPixmapPtr)
+ {
+ // OPT: I can do better with no brush
+ m_oldPixmapPtr = new QPixmap ();
+ *m_oldPixmapPtr = doc->getPixmapAt (m_rect);
+ }
+ else
+ kdError () << "kpToolRectangleCommand::execute() m_oldPixmapPtr not null" << endl;
+
+ doc->setPixmapAt (pixmap (m_mode, doc,
+ m_rect, m_startPoint, m_endPoint,
+ m_pen, m_maskPen,
+ m_brush, m_maskBrush),
+ m_rect.topLeft ());
+}
+
+// public virtual [base kpCommand]
+void kpToolRectangleCommand::unexecute ()
+{
+ kpDocument *doc = document ();
+ if (!doc)
+ return;
+
+ if (m_oldPixmapPtr)
+ {
+ doc->setPixmapAt (*m_oldPixmapPtr, m_rect.topLeft ());
+
+ delete m_oldPixmapPtr;
+ m_oldPixmapPtr = 0;
+ }
+ else
+ kdError () << "kpToolRectangleCommand::unexecute() m_oldPixmapPtr null" << endl;
+}
+
+#include <kptoolrectangle.moc>
diff --git a/kolourpaint/tools/kptoolrectangle.h b/kolourpaint/tools/kptoolrectangle.h
new file mode 100644
index 00000000..0fcf5ff4
--- /dev/null
+++ b/kolourpaint/tools/kptoolrectangle.h
@@ -0,0 +1,142 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kptoolrectangle_h__
+#define __kptoolrectangle_h__
+
+#include <qbrush.h>
+#include <qpen.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qrect.h>
+
+#include <kpcommandhistory.h>
+
+#include <kptool.h>
+
+class QString;
+
+class kpColor;
+class kpMainWindow;
+class kpToolWidgetFillStyle;
+class kpToolWidgetLineWidth;
+class kpViewManager;
+
+class kpToolRectangle : public kpTool
+{
+Q_OBJECT
+
+public:
+ // it turns out that these shapes are all really the same thing
+ // (same options, same feel) - the only real difference is the
+ // drawing functions (a one line change)
+ enum Mode {Rectangle, RoundedRectangle, Ellipse};
+
+ kpToolRectangle (Mode mode,
+ const QString &text, const QString &description,
+ int key,
+ kpMainWindow *mainWindow,
+ const char *name);
+ kpToolRectangle (kpMainWindow *);
+ virtual ~kpToolRectangle ();
+
+ void setMode (Mode mode);
+
+ virtual bool careAboutModifierState () const { return true; }
+
+private:
+ QString haventBegunDrawUserMessage () const;
+
+public:
+ virtual void begin ();
+ virtual void end ();
+
+ virtual void beginDraw ();
+private:
+ void updateShape ();
+public:
+ virtual void draw (const QPoint &, const QPoint &, const QRect &);
+ virtual void cancelShape ();
+ virtual void releasedAllButtons ();
+ virtual void endDraw (const QPoint &, const QRect &);
+
+private slots:
+ void updatePens ();
+ void updateBrushes ();
+
+ virtual void slotForegroundColorChanged (const kpColor &);
+ virtual void slotBackgroundColorChanged (const kpColor &);
+
+ virtual void slotLineWidthChanged ();
+ virtual void slotFillStyleChanged ();
+
+private:
+ Mode m_mode;
+
+ kpToolWidgetLineWidth *m_toolWidgetLineWidth;
+ kpToolWidgetFillStyle *m_toolWidgetFillStyle;
+
+ void updatePen (int mouseButton);
+ QPen m_pen [2], m_maskPen [2];
+
+ void updateBrush (int mouseButton);
+ QBrush m_brush [2], m_maskBrush [2];
+
+ void applyModifiers ();
+ QPoint m_toolRectangleStartPoint, m_toolRectangleEndPoint;
+ QRect m_toolRectangleRectWithoutLineWidth, m_toolRectangleRect;
+};
+
+class kpToolRectangleCommand : public kpCommand
+{
+public:
+ kpToolRectangleCommand (kpToolRectangle::Mode mode,
+ const QPen &pen, const QPen &maskPen,
+ const QBrush &brush, const QBrush &maskBrush,
+ const QRect &rect,
+ const QPoint &startPoint, const QPoint &endPoint,
+ kpMainWindow *mainWindow);
+ virtual ~kpToolRectangleCommand ();
+
+ virtual QString name () const;
+
+ virtual int size () const;
+
+ virtual void execute ();
+ virtual void unexecute ();
+
+private:
+ kpToolRectangle::Mode m_mode;
+ QPen m_pen, m_maskPen;
+ QBrush m_brush, m_maskBrush;
+ QRect m_rect;
+ QPoint m_startPoint, m_endPoint;
+ QPixmap *m_oldPixmapPtr;
+};
+
+#endif // __kptoolrectangle_h__
diff --git a/kolourpaint/tools/kptoolrectselection.cpp b/kolourpaint/tools/kptoolrectselection.cpp
new file mode 100644
index 00000000..3726cbfe
--- /dev/null
+++ b/kolourpaint/tools/kptoolrectselection.cpp
@@ -0,0 +1,46 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <kptoolrectselection.h>
+
+#include <klocale.h>
+
+
+kpToolRectSelection::kpToolRectSelection (kpMainWindow *mainWindow)
+ : kpToolSelection (Rectangle,
+ i18n ("Selection (Rectangular)"),
+ i18n ("Makes a rectangular selection"),
+ Qt::Key_S,
+ mainWindow, "tool_rect_selection")
+{
+}
+
+kpToolRectSelection::~kpToolRectSelection ()
+{
+}
+
diff --git a/kolourpaint/tools/kptoolrectselection.h b/kolourpaint/tools/kptoolrectselection.h
new file mode 100644
index 00000000..0d66b7a5
--- /dev/null
+++ b/kolourpaint/tools/kptoolrectselection.h
@@ -0,0 +1,43 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kptoolrectselection_h__
+#define __kptoolrectselection_h__
+
+#include <kptoolselection.h>
+
+class kpMainWindow;
+
+class kpToolRectSelection : public kpToolSelection
+{
+public:
+ kpToolRectSelection (kpMainWindow *);
+ virtual ~kpToolRectSelection ();
+};
+
+#endif // __kptoolrectselection_h__
diff --git a/kolourpaint/tools/kptoolresizescale.cpp b/kolourpaint/tools/kptoolresizescale.cpp
new file mode 100644
index 00000000..ce9c9059
--- /dev/null
+++ b/kolourpaint/tools/kptoolresizescale.cpp
@@ -0,0 +1,1222 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG_KP_TOOL_RESIZE_SCALE_COMMAND 0
+#define DEBUG_KP_TOOL_RESIZE_SCALE_DIALOG 0
+
+
+#include <kptoolresizescale.h>
+
+#include <math.h>
+
+#include <qaccel.h>
+#include <qapplication.h>
+#include <qbuttongroup.h>
+#include <qcheckbox.h>
+#include <qgroupbox.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpoint.h>
+#include <qpointarray.h>
+#include <qpushbutton.h>
+#include <qrect.h>
+#include <qsize.h>
+#include <qtoolbutton.h>
+#include <qwhatsthis.h>
+#include <qwmatrix.h>
+
+#include <kapplication.h>
+#include <kcombobox.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kiconeffect.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <knuminput.h>
+
+#include <kpdefs.h>
+#include <kpdocument.h>
+#include <kpmainwindow.h>
+#include <kppixmapfx.h>
+#include <kpselection.h>
+#include <kptool.h>
+
+
+/*
+ * kpToolResizeScaleCommand
+ */
+
+kpToolResizeScaleCommand::kpToolResizeScaleCommand (bool actOnSelection,
+ int newWidth, int newHeight,
+ Type type,
+ kpMainWindow *mainWindow)
+ : kpCommand (mainWindow),
+ m_actOnSelection (actOnSelection),
+ m_type (type),
+ m_backgroundColor (mainWindow ? mainWindow->backgroundColor () : kpColor::invalid),
+ m_oldSelection (0)
+{
+ kpDocument *doc = document ();
+
+ m_oldWidth = doc->width (m_actOnSelection);
+ m_oldHeight = doc->height (m_actOnSelection);
+
+ m_actOnTextSelection = (m_actOnSelection &&
+ doc && doc->selection () &&
+ doc->selection ()->isText ());
+
+ resize (newWidth, newHeight);
+
+ // If we have a selection _border_ (but not a floating selection),
+ // then scale the selection with the document
+ m_scaleSelectionWithImage = (!m_actOnSelection &&
+ (m_type == Scale || m_type == SmoothScale) &&
+ document ()->selection () &&
+ !document ()->selection ()->pixmap ());
+}
+
+kpToolResizeScaleCommand::~kpToolResizeScaleCommand ()
+{
+ delete m_oldSelection;
+}
+
+
+// public virtual [base kpCommand]
+QString kpToolResizeScaleCommand::name () const
+{
+ if (m_actOnSelection)
+ {
+ if (m_actOnTextSelection)
+ {
+ if (m_type == Resize)
+ return i18n ("Text: Resize Box");
+ }
+ else
+ {
+ if (m_type == Scale)
+ return i18n ("Selection: Scale");
+ else if (m_type == SmoothScale)
+ return i18n ("Selection: Smooth Scale");
+ }
+ }
+ else
+ {
+ switch (m_type)
+ {
+ case Resize:
+ return i18n ("Resize");
+ case Scale:
+ return i18n ("Scale");
+ case SmoothScale:
+ return i18n ("Smooth Scale");
+ }
+ }
+
+ return QString::null;
+}
+
+// public virtual [base kpCommand]
+int kpToolResizeScaleCommand::size () const
+{
+ return kpPixmapFX::pixmapSize (m_oldPixmap) +
+ kpPixmapFX::pixmapSize (m_oldRightPixmap) +
+ kpPixmapFX::pixmapSize (m_oldBottomPixmap) +
+ (m_oldSelection ? m_oldSelection->size () : 0);
+}
+
+
+// public
+int kpToolResizeScaleCommand::newWidth () const
+{
+ return m_newWidth;
+}
+
+// public
+void kpToolResizeScaleCommand::setNewWidth (int width)
+{
+ resize (width, newHeight ());
+}
+
+
+// public
+int kpToolResizeScaleCommand::newHeight () const
+{
+ return m_newHeight;
+}
+
+// public
+void kpToolResizeScaleCommand::setNewHeight (int height)
+{
+ resize (newWidth (), height);
+}
+
+
+// public
+QSize kpToolResizeScaleCommand::newSize () const
+{
+ return QSize (newWidth (), newHeight ());
+}
+
+// public virtual
+void kpToolResizeScaleCommand::resize (int width, int height)
+{
+ m_newWidth = width;
+ m_newHeight = height;
+
+ m_isLosslessScale = ((m_type == Scale) &&
+ (m_newWidth / m_oldWidth * m_oldWidth == m_newWidth) &&
+ (m_newHeight / m_oldHeight * m_oldHeight == m_newHeight));
+}
+
+
+// public
+bool kpToolResizeScaleCommand::scaleSelectionWithImage () const
+{
+ return m_scaleSelectionWithImage;
+}
+
+
+// private
+void kpToolResizeScaleCommand::scaleSelectionRegionWithDocument ()
+{
+#if DEBUG_KP_TOOL_RESIZE_SCALE_COMMAND
+ kdDebug () << "kpToolResizeScaleCommand::scaleSelectionRegionWithDocument"
+ << endl;
+#endif
+
+ if (!m_oldSelection)
+ {
+ kdError () << "kpToolResizeScaleCommand::scaleSelectionRegionWithDocument()"
+ << " without old sel" << endl;
+ return;
+ }
+
+ if (m_oldSelection->pixmap ())
+ {
+ kdError () << "kpToolResizeScaleCommand::scaleSelectionRegionWithDocument()"
+ << " old sel has pixmap" << endl;
+ return;
+ }
+
+
+ const double horizScale = double (m_newWidth) / double (m_oldWidth);
+ const double vertScale = double (m_newHeight) / double (m_oldHeight);
+
+ const int newX = (int) (m_oldSelection->x () * horizScale);
+ const int newY = (int) (m_oldSelection->y () * vertScale);
+
+
+ QPointArray currentPoints = m_oldSelection->points ();
+ currentPoints.detach ();
+
+ currentPoints.translate (-currentPoints.boundingRect ().x (),
+ -currentPoints.boundingRect ().y ());
+
+ // TODO: refactor into kpPixmapFX
+ QWMatrix scaleMatrix;
+ scaleMatrix.scale (horizScale, vertScale);
+ currentPoints = scaleMatrix.map (currentPoints);
+
+ currentPoints.translate (
+ -currentPoints.boundingRect ().x () + newX,
+ -currentPoints.boundingRect ().y () + newY);
+
+ document ()->setSelection (kpSelection (currentPoints, QPixmap (),
+ m_oldSelection->transparency ()));
+
+
+ if (m_mainWindow->tool ())
+ m_mainWindow->tool ()->somethingBelowTheCursorChanged ();
+}
+
+
+// public virtual [base kpCommand]
+void kpToolResizeScaleCommand::execute ()
+{
+#if DEBUG_KP_TOOL_RESIZE_SCALE_COMMAND
+ kdDebug () << "kpToolResizeScaleCommand::execute() type="
+ << (int) m_type
+ << " oldWidth=" << m_oldWidth
+ << " oldHeight=" << m_oldHeight
+ << " newWidth=" << m_newWidth
+ << " newHeight=" << m_newHeight
+ << endl;
+#endif
+
+ if (m_oldWidth == m_newWidth && m_oldHeight == m_newHeight)
+ return;
+
+ if (m_type == Resize)
+ {
+ if (m_actOnSelection)
+ {
+ if (!m_actOnTextSelection)
+ {
+ kdError () << "kpToolResizeScaleCommand::execute() resizing sel doesn't make sense" << endl;
+ return;
+ }
+ else
+ {
+ QApplication::setOverrideCursor (Qt::waitCursor);
+ document ()->selection ()->textResize (m_newWidth, m_newHeight);
+
+ if (m_mainWindow->tool ())
+ m_mainWindow->tool ()->somethingBelowTheCursorChanged ();
+
+ QApplication::restoreOverrideCursor ();
+ }
+ }
+ else
+ {
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+
+ if (m_newWidth < m_oldWidth)
+ {
+ m_oldRightPixmap = kpPixmapFX::getPixmapAt (
+ *document ()->pixmap (),
+ QRect (m_newWidth, 0,
+ m_oldWidth - m_newWidth, m_oldHeight));
+ }
+
+ if (m_newHeight < m_oldHeight)
+ {
+ m_oldBottomPixmap = kpPixmapFX::getPixmapAt (
+ *document ()->pixmap (),
+ QRect (0, m_newHeight,
+ m_newWidth, m_oldHeight - m_newHeight));
+ }
+
+ document ()->resize (m_newWidth, m_newHeight, m_backgroundColor);
+
+
+ QApplication::restoreOverrideCursor ();
+ }
+ }
+ else
+ {
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+
+ QPixmap oldPixmap = *document ()->pixmap (m_actOnSelection);
+
+ if (!m_isLosslessScale)
+ m_oldPixmap = oldPixmap;
+
+ QPixmap newPixmap = kpPixmapFX::scale (oldPixmap, m_newWidth, m_newHeight,
+ m_type == SmoothScale);
+
+
+ if (!m_oldSelection && document ()->selection ())
+ {
+ // Save sel border
+ m_oldSelection = new kpSelection (*document ()->selection ());
+ m_oldSelection->setPixmap (QPixmap ());
+ }
+
+ if (m_actOnSelection)
+ {
+ QRect newRect = QRect (m_oldSelection->x (), m_oldSelection->y (),
+ newPixmap.width (), newPixmap.height ());
+
+ // Not possible to retain non-rectangular selection borders on scale
+ // (think about e.g. a 45 deg line as part of the border & 2x scale)
+ document ()->setSelection (
+ kpSelection (kpSelection::Rectangle, newRect, newPixmap,
+ m_oldSelection->transparency ()));
+
+ if (m_mainWindow->tool ())
+ m_mainWindow->tool ()->somethingBelowTheCursorChanged ();
+ }
+ else
+ {
+ document ()->setPixmap (newPixmap);
+
+ if (m_scaleSelectionWithImage)
+ {
+ scaleSelectionRegionWithDocument ();
+ }
+ }
+
+
+ QApplication::restoreOverrideCursor ();
+ }
+}
+
+// public virtual [base kpCommand]
+void kpToolResizeScaleCommand::unexecute ()
+{
+#if DEBUG_KP_TOOL_RESIZE_SCALE_COMMAND
+ kdDebug () << "kpToolResizeScaleCommand::unexecute() type="
+ << m_type << endl;
+#endif
+
+ if (m_oldWidth == m_newWidth && m_oldHeight == m_newHeight)
+ return;
+
+ kpDocument *doc = document ();
+ if (!doc)
+ return;
+
+ if (m_type == Resize)
+ {
+ if (m_actOnSelection)
+ {
+ if (!m_actOnTextSelection)
+ {
+ kdError () << "kpToolResizeScaleCommand::unexecute() resizing sel doesn't make sense" << endl;
+ return;
+ }
+ else
+ {
+ QApplication::setOverrideCursor (Qt::waitCursor);
+ document ()->selection ()->textResize (m_oldWidth, m_oldHeight);
+
+ if (m_mainWindow->tool ())
+ m_mainWindow->tool ()->somethingBelowTheCursorChanged ();
+
+ QApplication::restoreOverrideCursor ();
+ }
+ }
+ else
+ {
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+
+ QPixmap newPixmap (m_oldWidth, m_oldHeight);
+
+ kpPixmapFX::setPixmapAt (&newPixmap, QPoint (0, 0),
+ *doc->pixmap ());
+
+ if (m_newWidth < m_oldWidth)
+ {
+ kpPixmapFX::setPixmapAt (&newPixmap,
+ QPoint (m_newWidth, 0),
+ m_oldRightPixmap);
+ }
+
+ if (m_newHeight < m_oldHeight)
+ {
+ kpPixmapFX::setPixmapAt (&newPixmap,
+ QPoint (0, m_newHeight),
+ m_oldBottomPixmap);
+ }
+
+ doc->setPixmap (newPixmap);
+
+
+ QApplication::restoreOverrideCursor ();
+ }
+ }
+ else
+ {
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+
+ QPixmap oldPixmap;
+
+ if (!m_isLosslessScale)
+ oldPixmap = m_oldPixmap;
+ else
+ oldPixmap = kpPixmapFX::scale (*doc->pixmap (m_actOnSelection),
+ m_oldWidth, m_oldHeight);
+
+
+ if (m_actOnSelection)
+ {
+ kpSelection oldSelection = *m_oldSelection;
+ oldSelection.setPixmap (oldPixmap);
+ doc->setSelection (oldSelection);
+
+ if (m_mainWindow->tool ())
+ m_mainWindow->tool ()->somethingBelowTheCursorChanged ();
+ }
+ else
+ {
+ doc->setPixmap (oldPixmap);
+
+ if (m_scaleSelectionWithImage)
+ {
+ doc->setSelection (*m_oldSelection);
+
+ if (m_mainWindow->tool ())
+ m_mainWindow->tool ()->somethingBelowTheCursorChanged ();
+ }
+ }
+
+
+ QApplication::restoreOverrideCursor ();
+ }
+}
+
+
+/*
+ * kpToolResizeScaleDialog
+ */
+
+#define SET_VALUE_WITHOUT_SIGNAL_EMISSION(knuminput_instance,value) \
+{ \
+ knuminput_instance->blockSignals (true); \
+ knuminput_instance->setValue (value); \
+ knuminput_instance->blockSignals (false); \
+}
+
+#define IGNORE_KEEP_ASPECT_RATIO(cmd) \
+{ \
+ m_ignoreKeepAspectRatio++; \
+ cmd; \
+ m_ignoreKeepAspectRatio--; \
+}
+
+
+// private static
+kpToolResizeScaleCommand::Type kpToolResizeScaleDialog::s_lastType =
+ kpToolResizeScaleCommand::Resize;
+
+// private static
+double kpToolResizeScaleDialog::s_lastPercentWidth = 100,
+ kpToolResizeScaleDialog::s_lastPercentHeight = 100;
+
+
+kpToolResizeScaleDialog::kpToolResizeScaleDialog (kpMainWindow *mainWindow)
+ : KDialogBase ((QWidget *) mainWindow,
+ 0/*name*/,
+ true/*modal*/,
+ i18n ("Resize / Scale")/*caption*/,
+ KDialogBase::Ok | KDialogBase::Cancel),
+ m_mainWindow (mainWindow),
+ m_ignoreKeepAspectRatio (0)
+{
+ // Using the percentage from last time become too confusing so disable for now
+ s_lastPercentWidth = 100, s_lastPercentHeight = 100;
+
+
+ QWidget *baseWidget = new QWidget (this);
+ setMainWidget (baseWidget);
+
+
+ createActOnBox (baseWidget);
+ createOperationGroupBox (baseWidget);
+ createDimensionsGroupBox (baseWidget);
+
+
+ QVBoxLayout *baseLayout = new QVBoxLayout (baseWidget, 0/*margin*/, spacingHint ());
+ baseLayout->addWidget (m_actOnBox);
+ baseLayout->addWidget (m_operationGroupBox);
+ baseLayout->addWidget (m_dimensionsGroupBox);
+
+
+ slotActOnChanged ();
+
+ m_newWidthInput->setEditFocus ();
+
+ //enableButtonOK (!isNoOp ());
+}
+
+kpToolResizeScaleDialog::~kpToolResizeScaleDialog ()
+{
+}
+
+
+// private
+kpDocument *kpToolResizeScaleDialog::document () const
+{
+ return m_mainWindow ? m_mainWindow->document () : 0;
+}
+
+// private
+kpSelection *kpToolResizeScaleDialog::selection () const
+{
+ return document () ? document ()->selection () : 0;
+}
+
+
+// private
+void kpToolResizeScaleDialog::createActOnBox (QWidget *baseWidget)
+{
+ m_actOnBox = new QHBox (baseWidget);
+ m_actOnBox->setSpacing (spacingHint () * 2);
+
+
+ m_actOnLabel = new QLabel (i18n ("Ac&t on:"), m_actOnBox);
+ m_actOnCombo = new KComboBox (m_actOnBox);
+
+
+ m_actOnLabel->setBuddy (m_actOnCombo);
+
+ m_actOnCombo->insertItem (i18n ("Entire Image"), Image);
+ if (selection ())
+ {
+ QString selName = i18n ("Selection");
+
+ if (selection ()->isText ())
+ selName = i18n ("Text Box");
+
+ m_actOnCombo->insertItem (selName, Selection);
+ m_actOnCombo->setCurrentItem (Selection);
+ }
+ else
+ {
+ m_actOnLabel->setEnabled (false);
+ m_actOnCombo->setEnabled (false);
+ }
+
+
+ m_actOnBox->setStretchFactor (m_actOnCombo, 1);
+
+
+ connect (m_actOnCombo, SIGNAL (activated (int)),
+ this, SLOT (slotActOnChanged ()));
+}
+
+
+static QIconSet toolButtonIconSet (const QString &iconName)
+{
+ QIconSet iconSet = UserIconSet (iconName);
+
+
+ // No "disabled" pixmap is generated by UserIconSet() so generate it
+ // ourselves:
+
+ QPixmap disabledIcon = KGlobal::iconLoader ()->iconEffect ()->apply (
+ UserIcon (iconName),
+ KIcon::Toolbar, KIcon::DisabledState);
+
+ const QPixmap iconSetNormalIcon = iconSet.pixmap (QIconSet::Small,
+ QIconSet::Normal);
+
+ // I bet past or future versions of KIconEffect::apply() resize the
+ // disabled icon if we claim it's in group KIcon::Toolbar. So resize
+ // it to match the QIconSet::Normal icon, just in case.
+ disabledIcon = kpPixmapFX::scale (disabledIcon,
+ iconSetNormalIcon.width (),
+ iconSetNormalIcon.height (),
+ true/*smooth scale*/);
+
+
+ iconSet.setPixmap (disabledIcon,
+ QIconSet::Small, QIconSet::Disabled);
+
+ return iconSet;
+}
+
+static void toolButtonSetLook (QToolButton *button,
+ const QString &iconName,
+ const QString &name)
+{
+ button->setIconSet (toolButtonIconSet (iconName));
+ button->setUsesTextLabel (true);
+ button->setTextLabel (name, false/*no tooltip*/);
+ button->setAccel (QAccel::shortcutKey (name));
+ button->setFocusPolicy (QWidget::StrongFocus);
+ button->setToggleButton (true);
+}
+
+
+// private
+void kpToolResizeScaleDialog::createOperationGroupBox (QWidget *baseWidget)
+{
+ m_operationGroupBox = new QGroupBox (i18n ("Operation"), baseWidget);
+ QWhatsThis::add (m_operationGroupBox,
+ i18n ("<qt>"
+ "<ul>"
+ "<li><b>Resize</b>: The size of the picture will be"
+ " increased"
+ " by creating new areas to the right and/or bottom"
+ " (filled in with the background color) or"
+ " decreased by cutting"
+ " it at the right and/or bottom.</li>"
+
+ "<li><b>Scale</b>: The picture will be expanded"
+ " by duplicating pixels or squashed by dropping pixels.</li>"
+
+ "<li><b>Smooth Scale</b>: This is the same as"
+ " <i>Scale</i> except that it blends neighboring"
+ " pixels to produce a smoother looking picture.</li>"
+ "</ul>"
+ "</qt>"));
+
+ // TODO: ALT+R doesn't select the button.
+ m_resizeButton = new QToolButton (m_operationGroupBox);
+ toolButtonSetLook (m_resizeButton,
+ QString::fromLatin1 ("resize"),
+ i18n ("&Resize"));
+
+ m_scaleButton = new QToolButton (m_operationGroupBox);
+ toolButtonSetLook (m_scaleButton,
+ QString::fromLatin1 ("scale"),
+ i18n ("&Scale"));
+
+ m_smoothScaleButton = new QToolButton (m_operationGroupBox);
+ toolButtonSetLook (m_smoothScaleButton,
+ QString::fromLatin1 ("smooth_scale"),
+ i18n ("S&mooth Scale"));
+
+
+ //m_resizeLabel = new QLabel (i18n ("&Resize"), m_operationGroupBox);
+ //m_scaleLabel = new QLabel (i18n ("&Scale"), m_operationGroupBox);
+ //m_smoothScaleLabel = new QLabel (i18n ("S&mooth scale"), m_operationGroupBox);
+
+
+ //m_resizeLabel->setAlignment (m_resizeLabel->alignment () | Qt::ShowPrefix);
+ //m_scaleLabel->setAlignment (m_scaleLabel->alignment () | Qt::ShowPrefix);
+ //m_smoothScaleLabel->setAlignment (m_smoothScaleLabel->alignment () | Qt::ShowPrefix);
+
+
+ QButtonGroup *resizeScaleButtonGroup = new QButtonGroup (baseWidget);
+ resizeScaleButtonGroup->setExclusive (true);
+ resizeScaleButtonGroup->hide ();
+
+ resizeScaleButtonGroup->insert (m_resizeButton);
+ resizeScaleButtonGroup->insert (m_scaleButton);
+ resizeScaleButtonGroup->insert (m_smoothScaleButton);
+
+
+ QGridLayout *operationLayout = new QGridLayout (m_operationGroupBox,
+ 1, 2,
+ marginHint () * 2/*don't overlap groupbox title*/,
+ spacingHint ());
+
+ operationLayout->addWidget (m_resizeButton, 0, 0, Qt::AlignCenter);
+ //operationLayout->addWidget (m_resizeLabel, 1, 0, Qt::AlignCenter);
+
+ operationLayout->addWidget (m_scaleButton, 0, 1, Qt::AlignCenter);
+ //operationLayout->addWidget (m_scaleLabel, 1, 1, Qt::AlignCenter);
+
+ operationLayout->addWidget (m_smoothScaleButton, 0, 2, Qt::AlignCenter);
+ //operationLayout->addWidget (m_smoothScaleLabel, 1, 2, Qt::AlignCenter);
+
+
+ connect (m_resizeButton, SIGNAL (toggled (bool)),
+ this, SLOT (slotTypeChanged ()));
+ connect (m_scaleButton, SIGNAL (toggled (bool)),
+ this, SLOT (slotTypeChanged ()));
+ connect (m_smoothScaleButton, SIGNAL (toggled (bool)),
+ this, SLOT (slotTypeChanged ()));
+}
+
+// private
+void kpToolResizeScaleDialog::createDimensionsGroupBox (QWidget *baseWidget)
+{
+ m_dimensionsGroupBox = new QGroupBox (i18n ("Dimensions"), baseWidget);
+
+ QLabel *widthLabel = new QLabel (i18n ("Width:"), m_dimensionsGroupBox);
+ widthLabel->setAlignment (widthLabel->alignment () | Qt::AlignHCenter);
+ QLabel *heightLabel = new QLabel (i18n ("Height:"), m_dimensionsGroupBox);
+ heightLabel->setAlignment (heightLabel->alignment () | Qt::AlignHCenter);
+
+ QLabel *originalLabel = new QLabel (i18n ("Original:"), m_dimensionsGroupBox);
+ m_originalWidthInput = new KIntNumInput (
+ document ()->width ((bool) selection ()),
+ m_dimensionsGroupBox);
+ QLabel *xLabel0 = new QLabel (i18n ("x"), m_dimensionsGroupBox);
+ m_originalHeightInput = new KIntNumInput (
+ document ()->height ((bool) selection ()),
+ m_dimensionsGroupBox);
+
+ QLabel *newLabel = new QLabel (i18n ("&New:"), m_dimensionsGroupBox);
+ m_newWidthInput = new KIntNumInput (m_dimensionsGroupBox);
+ QLabel *xLabel1 = new QLabel (i18n ("x"), m_dimensionsGroupBox);
+ m_newHeightInput = new KIntNumInput (m_dimensionsGroupBox);
+
+ QLabel *percentLabel = new QLabel (i18n ("&Percent:"), m_dimensionsGroupBox);
+ m_percentWidthInput = new KDoubleNumInput (0.01/*lower*/, 1000000/*upper*/,
+ 100/*value*/, 1/*step*/,
+ 2/*precision*/,
+ m_dimensionsGroupBox);
+ m_percentWidthInput->setSuffix (i18n ("%"));
+ QLabel *xLabel2 = new QLabel (i18n ("x"), m_dimensionsGroupBox);
+ m_percentHeightInput = new KDoubleNumInput (0.01/*lower*/, 1000000/*upper*/,
+ 100/*value*/, 1/*step*/,
+ 2/*precision*/,
+ m_dimensionsGroupBox);
+ m_percentHeightInput->setSuffix (i18n ("%"));
+
+ m_keepAspectRatioCheckBox = new QCheckBox (i18n ("Keep &aspect ratio"),
+ m_dimensionsGroupBox);
+
+
+ m_originalWidthInput->setEnabled (false);
+ m_originalHeightInput->setEnabled (false);
+ originalLabel->setBuddy (m_originalWidthInput);
+ newLabel->setBuddy (m_newWidthInput);
+ m_percentWidthInput->setValue (s_lastPercentWidth);
+ m_percentHeightInput->setValue (s_lastPercentHeight);
+ percentLabel->setBuddy (m_percentWidthInput);
+
+
+ QGridLayout *dimensionsLayout = new QGridLayout (m_dimensionsGroupBox,
+ 5, 4, marginHint () * 2, spacingHint ());
+ dimensionsLayout->setColStretch (1/*column*/, 1);
+ dimensionsLayout->setColStretch (3/*column*/, 1);
+
+
+ dimensionsLayout->addWidget (widthLabel, 0, 1);
+ dimensionsLayout->addWidget (heightLabel, 0, 3);
+
+ dimensionsLayout->addWidget (originalLabel, 1, 0);
+ dimensionsLayout->addWidget (m_originalWidthInput, 1, 1);
+ dimensionsLayout->addWidget (xLabel0, 1, 2);
+ dimensionsLayout->addWidget (m_originalHeightInput, 1, 3);
+
+ dimensionsLayout->addWidget (newLabel, 2, 0);
+ dimensionsLayout->addWidget (m_newWidthInput, 2, 1);
+ dimensionsLayout->addWidget (xLabel1, 2, 2);
+ dimensionsLayout->addWidget (m_newHeightInput, 2, 3);
+
+ dimensionsLayout->addWidget (percentLabel, 3, 0);
+ dimensionsLayout->addWidget (m_percentWidthInput, 3, 1);
+ dimensionsLayout->addWidget (xLabel2, 3, 2);
+ dimensionsLayout->addWidget (m_percentHeightInput, 3, 3);
+
+ dimensionsLayout->addMultiCellWidget (m_keepAspectRatioCheckBox, 4, 4, 0, 3);
+ dimensionsLayout->setRowStretch (4/*row*/, 1);
+ dimensionsLayout->setRowSpacing (4/*row*/, dimensionsLayout->rowSpacing (4) * 2);
+
+
+ connect (m_newWidthInput, SIGNAL (valueChanged (int)),
+ this, SLOT (slotWidthChanged (int)));
+ connect (m_newHeightInput, SIGNAL (valueChanged (int)),
+ this, SLOT (slotHeightChanged (int)));
+
+ connect (m_percentWidthInput, SIGNAL (valueChanged (double)),
+ this, SLOT (slotPercentWidthChanged (double)));
+ connect (m_percentHeightInput, SIGNAL (valueChanged (double)),
+ this, SLOT (slotPercentHeightChanged (double)));
+
+ connect (m_keepAspectRatioCheckBox, SIGNAL (toggled (bool)),
+ this, SLOT (setKeepAspectRatio (bool)));
+}
+
+
+// private
+void kpToolResizeScaleDialog::widthFitHeightToAspectRatio ()
+{
+ if (m_keepAspectRatioCheckBox->isChecked () && !m_ignoreKeepAspectRatio)
+ {
+ // width / height = oldWidth / oldHeight
+ // height = width * oldHeight / oldWidth
+ const int newHeight = qRound (double (imageWidth ()) * double (originalHeight ())
+ / double (originalWidth ()));
+ IGNORE_KEEP_ASPECT_RATIO (m_newHeightInput->setValue (newHeight));
+ }
+}
+
+// private
+void kpToolResizeScaleDialog::heightFitWidthToAspectRatio ()
+{
+ if (m_keepAspectRatioCheckBox->isChecked () && !m_ignoreKeepAspectRatio)
+ {
+ // width / height = oldWidth / oldHeight
+ // width = height * oldWidth / oldHeight
+ const int newWidth = qRound (double (imageHeight ()) * double (originalWidth ())
+ / double (originalHeight ()));
+ IGNORE_KEEP_ASPECT_RATIO (m_newWidthInput->setValue (newWidth));
+ }
+}
+
+
+// private
+bool kpToolResizeScaleDialog::resizeEnabled () const
+{
+ return (!actOnSelection () ||
+ (actOnSelection () && selection ()->isText ()));
+}
+
+// private
+bool kpToolResizeScaleDialog::scaleEnabled () const
+{
+ return (!(actOnSelection () && selection ()->isText ()));
+}
+
+// private
+bool kpToolResizeScaleDialog::smoothScaleEnabled () const
+{
+ return scaleEnabled ();
+}
+
+
+// public slot
+void kpToolResizeScaleDialog::slotActOnChanged ()
+{
+#if DEBUG_KP_TOOL_RESIZE_SCALE_DIALOG && 1
+ kdDebug () << "kpToolResizeScaleDialog::slotActOnChanged()" << endl;
+#endif
+
+ m_resizeButton->setEnabled (resizeEnabled ());
+ //m_resizeLabel->setEnabled (resizeEnabled ());
+
+ m_scaleButton->setEnabled (scaleEnabled ());
+ //m_scaleLabel->setEnabled (scaleEnabled ());
+
+ m_smoothScaleButton->setEnabled (smoothScaleEnabled ());
+ //m_smoothScaleLabel->setEnabled (smoothScaleEnabled ());
+
+
+ // TODO: somehow share logic with (resize|*scale)Enabled()
+ if (actOnSelection ())
+ {
+ if (selection ()->isText ())
+ {
+ m_resizeButton->setOn (true);
+ }
+ else
+ {
+ if (s_lastType == kpToolResizeScaleCommand::Scale)
+ m_scaleButton->setOn (true);
+ else
+ m_smoothScaleButton->setOn (true);
+ }
+ }
+ else
+ {
+ if (s_lastType == kpToolResizeScaleCommand::Resize)
+ m_resizeButton->setOn (true);
+ else if (s_lastType == kpToolResizeScaleCommand::Scale)
+ m_scaleButton->setOn (true);
+ else
+ m_smoothScaleButton->setOn (true);
+ }
+
+
+ m_originalWidthInput->setValue (originalWidth ());
+ m_originalHeightInput->setValue (originalHeight ());
+
+
+ m_newWidthInput->blockSignals (true);
+ m_newHeightInput->blockSignals (true);
+
+ m_newWidthInput->setMinValue (actOnSelection () ?
+ selection ()->minimumWidth () :
+ 1);
+ m_newHeightInput->setMinValue (actOnSelection () ?
+ selection ()->minimumHeight () :
+ 1);
+
+ m_newWidthInput->blockSignals (false);
+ m_newHeightInput->blockSignals (false);
+
+
+ IGNORE_KEEP_ASPECT_RATIO (slotPercentWidthChanged (m_percentWidthInput->value ()));
+ IGNORE_KEEP_ASPECT_RATIO (slotPercentHeightChanged (m_percentHeightInput->value ()));
+
+ setKeepAspectRatio (m_keepAspectRatioCheckBox->isChecked ());
+}
+
+
+// public slot
+void kpToolResizeScaleDialog::slotTypeChanged ()
+{
+ s_lastType = type ();
+}
+
+// public slot
+void kpToolResizeScaleDialog::slotWidthChanged (int width)
+{
+#if DEBUG_KP_TOOL_RESIZE_SCALE_DIALOG && 1
+ kdDebug () << "kpToolResizeScaleDialog::slotWidthChanged("
+ << width << ")" << endl;
+#endif
+ const double newPercentWidth = double (width) * 100 / double (originalWidth ());
+
+ SET_VALUE_WITHOUT_SIGNAL_EMISSION (m_percentWidthInput, newPercentWidth);
+
+ widthFitHeightToAspectRatio ();
+
+ //enableButtonOK (!isNoOp ());
+ s_lastPercentWidth = newPercentWidth;
+}
+
+// public slot
+void kpToolResizeScaleDialog::slotHeightChanged (int height)
+{
+#if DEBUG_KP_TOOL_RESIZE_SCALE_DIALOG && 1
+ kdDebug () << "kpToolResizeScaleDialog::slotHeightChanged("
+ << height << ")" << endl;
+#endif
+ const double newPercentHeight = double (height) * 100 / double (originalHeight ());
+
+ SET_VALUE_WITHOUT_SIGNAL_EMISSION (m_percentHeightInput, newPercentHeight);
+
+ heightFitWidthToAspectRatio ();
+
+ //enableButtonOK (!isNoOp ());
+ s_lastPercentHeight = newPercentHeight;
+}
+
+// public slot
+void kpToolResizeScaleDialog::slotPercentWidthChanged (double percentWidth)
+{
+#if DEBUG_KP_TOOL_RESIZE_SCALE_DIALOG && 1
+ kdDebug () << "kpToolResizeScaleDialog::slotPercentWidthChanged("
+ << percentWidth << ")" << endl;
+#endif
+
+ SET_VALUE_WITHOUT_SIGNAL_EMISSION (m_newWidthInput,
+ qRound (percentWidth * originalWidth () / 100.0));
+
+ widthFitHeightToAspectRatio ();
+
+ //enableButtonOK (!isNoOp ());
+ s_lastPercentWidth = percentWidth;
+}
+
+// public slot
+void kpToolResizeScaleDialog::slotPercentHeightChanged (double percentHeight)
+{
+#if DEBUG_KP_TOOL_RESIZE_SCALE_DIALOG && 1
+ kdDebug () << "kpToolResizeScaleDialog::slotPercentHeightChanged("
+ << percentHeight << ")" << endl;
+#endif
+
+ SET_VALUE_WITHOUT_SIGNAL_EMISSION (m_newHeightInput,
+ qRound (percentHeight * originalHeight () / 100.0));
+
+ heightFitWidthToAspectRatio ();
+
+ //enableButtonOK (!isNoOp ());
+ s_lastPercentHeight = percentHeight;
+}
+
+// public
+bool kpToolResizeScaleDialog::keepAspectRatio () const
+{
+ return m_keepAspectRatioCheckBox->isChecked ();
+}
+
+// public slot
+void kpToolResizeScaleDialog::setKeepAspectRatio (bool on)
+{
+#if DEBUG_KP_TOOL_RESIZE_SCALE_DIALOG && 1
+ kdDebug () << "kpToolResizeScaleDialog::setKeepAspectRatio("
+ << on << ")" << endl;
+#endif
+ if (on != m_keepAspectRatioCheckBox->isChecked ())
+ m_keepAspectRatioCheckBox->setChecked (on);
+
+ if (on)
+ widthFitHeightToAspectRatio ();
+}
+
+#undef IGNORE_KEEP_ASPECT_RATIO
+#undef SET_VALUE_WITHOUT_SIGNAL_EMISSION
+
+
+// private
+int kpToolResizeScaleDialog::originalWidth () const
+{
+ return document ()->width (actOnSelection ());
+}
+
+// private
+int kpToolResizeScaleDialog::originalHeight () const
+{
+ return document ()->height (actOnSelection ());
+}
+
+
+// public
+int kpToolResizeScaleDialog::imageWidth () const
+{
+ return m_newWidthInput->value ();
+}
+
+// public
+int kpToolResizeScaleDialog::imageHeight () const
+{
+ return m_newHeightInput->value ();
+}
+
+// public
+bool kpToolResizeScaleDialog::actOnSelection () const
+{
+ return (m_actOnCombo->currentItem () == Selection);
+}
+
+// public
+kpToolResizeScaleCommand::Type kpToolResizeScaleDialog::type () const
+{
+ if (m_resizeButton->isOn ())
+ return kpToolResizeScaleCommand::Resize;
+ else if (m_scaleButton->isOn ())
+ return kpToolResizeScaleCommand::Scale;
+ else
+ return kpToolResizeScaleCommand::SmoothScale;
+}
+
+// public
+bool kpToolResizeScaleDialog::isNoOp () const
+{
+ return (imageWidth () == originalWidth () &&
+ imageHeight () == originalHeight ());
+}
+
+
+// private slot virtual [base KDialogBase]
+void kpToolResizeScaleDialog::slotOk ()
+{
+ enum { eText, eSelection, eImage } actionTarget = eText;
+
+ if (actOnSelection ())
+ {
+ if (selection ()->isText ())
+ {
+ actionTarget = eText;
+ }
+ else
+ {
+ actionTarget = eSelection;
+ }
+ }
+ else
+ {
+ actionTarget = eImage;
+ }
+
+
+ QString message, caption, continueButtonText;
+
+ // Note: If eText, can't Scale nor SmoothScale.
+ // If eSelection, can't Resize.
+
+ switch (type ())
+ {
+ default:
+ case kpToolResizeScaleCommand::Resize:
+ if (actionTarget == eText)
+ {
+ message =
+ i18n ("<qt><p>Resizing the text box to %1x%2"
+ " may take a substantial amount of memory."
+ " This can reduce system"
+ " responsiveness and cause other application resource"
+ " problems.</p>"
+
+ "<p>Are you sure you want to resize the text box?</p></qt>");
+
+ caption = i18n ("Resize Text Box?");
+ continueButtonText = i18n ("R&esize Text Box");
+ }
+ else if (actionTarget == eImage)
+ {
+ message =
+ i18n ("<qt><p>Resizing the image to %1x%2"
+ " may take a substantial amount of memory."
+ " This can reduce system"
+ " responsiveness and cause other application resource"
+ " problems.</p>"
+
+ "<p>Are you sure you want to resize the image?</p></qt>");
+
+ caption = i18n ("Resize Image?");
+ continueButtonText = i18n ("R&esize Image");
+ }
+
+ break;
+
+ case kpToolResizeScaleCommand::Scale:
+ if (actionTarget == eImage)
+ {
+ message =
+ i18n ("<qt><p>Scaling the image to %1x%2"
+ " may take a substantial amount of memory."
+ " This can reduce system"
+ " responsiveness and cause other application resource"
+ " problems.</p>"
+
+ "<p>Are you sure you want to scale the image?</p></qt>");
+
+ caption = i18n ("Scale Image?");
+ continueButtonText = i18n ("Scal&e Image");
+ }
+ else if (actionTarget == eSelection)
+ {
+ message =
+ i18n ("<qt><p>Scaling the selection to %1x%2"
+ " may take a substantial amount of memory."
+ " This can reduce system"
+ " responsiveness and cause other application resource"
+ " problems.</p>"
+
+ "<p>Are you sure you want to scale the selection?</p></qt>");
+
+ caption = i18n ("Scale Selection?");
+ continueButtonText = i18n ("Scal&e Selection");
+ }
+
+ break;
+
+ case kpToolResizeScaleCommand::SmoothScale:
+ if (actionTarget == eImage)
+ {
+ message =
+ i18n ("<qt><p>Smooth Scaling the image to %1x%2"
+ " may take a substantial amount of memory."
+ " This can reduce system"
+ " responsiveness and cause other application resource"
+ " problems.</p>"
+
+ "<p>Are you sure you want to smooth scale the image?</p></qt>");
+
+ caption = i18n ("Smooth Scale Image?");
+ continueButtonText = i18n ("Smooth Scal&e Image");
+ }
+ else if (actionTarget == eSelection)
+ {
+ message =
+ i18n ("<qt><p>Smooth Scaling the selection to %1x%2"
+ " may take a substantial amount of memory."
+ " This can reduce system"
+ " responsiveness and cause other application resource"
+ " problems.</p>"
+
+ "<p>Are you sure you want to smooth scale the selection?</p></qt>");
+
+ caption = i18n ("Smooth Scale Selection?");
+ continueButtonText = i18n ("Smooth Scal&e Selection");
+ }
+
+ break;
+ }
+
+
+ if (kpTool::warnIfBigImageSize (originalWidth (),
+ originalHeight (),
+ imageWidth (), imageHeight (),
+ message.arg (imageWidth ()).arg (imageHeight ()),
+ caption,
+ continueButtonText,
+ this))
+ {
+ KDialogBase::slotOk ();
+ }
+}
+
+
+#include <kptoolresizescale.moc>
diff --git a/kolourpaint/tools/kptoolresizescale.h b/kolourpaint/tools/kptoolresizescale.h
new file mode 100644
index 00000000..e23b040e
--- /dev/null
+++ b/kolourpaint/tools/kptoolresizescale.h
@@ -0,0 +1,196 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kptoolresizescale_h__
+#define __kptoolresizescale_h__
+
+#include <qpixmap.h>
+
+#include <kpcommandhistory.h>
+#include <kdialogbase.h>
+
+#include <kpcolor.h>
+#include <kpselection.h>
+
+class QCheckBox;
+class QGroupBox;
+class QHBox;
+class QRadioButton;
+class QSize;
+class QString;
+class QToolButton;
+
+class KComboBox;
+class KDoubleNumInput;
+class KIntNumInput;
+
+class kpDocument;
+class kpMainWindow;
+class kpViewManager;
+
+class kpToolResizeScaleCommand : public kpCommand
+{
+public:
+ enum Type
+ {
+ Resize, Scale, SmoothScale
+ };
+
+ kpToolResizeScaleCommand (bool actOnSelection,
+ int newWidth, int newHeight,
+ Type type,
+ kpMainWindow *mainWindow);
+ virtual ~kpToolResizeScaleCommand ();
+
+ virtual QString name () const;
+ virtual int size () const;
+
+public:
+ int newWidth () const;
+ void setNewWidth (int width);
+
+ int newHeight () const;
+ void setNewHeight (int height);
+
+ QSize newSize () const;
+ virtual void resize (int width, int height);
+
+public:
+ bool scaleSelectionWithImage () const;
+
+private:
+ void scaleSelectionRegionWithDocument ();
+
+public:
+ virtual void execute ();
+ virtual void unexecute ();
+
+protected:
+ bool m_actOnSelection;
+ int m_newWidth, m_newHeight;
+ Type m_type;
+ bool m_isLosslessScale;
+ bool m_scaleSelectionWithImage;
+ kpColor m_backgroundColor;
+
+ int m_oldWidth, m_oldHeight;
+ bool m_actOnTextSelection;
+ QPixmap m_oldPixmap, m_oldRightPixmap, m_oldBottomPixmap;
+ kpSelection *m_oldSelection;
+};
+
+class kpToolResizeScaleDialog : public KDialogBase
+{
+Q_OBJECT
+
+public:
+ kpToolResizeScaleDialog (kpMainWindow *mainWindow);
+ virtual ~kpToolResizeScaleDialog ();
+
+ enum ActOn
+ {
+ Image, Selection
+ };
+
+private:
+ static kpToolResizeScaleCommand::Type s_lastType;
+ static double s_lastPercentWidth, s_lastPercentHeight;
+
+private:
+ kpDocument *document () const;
+ kpSelection *selection () const;
+
+ void createActOnBox (QWidget *baseWidget);
+ void createOperationGroupBox (QWidget *baseWidget);
+ void createDimensionsGroupBox (QWidget *baseWidget);
+
+ void widthFitHeightToAspectRatio ();
+ void heightFitWidthToAspectRatio ();
+
+private:
+ bool resizeEnabled () const;
+ bool scaleEnabled () const;
+ bool smoothScaleEnabled () const;
+
+public slots:
+ void slotActOnChanged ();
+ void slotTypeChanged ();
+
+ void slotWidthChanged (int width);
+ void slotHeightChanged (int height);
+
+ void slotPercentWidthChanged (double percentWidth);
+ void slotPercentHeightChanged (double percentHeight);
+
+public:
+ // (refers only to the state of the checkbox - user of dialog does
+ // not have to do extra calculations)
+ bool keepAspectRatio () const;
+public slots:
+ void setKeepAspectRatio (bool on);
+
+private:
+ int originalWidth () const;
+ int originalHeight () const;
+
+public:
+ int imageWidth () const;
+ int imageHeight () const;
+ bool actOnSelection () const;
+ kpToolResizeScaleCommand::Type type () const;
+
+ bool isNoOp () const;
+
+private slots:
+ virtual void slotOk ();
+
+private:
+ kpMainWindow *m_mainWindow;
+
+ QHBox *m_actOnBox;
+ QLabel *m_actOnLabel;
+ KComboBox *m_actOnCombo;
+
+ QGroupBox *m_operationGroupBox;
+ QToolButton *m_resizeButton,
+ *m_scaleButton,
+ *m_smoothScaleButton;
+ QLabel *m_resizeLabel,
+ *m_scaleLabel,
+ *m_smoothScaleLabel;
+
+ QGroupBox *m_dimensionsGroupBox;
+ KIntNumInput *m_originalWidthInput, *m_originalHeightInput,
+ *m_newWidthInput, *m_newHeightInput;
+ KDoubleNumInput *m_percentWidthInput, *m_percentHeightInput;
+ QCheckBox *m_keepAspectRatioCheckBox;
+
+ int m_ignoreKeepAspectRatio;
+};
+
+#endif // __kptoolresizescale_h__
diff --git a/kolourpaint/tools/kptoolrotate.cpp b/kolourpaint/tools/kptoolrotate.cpp
new file mode 100644
index 00000000..8a37b673
--- /dev/null
+++ b/kolourpaint/tools/kptoolrotate.cpp
@@ -0,0 +1,500 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG_KP_TOOL_ROTATE 0
+
+
+#include <kptoolrotate.h>
+
+#include <qapplication.h>
+#include <qbuttongroup.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qradiobutton.h>
+#include <qwmatrix.h>
+
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <knuminput.h>
+#include <klocale.h>
+
+#include <kpdefs.h>
+#include <kpdocument.h>
+#include <kpmainwindow.h>
+#include <kppixmapfx.h>
+#include <kpselection.h>
+#include <kptool.h>
+#include <kpviewmanager.h>
+
+
+kpToolRotateCommand::kpToolRotateCommand (bool actOnSelection,
+ double angle,
+ kpMainWindow *mainWindow)
+ : kpCommand (mainWindow),
+ m_actOnSelection (actOnSelection),
+ m_angle (angle),
+ m_backgroundColor (mainWindow ? mainWindow->backgroundColor (actOnSelection) : kpColor::invalid),
+ m_losslessRotation (kpPixmapFX::isLosslessRotation (angle))
+{
+}
+
+kpToolRotateCommand::~kpToolRotateCommand ()
+{
+}
+
+
+// public virtual [base kpCommand]
+QString kpToolRotateCommand::name () const
+{
+ QString opName = i18n ("Rotate");
+
+ if (m_actOnSelection)
+ return i18n ("Selection: %1").arg (opName);
+ else
+ return opName;
+}
+
+
+// public virtual [base kpCommand]
+int kpToolRotateCommand::size () const
+{
+ return kpPixmapFX::pixmapSize (m_oldPixmap) +
+ m_oldSelection.size ();
+}
+
+
+// public virtual [base kpCommand]
+void kpToolRotateCommand::execute ()
+{
+ kpDocument *doc = document ();
+ if (!doc)
+ return;
+
+
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+
+ if (!m_losslessRotation)
+ m_oldPixmap = *doc->pixmap (m_actOnSelection);
+
+
+ QPixmap newPixmap = kpPixmapFX::rotate (*doc->pixmap (m_actOnSelection),
+ m_angle,
+ m_backgroundColor);
+
+
+ if (m_actOnSelection)
+ {
+ kpSelection *sel = doc->selection ();
+
+ // Save old selection
+ m_oldSelection = *sel;
+ m_oldSelection.setPixmap (QPixmap ());
+
+
+ // Calculate new top left (so selection rotates about centre)
+ // (the Times2 trickery is used to reduce integer division error without
+ // resorting to the troublesome world of floating point)
+ QPoint oldCenterTimes2 (sel->x () * 2 + sel->width (),
+ sel->y () * 2 + sel->height ());
+ QPoint newTopLeftTimes2 (oldCenterTimes2 - QPoint (newPixmap.width (), newPixmap.height ()));
+ QPoint newTopLeft (newTopLeftTimes2.x () / 2, newTopLeftTimes2.y () / 2);
+
+
+ // Calculate rotated points
+ QPointArray currentPoints = sel->points ();
+ currentPoints.translate (-currentPoints.boundingRect ().x (),
+ -currentPoints.boundingRect ().y ());
+ QWMatrix rotateMatrix = kpPixmapFX::rotateMatrix (*doc->pixmap (m_actOnSelection), m_angle);
+ currentPoints = rotateMatrix.map (currentPoints);
+ currentPoints.translate (-currentPoints.boundingRect ().x () + newTopLeft.x (),
+ -currentPoints.boundingRect ().y () + newTopLeft.y ());
+
+
+ if (currentPoints.boundingRect ().width () == newPixmap.width () &&
+ currentPoints.boundingRect ().height () == newPixmap.height ())
+ {
+ doc->setSelection (kpSelection (currentPoints, newPixmap,
+ m_oldSelection.transparency ()));
+ }
+ else
+ {
+ // TODO: fix the latter "victim of" problem in kpSelection by
+ // allowing the border width & height != pixmap width & height
+ // Or maybe autocrop?
+ #if DEBUG_KP_TOOL_ROTATE
+ kdDebug () << "kpToolRotateCommand::execute() currentPoints.boundingRect="
+ << currentPoints.boundingRect ()
+ << " newPixmap: w=" << newPixmap.width ()
+ << " h=" << newPixmap.height ()
+ << " (victim of rounding error and/or rotated-a-(rectangular)-pixmap-that-was-transparent-in-the-corners-making-sel-uselessly-bigger-than-needs-be)"
+ << endl;
+ #endif
+ doc->setSelection (kpSelection (kpSelection::Rectangle,
+ QRect (newTopLeft.x (), newTopLeft.y (),
+ newPixmap.width (), newPixmap.height ()),
+ newPixmap,
+ m_oldSelection.transparency ()));
+ }
+
+ if (m_mainWindow->tool ())
+ m_mainWindow->tool ()->somethingBelowTheCursorChanged ();
+ }
+ else
+ doc->setPixmap (newPixmap);
+
+
+ QApplication::restoreOverrideCursor ();
+}
+
+// public virtual [base kpCommand]
+void kpToolRotateCommand::unexecute ()
+{
+ kpDocument *doc = document ();
+ if (!doc)
+ return;
+
+
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+
+ QPixmap oldPixmap;
+
+ if (!m_losslessRotation)
+ {
+ oldPixmap = m_oldPixmap;
+ m_oldPixmap.resize (0, 0);
+ }
+ else
+ {
+ oldPixmap = kpPixmapFX::rotate (*doc->pixmap (m_actOnSelection),
+ 360 - m_angle,
+ m_backgroundColor);
+ }
+
+
+ if (!m_actOnSelection)
+ doc->setPixmap (oldPixmap);
+ else
+ {
+ kpSelection oldSelection = m_oldSelection;
+ oldSelection.setPixmap (oldPixmap);
+ doc->setSelection (oldSelection);
+
+ if (m_mainWindow->tool ())
+ m_mainWindow->tool ()->somethingBelowTheCursorChanged ();
+ }
+
+
+ QApplication::restoreOverrideCursor ();
+}
+
+
+/*
+ * kpToolRotateDialog
+ */
+
+
+// private static
+int kpToolRotateDialog::s_lastWidth = -1,
+ kpToolRotateDialog::s_lastHeight = -1;
+
+// private static
+bool kpToolRotateDialog::s_lastIsClockwise = true;
+int kpToolRotateDialog::s_lastAngleRadioButtonID = 3;
+int kpToolRotateDialog::s_lastAngleCustom = 0;
+
+
+kpToolRotateDialog::kpToolRotateDialog (bool actOnSelection,
+ kpMainWindow *mainWindow,
+ const char *name)
+ : kpToolPreviewDialog (kpToolPreviewDialog::AllFeatures,
+ false/*don't reserve top row*/,
+ actOnSelection ? i18n ("Rotate Selection") : i18n ("Rotate Image"),
+ i18n ("After Rotate:"),
+ actOnSelection, mainWindow, name)
+{
+ // Too confusing - disable for now
+ s_lastAngleRadioButtonID = 3;
+ s_lastAngleCustom = 0;
+
+
+ createDirectionGroupBox ();
+ createAngleGroupBox ();
+
+
+ if (s_lastWidth > 0 && s_lastHeight > 0)
+ resize (s_lastWidth, s_lastHeight);
+
+
+ slotAngleCustomRadioButtonToggled (m_angleCustomRadioButton->isChecked ());
+ slotUpdate ();
+}
+
+kpToolRotateDialog::~kpToolRotateDialog ()
+{
+ s_lastWidth = width (), s_lastHeight = height ();
+}
+
+
+// private
+void kpToolRotateDialog::createDirectionGroupBox ()
+{
+ QGroupBox *directionGroupBox = new QGroupBox (i18n ("Direction"), mainWidget ());
+ addCustomWidget (directionGroupBox);
+
+
+ QLabel *antiClockwisePixmapLabel = new QLabel (directionGroupBox);
+ antiClockwisePixmapLabel->setPixmap (UserIcon ("image_rotate_anticlockwise"));
+
+ QLabel *clockwisePixmapLabel = new QLabel (directionGroupBox);
+ clockwisePixmapLabel->setPixmap (UserIcon ("image_rotate_clockwise"));
+
+
+ m_antiClockwiseRadioButton = new QRadioButton (i18n ("Cou&nterclockwise"), directionGroupBox);
+ m_clockwiseRadioButton = new QRadioButton (i18n ("C&lockwise"), directionGroupBox);
+
+
+ m_antiClockwiseRadioButton->setChecked (!s_lastIsClockwise);
+ m_clockwiseRadioButton->setChecked (s_lastIsClockwise);
+
+
+ QButtonGroup *buttonGroup = new QButtonGroup (directionGroupBox);
+ buttonGroup->hide ();
+
+ buttonGroup->insert (m_antiClockwiseRadioButton);
+ buttonGroup->insert (m_clockwiseRadioButton);
+
+
+ QGridLayout *directionLayout = new QGridLayout (directionGroupBox,
+ 2, 2, marginHint () * 2, spacingHint ());
+ directionLayout->addWidget (antiClockwisePixmapLabel, 0, 0, Qt::AlignCenter);
+ directionLayout->addWidget (clockwisePixmapLabel, 0, 1, Qt::AlignCenter);
+ directionLayout->addWidget (m_antiClockwiseRadioButton, 1, 0, Qt::AlignCenter);
+ directionLayout->addWidget (m_clockwiseRadioButton, 1, 1, Qt::AlignCenter);
+
+
+ connect (m_antiClockwiseRadioButton, SIGNAL (toggled (bool)),
+ this, SLOT (slotUpdate ()));
+ connect (m_clockwiseRadioButton, SIGNAL (toggled (bool)),
+ this, SLOT (slotUpdate ()));
+}
+
+// private
+void kpToolRotateDialog::createAngleGroupBox ()
+{
+ QGroupBox *angleGroupBox = new QGroupBox (i18n ("Angle"), mainWidget ());
+ addCustomWidget (angleGroupBox);
+
+
+ m_angle90RadioButton = new QRadioButton (i18n ("90 &degrees"), angleGroupBox);
+ m_angle180RadioButton = new QRadioButton (i18n ("180 d&egrees"), angleGroupBox);
+ m_angle270RadioButton = new QRadioButton (i18n ("270 de&grees"), angleGroupBox);
+
+ m_angleCustomRadioButton = new QRadioButton (i18n ("C&ustom:"), angleGroupBox);
+ m_angleCustomInput = new KIntNumInput (s_lastAngleCustom, angleGroupBox);
+ m_angleCustomInput->setMinValue (-359);
+ m_angleCustomInput->setMaxValue (+359);
+ QLabel *degreesLabel = new QLabel (i18n ("degrees"), angleGroupBox);
+
+
+ m_angleButtonGroup = new QButtonGroup (angleGroupBox);
+ m_angleButtonGroup->hide ();
+
+ m_angleButtonGroup->insert (m_angle90RadioButton);
+ m_angleButtonGroup->insert (m_angle180RadioButton);
+ m_angleButtonGroup->insert (m_angle270RadioButton);
+
+ m_angleButtonGroup->insert (m_angleCustomRadioButton);
+
+ m_angleButtonGroup->setButton (s_lastAngleRadioButtonID);
+
+
+ QGridLayout *angleLayout = new QGridLayout (angleGroupBox,
+ 6, 3,
+ marginHint () * 2, spacingHint ());
+
+ angleLayout->addMultiCellWidget (m_angle90RadioButton, 0, 0, 0, 2);
+ angleLayout->addMultiCellWidget (m_angle180RadioButton, 1, 1, 0, 2);
+ angleLayout->addMultiCellWidget (m_angle270RadioButton, 2, 2, 0, 2);
+
+ angleLayout->addWidget (m_angleCustomRadioButton, 3, 0);
+ angleLayout->addWidget (m_angleCustomInput, 3, 1);
+ angleLayout->addWidget (degreesLabel, 3, 2);
+
+ angleLayout->setColStretch (1, 2); // Stretch Custom Angle Input
+
+
+ connect (m_angle90RadioButton, SIGNAL (toggled (bool)),
+ this, SLOT (slotUpdate ()));
+ connect (m_angle180RadioButton, SIGNAL (toggled (bool)),
+ this, SLOT (slotUpdate ()));
+ connect (m_angle270RadioButton, SIGNAL (toggled (bool)),
+ this, SLOT (slotUpdate ()));
+
+ connect (m_angleCustomRadioButton, SIGNAL (toggled (bool)),
+ this, SLOT (slotAngleCustomRadioButtonToggled (bool)));
+ connect (m_angleCustomRadioButton, SIGNAL (toggled (bool)),
+ this, SLOT (slotUpdate ()));
+
+ connect (m_angleCustomInput, SIGNAL (valueChanged (int)),
+ this, SLOT (slotUpdate ()));
+}
+
+
+// public virtual [base kpToolPreviewDialog]
+bool kpToolRotateDialog::isNoOp () const
+{
+ return (angle () == 0);
+}
+
+// public
+int kpToolRotateDialog::angle () const
+{
+ int retAngle;
+
+
+ if (m_angle90RadioButton->isChecked ())
+ retAngle = 90;
+ else if (m_angle180RadioButton->isChecked ())
+ retAngle = 180;
+ else if (m_angle270RadioButton->isChecked ())
+ retAngle = 270;
+ else // if (m_angleCustomRadioButton->isChecked ())
+ retAngle = m_angleCustomInput->value ();
+
+
+ if (m_antiClockwiseRadioButton->isChecked ())
+ retAngle *= -1;
+
+
+ if (retAngle < 0)
+ retAngle += ((0 - retAngle) / 360 + 1) * 360;
+
+ if (retAngle >= 360)
+ retAngle -= ((retAngle - 360) / 360 + 1) * 360;
+
+
+ return retAngle;
+}
+
+
+// private virtual [base kpToolPreviewDialog]
+QSize kpToolRotateDialog::newDimensions () const
+{
+ QWMatrix matrix = kpPixmapFX::rotateMatrix (m_oldWidth, m_oldHeight, angle ());
+ // TODO: Should we be using QWMatrix::Areas?
+ QRect rect = matrix.map (QRect (0, 0, m_oldWidth, m_oldHeight));
+ return rect.size ();
+}
+
+// private virtual [base kpToolPreviewDialog]
+QPixmap kpToolRotateDialog::transformPixmap (const QPixmap &pixmap,
+ int targetWidth, int targetHeight) const
+{
+ return kpPixmapFX::rotate (pixmap, angle (),
+ m_mainWindow ? m_mainWindow->backgroundColor (m_actOnSelection) : kpColor::invalid,
+ targetWidth, targetHeight);
+}
+
+
+// private slot
+void kpToolRotateDialog::slotAngleCustomRadioButtonToggled (bool isChecked)
+{
+ m_angleCustomInput->setEnabled (isChecked);
+
+ if (isChecked)
+ m_angleCustomInput->setEditFocus ();
+}
+
+// private slot virtual [base kpToolPreviewDialog]
+void kpToolRotateDialog::slotUpdate ()
+{
+ s_lastIsClockwise = m_clockwiseRadioButton->isChecked ();
+ s_lastAngleRadioButtonID = m_angleButtonGroup->selectedId ();
+ s_lastAngleCustom = m_angleCustomInput->value ();
+
+ kpToolPreviewDialog::slotUpdate ();
+}
+
+
+// private slot virtual [base KDialogBase]
+void kpToolRotateDialog::slotOk ()
+{
+ QString message, caption, continueButtonText;
+
+ if (document ()->selection ())
+ {
+ if (!document ()->selection ()->isText ())
+ {
+ message =
+ i18n ("<qt><p>Rotating the selection to %1x%2"
+ " may take a substantial amount of memory."
+ " This can reduce system"
+ " responsiveness and cause other application resource"
+ " problems.</p>"
+
+ "<p>Are you sure want to rotate the selection?</p></qt>");
+
+ caption = i18n ("Rotate Selection?");
+ continueButtonText = i18n ("Rotat&e Selection");
+ }
+ }
+ else
+ {
+ message =
+ i18n ("<qt><p>Rotating the image to %1x%2"
+ " may take a substantial amount of memory."
+ " This can reduce system"
+ " responsiveness and cause other application resource"
+ " problems.</p>"
+
+ "<p>Are you sure want to rotate the image?</p></qt>");
+
+ caption = i18n ("Rotate Image?");
+ continueButtonText = i18n ("Rotat&e Image");
+ }
+
+
+ const int newWidth = newDimensions ().width ();
+ const int newHeight = newDimensions ().height ();
+
+ if (kpTool::warnIfBigImageSize (m_oldWidth,
+ m_oldHeight,
+ newWidth, newHeight,
+ message.arg (newWidth).arg (newHeight),
+ caption,
+ continueButtonText,
+ this))
+ {
+ KDialogBase::slotOk ();
+ }
+}
+
+#include <kptoolrotate.moc>
diff --git a/kolourpaint/tools/kptoolrotate.h b/kolourpaint/tools/kptoolrotate.h
new file mode 100644
index 00000000..887473dc
--- /dev/null
+++ b/kolourpaint/tools/kptoolrotate.h
@@ -0,0 +1,129 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kptoolrotate_h__
+#define __kptoolrotate_h__
+
+#include <qpixmap.h>
+#include <qpoint.h>
+
+#include <kdialogbase.h>
+
+#include <kpcolor.h>
+#include <kpcommandhistory.h>
+#include <kpselection.h>
+#include <kptoolpreviewdialog.h>
+
+
+class QButtonGroup;
+class QRadioButton;
+class QString;
+
+class KIntNumInput;
+
+class kpDocument;
+class kpViewManager;
+class kpMainWindow;
+
+
+class kpToolRotateCommand : public kpCommand
+{
+public:
+ kpToolRotateCommand (bool actOnSelection,
+ double angle, // 0 <= angle < 360 (clockwise)
+ kpMainWindow *mainWindow);
+ virtual ~kpToolRotateCommand ();
+
+ virtual QString name () const;
+
+ virtual int size () const;
+
+ virtual void execute ();
+ virtual void unexecute ();
+
+private:
+ bool m_actOnSelection;
+ double m_angle;
+
+ kpColor m_backgroundColor;
+
+ bool m_losslessRotation;
+ QPixmap m_oldPixmap;
+ kpSelection m_oldSelection;
+};
+
+
+class kpToolRotateDialog : public kpToolPreviewDialog
+{
+Q_OBJECT
+
+public:
+ kpToolRotateDialog (bool actOnSelection,
+ kpMainWindow *parent,
+ const char *name = 0);
+ virtual ~kpToolRotateDialog ();
+
+private:
+ static int s_lastWidth, s_lastHeight;
+ static bool s_lastIsClockwise;
+ static int s_lastAngleRadioButtonID;
+ static int s_lastAngleCustom;
+
+ void createDirectionGroupBox ();
+ void createAngleGroupBox ();
+
+public:
+ virtual bool isNoOp () const;
+ int angle () const; // 0 <= angle < 360 (clockwise);
+
+private:
+ virtual QSize newDimensions () const;
+ virtual QPixmap transformPixmap (const QPixmap &pixmap,
+ int targetWidth, int targetHeight) const;
+
+private slots:
+ void slotAngleCustomRadioButtonToggled (bool isChecked);
+ virtual void slotUpdate ();
+
+private slots:
+ virtual void slotOk ();
+
+private:
+ QRadioButton *m_antiClockwiseRadioButton,
+ *m_clockwiseRadioButton;
+
+ QButtonGroup *m_angleButtonGroup;
+ QRadioButton *m_angle90RadioButton,
+ *m_angle180RadioButton,
+ *m_angle270RadioButton,
+ *m_angleCustomRadioButton;
+ KIntNumInput *m_angleCustomInput;
+};
+
+
+#endif // __kptoolrotate_h__
diff --git a/kolourpaint/tools/kptoolroundedrectangle.cpp b/kolourpaint/tools/kptoolroundedrectangle.cpp
new file mode 100644
index 00000000..b0f4ba05
--- /dev/null
+++ b/kolourpaint/tools/kptoolroundedrectangle.cpp
@@ -0,0 +1,45 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <klocale.h>
+#include <kptoolroundedrectangle.h>
+
+kpToolRoundedRectangle::kpToolRoundedRectangle (kpMainWindow *mainWindow)
+ : kpToolRectangle (RoundedRectangle,
+ i18n ("Rounded Rectangle"),
+ i18n ("Draws rectangles and squares with rounded corners"),
+ Qt::Key_U,
+ mainWindow, "tool_rounded_rectangle")
+{
+}
+
+kpToolRoundedRectangle::~kpToolRoundedRectangle ()
+{
+}
+
+#include <kptoolroundedrectangle.moc>
diff --git a/kolourpaint/tools/kptoolroundedrectangle.h b/kolourpaint/tools/kptoolroundedrectangle.h
new file mode 100644
index 00000000..924c1b34
--- /dev/null
+++ b/kolourpaint/tools/kptoolroundedrectangle.h
@@ -0,0 +1,45 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kptoolroundedrectangle_h__
+#define __kptoolroundedrectangle_h__
+
+#include <kptoolrectangle.h>
+
+class kpMainWindow;
+
+class kpToolRoundedRectangle : public kpToolRectangle
+{
+Q_OBJECT
+
+public:
+ kpToolRoundedRectangle (kpMainWindow *);
+ virtual ~kpToolRoundedRectangle ();
+};
+
+#endif // __kptoolroundedrectangle_h__
diff --git a/kolourpaint/tools/kptoolselection.cpp b/kolourpaint/tools/kptoolselection.cpp
new file mode 100644
index 00000000..f664f01b
--- /dev/null
+++ b/kolourpaint/tools/kptoolselection.cpp
@@ -0,0 +1,2371 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define DEBUG_KP_TOOL_SELECTION 0
+
+
+#include <kptoolselection.h>
+
+#include <qapplication.h>
+#include <qbitmap.h>
+#include <qcursor.h>
+#include <qpainter.h>
+#include <qpopupmenu.h>
+#include <qtimer.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kpcommandhistory.h>
+#include <kpdefs.h>
+#include <kpdocument.h>
+#include <kpmainwindow.h>
+#include <kpselection.h>
+#include <kptooltoolbar.h>
+#include <kptoolwidgetopaqueortransparent.h>
+#include <kpview.h>
+#include <kpviewmanager.h>
+
+
+kpToolSelection::kpToolSelection (Mode mode,
+ const QString &text,
+ const QString &description,
+ int key,
+ kpMainWindow *mainWindow,
+ const char *name)
+ : kpTool (text, description, key, mainWindow, name),
+ m_mode (mode),
+ m_currentPullFromDocumentCommand (0),
+ m_currentMoveCommand (0),
+ m_currentResizeScaleCommand (0),
+ m_toolWidgetOpaqueOrTransparent (0),
+ m_currentCreateTextCommand (0),
+ m_createNOPTimer (new QTimer (this)),
+ m_RMBMoveUpdateGUITimer (new QTimer (this))
+{
+ connect (m_createNOPTimer, SIGNAL (timeout ()),
+ this, SLOT (delayedDraw ()));
+ connect (m_RMBMoveUpdateGUITimer, SIGNAL (timeout ()),
+ this, SLOT (slotRMBMoveUpdateGUI ()));
+}
+
+kpToolSelection::~kpToolSelection ()
+{
+}
+
+
+// private
+void kpToolSelection::pushOntoDocument ()
+{
+#if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "kpToolSelection::pushOntoDocument() CALLED" << endl;
+#endif
+ mainWindow ()->slotDeselect ();
+}
+
+
+// protected
+bool kpToolSelection::onSelectionToMove () const
+{
+ kpView *v = viewManager ()->viewUnderCursor ();
+ if (!v)
+ return 0;
+
+ return v->mouseOnSelectionToMove (m_currentViewPoint);
+}
+
+// protected
+int kpToolSelection::onSelectionResizeHandle () const
+{
+ kpView *v = viewManager ()->viewUnderCursor ();
+ if (!v)
+ return 0;
+
+ return v->mouseOnSelectionResizeHandle (m_currentViewPoint);
+}
+
+// protected
+bool kpToolSelection::onSelectionToSelectText () const
+{
+ kpView *v = viewManager ()->viewUnderCursor ();
+ if (!v)
+ return 0;
+
+ return v->mouseOnSelectionToSelectText (m_currentViewPoint);
+}
+
+
+// public
+QString kpToolSelection::haventBegunDrawUserMessage () const
+{
+#if DEBUG_KP_TOOL_SELECTION && 0
+ kdDebug () << "kpToolSelection::haventBegunDrawUserMessage()"
+ " cancelledShapeButStillHoldingButtons="
+ << m_cancelledShapeButStillHoldingButtons
+ << endl;
+#endif
+
+ if (m_cancelledShapeButStillHoldingButtons)
+ return i18n ("Let go of all the mouse buttons.");
+
+ kpSelection *sel = document ()->selection ();
+ if (sel && onSelectionResizeHandle () && !controlOrShiftPressed ())
+ {
+ if (m_mode == Text)
+ return i18n ("Left drag to resize text box.");
+ else
+ return i18n ("Left drag to scale selection.");
+ }
+ else if (sel && sel->contains (m_currentPoint))
+ {
+ if (m_mode == Text)
+ {
+ if (onSelectionToSelectText () && !controlOrShiftPressed ())
+ return i18n ("Left click to change cursor position.");
+ else
+ return i18n ("Left drag to move text box.");
+ }
+ else
+ {
+ return i18n ("Left drag to move selection.");
+ }
+ }
+ else
+ {
+ if (m_mode == Text)
+ return i18n ("Left drag to create text box.");
+ else
+ return i18n ("Left drag to create selection.");
+ }
+}
+
+
+// virtual
+void kpToolSelection::begin ()
+{
+#if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "kpToolSelection::begin()" << endl;
+#endif
+
+ kpToolToolBar *tb = toolToolBar ();
+
+ if (tb)
+ {
+ m_toolWidgetOpaqueOrTransparent = tb->toolWidgetOpaqueOrTransparent ();
+
+ if (m_toolWidgetOpaqueOrTransparent)
+ {
+ connect (m_toolWidgetOpaqueOrTransparent, SIGNAL (isOpaqueChanged (bool)),
+ this, SLOT (slotIsOpaqueChanged ()));
+ m_toolWidgetOpaqueOrTransparent->show ();
+ }
+ }
+ else
+ {
+ m_toolWidgetOpaqueOrTransparent = 0;
+ }
+
+ viewManager ()->setQueueUpdates ();
+ {
+ viewManager ()->setSelectionBorderVisible (true);
+ viewManager ()->setSelectionBorderFinished (true);
+ }
+ viewManager ()->restoreQueueUpdates ();
+
+ m_startDragFromSelectionTopLeft = QPoint ();
+ m_dragType = Unknown;
+ m_dragHasBegun = false;
+ m_hadSelectionBeforeDrag = false; // arbitrary
+ m_resizeScaleType = 0;
+
+ m_currentPullFromDocumentCommand = 0;
+ m_currentMoveCommand = 0;
+ m_currentResizeScaleCommand = 0;
+ m_currentCreateTextCommand = 0;
+
+ m_cancelledShapeButStillHoldingButtons = false;
+
+ setUserMessage (haventBegunDrawUserMessage ());
+}
+
+// virtual
+void kpToolSelection::end ()
+{
+#if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "kpToolSelection::end()" << endl;
+#endif
+
+ if (document ()->selection ())
+ pushOntoDocument ();
+
+ if (m_toolWidgetOpaqueOrTransparent)
+ {
+ disconnect (m_toolWidgetOpaqueOrTransparent, SIGNAL (isOpaqueChanged (bool)),
+ this, SLOT (slotIsOpaqueChanged ()));
+ m_toolWidgetOpaqueOrTransparent = 0;
+ }
+
+ viewManager ()->unsetCursor ();
+}
+
+// virtual
+void kpToolSelection::reselect ()
+{
+#if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "kpToolSelection::reselect()" << endl;
+#endif
+
+ if (document ()->selection ())
+ pushOntoDocument ();
+}
+
+
+// virtual
+void kpToolSelection::beginDraw ()
+{
+#if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "kpToolSelection::beginDraw() m_startPoint="
+ << m_startPoint
+ << " QCursor::pos() view startPoint="
+ << m_viewUnderStartPoint->mapFromGlobal (QCursor::pos ())
+ << endl;
+#endif
+
+ m_createNOPTimer->stop ();
+ m_RMBMoveUpdateGUITimer->stop ();
+
+
+ // In case the cursor was wrong to start with
+ // (forgot to call kpTool::somethingBelowTheCursorChanged()),
+ // make sure it is correct during this operation.
+ hover (m_currentPoint);
+
+ // Currently used only to end the current text
+ if (hasBegunShape ())
+ endShape (m_currentPoint, QRect (m_startPoint/* TODO: wrong */, m_currentPoint).normalize ());
+
+ m_dragType = Create;
+ m_dragHasBegun = false;
+
+ kpSelection *sel = document ()->selection ();
+ m_hadSelectionBeforeDrag = bool (sel);
+
+ if (sel)
+ {
+ #if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "\thas sel region rect=" << sel->boundingRect () << endl;
+ #endif
+ QRect selectionRect = sel->boundingRect ();
+
+ if (onSelectionResizeHandle () && !controlOrShiftPressed ())
+ {
+ #if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "\t\tis resize/scale" << endl;
+ #endif
+
+ m_startDragFromSelectionTopLeft = m_currentPoint - selectionRect.topLeft ();
+ m_dragType = ResizeScale;
+ m_resizeScaleType = onSelectionResizeHandle ();
+
+ viewManager ()->setQueueUpdates ();
+ {
+ viewManager ()->setSelectionBorderVisible (true);
+ viewManager ()->setSelectionBorderFinished (true);
+ viewManager ()->setTextCursorEnabled (false);
+ }
+ viewManager ()->restoreQueueUpdates ();
+ }
+ else if (sel->contains (m_currentPoint))
+ {
+ if (m_mode == Text && onSelectionToSelectText () && !controlOrShiftPressed ())
+ {
+ #if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "\t\tis select cursor pos" << endl;
+ #endif
+
+ m_dragType = SelectText;
+
+ viewManager ()->setTextCursorPosition (sel->textRowForPoint (m_currentPoint),
+ sel->textColForPoint (m_currentPoint));
+ }
+ else
+ {
+ #if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "\t\tis move" << endl;
+ #endif
+
+ m_startDragFromSelectionTopLeft = m_currentPoint - selectionRect.topLeft ();
+ m_dragType = Move;
+
+ if (m_mouseButton == 0)
+ {
+ setSelectionBorderForMove ();
+ }
+ else
+ {
+ // Don't hide sel border momentarily if user is just
+ // right _clicking_ selection
+ m_RMBMoveUpdateGUITimer->start (100, true/*single shot*/);
+ }
+ }
+ }
+ else
+ {
+ #if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "\t\tis new sel" << endl;
+ #endif
+
+ pushOntoDocument ();
+ }
+ }
+
+ // creating new selection?
+ if (m_dragType == Create)
+ {
+ viewManager ()->setQueueUpdates ();
+ {
+ viewManager ()->setSelectionBorderVisible (true);
+ viewManager ()->setSelectionBorderFinished (false);
+ viewManager ()->setTextCursorEnabled (false);
+ }
+ viewManager ()->restoreQueueUpdates ();
+
+ m_createNOPTimer->start (200, true/*single shot*/);
+ }
+
+ if (m_dragType != SelectText)
+ {
+ setUserMessage (cancelUserMessage ());
+ }
+}
+
+
+// protected
+const QCursor &kpToolSelection::cursor () const
+{
+#if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "kpToolSelection::cursor()"
+ << " m_currentPoint=" << m_currentPoint
+ << " QCursor::pos() view under cursor="
+ << (viewUnderCursor () ?
+ viewUnderCursor ()->mapFromGlobal (QCursor::pos ()) :
+ KP_INVALID_POINT)
+ << " controlOrShiftPressed=" << controlOrShiftPressed ()
+ << endl;
+#endif
+
+ kpSelection *sel = document () ? document ()->selection () : 0;
+
+ if (sel && onSelectionResizeHandle () && !controlOrShiftPressed ())
+ {
+ #if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "\tonSelectionResizeHandle="
+ << onSelectionResizeHandle () << endl;
+ #endif
+ switch (onSelectionResizeHandle ())
+ {
+ case (kpView::Top | kpView::Left):
+ case (kpView::Bottom | kpView::Right):
+ return Qt::sizeFDiagCursor;
+
+ case (kpView::Bottom | kpView::Left):
+ case (kpView::Top | kpView::Right):
+ return Qt::sizeBDiagCursor;
+
+ case kpView::Top:
+ case kpView::Bottom:
+ return Qt::sizeVerCursor;
+
+ case kpView::Left:
+ case kpView::Right:
+ return Qt::sizeHorCursor;
+ }
+
+ return Qt::arrowCursor;
+ }
+ else if (sel && sel->contains (m_currentPoint))
+ {
+ #if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "\tsel contains currentPoint; selecting text? "
+ << onSelectionToSelectText () << endl;
+ #endif
+
+ if (m_mode == Text && onSelectionToSelectText () && !controlOrShiftPressed ())
+ return Qt::ibeamCursor;
+ else
+ return Qt::sizeAllCursor;
+ }
+ else
+ {
+ #if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "\tnot on sel" << endl;
+ #endif
+ return Qt::crossCursor;
+ }
+}
+
+// virtual
+void kpToolSelection::hover (const QPoint &point)
+{
+#if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "kpToolSelection::hover" << point << endl;
+#endif
+
+ viewManager ()->setCursor (cursor ());
+
+ setUserShapePoints (point, KP_INVALID_POINT, false/*don't set size*/);
+ if (document () && document ()->selection ())
+ {
+ setUserShapeSize (document ()->selection ()->width (),
+ document ()->selection ()->height ());
+ }
+ else
+ {
+ setUserShapeSize (KP_INVALID_SIZE);
+ }
+
+ QString mess = haventBegunDrawUserMessage ();
+ if (mess != userMessage ())
+ setUserMessage (mess);
+}
+
+// protected
+void kpToolSelection::popupRMBMenu ()
+{
+ QPopupMenu *pop = mainWindow () ? mainWindow ()->selectionToolRMBMenu () : 0;
+ if (!pop)
+ return;
+
+ // WARNING: enters event loop - may re-enter view/tool event handlers
+ pop->exec (QCursor::pos ());
+
+ // Cursor may have moved while menu up, triggering mouseMoveEvents
+ // for the menu - not the view. Update cursor position now.
+ somethingBelowTheCursorChanged ();
+}
+
+// protected
+void kpToolSelection::setSelectionBorderForMove ()
+{
+ // don't show border while moving
+ viewManager ()->setQueueUpdates ();
+ {
+ viewManager ()->setSelectionBorderVisible (false);
+ viewManager ()->setSelectionBorderFinished (true);
+ viewManager ()->setTextCursorEnabled (false);
+ }
+ viewManager ()->restoreQueueUpdates ();
+}
+
+// protected slot
+void kpToolSelection::slotRMBMoveUpdateGUI ()
+{
+ // (just in case not called from single shot)
+ m_RMBMoveUpdateGUITimer->stop ();
+
+ setSelectionBorderForMove ();
+
+ kpSelection * const sel = document () ? document ()->selection () : 0;
+ if (sel)
+ setUserShapePoints (sel->topLeft ());
+}
+
+// protected slot
+void kpToolSelection::delayedDraw ()
+{
+#if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "kpToolSelection::delayedDraw() hasBegunDraw="
+ << hasBegunDraw ()
+ << " currentPoint=" << m_currentPoint
+ << " lastPoint=" << m_lastPoint
+ << " startPoint=" << m_startPoint
+ << endl;
+#endif
+
+ // (just in case not called from single shot)
+ m_createNOPTimer->stop ();
+
+ if (hasBegunDraw ())
+ {
+ draw (m_currentPoint, m_lastPoint,
+ QRect (m_startPoint, m_currentPoint).normalize ());
+ }
+}
+
+// virtual
+void kpToolSelection::draw (const QPoint &inThisPoint, const QPoint & /*lastPoint*/,
+ const QRect &inNormalizedRect)
+{
+ QPoint thisPoint = inThisPoint;
+ QRect normalizedRect = inNormalizedRect;
+
+#if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "kpToolSelection::draw" << thisPoint
+ << " startPoint=" << m_startPoint
+ << " normalizedRect=" << normalizedRect << endl;
+#endif
+
+
+ // OPT: return when thisPoint == m_lastPoint so that e.g. when creating
+ // Points sel, press modifiers doesn't add multiple points in same
+ // place
+
+
+ bool nextDragHasBegun = true;
+
+
+ if (m_dragType == Create)
+ {
+ #if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "\tnot moving - resizing rect to" << normalizedRect
+ << endl;
+ kdDebug () << "\t\tcreateNOPTimer->isActive()="
+ << m_createNOPTimer->isActive ()
+ << " viewManhattanLength from startPoint="
+ << m_viewUnderStartPoint->transformDocToViewX ((thisPoint - m_startPoint).manhattanLength ())
+ << endl;
+ #endif
+
+ if (m_createNOPTimer->isActive ())
+ {
+ if (m_viewUnderStartPoint->transformDocToViewX ((thisPoint - m_startPoint).manhattanLength ()) <= 6)
+ {
+ #if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "\t\tsuppress accidental movement" << endl;
+ #endif
+ thisPoint = m_startPoint;
+ }
+ else
+ {
+ #if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "\t\tit's a \"big\" intended move - stop timer" << endl;
+ #endif
+ m_createNOPTimer->stop ();
+ }
+ }
+
+
+ // Prevent unintentional 1-pixel selections
+ if (!m_dragHasBegun && thisPoint == m_startPoint)
+ {
+ if (m_mode != kpToolSelection::Text)
+ {
+ #if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "\tnon-text NOP - return" << endl;
+ #endif
+ setUserShapePoints (thisPoint);
+ return;
+ }
+ else // m_mode == kpToolSelection::Text
+ {
+ // Attempt to deselect text box by clicking?
+ if (m_hadSelectionBeforeDrag)
+ {
+ #if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "\ttext box deselect - NOP - return" << endl;
+ #endif
+ setUserShapePoints (thisPoint);
+ return;
+ }
+
+ // Drag-wise, this is a NOP so we'd normally return (hence
+ // m_dragHasBegun would not change). However, as a special
+ // case, allow user to create a text box using a single
+ // click. But don't set m_dragHasBegun for next iteration
+ // since it would be untrue.
+ //
+ // This makes sure that a single click creation of text box
+ // works even if draw() is invoked more than once at the
+ // same position (esp. with accidental drag suppression
+ // (above)).
+ nextDragHasBegun = false;
+ }
+ }
+
+
+ switch (m_mode)
+ {
+ case kpToolSelection::Rectangle:
+ {
+ const QRect usefulRect = normalizedRect.intersect (document ()->rect ());
+ document ()->setSelection (kpSelection (kpSelection::Rectangle, usefulRect,
+ mainWindow ()->selectionTransparency ()));
+
+ setUserShapePoints (m_startPoint,
+ QPoint (QMAX (0, QMIN (m_currentPoint.x (), document ()->width () - 1)),
+ QMAX (0, QMIN (m_currentPoint.y (), document ()->height () - 1))));
+ break;
+ }
+ case kpToolSelection::Text:
+ {
+ const kpTextStyle textStyle = mainWindow ()->textStyle ();
+
+ int minimumWidth, minimumHeight;
+
+ // Just a click?
+ if (!m_dragHasBegun && thisPoint == m_startPoint)
+ {
+ #if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "\tclick creating text box" << endl;
+ #endif
+
+ // (Click creating text box with RMB would not be obvious
+ // since RMB menu most likely hides text box immediately
+ // afterwards)
+ if (m_mouseButton == 1)
+ break;
+
+
+ minimumWidth = kpSelection::preferredMinimumWidthForTextStyle (textStyle);
+ if (thisPoint.x () >= m_startPoint.x ())
+ {
+ if (m_startPoint.x () + minimumWidth - 1 >= document ()->width ())
+ {
+ minimumWidth = QMAX (kpSelection::minimumWidthForTextStyle (textStyle),
+ document ()->width () - m_startPoint.x ());
+ }
+ }
+ else
+ {
+ if (m_startPoint.x () - minimumWidth + 1 < 0)
+ {
+ minimumWidth = QMAX (kpSelection::minimumWidthForTextStyle (textStyle),
+ m_startPoint.x () + 1);
+ }
+ }
+
+ minimumHeight = kpSelection::preferredMinimumHeightForTextStyle (textStyle);
+ if (thisPoint.y () >= m_startPoint.y ())
+ {
+ if (m_startPoint.y () + minimumHeight - 1 >= document ()->height ())
+ {
+ minimumHeight = QMAX (kpSelection::minimumHeightForTextStyle (textStyle),
+ document ()->height () - m_startPoint.y ());
+ }
+ }
+ else
+ {
+ if (m_startPoint.y () - minimumHeight + 1 < 0)
+ {
+ minimumHeight = QMAX (kpSelection::minimumHeightForTextStyle (textStyle),
+ m_startPoint.y () + 1);
+ }
+ }
+ }
+ else
+ {
+ #if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "\tdrag creating text box" << endl;
+ #endif
+ minimumWidth = kpSelection::minimumWidthForTextStyle (textStyle);
+ minimumHeight = kpSelection::minimumHeightForTextStyle (textStyle);
+ }
+
+
+ if (normalizedRect.width () < minimumWidth)
+ {
+ if (thisPoint.x () >= m_startPoint.x ())
+ normalizedRect.setWidth (minimumWidth);
+ else
+ normalizedRect.setX (normalizedRect.right () - minimumWidth + 1);
+ }
+
+ if (normalizedRect.height () < minimumHeight)
+ {
+ if (thisPoint.y () >= m_startPoint.y ())
+ normalizedRect.setHeight (minimumHeight);
+ else
+ normalizedRect.setY (normalizedRect.bottom () - minimumHeight + 1);
+ }
+ #if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "\t\tnormalizedRect=" << normalizedRect
+ << " kpSelection::preferredMinimumSize="
+ << QSize (minimumWidth, minimumHeight)
+ << endl;
+ #endif
+
+ QValueVector <QString> textLines (1, QString ());
+ kpSelection sel (normalizedRect, textLines, textStyle);
+
+ if (!m_currentCreateTextCommand)
+ {
+ m_currentCreateTextCommand = new kpToolSelectionCreateCommand (
+ i18n ("Text: Create Box"),
+ sel,
+ mainWindow ());
+ }
+ else
+ m_currentCreateTextCommand->setFromSelection (sel);
+
+ viewManager ()->setTextCursorPosition (0, 0);
+ document ()->setSelection (sel);
+
+ QPoint actualEndPoint = KP_INVALID_POINT;
+ if (m_startPoint == normalizedRect.topLeft ())
+ actualEndPoint = normalizedRect.bottomRight ();
+ else if (m_startPoint == normalizedRect.bottomRight ())
+ actualEndPoint = normalizedRect.topLeft ();
+ else if (m_startPoint == normalizedRect.topRight ())
+ actualEndPoint = normalizedRect.bottomLeft ();
+ else if (m_startPoint == normalizedRect.bottomLeft ())
+ actualEndPoint = normalizedRect.topRight ();
+
+ setUserShapePoints (m_startPoint, actualEndPoint);
+ break;
+ }
+ case kpToolSelection::Ellipse:
+ document ()->setSelection (kpSelection (kpSelection::Ellipse, normalizedRect,
+ mainWindow ()->selectionTransparency ()));
+ setUserShapePoints (m_startPoint, m_currentPoint);
+ break;
+ case kpToolSelection::FreeForm:
+ QPointArray points;
+
+ if (document ()->selection ())
+ points = document ()->selection ()->points ();
+
+
+ // (not detached so will modify "points" directly but
+ // still need to call kpDocument::setSelection() to
+ // update screen)
+
+ if (!m_dragHasBegun)
+ {
+ // We thought the drag at startPoint was a NOP
+ // but it turns out that it wasn't...
+ points.putPoints (points.count (), 1, m_startPoint.x (), m_startPoint.y ());
+ }
+
+ // TODO: there should be an upper limit on this before drawing the
+ // polygon becomes too slow
+ points.putPoints (points.count (), 1, thisPoint.x (), thisPoint.y ());
+
+
+ document ()->setSelection (kpSelection (points, mainWindow ()->selectionTransparency ()));
+ #if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "\t\tfreeform; #points=" << document ()->selection ()->points ().count () << endl;
+ #endif
+
+ setUserShapePoints (m_currentPoint);
+ break;
+ }
+
+ viewManager ()->setSelectionBorderVisible (true);
+ }
+ else if (m_dragType == Move)
+ {
+ #if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "\tmoving selection" << endl;
+ #endif
+
+ kpSelection *sel = document ()->selection ();
+
+ QRect targetSelRect = QRect (thisPoint.x () - m_startDragFromSelectionTopLeft.x (),
+ thisPoint.y () - m_startDragFromSelectionTopLeft.y (),
+ sel->width (),
+ sel->height ());
+
+ #if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "\t\tstartPoint=" << m_startPoint
+ << " thisPoint=" << thisPoint
+ << " startDragFromSel=" << m_startDragFromSelectionTopLeft
+ << " targetSelRect=" << targetSelRect
+ << endl;
+ #endif
+
+ // Try to make sure selection still intersects document so that it's
+ // reachable.
+
+ if (targetSelRect.right () < 0)
+ targetSelRect.moveBy (-targetSelRect.right (), 0);
+ else if (targetSelRect.left () >= document ()->width ())
+ targetSelRect.moveBy (document ()->width () - targetSelRect.left () - 1, 0);
+
+ if (targetSelRect.bottom () < 0)
+ targetSelRect.moveBy (0, -targetSelRect.bottom ());
+ else if (targetSelRect.top () >= document ()->height ())
+ targetSelRect.moveBy (0, document ()->height () - targetSelRect.top () - 1);
+
+ #if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "\t\t\tafter ensure sel rect clickable=" << targetSelRect << endl;
+ #endif
+
+
+ if (!m_dragHasBegun &&
+ targetSelRect.topLeft () + m_startDragFromSelectionTopLeft == m_startPoint)
+ {
+ #if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "\t\t\t\tnop" << endl;
+ #endif
+
+
+ if (!m_RMBMoveUpdateGUITimer->isActive ())
+ {
+ // (slotRMBMoveUpdateGUI() calls similar line)
+ setUserShapePoints (sel->topLeft ());
+ }
+
+ // Prevent both NOP drag-moves
+ return;
+ }
+
+
+ if (m_RMBMoveUpdateGUITimer->isActive ())
+ {
+ m_RMBMoveUpdateGUITimer->stop ();
+ slotRMBMoveUpdateGUI ();
+ }
+
+
+ if (!sel->pixmap () && !m_currentPullFromDocumentCommand)
+ {
+ m_currentPullFromDocumentCommand = new kpToolSelectionPullFromDocumentCommand (
+ QString::null/*uninteresting child of macro cmd*/,
+ mainWindow ());
+ m_currentPullFromDocumentCommand->execute ();
+ }
+
+ if (!m_currentMoveCommand)
+ {
+ m_currentMoveCommand = new kpToolSelectionMoveCommand (
+ QString::null/*uninteresting child of macro cmd*/,
+ mainWindow ());
+ m_currentMoveCommandIsSmear = false;
+ }
+
+
+ //viewManager ()->setQueueUpdates ();
+ //viewManager ()->setFastUpdates ();
+
+ if (m_shiftPressed)
+ m_currentMoveCommandIsSmear = true;
+
+ if (!m_dragHasBegun && (m_controlPressed || m_shiftPressed))
+ m_currentMoveCommand->copyOntoDocument ();
+
+ m_currentMoveCommand->moveTo (targetSelRect.topLeft ());
+
+ if (m_shiftPressed)
+ m_currentMoveCommand->copyOntoDocument ();
+
+ //viewManager ()->restoreFastUpdates ();
+ //viewManager ()->restoreQueueUpdates ();
+
+ QPoint start = m_currentMoveCommand->originalSelection ().topLeft ();
+ QPoint end = targetSelRect.topLeft ();
+ setUserShapePoints (start, end, false/*don't set size*/);
+ setUserShapeSize (end.x () - start.x (), end.y () - start.y ());
+ }
+ else if (m_dragType == ResizeScale)
+ {
+ #if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "\tresize/scale" << endl;
+ #endif
+
+ kpSelection *sel = document ()->selection ();
+
+ if (!m_dragHasBegun && thisPoint == m_startPoint)
+ {
+ #if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "\t\tnop" << endl;
+ #endif
+
+ setUserShapePoints (QPoint (sel->width (), sel->height ()));
+ return;
+ }
+
+
+ if (!sel->pixmap () && !m_currentPullFromDocumentCommand)
+ {
+ m_currentPullFromDocumentCommand = new kpToolSelectionPullFromDocumentCommand (
+ QString::null/*uninteresting child of macro cmd*/,
+ mainWindow ());
+ m_currentPullFromDocumentCommand->execute ();
+ }
+
+ if (!m_currentResizeScaleCommand)
+ {
+ m_currentResizeScaleCommand = new kpToolSelectionResizeScaleCommand (mainWindow ());
+ }
+
+
+ kpSelection originalSelection = m_currentResizeScaleCommand->originalSelection ();
+ const int oldWidth = originalSelection.width ();
+ const int oldHeight = originalSelection.height ();
+
+
+ // Determine new width.
+
+ int userXSign = 0;
+ if (m_resizeScaleType & kpView::Left)
+ userXSign = -1;
+ else if (m_resizeScaleType & kpView::Right)
+ userXSign = +1;
+
+ int newWidth = oldWidth + userXSign * (thisPoint.x () - m_startPoint.x ());
+
+ newWidth = QMAX (originalSelection.minimumWidth (), newWidth);
+
+
+ // Determine new height.
+
+ int userYSign = 0;
+ if (m_resizeScaleType & kpView::Top)
+ userYSign = -1;
+ else if (m_resizeScaleType & kpView::Bottom)
+ userYSign = +1;
+
+ int newHeight = oldHeight + userYSign * (thisPoint.y () - m_startPoint.y ());
+
+ newHeight = QMAX (originalSelection.minimumHeight (), newHeight);
+
+
+ // Keep aspect ratio?
+ if (m_shiftPressed && !sel->isText ())
+ {
+ // Width changed more than height? At equality, favour width.
+ // Fix width, change height.
+ if ((userXSign ? double (newWidth) / oldWidth : 0) >=
+ (userYSign ? double (newHeight) / oldHeight : 0))
+ {
+ newHeight = newWidth * oldHeight / oldWidth;
+ newHeight = QMAX (originalSelection.minimumHeight (),
+ newHeight);
+ }
+ // Height changed more than width?
+ // Fix height, change width.
+ else
+ {
+ newWidth = newHeight * oldWidth / oldHeight;
+ newWidth = QMAX (originalSelection.minimumWidth (), newWidth);
+ }
+ }
+
+
+ // Adjust x/y to new width/height for left/top resizes.
+
+ int newX = originalSelection.x ();
+ int newY = originalSelection.y ();
+
+ if (m_resizeScaleType & kpView::Left)
+ {
+ newX -= (newWidth - originalSelection.width ());
+ }
+
+ if (m_resizeScaleType & kpView::Top)
+ {
+ newY -= (newHeight - originalSelection.height ());
+ }
+
+ #if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "\t\tnewX=" << newX
+ << " newY=" << newY
+ << " newWidth=" << newWidth
+ << " newHeight=" << newHeight
+ << endl;
+ #endif
+
+
+ viewManager ()->setFastUpdates ();
+ m_currentResizeScaleCommand->resizeAndMoveTo (newWidth, newHeight,
+ QPoint (newX, newY),
+ true/*smooth scale delayed*/);
+ viewManager ()->restoreFastUpdates ();
+
+ setUserShapePoints (QPoint (originalSelection.width (),
+ originalSelection.height ()),
+ QPoint (newWidth,
+ newHeight),
+ false/*don't set size*/);
+ setUserShapeSize (newWidth - originalSelection.width (),
+ newHeight - originalSelection.height ());
+ }
+
+
+ m_dragHasBegun = nextDragHasBegun;
+}
+
+// virtual
+void kpToolSelection::cancelShape ()
+{
+#if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "kpToolSelection::cancelShape() mouseButton=" << m_mouseButton << endl;
+#endif
+
+ m_createNOPTimer->stop ();
+ m_RMBMoveUpdateGUITimer->stop ();
+
+
+ viewManager ()->setQueueUpdates ();
+ {
+ if (m_dragType == Move)
+ {
+ #if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "\twas drag moving - undo drag and undo acquire" << endl;
+ #endif
+
+ if (m_currentMoveCommand)
+ {
+ #if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "\t\tundo currentMoveCommand" << endl;
+ #endif
+ m_currentMoveCommand->finalize ();
+ m_currentMoveCommand->unexecute ();
+ delete m_currentMoveCommand;
+ m_currentMoveCommand = 0;
+
+ if (document ()->selection ()->isText ())
+ viewManager ()->setTextCursorBlinkState (true);
+ }
+ }
+ else if (m_dragType == Create)
+ {
+ #if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "\twas creating sel - kill" << endl;
+ #endif
+
+ // TODO: should we give the user back the selection s/he had before (if any)?
+ document ()->selectionDelete ();
+
+ if (m_currentCreateTextCommand)
+ {
+ delete m_currentCreateTextCommand;
+ m_currentCreateTextCommand = 0;
+ }
+ }
+ else if (m_dragType == ResizeScale)
+ {
+ #if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "\twas resize/scale sel - kill" << endl;
+ #endif
+
+ if (m_currentResizeScaleCommand)
+ {
+ #if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "\t\tundo currentResizeScaleCommand" << endl;
+ #endif
+ m_currentResizeScaleCommand->finalize (); // (unneeded but let's be safe)
+ m_currentResizeScaleCommand->unexecute ();
+ delete m_currentResizeScaleCommand;
+ m_currentResizeScaleCommand = 0;
+
+ if (document ()->selection ()->isText ())
+ viewManager ()->setTextCursorBlinkState (true);
+ }
+ }
+
+
+ if (m_currentPullFromDocumentCommand)
+ {
+ #if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "\t\tundo pullFromDocumentCommand" << endl;
+ #endif
+ m_currentPullFromDocumentCommand->unexecute ();
+ delete m_currentPullFromDocumentCommand;
+ m_currentPullFromDocumentCommand = 0;
+ }
+
+
+ viewManager ()->setSelectionBorderVisible (true);
+ viewManager ()->setSelectionBorderFinished (true);
+ viewManager ()->setTextCursorEnabled (m_mode == Text && true);
+ }
+ viewManager ()->restoreQueueUpdates ();
+
+
+ m_dragType = Unknown;
+ m_cancelledShapeButStillHoldingButtons = true;
+ setUserMessage (i18n ("Let go of all the mouse buttons."));
+}
+
+// virtual
+void kpToolSelection::releasedAllButtons ()
+{
+ m_cancelledShapeButStillHoldingButtons = false;
+ setUserMessage (haventBegunDrawUserMessage ());
+}
+
+// virtual
+void kpToolSelection::endDraw (const QPoint & /*thisPoint*/, const QRect & /*normalizedRect*/)
+{
+ m_createNOPTimer->stop ();
+ m_RMBMoveUpdateGUITimer->stop ();
+
+
+ viewManager ()->setQueueUpdates ();
+ {
+ if (m_currentCreateTextCommand)
+ {
+ commandHistory ()->addCommand (m_currentCreateTextCommand, false/*no exec*/);
+ m_currentCreateTextCommand = 0;
+ }
+
+ kpMacroCommand *cmd = 0;
+ if (m_currentMoveCommand)
+ {
+ if (m_currentMoveCommandIsSmear)
+ {
+ cmd = new kpMacroCommand (i18n ("%1: Smear")
+ .arg (document ()->selection ()->name ()),
+ mainWindow ());
+ }
+ else
+ {
+ cmd = new kpMacroCommand ((document ()->selection ()->isText () ?
+ i18n ("Text: Move Box") :
+ i18n ("Selection: Move")),
+ mainWindow ());
+ }
+
+ if (document ()->selection ()->isText ())
+ viewManager ()->setTextCursorBlinkState (true);
+ }
+ else if (m_currentResizeScaleCommand)
+ {
+ cmd = new kpMacroCommand (m_currentResizeScaleCommand->kpNamedCommand::name (),
+ mainWindow ());
+
+ if (document ()->selection ()->isText ())
+ viewManager ()->setTextCursorBlinkState (true);
+ }
+
+ if (m_currentPullFromDocumentCommand)
+ {
+ if (!m_currentMoveCommand && !m_currentResizeScaleCommand)
+ {
+ kdError () << "kpToolSelection::endDraw() pull without move nor resize/scale" << endl;
+ delete m_currentPullFromDocumentCommand;
+ m_currentPullFromDocumentCommand = 0;
+ }
+ else
+ {
+ kpSelection selection;
+
+ if (m_currentMoveCommand)
+ selection = m_currentMoveCommand->originalSelection ();
+ else if (m_currentResizeScaleCommand)
+ selection = m_currentResizeScaleCommand->originalSelection ();
+
+ // just the border
+ selection.setPixmap (QPixmap ());
+
+ kpCommand *createCommand = new kpToolSelectionCreateCommand (
+ i18n ("Selection: Create"),
+ selection,
+ mainWindow ());
+
+ if (kpToolSelectionCreateCommand::nextUndoCommandIsCreateBorder (commandHistory ()))
+ commandHistory ()->setNextUndoCommand (createCommand);
+ else
+ commandHistory ()->addCommand (createCommand,
+ false/*no exec - user already dragged out sel*/);
+
+
+ cmd->addCommand (m_currentPullFromDocumentCommand);
+ m_currentPullFromDocumentCommand = 0;
+ }
+ }
+
+ if (m_currentMoveCommand)
+ {
+ m_currentMoveCommand->finalize ();
+ cmd->addCommand (m_currentMoveCommand);
+ m_currentMoveCommand = 0;
+
+ if (document ()->selection ()->isText ())
+ viewManager ()->setTextCursorBlinkState (true);
+ }
+
+ if (m_currentResizeScaleCommand)
+ {
+ m_currentResizeScaleCommand->finalize ();
+ cmd->addCommand (m_currentResizeScaleCommand);
+ m_currentResizeScaleCommand = 0;
+
+ if (document ()->selection ()->isText ())
+ viewManager ()->setTextCursorBlinkState (true);
+ }
+
+ if (cmd)
+ commandHistory ()->addCommand (cmd, false/*no exec*/);
+
+ viewManager ()->setSelectionBorderVisible (true);
+ viewManager ()->setSelectionBorderFinished (true);
+ viewManager ()->setTextCursorEnabled (m_mode == Text && true);
+ }
+ viewManager ()->restoreQueueUpdates ();
+
+
+ m_dragType = Unknown;
+ setUserMessage (haventBegunDrawUserMessage ());
+
+
+ if (m_mouseButton == 1/*right*/)
+ popupRMBMenu ();
+}
+
+
+// protected virtual [base kpTool]
+void kpToolSelection::keyPressEvent (QKeyEvent *e)
+{
+#if DEBUG_KP_TOOL_SELECTION && 0
+ kdDebug () << "kpToolSelection::keyPressEvent(e->text='" << e->text () << "')" << endl;
+#endif
+
+
+ e->ignore ();
+
+
+ if (document ()->selection () &&
+ !hasBegunDraw () &&
+ e->key () == Qt::Key_Escape)
+ {
+ #if DEBUG_KP_TOOL_SELECTION && 0
+ kdDebug () << "\tescape pressed with sel when not begun draw - deselecting" << endl;
+ #endif
+
+ pushOntoDocument ();
+ e->accept ();
+ }
+
+
+ if (!e->isAccepted ())
+ {
+ #if DEBUG_KP_TOOL_SELECTION && 0
+ kdDebug () << "\tkey processing did not accept (text was '"
+ << e->text ()
+ << "') - passing on event to kpTool"
+ << endl;
+ #endif
+
+ kpTool::keyPressEvent (e);
+ return;
+ }
+}
+
+
+// private slot
+void kpToolSelection::selectionTransparencyChanged (const QString & /*name*/)
+{
+#if 0
+#if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "kpToolSelection::selectionTransparencyChanged(" << name << ")" << endl;
+#endif
+
+ if (mainWindow ()->settingSelectionTransparency ())
+ {
+ #if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "\trecursion - abort setting selection transparency: "
+ << mainWindow ()->settingSelectionTransparency () << endl;
+ #endif
+ return;
+ }
+
+ if (document ()->selection ())
+ {
+ #if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "\thave sel - set transparency" << endl;
+ #endif
+
+ kpSelectionTransparency oldST = document ()->selection ()->transparency ();
+ kpSelectionTransparency st = mainWindow ()->selectionTransparency ();
+
+ // TODO: This "NOP" check causes us a great deal of trouble e.g.:
+ //
+ // Select a solid red rectangle.
+ // Switch to transparent and set red as the background colour.
+ // (the selection is now invisible)
+ // Invert Colours.
+ // (the selection is now cyan)
+ // Change the background colour to green.
+ // (no command is added to undo this as the selection does not change)
+ // Undo.
+ // The rectangle is no longer invisible.
+ //
+ //if (document ()->selection ()->setTransparency (st, true/*check harder for no change in mask*/))
+
+ document ()->selection ()->setTransparency (st);
+ if (true)
+ {
+ #if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "\t\twhich changed the pixmap" << endl;
+ #endif
+
+ commandHistory ()->addCommand (new kpToolSelectionTransparencyCommand (
+ i18n ("Selection: Transparency"), // name,
+ st, oldST,
+ mainWindow ()),
+ false/* no exec*/);
+ }
+ }
+#endif
+
+ // TODO: I've duplicated the code (see below 3x) to make sure
+ // kpSelectionTransparency(oldST)::transparentColor() is defined
+ // and not taken from kpDocument (where it may not be defined because
+ // the transparency may be opaque).
+ //
+ // That way kpToolSelectionTransparencyCommand can force set colours.
+}
+
+
+// protected slot virtual
+void kpToolSelection::slotIsOpaqueChanged ()
+{
+#if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "kpToolSelection::slotIsOpaqueChanged()" << endl;
+#endif
+
+ if (mainWindow ()->settingSelectionTransparency ())
+ {
+ #if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "\trecursion - abort setting selection transparency: "
+ << mainWindow ()->settingSelectionTransparency () << endl;
+ #endif
+ return;
+ }
+
+ if (document ()->selection ())
+ {
+ #if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "\thave sel - set transparency" << endl;
+ #endif
+
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+ if (hasBegunShape ())
+ endShapeInternal ();
+
+ kpSelectionTransparency st = mainWindow ()->selectionTransparency ();
+ kpSelectionTransparency oldST = st;
+ oldST.setOpaque (!oldST.isOpaque ());
+
+ document ()->selection ()->setTransparency (st);
+ commandHistory ()->addCommand (new kpToolSelectionTransparencyCommand (
+ st.isOpaque () ?
+ i18n ("Selection: Opaque") :
+ i18n ("Selection: Transparent"),
+ st, oldST,
+ mainWindow ()),
+ false/* no exec*/);
+
+ QApplication::restoreOverrideCursor ();
+ }
+}
+
+// protected slot virtual [base kpTool]
+void kpToolSelection::slotBackgroundColorChanged (const kpColor &)
+{
+#if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "kpToolSelection::slotBackgroundColorChanged()" << endl;
+#endif
+
+ if (mainWindow ()->settingSelectionTransparency ())
+ {
+ #if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "\trecursion - abort setting selection transparency: "
+ << mainWindow ()->settingSelectionTransparency () << endl;
+ #endif
+ return;
+ }
+
+ if (document ()->selection ())
+ {
+ #if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "\thave sel - set transparency" << endl;
+ #endif
+
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+ kpSelectionTransparency st = mainWindow ()->selectionTransparency ();
+ kpSelectionTransparency oldST = st;
+ oldST.setTransparentColor (oldBackgroundColor ());
+
+ document ()->selection ()->setTransparency (st);
+ commandHistory ()->addCommand (new kpToolSelectionTransparencyCommand (
+ i18n ("Selection: Transparency Color"),
+ st, oldST,
+ mainWindow ()),
+ false/* no exec*/);
+
+ QApplication::restoreOverrideCursor ();
+ }
+}
+
+// protected slot virtual [base kpTool]
+void kpToolSelection::slotColorSimilarityChanged (double, int)
+{
+#if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "kpToolSelection::slotColorSimilarityChanged()" << endl;
+#endif
+
+ if (mainWindow ()->settingSelectionTransparency ())
+ {
+ #if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "\trecursion - abort setting selection transparency: "
+ << mainWindow ()->settingSelectionTransparency () << endl;
+ #endif
+ return;
+ }
+
+ if (document ()->selection ())
+ {
+ #if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "\thave sel - set transparency" << endl;
+ #endif
+
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+ kpSelectionTransparency st = mainWindow ()->selectionTransparency ();
+ kpSelectionTransparency oldST = st;
+ oldST.setColorSimilarity (oldColorSimilarity ());
+
+ document ()->selection ()->setTransparency (st);
+ commandHistory ()->addCommand (new kpToolSelectionTransparencyCommand (
+ i18n ("Selection: Transparency Color Similarity"),
+ st, oldST,
+ mainWindow ()),
+ false/* no exec*/);
+
+ QApplication::restoreOverrideCursor ();
+ }
+}
+
+
+/*
+ * kpToolSelectionCreateCommand
+ */
+
+kpToolSelectionCreateCommand::kpToolSelectionCreateCommand (const QString &name,
+ const kpSelection &fromSelection,
+ kpMainWindow *mainWindow)
+ : kpNamedCommand (name, mainWindow),
+ m_fromSelection (0),
+ m_textRow (0), m_textCol (0)
+{
+ setFromSelection (fromSelection);
+}
+
+kpToolSelectionCreateCommand::~kpToolSelectionCreateCommand ()
+{
+ delete m_fromSelection;
+}
+
+
+// public virtual [base kpCommand]
+int kpToolSelectionCreateCommand::size () const
+{
+ return kpPixmapFX::selectionSize (m_fromSelection);
+}
+
+
+// public static
+bool kpToolSelectionCreateCommand::nextUndoCommandIsCreateBorder (
+ kpCommandHistory *commandHistory)
+{
+ if (!commandHistory)
+ return false;
+
+ kpCommand *cmd = commandHistory->nextUndoCommand ();
+ if (!cmd)
+ return false;
+
+ kpToolSelectionCreateCommand *c = dynamic_cast <kpToolSelectionCreateCommand *> (cmd);
+ if (!c)
+ return false;
+
+ const kpSelection *sel = c->fromSelection ();
+ if (!sel)
+ return false;
+
+ return (!sel->pixmap ());
+}
+
+
+// public
+const kpSelection *kpToolSelectionCreateCommand::fromSelection () const
+{
+ return m_fromSelection;
+}
+
+// public
+void kpToolSelectionCreateCommand::setFromSelection (const kpSelection &fromSelection)
+{
+ delete m_fromSelection;
+ m_fromSelection = new kpSelection (fromSelection);
+}
+
+// public virtual [base kpCommand]
+void kpToolSelectionCreateCommand::execute ()
+{
+#if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "kpToolSelectionCreateCommand::execute()" << endl;
+#endif
+
+ kpDocument *doc = document ();
+ if (!doc)
+ {
+ kdError () << "kpToolSelectionCreateCommand::execute() without doc" << endl;
+ return;
+ }
+
+ if (m_fromSelection)
+ {
+ #if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "\tusing fromSelection" << endl;
+ kdDebug () << "\t\thave sel=" << doc->selection ()
+ << " pixmap=" << (doc->selection () ? doc->selection ()->pixmap () : 0)
+ << endl;
+ #endif
+ if (!m_fromSelection->isText ())
+ {
+ if (m_fromSelection->transparency () != m_mainWindow->selectionTransparency ())
+ m_mainWindow->setSelectionTransparency (m_fromSelection->transparency ());
+ }
+ else
+ {
+ if (m_fromSelection->textStyle () != m_mainWindow->textStyle ())
+ m_mainWindow->setTextStyle (m_fromSelection->textStyle ());
+ }
+
+ m_mainWindow->viewManager ()->setTextCursorPosition (m_textRow, m_textCol);
+ doc->setSelection (*m_fromSelection);
+
+ if (m_mainWindow->tool ())
+ m_mainWindow->tool ()->somethingBelowTheCursorChanged ();
+ }
+}
+
+// public virtual [base kpCommand]
+void kpToolSelectionCreateCommand::unexecute ()
+{
+ kpDocument *doc = document ();
+ if (!doc)
+ {
+ kdError () << "kpToolSelectionCreateCommand::unexecute() without doc" << endl;
+ return;
+ }
+
+ if (!doc->selection ())
+ {
+ // Was just a border that got deselected?
+ if (m_fromSelection && !m_fromSelection->pixmap ())
+ return;
+
+ kdError () << "kpToolSelectionCreateCommand::unexecute() without sel region" << endl;
+ return;
+ }
+
+ m_textRow = m_mainWindow->viewManager ()->textCursorRow ();
+ m_textCol = m_mainWindow->viewManager ()->textCursorCol ();
+
+ doc->selectionDelete ();
+
+ if (m_mainWindow->tool ())
+ m_mainWindow->tool ()->somethingBelowTheCursorChanged ();
+}
+
+
+/*
+ * kpToolSelectionPullFromDocumentCommand
+ */
+
+kpToolSelectionPullFromDocumentCommand::kpToolSelectionPullFromDocumentCommand (const QString &name,
+ kpMainWindow *mainWindow)
+ : kpNamedCommand (name, mainWindow),
+ m_backgroundColor (mainWindow ? mainWindow->backgroundColor () : kpColor::invalid),
+ m_originalSelectionRegion (0)
+{
+#if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "kpToolSelectionPullFromDocumentCommand::<ctor>() mainWindow="
+ << m_mainWindow
+ << endl;
+#endif
+}
+
+kpToolSelectionPullFromDocumentCommand::~kpToolSelectionPullFromDocumentCommand ()
+{
+ delete m_originalSelectionRegion;
+}
+
+
+// public virtual [base kpCommand]
+int kpToolSelectionPullFromDocumentCommand::size () const
+{
+ return kpPixmapFX::selectionSize (m_originalSelectionRegion);
+}
+
+
+// public virtual [base kpCommand]
+void kpToolSelectionPullFromDocumentCommand::execute ()
+{
+#if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "kpToolSelectionPullFromDocumentCommand::execute()" << endl;
+#endif
+
+ kpDocument *doc = document ();
+
+ if (!doc)
+ {
+ kdError () << "kpToolSelectionPullFromDocumentCommand::execute() without doc" << endl;
+ return;
+ }
+
+ kpViewManager *vm = m_mainWindow ? m_mainWindow->viewManager () : 0;
+ if (vm)
+ vm->setQueueUpdates ();
+
+ // In case the user CTRL+Z'ed, selected a random region to throw us off
+ // and then CTRL+Shift+Z'ed putting us here. Make sure we pull from the
+ // originally requested region - not the random one.
+ if (m_originalSelectionRegion)
+ {
+ if (m_originalSelectionRegion->transparency () != m_mainWindow->selectionTransparency ())
+ m_mainWindow->setSelectionTransparency (m_originalSelectionRegion->transparency ());
+
+ doc->setSelection (*m_originalSelectionRegion);
+ }
+ else
+ {
+ // must have selection region but not pixmap
+ if (!doc->selection () || doc->selection ()->pixmap ())
+ {
+ kdError () << "kpToolSelectionPullFromDocumentCommand::execute() sel="
+ << doc->selection ()
+ << " pixmap="
+ << (doc->selection () ? doc->selection ()->pixmap () : 0)
+ << endl;
+ if (vm)
+ vm->restoreQueueUpdates ();
+ return;
+ }
+ }
+
+ doc->selectionPullFromDocument (m_backgroundColor);
+
+ if (vm)
+ vm->restoreQueueUpdates ();
+}
+
+// public virtual [base kpCommand]
+void kpToolSelectionPullFromDocumentCommand::unexecute ()
+{
+#if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "kpToolSelectionPullFromDocumentCommand::unexecute()" << endl;
+#endif
+
+ kpDocument *doc = document ();
+
+ if (!doc)
+ {
+ kdError () << "kpToolSelectionPullFromDocumentCommand::unexecute() without doc" << endl;
+ return;
+ }
+
+ // must have selection pixmap
+ if (!doc->selection () || !doc->selection ()->pixmap ())
+ {
+ kdError () << "kpToolSelectionPullFromDocumentCommand::unexecute() sel="
+ << doc->selection ()
+ << " pixmap="
+ << (doc->selection () ? doc->selection ()->pixmap () : 0)
+ << endl;
+ return;
+ }
+
+
+ // We can have faith that this is the state of the selection after
+ // execute(), rather than after the user tried to throw us off by
+ // simply selecting another region as to do that, a destroy command
+ // must have been used.
+ doc->selectionCopyOntoDocument (false/*use opaque pixmap*/);
+ doc->selection ()->setPixmap (QPixmap ());
+
+ delete m_originalSelectionRegion;
+ m_originalSelectionRegion = new kpSelection (*doc->selection ());
+}
+
+
+/*
+ * kpToolSelectionTransparencyCommand
+ */
+
+kpToolSelectionTransparencyCommand::kpToolSelectionTransparencyCommand (const QString &name,
+ const kpSelectionTransparency &st,
+ const kpSelectionTransparency &oldST,
+ kpMainWindow *mainWindow)
+ : kpNamedCommand (name, mainWindow),
+ m_st (st),
+ m_oldST (oldST)
+{
+}
+
+kpToolSelectionTransparencyCommand::~kpToolSelectionTransparencyCommand ()
+{
+}
+
+
+// public virtual [base kpCommand]
+int kpToolSelectionTransparencyCommand::size () const
+{
+ return 0;
+}
+
+
+// public virtual [base kpCommand]
+void kpToolSelectionTransparencyCommand::execute ()
+{
+#if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "kpToolSelectionTransparencyCommand::execute()" << endl;
+#endif
+ kpDocument *doc = document ();
+ if (!doc)
+ return;
+
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+ m_mainWindow->setSelectionTransparency (m_st, true/*force colour change*/);
+
+ if (doc->selection ())
+ doc->selection ()->setTransparency (m_st);
+
+ QApplication::restoreOverrideCursor ();
+}
+
+// public virtual [base kpCommand]
+void kpToolSelectionTransparencyCommand::unexecute ()
+{
+#if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "kpToolSelectionTransparencyCommand::unexecute()" << endl;
+#endif
+
+ kpDocument *doc = document ();
+ if (!doc)
+ return;
+
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+ m_mainWindow->setSelectionTransparency (m_oldST, true/*force colour change*/);
+
+ if (doc->selection ())
+ doc->selection ()->setTransparency (m_oldST);
+
+ QApplication::restoreOverrideCursor ();
+}
+
+
+/*
+ * kpToolSelectionMoveCommand
+ */
+
+kpToolSelectionMoveCommand::kpToolSelectionMoveCommand (const QString &name,
+ kpMainWindow *mainWindow)
+ : kpNamedCommand (name, mainWindow)
+{
+ kpDocument *doc = document ();
+ if (doc && doc->selection ())
+ {
+ m_startPoint = m_endPoint = doc->selection ()->topLeft ();
+ }
+}
+
+kpToolSelectionMoveCommand::~kpToolSelectionMoveCommand ()
+{
+}
+
+
+// public
+kpSelection kpToolSelectionMoveCommand::originalSelection () const
+{
+ kpDocument *doc = document ();
+ if (!doc || !doc->selection ())
+ {
+ kdError () << "kpToolSelectionMoveCommand::originalSelection() doc="
+ << doc
+ << " sel="
+ << (doc ? doc->selection () : 0)
+ << endl;
+ return kpSelection (kpSelection::Rectangle, QRect ());
+ }
+
+ kpSelection selection = *doc->selection();
+ selection.moveTo (m_startPoint);
+
+ return selection;
+}
+
+
+// public virtual [base kpComand]
+int kpToolSelectionMoveCommand::size () const
+{
+ return kpPixmapFX::pixmapSize (m_oldDocumentPixmap) +
+ kpPixmapFX::pointArraySize (m_copyOntoDocumentPoints);
+}
+
+
+// public virtual [base kpCommand]
+void kpToolSelectionMoveCommand::execute ()
+{
+#if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "kpToolSelectionMoveCommand::execute()" << endl;
+#endif
+
+ kpDocument *doc = document ();
+ if (!doc)
+ {
+ kdError () << "kpToolSelectionMoveCommand::execute() no doc" << endl;
+ return;
+ }
+
+ kpSelection *sel = doc->selection ();
+
+ // have to have pulled pixmap by now
+ if (!sel || !sel->pixmap ())
+ {
+ kdError () << "kpToolSelectionMoveCommand::execute() but haven't pulled pixmap yet: "
+ << "sel=" << sel << " sel->pixmap=" << (sel ? sel->pixmap () : 0)
+ << endl;
+ return;
+ }
+
+ kpViewManager *vm = m_mainWindow ? m_mainWindow->viewManager () : 0;
+
+ if (vm)
+ vm->setQueueUpdates ();
+
+ QPointArray::ConstIterator copyOntoDocumentPointsEnd = m_copyOntoDocumentPoints.end ();
+ for (QPointArray::ConstIterator it = m_copyOntoDocumentPoints.begin ();
+ it != copyOntoDocumentPointsEnd;
+ it++)
+ {
+ sel->moveTo (*it);
+ doc->selectionCopyOntoDocument ();
+ }
+
+ sel->moveTo (m_endPoint);
+
+ if (m_mainWindow->tool ())
+ m_mainWindow->tool ()->somethingBelowTheCursorChanged ();
+
+ if (vm)
+ vm->restoreQueueUpdates ();
+}
+
+// public virtual [base kpCommand]
+void kpToolSelectionMoveCommand::unexecute ()
+{
+#if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "kpToolSelectionMoveCommand::unexecute()" << endl;
+#endif
+
+ kpDocument *doc = document ();
+ if (!doc)
+ {
+ kdError () << "kpToolSelectionMoveCommand::unexecute() no doc" << endl;
+ return;
+ }
+
+ kpSelection *sel = doc->selection ();
+
+ // have to have pulled pixmap by now
+ if (!sel || !sel->pixmap ())
+ {
+ kdError () << "kpToolSelectionMoveCommand::unexecute() but haven't pulled pixmap yet: "
+ << "sel=" << sel << " sel->pixmap=" << (sel ? sel->pixmap () : 0)
+ << endl;
+ return;
+ }
+
+ kpViewManager *vm = m_mainWindow ? m_mainWindow->viewManager () : 0;
+
+ if (vm)
+ vm->setQueueUpdates ();
+
+ if (!m_oldDocumentPixmap.isNull ())
+ doc->setPixmapAt (m_oldDocumentPixmap, m_documentBoundingRect.topLeft ());
+#if DEBUG_KP_TOOL_SELECTION && 1
+ kdDebug () << "\tmove to startPoint=" << m_startPoint << endl;
+#endif
+ sel->moveTo (m_startPoint);
+
+ if (m_mainWindow->tool ())
+ m_mainWindow->tool ()->somethingBelowTheCursorChanged ();
+
+ if (vm)
+ vm->restoreQueueUpdates ();
+}
+
+// public
+void kpToolSelectionMoveCommand::moveTo (const QPoint &point, bool moveLater)
+{
+#if DEBUG_KP_TOOL_SELECTION && 0
+ kdDebug () << "kpToolSelectionMoveCommand::moveTo" << point
+ << " moveLater=" << moveLater
+ <<endl;
+#endif
+
+ if (!moveLater)
+ {
+ kpDocument *doc = document ();
+ if (!doc)
+ {
+ kdError () << "kpToolSelectionMoveCommand::moveTo() without doc" << endl;
+ return;
+ }
+
+ kpSelection *sel = doc->selection ();
+
+ // have to have pulled pixmap by now
+ if (!sel)
+ {
+ kdError () << "kpToolSelectionMoveCommand::moveTo() no sel region" << endl;
+ return;
+ }
+
+ if (!sel->pixmap ())
+ {
+ kdError () << "kpToolSelectionMoveCommand::moveTo() no sel pixmap" << endl;
+ return;
+ }
+
+ if (point == sel->topLeft ())
+ return;
+
+ sel->moveTo (point);
+ }
+
+ m_endPoint = point;
+}
+
+// public
+void kpToolSelectionMoveCommand::moveTo (int x, int y, bool moveLater)
+{
+ moveTo (QPoint (x, y), moveLater);
+}
+
+// public
+void kpToolSelectionMoveCommand::copyOntoDocument ()
+{
+#if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "kpToolSelectionMoveCommand::copyOntoDocument()" << endl;
+#endif
+
+ kpDocument *doc = document ();
+ if (!doc)
+ return;
+
+ kpSelection *sel = doc->selection ();
+
+ // have to have pulled pixmap by now
+ if (!sel)
+ {
+ kdError () << "\tkpToolSelectionMoveCommand::copyOntoDocument() without sel region" << endl;
+ return;
+ }
+
+ if (!sel->pixmap ())
+ {
+ kdError () << "kpToolSelectionMoveCommand::moveTo() no sel pixmap" << endl;
+ return;
+ }
+
+ if (m_oldDocumentPixmap.isNull ())
+ m_oldDocumentPixmap = *doc->pixmap ();
+
+ QRect selBoundingRect = sel->boundingRect ();
+ m_documentBoundingRect.unite (selBoundingRect);
+
+ doc->selectionCopyOntoDocument ();
+
+ m_copyOntoDocumentPoints.putPoints (m_copyOntoDocumentPoints.count (),
+ 1,
+ selBoundingRect.x (),
+ selBoundingRect.y ());
+}
+
+// public
+void kpToolSelectionMoveCommand::finalize ()
+{
+ if (!m_oldDocumentPixmap.isNull () && !m_documentBoundingRect.isNull ())
+ {
+ m_oldDocumentPixmap = kpTool::neededPixmap (m_oldDocumentPixmap,
+ m_documentBoundingRect);
+ }
+}
+
+
+/*
+ * kpToolSelectionResizeScaleCommand
+ */
+
+kpToolSelectionResizeScaleCommand::kpToolSelectionResizeScaleCommand (
+ kpMainWindow *mainWindow)
+ : kpNamedCommand (mainWindow->document ()->selection ()->isText () ?
+ i18n ("Text: Resize Box") :
+ i18n ("Selection: Smooth Scale"),
+ mainWindow),
+ m_smoothScaleTimer (new QTimer (this))
+{
+ m_originalSelection = *selection ();
+
+ m_newTopLeft = selection ()->topLeft ();
+ m_newWidth = selection ()->width ();
+ m_newHeight = selection ()->height ();
+
+ connect (m_smoothScaleTimer, SIGNAL (timeout ()),
+ this, SLOT (resizeScaleAndMove ()));
+}
+
+kpToolSelectionResizeScaleCommand::~kpToolSelectionResizeScaleCommand ()
+{
+}
+
+
+// public virtual
+int kpToolSelectionResizeScaleCommand::size () const
+{
+ return m_originalSelection.size ();
+}
+
+
+// public
+kpSelection kpToolSelectionResizeScaleCommand::originalSelection () const
+{
+ return m_originalSelection;
+}
+
+
+// public
+QPoint kpToolSelectionResizeScaleCommand::topLeft () const
+{
+ return m_newTopLeft;
+}
+
+// public
+void kpToolSelectionResizeScaleCommand::moveTo (const QPoint &point)
+{
+ if (point == m_newTopLeft)
+ return;
+
+ m_newTopLeft = point;
+ selection ()->moveTo (m_newTopLeft);
+}
+
+
+// public
+int kpToolSelectionResizeScaleCommand::width () const
+{
+ return m_newWidth;
+}
+
+// public
+int kpToolSelectionResizeScaleCommand::height () const
+{
+ return m_newHeight;
+}
+
+// public
+void kpToolSelectionResizeScaleCommand::resize (int width, int height,
+ bool delayed)
+{
+ if (width == m_newWidth && height == m_newHeight)
+ return;
+
+ m_newWidth = width;
+ m_newHeight = height;
+
+ resizeScaleAndMove (delayed);
+}
+
+
+// public
+void kpToolSelectionResizeScaleCommand::resizeAndMoveTo (int width, int height,
+ const QPoint &point,
+ bool delayed)
+{
+ if (width == m_newWidth && height == m_newHeight &&
+ point == m_newTopLeft)
+ {
+ return;
+ }
+
+ m_newWidth = width;
+ m_newHeight = height;
+ m_newTopLeft = point;
+
+ resizeScaleAndMove (delayed);
+}
+
+
+// protected
+void kpToolSelectionResizeScaleCommand::killSmoothScaleTimer ()
+{
+ m_smoothScaleTimer->stop ();
+}
+
+
+// protected
+void kpToolSelectionResizeScaleCommand::resizeScaleAndMove (bool delayed)
+{
+#if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "kpToolSelectionResizeScaleCommand::resizeScaleAndMove(delayed="
+ << delayed << ")" << endl;
+#endif
+
+ killSmoothScaleTimer ();
+
+ kpSelection newSel;
+
+ if (selection ()->isText ())
+ {
+ newSel = m_originalSelection;
+ newSel.textResize (m_newWidth, m_newHeight);
+ }
+ else
+ {
+ newSel = kpSelection (kpSelection::Rectangle,
+ QRect (m_originalSelection.x (),
+ m_originalSelection.y (),
+ m_newWidth,
+ m_newHeight),
+ kpPixmapFX::scale (*m_originalSelection.pixmap (),
+ m_newWidth, m_newHeight,
+ !delayed/*if not delayed, smooth*/),
+ m_originalSelection.transparency ());
+
+ if (delayed)
+ {
+ // Call self with delayed==false in 200ms
+ m_smoothScaleTimer->start (200/*ms*/, true/*single shot*/);
+ }
+ }
+
+ newSel.moveTo (m_newTopLeft);
+
+ m_mainWindow->document ()->setSelection (newSel);
+}
+
+// protected slots
+void kpToolSelectionResizeScaleCommand::resizeScaleAndMove ()
+{
+#if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "kpToolSelectionResizeScaleCommand::resizeScaleAndMove()" << endl;
+#endif
+ resizeScaleAndMove (false/*no delay*/);
+}
+
+
+// public
+void kpToolSelectionResizeScaleCommand::finalize ()
+{
+#if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "kpToolSelectionResizeScaleCommand::finalize()"
+ << " smoothScaleTimer->isActive="
+ << m_smoothScaleTimer->isActive ()
+ << endl;
+#endif
+
+ // Make sure the selection contains the final image and the timer won't
+ // fire afterwards.
+ if (m_smoothScaleTimer->isActive ())
+ {
+ resizeScaleAndMove ();
+ Q_ASSERT (!m_smoothScaleTimer->isActive ());
+ }
+}
+
+
+// public virtual [base kpToolResizeScaleCommand]
+void kpToolSelectionResizeScaleCommand::execute ()
+{
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+ killSmoothScaleTimer ();
+
+ resizeScaleAndMove ();
+
+ if (m_mainWindow->tool ())
+ m_mainWindow->tool ()->somethingBelowTheCursorChanged ();
+
+ QApplication::restoreOverrideCursor ();
+}
+
+// public virtual [base kpToolResizeScaleCommand]
+void kpToolSelectionResizeScaleCommand::unexecute ()
+{
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+ killSmoothScaleTimer ();
+
+ m_mainWindow->document ()->setSelection (m_originalSelection);
+
+ if (m_mainWindow->tool ())
+ m_mainWindow->tool ()->somethingBelowTheCursorChanged ();
+
+ QApplication::restoreOverrideCursor ();
+}
+
+
+/*
+ * kpToolSelectionDestroyCommand
+ */
+
+kpToolSelectionDestroyCommand::kpToolSelectionDestroyCommand (const QString &name,
+ bool pushOntoDocument,
+ kpMainWindow *mainWindow)
+ : kpNamedCommand (name, mainWindow),
+ m_pushOntoDocument (pushOntoDocument),
+ m_oldSelection (0)
+{
+}
+
+kpToolSelectionDestroyCommand::~kpToolSelectionDestroyCommand ()
+{
+ delete m_oldSelection;
+}
+
+
+// public virtual [base kpCommand]
+int kpToolSelectionDestroyCommand::size () const
+{
+ return kpPixmapFX::pixmapSize (m_oldDocPixmap) +
+ kpPixmapFX::selectionSize (m_oldSelection);
+}
+
+
+// public virtual [base kpCommand]
+void kpToolSelectionDestroyCommand::execute ()
+{
+#if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "kpToolSelectionDestroyCommand::execute () CALLED" << endl;
+#endif
+
+ kpDocument *doc = document ();
+ if (!doc)
+ {
+ kdError () << "kpToolSelectionDestroyCommand::execute() without doc" << endl;
+ return;
+ }
+
+ if (!doc->selection ())
+ {
+ kdError () << "kpToolSelectionDestroyCommand::execute() without sel region" << endl;
+ return;
+ }
+
+ m_textRow = m_mainWindow->viewManager ()->textCursorRow ();
+ m_textCol = m_mainWindow->viewManager ()->textCursorCol ();
+
+ m_oldSelection = new kpSelection (*doc->selection ());
+ if (m_pushOntoDocument)
+ {
+ m_oldDocPixmap = doc->getPixmapAt (doc->selection ()->boundingRect ());
+ doc->selectionPushOntoDocument ();
+ }
+ else
+ doc->selectionDelete ();
+
+ if (m_mainWindow->tool ())
+ m_mainWindow->tool ()->somethingBelowTheCursorChanged ();
+}
+
+// public virtual [base kpCommand]
+void kpToolSelectionDestroyCommand::unexecute ()
+{
+#if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "kpToolSelectionDestroyCommand::unexecute () CALLED" << endl;
+#endif
+
+ kpDocument *doc = document ();
+ if (!doc)
+ {
+ kdError () << "kpToolSelectionDestroyCommand::unexecute() without doc" << endl;
+ return;
+ }
+
+ if (doc->selection ())
+ {
+ // not error because it's possible that the user dragged out a new
+ // region (without pulling pixmap), and then CTRL+Z
+ #if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "kpToolSelectionDestroyCommand::unexecute() already has sel region" << endl;
+ #endif
+
+ if (doc->selection ()->pixmap ())
+ {
+ kdError () << "kpToolSelectionDestroyCommand::unexecute() already has sel pixmap" << endl;
+ return;
+ }
+ }
+
+ if (!m_oldSelection)
+ {
+ kdError () << "kpToolSelectionDestroyCommand::unexecute() without old sel" << endl;
+ return;
+ }
+
+ if (m_pushOntoDocument)
+ {
+ #if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "\tunpush oldDocPixmap onto doc first" << endl;
+ #endif
+ doc->setPixmapAt (m_oldDocPixmap, m_oldSelection->topLeft ());
+ }
+
+#if DEBUG_KP_TOOL_SELECTION
+ kdDebug () << "\tsetting selection to: rect=" << m_oldSelection->boundingRect ()
+ << " pixmap=" << m_oldSelection->pixmap ()
+ << " pixmap.isNull()=" << (m_oldSelection->pixmap ()
+ ?
+ m_oldSelection->pixmap ()->isNull ()
+ :
+ true)
+ << endl;
+#endif
+ if (!m_oldSelection->isText ())
+ {
+ if (m_oldSelection->transparency () != m_mainWindow->selectionTransparency ())
+ m_mainWindow->setSelectionTransparency (m_oldSelection->transparency ());
+ }
+ else
+ {
+ if (m_oldSelection->textStyle () != m_mainWindow->textStyle ())
+ m_mainWindow->setTextStyle (m_oldSelection->textStyle ());
+ }
+
+ m_mainWindow->viewManager ()->setTextCursorPosition (m_textRow, m_textCol);
+ doc->setSelection (*m_oldSelection);
+
+ if (m_mainWindow->tool ())
+ m_mainWindow->tool ()->somethingBelowTheCursorChanged ();
+
+ delete m_oldSelection;
+ m_oldSelection = 0;
+}
+
+#include <kptoolselection.moc>
diff --git a/kolourpaint/tools/kptoolselection.h b/kolourpaint/tools/kptoolselection.h
new file mode 100644
index 00000000..ee978a15
--- /dev/null
+++ b/kolourpaint/tools/kptoolselection.h
@@ -0,0 +1,313 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kp_tool_selection_h__
+#define __kp_tool_selection_h__
+
+
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qpointarray.h>
+#include <qrect.h>
+
+#include <kpcolor.h>
+#include <kpcommandhistory.h>
+#include <kpselection.h>
+#include <kpselectiontransparency.h>
+#include <kptool.h>
+
+
+class QPoint;
+class QRect;
+class QTimer;
+
+class kpMainWindow;
+class kpSelection;
+
+class kpToolSelectionCreateCommand;
+class kpToolSelectionMoveCommand;
+class kpToolSelectionPullFromDocumentCommand;
+class kpToolSelectionResizeScaleCommand;
+class kpToolWidgetOpaqueOrTransparent;
+
+
+class kpToolSelection : public kpTool
+{
+Q_OBJECT
+
+public:
+ enum Mode {Rectangle, Ellipse, FreeForm, Text};
+
+ kpToolSelection (Mode mode,
+ const QString &text, const QString &description,
+ int key,
+ kpMainWindow *mainWindow, const char *name);
+ virtual ~kpToolSelection ();
+
+ void setMode (Mode mode) { m_mode = mode; }
+
+private:
+ void pushOntoDocument ();
+
+protected:
+ bool onSelectionToMove () const;
+ int onSelectionResizeHandle () const;
+ bool onSelectionToSelectText () const;
+
+public:
+ QString haventBegunDrawUserMessage () const;
+
+ virtual void begin ();
+ virtual void end ();
+ virtual void reselect ();
+
+ virtual bool careAboutModifierState () const { return true; }
+ bool controlOrShiftPressed () const { return (m_controlPressed || m_shiftPressed); }
+
+ virtual void beginDraw ();
+protected:
+ const QCursor &cursor () const;
+public:
+ virtual void hover (const QPoint &point);
+protected:
+ void popupRMBMenu ();
+ void setSelectionBorderForMove ();
+protected slots:
+ void slotRMBMoveUpdateGUI ();
+ void delayedDraw ();
+public:
+ virtual void draw (const QPoint &thisPoint, const QPoint &lastPoint,
+ const QRect &normalizedRect);
+ virtual void cancelShape ();
+ virtual void releasedAllButtons ();
+ virtual void endDraw (const QPoint &thisPoint, const QRect &normalizedRect);
+
+protected:
+ virtual void keyPressEvent (QKeyEvent *e);
+
+protected:
+ void selectionTransparencyChanged (const QString &name);
+
+protected slots:
+ virtual void slotIsOpaqueChanged ();
+ virtual void slotBackgroundColorChanged (const kpColor &color);
+ virtual void slotColorSimilarityChanged (double similarity, int);
+
+protected:
+ Mode m_mode;
+
+ QPoint m_startDragFromSelectionTopLeft;
+ enum DragType
+ {
+ Unknown, Create, Move, SelectText, ResizeScale
+ };
+ DragType m_dragType;
+ bool m_dragHasBegun;
+ bool m_hadSelectionBeforeDrag;
+ int m_resizeScaleType;
+
+ kpToolSelectionPullFromDocumentCommand *m_currentPullFromDocumentCommand;
+ kpToolSelectionMoveCommand *m_currentMoveCommand;
+ bool m_currentMoveCommandIsSmear;
+ kpToolSelectionResizeScaleCommand *m_currentResizeScaleCommand;
+ kpToolWidgetOpaqueOrTransparent *m_toolWidgetOpaqueOrTransparent;
+
+ kpToolSelectionCreateCommand *m_currentCreateTextCommand;
+ bool m_cancelledShapeButStillHoldingButtons;
+
+ QTimer *m_createNOPTimer, *m_RMBMoveUpdateGUITimer;
+};
+
+class kpToolSelectionCreateCommand : public kpNamedCommand
+{
+public:
+ // (if fromSelection doesn't have a pixmap, it will only recreate the region)
+ kpToolSelectionCreateCommand (const QString &name, const kpSelection &fromSelection,
+ kpMainWindow *mainWindow);
+ virtual ~kpToolSelectionCreateCommand ();
+
+ virtual int size () const;
+
+ static bool nextUndoCommandIsCreateBorder (kpCommandHistory *commandHistory);
+
+ const kpSelection *fromSelection () const;
+ void setFromSelection (const kpSelection &fromSelection);
+
+ virtual void execute ();
+ virtual void unexecute ();
+
+private:
+ kpSelection *m_fromSelection;
+
+ int m_textRow, m_textCol;
+};
+
+class kpToolSelectionPullFromDocumentCommand : public kpNamedCommand
+{
+public:
+ kpToolSelectionPullFromDocumentCommand (const QString &name, kpMainWindow *mainWindow);
+ virtual ~kpToolSelectionPullFromDocumentCommand ();
+
+ virtual int size () const;
+
+ virtual void execute ();
+ virtual void unexecute ();
+
+private:
+ kpColor m_backgroundColor;
+ kpSelection *m_originalSelectionRegion;
+};
+
+class kpToolSelectionTransparencyCommand : public kpNamedCommand
+{
+public:
+ kpToolSelectionTransparencyCommand (const QString &name,
+ const kpSelectionTransparency &st,
+ const kpSelectionTransparency &oldST,
+ kpMainWindow *mainWindow);
+ virtual ~kpToolSelectionTransparencyCommand ();
+
+ virtual int size () const;
+
+ virtual void execute ();
+ virtual void unexecute ();
+
+private:
+ kpSelectionTransparency m_st, m_oldST;
+};
+
+class kpToolSelectionMoveCommand : public kpNamedCommand
+{
+public:
+ kpToolSelectionMoveCommand (const QString &name, kpMainWindow *mainWindow);
+ virtual ~kpToolSelectionMoveCommand ();
+
+ kpSelection originalSelection () const;
+
+ virtual int size () const;
+
+ virtual void execute ();
+ virtual void unexecute ();
+
+ void moveTo (const QPoint &point, bool moveLater = false);
+ void moveTo (int x, int y, bool moveLater = false);
+ void copyOntoDocument ();
+ void finalize ();
+
+private:
+ QPoint m_startPoint, m_endPoint;
+
+ QPixmap m_oldDocumentPixmap;
+
+ // area of document affected (not the bounding rect of the sel)
+ QRect m_documentBoundingRect;
+
+ QPointArray m_copyOntoDocumentPoints;
+};
+
+// You could subclass kpToolResizeScaleCommand and/or
+// kpToolSelectionMoveCommand instead if want a disaster.
+// This is different to kpToolResizeScaleCommand in that:
+//
+// 1. This only works for selections.
+// 2. This is designed for the size and position to change several times
+// before execute().
+//
+class kpToolSelectionResizeScaleCommand : public QObject,
+ public kpNamedCommand
+{
+Q_OBJECT
+
+public:
+ kpToolSelectionResizeScaleCommand (kpMainWindow *mainWindow);
+ virtual ~kpToolSelectionResizeScaleCommand ();
+
+ virtual int size () const;
+
+public:
+ kpSelection originalSelection () const;
+
+ QPoint topLeft () const;
+ void moveTo (const QPoint &point);
+
+ int width () const;
+ int height () const;
+ void resize (int width, int height, bool delayed = false);
+
+ // (equivalent to resize() followed by moveTo() but faster)
+ void resizeAndMoveTo (int width, int height, const QPoint &point,
+ bool delayed = false);
+
+protected:
+ void killSmoothScaleTimer ();
+
+ // If <delayed>, does a fast, low-quality scale and then calls itself
+ // with <delayed> unset for a smooth scale, a short time later.
+ // If acting on a text box, <delayed> is ignored.
+ void resizeScaleAndMove (bool delayed);
+
+protected slots:
+ void resizeScaleAndMove (/*delayed = false*/);
+
+public:
+ void finalize ();
+
+public:
+ virtual void execute ();
+ virtual void unexecute ();
+
+protected:
+ kpSelection m_originalSelection;
+
+ QPoint m_newTopLeft;
+ int m_newWidth, m_newHeight;
+
+ QTimer *m_smoothScaleTimer;
+};
+
+class kpToolSelectionDestroyCommand : public kpNamedCommand
+{
+public:
+ kpToolSelectionDestroyCommand (const QString &name, bool pushOntoDocument,
+ kpMainWindow *mainWindow);
+ virtual ~kpToolSelectionDestroyCommand ();
+
+ virtual int size () const;
+
+ virtual void execute ();
+ virtual void unexecute ();
+
+private:
+ bool m_pushOntoDocument;
+ QPixmap m_oldDocPixmap;
+ kpSelection *m_oldSelection;
+
+ int m_textRow, m_textCol;
+};
+
+#endif // __kp_tool_selection_h__
diff --git a/kolourpaint/tools/kptoolskew.cpp b/kolourpaint/tools/kptoolskew.cpp
new file mode 100644
index 00000000..f1e446be
--- /dev/null
+++ b/kolourpaint/tools/kptoolskew.cpp
@@ -0,0 +1,449 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG_KP_TOOL_SKEW 0
+#define DEBUG_KP_TOOL_SKEW_DIALOG 0
+
+
+#include <kptoolskew.h>
+
+#include <qapplication.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qwmatrix.h>
+
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <knuminput.h>
+
+#include <kpdefs.h>
+#include <kpdocument.h>
+#include <kpmainwindow.h>
+#include <kppixmapfx.h>
+#include <kpselection.h>
+#include <kptool.h>
+
+
+/*
+ * kpToolSkewCommand
+ */
+
+kpToolSkewCommand::kpToolSkewCommand (bool actOnSelection,
+ int hangle, int vangle,
+ kpMainWindow *mainWindow)
+ : kpCommand (mainWindow),
+ m_actOnSelection (actOnSelection),
+ m_hangle (hangle), m_vangle (vangle),
+ m_backgroundColor (mainWindow ? mainWindow->backgroundColor (actOnSelection) : kpColor::invalid),
+ m_oldPixmapPtr (0)
+{
+}
+
+kpToolSkewCommand::~kpToolSkewCommand ()
+{
+ delete m_oldPixmapPtr;
+}
+
+
+// public virtual [base kpCommand]
+QString kpToolSkewCommand::name () const
+{
+ QString opName = i18n ("Skew");
+
+ if (m_actOnSelection)
+ return i18n ("Selection: %1").arg (opName);
+ else
+ return opName;
+}
+
+
+// public virtual [base kpCommand]
+int kpToolSkewCommand::size () const
+{
+ return kpPixmapFX::pixmapSize (m_oldPixmapPtr) +
+ m_oldSelection.size ();
+}
+
+
+// public virtual [base kpCommand]
+void kpToolSkewCommand::execute ()
+{
+ kpDocument *doc = document ();
+ if (!doc)
+ return;
+
+
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+
+ m_oldPixmapPtr = new QPixmap ();
+ *m_oldPixmapPtr = *doc->pixmap (m_actOnSelection);
+
+
+ QPixmap newPixmap = kpPixmapFX::skew (*doc->pixmap (m_actOnSelection),
+ kpToolSkewDialog::horizontalAngleForPixmapFX (m_hangle),
+ kpToolSkewDialog::verticalAngleForPixmapFX (m_vangle),
+ m_backgroundColor);
+
+ if (m_actOnSelection)
+ {
+ kpSelection *sel = doc->selection ();
+
+ // Save old selection
+ m_oldSelection = *sel;
+
+
+ // Calculate skewed points
+ QPointArray currentPoints = sel->points ();
+ currentPoints.translate (-currentPoints.boundingRect ().x (),
+ -currentPoints.boundingRect ().y ());
+ QWMatrix skewMatrix = kpPixmapFX::skewMatrix (
+ *doc->pixmap (m_actOnSelection),
+ kpToolSkewDialog::horizontalAngleForPixmapFX (m_hangle),
+ kpToolSkewDialog::verticalAngleForPixmapFX (m_vangle));
+ currentPoints = skewMatrix.map (currentPoints);
+ currentPoints.translate (-currentPoints.boundingRect ().x () + m_oldSelection.x (),
+ -currentPoints.boundingRect ().y () + m_oldSelection.y ());
+
+
+ if (currentPoints.boundingRect ().width () == newPixmap.width () &&
+ currentPoints.boundingRect ().height () == newPixmap.height ())
+ {
+ doc->setSelection (kpSelection (currentPoints, newPixmap,
+ m_oldSelection.transparency ()));
+ }
+ else
+ {
+ // TODO: fix the latter "victim of" problem in kpSelection by
+ // allowing the border width & height != pixmap width & height
+ // Or maybe autocrop?
+ #if DEBUG_KP_TOOL_SKEW
+ kdDebug () << "kpToolSkewCommand::execute() currentPoints.boundingRect="
+ << currentPoints.boundingRect ()
+ << " newPixmap: w=" << newPixmap.width ()
+ << " h=" << newPixmap.height ()
+ << " (victim of rounding error and/or skewed-a-(rectangular)-pixmap-that-was-transparent-in-the-corners-making-sel-uselessly-bigger-than-needs-be))"
+ << endl;
+ #endif
+ doc->setSelection (kpSelection (kpSelection::Rectangle,
+ QRect (currentPoints.boundingRect ().x (),
+ currentPoints.boundingRect ().y (),
+ newPixmap.width (),
+ newPixmap.height ()),
+ newPixmap,
+ m_oldSelection.transparency ()));
+ }
+
+ if (m_mainWindow->tool ())
+ m_mainWindow->tool ()->somethingBelowTheCursorChanged ();
+ }
+ else
+ {
+ doc->setPixmap (newPixmap);
+ }
+
+
+ QApplication::restoreOverrideCursor ();
+}
+
+// public virtual [base kpCommand]
+void kpToolSkewCommand::unexecute ()
+{
+ kpDocument *doc = document ();
+ if (!doc)
+ return;
+
+
+ QApplication::setOverrideCursor (Qt::waitCursor);
+
+
+ QPixmap oldPixmap = *m_oldPixmapPtr;
+ delete m_oldPixmapPtr; m_oldPixmapPtr = 0;
+
+
+ if (!m_actOnSelection)
+ doc->setPixmap (oldPixmap);
+ else
+ {
+ kpSelection oldSelection = m_oldSelection;
+ doc->setSelection (oldSelection);
+
+ if (m_mainWindow->tool ())
+ m_mainWindow->tool ()->somethingBelowTheCursorChanged ();
+ }
+
+
+ QApplication::restoreOverrideCursor ();
+}
+
+
+/*
+ * kpToolSkewDialog
+ */
+
+
+// private static
+int kpToolSkewDialog::s_lastWidth = -1,
+ kpToolSkewDialog::s_lastHeight = -1;
+
+// private static
+int kpToolSkewDialog::s_lastHorizontalAngle = 0,
+ kpToolSkewDialog::s_lastVerticalAngle = 0;
+
+
+kpToolSkewDialog::kpToolSkewDialog (bool actOnSelection, kpMainWindow *parent,
+ const char *name)
+ : kpToolPreviewDialog (kpToolPreviewDialog::AllFeatures,
+ false/*don't reserve top row*/,
+ actOnSelection ? i18n ("Skew Selection") : i18n ("Skew Image"),
+ i18n ("After Skew:"),
+ actOnSelection, parent, name)
+{
+ // Too confusing - disable for now
+ s_lastHorizontalAngle = s_lastVerticalAngle = 0;
+
+
+ createAngleGroupBox ();
+
+
+ if (s_lastWidth > 0 && s_lastHeight > 0)
+ resize (s_lastWidth, s_lastHeight);
+
+
+ slotUpdate ();
+
+
+ m_horizontalSkewInput->setEditFocus ();
+}
+
+kpToolSkewDialog::~kpToolSkewDialog ()
+{
+ s_lastWidth = width (), s_lastHeight = height ();
+}
+
+
+// private
+void kpToolSkewDialog::createAngleGroupBox ()
+{
+ QGroupBox *angleGroupBox = new QGroupBox (i18n ("Angle"), mainWidget ());
+ addCustomWidget (angleGroupBox);
+
+
+ QLabel *horizontalSkewPixmapLabel = new QLabel (angleGroupBox);
+ horizontalSkewPixmapLabel->setPixmap (UserIcon ("image_skew_horizontal"));
+
+ QLabel *horizontalSkewLabel = new QLabel (i18n ("&Horizontal:"), angleGroupBox);
+ m_horizontalSkewInput = new KIntNumInput (s_lastHorizontalAngle, angleGroupBox);
+ m_horizontalSkewInput->setMinValue (-89);
+ m_horizontalSkewInput->setMaxValue (+89);
+
+ QLabel *horizontalSkewDegreesLabel = new QLabel (i18n ("degrees"), angleGroupBox);
+
+
+ QLabel *verticalSkewPixmapLabel = new QLabel (angleGroupBox);
+ verticalSkewPixmapLabel->setPixmap (UserIcon ("image_skew_vertical"));
+
+ QLabel *verticalSkewLabel = new QLabel (i18n ("&Vertical:"), angleGroupBox);
+ m_verticalSkewInput = new KIntNumInput (s_lastVerticalAngle, angleGroupBox);
+ m_verticalSkewInput->setMinValue (-89);
+ m_verticalSkewInput->setMaxValue (+89);
+
+ QLabel *verticalSkewDegreesLabel = new QLabel (i18n ("degrees"), angleGroupBox);
+
+
+ horizontalSkewLabel->setBuddy (m_horizontalSkewInput);
+ verticalSkewLabel->setBuddy (m_verticalSkewInput);
+
+
+ QGridLayout *angleLayout = new QGridLayout (angleGroupBox, 4, 4,
+ marginHint () * 2, spacingHint ());
+
+ angleLayout->addWidget (horizontalSkewPixmapLabel, 0, 0);
+ angleLayout->addWidget (horizontalSkewLabel, 0, 1);
+ angleLayout->addWidget (m_horizontalSkewInput, 0, 2);
+ angleLayout->addWidget (horizontalSkewDegreesLabel, 0, 3);
+
+ angleLayout->addWidget (verticalSkewPixmapLabel, 1, 0);
+ angleLayout->addWidget (verticalSkewLabel, 1, 1);
+ angleLayout->addWidget (m_verticalSkewInput, 1, 2);
+ angleLayout->addWidget (verticalSkewDegreesLabel, 1, 3);
+
+
+ connect (m_horizontalSkewInput, SIGNAL (valueChanged (int)),
+ this, SLOT (slotUpdate ()));
+ connect (m_verticalSkewInput, SIGNAL (valueChanged (int)),
+ this, SLOT (slotUpdate ()));
+}
+
+
+// private virtual [base kpToolPreviewDialog]
+QSize kpToolSkewDialog::newDimensions () const
+{
+ kpDocument *doc = document ();
+ if (!doc)
+ return QSize ();
+
+ QWMatrix skewMatrix = kpPixmapFX::skewMatrix (*doc->pixmap (),
+ horizontalAngleForPixmapFX (),
+ verticalAngleForPixmapFX ());
+ // TODO: Should we be using QWMatrix::Areas?
+ QRect skewRect = skewMatrix.mapRect (doc->rect (m_actOnSelection));
+
+ return QSize (skewRect.width (), skewRect.height ());
+}
+
+// private virtual [base kpToolPreviewDialog]
+QPixmap kpToolSkewDialog::transformPixmap (const QPixmap &pixmap,
+ int targetWidth, int targetHeight) const
+{
+ return kpPixmapFX::skew (pixmap,
+ horizontalAngleForPixmapFX (),
+ verticalAngleForPixmapFX (),
+ m_mainWindow ? m_mainWindow->backgroundColor (m_actOnSelection) : kpColor::invalid,
+ targetWidth,
+ targetHeight);
+}
+
+
+// private
+void kpToolSkewDialog::updateLastAngles ()
+{
+ s_lastHorizontalAngle = horizontalAngle ();
+ s_lastVerticalAngle = verticalAngle ();
+}
+
+// private slot virtual [base kpToolPreviewDialog]
+void kpToolSkewDialog::slotUpdate ()
+{
+ updateLastAngles ();
+ kpToolPreviewDialog::slotUpdate ();
+}
+
+
+// public
+int kpToolSkewDialog::horizontalAngle () const
+{
+ return m_horizontalSkewInput->value ();
+}
+
+// public
+int kpToolSkewDialog::verticalAngle () const
+{
+ return m_verticalSkewInput->value ();
+}
+
+
+// public static
+int kpToolSkewDialog::horizontalAngleForPixmapFX (int hangle)
+{
+ return -hangle;
+}
+
+// public static
+int kpToolSkewDialog::verticalAngleForPixmapFX (int vangle)
+{
+ return -vangle;
+}
+
+
+// public
+int kpToolSkewDialog::horizontalAngleForPixmapFX () const
+{
+ return kpToolSkewDialog::horizontalAngleForPixmapFX (horizontalAngle ());
+}
+
+// public
+int kpToolSkewDialog::verticalAngleForPixmapFX () const
+{
+ return kpToolSkewDialog::verticalAngleForPixmapFX (verticalAngle ());
+}
+
+
+// public virtual [base kpToolPreviewDialog]
+bool kpToolSkewDialog::isNoOp () const
+{
+ return (horizontalAngle () == 0) && (verticalAngle () == 0);
+}
+
+
+// private slot virtual [base KDialogBase]
+void kpToolSkewDialog::slotOk ()
+{
+ QString message, caption, continueButtonText;
+
+ if (document ()->selection ())
+ {
+ if (!document ()->selection ()->isText ())
+ {
+ message =
+ i18n ("<qt><p>Skewing the selection to %1x%2"
+ " may take a substantial amount of memory."
+ " This can reduce system"
+ " responsiveness and cause other application resource"
+ " problems.</p>"
+
+ "<p>Are you sure want to skew the selection?</p></qt>");
+
+ caption = i18n ("Skew Selection?");
+ continueButtonText = i18n ("Sk&ew Selection");
+ }
+ }
+ else
+ {
+ message =
+ i18n ("<qt><p>Skewing the image to %1x%2"
+ " may take a substantial amount of memory."
+ " This can reduce system"
+ " responsiveness and cause other application resource"
+ " problems.</p>"
+
+ "<p>Are you sure want to skew the image?</p></qt>");
+
+ caption = i18n ("Skew Image?");
+ continueButtonText = i18n ("Sk&ew Image");
+ }
+
+
+ const int newWidth = newDimensions ().width ();
+ const int newHeight = newDimensions ().height ();
+
+ if (kpTool::warnIfBigImageSize (m_oldWidth,
+ m_oldHeight,
+ newWidth, newHeight,
+ message.arg (newWidth).arg (newHeight),
+ caption,
+ continueButtonText,
+ this))
+ {
+ KDialogBase::slotOk ();
+ }
+}
+
+#include <kptoolskew.moc>
diff --git a/kolourpaint/tools/kptoolskew.h b/kolourpaint/tools/kptoolskew.h
new file mode 100644
index 00000000..570909c2
--- /dev/null
+++ b/kolourpaint/tools/kptoolskew.h
@@ -0,0 +1,121 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kptool_skew_h__
+#define __kptool_skew_h__
+
+#include <qpixmap.h>
+
+#include <kpcommandhistory.h>
+#include <kdialogbase.h>
+
+#include <kpcolor.h>
+#include <kpselection.h>
+#include <kptoolpreviewdialog.h>
+
+class QGroupBox;
+class QLabel;
+class QPixmap;
+
+class KIntNumInput;
+
+class kpDocument;
+class kpMainWindow;
+
+
+class kpToolSkewCommand : public kpCommand
+{
+public:
+ kpToolSkewCommand (bool actOnSelection,
+ int hangle, int vangle,
+ kpMainWindow *mainWindow);
+ virtual ~kpToolSkewCommand ();
+
+ virtual QString name () const;
+
+ virtual int size () const;
+
+ virtual void execute ();
+ virtual void unexecute ();
+
+private:
+ bool m_actOnSelection;
+ int m_hangle, m_vangle;
+
+ kpColor m_backgroundColor;
+ QPixmap *m_oldPixmapPtr;
+ kpSelection m_oldSelection;
+};
+
+
+class kpToolSkewDialog : public kpToolPreviewDialog
+{
+Q_OBJECT
+
+public:
+ kpToolSkewDialog (bool actOnSelection, kpMainWindow *parent,
+ const char *name = 0);
+ virtual ~kpToolSkewDialog ();
+
+private:
+ static int s_lastWidth, s_lastHeight;
+ static int s_lastHorizontalAngle, s_lastVerticalAngle;
+
+ void createAngleGroupBox ();
+
+ virtual QSize newDimensions () const;
+ virtual QPixmap transformPixmap (const QPixmap &pixmap,
+ int targetWidth, int targetHeight) const;
+
+ void updateLastAngles ();
+
+private slots:
+ virtual void slotUpdate ();
+
+public:
+ // These are the angles the users sees in the dialog and...
+ int horizontalAngle () const;
+ int verticalAngle () const;
+
+ // ...these functions translate them for use in kpPixmapFX::skew().
+ static int horizontalAngleForPixmapFX (int hangle);
+ static int verticalAngleForPixmapFX (int vangle);
+
+ int horizontalAngleForPixmapFX () const;
+ int verticalAngleForPixmapFX () const;
+
+ virtual bool isNoOp () const;
+
+private slots:
+ virtual void slotOk ();
+
+private:
+ KIntNumInput *m_horizontalSkewInput, *m_verticalSkewInput;
+};
+
+#endif // __kptool_skew_h__
diff --git a/kolourpaint/tools/kptooltext.cpp b/kolourpaint/tools/kptooltext.cpp
new file mode 100644
index 00000000..73a60e66
--- /dev/null
+++ b/kolourpaint/tools/kptooltext.cpp
@@ -0,0 +1,1394 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG_KP_TOOL_TEXT 0
+
+
+#include <kptooltext.h>
+
+#include <qvaluevector.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kpcommandhistory.h>
+#include <kpdocument.h>
+#include <kpmainwindow.h>
+#include <kpselection.h>
+#include <kptoolwidgetopaqueortransparent.h>
+#include <kpviewmanager.h>
+
+
+kpToolText::kpToolText (kpMainWindow *mainWindow)
+ : kpToolSelection (Text,
+ i18n ("Text"), i18n ("Writes text"),
+ Qt::Key_T,
+ mainWindow, "tool_text"),
+ m_isIMStarted (false),
+ m_IMStartCursorRow (0),
+ m_IMStartCursorCol (0),
+ m_IMPreeditStr (0)
+{
+}
+
+kpToolText::~kpToolText ()
+{
+}
+
+
+// public virtual [base kpToolSelection]
+void kpToolText::begin ()
+{
+#if DEBUG_KP_TOOL_TEXT && 1
+ kdDebug () << "kpToolText::begin()" << endl;
+#endif
+
+ mainWindow ()->enableTextToolBarActions (true);
+ viewManager ()->setTextCursorEnabled (true);
+
+ m_insertCommand = 0;
+ m_enterCommand = 0;
+ m_backspaceCommand = 0;
+ m_deleteCommand = 0;
+
+ kpToolSelection::begin ();
+}
+
+// public virtual [base kpToolSelection]
+void kpToolText::end ()
+{
+#if DEBUG_KP_TOOL_TEXT && 1
+ kdDebug () << "kpToolText::end()" << endl;
+#endif
+
+ kpToolSelection::end ();
+
+ viewManager ()->setTextCursorEnabled (false);
+ mainWindow ()->enableTextToolBarActions (false);
+}
+
+
+// public
+bool kpToolText::hasBegunText () const
+{
+ return (m_insertCommand ||
+ m_enterCommand ||
+ m_backspaceCommand ||
+ m_deleteCommand);
+}
+
+// public virtual [base kpTool]
+bool kpToolText::hasBegunShape () const
+{
+ return (hasBegunDraw () || hasBegunText ());
+}
+
+
+// public virtual [base kpToolSelection]
+void kpToolText::cancelShape ()
+{
+#if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "kpToolText::cancelShape()" << endl;
+#endif
+
+ if (m_dragType != Unknown)
+ kpToolSelection::cancelShape ();
+ else if (hasBegunText ())
+ {
+ m_insertCommand = 0;
+ m_enterCommand = 0;
+ m_backspaceCommand = 0;
+ m_deleteCommand = 0;
+
+ commandHistory ()->undo ();
+ }
+ else
+ kpToolSelection::cancelShape ();
+}
+
+// public virtual [base kpTool]
+void kpToolText::endShape (const QPoint &thisPoint, const QRect &normalizedRect)
+{
+#if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "kpToolText::endShape()" << endl;
+#endif
+
+ if (m_dragType != Unknown)
+ kpToolSelection::endDraw (thisPoint, normalizedRect);
+ else if (hasBegunText ())
+ {
+ m_insertCommand = 0;
+ m_enterCommand = 0;
+ m_backspaceCommand = 0;
+ m_deleteCommand = 0;
+ }
+ else
+ kpToolSelection::endDraw (thisPoint, normalizedRect);
+}
+
+
+// protected virtual [base kpTool]
+void kpToolText::keyPressEvent (QKeyEvent *e)
+{
+#if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "kpToolText::keyPressEvent(e->text='" << e->text () << "')" << endl;
+#endif
+
+
+ e->ignore ();
+
+
+ if (hasBegunDraw ())
+ {
+ #if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "\talready began draw with mouse - passing on event to kpTool" << endl;
+ #endif
+ kpToolSelection::keyPressEvent (e);
+ return;
+ }
+
+
+ kpSelection *sel = document ()->selection ();
+
+ if (!sel || !sel->isText ())
+ {
+ #if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "\tno text sel - passing on event to kpTool" << endl;
+ #endif
+ //if (hasBegunShape ())
+ // endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ());
+
+ kpToolSelection::keyPressEvent (e);
+ return;
+ }
+
+
+ const QValueVector <QString> textLines = sel->textLines ();
+ int cursorRow = viewManager ()->textCursorRow ();
+ int cursorCol = viewManager ()->textCursorCol ();
+
+
+#define IS_SPACE(c) ((c).isSpace () || (c).isNull ())
+ if (e->key () == Qt::Key_Enter || e->key () == Qt::Key_Return)
+ {
+ #if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "\tenter pressed" << endl;
+ #endif
+ if (!m_enterCommand)
+ {
+ // TODO: why not endShapeInternal(); ditto for everywhere else in this file?
+ if (hasBegunShape ())
+ endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ());
+
+ m_enterCommand = new kpToolTextEnterCommand (i18n ("Text: New Line"),
+ viewManager ()->textCursorRow (), viewManager ()->textCursorCol (),
+ mainWindow ());
+ commandHistory ()->addCommand (m_enterCommand, false/*no exec*/);
+ }
+ else
+ m_enterCommand->addEnter ();
+
+ e->accept ();
+ }
+ else if (e->key () == Qt::Key_Backspace)
+ {
+ #if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "\tbackspace pressed" << endl;
+ #endif
+
+ if (!m_backspaceCommand)
+ {
+ if (hasBegunShape ())
+ endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ());
+
+ m_backspaceCommand = new kpToolTextBackspaceCommand (i18n ("Text: Backspace"),
+ viewManager ()->textCursorRow (), viewManager ()->textCursorCol (),
+ mainWindow ());
+ commandHistory ()->addCommand (m_backspaceCommand, false/*no exec*/);
+ }
+ else
+ m_backspaceCommand->addBackspace ();
+
+ e->accept ();
+ }
+ else if (e->key () == Qt::Key_Delete)
+ {
+ #if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "\tdelete pressed" << endl;
+ #endif
+
+ if (!m_deleteCommand)
+ {
+ if (hasBegunShape ())
+ endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ());
+
+ m_deleteCommand = new kpToolTextDeleteCommand (i18n ("Text: Delete"),
+ viewManager ()->textCursorRow (), viewManager ()->textCursorCol (),
+ mainWindow ());
+ commandHistory ()->addCommand (m_deleteCommand, false/*no exec*/);
+ }
+ else
+ m_deleteCommand->addDelete ();
+
+ e->accept ();
+ }
+ else if (e->key () == Qt::Key_Up)
+ {
+ #if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "\tup pressed" << endl;
+ #endif
+
+ if (hasBegunShape ())
+ endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ());
+
+ if (cursorRow > 0)
+ {
+ cursorRow--;
+ cursorCol = QMIN (cursorCol, (int) textLines [cursorRow].length ());
+ viewManager ()->setTextCursorPosition (cursorRow, cursorCol);
+ }
+
+ e->accept ();
+ }
+ else if (e->key () == Qt::Key_Down)
+ {
+ #if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "\tdown pressed" << endl;
+ #endif
+
+ if (hasBegunShape ())
+ endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ());
+
+ if (cursorRow < (int) textLines.size () - 1)
+ {
+ cursorRow++;
+ cursorCol = QMIN (cursorCol, (int) textLines [cursorRow].length ());
+ viewManager ()->setTextCursorPosition (cursorRow, cursorCol);
+ }
+
+ e->accept ();
+ }
+ else if (e->key () == Qt::Key_Left)
+ {
+ #if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "\tleft pressed" << endl;
+ #endif
+
+ #define MOVE_CURSOR_LEFT() \
+ { \
+ cursorCol--; \
+ \
+ if (cursorCol < 0) \
+ { \
+ cursorRow--; \
+ if (cursorRow < 0) \
+ { \
+ cursorRow = 0; \
+ cursorCol = 0; \
+ } \
+ else \
+ cursorCol = textLines [cursorRow].length (); \
+ } \
+ }
+
+ if (hasBegunShape ())
+ endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ());
+
+ if ((e->state () & Qt::ControlButton) == 0)
+ {
+ #if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "\tmove single char" << endl;
+ #endif
+
+ MOVE_CURSOR_LEFT ();
+ viewManager ()->setTextCursorPosition (cursorRow, cursorCol);
+ }
+ else
+ {
+ #if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "\tmove to start of word" << endl;
+ #endif
+
+ // (these comments will exclude the row=0,col=0 boundary case)
+
+ #define IS_ON_ANCHOR() (!IS_SPACE (textLines [cursorRow][cursorCol]) && \
+ (cursorCol == 0 || IS_SPACE (textLines [cursorRow][cursorCol - 1])))
+ if (IS_ON_ANCHOR ())
+ MOVE_CURSOR_LEFT ();
+
+ // --- now we're not on an anchor point (start of word) ---
+
+ // End up on a letter...
+ while (!(cursorRow == 0 && cursorCol == 0) &&
+ (IS_SPACE (textLines [cursorRow][cursorCol])))
+ {
+ MOVE_CURSOR_LEFT ();
+ }
+
+ // --- now we're on a letter ---
+
+ // Find anchor point
+ while (!(cursorRow == 0 && cursorCol == 0) && !IS_ON_ANCHOR ())
+ {
+ MOVE_CURSOR_LEFT ();
+ }
+
+ #undef IS_ON_ANCHOR
+
+ viewManager ()->setTextCursorPosition (cursorRow, cursorCol);
+ }
+
+ #undef MOVE_CURSOR_LEFT
+
+ e->accept ();
+
+ }
+ else if (e->key () == Qt::Key_Right)
+ {
+ #if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "\tright pressed" << endl;
+ #endif
+
+ #define MOVE_CURSOR_RIGHT() \
+ { \
+ cursorCol++; \
+ \
+ if (cursorCol > (int) textLines [cursorRow].length ()) \
+ { \
+ cursorRow++; \
+ if (cursorRow > (int) textLines.size () - 1) \
+ { \
+ cursorRow = textLines.size () - 1; \
+ cursorCol = textLines [cursorRow].length (); \
+ } \
+ else \
+ cursorCol = 0; \
+ } \
+ }
+
+ if (hasBegunShape ())
+ endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ());
+
+ if ((e->state () & Qt::ControlButton) == 0)
+ {
+ #if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "\tmove single char" << endl;
+ #endif
+
+ MOVE_CURSOR_RIGHT ();
+ viewManager ()->setTextCursorPosition (cursorRow, cursorCol);
+ }
+ else
+ {
+ #if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "\tmove to start of word" << endl;
+ #endif
+
+ // (these comments will exclude the last row,end col boundary case)
+
+ #define IS_AT_END() (cursorRow == (int) textLines.size () - 1 && \
+ cursorCol == (int) textLines [cursorRow].length ())
+
+ // Find space
+ while (!IS_AT_END () && !IS_SPACE (textLines [cursorRow][cursorCol]))
+ {
+ MOVE_CURSOR_RIGHT ();
+ }
+
+ // --- now we're on a space ---
+
+ // Find letter
+ while (!IS_AT_END () && IS_SPACE (textLines [cursorRow][cursorCol]))
+ {
+ MOVE_CURSOR_RIGHT ();
+ }
+
+ // --- now we're on a letter ---
+
+ viewManager ()->setTextCursorPosition (cursorRow, cursorCol);
+
+ #undef IS_AT_END
+ }
+
+ #undef MOVE_CURSOR_RIGHT
+
+ e->accept ();
+ }
+ else if (e->key () == Qt::Key_Home)
+ {
+ #if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "\thome pressed" << endl;
+ #endif
+
+ if (hasBegunShape ())
+ endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ());
+
+ if (e->state () & Qt::ControlButton)
+ cursorRow = 0;
+
+ cursorCol = 0;
+
+ viewManager ()->setTextCursorPosition (cursorRow, cursorCol);
+
+ e->accept ();
+ }
+ else if (e->key () == Qt::Key_End)
+ {
+ #if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "\tend pressed" << endl;
+ #endif
+
+ if (hasBegunShape ())
+ endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ());
+
+ if (e->state () & Qt::ControlButton)
+ cursorRow = textLines.size () - 1;
+
+ cursorCol = textLines [cursorRow].length ();
+
+ viewManager ()->setTextCursorPosition (cursorRow, cursorCol);
+
+ e->accept ();
+ }
+ else
+ {
+ #if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "\ttext='" << e->text () << "'" << endl;
+ #endif
+ QString usableText;
+ for (int i = 0; i < (int) e->text ().length (); i++)
+ {
+ if (e->text ().at (i).isPrint ())
+ usableText += e->text ().at (i);
+ }
+ #if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "\tusableText='" << usableText << "'" << endl;
+ #endif
+
+ if (usableText.length () > 0)
+ {
+ if (!m_insertCommand)
+ {
+ if (hasBegunShape ())
+ endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ());
+
+ m_insertCommand = new kpToolTextInsertCommand (i18n ("Text: Write"),
+ viewManager ()->textCursorRow (), viewManager ()->textCursorCol (),
+ usableText,
+ mainWindow ());
+ commandHistory ()->addCommand (m_insertCommand, false/*no exec*/);
+ }
+ else
+ m_insertCommand->addText (usableText);
+
+ e->accept ();
+ }
+ }
+#undef IS_SPACE
+
+
+ if (!e->isAccepted ())
+ {
+ #if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "\tkey processing did not accept (text was '"
+ << e->text ()
+ << "') - passing on event to kpToolSelection"
+ << endl;
+ #endif
+ //if (hasBegunShape ())
+ // endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ());
+
+ kpToolSelection::keyPressEvent (e);
+ return;
+ }
+}
+
+void kpToolText::imStartEvent (QIMEvent *e)
+{
+#if DEBUG_KP_TOOL_TEXT && 1
+ kdDebug () << "kpToolText::imStartEvent() text='" << e->text ()
+ << " cursorPos=" << e->cursorPos ()
+ << " selectionLength=" << e->selectionLength ()
+ << endl;
+#endif
+
+ kpSelection *sel = document ()->selection ();
+ if (hasBegunDraw() || !sel || !sel->isText ())
+ {
+ e->ignore();
+ return;
+ }
+
+ m_IMStartCursorRow = viewManager ()->textCursorRow ();
+ m_IMStartCursorCol = viewManager ()->textCursorCol ();
+ m_IMPreeditStr = QString::null;
+}
+
+void kpToolText::imComposeEvent (QIMEvent *e)
+{
+#if DEBUG_KP_TOOL_TEXT && 1
+ kdDebug () << "kpToolText::imComposeEvent() text='" << e->text ()
+ << " cursorPos=" << e->cursorPos ()
+ << " selectionLength=" << e->selectionLength ()
+ << endl;
+#endif
+
+ kpSelection *sel = document ()->selection ();
+ if (hasBegunDraw() || !sel || !sel->isText ())
+ {
+ e->ignore();
+ return;
+ }
+
+ // remove old preedit
+ if (m_IMPreeditStr.length() > 0 )
+ {
+ // set cursor at the start input point
+ viewManager ()->setTextCursorPosition (m_IMStartCursorRow, m_IMStartCursorCol);
+ for (unsigned int i = 0; i < m_IMPreeditStr.length(); i++)
+ {
+ if (!m_deleteCommand)
+ {
+ if (hasBegunShape ())
+ endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ());
+
+ m_deleteCommand = new kpToolTextDeleteCommand (i18n ("Text: Delete"),
+ viewManager ()->textCursorRow (), viewManager ()->textCursorCol (),
+ mainWindow ());
+ commandHistory ()->addCommand (m_deleteCommand, false/*no exec*/);
+ }
+ else
+ m_deleteCommand->addDelete ();
+ }
+ }
+
+ // insert new preedit
+ m_IMPreeditStr = e->text();
+ if (m_IMPreeditStr.length() > 0)
+ {
+ if (!m_insertCommand)
+ {
+ if (hasBegunShape ())
+ endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ());
+
+ m_insertCommand = new kpToolTextInsertCommand (i18n ("Text: Write"),
+ viewManager ()->textCursorRow (), viewManager ()->textCursorCol (),
+ m_IMPreeditStr,
+ mainWindow ());
+ commandHistory ()->addCommand (m_insertCommand, false/*no exec*/);
+ }
+ else
+ m_insertCommand->addText (m_IMPreeditStr);
+ }
+
+ // set cursor pos
+ if (m_IMStartCursorRow >= 0)
+ {
+ int row = m_IMStartCursorRow;
+ int col = m_IMStartCursorCol + e->cursorPos () /* + e->selectionLength()*/;
+ viewManager ()->setTextCursorPosition (row, col, true /* update MicroFocusHint */);
+ }
+}
+
+void kpToolText::imEndEvent (QIMEvent *e)
+{
+#if DEBUG_KP_TOOL_TEXT && 1
+ kdDebug () << "kpToolText::imEndEvent() text='" << e->text ()
+ << " cursorPos=" << e->cursorPos ()
+ << " selectionLength=" << e->selectionLength ()
+ << endl;
+#endif
+
+ kpSelection *sel = document ()->selection ();
+ if (hasBegunDraw() || !sel || !sel->isText ())
+ {
+ e->ignore();
+ return;
+ }
+
+ // remove old preedit
+ if (m_IMPreeditStr.length() > 0 )
+ {
+ // set cursor at the start input point
+ viewManager ()->setTextCursorPosition (m_IMStartCursorRow, m_IMStartCursorCol);
+ for (unsigned int i = 0; i < m_IMPreeditStr.length(); i++)
+ {
+ if (!m_deleteCommand)
+ {
+ if (hasBegunShape ())
+ endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ());
+
+ m_deleteCommand = new kpToolTextDeleteCommand (i18n ("Text: Delete"),
+ viewManager ()->textCursorRow (), viewManager ()->textCursorCol (),
+ mainWindow ());
+ commandHistory ()->addCommand (m_deleteCommand, false/*no exec*/);
+ }
+ else
+ m_deleteCommand->addDelete ();
+ }
+ }
+ m_IMPreeditStr = QString::null;
+
+ // commit string
+ QString inputStr = e->text();
+ if (inputStr.length() > 0)
+ {
+ if (!m_insertCommand)
+ {
+ if (hasBegunShape ())
+ endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ());
+
+ m_insertCommand = new kpToolTextInsertCommand (i18n ("Text: Write"),
+ viewManager ()->textCursorRow (), viewManager ()->textCursorCol (),
+ inputStr,
+ mainWindow ());
+ commandHistory ()->addCommand (m_insertCommand, false/*no exec*/);
+ }
+ else
+ m_insertCommand->addText (inputStr);
+ }
+}
+
+
+// protected
+bool kpToolText::shouldChangeTextStyle () const
+{
+ if (mainWindow ()->settingTextStyle ())
+ {
+ #if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "\trecursion - abort setting text style: "
+ << mainWindow ()->settingTextStyle ()
+ << endl;
+ #endif
+ return false;
+ }
+
+ if (!document ()->selection () ||
+ !document ()->selection ()->isText ())
+ {
+ #if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "\tno text selection - abort setting text style" << endl;
+ #endif
+ return false;
+ }
+
+ return true;
+}
+
+// protected
+void kpToolText::changeTextStyle (const QString &name,
+ const kpTextStyle &newTextStyle,
+ const kpTextStyle &oldTextStyle)
+{
+#if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "kpToolText::changeTextStyle(" << name << ")" << endl;
+#endif
+
+ if (hasBegunShape ())
+ endShape (m_currentPoint, QRect (m_startPoint, m_currentPoint).normalize ());
+
+ commandHistory ()->addCommand (
+ new kpToolTextChangeStyleCommand (
+ name,
+ newTextStyle,
+ oldTextStyle,
+ mainWindow ()));
+}
+
+
+// protected slot virtual [base kpToolSelection]
+void kpToolText::slotIsOpaqueChanged ()
+{
+#if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "kpToolText::slotIsOpaqueChanged()" << endl;
+#endif
+
+ if (!shouldChangeTextStyle ())
+ return;
+
+ kpTextStyle newTextStyle = mainWindow ()->textStyle ();
+ kpTextStyle oldTextStyle = newTextStyle;
+ oldTextStyle.setBackgroundOpaque (!m_toolWidgetOpaqueOrTransparent->isOpaque ());
+
+ changeTextStyle (newTextStyle.isBackgroundOpaque () ?
+ i18n ("Text: Opaque Background") :
+ i18n ("Text: Transparent Background"),
+ newTextStyle,
+ oldTextStyle);
+}
+
+// protected slot virtual [base kpTool]
+void kpToolText::slotColorsSwapped (const kpColor &newForegroundColor,
+ const kpColor &newBackgroundColor)
+{
+#if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "kpToolText::slotColorsSwapped()" << endl;
+#endif
+
+ if (!shouldChangeTextStyle ())
+ return;
+
+ kpTextStyle newTextStyle = mainWindow ()->textStyle ();
+ kpTextStyle oldTextStyle = newTextStyle;
+ oldTextStyle.setForegroundColor (newBackgroundColor);
+ oldTextStyle.setBackgroundColor (newForegroundColor);
+
+ changeTextStyle (i18n ("Text: Swap Colors"),
+ newTextStyle,
+ oldTextStyle);
+}
+
+// protected slot virtual [base kpTool]
+void kpToolText::slotForegroundColorChanged (const kpColor & /*color*/)
+{
+#if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "kpToolText::slotForegroundColorChanged()" << endl;
+#endif
+
+ if (!shouldChangeTextStyle ())
+ return;
+
+ kpTextStyle newTextStyle = mainWindow ()->textStyle ();
+ kpTextStyle oldTextStyle = newTextStyle;
+ oldTextStyle.setForegroundColor (oldForegroundColor ());
+
+ changeTextStyle (i18n ("Text: Foreground Color"),
+ newTextStyle,
+ oldTextStyle);
+}
+
+// protected slot virtual [base kpToolSelection]
+void kpToolText::slotBackgroundColorChanged (const kpColor & /*color*/)
+{
+#if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "kpToolText::slotBackgroundColorChanged()" << endl;
+#endif
+
+ if (!shouldChangeTextStyle ())
+ return;
+
+ kpTextStyle newTextStyle = mainWindow ()->textStyle ();
+ kpTextStyle oldTextStyle = newTextStyle;
+ oldTextStyle.setBackgroundColor (oldBackgroundColor ());
+
+ changeTextStyle (i18n ("Text: Background Color"),
+ newTextStyle,
+ oldTextStyle);
+}
+
+// protected slot virtual [base kpToolSelection]
+void kpToolText::slotColorSimilarityChanged (double, int)
+{
+ // --- don't pass on event to kpToolSelection which would have set the
+ // SelectionTransparency - not relevant to the Text Tool ---
+}
+
+
+// public slot
+void kpToolText::slotFontFamilyChanged (const QString &fontFamily,
+ const QString &oldFontFamily)
+{
+#if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "kpToolText::slotFontFamilyChanged() new="
+ << fontFamily
+ << " old="
+ << oldFontFamily
+ << endl;
+#else
+ (void) fontFamily;
+#endif
+
+ if (!shouldChangeTextStyle ())
+ return;
+
+ kpTextStyle newTextStyle = mainWindow ()->textStyle ();
+ kpTextStyle oldTextStyle = newTextStyle;
+ oldTextStyle.setFontFamily (oldFontFamily);
+
+ changeTextStyle (i18n ("Text: Font"),
+ newTextStyle,
+ oldTextStyle);
+}
+
+// public slot
+void kpToolText::slotFontSizeChanged (int fontSize, int oldFontSize)
+{
+#if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "kpToolText::slotFontSizeChanged() new="
+ << fontSize
+ << " old="
+ << oldFontSize
+ << endl;
+#else
+ (void) fontSize;
+#endif
+
+ if (!shouldChangeTextStyle ())
+ return;
+
+ kpTextStyle newTextStyle = mainWindow ()->textStyle ();
+ kpTextStyle oldTextStyle = newTextStyle;
+ oldTextStyle.setFontSize (oldFontSize);
+
+ changeTextStyle (i18n ("Text: Font Size"),
+ newTextStyle,
+ oldTextStyle);
+}
+
+
+// public slot
+void kpToolText::slotBoldChanged (bool isBold)
+{
+#if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "kpToolText::slotBoldChanged(" << isBold << ")" << endl;
+#endif
+
+ if (!shouldChangeTextStyle ())
+ return;
+
+ kpTextStyle newTextStyle = mainWindow ()->textStyle ();
+ kpTextStyle oldTextStyle = newTextStyle;
+ oldTextStyle.setBold (!isBold);
+
+ changeTextStyle (i18n ("Text: Bold"),
+ newTextStyle,
+ oldTextStyle);
+}
+
+// public slot
+void kpToolText::slotItalicChanged (bool isItalic)
+{
+#if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "kpToolText::slotItalicChanged(" << isItalic << ")" << endl;
+#endif
+
+ if (!shouldChangeTextStyle ())
+ return;
+
+ kpTextStyle newTextStyle = mainWindow ()->textStyle ();
+ kpTextStyle oldTextStyle = newTextStyle;
+ oldTextStyle.setItalic (!isItalic);
+
+ changeTextStyle (i18n ("Text: Italic"),
+ newTextStyle,
+ oldTextStyle);
+}
+
+// public slot
+void kpToolText::slotUnderlineChanged (bool isUnderline)
+{
+#if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "kpToolText::slotUnderlineChanged(" << isUnderline << ")" << endl;
+#endif
+
+ if (!shouldChangeTextStyle ())
+ return;
+
+ kpTextStyle newTextStyle = mainWindow ()->textStyle ();
+ kpTextStyle oldTextStyle = newTextStyle;
+ oldTextStyle.setUnderline (!isUnderline);
+
+ changeTextStyle (i18n ("Text: Underline"),
+ newTextStyle,
+ oldTextStyle);
+}
+
+// public slot
+void kpToolText::slotStrikeThruChanged (bool isStrikeThru)
+{
+#if DEBUG_KP_TOOL_TEXT
+ kdDebug () << "kpToolText::slotStrikeThruChanged(" << isStrikeThru << ")" << endl;
+#endif
+
+ if (!shouldChangeTextStyle ())
+ return;
+
+ kpTextStyle newTextStyle = mainWindow ()->textStyle ();
+ kpTextStyle oldTextStyle = newTextStyle;
+ oldTextStyle.setStrikeThru (!isStrikeThru);
+
+ changeTextStyle (i18n ("Text: Strike Through"),
+ newTextStyle,
+ oldTextStyle);
+}
+
+
+/*
+ * kpToolTextChangeStyleCommand
+ */
+
+kpToolTextChangeStyleCommand::kpToolTextChangeStyleCommand (const QString &name,
+ const kpTextStyle &newTextStyle, const kpTextStyle &oldTextStyle,
+ kpMainWindow *mainWindow)
+ : kpNamedCommand (name, mainWindow),
+ m_newTextStyle (newTextStyle),
+ m_oldTextStyle (oldTextStyle)
+{
+}
+
+kpToolTextChangeStyleCommand::~kpToolTextChangeStyleCommand ()
+{
+}
+
+
+// public virtual [base kpCommand]
+int kpToolTextChangeStyleCommand::size () const
+{
+ return 0;
+}
+
+
+// public virtual [base kpCommand]
+void kpToolTextChangeStyleCommand::execute ()
+{
+#if DEBUG_KP_TOOL_TEXT && 1
+ kdDebug () << "kpToolTextChangeStyleCommand::execute()"
+ << " font=" << m_newTextStyle.fontFamily ()
+ << " fontSize=" << m_newTextStyle.fontSize ()
+ << " isBold=" << m_newTextStyle.isBold ()
+ << " isItalic=" << m_newTextStyle.isItalic ()
+ << " isUnderline=" << m_newTextStyle.isUnderline ()
+ << " isStrikeThru=" << m_newTextStyle.isStrikeThru ()
+ << endl;
+#endif
+
+ m_mainWindow->setTextStyle (m_newTextStyle);
+ if (selection ())
+ selection ()->setTextStyle (m_newTextStyle);
+ else
+ kdError () << "kpToolTextChangeStyleCommand::execute() without sel" << endl;
+}
+
+// public virtual [base kpCommand]
+void kpToolTextChangeStyleCommand::unexecute ()
+{
+#if DEBUG_KP_TOOL_TEXT && 1
+ kdDebug () << "kpToolTextChangeStyleCommand::unexecute()"
+ << " font=" << m_newTextStyle.fontFamily ()
+ << " fontSize=" << m_newTextStyle.fontSize ()
+ << " isBold=" << m_newTextStyle.isBold ()
+ << " isItalic=" << m_newTextStyle.isItalic ()
+ << " isUnderline=" << m_newTextStyle.isUnderline ()
+ << " isStrikeThru=" << m_newTextStyle.isStrikeThru ()
+ << endl;
+#endif
+
+ m_mainWindow->setTextStyle (m_oldTextStyle);
+ if (selection ())
+ selection ()->setTextStyle (m_oldTextStyle);
+ else
+ kdError () << "kpToolTextChangeStyleCommand::unexecute() without sel" << endl;
+}
+
+
+/*
+ * kpToolTextInsertCommand
+ */
+
+kpToolTextInsertCommand::kpToolTextInsertCommand (const QString &name,
+ int row, int col, QString newText,
+ kpMainWindow *mainWindow)
+ : kpNamedCommand (name, mainWindow),
+ m_row (row), m_col (col)
+{
+ viewManager ()->setTextCursorPosition (m_row, m_col);
+ addText (newText);
+}
+
+kpToolTextInsertCommand::~kpToolTextInsertCommand ()
+{
+}
+
+
+// public
+void kpToolTextInsertCommand::addText (const QString &moreText)
+{
+ if (moreText.isEmpty ())
+ return;
+
+ QValueVector <QString> textLines = selection ()->textLines ();
+ const QString leftHalf = textLines [m_row].left (m_col);
+ const QString rightHalf = textLines [m_row].mid (m_col);
+ textLines [m_row] = leftHalf + moreText + rightHalf;
+ selection ()->setTextLines (textLines);
+
+ m_newText += moreText;
+ m_col += moreText.length ();
+
+ viewManager ()->setTextCursorPosition (m_row, m_col);
+}
+
+
+// public virtual [base kpCommand]
+int kpToolTextInsertCommand::size () const
+{
+ return m_newText.length () * sizeof (QChar);
+}
+
+
+// public virtual [base kpCommand]
+void kpToolTextInsertCommand::execute ()
+{
+ viewManager ()->setTextCursorPosition (m_row, m_col);
+
+ QString text = m_newText;
+ m_newText = QString::null;
+ addText (text);
+}
+
+// public virtual [base kpCommand]
+void kpToolTextInsertCommand::unexecute ()
+{
+ viewManager ()->setTextCursorPosition (m_row, m_col);
+
+ QValueVector <QString> textLines = selection ()->textLines ();
+ const QString leftHalf = textLines [m_row].left (m_col - m_newText.length ());
+ const QString rightHalf = textLines [m_row].mid (m_col);
+ textLines [m_row] = leftHalf + rightHalf;
+ selection ()->setTextLines (textLines);
+
+ m_col -= m_newText.length ();
+
+ viewManager ()->setTextCursorPosition (m_row, m_col);
+}
+
+
+/*
+ * kpToolTextEnterCommand
+ */
+
+kpToolTextEnterCommand::kpToolTextEnterCommand (const QString &name,
+ int row, int col,
+ kpMainWindow *mainWindow)
+ : kpNamedCommand (name, mainWindow),
+ m_row (row), m_col (col),
+ m_numEnters (0)
+{
+ viewManager ()->setTextCursorPosition (m_row, m_col);
+ addEnter ();
+}
+
+kpToolTextEnterCommand::~kpToolTextEnterCommand ()
+{
+}
+
+
+// public
+void kpToolTextEnterCommand::addEnter ()
+{
+ QValueVector <QString> textLines = selection ()->textLines ();
+
+ const QString rightHalf = textLines [m_row].mid (m_col);
+
+ textLines [m_row].truncate (m_col);
+ textLines.insert (textLines.begin () + m_row + 1, rightHalf);
+
+ selection ()->setTextLines (textLines);
+
+ m_row++;
+ m_col = 0;
+
+ viewManager ()->setTextCursorPosition (m_row, m_col);
+
+ m_numEnters++;
+}
+
+
+// public virtual [base kpCommand]
+int kpToolTextEnterCommand::size () const
+{
+ return 0;
+}
+
+
+// public virtual [base kpCommand]
+void kpToolTextEnterCommand::execute ()
+{
+ viewManager ()->setTextCursorPosition (m_row, m_col);
+ int oldNumEnters = m_numEnters;
+ m_numEnters = 0;
+
+ for (int i = 0; i < oldNumEnters; i++)
+ addEnter ();
+}
+
+// public virtual [base kpCommand]
+void kpToolTextEnterCommand::unexecute ()
+{
+ viewManager ()->setTextCursorPosition (m_row, m_col);
+
+ QValueVector <QString> textLines = selection ()->textLines ();
+
+ for (int i = 0; i < m_numEnters; i++)
+ {
+ if (m_col != 0)
+ {
+ kdError () << "kpToolTextEnterCommand::unexecute() col=" << m_col << endl;
+ break;
+ }
+
+ if (m_row <= 0)
+ break;
+
+ int newRow = m_row - 1;
+ int newCol = textLines [newRow].length ();
+
+ textLines [newRow] += textLines [m_row];
+
+ textLines.erase (textLines.begin () + m_row);
+
+ m_row = newRow;
+ m_col = newCol;
+ }
+
+ selection ()->setTextLines (textLines);
+
+ viewManager ()->setTextCursorPosition (m_row, m_col);
+}
+
+
+/*
+ * kpToolTextBackspaceCommand
+ */
+
+kpToolTextBackspaceCommand::kpToolTextBackspaceCommand (const QString &name,
+ int row, int col,
+ kpMainWindow *mainWindow)
+ : kpNamedCommand (name, mainWindow),
+ m_row (row), m_col (col),
+ m_numBackspaces (0)
+{
+ viewManager ()->setTextCursorPosition (m_row, m_col);
+ addBackspace ();
+}
+
+kpToolTextBackspaceCommand::~kpToolTextBackspaceCommand ()
+{
+}
+
+
+// public
+void kpToolTextBackspaceCommand::addBackspace ()
+{
+ QValueVector <QString> textLines = selection ()->textLines ();
+
+ if (m_col > 0)
+ {
+ m_deletedText.prepend (textLines [m_row][m_col - 1]);
+
+ textLines [m_row] = textLines [m_row].left (m_col - 1) +
+ textLines [m_row].mid (m_col);
+ m_col--;
+ }
+ else
+ {
+ if (m_row > 0)
+ {
+ int newCursorRow = m_row - 1;
+ int newCursorCol = textLines [newCursorRow].length ();
+
+ m_deletedText.prepend ('\n');
+
+ textLines [newCursorRow] += textLines [m_row];
+
+ textLines.erase (textLines.begin () + m_row);
+
+ m_row = newCursorRow;
+ m_col = newCursorCol;
+ }
+ }
+
+ selection ()->setTextLines (textLines);
+
+ viewManager ()->setTextCursorPosition (m_row, m_col);
+
+ m_numBackspaces++;
+}
+
+
+// public virtual [base kpCommand]
+int kpToolTextBackspaceCommand::size () const
+{
+ return m_deletedText.length () * sizeof (QChar);
+}
+
+
+// public virtual [base kpCommand]
+void kpToolTextBackspaceCommand::execute ()
+{
+ viewManager ()->setTextCursorPosition (m_row, m_col);
+
+ m_deletedText = QString::null;
+ int oldNumBackspaces = m_numBackspaces;
+ m_numBackspaces = 0;
+
+ for (int i = 0; i < oldNumBackspaces; i++)
+ addBackspace ();
+}
+
+// public virtual [base kpCommand]
+void kpToolTextBackspaceCommand::unexecute ()
+{
+ viewManager ()->setTextCursorPosition (m_row, m_col);
+
+ QValueVector <QString> textLines = selection ()->textLines ();
+
+ for (int i = 0; i < (int) m_deletedText.length (); i++)
+ {
+ if (m_deletedText [i] == '\n')
+ {
+ const QString rightHalf = textLines [m_row].mid (m_col);
+
+ textLines [m_row].truncate (m_col);
+ textLines.insert (textLines.begin () + m_row + 1, rightHalf);
+
+ m_row++;
+ m_col = 0;
+ }
+ else
+ {
+ const QString leftHalf = textLines [m_row].left (m_col);
+ const QString rightHalf = textLines [m_row].mid (m_col);
+
+ textLines [m_row] = leftHalf + m_deletedText [i] + rightHalf;
+ m_col++;
+ }
+ }
+
+ m_deletedText = QString::null;
+
+ selection ()->setTextLines (textLines);
+
+ viewManager ()->setTextCursorPosition (m_row, m_col);
+}
+
+
+/*
+ * kpToolTextDeleteCommand
+ */
+
+kpToolTextDeleteCommand::kpToolTextDeleteCommand (const QString &name,
+ int row, int col,
+ kpMainWindow *mainWindow)
+ : kpNamedCommand (name, mainWindow),
+ m_row (row), m_col (col),
+ m_numDeletes (0)
+{
+ viewManager ()->setTextCursorPosition (m_row, m_col);
+ addDelete ();
+}
+
+kpToolTextDeleteCommand::~kpToolTextDeleteCommand ()
+{
+}
+
+
+// public
+void kpToolTextDeleteCommand::addDelete ()
+{
+ QValueVector <QString> textLines = selection ()->textLines ();
+
+ if (m_col < (int) textLines [m_row].length ())
+ {
+ m_deletedText.prepend (textLines [m_row][m_col]);
+
+ textLines [m_row] = textLines [m_row].left (m_col) +
+ textLines [m_row].mid (m_col + 1);
+ }
+ else
+ {
+ if (m_row < (int) textLines.size () - 1)
+ {
+ m_deletedText.prepend ('\n');
+
+ textLines [m_row] += textLines [m_row + 1];
+ textLines.erase (textLines.begin () + m_row + 1);
+ }
+ }
+
+ selection ()->setTextLines (textLines);
+
+ viewManager ()->setTextCursorPosition (m_row, m_col);
+
+ m_numDeletes++;
+}
+
+
+// public virtual [base kpCommand]
+int kpToolTextDeleteCommand::size () const
+{
+ return m_deletedText.length () * sizeof (QChar);
+}
+
+
+// public virtual [base kpCommand]
+void kpToolTextDeleteCommand::execute ()
+{
+ viewManager ()->setTextCursorPosition (m_row, m_col);
+
+ m_deletedText = QString::null;
+ int oldNumDeletes = m_numDeletes;
+ m_numDeletes = 0;
+
+ for (int i = 0; i < oldNumDeletes; i++)
+ addDelete ();
+}
+
+// public virtual [base kpCommand]
+void kpToolTextDeleteCommand::unexecute ()
+{
+ viewManager ()->setTextCursorPosition (m_row, m_col);
+
+ QValueVector <QString> textLines = selection ()->textLines ();
+
+ for (int i = 0; i < (int) m_deletedText.length (); i++)
+ {
+ if (m_deletedText [i] == '\n')
+ {
+ const QString rightHalf = textLines [m_row].mid (m_col);
+
+ textLines [m_row].truncate (m_col);
+ textLines.insert (textLines.begin () + m_row + 1, rightHalf);
+ }
+ else
+ {
+ const QString leftHalf = textLines [m_row].left (m_col);
+ const QString rightHalf = textLines [m_row].mid (m_col);
+
+ textLines [m_row] = leftHalf + m_deletedText [i] + rightHalf;
+ }
+ }
+
+ m_deletedText = QString::null;
+
+ selection ()->setTextLines (textLines);
+
+ viewManager ()->setTextCursorPosition (m_row, m_col);
+}
+
+
+#include <kptooltext.moc>
diff --git a/kolourpaint/tools/kptooltext.h b/kolourpaint/tools/kptooltext.h
new file mode 100644
index 00000000..a99654b7
--- /dev/null
+++ b/kolourpaint/tools/kptooltext.h
@@ -0,0 +1,203 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kp_tool_text_h__
+#define __kp_tool_text_h__
+
+#include <qstring.h>
+
+#include <kpcommandhistory.h>
+
+#include <kptextstyle.h>
+#include <kptoolselection.h>
+
+class kpColor;
+class kpMainWindow;
+class kpSelection;
+class kpViewManager;
+
+class kpToolText : public kpToolSelection
+{
+Q_OBJECT
+
+public:
+ kpToolText (kpMainWindow *mainWindow);
+ virtual ~kpToolText ();
+
+ virtual bool careAboutColorsSwapped () const { return true; }
+
+ virtual void begin ();
+ virtual void end ();
+
+ bool hasBegunText () const;
+ virtual bool hasBegunShape () const;
+ virtual void cancelShape ();
+ virtual void endShape (const QPoint &thisPoint, const QRect &normalizedRect);
+
+protected:
+ virtual void keyPressEvent (QKeyEvent *e);
+ virtual void imStartEvent (QIMEvent *e);
+ virtual void imComposeEvent (QIMEvent *e);
+ virtual void imEndEvent (QIMEvent *e);
+
+protected:
+ bool shouldChangeTextStyle () const;
+ void changeTextStyle (const QString &name,
+ const kpTextStyle &newTextStyle,
+ const kpTextStyle &oldTextStyle);
+
+protected slots:
+ virtual void slotIsOpaqueChanged ();
+ virtual void slotColorsSwapped (const kpColor &newForegroundColor,
+ const kpColor &newBackgroundColor);
+ virtual void slotForegroundColorChanged (const kpColor &color);
+ virtual void slotBackgroundColorChanged (const kpColor &color);
+ virtual void slotColorSimilarityChanged (double, int);
+
+public slots:
+ void slotFontFamilyChanged (const QString &fontFamily, const QString &oldFontFamily);
+ void slotFontSizeChanged (int fontSize, int oldFontSize);
+ void slotBoldChanged (bool isBold);
+ void slotItalicChanged (bool isItalic);
+ void slotUnderlineChanged (bool isUnderline);
+ void slotStrikeThruChanged (bool isStrikeThru);
+
+protected:
+ class kpToolTextInsertCommand *m_insertCommand;
+ class kpToolTextEnterCommand *m_enterCommand;
+ class kpToolTextBackspaceCommand *m_backspaceCommand;
+ class kpToolTextDeleteCommand *m_deleteCommand;
+
+ bool m_isIMStarted;
+ int m_IMStartCursorRow;
+ int m_IMStartCursorCol;
+ QString m_IMPreeditStr;
+};
+
+
+class kpToolTextChangeStyleCommand : public kpNamedCommand
+{
+public:
+ kpToolTextChangeStyleCommand (const QString &name,
+ const kpTextStyle &newTextStyle, const kpTextStyle &oldTextStyle,
+ kpMainWindow *mainWindow);
+ virtual ~kpToolTextChangeStyleCommand ();
+
+ virtual int size () const;
+
+ virtual void execute ();
+ virtual void unexecute ();
+
+protected:
+ kpTextStyle m_newTextStyle, m_oldTextStyle;
+};
+
+class kpToolTextInsertCommand : public kpNamedCommand
+{
+public:
+ kpToolTextInsertCommand (const QString &name,
+ int row, int col, QString newText,
+ kpMainWindow *mainWindow);
+ virtual ~kpToolTextInsertCommand ();
+
+ void addText (const QString &moreText);
+
+ virtual int size () const;
+
+ virtual void execute ();
+ virtual void unexecute ();
+
+protected:
+ int m_row, m_col;
+ QString m_newText;
+};
+
+class kpToolTextEnterCommand : public kpNamedCommand
+{
+public:
+ kpToolTextEnterCommand (const QString &name,
+ int row, int col,
+ kpMainWindow *mainWindow);
+ virtual ~kpToolTextEnterCommand ();
+
+ void addEnter ();
+
+ virtual int size () const;
+
+ virtual void execute ();
+ virtual void unexecute ();
+
+protected:
+ int m_row, m_col;
+ int m_numEnters;
+};
+
+class kpToolTextBackspaceCommand : public kpNamedCommand
+{
+public:
+ kpToolTextBackspaceCommand (const QString &name,
+ int row, int col,
+ kpMainWindow *mainWindow);
+ virtual ~kpToolTextBackspaceCommand ();
+
+ void addBackspace ();
+
+ virtual int size () const;
+
+ virtual void execute ();
+ virtual void unexecute ();
+
+protected:
+ int m_row, m_col;
+ int m_numBackspaces;
+ QString m_deletedText;
+};
+
+class kpToolTextDeleteCommand : public kpNamedCommand
+{
+public:
+ kpToolTextDeleteCommand (const QString &name,
+ int row, int col,
+ kpMainWindow *mainWindow);
+ virtual ~kpToolTextDeleteCommand ();
+
+ void addDelete ();
+
+ virtual int size () const;
+
+ virtual void execute ();
+ virtual void unexecute ();
+
+protected:
+ int m_row, m_col;
+ int m_numDeletes;
+ QString m_deletedText;
+};
+
+#endif // __kp_tool_text_h__
+
diff --git a/kolourpaint/views/Makefile.am b/kolourpaint/views/Makefile.am
new file mode 100644
index 00000000..2d771cfc
--- /dev/null
+++ b/kolourpaint/views/Makefile.am
@@ -0,0 +1,14 @@
+INCLUDES = -I$(srcdir)/.. -I$(srcdir)/../cursors -I$(srcdir)/../interfaces \
+ -I$(srcdir)/../pixmapfx \
+ -I$(srcdir)/../tools \
+ -I$(srcdir)/../views \
+ -I$(srcdir)/../widgets $(all_includes)
+
+noinst_LTLIBRARIES = libkolourpaintviews.la
+libkolourpaintviews_la_SOURCES = kpthumbnailview.cpp \
+ kpunzoomedthumbnailview.cpp \
+ kpzoomedthumbnailview.cpp \
+ kpzoomedview.cpp
+
+METASOURCES = AUTO
+
diff --git a/kolourpaint/views/kpthumbnailview.cpp b/kolourpaint/views/kpthumbnailview.cpp
new file mode 100644
index 00000000..32c54376
--- /dev/null
+++ b/kolourpaint/views/kpthumbnailview.cpp
@@ -0,0 +1,98 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define DEBUG_KP_THUMBNAIL_VIEW 0
+
+
+#include <kpthumbnailview.h>
+
+#include <kdebug.h>
+
+
+kpThumbnailView::kpThumbnailView (kpDocument *document,
+ kpToolToolBar *toolToolBar,
+ kpViewManager *viewManager,
+ kpView *buddyView,
+ kpViewScrollableContainer *scrollableContainer,
+ QWidget *parent, const char *name)
+
+ : kpView (document, toolToolBar, viewManager,
+ buddyView,
+ scrollableContainer,
+ parent, name)
+{
+}
+
+kpThumbnailView::~kpThumbnailView ()
+{
+}
+
+
+// protected
+void kpThumbnailView::setMaskToCoverDocument ()
+{
+#if DEBUG_KP_THUMBNAIL_VIEW
+ kdDebug () << "kpThumbnailView::setMaskToCoverDocument()"
+ << " origin=" << origin ()
+ << " zoomedDoc: width=" << zoomedDocWidth ()
+ << " height=" << zoomedDocHeight ()
+ << endl;
+#endif
+
+ setMask (QRegion (QRect (origin ().x (), origin ().y (),
+ zoomedDocWidth (), zoomedDocHeight ())));
+}
+
+
+// protected virtual [base kpView]
+void kpThumbnailView::resizeEvent (QResizeEvent *e)
+{
+#if DEBUG_KP_THUMBNAIL_VIEW
+ kdDebug () << "kpThumbnailView(" << name () << ")::resizeEvent()"
+ << endl;
+#endif
+
+ // For QResizeEvent's, Qt already throws an entire widget repaint into
+ // the event loop. So eat useless update() calls that can only slow
+ // things down.
+ // TODO: this doesn't seem to work.
+ const bool oldIsUpdatesEnabled = isUpdatesEnabled ();
+ setUpdatesEnabled (false);
+
+ {
+ kpView::resizeEvent (e);
+
+ adjustToEnvironment ();
+ }
+
+ setUpdatesEnabled (oldIsUpdatesEnabled);
+}
+
+
+#include <kpthumbnailview.moc>
+
diff --git a/kolourpaint/views/kpthumbnailview.h b/kolourpaint/views/kpthumbnailview.h
new file mode 100644
index 00000000..c3420833
--- /dev/null
+++ b/kolourpaint/views/kpthumbnailview.h
@@ -0,0 +1,90 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef KP_THUMBNAIL_VIEW
+#define KP_THUMBNAIL_VIEW
+
+
+#include <kpview.h>
+
+
+/**
+ * @short Abstract base class for all thumbnail views.
+ *
+ * @author Clarence Dang <dang@kde.org>
+ */
+class kpThumbnailView : public kpView
+{
+Q_OBJECT
+
+public:
+ /**
+ * Constructs a thumbnail view.
+ *
+ * You must call adjustEnvironment() at the end of your constructor.
+ */
+ kpThumbnailView (kpDocument *document,
+ kpToolToolBar *toolToolBar,
+ kpViewManager *viewManager,
+ kpView *buddyView,
+ kpViewScrollableContainer *scrollableContainer,
+ QWidget *parent, const char *name);
+
+ /**
+ * Destructs this thumbnail view.
+ */
+ virtual ~kpThumbnailView ();
+
+
+ /**
+ * @returns the caption to display in an enclosing thumbnail window.
+ */
+ virtual QString caption () const = 0;
+
+
+protected:
+ /**
+ * Sets the mask to cover the rectangle with top-left, origin() and
+ * dimensions equal to or slightly less than (in case of rounding
+ * error) the size of the document in view coordinates. This ensures
+ * that all pixels are initialised with either document pixels or the
+ * standard widget background.
+ */
+ void setMaskToCoverDocument ();
+
+
+ /**
+ * Calls adjustToEnvironment() in response to a resize event.
+ *
+ * Extends @ref kpView.
+ */
+ virtual void resizeEvent (QResizeEvent *e);
+};
+
+
+#endif // KP_THUMBNAIL_VIEW
diff --git a/kolourpaint/views/kpunzoomedthumbnailview.cpp b/kolourpaint/views/kpunzoomedthumbnailview.cpp
new file mode 100644
index 00000000..09d5aed1
--- /dev/null
+++ b/kolourpaint/views/kpunzoomedthumbnailview.cpp
@@ -0,0 +1,212 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define DEBUG_KP_UNZOOMED_THUMBNAIL_VIEW 0
+
+
+#include <kpunzoomedthumbnailview.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kpdocument.h>
+#include <kpviewmanager.h>
+#include <kpviewscrollablecontainer.h>
+
+
+struct kpUnzoomedThumbnailViewPrivate
+{
+};
+
+
+kpUnzoomedThumbnailView::kpUnzoomedThumbnailView (
+ kpDocument *document,
+ kpToolToolBar *toolToolBar,
+ kpViewManager *viewManager,
+ kpView *buddyView,
+ kpViewScrollableContainer *scrollableContainer,
+ QWidget *parent, const char *name)
+
+ : kpThumbnailView (document, toolToolBar, viewManager,
+ buddyView,
+ scrollableContainer,
+ parent, name),
+ d (new kpUnzoomedThumbnailViewPrivate ())
+{
+ if (buddyViewScrollableContainer ())
+ {
+ connect (buddyViewScrollableContainer (),
+ SIGNAL (contentsMovingSoon (int, int)),
+ this,
+ SLOT (adjustToEnvironment ()));
+ }
+
+ // Call to virtual function - this is why the class is sealed
+ adjustToEnvironment ();
+}
+
+
+kpUnzoomedThumbnailView::~kpUnzoomedThumbnailView ()
+{
+ delete d;
+}
+
+
+// public virtual [base kpThumbnailView]
+QString kpUnzoomedThumbnailView::caption () const
+{
+ return i18n ("Unzoomed Mode - Thumbnail");
+}
+
+
+// public slot virtual [base kpView]
+void kpUnzoomedThumbnailView::adjustToEnvironment ()
+{
+ if (!buddyView () || !buddyViewScrollableContainer () || !document ())
+ return;
+
+ const int scrollViewContentsX =
+ buddyViewScrollableContainer ()->contentsXSoon ();
+ const int scrollViewContentsY =
+ buddyViewScrollableContainer ()->contentsYSoon ();
+
+#if DEBUG_KP_UNZOOMED_THUMBNAIL_VIEW
+ kdDebug () << "kpUnzoomedThumbnailView(" << name ()
+ << ")::adjustToEnvironment("
+ << scrollViewContentsX
+ << ","
+ << scrollViewContentsY
+ << ") width=" << width ()
+ << " height=" << height ()
+ << endl;
+#endif
+
+
+#if 1
+ int x;
+ if (document ()->width () > width ())
+ {
+ x = (int) buddyView ()->transformViewToDocX (scrollViewContentsX);
+ const int rightMostAllowedX = QMAX (0, document ()->width () - width ());
+ #if DEBUG_KP_UNZOOMED_THUMBNAIL_VIEW
+ kdDebug () << "\tdocX=" << x
+ << " docWidth=" << document ()->width ()
+ << " rightMostAllowedX=" << rightMostAllowedX
+ << endl;
+ #endif
+ if (x > rightMostAllowedX)
+ x = rightMostAllowedX;
+ }
+ // Thumbnail width <= doc width
+ else
+ {
+ // Centre X (rather than flush left to be consistent with
+ // kpZoomedThumbnailView)
+ x = -(width () - document ()->width ()) / 2;
+ }
+
+
+ int y;
+ if (document ()->height () > height ())
+ {
+ y = (int) buddyView ()->transformViewToDocY (scrollViewContentsY);
+ const int bottomMostAllowedY = QMAX (0, document ()->height () - height ());
+ #if DEBUG_KP_UNZOOMED_THUMBNAIL_VIEW
+ kdDebug () << "\tdocY=" << y
+ << " docHeight=" << document ()->height ()
+ << " bottomMostAllowedY=" << bottomMostAllowedY
+ << endl;
+ #endif
+ if (y > bottomMostAllowedY)
+ y = bottomMostAllowedY;
+ }
+ // Thumbnail height <= doc height
+ else
+ {
+ // Centre Y (rather than flush top to be consistent with
+ // kpZoomedThumbnailView)
+ y = -(height () - document ()->height ()) / 2;
+ }
+// Prefer to keep visible area centred in thumbnail instead of flushed left.
+// Gives more editing context to the left and top.
+// But feels awkward for left-to-right users. So disabled for now.
+// Not totally tested.
+#else
+ if (!buddyViewScrollableContainer ())
+ return;
+
+ QRect docRect = buddyView ()->transformViewToDoc (
+ QRect (buddyViewScrollableContainer ()->contentsXSoon (),
+ buddyViewScrollableContainer ()->contentsYSoon (),
+ QMIN (buddyView ()->width (), buddyViewScrollableContainer ()->visibleWidth ()),
+ QMIN (buddyView ()->height (), buddyViewScrollableContainer ()->visibleHeight ())));
+
+ x = docRect.x () - (width () - docRect.width ()) / 2;
+ kdDebug () << "\tnew suggest x=" << x << endl;
+ const int rightMostAllowedX = QMAX (0, document ()->width () - width ());
+ if (x < 0)
+ x = 0;
+ if (x > rightMostAllowedX)
+ x = rightMostAllowedX;
+
+ y = docRect.y () - (height () - docRect.height ()) / 2;
+ kdDebug () << "\tnew suggest y=" << y << endl;
+ const int bottomMostAllowedY = QMAX (0, document ()->height () - height ());
+ if (y < 0)
+ y = 0;
+ if (y > bottomMostAllowedY)
+ y = bottomMostAllowedY;
+#endif
+
+
+ if (viewManager ())
+ {
+ viewManager ()->setFastUpdates ();
+ viewManager ()->setQueueUpdates ();
+ }
+
+ {
+ // OPT: scrollView impl would be much, much faster
+ setOrigin (QPoint (-x, -y));
+ setMaskToCoverDocument ();
+
+ // Above might be a NOP even if e.g. doc size changed so force
+ // update
+ if (viewManager ())
+ viewManager ()->updateView (this);
+ }
+
+ if (viewManager ())
+ {
+ viewManager ()->restoreQueueUpdates ();
+ viewManager ()->restoreFastUpdates ();
+ }
+}
+
+
+#include <kpunzoomedthumbnailview.moc>
diff --git a/kolourpaint/views/kpunzoomedthumbnailview.h b/kolourpaint/views/kpunzoomedthumbnailview.h
new file mode 100644
index 00000000..0f7ccf53
--- /dev/null
+++ b/kolourpaint/views/kpunzoomedthumbnailview.h
@@ -0,0 +1,106 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef KP_UNZOOMED_THUMBNAIL_VIEW_H
+#define KP_UNZOOMED_THUMBNAIL_VIEW_H
+
+
+#include <kpthumbnailview.h>
+
+
+class kpViewScrollableContainer;
+
+
+/**
+ * @short Unzoomed thumbnail view of a document.
+ *
+ * This is an unzoomed thumbnail view of a document. Unlike
+ * @ref kpZoomedThumbnailView, it never changes the zoom level. And unlike
+ * @ref kpZoomedView, it never resizes itself. Instead, it changes its
+ * origin according to the main view's scrollable container so that the
+ * top-left most document pixel displayed in the scrollable container will
+ * be visible.
+ *
+ * Do not call setZoomLevel() nor setOrigin().
+ *
+ * This class is sealed. Do not derive from it.
+ *
+ * @author Clarence Dang <dang@kde.org>
+ */
+/*sealed*/ class kpUnzoomedThumbnailView : public kpThumbnailView
+{
+Q_OBJECT
+
+public:
+ /**
+ * Constructs an unzoomed thumbnail view.
+ */
+ kpUnzoomedThumbnailView (kpDocument *document,
+ kpToolToolBar *toolToolBar,
+ kpViewManager *viewManager,
+ kpView *buddyView,
+ kpViewScrollableContainer *scrollableContainer,
+ QWidget *parent, const char *name);
+
+ /**
+ * Destructs an unzoomed thumbnail view.
+ */
+ virtual ~kpUnzoomedThumbnailView ();
+
+
+ /**
+ * Implements @ref kpThumbnailView.
+ */
+ QString caption () const;
+
+
+public slots:
+ /**
+ * Changes its origin according to the main view's scrollable container
+ * so that the top-left most document pixel displayed in the scrollable
+ * container will be visible.
+ *
+ * It tries to maximise the used area of this view. Unused areas will
+ * be set to the widget background thanks to the mask.
+ *
+ * Call this if the size of the document changes.
+ * Already connected to buddyViewScrollableContainer()'s
+ * contentsMovingSoon(int,int) signal.
+ * Already called by @ref kpThumbnailView resizeEvent().
+ *
+ * Implements @ref kpView.
+ */
+ virtual void adjustToEnvironment ();
+
+
+private:
+ struct kpUnzoomedThumbnailViewPrivate *d;
+};
+
+
+#endif // KP_UNZOOMED_THUMBNAIL_VIEW_H
diff --git a/kolourpaint/views/kpzoomedthumbnailview.cpp b/kolourpaint/views/kpzoomedthumbnailview.cpp
new file mode 100644
index 00000000..ecbfd317
--- /dev/null
+++ b/kolourpaint/views/kpzoomedthumbnailview.cpp
@@ -0,0 +1,140 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define DEBUG_KP_ZOOMED_THUMBNAIL_VIEW 0
+
+
+#include <kpzoomedthumbnailview.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kpdocument.h>
+#include <kpviewmanager.h>
+
+
+kpZoomedThumbnailView::kpZoomedThumbnailView (kpDocument *document,
+ kpToolToolBar *toolToolBar,
+ kpViewManager *viewManager,
+ kpView *buddyView,
+ kpViewScrollableContainer *scrollableContainer,
+ QWidget *parent, const char *name)
+
+ : kpThumbnailView (document, toolToolBar, viewManager,
+ buddyView,
+ scrollableContainer,
+ parent, name)
+{
+ // Call to virtual function - this is why the class is sealed
+ adjustToEnvironment ();
+}
+
+
+kpZoomedThumbnailView::~kpZoomedThumbnailView ()
+{
+}
+
+
+// public virtual [base kpThumbnailView]
+QString kpZoomedThumbnailView::caption () const
+{
+ return i18n ("%1% - Thumbnail").arg (zoomLevelX ());
+}
+
+
+// public slot virtual [base kpView]
+void kpZoomedThumbnailView::adjustToEnvironment ()
+{
+#if DEBUG_KP_ZOOMED_THUMBNAIL_VIEW
+ kdDebug () << "kpZoomedThumbnailView(" << name ()
+ << ")::adjustToEnvironment()"
+ << " width=" << width ()
+ << " height=" << height ()
+ << endl;
+#endif
+
+ if (!document ())
+ return;
+
+#if DEBUG_KP_ZOOMED_THUMBNAIL_VIEW
+ kdDebug () << "\tdoc: width=" << document ()->width ()
+ << " height=" << document ()->height ()
+ << endl;
+#endif
+
+ if (document ()->width () <= 0 || document ()->height () <= 0)
+ {
+ kdError () << "kpZoomedThumbnailView::adjustToEnvironment() doc:"
+ << " width=" << document ()->width ()
+ << " height=" << document ()->height ()
+ << endl;
+ return;
+ }
+
+
+ int hzoom = QMAX (1, width () * 100 / document ()->width ());
+ int vzoom = QMAX (1, height () * 100 / document ()->height ());
+
+ // keep aspect ratio
+ if (hzoom < vzoom)
+ vzoom = hzoom;
+ else
+ hzoom = vzoom;
+
+#if DEBUG_KP_ZOOMED_THUMBNAIL_VIEW && 1
+ kdDebug () << "\tproposed zoom=" << hzoom << endl;
+#endif
+ if (hzoom > 100 || vzoom > 100)
+ {
+ #if DEBUG_KP_ZOOMED_THUMBNAIL_VIEW && 1
+ kdDebug () << "\twon't magnify - setting zoom to 100%" << endl;
+ #endif
+ hzoom = 100, vzoom = 100;
+ }
+
+
+ if (viewManager ())
+ viewManager ()->setQueueUpdates ();
+
+ {
+ setZoomLevel (hzoom, vzoom);
+
+ setOrigin (QPoint ((width () - zoomedDocWidth ()) / 2,
+ (height () - zoomedDocHeight ()) / 2));
+ setMaskToCoverDocument ();
+
+ if (viewManager ())
+ viewManager ()->updateView (this);
+ }
+
+ if (viewManager ())
+ viewManager ()->restoreQueueUpdates ();
+}
+
+
+#include <kpzoomedthumbnailview.moc>
diff --git a/kolourpaint/views/kpzoomedthumbnailview.h b/kolourpaint/views/kpzoomedthumbnailview.h
new file mode 100644
index 00000000..0bcb367c
--- /dev/null
+++ b/kolourpaint/views/kpzoomedthumbnailview.h
@@ -0,0 +1,95 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef KP_ZOOMED_THUMBNAIL_VIEW_H
+#define KP_ZOOMED_THUMBNAIL_VIEW_H
+
+
+#include <kpthumbnailview.h>
+
+
+/**
+ * @short Zoomed thumbnail view of a document.
+ *
+ * This is a zoomed thumbnail view of a document. Unlike @ref kpZoomedView,
+ * it never resizes itself. Instead, it changes its zoom level to
+ * accommodate the display of entire document in the view, while
+ * maintaining aspect.
+ *
+ * Do not call setZoomLevel() nor setOrigin().
+ *
+ * This class is sealed. Do not derive from it.
+ *
+ * @author Clarence Dang <dang@kde.org>
+ */
+/*sealed*/ class kpZoomedThumbnailView : public kpThumbnailView
+{
+Q_OBJECT
+
+public:
+ /**
+ * Constructs a zoomed thumbnail view.
+ */
+ kpZoomedThumbnailView (kpDocument *document,
+ kpToolToolBar *toolToolBar,
+ kpViewManager *viewManager,
+ kpView *buddyView,
+ kpViewScrollableContainer *scrollableContainer,
+ QWidget *parent, const char *name);
+
+ /**
+ * Destructs a zoomed thumbnail view.
+ */
+ virtual ~kpZoomedThumbnailView ();
+
+
+ /**
+ * Implements @ref kpThumbnailView.
+ */
+ QString caption () const;
+
+
+public slots:
+ /**
+ * Changes its zoom level to accommodate the display of entire document
+ * in the view. It maintains aspect by changing the origin and mask.
+ *
+ * Call this if the size of the document changes.
+ * Already called by @ref kpThumbnailView resizeEvent().
+ *
+ * Implements @ref kpView.
+ */
+ virtual void adjustToEnvironment ();
+
+
+private:
+ struct kpZoomedThumbnailViewPrivate *d;
+};
+
+
+#endif // KP_ZOOMED_THUMBNAIL_VIEW_H
diff --git a/kolourpaint/views/kpzoomedview.cpp b/kolourpaint/views/kpzoomedview.cpp
new file mode 100644
index 00000000..ef1d6981
--- /dev/null
+++ b/kolourpaint/views/kpzoomedview.cpp
@@ -0,0 +1,103 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define DEBUG_KP_ZOOMED_VIEW 0
+
+
+#include <kpzoomedview.h>
+
+#include <kdebug.h>
+
+#include <kpdocument.h>
+#include <kpview.h>
+#include <kpviewmanager.h>
+
+
+kpZoomedView::kpZoomedView (kpDocument *document,
+ kpToolToolBar *toolToolBar,
+ kpViewManager *viewManager,
+ kpView *buddyView,
+ kpViewScrollableContainer *scrollableContainer,
+ QWidget *parent, const char *name)
+
+ : kpView (document, toolToolBar, viewManager,
+ buddyView,
+ scrollableContainer,
+ parent, name)
+{
+ // Call to virtual function - this is why the class is sealed
+ adjustToEnvironment ();
+}
+
+kpZoomedView::~kpZoomedView ()
+{
+}
+
+
+// public virtual [base kpView]
+void kpZoomedView::setZoomLevel (int hzoom, int vzoom)
+{
+#if DEBUG_KP_ZOOMED_VIEW
+ kdDebug () << "kpZoomedView(" << name () << ")::setZoomLevel("
+ << hzoom << "," << vzoom << ")" << endl;
+#endif
+
+ if (viewManager ())
+ viewManager ()->setQueueUpdates ();
+
+ {
+ kpView::setZoomLevel (hzoom, vzoom);
+
+ adjustToEnvironment ();
+ }
+
+ if (viewManager ())
+ viewManager ()->restoreQueueUpdates ();
+}
+
+
+// public slot virtual [base kpView]
+void kpZoomedView::adjustToEnvironment ()
+{
+#if DEBUG_KP_ZOOMED_VIEW
+ kdDebug () << "kpZoomedView(" << name () << ")::adjustToEnvironment()"
+ << " doc: width=" << document ()->width ()
+ << " height=" << document ()->height ()
+ << endl;
+#endif
+
+ if (document ())
+ {
+ // TODO: use zoomedDocWidth() & zoomedDocHeight()?
+ resize ((int) transformDocToViewX (document ()->width ()),
+ (int) transformDocToViewY (document ()->height ()));
+ }
+}
+
+
+#include <kpzoomedview.moc>
diff --git a/kolourpaint/views/kpzoomedview.h b/kolourpaint/views/kpzoomedview.h
new file mode 100644
index 00000000..c3729282
--- /dev/null
+++ b/kolourpaint/views/kpzoomedview.h
@@ -0,0 +1,96 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef KP_ZOOMED_VIEW_H
+#define KP_ZOOMED_VIEW_H
+
+
+#include <kpview.h>
+
+
+/**
+ * @short Zoomed view of a document. Suitable as an ordinary editing view.
+ *
+ * This is a zoomed view of a document. It resizes according to the size
+ * of the document and the zoom level. Do not manually call resize() for
+ * this reason.
+ *
+ * It is suitable as an ordinary editing view.
+ *
+ * Do not call setOrigin().
+ *
+ * This class is sealed. Do not derive from it.
+ *
+ * @author Clarence Dang <dang@kde.org>
+ */
+/*sealed*/ class kpZoomedView : public kpView
+{
+Q_OBJECT
+
+public:
+ /**
+ * Constructs a zoomed view.
+ */
+ kpZoomedView (kpDocument *document,
+ kpToolToolBar *toolToolBar,
+ kpViewManager *viewManager,
+ kpView *buddyView,
+ kpViewScrollableContainer *scrollableContainer,
+ QWidget *parent, const char *name);
+
+ /**
+ * Destructs an unzoomed view.
+ */
+ virtual ~kpZoomedView ();
+
+
+ /**
+ * Extends @kpView. Calls adjustToEnvironment().
+ */
+ virtual void setZoomLevel (int hzoom, int vzoom);
+
+
+public slots:
+ /**
+ * Resizes itself so that the entire document in the zoom level fits
+ * almost perfectly.
+ *
+ * Call this if the size of the document changes.
+ * Already called by setZoomLevel().
+ *
+ * Implements @ref kpView.
+ */
+ virtual void adjustToEnvironment ();
+
+
+private:
+ struct kpZoomedViewPrivate *d;
+};
+
+
+#endif // KP_ZOOMED_VIEW_H
diff --git a/kolourpaint/widgets/Makefile.am b/kolourpaint/widgets/Makefile.am
new file mode 100644
index 00000000..f6501fac
--- /dev/null
+++ b/kolourpaint/widgets/Makefile.am
@@ -0,0 +1,21 @@
+INCLUDES = -I$(srcdir)/.. -I$(srcdir)/../cursors -I$(srcdir)/../interfaces \
+ -I$(srcdir)/../pixmapfx \
+ -I$(srcdir)/../tools \
+ -I$(srcdir)/../views \
+ -I$(srcdir)/../widgets $(all_includes)
+
+noinst_LTLIBRARIES = libkolourpaintwidgets.la
+libkolourpaintwidgets_la_SOURCES = kpcolorsimilaritycube.cpp \
+ kpcolorsimilaritydialog.cpp \
+ kpcolortoolbar.cpp \
+ kpresizesignallinglabel.cpp \
+ kpsqueezedtextlabel.cpp \
+ kptooltoolbar.cpp \
+ kptoolwidgetbase.cpp kptoolwidgetbrush.cpp \
+ kptoolwidgeterasersize.cpp kptoolwidgetfillstyle.cpp \
+ kptoolwidgetlinewidth.cpp \
+ kptoolwidgetopaqueortransparent.cpp \
+ kptoolwidgetspraycansize.cpp
+
+METASOURCES = AUTO
+
diff --git a/kolourpaint/widgets/kpcolorsimilaritycube.cpp b/kolourpaint/widgets/kpcolorsimilaritycube.cpp
new file mode 100644
index 00000000..9fe3f29b
--- /dev/null
+++ b/kolourpaint/widgets/kpcolorsimilaritycube.cpp
@@ -0,0 +1,348 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG_KP_COLOR_SIMILARITY_CUBE 0
+
+
+#include <kpcolorsimilaritycube.h>
+
+#include <math.h>
+
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qwhatsthis.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kpcolor.h>
+#include <kpcolorsimilaritydialog.h>
+#include <kpdefs.h>
+
+
+const double kpColorSimilarityCube::colorCubeDiagonalDistance =
+ sqrt (255 * 255 * 3);
+
+kpColorSimilarityCube::kpColorSimilarityCube (int look,
+ kpMainWindow *mainWindow,
+ QWidget *parent,
+ const char *name)
+ : QFrame (parent, name, Qt::WNoAutoErase/*no flicker*/),
+ m_mainWindow (mainWindow),
+ m_colorSimilarity (-1)
+{
+ if (look & Depressed)
+ setFrameStyle (QFrame::Panel | QFrame::Sunken);
+
+ setColorSimilarity (0);
+
+
+ // Don't cause the translators grief by appending strings
+ // - duplicate text with 2 cases
+
+ if (look & DoubleClickInstructions)
+ {
+ QWhatsThis::add (this,
+ i18n ("<qt><p><b>Color Similarity</b> is how close "
+ "colors must be in the RGB Color Cube "
+ "to be considered the same.</p>"
+
+ "<p>If you set it to something "
+ "other than <b>Exact</b>, "
+ "you can work more effectively with dithered "
+ "images and photos.</p>"
+
+ "<p>This feature applies to transparent selections, as well as "
+ "the Flood Fill, Color Eraser and Autocrop "
+ "tools.</p>"
+
+ // sync: different to else case
+ "<p>To configure it, double click on the cube.</p>"
+
+ "</qt>"));
+ }
+ else
+ {
+ QWhatsThis::add (this,
+ i18n ("<qt><p><b>Color Similarity</b> is how close "
+ "colors must be in the RGB Color Cube "
+ "to be considered the same.</p>"
+
+ "<p>If you set it to something "
+ "other than <b>Exact</b>, "
+ "you can work more effectively with dithered "
+ "images and photos.</p>"
+
+ "<p>This feature applies to transparent selections, as well as "
+ "the Flood Fill, Color Eraser and Autocrop "
+ "tools.</p>"
+
+ "</qt>"));
+ }
+}
+
+kpColorSimilarityCube::~kpColorSimilarityCube ()
+{
+}
+
+
+// public
+double kpColorSimilarityCube::colorSimilarity () const
+{
+ return m_colorSimilarity;
+}
+
+// public
+void kpColorSimilarityCube::setColorSimilarity (double similarity)
+{
+#if DEBUG_KP_COLOR_SIMILARITY_CUBE
+ kdDebug () << "kpColorSimilarityCube::setColorSimilarity(" << similarity << ")" << endl;
+#endif
+
+ if (m_colorSimilarity == similarity)
+ return;
+
+ if (similarity < 0)
+ similarity = 0;
+ else if (similarity > kpColorSimilarityDialog::maximumColorSimilarity)
+ similarity = kpColorSimilarityDialog::maximumColorSimilarity;
+
+ m_colorSimilarity = similarity;
+
+ repaint (false/*no erase*/);
+}
+
+
+// protected virtual [base QWidget]
+QSize kpColorSimilarityCube::sizeHint () const
+{
+ return QSize (52, 52);
+}
+
+
+// protected
+QColor kpColorSimilarityCube::color (int redOrGreenOrBlue,
+ int baseBrightness,
+ int similarityDirection) const
+{
+ int brightness = int (baseBrightness +
+ similarityDirection *
+ .5 * m_colorSimilarity * kpColorSimilarityCube::colorCubeDiagonalDistance);
+
+ if (brightness < 0)
+ brightness = 0;
+ else if (brightness > 255)
+ brightness = 255;
+
+ switch (redOrGreenOrBlue)
+ {
+ default:
+ case 0: return QColor (brightness, 0, 0);
+ case 1: return QColor (0, brightness, 0);
+ case 2: return QColor (0, 0, brightness);
+ }
+}
+
+static QPoint pointBetween (const QPoint &p, const QPoint &q)
+{
+ return QPoint ((p.x () + q.x ()) / 2, (p.y () + q.y ()) / 2);
+}
+
+static void drawQuadrant (QPainter *p,
+ const QColor &col,
+ const QPoint &p1, const QPoint &p2, const QPoint &p3,
+ const QPoint pointNotOnOutline)
+{
+ p->save ();
+
+
+ QPointArray points (4);
+ points [0] = p1;
+ points [1] = p2;
+ points [2] = p3;
+ points [3] = pointNotOnOutline;
+
+ p->setPen (col);
+ p->setBrush (col);
+ p->drawPolygon (points);
+
+
+ points.resize (3);
+
+ p->setPen (Qt::black);
+ p->setBrush (Qt::NoBrush);
+ p->drawPolyline (points);
+
+
+ p->restore ();
+}
+
+// protected
+void kpColorSimilarityCube::drawFace (QPainter *p,
+ int redOrGreenOrBlue,
+ const QPoint &tl, const QPoint &tr,
+ const QPoint &bl, const QPoint &br)
+{
+#if DEBUG_KP_COLOR_SIMILARITY_CUBE
+ kdDebug () << "kpColorSimilarityCube(RorGorB=" << redOrGreenOrBlue
+ << ",tl=" << tl
+ << ",tr=" << tr
+ << ",bl=" << bl
+ << ",br=" << br
+ << ")"
+ << endl;
+#endif
+
+ // tl --- tm --- tr
+ // | | |
+ // | | |
+ // ml --- mm --- mr
+ // | | |
+ // | | |
+ // bl --- bm --- br
+
+ const QPoint tm (::pointBetween (tl, tr));
+ const QPoint bm (::pointBetween (bl, br));
+
+ const QPoint ml (::pointBetween (tl, bl));
+ const QPoint mr (::pointBetween (tr, br));
+ const QPoint mm (::pointBetween (ml, mr));
+
+
+ const int baseBrightness = QMAX (127,
+ 255 - int (kpColorSimilarityDialog::maximumColorSimilarity *
+ kpColorSimilarityCube::colorCubeDiagonalDistance / 2));
+ QColor colors [2] =
+ {
+ color (redOrGreenOrBlue, baseBrightness, -1),
+ color (redOrGreenOrBlue, baseBrightness, +1)
+ };
+
+ if (!isEnabled ())
+ {
+ #if DEBUG_KP_COLOR_SIMILARITY_CUBE
+ kdDebug () << "\tnot enabled - making us grey" << endl;
+ #endif
+ colors [0] = colorGroup ().background ();
+ colors [1] = colorGroup ().background ();
+ }
+
+#if DEBUG_KP_COLOR_SIMILARITY_CUBE
+ kdDebug () << "\tmaxColorSimilarity=" << kpColorSimilarityDialog::maximumColorSimilarity
+ << " colorCubeDiagDist=" << kpColorSimilarityCube::colorCubeDiagonalDistance
+ << endl
+ << "\tbaseBrightness=" << baseBrightness
+ << " color[0]=" << ((colors [0].rgb () & RGB_MASK) >> ((2 - redOrGreenOrBlue) * 8))
+ << " color[1]=" << ((colors [1].rgb () & RGB_MASK) >> ((2 - redOrGreenOrBlue) * 8))
+ << endl;
+#endif
+
+
+ ::drawQuadrant (p, colors [0], tm, tl, ml, mm);
+ ::drawQuadrant (p, colors [1], tm, tr, mr, mm);
+ ::drawQuadrant (p, colors [1], ml, bl, bm, mm);
+ ::drawQuadrant (p, colors [0], bm, br, mr, mm);
+}
+
+// protected virtual [base QFrame]
+void kpColorSimilarityCube::drawContents (QPainter *p)
+{
+ QRect cr (contentsRect ());
+
+ QPixmap backBuffer (cr.width (), cr.height ());
+ backBuffer.fill (colorGroup ().background ());
+
+ QPainter backBufferPainter (&backBuffer);
+
+ int cubeRectSize = QMIN (cr.width () * 6 / 8, cr.height () * 6 / 8);
+ int dx = (cr.width () - cubeRectSize) / 2,
+ dy = (cr.height () - cubeRectSize) / 2;
+ backBufferPainter.translate (dx, dy);
+
+ //
+ // P------- Q --- ---
+ // / / | | |
+ // /A / | side |
+ // R-------S T --- cubeRectSize
+ // | | / / |
+ // S | | / side |
+ // U-------V --- ---
+ // |-------|
+ // side
+ // |-----------|
+ // cubeRectSize
+ //
+ //
+
+ const double angle = KP_DEGREES_TO_RADIANS (45);
+ // S + S sin A = cubeRectSize
+ // (1 + sin A) x S = cubeRectSize
+ const double side = double (cubeRectSize) / (1 + sin (angle));
+
+
+ const QPoint pointP ((int) (side * cos (angle)), 0);
+ const QPoint pointQ ((int) (side * cos (angle) + side), 0);
+ const QPoint pointR (0, (int) (side * sin (angle)));
+ const QPoint pointS ((int) (side), (int) (side * sin (angle)));
+ const QPoint pointU (0, (int) (side * sin (angle) + side));
+ const QPoint pointT ((int) (side + side * cos (angle)), (int) (side));
+ const QPoint pointV ((int) (side), (int) (side * sin (angle) + side));
+
+
+ // Top Face
+ drawFace (&backBufferPainter,
+ 0/*red*/,
+ pointP, pointQ,
+ pointR, pointS);
+
+
+ // Bottom Face
+ drawFace (&backBufferPainter,
+ 1/*green*/,
+ pointR, pointS,
+ pointU, pointV);
+
+
+ // Right Face
+ drawFace (&backBufferPainter,
+ 2/*blue*/,
+ pointS, pointQ,
+ pointV, pointT);
+
+
+#if 0
+ backBufferPainter.save ();
+ backBufferPainter.setPen (Qt::cyan);
+ backBufferPainter.drawRect (0, 0, cubeRectSize, cubeRectSize);
+ backBufferPainter.restore ();
+#endif
+
+
+ backBufferPainter.end ();
+
+ p->drawPixmap (cr, backBuffer);
+}
diff --git a/kolourpaint/widgets/kpcolorsimilaritycube.h b/kolourpaint/widgets/kpcolorsimilaritycube.h
new file mode 100644
index 00000000..358d4b3a
--- /dev/null
+++ b/kolourpaint/widgets/kpcolorsimilaritycube.h
@@ -0,0 +1,72 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kp_color_similarity_cube_h__
+#define __kp_color_similarity_cube_h__
+
+#include <qframe.h>
+
+class kpColor;
+class kpMainWindow;
+
+class kpColorSimilarityCube : public QFrame
+{
+public:
+ enum Look
+ {
+ Plain = 0,
+ Depressed = 1,
+ DoubleClickInstructions = 2
+ };
+
+ kpColorSimilarityCube (int look,
+ kpMainWindow *mainWindow,
+ QWidget *parent,
+ const char *name = 0);
+ virtual ~kpColorSimilarityCube ();
+
+ static const double colorCubeDiagonalDistance;
+
+ double colorSimilarity () const;
+ void setColorSimilarity (double similarity);
+
+ virtual QSize sizeHint () const;
+
+protected:
+ QColor color (int redOrGreenOrBlue, int baseBrightness, int similarityDirection) const;
+ void drawFace (QPainter *p,
+ int redOrGreenOrBlue,
+ const QPoint &tl, const QPoint &tr,
+ const QPoint &bl, const QPoint &br);
+ virtual void drawContents (QPainter *p);
+
+ kpMainWindow *m_mainWindow;
+ double m_colorSimilarity;
+};
+
+#endif // __kp_color_similarity_cube_h__
diff --git a/kolourpaint/widgets/kpcolorsimilaritydialog.cpp b/kolourpaint/widgets/kpcolorsimilaritydialog.cpp
new file mode 100644
index 00000000..d2766568
--- /dev/null
+++ b/kolourpaint/widgets/kpcolorsimilaritydialog.cpp
@@ -0,0 +1,123 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <kpcolorsimilaritydialog.h>
+
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpushbutton.h>
+
+#include <klocale.h>
+#include <knuminput.h>
+
+#include <kpcolorsimilaritycube.h>
+
+
+// public static
+const double kpColorSimilarityDialog::maximumColorSimilarity = .30;
+
+
+kpColorSimilarityDialog::kpColorSimilarityDialog (kpMainWindow *mainWindow,
+ QWidget *parent,
+ const char *name)
+ : KDialogBase (parent, name, true/*modal*/,
+ i18n ("Color Similarity"),
+ KDialogBase::Ok | KDialogBase::Cancel),
+ m_mainWindow (mainWindow)
+{
+ QWidget *baseWidget = new QWidget (this);
+ setMainWidget (baseWidget);
+
+
+ QGroupBox *cubeGroupBox = new QGroupBox (i18n ("Preview"), baseWidget);
+
+ m_colorSimilarityCube = new kpColorSimilarityCube (kpColorSimilarityCube::Plain,
+ mainWindow, cubeGroupBox);
+ m_colorSimilarityCube->setMinimumSize (240, 180);
+
+ QPushButton *updatePushButton = new QPushButton (i18n ("&Update"), cubeGroupBox);
+
+
+ QVBoxLayout *cubeLayout = new QVBoxLayout (cubeGroupBox, marginHint () * 2, spacingHint ());
+ cubeLayout->addWidget (m_colorSimilarityCube, 1/*stretch*/);
+ cubeLayout->addWidget (updatePushButton, 0/*stretch*/, Qt::AlignHCenter);
+
+
+ connect (updatePushButton, SIGNAL (clicked ()),
+ this, SLOT (slotColorSimilarityValueChanged ()));
+
+
+ QGroupBox *inputGroupBox = new QGroupBox (i18n ("RGB Color Cube Distance"), baseWidget);
+
+ m_colorSimilarityInput = new KIntNumInput (inputGroupBox);
+ m_colorSimilarityInput->setRange (0, int (kpColorSimilarityDialog::maximumColorSimilarity * 100 + .1/*don't floor below target int*/),
+ 5/*step*/, true/*slider*/);
+ m_colorSimilarityInput->setSuffix (i18n ("%"));
+ m_colorSimilarityInput->setSpecialValueText (i18n ("Exact Match"));
+
+
+ QVBoxLayout *inputLayout = new QVBoxLayout (inputGroupBox, marginHint () * 2, spacingHint ());
+ inputLayout->addWidget (m_colorSimilarityInput);
+
+
+ connect (m_colorSimilarityInput, SIGNAL (valueChanged (int)),
+ this, SLOT (slotColorSimilarityValueChanged ()));
+
+
+ QVBoxLayout *baseLayout = new QVBoxLayout (baseWidget, 0/*margin*/, spacingHint () * 2);
+ baseLayout->addWidget (cubeGroupBox, 1/*stretch*/);
+ baseLayout->addWidget (inputGroupBox);
+}
+
+kpColorSimilarityDialog::~kpColorSimilarityDialog ()
+{
+}
+
+
+// public
+double kpColorSimilarityDialog::colorSimilarity () const
+{
+ return m_colorSimilarityCube->colorSimilarity ();
+}
+
+// public
+void kpColorSimilarityDialog::setColorSimilarity (double similarity)
+{
+ m_colorSimilarityInput->setValue (qRound (similarity * 100));
+}
+
+
+// private slot
+void kpColorSimilarityDialog::slotColorSimilarityValueChanged ()
+{
+ m_colorSimilarityCube->setColorSimilarity (double (m_colorSimilarityInput->value ()) / 100);
+}
+
+
+#include <kpcolorsimilaritydialog.moc>
diff --git a/kolourpaint/widgets/kpcolorsimilaritydialog.h b/kolourpaint/widgets/kpcolorsimilaritydialog.h
new file mode 100644
index 00000000..fd70ecd0
--- /dev/null
+++ b/kolourpaint/widgets/kpcolorsimilaritydialog.h
@@ -0,0 +1,62 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef __kp_color_similarity_dialog_h__
+#define __kp_color_similarity_dialog_h__
+
+#include <kdialogbase.h>
+
+class KIntNumInput;
+
+class kpColorSimilarityCube;
+class kpMainWindow;
+
+class kpColorSimilarityDialog : public KDialogBase
+{
+Q_OBJECT
+
+public:
+ kpColorSimilarityDialog (kpMainWindow *mainWindow,
+ QWidget *parent,
+ const char *name = 0);
+ virtual ~kpColorSimilarityDialog ();
+
+ double colorSimilarity () const;
+ void setColorSimilarity (double similarity);
+
+ static const double maximumColorSimilarity;
+
+private slots:
+ void slotColorSimilarityValueChanged ();
+
+private:
+ kpMainWindow *m_mainWindow;
+ kpColorSimilarityCube *m_colorSimilarityCube;
+ KIntNumInput *m_colorSimilarityInput;
+};
+
+#endif // __kp_color_similarity_dialog_h__
diff --git a/kolourpaint/widgets/kpcolortoolbar.cpp b/kolourpaint/widgets/kpcolortoolbar.cpp
new file mode 100644
index 00000000..cba73b4f
--- /dev/null
+++ b/kolourpaint/widgets/kpcolortoolbar.cpp
@@ -0,0 +1,1112 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define DEBUG_KP_COLOR_TOOL_BAR 0
+
+
+#include <kpcolortoolbar.h>
+
+#include <qbitmap.h>
+#include <qdrawutil.h>
+#include <qframe.h>
+#include <qlayout.h>
+#include <qpainter.h>
+#include <qsize.h>
+#include <qtooltip.h>
+#include <qwidget.h>
+
+#include <kapplication.h>
+#include <kcolordialog.h>
+#include <kcolordrag.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <klocale.h>
+
+#include <kpcolorsimilaritydialog.h>
+#include <kpdefs.h>
+#include <kpmainwindow.h>
+#include <kppixmapfx.h>
+#include <kptool.h>
+#include <kpview.h>
+
+
+/*
+ * kpDualColorButton
+ */
+
+kpDualColorButton::kpDualColorButton (kpMainWindow *mainWindow,
+ QWidget *parent, const char *name)
+ : QFrame (parent, name, Qt::WNoAutoErase/*no flicker*/),
+ m_mainWindow (mainWindow),
+ m_backBuffer (0)
+{
+ setFrameStyle (QFrame::Panel | QFrame::Sunken);
+
+ m_color [0] = kpColor (0, 0, 0); // black
+ m_color [1] = kpColor (255, 255, 255); // white
+
+ setAcceptDrops (true);
+}
+
+kpDualColorButton::~kpDualColorButton ()
+{
+ delete m_backBuffer; m_backBuffer = 0;
+}
+
+
+kpColor kpDualColorButton::color (int which) const
+{
+ if (which < 0 || which > 1)
+ {
+ kdWarning () << "kpDualColorButton::color (" << which
+ << ") - out of range" << endl;
+ which = 0;
+ }
+
+ return m_color [which];
+}
+
+kpColor kpDualColorButton::foregroundColor () const
+{
+ return color (0);
+}
+
+kpColor kpDualColorButton::backgroundColor () const
+{
+ return color (1);
+}
+
+
+void kpDualColorButton::setColor (int which, const kpColor &color)
+{
+ if (which < 0 || which > 1)
+ {
+ kdWarning () << "kpDualColorButton::setColor (" << which
+ << ") - out of range" << endl;
+ which = 0;
+ }
+
+ if (m_color [which] == color)
+ return;
+
+ m_oldColor [which] = m_color [which];
+ m_color [which] = color;
+ update ();
+
+ if (which == 0)
+ emit foregroundColorChanged (color);
+ else
+ emit backgroundColorChanged (color);
+}
+
+void kpDualColorButton::setForegroundColor (const kpColor &color)
+{
+ setColor (0, color);
+}
+
+void kpDualColorButton::setBackgroundColor (const kpColor &color)
+{
+ setColor (1, color);
+}
+
+
+// public
+kpColor kpDualColorButton::oldForegroundColor () const
+{
+ return m_oldColor [0];
+}
+
+// public
+kpColor kpDualColorButton::oldBackgroundColor () const
+{
+ return m_oldColor [1];
+}
+
+
+// public virtual [base QWidget]
+QSize kpDualColorButton::sizeHint () const
+{
+ return QSize (52, 52);
+}
+
+
+// protected
+QRect kpDualColorButton::swapPixmapRect () const
+{
+ QPixmap swapPixmap = UserIcon ("colorbutton_swap_16x16");
+
+ return QRect (contentsRect ().width () - swapPixmap.width (),
+ 0,
+ swapPixmap.width (),
+ swapPixmap.height ());
+}
+
+// protected
+QRect kpDualColorButton::foregroundBackgroundRect () const
+{
+ QRect cr (contentsRect ());
+ return QRect (cr.width () / 8,
+ cr.height () / 8,
+ cr.width () * 6 / 8,
+ cr.height () * 6 / 8);
+}
+
+// protected
+QRect kpDualColorButton::foregroundRect () const
+{
+ QRect fbr (foregroundBackgroundRect ());
+ return QRect (fbr.x (),
+ fbr.y (),
+ fbr.width () * 3 / 4,
+ fbr.height () * 3 / 4);
+}
+
+// protected
+QRect kpDualColorButton::backgroundRect () const
+{
+ QRect fbr (foregroundBackgroundRect ());
+ return QRect (fbr.x () + fbr.width () / 4,
+ fbr.y () + fbr.height () / 4,
+ fbr.width () * 3 / 4,
+ fbr.height () * 3 / 4);
+}
+
+
+// TODO: drag a colour from this widget
+
+// protected virtual [base QWidget]
+void kpDualColorButton::dragMoveEvent (QDragMoveEvent *e)
+{
+ e->accept ((foregroundRect ().contains (e->pos ()) ||
+ backgroundRect ().contains (e->pos ())) &&
+ KColorDrag::canDecode (e));
+}
+
+// protected virtual [base QWidget]
+void kpDualColorButton::dropEvent (QDropEvent *e)
+{
+ QColor col;
+ KColorDrag::decode (e, col/*ref*/);
+
+ if (col.isValid ())
+ {
+ if (foregroundRect ().contains (e->pos ()))
+ setForegroundColor (kpColor (col.rgb ()));
+ else if (backgroundRect ().contains (e->pos ()))
+ setBackgroundColor (kpColor (col.rgb ()));
+ }
+}
+
+
+// protected virtual [base QWidget]
+void kpDualColorButton::mousePressEvent (QMouseEvent * /*e*/)
+{
+ // eat right-mouse click to prevent it from getting to the toolbar
+}
+
+// protected virtual [base QWidget]
+void kpDualColorButton::mouseDoubleClickEvent (QMouseEvent *e)
+{
+ int whichColor = -1;
+
+ if (foregroundRect ().contains (e->pos ()))
+ whichColor = 0;
+ else if (backgroundRect ().contains (e->pos ()))
+ whichColor = 1;
+
+ if (whichColor == 0 || whichColor == 1)
+ {
+ QColor col = Qt::black;
+ if (color (whichColor).isOpaque ())
+ col = color (whichColor).toQColor ();
+ else
+ {
+ // TODO: If you double-click on a transparent color and press OK, you get
+ // black, instead of the color staying as transparent.
+ //
+ // We should modify or fork KColorDialog to allow us to fix this.
+ //
+ // It would be wrong to stop the user from double-clicking on a
+ // transparent color as that would make the UI inconsistent, compared
+ // to opaque colors.
+ }
+
+ // TODO: parent
+ if (KColorDialog::getColor (col/*ref*/))
+ setColor (whichColor, kpColor (col.rgb ()));
+ }
+}
+
+// protected virtual [base QWidget]
+void kpDualColorButton::mouseReleaseEvent (QMouseEvent *e)
+{
+ if (swapPixmapRect ().contains (e->pos ()) &&
+ m_color [0] != m_color [1])
+ {
+ #if DEBUG_KP_COLOR_TOOL_BAR && 1
+ kdDebug () << "kpDualColorButton::mouseReleaseEvent() swap colors:" << endl;
+ #endif
+ m_oldColor [0] = m_color [0];
+ m_oldColor [1] = m_color [1];
+
+ kpColor temp = m_color [0];
+ m_color [0] = m_color [1];
+ m_color [1] = temp;
+
+ update ();
+
+ emit colorsSwapped (m_color [0], m_color [1]);
+ emit foregroundColorChanged (m_color [0]);
+ emit backgroundColorChanged (m_color [1]);
+ }
+}
+
+
+// protected virtual [base QFrame]
+void kpDualColorButton::drawContents (QPainter *p)
+{
+#if DEBUG_KP_COLOR_TOOL_BAR && 1
+ kdDebug () << "kpDualColorButton::draw() rect=" << rect ()
+ << " contentsRect=" << contentsRect ()
+ << endl;
+#endif
+
+ if (!m_backBuffer ||
+ m_backBuffer->width () != contentsRect ().width () ||
+ m_backBuffer->height () != contentsRect ().height ())
+ {
+ delete m_backBuffer;
+ m_backBuffer = new QPixmap (contentsRect ().width (), contentsRect ().height ());
+ }
+
+
+ QPainter backBufferPainter (m_backBuffer);
+
+ if (isEnabled () && m_mainWindow)
+ {
+ kpView::drawTransparentBackground (&backBufferPainter,
+ m_backBuffer->width (), m_backBuffer->height (),
+ m_backBuffer->rect (),
+ true/*preview*/);
+ }
+ else
+ {
+ backBufferPainter.fillRect (m_backBuffer->rect (),
+ colorGroup ().color (QColorGroup::Background));
+ }
+
+ QPixmap swapPixmap = UserIcon ("colorbutton_swap_16x16");
+ if (!isEnabled ())
+ {
+ // swapPixmap has a mask after all
+ swapPixmap.fill (colorGroup ().color (QColorGroup::Dark));
+ }
+ backBufferPainter.drawPixmap (swapPixmapRect ().topLeft (), swapPixmap);
+
+ // foreground patch must be drawn after background patch
+ // as it overlaps on top of background patch
+ QRect bgRect = backgroundRect ();
+ QRect bgRectInside = QRect (bgRect.x () + 2, bgRect.y () + 2,
+ bgRect.width () - 4, bgRect.height () - 4);
+ if (isEnabled ())
+ {
+ #if DEBUG_KP_COLOR_TOOL_BAR && 1
+ kdDebug () << "\tbackgroundColor=" << (int *) m_color [1].toQRgb ()
+ << endl;
+ #endif
+ if (m_color [1].isOpaque ())
+ backBufferPainter.fillRect (bgRectInside, m_color [1].toQColor ());
+ else
+ backBufferPainter.drawPixmap (bgRectInside, UserIcon ("color_transparent_26x26"));
+ }
+ else
+ backBufferPainter.fillRect (bgRectInside, colorGroup ().color (QColorGroup::Button));
+ qDrawShadePanel (&backBufferPainter, bgRect, colorGroup (),
+ false/*not sunken*/, 2/*lineWidth*/,
+ 0/*never fill*/);
+
+ QRect fgRect = foregroundRect ();
+ QRect fgRectInside = QRect (fgRect.x () + 2, fgRect.y () + 2,
+ fgRect.width () - 4, fgRect.height () - 4);
+ if (isEnabled ())
+ {
+ #if DEBUG_KP_COLOR_TOOL_BAR && 1
+ kdDebug () << "\tforegroundColor=" << (int *) m_color [0].toQRgb ()
+ << endl;
+ #endif
+ if (m_color [0].isOpaque ())
+ backBufferPainter.fillRect (fgRectInside, m_color [0].toQColor ());
+ else
+ backBufferPainter.drawPixmap (fgRectInside, UserIcon ("color_transparent_26x26"));
+ }
+ else
+ backBufferPainter.fillRect (fgRectInside, colorGroup ().color (QColorGroup::Button));
+ qDrawShadePanel (&backBufferPainter, fgRect, colorGroup (),
+ false/*not sunken*/, 2/*lineWidth*/,
+ 0/*never fill*/);
+
+ backBufferPainter.end ();
+
+ p->drawPixmap (contentsRect (), *m_backBuffer);
+}
+
+
+/*
+ * kpColorCells
+ */
+
+static inline int roundUp2 (int val)
+{
+ return val % 2 ? val + 1 : val;
+}
+
+static inline int btwn0_255 (int val)
+{
+ if (val < 0)
+ return 0;
+ else if (val > 255)
+ return 255;
+ else
+ return val;
+}
+
+enum
+{
+ blendDark = 25,
+ blendNormal = 50,
+ blendLight = 75,
+ blendAdd = 100
+};
+
+static QColor blend (const QColor &a, const QColor &b, int percent = blendNormal)
+{
+ return QColor (btwn0_255 (roundUp2 (a.red () + b.red ()) * percent / 100),
+ btwn0_255 (roundUp2 (a.green () + b.green ()) * percent / 100),
+ btwn0_255 (roundUp2 (a.blue () + b.blue ()) * percent / 100));
+}
+
+static QColor add (const QColor &a, const QColor &b)
+{
+ return blend (a, b, blendAdd);
+}
+
+
+
+
+
+//
+// make our own colors in case weird ones like "Qt::cyan"
+// (turquoise) get changed by Qt
+//
+
+// primary colors + B&W
+static QColor kpRed;
+static QColor kpGreen;
+static QColor kpBlue;
+static QColor kpBlack;
+static QColor kpWhite;
+
+// intentionally _not_ an HSV darkener
+static QColor dark (const QColor &color)
+{
+ return blend (color, kpBlack);
+}
+
+// full-brightness colors
+static QColor kpYellow;
+static QColor kpPurple;
+static QColor kpAqua;
+
+// mixed colors
+static QColor kpGrey;
+static QColor kpLightGrey;
+static QColor kpOrange;
+
+// pastel colors
+static QColor kpPink;
+static QColor kpLightGreen;
+static QColor kpLightBlue;
+static QColor kpTan;
+
+static bool ownColorsInitialised = false;
+
+/* TODO: clean up this code!!!
+ * (probably when adding palette load/save)
+ */
+#define rows 2
+#define cols 11
+kpColorCells::kpColorCells (QWidget *parent,
+ Qt::Orientation o,
+ const char *name)
+ : KColorCells (parent, rows, cols),
+ m_mouseButton (-1)
+{
+ setName (name);
+
+ setShading (false); // no 3D look
+
+ // Trap KColorDrag so that kpMainWindow does not trap it.
+ // See our impl of dropEvent().
+ setAcceptDrops (true);
+ setAcceptDrags (true);
+
+ connect (this, SIGNAL (colorDoubleClicked (int)),
+ SLOT (slotColorDoubleClicked (int)));
+
+ if (!ownColorsInitialised)
+ {
+ // Don't initialise globally when we probably don't have a colour
+ // allocation context. This way, the colours aren't sometimes
+ // invalid (e.g. at 8-bit).
+
+ kpRed = QColor (255, 0, 0);
+ kpGreen = QColor (0, 255, 0);
+ kpBlue = QColor (0, 0, 255);
+ kpBlack = QColor (0, 0, 0);
+ kpWhite = QColor (255, 255, 255);
+
+ kpYellow = add (kpRed, kpGreen);
+ kpPurple = add (kpRed, kpBlue);
+ kpAqua = add (kpGreen, kpBlue);
+
+ kpGrey = blend (kpBlack, kpWhite);
+ kpLightGrey = blend (kpGrey, kpWhite);
+ kpOrange = blend (kpRed, kpYellow);
+
+ kpPink = blend (kpRed, kpWhite);
+ kpLightGreen = blend (kpGreen, kpWhite);
+ kpLightBlue = blend (kpBlue, kpWhite);
+ kpTan = blend (kpYellow, kpWhite);
+
+ ownColorsInitialised = true;
+ }
+
+ setOrientation (o);
+}
+
+kpColorCells::~kpColorCells ()
+{
+}
+
+Qt::Orientation kpColorCells::orientation () const
+{
+ return m_orientation;
+}
+
+void kpColorCells::setOrientation (Qt::Orientation o)
+{
+ int c, r;
+
+ if (o == Qt::Horizontal)
+ {
+ c = cols;
+ r = rows;
+ }
+ else
+ {
+ c = rows;
+ r = cols;
+ }
+
+#if DEBUG_KP_COLOR_TOOL_BAR
+ kdDebug () << "kpColorCells::setOrientation(): r=" << r << " c=" << c << endl;
+#endif
+
+ setNumRows (r);
+ setNumCols (c);
+
+ setCellWidth (26);
+ setCellHeight (26);
+
+ setFixedSize (numCols () * cellWidth () + frameWidth () * 2,
+ numRows () * cellHeight () + frameWidth () * 2);
+
+/*
+ kdDebug () << "\tlimits: array=" << sizeof (colors) / sizeof (colors [0])
+ << " r*c=" << r * c << endl;
+ kdDebug () << "\tsizeof (colors)=" << sizeof (colors)
+ << " sizeof (colors [0])=" << sizeof (colors [0])
+ << endl;*/
+ QColor colors [] =
+ {
+ kpBlack,
+ kpGrey,
+ kpRed,
+ kpOrange,
+ kpYellow,
+ kpGreen,
+ kpAqua,
+ kpBlue,
+ kpPurple,
+ kpPink,
+ kpLightGreen,
+
+ kpWhite,
+ kpLightGrey,
+ dark (kpRed),
+ dark (kpOrange)/*brown*/,
+ dark (kpYellow),
+ dark (kpGreen),
+ dark (kpAqua),
+ dark (kpBlue),
+ dark (kpPurple),
+ kpLightBlue,
+ kpTan
+ };
+
+ for (int i = 0;
+ /*i < int (sizeof (colors) / sizeof (colors [0])) &&*/
+ i < r * c;
+ i++)
+ {
+ int y, x;
+ int pos;
+
+ if (o == Qt::Horizontal)
+ {
+ y = i / cols;
+ x = i % cols;
+ pos = i;
+ }
+ else
+ {
+ y = i % cols;
+ x = i / cols;
+ // int x = rows - 1 - i / cols;
+ pos = y * rows + x;
+ }
+
+ KColorCells::setColor (pos, colors [i]);
+ //QToolTip::add (this, cellGeometry (y, x), colors [i].name ());
+ }
+
+ m_orientation = o;
+}
+
+// virtual protected [base KColorCells]
+void kpColorCells::dropEvent (QDropEvent *e)
+{
+ // Eat event so that:
+ //
+ // 1. User doesn't clobber the palette (until we support reconfigurable
+ // palettes)
+ // 2. kpMainWindow::dropEvent() doesn't try to paste colour code as text
+ // (when the user slips and drags colour cell a little instead of clicking)
+ e->accept ();
+}
+
+// virtual protected
+void kpColorCells::paintCell (QPainter *painter, int row, int col)
+{
+ QColor oldColor;
+ int cellNo;
+
+ if (!isEnabled ())
+ {
+ cellNo = row * numCols () + col;
+
+ // make all cells 3D (so that disabled palette doesn't look flat)
+ setShading (true);
+
+ oldColor = KColorCells::color (cellNo);
+ KColorCells::colors [cellNo] = backgroundColor ();
+ }
+
+
+ // no focus rect as it doesn't make sense
+ // since 2 colors (foreground & background) can be selected
+ KColorCells::selected = -1;
+ KColorCells::paintCell (painter, row, col);
+
+
+ if (!isEnabled ())
+ {
+ KColorCells::colors [cellNo] = oldColor;
+ setShading (false);
+ }
+}
+
+// virtual protected
+void kpColorCells::mouseReleaseEvent (QMouseEvent *e)
+{
+ m_mouseButton = -1;
+
+ Qt::ButtonState button = e->button ();
+#if DEBUG_KP_COLOR_TOOL_BAR
+ kdDebug () << "kpColorCells::mouseReleaseEvent(left="
+ << (button & Qt::LeftButton)
+ << ",right="
+ << (button & Qt::RightButton)
+ << ")"
+ << endl;
+#endif
+ if (!((button & Qt::LeftButton) && (button & Qt::RightButton)))
+ {
+ if (button & Qt::LeftButton)
+ m_mouseButton = 0;
+ else if (button & Qt::RightButton)
+ m_mouseButton = 1;
+ }
+
+ connect (this, SIGNAL (colorSelected (int)), this, SLOT (slotColorSelected (int)));
+ KColorCells::mouseReleaseEvent (e);
+ disconnect (this, SIGNAL (colorSelected (int)), this, SLOT (slotColorSelected (int)));
+
+#if DEBUG_KP_COLOR_TOOL_BAR
+ kdDebug () << "kpColorCells::mouseReleaseEvent() setting m_mouseButton back to -1" << endl;
+#endif
+ m_mouseButton = -1;
+}
+
+// protected virtual [base KColorCells]
+void kpColorCells::resizeEvent (QResizeEvent *e)
+{
+ // KColorCells::resizeEvent() tries to adjust the cellWidth and cellHeight
+ // to the current dimensions but doesn't take into account
+ // frame{Width,Height}().
+ //
+ // In any case, we already set the cell{Width,Height} and a fixed
+ // widget size and don't want any of it changed. Eat the resize event.
+ (void) e;
+}
+
+// protected slot
+void kpColorCells::slotColorSelected (int cell)
+{
+#if DEBUG_KP_COLOR_TOOL_BAR
+ kdDebug () << "kpColorCells::slotColorSelected(cell=" << cell
+ << ") mouseButton = " << m_mouseButton << endl;
+#endif
+ QColor c = KColorCells::color (cell);
+
+ if (m_mouseButton == 0)
+ {
+ emit foregroundColorChanged (c);
+ emit foregroundColorChanged (kpColor (c.rgb ()));
+ }
+ else if (m_mouseButton == 1)
+ {
+ emit backgroundColorChanged (c);
+ emit backgroundColorChanged (kpColor (c.rgb ()));
+ }
+
+ m_mouseButton = -1; // just in case
+}
+
+// protected slot
+void kpColorCells::slotColorDoubleClicked (int cell)
+{
+#if DEBUG_KP_COLOR_TOOL_BAR
+ kdDebug () << "kpColorCells::slotColorDoubleClicked(cell="
+ << cell << ")" << endl;
+#endif
+
+ QColor color = KColorCells::color (cell);
+
+ // TODO: parent
+ if (KColorDialog::getColor (color/*ref*/))
+ KColorCells::setColor (cell, color);
+}
+
+
+/*
+ * kpTransparentColorCell
+ */
+
+kpTransparentColorCell::kpTransparentColorCell (QWidget *parent, const char *name)
+ : QFrame (parent, name)
+{
+#if DEBUG_KP_COLOR_TOOL_BAR
+ kdDebug () << "kpTransparentColorCell::kpTransparentColorCell()" << endl;
+#endif
+
+ setFrameStyle (QFrame::Panel | QFrame::Sunken);
+#if DEBUG_KP_COLOR_TOOL_BAR && 0
+ kdDebug () << "\tdefault line width=" << lineWidth ()
+ << " frame width=" << frameWidth () << endl;
+#endif
+ //setLineWidth (2);
+#if DEBUG_KP_COLOR_TOOL_BAR && 0
+ kdDebug () << "\tline width=" << lineWidth ()
+ << " frame width=" << frameWidth () << endl;
+#endif
+
+ m_pixmap = UserIcon ("color_transparent_26x26");
+
+ QToolTip::add (this, i18n ("Transparent"));
+}
+
+kpTransparentColorCell::~kpTransparentColorCell ()
+{
+}
+
+
+// public virtual [base QWidget]
+QSize kpTransparentColorCell::sizeHint () const
+{
+ return QSize (m_pixmap.width () + frameWidth () * 2,
+ m_pixmap.height () + frameWidth () * 2);
+}
+
+// protected virtual [base QWidget]
+void kpTransparentColorCell::mousePressEvent (QMouseEvent * /*e*/)
+{
+ // eat right-mouse click to prevent it from getting to the toolbar
+}
+
+// protected virtual [base QWidget]
+void kpTransparentColorCell::mouseReleaseEvent (QMouseEvent *e)
+{
+ if (rect ().contains (e->pos ()))
+ {
+ if (e->button () == Qt::LeftButton)
+ {
+ emit transparentColorSelected (0);
+ emit foregroundColorChanged (kpColor::transparent);
+ }
+ else if (e->button () == Qt::RightButton)
+ {
+ emit transparentColorSelected (1);
+ emit backgroundColorChanged (kpColor::transparent);
+ }
+ }
+}
+
+// protected virtual [base QFrame]
+void kpTransparentColorCell::drawContents (QPainter *p)
+{
+ QFrame::drawContents (p);
+ if (isEnabled ())
+ {
+ #if DEBUG_KP_COLOR_TOOL_BAR
+ kdDebug () << "kpTransparentColorCell::drawContents() contentsRect="
+ << contentsRect ()
+ << endl;
+ #endif
+ p->drawPixmap (contentsRect (), m_pixmap);
+ }
+}
+
+
+/*
+ * kpColorPalette
+ */
+
+kpColorPalette::kpColorPalette (QWidget *parent,
+ Qt::Orientation o,
+ const char *name)
+ : QWidget (parent, name),
+ m_boxLayout (0)
+{
+#if DEBUG_KP_COLOR_TOOL_BAR
+ kdDebug () << "kpColorPalette::kpColorPalette()" << endl;
+#endif
+
+ m_transparentColorCell = new kpTransparentColorCell (this);
+ m_transparentColorCell->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed);
+ connect (m_transparentColorCell, SIGNAL (foregroundColorChanged (const kpColor &)),
+ this, SIGNAL (foregroundColorChanged (const kpColor &)));
+ connect (m_transparentColorCell, SIGNAL (backgroundColorChanged (const kpColor &)),
+ this, SIGNAL (backgroundColorChanged (const kpColor &)));
+
+ m_colorCells = new kpColorCells (this);
+ connect (m_colorCells, SIGNAL (foregroundColorChanged (const kpColor &)),
+ this, SIGNAL (foregroundColorChanged (const kpColor &)));
+ connect (m_colorCells, SIGNAL (backgroundColorChanged (const kpColor &)),
+ this, SIGNAL (backgroundColorChanged (const kpColor &)));
+
+ setOrientation (o);
+}
+
+kpColorPalette::~kpColorPalette ()
+{
+}
+
+// public
+Qt::Orientation kpColorPalette::orientation () const
+{
+ return m_orientation;
+}
+
+void kpColorPalette::setOrientation (Qt::Orientation o)
+{
+ m_colorCells->setOrientation (o);
+
+ delete m_boxLayout;
+
+ if (o == Qt::Horizontal)
+ {
+ m_boxLayout = new QBoxLayout (this, QBoxLayout::LeftToRight, 0/*margin*/, 5/*spacing*/);
+ m_boxLayout->addWidget (m_transparentColorCell, 0/*stretch*/, Qt::AlignVCenter);
+ m_boxLayout->addWidget (m_colorCells);
+ }
+ else
+ {
+ m_boxLayout = new QBoxLayout (this, QBoxLayout::TopToBottom, 0/*margin*/, 5/*spacing*/);
+ m_boxLayout->addWidget (m_transparentColorCell, 0/*stretch*/, Qt::AlignHCenter);
+ m_boxLayout->addWidget (m_colorCells);
+ }
+
+ m_orientation = o;
+}
+
+
+/*
+ * kpColorSimilarityToolBarItem
+ */
+
+kpColorSimilarityToolBarItem::kpColorSimilarityToolBarItem (kpMainWindow *mainWindow,
+ QWidget *parent,
+ const char *name)
+ : kpColorSimilarityCube (kpColorSimilarityCube::Depressed |
+ kpColorSimilarityCube::DoubleClickInstructions,
+ mainWindow, parent, name),
+ m_mainWindow (mainWindow),
+ m_processedColorSimilarity (kpColor::Exact)
+{
+ setColorSimilarity (mainWindow->configColorSimilarity ());
+}
+
+kpColorSimilarityToolBarItem::~kpColorSimilarityToolBarItem ()
+{
+}
+
+
+// public
+int kpColorSimilarityToolBarItem::processedColorSimilarity () const
+{
+ return m_processedColorSimilarity;
+}
+
+
+// public slot
+void kpColorSimilarityToolBarItem::setColorSimilarity (double similarity)
+{
+ m_oldColorSimilarity = colorSimilarity ();
+
+ kpColorSimilarityCube::setColorSimilarity (similarity);
+ if (similarity > 0)
+ QToolTip::add (this, i18n ("Color similarity: %1%").arg (qRound (similarity * 100)));
+ else
+ QToolTip::add (this, i18n ("Color similarity: Exact"));
+
+ m_processedColorSimilarity = kpColor::processSimilarity (colorSimilarity ());
+
+ m_mainWindow->configSetColorSimilarity (colorSimilarity ());
+
+ emit colorSimilarityChanged (colorSimilarity (), m_processedColorSimilarity);
+}
+
+// public
+double kpColorSimilarityToolBarItem::oldColorSimilarity () const
+{
+ return m_oldColorSimilarity;
+}
+
+
+// private virtual [base QWidget]
+void kpColorSimilarityToolBarItem::mousePressEvent (QMouseEvent * /*e*/)
+{
+ // eat right-mouse click to prevent it from getting to the toolbar
+}
+
+// private virtual [base QWidget]
+void kpColorSimilarityToolBarItem::mouseDoubleClickEvent (QMouseEvent * /*e*/)
+{
+ kpColorSimilarityDialog dialog (m_mainWindow, this);
+ dialog.setColorSimilarity (colorSimilarity ());
+ if (dialog.exec ())
+ {
+ setColorSimilarity (dialog.colorSimilarity ());
+ }
+}
+
+
+/*
+ * kpColorToolBar
+ */
+
+kpColorToolBar::kpColorToolBar (const QString &label, kpMainWindow *mainWindow, const char *name)
+ : KToolBar (mainWindow, name),
+ m_mainWindow (mainWindow)
+{
+ setText (label);
+
+
+ QWidget *base = new QWidget (this);
+ m_boxLayout = new QBoxLayout (base, QBoxLayout::LeftToRight,
+ 5/*margin*/, (10 * 4)/*spacing*/);
+
+ m_dualColorButton = new kpDualColorButton (mainWindow, base);
+ m_dualColorButton->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed);
+ connect (m_dualColorButton, SIGNAL (colorsSwapped (const kpColor &, const kpColor &)),
+ this, SIGNAL (colorsSwapped (const kpColor &, const kpColor &)));
+ connect (m_dualColorButton, SIGNAL (foregroundColorChanged (const kpColor &)),
+ this, SIGNAL (foregroundColorChanged (const kpColor &)));
+ connect (m_dualColorButton, SIGNAL (backgroundColorChanged (const kpColor &)),
+ this, SIGNAL (backgroundColorChanged (const kpColor &)));
+ m_boxLayout->addWidget (m_dualColorButton, 0/*stretch*/);
+
+ m_colorPalette = new kpColorPalette (base);
+ connect (m_colorPalette, SIGNAL (foregroundColorChanged (const kpColor &)),
+ m_dualColorButton, SLOT (setForegroundColor (const kpColor &)));
+ connect (m_colorPalette, SIGNAL (backgroundColorChanged (const kpColor &)),
+ m_dualColorButton, SLOT (setBackgroundColor (const kpColor &)));
+ m_boxLayout->addWidget (m_colorPalette, 0/*stretch*/);
+
+ m_colorSimilarityToolBarItem = new kpColorSimilarityToolBarItem (mainWindow, base);
+ m_colorSimilarityToolBarItem->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed);
+ connect (m_colorSimilarityToolBarItem, SIGNAL (colorSimilarityChanged (double, int)),
+ this, SIGNAL (colorSimilarityChanged (double, int)));
+ m_boxLayout->addWidget (m_colorSimilarityToolBarItem, 0/*stretch*/);
+
+ // HACK: couldn't get QSpacerItem to work
+ QWidget *fakeSpacer = new QWidget (base);
+ m_boxLayout->addWidget (fakeSpacer, 1/*stretch*/);
+
+ m_lastDockedOrientationSet = false;
+ setOrientation (orientation ());
+
+ KToolBar::insertWidget (0, base->width (), base);
+}
+
+// virtual
+void kpColorToolBar::setOrientation (Qt::Orientation o)
+{
+ // (QDockWindow::undock() calls us)
+ bool isOutsideDock = (place () == QDockWindow::OutsideDock);
+
+ if (!m_lastDockedOrientationSet || !isOutsideDock)
+ {
+ m_lastDockedOrientation = o;
+ m_lastDockedOrientationSet = true;
+ }
+
+ if (isOutsideDock)
+ {
+ //kdDebug () << "\toutside dock, forcing orientation to last" << endl;
+ o = m_lastDockedOrientation;
+ }
+
+ if (o == Qt::Horizontal)
+ {
+ m_boxLayout->setDirection (QBoxLayout::LeftToRight);
+ }
+ else
+ {
+ m_boxLayout->setDirection (QBoxLayout::TopToBottom);
+ }
+
+ m_colorPalette->setOrientation (o);
+
+ KToolBar::setOrientation (o);
+}
+
+kpColorToolBar::~kpColorToolBar ()
+{
+}
+
+kpColor kpColorToolBar::color (int which) const
+{
+ if (which < 0 || which > 1)
+ {
+ kdWarning () << "kpColorToolBar::color (" << which
+ << ") - out of range" << endl;
+ which = 0;
+ }
+
+ return m_dualColorButton->color (which);
+}
+
+void kpColorToolBar::setColor (int which, const kpColor &color)
+{
+ if (which < 0 || which > 1)
+ {
+ kdWarning () << "kpColorToolBar::setColor (" << which
+ << ") - out of range" << endl;
+ which = 0;
+ }
+
+ m_dualColorButton->setColor (which, color);
+}
+
+kpColor kpColorToolBar::foregroundColor () const
+{
+ return m_dualColorButton->foregroundColor ();
+}
+
+void kpColorToolBar::setForegroundColor (const kpColor &color)
+{
+ m_dualColorButton->setForegroundColor (color);
+}
+
+kpColor kpColorToolBar::backgroundColor () const
+{
+ return m_dualColorButton->backgroundColor ();
+}
+
+void kpColorToolBar::setBackgroundColor (const kpColor &color)
+{
+ m_dualColorButton->setBackgroundColor (color);
+}
+
+
+kpColor kpColorToolBar::oldForegroundColor () const
+{
+ return m_dualColorButton->oldForegroundColor ();
+}
+
+kpColor kpColorToolBar::oldBackgroundColor () const
+{
+ return m_dualColorButton->oldBackgroundColor ();
+}
+
+double kpColorToolBar::oldColorSimilarity () const
+{
+ return m_colorSimilarityToolBarItem->oldColorSimilarity ();
+}
+
+
+double kpColorToolBar::colorSimilarity () const
+{
+ return m_colorSimilarityToolBarItem->colorSimilarity ();
+}
+
+void kpColorToolBar::setColorSimilarity (double similarity)
+{
+ m_colorSimilarityToolBarItem->setColorSimilarity (similarity);
+}
+
+int kpColorToolBar::processedColorSimilarity () const
+{
+ return m_colorSimilarityToolBarItem->processedColorSimilarity ();
+}
+
+
+#include <kpcolortoolbar.moc>
diff --git a/kolourpaint/widgets/kpcolortoolbar.h b/kolourpaint/widgets/kpcolortoolbar.h
new file mode 100644
index 00000000..b4a77bfb
--- /dev/null
+++ b/kolourpaint/widgets/kpcolortoolbar.h
@@ -0,0 +1,297 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kp_color_toolbar_h__
+#define __kp_color_toolbar_h__
+
+
+#include <qframe.h>
+#include <qwidget.h>
+
+#include <kcolordialog.h>
+#include <ktoolbar.h>
+
+#include <kpcolor.h>
+#include <kpcolorsimilaritycube.h>
+
+
+class QGridLayout;
+class KColorButton;
+
+class kpColorSimilarityCube;
+class kpMainWindow;
+
+
+//
+// Widget similar to KDualColorButton.
+// Main differences:
+// - more consistent feel with other KolourPaint widgets
+// (esp. kpColorPalette)
+// - displays the transparent colour using the special pixmap
+// used by kpTransparentColorCell
+// - no obscure "current" colour
+//
+class kpDualColorButton : public QFrame
+{
+Q_OBJECT
+
+public:
+ kpDualColorButton (kpMainWindow *mainWindow,
+ QWidget *parent, const char *name = 0);
+ virtual ~kpDualColorButton ();
+
+ kpColor color (int which) const;
+ kpColor foregroundColor () const;
+ kpColor backgroundColor () const;
+
+public slots:
+ void setColor (int which, const kpColor &color);
+ void setForegroundColor (const kpColor &color);
+ void setBackgroundColor (const kpColor &color);
+
+signals:
+ // If you connect to this signal, ignore the following
+ // foregroundColorChanged() and backgroundColorChanged() signals
+ void colorsSwapped (const kpColor &newForegroundColor,
+ const kpColor &newBackgroundColor);
+
+ void foregroundColorChanged (const kpColor &color);
+ void backgroundColorChanged (const kpColor &color);
+
+public:
+ // (only valid in slots connected to foregroundColorChanged())
+ kpColor oldForegroundColor () const;
+ // (only valid in slots connected to backgroundColorChanged())
+ kpColor oldBackgroundColor () const;
+
+public:
+ virtual QSize sizeHint () const;
+
+protected:
+ QRect swapPixmapRect () const;
+ QRect foregroundBackgroundRect () const;
+ QRect foregroundRect () const;
+ QRect backgroundRect () const;
+
+ //virtual void dragEnterEvent (QDragEnterEvent *e);
+ virtual void dragMoveEvent (QDragMoveEvent *e);
+ virtual void dropEvent (QDropEvent *e);
+
+ virtual void mousePressEvent (QMouseEvent *e);
+ virtual void mouseDoubleClickEvent (QMouseEvent *e);
+ virtual void mouseReleaseEvent (QMouseEvent *e);
+
+ virtual void drawContents (QPainter *p);
+
+ kpMainWindow *m_mainWindow;
+ kpColor m_color [2];
+ kpColor m_oldColor [2];
+ QPixmap *m_backBuffer;
+};
+
+
+class kpColorCells : public KColorCells
+{
+Q_OBJECT
+
+public:
+ kpColorCells (QWidget *parent,
+ Qt::Orientation o = Qt::Horizontal,
+ const char *name = 0);
+ virtual ~kpColorCells ();
+
+ Qt::Orientation orientation () const;
+ void setOrientation (Qt::Orientation o);
+
+signals:
+ void foregroundColorChanged (const QColor &color);
+ void backgroundColorChanged (const QColor &color);
+
+ // lazy
+ void foregroundColorChanged (const kpColor &color);
+ void backgroundColorChanged (const kpColor &color);
+
+protected:
+ Qt::Orientation m_orientation;
+
+ virtual void dropEvent (QDropEvent *e);
+ virtual void paintCell (QPainter *painter, int row, int col);
+ virtual void mouseReleaseEvent (QMouseEvent *e);
+ virtual void resizeEvent (QResizeEvent *e);
+
+ int m_mouseButton;
+
+protected slots:
+ void slotColorSelected (int cell);
+ void slotColorDoubleClicked (int cell);
+};
+
+
+class kpTransparentColorCell : public QFrame
+{
+Q_OBJECT
+
+public:
+ kpTransparentColorCell (QWidget *parent, const char *name = 0);
+ virtual ~kpTransparentColorCell ();
+
+ virtual QSize sizeHint () const;
+
+signals:
+ void transparentColorSelected (int mouseButton);
+
+ // lazy
+ void foregroundColorChanged (const kpColor &color);
+ void backgroundColorChanged (const kpColor &color);
+
+protected:
+ virtual void mousePressEvent (QMouseEvent *e);
+ virtual void mouseReleaseEvent (QMouseEvent *e);
+
+ virtual void drawContents (QPainter *p);
+
+ QPixmap m_pixmap;
+};
+
+
+class kpColorPalette : public QWidget
+{
+Q_OBJECT
+
+public:
+ kpColorPalette (QWidget *parent,
+ Qt::Orientation o = Qt::Horizontal,
+ const char *name = 0);
+ virtual ~kpColorPalette ();
+
+ Qt::Orientation orientation () const;
+ void setOrientation (Qt::Orientation o);
+
+signals:
+ void foregroundColorChanged (const kpColor &color);
+ void backgroundColorChanged (const kpColor &color);
+
+protected:
+ Qt::Orientation m_orientation;
+
+ QBoxLayout *m_boxLayout;
+ kpTransparentColorCell *m_transparentColorCell;
+ kpColorCells *m_colorCells;
+};
+
+
+class kpColorSimilarityToolBarItem : public kpColorSimilarityCube
+{
+Q_OBJECT
+
+public:
+ kpColorSimilarityToolBarItem (kpMainWindow *mainWindow,
+ QWidget *parent,
+ const char *name = 0);
+ virtual ~kpColorSimilarityToolBarItem ();
+
+public:
+ int processedColorSimilarity () const;
+
+public slots:
+ void setColorSimilarity (double similarity);
+
+signals:
+ void colorSimilarityChanged (double similarity, int processedSimilarity);
+
+public:
+ // (only valid in slots connected to colorSimilarityChanged());
+ double oldColorSimilarity () const;
+
+private:
+ virtual void mousePressEvent (QMouseEvent *e);
+ virtual void mouseDoubleClickEvent (QMouseEvent *e);
+
+private:
+ kpMainWindow *m_mainWindow;
+
+ double m_oldColorSimilarity;
+ int m_processedColorSimilarity;
+};
+
+
+class kpColorToolBar : public KToolBar
+{
+Q_OBJECT
+
+public:
+ kpColorToolBar (const QString &label, kpMainWindow *mainWindow, const char *name = 0);
+ virtual ~kpColorToolBar ();
+
+ kpColor color (int which) const;
+ void setColor (int which, const kpColor &color);
+
+ kpColor foregroundColor () const;
+ kpColor backgroundColor () const;
+
+ double colorSimilarity () const;
+ void setColorSimilarity (double similarity);
+ int processedColorSimilarity () const;
+
+signals:
+ // If you connect to this signal, ignore the following
+ // foregroundColorChanged() and backgroundColorChanged() signals
+ void colorsSwapped (const kpColor &newForegroundColor,
+ const kpColor &newBackgroundColor);
+
+ void foregroundColorChanged (const kpColor &color);
+ void backgroundColorChanged (const kpColor &color);
+ void colorSimilarityChanged (double similarity, int processedSimilarity);
+
+public:
+ // (only valid in slots connected to foregroundColorChanged())
+ kpColor oldForegroundColor () const;
+ // (only valid in slots connected to backgroundColorChanged())
+ kpColor oldBackgroundColor () const;
+
+ // (only valid in slots connected to colorSimilarityChanged())
+ double oldColorSimilarity () const;
+
+public slots:
+ void setForegroundColor (const kpColor &color);
+ void setBackgroundColor (const kpColor &color);
+
+private:
+ kpMainWindow *m_mainWindow;
+
+ Qt::Orientation m_lastDockedOrientation;
+ bool m_lastDockedOrientationSet;
+ virtual void setOrientation (Qt::Orientation o);
+
+ QBoxLayout *m_boxLayout;
+ kpDualColorButton *m_dualColorButton;
+ kpColorPalette *m_colorPalette;
+ kpColorSimilarityToolBarItem *m_colorSimilarityToolBarItem;
+};
+
+#endif // __kp_color_toolbar_h__
diff --git a/kolourpaint/widgets/kpresizesignallinglabel.cpp b/kolourpaint/widgets/kpresizesignallinglabel.cpp
new file mode 100644
index 00000000..77d0ad2b
--- /dev/null
+++ b/kolourpaint/widgets/kpresizesignallinglabel.cpp
@@ -0,0 +1,67 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG_KP_RESIZE_SIGNALLING_LABEL 0
+
+
+#include <kpresizesignallinglabel.h>
+
+#include <kdebug.h>
+
+
+kpResizeSignallingLabel::kpResizeSignallingLabel (const QString &string,
+ QWidget *parent,
+ const char *name)
+ : QLabel (string, parent, name)
+{
+}
+
+kpResizeSignallingLabel::kpResizeSignallingLabel (QWidget *parent,
+ const char *name)
+ : QLabel (parent, name)
+{
+}
+
+kpResizeSignallingLabel::~kpResizeSignallingLabel ()
+{
+}
+
+
+// protected virtual [base QLabel]
+void kpResizeSignallingLabel::resizeEvent (QResizeEvent *e)
+{
+#if DEBUG_KP_RESIZE_SIGNALLING_LABEL
+ kdDebug () << "kpResizeSignallingLabel::resizeEvent() newSize=" << e->size ()
+ << " oldSize=" << e->oldSize () << endl;
+#endif
+ QLabel::resizeEvent (e);
+
+ emit resized ();
+}
+
+
+#include <kpresizesignallinglabel.moc>
diff --git a/kolourpaint/widgets/kpresizesignallinglabel.h b/kolourpaint/widgets/kpresizesignallinglabel.h
new file mode 100644
index 00000000..6cd3beba
--- /dev/null
+++ b/kolourpaint/widgets/kpresizesignallinglabel.h
@@ -0,0 +1,52 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef KP_RESIZE_SIGNALLING_LABEL
+#define KP_RESIZE_SIGNALLING_LABEL
+
+
+#include <qlabel.h>
+
+
+class kpResizeSignallingLabel : public QLabel
+{
+Q_OBJECT
+
+public:
+ kpResizeSignallingLabel (const QString &string, QWidget *parent, const char *name = 0);
+ kpResizeSignallingLabel (QWidget *parent, const char *name = 0);
+ virtual ~kpResizeSignallingLabel ();
+
+signals:
+ void resized ();
+
+protected:
+ virtual void resizeEvent (QResizeEvent *e);
+};
+
+
+#endif // KP_RESIZE_SIGNALLING_LABEL
diff --git a/kolourpaint/widgets/kpsqueezedtextlabel.cpp b/kolourpaint/widgets/kpsqueezedtextlabel.cpp
new file mode 100644
index 00000000..53fd85c9
--- /dev/null
+++ b/kolourpaint/widgets/kpsqueezedtextlabel.cpp
@@ -0,0 +1,215 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG_KP_SQUEEZED_TEXT_LABEL 0
+
+
+#include <kpsqueezedtextlabel.h>
+
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qstring.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+
+kpSqueezedTextLabel::kpSqueezedTextLabel (QWidget *parent, const char *name)
+ : QLabel (parent, name),
+ m_showEllipsis (true)
+{
+}
+
+kpSqueezedTextLabel::kpSqueezedTextLabel (const QString &text, QWidget *parent, const char *name)
+ : QLabel (parent, name),
+ m_showEllipsis (true)
+{
+ setText (text);
+}
+
+
+// public virtual
+QSize kpSqueezedTextLabel::minimumSizeHint () const
+{
+#if DEBUG_KP_SQUEEZED_TEXT_LABEL && 1
+ kdDebug () << "kpSqueezedTextLabel::minimumSizeHint() qLabel prefers"
+ << QLabel::minimumSizeHint () << endl;
+#endif
+ return QSize (-1/*no minimum width*/, QLabel::minimumHeight ());
+}
+
+
+// public
+QString kpSqueezedTextLabel::fullText () const
+{
+ return m_fullText;
+}
+
+
+// public
+bool kpSqueezedTextLabel::showEllipsis () const
+{
+ return m_showEllipsis;
+}
+
+// public
+void kpSqueezedTextLabel::setShowEllipsis (bool yes)
+{
+ if (m_showEllipsis == yes)
+ return;
+
+ m_showEllipsis = yes;
+
+ squeezeText ();
+}
+
+
+// public slots virtual [base QLabel]
+void kpSqueezedTextLabel::setText (const QString &text)
+{
+ m_fullText = text;
+ squeezeText ();
+}
+
+
+// protected virtual [base QWidget]
+void kpSqueezedTextLabel::resizeEvent (QResizeEvent *e)
+{
+#if DEBUG_KP_SQUEEZED_TEXT_LABEL && 1
+ kdDebug () << "kpSqueezedTextLabeL::resizeEvent() size=" << e->size ()
+ << " oldSize=" << e->oldSize ()
+ << endl;
+#endif
+ squeezeText ();
+}
+
+
+// protected
+QString kpSqueezedTextLabel::ellipsisText () const
+{
+ return m_showEllipsis ? i18n ("...") : QString::null;
+}
+
+// protected
+void kpSqueezedTextLabel::squeezeText ()
+{
+#if DEBUG_KP_SQUEEZED_TEXT_LABEL && 1
+ kdDebug () << "kpSqueezedTextLabeL::squeezeText" << endl;
+#endif
+
+ QFontMetrics fontMetrics (font ());
+ int fullTextWidth = fontMetrics.width (m_fullText);
+#if DEBUG_KP_SQUEEZED_TEXT_LABEL && 1
+ kdDebug () << "\tfullText=" << m_fullText
+ << " fullTextWidth=" << fullTextWidth
+ << " labelWidth=" << width ()
+ << endl;
+#endif
+
+ if (fullTextWidth <= width ())
+ {
+ #if DEBUG_KP_SQUEEZED_TEXT_LABEL && 1
+ kdDebug () << "\tfullText will fit - display" << endl;
+ #endif
+ QLabel::setText (m_fullText);
+ }
+ else
+ {
+ #if DEBUG_KP_SQUEEZED_TEXT_LABEL && 1
+ kdDebug () << "\tfullText won't fit :( - squeeze" << endl;
+ kdDebug () << "\t\twidth of \"...\"="
+ << fontMetrics.width (ellipsisText ())
+ << endl;
+
+ #endif
+ if (fontMetrics.width (ellipsisText ()) > width ())
+ {
+ #if DEBUG_KP_SQUEEZED_TEXT_LABEL && 1
+ kdDebug () << "\t\t\tcan't even fit \"...\" - forget it" << endl;
+ #endif
+ QLabel::setText (QString::null);
+ return;
+ }
+
+ // Binary search our way to fit squeezed text
+ int numLettersToUseLo = 0;
+ int numLettersToUseHi = m_fullText.length ();
+ int numLettersToUse = 0;
+
+ while (numLettersToUseLo <= numLettersToUseHi)
+ {
+ int numLettersToUseMid = (numLettersToUseLo + numLettersToUseHi) / 2;
+ int squeezedWidth = fontMetrics.width (m_fullText.left (numLettersToUseMid) + ellipsisText ());
+ #if DEBUG_KP_SQUEEZED_TEXT_LABEL && 1
+ kdDebug () << "\tbsearch: lo=" << numLettersToUseLo
+ << " hi=" << numLettersToUseHi
+ << " mid=" << numLettersToUseMid
+ << " acceptable=" << numLettersToUse
+ << " squeezedWidth=" << squeezedWidth
+ << endl;
+ #endif
+
+ if (squeezedWidth == width ())
+ {
+ #if DEBUG_KP_SQUEEZED_TEXT_LABEL && 1
+ kdDebug () << "\t\tperfect match!" << endl;
+ #endif
+ numLettersToUse = numLettersToUseMid;
+ break;
+ }
+ else if (squeezedWidth < width ())
+ {
+ #if DEBUG_KP_SQUEEZED_TEXT_LABEL && 1
+ kdDebug () << "\t\tsmall enough - numLettersToUse="
+ << numLettersToUse << endl;
+ #endif
+ if (numLettersToUseMid > numLettersToUse)
+ {
+ numLettersToUse = numLettersToUseMid;
+ #if DEBUG_KP_SQUEEZED_TEXT_LABEL && 1
+ kdDebug () << "\t\t\tset numLettersToUse="
+ << numLettersToUse
+ << endl;
+ #endif
+ }
+
+ numLettersToUseLo = numLettersToUseMid + 1;
+ }
+ else
+ {
+ #if DEBUG_KP_SQUEEZED_TEXT_LABEL && 1
+ kdDebug () << "\t\ttoo big" << endl;
+ #endif
+ numLettersToUseHi = numLettersToUseMid - 1;
+ }
+ }
+
+ QLabel::setText (m_fullText.left (numLettersToUse) + ellipsisText ());
+ }
+}
+
+#include <kpsqueezedtextlabel.moc>
diff --git a/kolourpaint/widgets/kpsqueezedtextlabel.h b/kolourpaint/widgets/kpsqueezedtextlabel.h
new file mode 100644
index 00000000..57aa7b2f
--- /dev/null
+++ b/kolourpaint/widgets/kpsqueezedtextlabel.h
@@ -0,0 +1,65 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef __kp_squeezed_text_label_h__
+#define __kp_squeezed_text_label_h__
+
+#include <qlabel.h>
+#include <qstring.h>
+
+
+// KSqueezedTextLabel done properly - squeeze at the end of the string,
+// not the middle.
+class kpSqueezedTextLabel : public QLabel
+{
+Q_OBJECT
+
+public:
+ kpSqueezedTextLabel (QWidget *parent, const char *name = 0);
+ kpSqueezedTextLabel (const QString &text, QWidget *parent, const char *name = 0);
+
+ virtual QSize minimumSizeHint () const;
+
+ // TODO: maybe text() should return the full text?
+ QString fullText () const;
+
+ bool showEllipsis () const;
+ void setShowEllipsis (bool yes = true);
+
+public slots:
+ virtual void setText (const QString &text);
+
+protected:
+ virtual void resizeEvent (QResizeEvent *);
+ QString ellipsisText () const;
+ void squeezeText ();
+
+ QString m_fullText;
+ bool m_showEllipsis;
+};
+
+#endif // __kp_squeezed_text_label_h__
diff --git a/kolourpaint/widgets/kptooltoolbar.cpp b/kolourpaint/widgets/kptooltoolbar.cpp
new file mode 100644
index 00000000..b8d1985c
--- /dev/null
+++ b/kolourpaint/widgets/kptooltoolbar.cpp
@@ -0,0 +1,640 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define DEBUG_KP_TOOL_TOOL_BAR 0
+
+
+#include <kptooltoolbar.h>
+
+#include <qbuttongroup.h>
+#include <qlayout.h>
+#include <qdatetime.h>
+#include <qtoolbutton.h>
+#include <qtooltip.h>
+#include <qwidget.h>
+#include <qwhatsthis.h>
+
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kglobalsettings.h>
+#include <kicontheme.h>
+
+#include <kpdefs.h>
+#include <kptool.h>
+#include <kptoolaction.h>
+#include <kptoolwidgetbrush.h>
+#include <kptoolwidgeterasersize.h>
+#include <kptoolwidgetfillstyle.h>
+#include <kptoolwidgetlinewidth.h>
+#include <kptoolwidgetopaqueortransparent.h>
+#include <kptoolwidgetspraycansize.h>
+
+
+class kpToolButton : public QToolButton
+{
+public:
+ kpToolButton (kpTool *tool, QWidget *parent)
+ : QToolButton (parent),
+ m_tool (tool)
+ {
+ }
+
+ virtual ~kpToolButton ()
+ {
+ }
+
+protected:
+ // virtual [base QWidget]
+ void mouseDoubleClickEvent (QMouseEvent *e)
+ {
+ if (e->button () == Qt::LeftButton && m_tool)
+ m_tool->globalDraw ();
+ }
+
+ kpTool *m_tool;
+};
+
+
+kpToolToolBar::kpToolToolBar (const QString &label, kpMainWindow *mainWindow, int colsOrRows, const char *name)
+ : KToolBar ((QWidget *) mainWindow, name, false/*don't use global toolBar settings*/, true/*readConfig*/),
+ m_vertCols (colsOrRows),
+ m_buttonGroup (0),
+ m_baseWidget (0),
+ m_baseLayout (0),
+ m_toolLayout (0),
+ m_previousTool (0), m_currentTool (0),
+ m_defaultIconSize (0)
+{
+ setText (label);
+
+
+ // With these lines enabled, mousePressEvent's weren't being generated
+ // when right clicking in empty part of the toolbar (each call affects
+ // the toolbar in its respective orientation). They don't seem to be
+ // needed anyway since !isResizeEnabled().
+
+ //setHorizontallyStretchable (false);
+ //setVerticallyStretchable (false);
+
+
+ m_baseWidget = new QWidget (this);
+
+#if DEBUG_KP_TOOL_TOOL_BAR
+ QTime timer;
+ timer.start ();
+#endif
+
+ m_toolWidgets.append (m_toolWidgetBrush =
+ new kpToolWidgetBrush (m_baseWidget, "Tool Widget Brush"));
+ m_toolWidgets.append (m_toolWidgetEraserSize =
+ new kpToolWidgetEraserSize (m_baseWidget, "Tool Widget Eraser Size"));
+ m_toolWidgets.append (m_toolWidgetFillStyle =
+ new kpToolWidgetFillStyle (m_baseWidget, "Tool Widget Fill Style"));
+ m_toolWidgets.append (m_toolWidgetLineWidth =
+ new kpToolWidgetLineWidth (m_baseWidget, "Tool Widget Line Width"));
+ m_toolWidgets.append (m_toolWidgetOpaqueOrTransparent =
+ new kpToolWidgetOpaqueOrTransparent (m_baseWidget, "Tool Widget Opaque/Transparent"));
+ m_toolWidgets.append (m_toolWidgetSpraycanSize =
+ new kpToolWidgetSpraycanSize (m_baseWidget, "Tool Widget Spraycan Size"));
+
+#if DEBUG_KP_TOOL_TOOL_BAR
+ kdDebug () << "kpToolToolBar::<ctor> create tool widgets msec="
+ << timer.restart () << endl;
+#endif
+
+ for (QValueVector <kpToolWidgetBase *>::const_iterator it = m_toolWidgets.begin ();
+ it != m_toolWidgets.end ();
+ it++)
+ {
+ connect (*it, SIGNAL (optionSelected (int, int)),
+ this, SIGNAL (toolWidgetOptionSelected ()));
+ }
+
+#if DEBUG_KP_TOOL_TOOL_BAR
+ kdDebug () << "kpToolToolBar::<ctor> connect widgets msec="
+ << timer.restart () << endl;
+#endif
+
+ m_lastDockedOrientationSet = false;
+ setOrientation (orientation ());
+
+#if DEBUG_KP_TOOL_TOOL_BAR
+ kdDebug () << "kpToolToolBar::<ctor> layout tool widgets msec="
+ << timer.elapsed () << endl;
+#endif
+
+ m_buttonGroup = new QButtonGroup (); // invisible
+ m_buttonGroup->setExclusive (true);
+
+ connect (m_buttonGroup, SIGNAL (clicked (int)), SLOT (slotToolButtonClicked ()));
+
+ hideAllToolWidgets ();
+}
+
+kpToolToolBar::~kpToolToolBar ()
+{
+ unregisterAllTools ();
+ delete m_buttonGroup;
+}
+
+
+// private
+int kpToolToolBar::defaultIconSize ()
+{
+ // Cached?
+ if (m_defaultIconSize > 0)
+ return m_defaultIconSize;
+
+#if DEBUG_KP_TOOL_TOOL_BAR
+ kdDebug () << "kpToolToolBar::defaultIconSize()" << endl;
+#endif
+
+
+ KConfigGroupSaver cfgGroupSaver (KGlobal::config (),
+ kpSettingsGroupTools);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ if (cfg->hasKey (kpSettingToolBoxIconSize))
+ {
+ m_defaultIconSize = cfg->readNumEntry (kpSettingToolBoxIconSize);
+ #if DEBUG_KP_TOOL_TOOL_BAR
+ kdDebug () << "\tread: " << m_defaultIconSize << endl;
+ #endif
+ }
+ else
+ {
+ m_defaultIconSize = -1;
+#if DEBUG_KP_TOOL_TOOL_BAR
+ kdDebug () << "\tfirst time - writing default: " << m_defaultIconSize << endl;
+#endif
+ cfg->writeEntry (kpSettingToolBoxIconSize, m_defaultIconSize);
+ cfg->sync ();
+ }
+
+
+ if (m_defaultIconSize <= 0)
+ {
+ // Adapt according to screen geometry
+ const QRect desktopSize = KGlobalSettings::desktopGeometry (this);
+ #if DEBUG_KP_TOOL_TOOL_BAR
+ kdDebug () << "\tadapting to screen size=" << desktopSize << endl;
+ #endif
+
+ if (desktopSize.width () >= 1024 && desktopSize.height () >= 768)
+ m_defaultIconSize = KIcon::SizeSmallMedium/*22x22*/;
+ else
+ m_defaultIconSize = KIcon::SizeSmall/*16x16*/;
+ }
+
+
+#if DEBUG_KP_TOOL_TOOL_BAR
+ kdDebug () << "\treturning " << m_defaultIconSize << endl;
+#endif
+ return m_defaultIconSize;
+}
+
+// public
+void kpToolToolBar::registerTool (kpTool *tool)
+{
+ for (QValueVector <kpButtonToolPair>::const_iterator it = m_buttonToolPairs.begin ();
+ it != m_buttonToolPairs.end ();
+ it++)
+ {
+ if ((*it).m_tool == tool)
+ return;
+ }
+ int num = m_buttonToolPairs.count ();
+
+ QToolButton *b = new kpToolButton (tool, m_baseWidget);
+ b->setAutoRaise (true);
+ b->setUsesBigPixmap (false);
+ b->setUsesTextLabel (false);
+ b->setToggleButton (true);
+
+ b->setText (tool->text ());
+ b->setIconSet (tool->iconSet (defaultIconSize ()));
+ QToolTip::add (b, tool->toolTip ());
+ QWhatsThis::add (b, tool->description ());
+
+ m_buttonGroup->insert (b);
+ addButton (b, orientation (), num);
+
+ m_buttonToolPairs.append (kpButtonToolPair (b, tool));
+
+
+ connect (tool, SIGNAL (actionActivated ()),
+ this, SLOT (slotToolActionActivated ()));
+ connect (tool, SIGNAL (actionToolTipChanged (const QString &)),
+ this, SLOT (slotToolActionToolTipChanged ()));
+}
+
+// public
+void kpToolToolBar::unregisterTool (kpTool *tool)
+{
+ for (QValueVector <kpButtonToolPair>::iterator it = m_buttonToolPairs.begin ();
+ it != m_buttonToolPairs.end ();
+ it++)
+ {
+ if ((*it).m_tool == tool)
+ {
+ delete ((*it).m_button);
+ m_buttonToolPairs.erase (it);
+
+ disconnect (tool, SIGNAL (actionActivated ()),
+ this, SLOT (slotToolActionActivated ()));
+ disconnect (tool, SIGNAL (actionToolTipChanged (const QString &)),
+ this, SLOT (slotToolActionToolTipChanged ()));
+ break;
+ }
+ }
+}
+
+// public
+void kpToolToolBar::unregisterAllTools ()
+{
+ for (QValueVector <kpButtonToolPair>::iterator it = m_buttonToolPairs.begin ();
+ it != m_buttonToolPairs.end ();
+ it++)
+ {
+ delete ((*it).m_button);
+ }
+
+ m_buttonToolPairs.clear ();
+}
+
+
+// public
+kpTool *kpToolToolBar::tool () const
+{
+ return m_currentTool;
+}
+
+// public
+void kpToolToolBar::selectTool (const kpTool *tool, bool reselectIfSameTool)
+{
+#if DEBUG_KP_TOOL_TOOL_BAR
+ kdDebug () << "kpToolToolBar::selectTool (tool=" << tool
+ << ") currentTool=" << m_currentTool
+ << endl;
+#endif
+
+ if (!reselectIfSameTool && tool == m_currentTool)
+ return;
+
+ if (tool)
+ {
+ for (QValueVector <kpButtonToolPair>::iterator it = m_buttonToolPairs.begin ();
+ it != m_buttonToolPairs.end ();
+ it++)
+ {
+ if ((*it).m_tool == tool)
+ {
+ m_buttonGroup->setButton (m_buttonGroup->id ((*it).m_button));
+ slotToolButtonClicked ();
+ break;
+ }
+ }
+ }
+ else
+ {
+ QButton *b = m_buttonGroup->selected ();
+ #if DEBUG_KP_TOOL_TOOL_BAR
+ kdDebug () << "\twant to select no tool - button selected=" << b << endl;
+ #endif
+ if (b)
+ {
+ b->toggle ();
+ slotToolButtonClicked ();
+ }
+ }
+}
+
+
+// public
+kpTool *kpToolToolBar::previousTool () const
+{
+ return m_previousTool;
+}
+
+// public
+void kpToolToolBar::selectPreviousTool ()
+{
+ selectTool (m_previousTool);
+}
+
+
+// public
+void kpToolToolBar::hideAllToolWidgets ()
+{
+ for (QValueVector <kpToolWidgetBase *>::const_iterator it = m_toolWidgets.begin ();
+ it != m_toolWidgets.end ();
+ it++)
+ {
+ (*it)->hide ();
+ }
+}
+
+// public
+int kpToolToolBar::numShownToolWidgets () const
+{
+#if DEBUG_KP_TOOL_TOOL_BAR
+ kdDebug () << "kpToolToolBar::numShownToolWidgets()" << endl;
+#endif
+
+ int ret = 0;
+
+ for (QValueVector <kpToolWidgetBase *>::const_iterator it = m_toolWidgets.begin ();
+ it != m_toolWidgets.end ();
+ it++)
+ {
+ #if DEBUG_KP_TOOL_TOOL_BAR
+ kdDebug () << "\t" << (*it)->name ()
+ << " isShown=" << (*it)->isShown ()
+ << endl;
+ #endif
+ if ((*it)->isShown ())
+ ret++;
+ }
+
+ return ret;
+}
+
+// public
+kpToolWidgetBase *kpToolToolBar::shownToolWidget (int which) const
+{
+ int uptoVisibleWidget = 0;
+
+ for (QValueVector <kpToolWidgetBase *>::const_iterator it = m_toolWidgets.begin ();
+ it != m_toolWidgets.end ();
+ it++)
+ {
+ if ((*it)->isShown ())
+ {
+ if (which == uptoVisibleWidget)
+ return *it;
+
+ uptoVisibleWidget++;
+ }
+ }
+
+ return 0;
+}
+
+
+// public
+bool kpToolToolBar::toolsSingleKeyTriggersEnabled () const
+{
+ for (QValueVector <kpButtonToolPair>::const_iterator it = m_buttonToolPairs.begin ();
+ it != m_buttonToolPairs.end ();
+ it++)
+ {
+ if (!(*it).m_tool->singleKeyTriggersEnabled ())
+ return false;
+ }
+
+ return true;
+}
+
+// public
+void kpToolToolBar::enableToolsSingleKeyTriggers (bool enable)
+{
+#if DEBUG_KP_TOOL_TOOL_BAR
+ kdDebug () << "kpToolToolBar::enableToolsSingleKeyTriggers(" << enable << ")" << endl;
+#endif
+
+ for (QValueVector <kpButtonToolPair>::const_iterator it = m_buttonToolPairs.begin ();
+ it != m_buttonToolPairs.end ();
+ it++)
+ {
+ (*it).m_tool->enableSingleKeyTriggers (enable);
+ }
+}
+
+
+// private slot
+void kpToolToolBar::slotToolButtonClicked ()
+{
+ QButton *b = m_buttonGroup->selected ();
+
+#if DEBUG_KP_TOOL_TOOL_BAR
+ kdDebug () << "kpToolToolBar::slotToolButtonClicked() button=" << b << endl;
+#endif
+
+ kpTool *tool = 0;
+ for (QValueVector <kpButtonToolPair>::iterator it = m_buttonToolPairs.begin ();
+ it != m_buttonToolPairs.end ();
+ it++)
+ {
+ if ((*it).m_button == b)
+ {
+ tool = (*it).m_tool;
+ break;
+ }
+ }
+
+#if DEBUG_KP_TOOL_TOOL_BAR
+ kdDebug () << "\ttool=" << tool
+ << " currentTool=" << m_currentTool
+ << endl;
+#endif
+
+ if (tool == m_currentTool)
+ {
+ if (m_currentTool)
+ m_currentTool->reselect ();
+
+ return;
+ }
+
+ if (m_currentTool)
+ m_currentTool->endInternal ();
+
+ m_previousTool = m_currentTool;
+ m_currentTool = tool;
+
+ if (m_currentTool)
+ {
+ kpToolAction *action = m_currentTool->action ();
+ if (action)
+ {
+ action->setChecked (true);
+ }
+
+ m_currentTool->beginInternal ();
+ }
+
+ emit sigToolSelected (m_currentTool);
+}
+
+
+#define CONST_KP_TOOL_SENDER() (dynamic_cast <const kpTool *> (sender ()))
+
+// private slot
+void kpToolToolBar::slotToolActionActivated ()
+{
+ const kpTool *tool = CONST_KP_TOOL_SENDER ();
+
+#if DEBUG_KP_TOOL_TOOL_BAR
+ kdDebug () << "kpToolToolBar::slotToolActionActivated() tool="
+ << (tool ? tool->name () : "null")
+ << endl;
+#endif
+
+ if (m_currentTool)
+ {
+ // If the user clicks on the same KToggleAction, it unchecks it
+ // - this is inconsistent with the Tool Box so always make sure it's
+ // checked.
+ kpToolAction *action = m_currentTool->action ();
+ if (action)
+ {
+ action->setChecked (true);
+ }
+ }
+
+ selectTool (tool, true/*reselect if same tool*/);
+}
+
+// private slot
+void kpToolToolBar::slotToolActionToolTipChanged ()
+{
+ const kpTool *tool = CONST_KP_TOOL_SENDER ();
+
+#if DEBUG_KP_TOOL_TOOL_BAR
+ kdDebug () << "kpToolToolBar::slotToolActionToolTipChanged() tool="
+ << (tool ? tool->name () : "null")
+ << endl;
+#endif
+
+ if (!tool)
+ return;
+
+ for (QValueVector <kpButtonToolPair>::const_iterator it = m_buttonToolPairs.begin ();
+ it != m_buttonToolPairs.end ();
+ it++)
+ {
+ if (tool == (*it).m_tool)
+ {
+ QToolTip::add ((*it).m_button, tool->toolTip ());
+ return;
+ }
+ }
+}
+
+
+// public slot virtual [base QDockWindow]
+void kpToolToolBar::setOrientation (Qt::Orientation o)
+{
+#if DEBUG_KP_TOOL_TOOL_BAR
+ kdDebug () << "kpToolToolBar::setOrientation("
+ << (o == Qt::Vertical ? "vertical" : "horizontal")
+ << ") called!" << endl;
+#endif
+
+ // (QDockWindow::undock() calls us)
+ bool isOutsideDock = (place () == QDockWindow::OutsideDock);
+
+ if (!m_lastDockedOrientationSet || !isOutsideDock)
+ {
+ m_lastDockedOrientation = o;
+ m_lastDockedOrientationSet = true;
+ }
+
+ if (isOutsideDock)
+ {
+ #if DEBUG_KP_TOOL_TOOL_BAR
+ kdDebug () << "\toutside dock, forcing orientation to last" << endl;
+ #endif
+ o = m_lastDockedOrientation;
+ }
+
+ delete m_toolLayout;
+ delete m_baseLayout;
+ if (o == Qt::Vertical)
+ {
+ m_baseLayout = new QBoxLayout (m_baseWidget, QBoxLayout::TopToBottom,
+ 5/*margin*/,
+ 10/*spacing*/);
+ m_toolLayout = new QGridLayout (m_baseLayout,
+ 5/*arbitrary rows since toolBar auto-expands*/,
+ m_vertCols,
+ 0/*margin*/,
+ 0/*spacing*/);
+ }
+ else // if (o == Qt::Horizontal)
+ {
+ m_baseLayout = new QBoxLayout (m_baseWidget, QBoxLayout::LeftToRight,
+ 5/*margin*/,
+ 10/*spacing*/);
+ m_toolLayout = new QGridLayout (m_baseLayout,
+ m_vertCols/*rows in this case, since horiz*/,
+ 5/*arbitrary cols since toolBar auto-expands*/,
+ 0/*margin*/,
+ 0/*spacing*/);
+ }
+
+ int num = 0;
+
+ for (QValueVector <kpButtonToolPair>::iterator it = m_buttonToolPairs.begin ();
+ it != m_buttonToolPairs.end ();
+ it++)
+ {
+ addButton ((*it).m_button, o, num);
+ num++;
+ }
+
+ for (QValueVector <kpToolWidgetBase *>::const_iterator it = m_toolWidgets.begin ();
+ it != m_toolWidgets.end ();
+ it++)
+ {
+ if (*it)
+ {
+ m_baseLayout->addWidget (*it,
+ 0/*stretch*/,
+ o == Qt::Vertical ? Qt::AlignHCenter : Qt::AlignVCenter);
+ }
+ }
+
+ KToolBar::setOrientation (o);
+}
+
+// private
+void kpToolToolBar::addButton (QButton *button, Qt::Orientation o, int num)
+{
+ if (o == Qt::Vertical)
+ m_toolLayout->addWidget (button, num / m_vertCols, num % m_vertCols);
+ else
+ {
+ // maps Left (o = vertical) to Bottom (o = horizontal)
+ int row = (m_vertCols - 1) - (num % m_vertCols);
+ m_toolLayout->addWidget (button, row, num / m_vertCols);
+ }
+}
+
+
+#include <kptooltoolbar.moc>
diff --git a/kolourpaint/widgets/kptooltoolbar.h b/kolourpaint/widgets/kptooltoolbar.h
new file mode 100644
index 00000000..c3a7d1b7
--- /dev/null
+++ b/kolourpaint/widgets/kptooltoolbar.h
@@ -0,0 +1,155 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kp_tool_tool_bar_h__
+#define __kp_tool_tool_bar_h__
+
+#include <qvaluevector.h>
+
+#include <ktoolbar.h>
+
+
+class QBoxLayout;
+class QButton;
+class QButtonGroup;
+class QWidget;
+class QGridLayout;
+
+class kpMainWindow;
+class kpTool;
+
+class kpToolWidgetBase;
+class kpToolWidgetBrush;
+class kpToolWidgetEraserSize;
+class kpToolWidgetFillStyle;
+class kpToolWidgetLineWidth;
+class kpToolWidgetOpaqueOrTransparent;
+class kpToolWidgetSpraycanSize;
+
+class kpToolToolBar : public KToolBar
+{
+Q_OBJECT
+
+public:
+ kpToolToolBar (const QString &label, kpMainWindow *mainWindow, int colsOrRows = 2, const char *name = 0);
+ virtual ~kpToolToolBar ();
+
+private:
+ int defaultIconSize ();
+public:
+ void registerTool (kpTool *tool);
+ void unregisterTool (kpTool *tool);
+ void unregisterAllTools ();
+
+ kpTool *tool () const;
+ void selectTool (const kpTool *tool, bool reselectIfSameTool = false);
+
+ kpTool *previousTool () const;
+ void selectPreviousTool ();
+
+ void hideAllToolWidgets ();
+ // could this be cleaner (the tools have to access them individually somehow)?
+ kpToolWidgetBrush *toolWidgetBrush () const { return m_toolWidgetBrush; }
+ kpToolWidgetEraserSize *toolWidgetEraserSize () const { return m_toolWidgetEraserSize; }
+ kpToolWidgetFillStyle *toolWidgetFillStyle () const { return m_toolWidgetFillStyle; }
+ kpToolWidgetLineWidth *toolWidgetLineWidth () const { return m_toolWidgetLineWidth; }
+ kpToolWidgetOpaqueOrTransparent *toolWidgetOpaqueOrTransparent () const { return m_toolWidgetOpaqueOrTransparent; }
+ kpToolWidgetSpraycanSize *toolWidgetSpraycanSize () const { return m_toolWidgetSpraycanSize; }
+
+public:
+ int numShownToolWidgets () const;
+ kpToolWidgetBase *shownToolWidget (int which) const;
+
+ bool toolsSingleKeyTriggersEnabled () const;
+ void enableToolsSingleKeyTriggers (bool enable);
+
+signals:
+ void sigToolSelected (kpTool *tool); // tool may be 0
+ void toolWidgetOptionSelected ();
+
+private slots:
+ void slotToolButtonClicked ();
+
+ void slotToolActionActivated ();
+ void slotToolActionToolTipChanged ();
+
+public slots:
+ virtual void setOrientation (Qt::Orientation o);
+
+private:
+ void addButton (QButton *button, Qt::Orientation o, int num);
+
+ Qt::Orientation m_lastDockedOrientation;
+ bool m_lastDockedOrientationSet;
+ int m_vertCols;
+
+ QButtonGroup *m_buttonGroup;
+ QWidget *m_baseWidget;
+ QBoxLayout *m_baseLayout;
+ QGridLayout *m_toolLayout;
+
+ kpToolWidgetBrush *m_toolWidgetBrush;
+ kpToolWidgetEraserSize *m_toolWidgetEraserSize;
+ kpToolWidgetFillStyle *m_toolWidgetFillStyle;
+ kpToolWidgetLineWidth *m_toolWidgetLineWidth;
+ kpToolWidgetOpaqueOrTransparent *m_toolWidgetOpaqueOrTransparent;
+ kpToolWidgetSpraycanSize *m_toolWidgetSpraycanSize;
+
+ QValueVector <kpToolWidgetBase *> m_toolWidgets;
+
+private:
+ struct kpButtonToolPair
+ {
+ kpButtonToolPair (QButton *button, kpTool *tool)
+ : m_button (button), m_tool (tool)
+ {
+ }
+
+ kpButtonToolPair ()
+ : m_button (0), m_tool (0)
+ {
+ }
+
+ QButton *m_button;
+ kpTool *m_tool;
+ };
+
+ QValueVector <kpButtonToolPair> m_buttonToolPairs;
+
+ kpTool *m_previousTool, *m_currentTool;
+
+ int m_defaultIconSize;
+
+private:
+ // There is no need to maintain binary compatibility at this stage.
+ // The d-pointer is just so that you can experiment without recompiling
+ // the kitchen sink.
+ class kpToolToolBarPrivate *d;
+};
+
+#endif // __kp_tool_tool_bar_h__
diff --git a/kolourpaint/widgets/kptoolwidgetbase.cpp b/kolourpaint/widgets/kptoolwidgetbase.cpp
new file mode 100644
index 00000000..a0042dbc
--- /dev/null
+++ b/kolourpaint/widgets/kptoolwidgetbase.cpp
@@ -0,0 +1,608 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define DEBUG_KP_TOOL_WIDGET_BASE 0
+
+
+#include <kptoolwidgetbase.h>
+
+#include <qbitmap.h>
+#include <qcolor.h>
+#include <qimage.h>
+#include <qpainter.h>
+#include <qtooltip.h>
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kdebug.h>
+
+#include <kpdefs.h>
+#include <kpeffectinvert.h>
+
+
+kpToolWidgetBase::kpToolWidgetBase (QWidget *parent, const char *name)
+ : QFrame (parent, name),
+ m_invertSelectedPixmap (true),
+ m_selectedRow (-1), m_selectedCol (-1)
+{
+ if (!name)
+ kdError () << "kpToolWidgetBase::kpToolWidgetBase() without name" << endl;
+
+ setFrameStyle (QFrame::Panel | QFrame::Sunken);
+ setFixedSize (44, 66);
+}
+
+kpToolWidgetBase::~kpToolWidgetBase ()
+{
+}
+
+
+// public
+void kpToolWidgetBase::addOption (const QPixmap &pixmap, const QString &toolTip)
+{
+ if (m_pixmaps.isEmpty ())
+ startNewOptionRow ();
+
+ m_pixmaps.last ().append (pixmap);
+ m_pixmapRects.last ().append (QRect ());
+ m_toolTips.last ().append (toolTip);
+}
+
+// public
+void kpToolWidgetBase::startNewOptionRow ()
+{
+ m_pixmaps.resize (m_pixmaps.count () + 1);
+ m_pixmapRects.resize (m_pixmapRects.count () + 1);
+ m_toolTips.resize (m_toolTips.count () + 1);
+}
+
+// public
+void kpToolWidgetBase::finishConstruction (int fallBackRow, int fallBackCol)
+{
+#if DEBUG_KP_TOOL_WIDGET_BASE
+ kdDebug () << "kpToolWidgetBase(" << name ()
+ << ")::kpToolWidgetBase(fallBack:row=" << fallBackRow
+ << ",col=" << fallBackCol
+ << ")"
+ << endl;
+#endif
+
+ relayoutOptions ();
+
+ const QPair <int, int> rowColPair = defaultSelectedRowAndCol ();
+ if (!setSelected (rowColPair.first, rowColPair.second, false/*don't save*/))
+ {
+ if (!setSelected (fallBackRow, fallBackCol))
+ {
+ if (!setSelected (0, 0))
+ {
+ kdError () << "kpToolWidgetBase::finishConstruction() "
+ "can't even fall back to setSelected(row=0,col=0)" << endl;
+ }
+ }
+ }
+}
+
+
+// private
+QValueVector <int> kpToolWidgetBase::spreadOutElements (const QValueVector <int> &sizes, int max)
+{
+ if (sizes.count () == 0)
+ return QValueVector <int> ();
+ else if (sizes.count () == 1)
+ return QValueVector <int> (1, sizes.first () > max ? 0 : 1/*margin*/);
+
+ QValueVector <int> retOffsets (sizes.count ());
+
+ int totalSize = 0;
+ for (int i = 0; i < (int) sizes.count (); i++)
+ totalSize += sizes [i];
+
+ int margin = 1;
+
+ // if don't fit with margin, then just return elements
+ // packed right next to each other
+ if (totalSize + margin * 2 > max)
+ {
+ retOffsets [0] = 0;
+ for (int i = 1; i < (int) sizes.count (); i++)
+ retOffsets [i] = retOffsets [i - 1] + sizes [i - 1];
+
+ return retOffsets;
+ }
+
+ int maxLeftOver = max - (totalSize + margin * 2);
+
+ int startCompensating = -1;
+ int numCompensate = 0;
+
+ int spacing = 0;
+
+ spacing = maxLeftOver / (sizes.count () - 1);
+ if (spacing * int (sizes.count () - 1) < maxLeftOver)
+ {
+ numCompensate = maxLeftOver - spacing * (sizes.count () - 1);
+ startCompensating = ((sizes.count () - 1) - numCompensate) / 2;
+ }
+
+ retOffsets [0] = margin;
+ for (int i = 1; i < (int) sizes.count (); i++)
+ {
+ retOffsets [i] += retOffsets [i - 1] +
+ sizes [i - 1] +
+ spacing +
+ ((numCompensate &&
+ i >= startCompensating &&
+ i < startCompensating + numCompensate) ? 1 : 0);
+ }
+
+ return retOffsets;
+}
+
+
+// public
+QPair <int, int> kpToolWidgetBase::defaultSelectedRowAndCol () const
+{
+ int row = -1, col = -1;
+
+ if (name ())
+ {
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupTools);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ QString nameString = QString::fromLatin1 (name ());
+
+ row = cfg->readNumEntry (nameString + QString::fromLatin1 (" Row"), -1);
+ col = cfg->readNumEntry (nameString + QString::fromLatin1 (" Col"), -1);
+ }
+
+#if DEBUG_KP_TOOL_WIDGET_BASE
+ kdDebug () << "kpToolWidgetBase(" << name ()
+ << ")::defaultSelectedRowAndCol() returning row=" << row
+ << " col=" << col
+ << endl;
+#endif
+
+ return qMakePair (row, col);
+}
+
+// public
+int kpToolWidgetBase::defaultSelectedRow () const
+{
+ return defaultSelectedRowAndCol ().first;
+}
+
+// public
+int kpToolWidgetBase::defaultSelectedCol () const
+{
+ return defaultSelectedRowAndCol ().second;
+}
+
+// public
+void kpToolWidgetBase::saveSelectedAsDefault () const
+{
+#if DEBUG_KP_TOOL_WIDGET_BASE
+ kdDebug () << "kpToolWidgetBase(" << name ()
+ << ")::saveSelectedAsDefault() row=" << m_selectedRow
+ << " col=" << m_selectedCol << endl;
+#endif
+
+ if (!name ())
+ return;
+
+ KConfigGroupSaver cfgGroupSaver (kapp->config (), kpSettingsGroupTools);
+ KConfigBase *cfg = cfgGroupSaver.config ();
+
+ QString nameString = QString::fromLatin1 (name ());
+ cfg->writeEntry (nameString + QString::fromLatin1 (" Row"), m_selectedRow);
+ cfg->writeEntry (nameString + QString::fromLatin1 (" Col"), m_selectedCol);
+ cfg->sync ();
+}
+
+
+// public
+void kpToolWidgetBase::relayoutOptions ()
+{
+#if DEBUG_KP_TOOL_WIDGET_BASE
+ kdDebug () << "kpToolWidgetBase::relayoutOptions()" << endl;
+#endif
+
+ while (!m_pixmaps.isEmpty () && m_pixmaps.last ().count () == 0)
+ {
+ #if DEBUG_KP_TOOL_WIDGET_BASE
+ kdDebug () << "\tkilling #" << m_pixmaps.count () - 1 << endl;
+ #endif
+ m_pixmaps.resize (m_pixmaps.count () - 1);
+ m_pixmapRects.resize (m_pixmapRects.count () - 1);
+ m_toolTips.resize (m_toolTips.count () - 1);
+ }
+
+ if (m_pixmaps.isEmpty ())
+ return;
+
+#if DEBUG_KP_TOOL_WIDGET_BASE
+ kdDebug () << "\tsurvived killing of empty rows" << endl;
+ kdDebug () << "\tfinding heights of rows:" << endl;
+#endif
+
+ QValueVector <int> maxHeightOfRow (m_pixmaps.count ());
+
+ for (int r = 0; r < (int) m_pixmaps.count (); r++)
+ {
+ for (int c = 0; c < (int) m_pixmaps [r].count (); c++)
+ {
+ if (c == 0 || m_pixmaps [r][c].height () > maxHeightOfRow [r])
+ maxHeightOfRow [r] = m_pixmaps [r][c].height ();
+ }
+ #if DEBUG_KP_TOOL_WIDGET_BASE
+ kdDebug () << "\t\t" << r << ": " << maxHeightOfRow [r] << endl;
+ #endif
+ }
+
+ QValueVector <int> rowYOffset = spreadOutElements (maxHeightOfRow, height ());
+#if DEBUG_KP_TOOL_WIDGET_BASE
+ kdDebug () << "\tspread out offsets of rows:" << endl;
+ for (int r = 0; r < (int) rowYOffset.count (); r++)
+ kdDebug () << "\t\t" << r << ": " << rowYOffset [r] << endl;
+#endif
+
+ for (int r = 0; r < (int) m_pixmaps.count (); r++)
+ {
+ #if DEBUG_KP_TOOL_WIDGET_BASE
+ kdDebug () << "\tlaying out row " << r << ":" << endl;
+ #endif
+
+ QValueVector <int> widths (m_pixmaps [r].count ());
+ for (int c = 0; c < (int) m_pixmaps [r].count (); c++)
+ widths [c] = m_pixmaps [r][c].width ();
+ #if DEBUG_KP_TOOL_WIDGET_BASE
+ kdDebug () << "\t\twidths of cols:" << endl;
+ for (int c = 0; c < (int) m_pixmaps [r].count (); c++)
+ kdDebug () << "\t\t\t" << c << ": " << widths [c] << endl;
+ #endif
+
+ QValueVector <int> colXOffset = spreadOutElements (widths, width ());
+ #if DEBUG_KP_TOOL_WIDGET_BASE
+ kdDebug () << "\t\tspread out offsets of cols:" << endl;
+ for (int c = 0; c < (int) colXOffset.count (); c++)
+ kdDebug () << "\t\t\t" << c << ": " << colXOffset [c] << endl;
+ #endif
+
+ for (int c = 0; c < (int) colXOffset.count (); c++)
+ {
+ int x = colXOffset [c];
+ int y = rowYOffset [r];
+ int w, h;
+
+ if (c == (int) colXOffset.count () - 1)
+ {
+ if (x + m_pixmaps [r][c].width () >= width ())
+ w = m_pixmaps [r][c].width ();
+ else
+ w = width () - 1 - x;
+ }
+ else
+ w = colXOffset [c + 1] - x;
+
+ if (r == (int) m_pixmaps.count () - 1)
+ {
+ if (y + m_pixmaps [r][c].height () >= height ())
+ h = m_pixmaps [r][c].height ();
+ else
+ h = height () - 1 - y;
+ }
+ else
+ h = rowYOffset [r + 1] - y;
+
+ m_pixmapRects [r][c] = QRect (x, y, w, h);
+
+ if (!m_toolTips [r][c].isEmpty ())
+ QToolTip::add (this, m_pixmapRects [r][c], m_toolTips [r][c]);
+ }
+ }
+
+ update ();
+}
+
+
+// public
+int kpToolWidgetBase::selectedRow () const
+{
+ return m_selectedRow;
+}
+
+// public
+int kpToolWidgetBase::selectedCol () const
+{
+ return m_selectedCol;
+}
+
+// public
+int kpToolWidgetBase::selected () const
+{
+ if (m_selectedRow < 0 ||
+ m_selectedRow >= (int) m_pixmaps.count () ||
+ m_selectedCol < 0)
+ {
+ return -1;
+ }
+
+ int upto = 0;
+ for (int y = 0; y < m_selectedRow; y++)
+ upto += m_pixmaps [y].count ();
+
+ if (m_selectedCol >= (int) m_pixmaps [m_selectedRow].count ())
+ return -1;
+
+ upto += m_selectedCol;
+
+ return upto;
+}
+
+
+// public
+bool kpToolWidgetBase::hasPreviousOption (int *row, int *col) const
+{
+#if DEBUG_KP_TOOL_WIDGET_BASE
+ kdDebug () << "kpToolWidgetBase" << name ()
+ << "::hasPreviousOption() current row=" << m_selectedRow
+ << " col=" << m_selectedCol
+ << endl;
+#endif
+ if (row)
+ *row = -1;
+ if (col)
+ *col = -1;
+
+
+ if (m_selectedRow < 0 || m_selectedCol < 0)
+ return false;
+
+ int newRow = m_selectedRow,
+ newCol = m_selectedCol;
+
+ newCol--;
+ if (newCol < 0)
+ {
+ newRow--;
+ if (newRow < 0)
+ return false;
+
+ newCol = m_pixmaps [newRow].count () - 1;
+ if (newCol < 0)
+ return false;
+ }
+
+
+ if (row)
+ *row = newRow;
+ if (col)
+ *col = newCol;
+
+ return true;
+}
+
+// public
+bool kpToolWidgetBase::hasNextOption (int *row, int *col) const
+{
+#if DEBUG_KP_TOOL_WIDGET_BASE
+ kdDebug () << "kpToolWidgetBase" << name ()
+ << "::hasNextOption() current row=" << m_selectedRow
+ << " col=" << m_selectedCol
+ << endl;
+#endif
+
+ if (row)
+ *row = -1;
+ if (col)
+ *col = -1;
+
+
+ if (m_selectedRow < 0 || m_selectedCol < 0)
+ return false;
+
+ int newRow = m_selectedRow,
+ newCol = m_selectedCol;
+
+ newCol++;
+ if (newCol >= (int) m_pixmaps [newRow].count ())
+ {
+ newRow++;
+ if (newRow >= (int) m_pixmaps.count ())
+ return false;
+
+ newCol = 0;
+ if (newCol >= (int) m_pixmaps [newRow].count ())
+ return false;
+ }
+
+
+ if (row)
+ *row = newRow;
+ if (col)
+ *col = newCol;
+
+ return true;
+}
+
+
+// public slot virtual
+bool kpToolWidgetBase::setSelected (int row, int col, bool saveAsDefault)
+{
+#if DEBUG_KP_TOOL_WIDGET_BASE
+ kdDebug () << "kpToolWidgetBase::setSelected(row=" << row
+ << ",col=" << col
+ << ",saveAsDefault=" << saveAsDefault
+ << ")"
+ << endl;
+#endif
+
+ if (row < 0 || col < 0 ||
+ row >= (int) m_pixmapRects.count () || col >= (int) m_pixmapRects [row].count ())
+ {
+ #if DEBUG_KP_TOOL_WIDGET_BASE
+ kdDebug () << "\tout of range" << endl;
+ #endif
+ return false;
+ }
+
+ if (row == m_selectedRow && col == m_selectedCol)
+ {
+ #if DEBUG_KP_TOOL_WIDGET_BASE
+ kdDebug () << "\tNOP" << endl;
+ #endif
+
+ if (saveAsDefault)
+ saveSelectedAsDefault ();
+
+ return true;
+ }
+
+ const int wasSelectedRow = m_selectedRow;
+ const int wasSelectedCol = m_selectedCol;
+
+ m_selectedRow = row, m_selectedCol = col;
+
+ if (wasSelectedRow >= 0 && wasSelectedCol >= 0)
+ {
+ // unhighlight old option
+ update (m_pixmapRects [wasSelectedRow][wasSelectedCol]);
+ }
+
+ // highlight new option
+ update (m_pixmapRects [row][col]);
+
+#if DEBUG_KP_TOOL_WIDGET_BASE
+ kdDebug () << "\tOK" << endl;
+#endif
+
+ if (saveAsDefault)
+ saveSelectedAsDefault ();
+
+ emit optionSelected (row, col);
+ return true;
+}
+
+// public slot
+bool kpToolWidgetBase::setSelected (int row, int col)
+{
+ return setSelected (row, col, true/*set as default*/);
+}
+
+
+// public slot
+bool kpToolWidgetBase::selectPreviousOption ()
+{
+ int newRow, newCol;
+ if (!hasPreviousOption (&newRow, &newCol))
+ return false;
+
+ return setSelected (newRow, newCol);
+}
+
+// public slot
+bool kpToolWidgetBase::selectNextOption ()
+{
+ int newRow, newCol;
+ if (!hasNextOption (&newRow, &newCol))
+ return false;
+
+ return setSelected (newRow, newCol);
+}
+
+
+// protected virtual [base QWidget]
+void kpToolWidgetBase::mousePressEvent (QMouseEvent *e)
+{
+ e->ignore ();
+
+ if (e->button () != Qt::LeftButton)
+ return;
+
+
+ for (int i = 0; i < (int) m_pixmapRects.count (); i++)
+ {
+ for (int j = 0; j < (int) m_pixmapRects [i].count (); j++)
+ {
+ if (m_pixmapRects [i][j].contains (e->pos ()))
+ {
+ setSelected (i, j);
+ e->accept ();
+ return;
+ }
+ }
+ }
+}
+
+// protected virtual [base QFrame]
+void kpToolWidgetBase::drawContents (QPainter *painter)
+{
+#if DEBUG_KP_TOOL_WIDGET_BASE && 1
+ kdDebug () << "kpToolWidgetBase::drawContents(): rect=" << contentsRect () << endl;
+#endif
+
+ for (int i = 0; i < (int) m_pixmaps.count (); i++)
+ {
+ #if DEBUG_KP_TOOL_WIDGET_BASE && 1
+ kdDebug () << "\tRow: " << i << endl;
+ #endif
+
+ for (int j = 0; j < (int) m_pixmaps [i].count (); j++)
+ {
+ QRect rect = m_pixmapRects [i][j];
+ QPixmap pixmap = m_pixmaps [i][j];
+
+ #if DEBUG_KP_TOOL_WIDGET_BASE && 1
+ kdDebug () << "\t\tCol: " << j << " rect=" << rect << endl;
+ #endif
+
+ if (i == m_selectedRow && j == m_selectedCol)
+ {
+ painter->fillRect (rect, Qt::blue/*selection color*/);
+
+ if (m_invertSelectedPixmap)
+ kpEffectInvertCommand::apply (&pixmap);
+ }
+
+ #if DEBUG_KP_TOOL_WIDGET_BASE && 1
+ kdDebug () << "\t\t\tdraw pixmap @ x="
+ << rect.x () + (rect.width () - pixmap.width ()) / 2
+ << " y="
+ << rect.y () + (rect.height () - pixmap.height ()) / 2
+ << endl;
+
+ #endif
+
+ painter->drawPixmap (QPoint (rect.x () + (rect.width () - pixmap.width ()) / 2,
+ rect.y () + (rect.height () - pixmap.height ()) / 2),
+ pixmap);
+ }
+ }
+}
+
+#include <kptoolwidgetbase.moc>
diff --git a/kolourpaint/widgets/kptoolwidgetbase.h b/kolourpaint/widgets/kptoolwidgetbase.h
new file mode 100644
index 00000000..a23f9a16
--- /dev/null
+++ b/kolourpaint/widgets/kptoolwidgetbase.h
@@ -0,0 +1,112 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kp_tool_widget_base_h__
+#define __kp_tool_widget_base_h__
+
+#include <qframe.h>
+#include <qpair.h>
+#include <qpixmap.h>
+#include <qrect.h>
+#include <qvaluevector.h>
+#include <qwidget.h>
+
+
+class QPainter;
+
+
+// TODO: frame becomes a combobox when its parent kpToolToolBar becomes too small
+class kpToolWidgetBase : public QFrame
+{
+Q_OBJECT
+
+public:
+ kpToolWidgetBase (QWidget *parent, const char *name); // must provide a name for config to work
+ virtual ~kpToolWidgetBase ();
+
+public:
+ void addOption (const QPixmap &pixmap, const QString &toolTip = QString::null);
+ void startNewOptionRow ();
+
+ // Call this at the end of your constructor.
+ // If the default row & col could not be read from the config,
+ // <fallBackRow> & <fallBackCol> are passed to setSelected().
+ void finishConstruction (int fallBackRow, int fallBackCol);
+
+private:
+ QValueVector <int> spreadOutElements (const QValueVector <int> &sizes, int maxSize);
+
+public: // (only have to use these if you don't use finishConstruction())
+ // (rereads from config file)
+ QPair <int, int> defaultSelectedRowAndCol () const;
+ int defaultSelectedRow () const;
+ int defaultSelectedCol () const;
+
+ void saveSelectedAsDefault () const;
+
+ void relayoutOptions ();
+
+public:
+ int selectedRow () const;
+ int selectedCol () const;
+
+ int selected () const;
+
+ bool hasPreviousOption (int *row = 0, int *col = 0) const;
+ bool hasNextOption (int *row = 0, int *col = 0) const;
+
+public slots:
+ // (returns whether <row> and <col> were in range)
+ virtual bool setSelected (int row, int col, bool saveAsDefault);
+ bool setSelected (int row, int col);
+
+ bool selectPreviousOption ();
+ bool selectNextOption ();
+
+signals:
+ void optionSelected (int row, int col);
+
+protected:
+ virtual void mousePressEvent (QMouseEvent *e);
+ virtual void drawContents (QPainter *painter);
+
+ void setInvertSelectedPixmap (bool yes = true) { m_invertSelectedPixmap = yes; }
+ bool m_invertSelectedPixmap;
+
+ // coulbe be a QFrame or a ComboBox
+ QWidget *m_baseWidget;
+
+ QValueVector < QValueVector <QPixmap> > m_pixmaps;
+ QValueVector < QValueVector <QString> > m_toolTips;
+
+ QValueVector < QValueVector <QRect> > m_pixmapRects;
+
+ int m_selectedRow, m_selectedCol;
+};
+
+#endif // __kp_tool_widget_base_h__
diff --git a/kolourpaint/widgets/kptoolwidgetbrush.cpp b/kolourpaint/widgets/kptoolwidgetbrush.cpp
new file mode 100644
index 00000000..046dc8b5
--- /dev/null
+++ b/kolourpaint/widgets/kptoolwidgetbrush.cpp
@@ -0,0 +1,184 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define DEBUG_KP_TOOL_WIDGET_BRUSH 0
+
+
+#include <kptoolwidgetbrush.h>
+
+#include <qbitmap.h>
+#include <qpainter.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kpdefs.h>
+
+
+/* sync: <brushes> */
+static int brushSize [][3] =
+{
+ {8, 4, 1/*like Pen*/},
+ {9, 5, 2},
+ {9, 5, 2},
+ {9, 5, 2}
+};
+
+#define BRUSH_SIZE_NUM_COLS (int (sizeof (brushSize [0]) / sizeof (brushSize [0][0])))
+#define BRUSH_SIZE_NUM_ROWS (int (sizeof (brushSize) / sizeof (brushSize [0])))
+
+kpToolWidgetBrush::kpToolWidgetBrush (QWidget *parent, const char *name)
+ : kpToolWidgetBase (parent, name)
+{
+ setInvertSelectedPixmap ();
+
+ QPixmap *pm = m_brushBitmaps;
+
+ for (int shape = 0; shape < BRUSH_SIZE_NUM_ROWS; shape++)
+ {
+ for (int i = 0; i < BRUSH_SIZE_NUM_COLS; i++)
+ {
+ int w = (width () - 2/*margin*/ - 2/*spacing*/) / BRUSH_SIZE_NUM_COLS;
+ int h = (height () - 2/*margin*/ - 3/*spacing*/) / BRUSH_SIZE_NUM_ROWS;
+ pm->resize ((w <= 0 ? width () : w),
+ (h <= 0 ? height () : h));
+
+ const int s = brushSize [shape][i];
+ QRect rect;
+
+ if (s >= pm->width () || s >= pm->height ())
+ rect = QRect (0, 0, pm->width (), pm->height ());
+ else
+ {
+ rect = QRect ((pm->width () - s) / 2,
+ (pm->height () - s) / 2,
+ s,
+ s);
+ }
+
+ #if DEBUG_KP_TOOL_WIDGET_BRUSH
+ kdDebug () << "kpToolWidgetBrush::kpToolWidgetBrush() rect=" << rect << endl;
+ #endif
+
+ pm->fill (Qt::white);
+
+ QPainter painter (pm);
+ painter.setPen (Qt::black);
+ painter.setBrush (Qt::black);
+
+ // sync: <brushes>
+ switch (shape)
+ {
+ case 0:
+ painter.drawEllipse (rect);
+ break;
+ case 1:
+ painter.drawRect (rect);
+ break;
+ case 2:
+ painter.drawLine (rect.topRight (), rect.bottomLeft ());
+ break;
+ case 3:
+ painter.drawLine (rect.topLeft (), rect.bottomRight ());
+ break;
+ }
+ painter.end ();
+
+ pm->setMask (pm->createHeuristicMask ());
+ addOption (*pm, brushName (shape, i)/*tooltip*/);
+
+ pm++;
+ }
+
+ startNewOptionRow ();
+ }
+
+ finishConstruction (0, 0);
+}
+
+kpToolWidgetBrush::~kpToolWidgetBrush ()
+{
+}
+
+
+// private
+QString kpToolWidgetBrush::brushName (int shape, int whichSize)
+{
+ int s = brushSize [shape][whichSize];
+
+ if (s == 1)
+ return i18n ("1x1");
+
+ QString shapeName;
+
+ // sync: <brushes>
+ switch (shape)
+ {
+ case 0:
+ shapeName = i18n ("Circle");
+ break;
+ case 1:
+ shapeName = i18n ("Square");
+ break;
+ case 2:
+ // TODO: is this really the name of a shape? :)
+ shapeName = i18n ("Slash");
+ break;
+ case 3:
+ // TODO: is this really the name of a shape? :)
+ shapeName = i18n ("Backslash");
+ break;
+ }
+
+ if (shapeName.isEmpty ())
+ return QString::null;
+
+ return i18n ("%1x%2 %3").arg (s).arg (s).arg (shapeName);
+}
+
+QPixmap kpToolWidgetBrush::brush () const
+{
+ return m_brushBitmaps [selectedRow () * BRUSH_SIZE_NUM_COLS + selectedCol ()];
+}
+
+bool kpToolWidgetBrush::brushIsDiagonalLine () const
+{
+ // sync: <brushes>
+ return (selectedRow () >= 2);
+}
+
+// virtual protected slot [base kpToolWidgetBase]
+bool kpToolWidgetBrush::setSelected (int row, int col, bool saveAsDefault)
+{
+ const bool ret = kpToolWidgetBase::setSelected (row, col, saveAsDefault);
+ if (ret)
+ emit brushChanged (brush (), brushIsDiagonalLine ());
+ return ret;
+}
+
+#include <kptoolwidgetbrush.moc>
diff --git a/kolourpaint/widgets/kptoolwidgetbrush.h b/kolourpaint/widgets/kptoolwidgetbrush.h
new file mode 100644
index 00000000..db222e79
--- /dev/null
+++ b/kolourpaint/widgets/kptoolwidgetbrush.h
@@ -0,0 +1,61 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kptoolwidgetbrush_h__
+#define __kptoolwidgetbrush_h__
+
+#include <qpixmap.h>
+
+#include <kptoolwidgetbase.h>
+
+class kpToolWidgetBrush : public kpToolWidgetBase
+{
+Q_OBJECT
+
+public:
+ kpToolWidgetBrush (QWidget *parent, const char *name);
+ virtual ~kpToolWidgetBrush ();
+
+private:
+ QString brushName (int shape, int whichSize);
+
+public:
+ QPixmap brush () const;
+ bool brushIsDiagonalLine () const;
+
+signals:
+ void brushChanged (const QPixmap &pixmap, bool isDiagonalLine);
+
+protected slots:
+ virtual bool setSelected (int row, int col, bool saveAsDefault);
+
+private:
+ QPixmap m_brushBitmaps [16];
+};
+
+#endif // __kptoolwidgetbrush_h__
diff --git a/kolourpaint/widgets/kptoolwidgeterasersize.cpp b/kolourpaint/widgets/kptoolwidgeterasersize.cpp
new file mode 100644
index 00000000..cc58c0d1
--- /dev/null
+++ b/kolourpaint/widgets/kptoolwidgeterasersize.cpp
@@ -0,0 +1,161 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG_KP_TOOL_WIDGET_ERASER_SIZE 0
+
+
+#include <kptoolwidgeterasersize.h>
+
+#include <qbitmap.h>
+#include <qpainter.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kpcolor.h>
+#include <kptool.h>
+
+
+static int eraserSizes [] = {2, 3, 5, 9, 17, 29};
+static const int numEraserSizes = int (sizeof (eraserSizes) / sizeof (eraserSizes [0]));
+
+
+kpToolWidgetEraserSize::kpToolWidgetEraserSize (QWidget *parent, const char *name)
+ : kpToolWidgetBase (parent, name)
+{
+ setInvertSelectedPixmap ();
+
+ m_cursorPixmaps = new QPixmap [numEraserSizes];
+ QPixmap *cursorPixmap = m_cursorPixmaps;
+
+ for (int i = 0; i < numEraserSizes; i++)
+ {
+ if (i == 3 || i == 5)
+ startNewOptionRow ();
+
+ int s = eraserSizes [i];
+
+ cursorPixmap->resize (s, s);
+ cursorPixmap->fill (Qt::black);
+
+
+ QPixmap previewPixmap (s, s);
+ if (i < 3)
+ {
+ // HACK: kpToolWidgetBase's layout code sucks and gives uneven spacing
+ previewPixmap.resize ((width () - 4) / 3, 9);
+ }
+
+ QPainter painter (&previewPixmap);
+ QRect rect ((previewPixmap.width () - s) / 2, (previewPixmap.height () - s) / 2, s, s);
+ painter.fillRect (rect, Qt::black);
+ painter.end ();
+
+ QBitmap mask (previewPixmap.width (), previewPixmap.height ());
+ mask.fill (Qt::color0/*transparent*/);
+
+ QPainter maskPainter (&mask);
+ maskPainter.fillRect (rect, Qt::color1/*opaque*/);
+ maskPainter.end ();
+
+ previewPixmap.setMask (mask);
+
+
+ addOption (previewPixmap, i18n ("%1x%2").arg (s).arg (s)/*tooltip*/);
+
+
+ cursorPixmap++;
+ }
+
+ finishConstruction (1, 0);
+}
+
+kpToolWidgetEraserSize::~kpToolWidgetEraserSize ()
+{
+ delete [] m_cursorPixmaps;
+}
+
+int kpToolWidgetEraserSize::eraserSize () const
+{
+ return eraserSizes [selected ()];
+}
+
+QPixmap kpToolWidgetEraserSize::cursorPixmap (const kpColor &color) const
+{
+#if DEBUG_KP_TOOL_WIDGET_ERASER_SIZE
+ kdDebug () << "kpToolWidgetEraseSize::cursorPixmap() selected=" << selected ()
+ << " numEraserSizes=" << numEraserSizes
+ << endl;
+#endif
+
+ // TODO: why are we even storing m_cursorPixmaps?
+ QPixmap pixmap = m_cursorPixmaps [selected ()];
+ if (color.isOpaque ())
+ pixmap.fill (color.toQColor ());
+
+
+ bool showBorder = (pixmap.width () > 2 && pixmap.height () > 2);
+
+ if (showBorder)
+ {
+ QPainter painter (&pixmap);
+ painter.setPen (Qt::black);
+ painter.drawRect (pixmap.rect ());
+ }
+
+
+ if (color.isTransparent ())
+ {
+ QBitmap maskBitmap (pixmap.width (), pixmap.height ());
+ maskBitmap.fill (Qt::color0/*transparent*/);
+
+
+ if (showBorder)
+ {
+ QPainter maskBitmapPainter (&maskBitmap);
+ maskBitmapPainter.setPen (Qt::color1/*opaque*/);
+ maskBitmapPainter.drawRect (maskBitmap.rect ());
+ }
+
+
+ pixmap.setMask (maskBitmap);
+ }
+
+
+ return pixmap;
+}
+
+// virtual protected slot [base kpToolWidgetBase]
+bool kpToolWidgetEraserSize::setSelected (int row, int col, bool saveAsDefault)
+{
+ const bool ret = kpToolWidgetBase::setSelected (row, col, saveAsDefault);
+ if (ret)
+ emit eraserSizeChanged (eraserSize ());
+ return ret;
+}
+
+#include <kptoolwidgeterasersize.moc>
diff --git a/kolourpaint/widgets/kptoolwidgeterasersize.h b/kolourpaint/widgets/kptoolwidgeterasersize.h
new file mode 100644
index 00000000..71093fd6
--- /dev/null
+++ b/kolourpaint/widgets/kptoolwidgeterasersize.h
@@ -0,0 +1,59 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kptoolwidgeterasersize_h__
+#define __kptoolwidgeterasersize_h__
+
+#include <qpixmap.h>
+#include <kptoolwidgetbase.h>
+
+
+class kpColor;
+
+class kpToolWidgetEraserSize : public kpToolWidgetBase
+{
+Q_OBJECT
+
+public:
+ kpToolWidgetEraserSize (QWidget *parent, const char *name);
+ virtual ~kpToolWidgetEraserSize ();
+
+ int eraserSize () const;
+ QPixmap cursorPixmap (const kpColor &color) const;
+
+signals:
+ void eraserSizeChanged (int size);
+
+protected slots:
+ virtual bool setSelected (int row, int col, bool saveAsDefault);
+
+private:
+ QPixmap *m_cursorPixmaps;
+};
+
+#endif // __kptoolwidgeterasersize_h__
diff --git a/kolourpaint/widgets/kptoolwidgetfillstyle.cpp b/kolourpaint/widgets/kptoolwidgetfillstyle.cpp
new file mode 100644
index 00000000..74c174ce
--- /dev/null
+++ b/kolourpaint/widgets/kptoolwidgetfillstyle.cpp
@@ -0,0 +1,222 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG_KP_TOOL_WIDGET_FILL_STYLE 0
+
+
+#include <kptoolwidgetfillstyle.h>
+
+#include <qbitmap.h>
+#include <qbrush.h>
+#include <qpainter.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <kpcolor.h>
+#include <kpdefs.h>
+#include <kptool.h>
+
+
+kpToolWidgetFillStyle::kpToolWidgetFillStyle (QWidget *parent, const char *name)
+ : kpToolWidgetBase (parent, name)
+{
+ setInvertSelectedPixmap ();
+
+ for (int i = 0; i < (int) FillStyleNum; i++)
+ {
+ QPixmap pixmap;
+
+ pixmap = fillStylePixmap ((FillStyle) i,
+ (width () - 2/*margin*/) * 3 / 4,
+ (height () - 2/*margin*/ - 2/*spacing*/) * 3 / (3 * 4));
+ addOption (pixmap, fillStyleName ((FillStyle) i)/*tooltip*/);
+
+ startNewOptionRow ();
+ }
+
+ finishConstruction (0, 0);
+}
+
+kpToolWidgetFillStyle::~kpToolWidgetFillStyle ()
+{
+}
+
+
+// private
+QPixmap kpToolWidgetFillStyle::fillStylePixmap (FillStyle fs, int w, int h)
+{
+ QPixmap pixmap ((w <= 0 ? width () : w), (h <= 0 ? height () : h));
+ pixmap.fill (Qt::white);
+
+ QPainter painter (&pixmap);
+
+ painter.setPen (QPen (Qt::black, 2));
+ painter.setBrush (brushForFillStyle (fs,
+ kpColor (Qt::black.rgb ())/*foreground*/,
+ kpColor (Qt::gray.rgb ())/*background*/));
+
+ painter.drawRect (2, 2, w - 3, h - 3);
+
+ painter.end ();
+
+
+ QBitmap mask (pixmap.width (), pixmap.height ());
+ mask.fill (Qt::color0);
+
+ painter.begin (&mask);
+ painter.setPen (QPen (Qt::color1, 2));
+
+ if (fs == FillWithBackground || fs == FillWithForeground)
+ painter.setBrush (Qt::color1);
+
+ painter.drawRect (2, 2, w - 3, h - 3);
+
+ painter.end ();
+
+ pixmap.setMask (mask);
+
+ return pixmap;
+}
+
+// private
+QString kpToolWidgetFillStyle::fillStyleName (FillStyle fs) const
+{
+ // do not complain about the "useless" breaks
+ // as the return statements might not be return statements one day
+
+ switch (fs)
+ {
+ case NoFill:
+ return i18n ("No Fill");
+ break;
+ case FillWithBackground:
+ return i18n ("Fill with Background Color");
+ break;
+ case FillWithForeground:
+ return i18n ("Fill with Foreground Color");
+ break;
+ default:
+ return QString::null;
+ break;
+ }
+}
+
+
+// public
+kpToolWidgetFillStyle::FillStyle kpToolWidgetFillStyle::fillStyle () const
+{
+#if DEBUG_KP_TOOL_WIDGET_FILL_STYLE
+ kdDebug () << "kpToolWidgetFillStyle::fillStyle() selected="
+ << selectedRow ()
+ << endl;
+#endif
+ return (FillStyle) selectedRow ();
+}
+
+// public static
+QBrush kpToolWidgetFillStyle::maskBrushForFillStyle (FillStyle fs,
+ const kpColor &foregroundColor,
+ const kpColor &backgroundColor)
+{
+ // do not complain about the "useless" breaks
+ // as the return statements might not be return statements one day
+
+ switch (fs)
+ {
+ case NoFill:
+ return Qt::NoBrush;
+ break;
+ case FillWithBackground:
+ return QBrush (backgroundColor.maskColor ());
+ break;
+ case FillWithForeground:
+ return QBrush (foregroundColor.maskColor ());
+ break;
+ default:
+ return Qt::NoBrush;
+ break;
+ }
+}
+
+QBrush kpToolWidgetFillStyle::maskBrush (const kpColor &foregroundColor,
+ const kpColor &backgroundColor)
+{
+ return maskBrushForFillStyle (fillStyle (), foregroundColor, backgroundColor);
+}
+
+// public static
+QBrush kpToolWidgetFillStyle::brushForFillStyle (FillStyle fs,
+ const kpColor &foregroundColor,
+ const kpColor &backgroundColor)
+{
+ // do not complain about the "useless" breaks
+ // as the return statements might not be return statements one day
+
+ // sync: kptoolpolygon.cpp pixmap()
+
+ switch (fs)
+ {
+ case NoFill:
+ return Qt::NoBrush;
+ break;
+ case FillWithBackground:
+ if (backgroundColor.isOpaque ())
+ return QBrush (backgroundColor.toQColor ());
+ else
+ return Qt::NoBrush;
+ break;
+ case FillWithForeground:
+ if (foregroundColor.isOpaque ())
+ return QBrush (foregroundColor.toQColor ());
+ else
+ return Qt::NoBrush;
+ break;
+ default:
+ return Qt::NoBrush;
+ break;
+ }
+}
+
+// public
+QBrush kpToolWidgetFillStyle::brush (const kpColor &foregroundColor,
+ const kpColor &backgroundColor)
+{
+ return brushForFillStyle (fillStyle (), foregroundColor, backgroundColor);
+}
+
+
+// virtual protected slot [base kpToolWidgetBase]
+bool kpToolWidgetFillStyle::setSelected (int row, int col, bool saveAsDefault)
+{
+ const bool ret = kpToolWidgetBase::setSelected (row, col, saveAsDefault);
+ if (ret)
+ emit fillStyleChanged (fillStyle ());
+ return ret;
+}
+
+#include <kptoolwidgetfillstyle.moc>
diff --git a/kolourpaint/widgets/kptoolwidgetfillstyle.h b/kolourpaint/widgets/kptoolwidgetfillstyle.h
new file mode 100644
index 00000000..219d47f2
--- /dev/null
+++ b/kolourpaint/widgets/kptoolwidgetfillstyle.h
@@ -0,0 +1,80 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kptoolwidgetfillstyle_h__
+#define __kptoolwidgetfillstyle_h__
+
+#include <kptoolwidgetbase.h>
+
+class QBrush;
+
+class kpColor;
+
+class kpToolWidgetFillStyle : public kpToolWidgetBase
+{
+Q_OBJECT
+
+public:
+ kpToolWidgetFillStyle (QWidget *parent, const char *name);
+ virtual ~kpToolWidgetFillStyle ();
+
+ enum FillStyle
+ {
+ NoFill,
+ FillWithBackground,
+ FillWithForeground,
+ FillStyleNum /* not (a valid FillStyle) */
+ };
+
+private:
+ QPixmap fillStylePixmap (FillStyle fs, int width, int height);
+ QString fillStyleName (FillStyle fs) const;
+
+public:
+ FillStyle fillStyle () const;
+
+ static QBrush maskBrushForFillStyle (FillStyle fs,
+ const kpColor &foregroundColor,
+ const kpColor &backgroundColor);
+ QBrush maskBrush (const kpColor &foregroundColor,
+ const kpColor &backgroundColor);
+
+ static QBrush brushForFillStyle (FillStyle fs,
+ const kpColor &foregroundColor,
+ const kpColor &backgroundColor);
+ QBrush brush (const kpColor &foregroundColor,
+ const kpColor &backgroundColor);
+
+signals:
+ void fillStyleChanged (kpToolWidgetFillStyle::FillStyle fillStyle);
+
+protected slots:
+ virtual bool setSelected (int row, int col, bool saveAsDefault);
+};
+
+#endif // __kptoolwidgetfillstyle_h__
diff --git a/kolourpaint/widgets/kptoolwidgetlinewidth.cpp b/kolourpaint/widgets/kptoolwidgetlinewidth.cpp
new file mode 100644
index 00000000..27e34ecb
--- /dev/null
+++ b/kolourpaint/widgets/kptoolwidgetlinewidth.cpp
@@ -0,0 +1,97 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <kptoolwidgetlinewidth.h>
+
+#include <qbitmap.h>
+#include <qpainter.h>
+
+#include <klocale.h>
+
+
+static int lineWidths [] = {1, 2, 3, 5, 8};
+
+kpToolWidgetLineWidth::kpToolWidgetLineWidth (QWidget *parent, const char *name)
+ : kpToolWidgetBase (parent, name)
+{
+ setInvertSelectedPixmap ();
+
+ int numLineWidths = sizeof (lineWidths) / sizeof (lineWidths [0]);
+
+ int w = (width () - 2/*margin*/) * 3 / 4;
+ int h = (height () - 2/*margin*/ - (numLineWidths - 1)/*spacing*/) * 3 / (numLineWidths * 4);
+
+ for (int i = 0; i < numLineWidths; i++)
+ {
+ QPixmap pixmap ((w <= 0 ? width () : w),
+ (h <= 0 ? height () : h));
+ pixmap.fill (Qt::white);
+
+ QBitmap maskBitmap (pixmap.width (), pixmap.height ());
+ maskBitmap.fill (Qt::color0/*transparent*/);
+
+
+ QPainter painter (&pixmap), maskPainter (&maskBitmap);
+ painter.setPen (Qt::black), maskPainter.setPen (Qt::color1/*opaque*/);
+ painter.setBrush (Qt::black), maskPainter.setBrush (Qt::color1/*opaque*/);
+
+ QRect rect = QRect (0, (pixmap.height () - lineWidths [i]) / 2,
+ pixmap.width (), lineWidths [i]);
+ painter.drawRect (rect), maskPainter.drawRect (rect);
+
+ painter.end (), maskPainter.end ();
+
+
+ pixmap.setMask (maskBitmap);
+
+ addOption (pixmap, QString::number (lineWidths [i]));
+ startNewOptionRow ();
+ }
+
+ finishConstruction (0, 0);
+}
+
+kpToolWidgetLineWidth::~kpToolWidgetLineWidth ()
+{
+}
+
+int kpToolWidgetLineWidth::lineWidth () const
+{
+ return lineWidths [selectedRow ()];
+}
+
+// virtual protected slot [base kpToolWidgetBase]
+bool kpToolWidgetLineWidth::setSelected (int row, int col, bool saveAsDefault)
+{
+ const bool ret = kpToolWidgetBase::setSelected (row, col, saveAsDefault);
+ if (ret)
+ emit lineWidthChanged (lineWidth ());
+ return ret;
+}
+
+#include <kptoolwidgetlinewidth.moc>
diff --git a/kolourpaint/widgets/kptoolwidgetlinewidth.h b/kolourpaint/widgets/kptoolwidgetlinewidth.h
new file mode 100644
index 00000000..3255e443
--- /dev/null
+++ b/kolourpaint/widgets/kptoolwidgetlinewidth.h
@@ -0,0 +1,51 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kptoolwidgetlinewidth_h__
+#define __kptoolwidgetlinewidth_h__
+
+#include <kptoolwidgetbase.h>
+
+class kpToolWidgetLineWidth : public kpToolWidgetBase
+{
+Q_OBJECT
+
+public:
+ kpToolWidgetLineWidth (QWidget *parent, const char *name);
+ virtual ~kpToolWidgetLineWidth ();
+
+ int lineWidth () const;
+
+signals:
+ void lineWidthChanged (int width);
+
+protected slots:
+ virtual bool setSelected (int row, int col, bool saveAsDefault);
+};
+
+#endif // __kptoolwidgetlinewidth_h__
diff --git a/kolourpaint/widgets/kptoolwidgetopaqueortransparent.cpp b/kolourpaint/widgets/kptoolwidgetopaqueortransparent.cpp
new file mode 100644
index 00000000..41b55d0f
--- /dev/null
+++ b/kolourpaint/widgets/kptoolwidgetopaqueortransparent.cpp
@@ -0,0 +1,100 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define DEBUG_KP_TOOL_WIDGET_OPAQUE_OR_TRANSPARENT 0
+
+
+#include <kptoolwidgetopaqueortransparent.h>
+
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <klocale.h>
+
+
+kpToolWidgetOpaqueOrTransparent::kpToolWidgetOpaqueOrTransparent (QWidget *parent, const char *name)
+ : kpToolWidgetBase (parent, name)
+{
+ setInvertSelectedPixmap (false);
+
+ addOption (UserIcon ("option_opaque"), i18n ("Opaque")/*tooltip*/);
+ startNewOptionRow ();
+ addOption (UserIcon ("option_transparent"), i18n ("Transparent")/*tooltip*/);
+
+ finishConstruction (0, 0);
+}
+
+kpToolWidgetOpaqueOrTransparent::~kpToolWidgetOpaqueOrTransparent ()
+{
+}
+
+
+// public
+bool kpToolWidgetOpaqueOrTransparent::isOpaque () const
+{
+ return (selected () == 0);
+}
+
+// public
+bool kpToolWidgetOpaqueOrTransparent::isTransparent () const
+{
+ return (!isOpaque ());
+}
+
+// public
+void kpToolWidgetOpaqueOrTransparent::setOpaque (bool yes)
+{
+#if DEBUG_KP_TOOL_WIDGET_OPAQUE_OR_TRANSPARENT && 1
+ kdDebug () << "kpToolWidgetOpaqueOrTransparent::setOpaque(" << yes << ")" << endl;
+#endif
+ setSelected (yes ? 0 : 1, 0, false/*don't save*/);
+}
+
+// public
+void kpToolWidgetOpaqueOrTransparent::setTransparent (bool yes)
+{
+#if DEBUG_KP_TOOL_WIDGET_OPAQUE_OR_TRANSPARENT && 1
+ kdDebug () << "kpToolWidgetOpaqueOrTransparent::setTransparent(" << yes << ")" << endl;
+#endif
+ setSelected (yes ? 1 : 0, 0, false/*don't save*/);
+}
+
+
+// protected slot virtual [base kpToolWidgetBase]
+bool kpToolWidgetOpaqueOrTransparent::setSelected (int row, int col, bool saveAsDefault)
+{
+#if DEBUG_KP_TOOL_WIDGET_OPAQUE_OR_TRANSPARENT && 1
+ kdDebug () << "kpToolWidgetOpaqueOrTransparent::setSelected("
+ << row << "," << col << ")" << endl;
+#endif
+ const bool ret = kpToolWidgetBase::setSelected (row, col, saveAsDefault);
+ if (ret)
+ emit isOpaqueChanged (isOpaque ());
+ return ret;
+}
+
+
+#include <kptoolwidgetopaqueortransparent.moc>
diff --git a/kolourpaint/widgets/kptoolwidgetopaqueortransparent.h b/kolourpaint/widgets/kptoolwidgetopaqueortransparent.h
new file mode 100644
index 00000000..c24cd308
--- /dev/null
+++ b/kolourpaint/widgets/kptoolwidgetopaqueortransparent.h
@@ -0,0 +1,56 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kp_tool_widget_opaque_or_transparent_h__
+#define __kp_tool_widget_opaque_or_transparent_h__
+
+
+#include <kptoolwidgetbase.h>
+
+class kpToolWidgetOpaqueOrTransparent : public kpToolWidgetBase
+{
+Q_OBJECT
+
+public:
+ kpToolWidgetOpaqueOrTransparent (QWidget *parent, const char *name);
+ virtual ~kpToolWidgetOpaqueOrTransparent ();
+
+ bool isOpaque () const;
+ bool isTransparent () const;
+ void setOpaque (bool yes = true);
+ void setTransparent (bool yes = true);
+
+signals:
+ void isOpaqueChanged (bool isOpaque_);
+
+protected slots:
+ virtual bool setSelected (int row, int col, bool saveAsDefault);
+};
+
+
+#endif // kp_tool_widget_opaque_or_transparent_h__
diff --git a/kolourpaint/widgets/kptoolwidgetspraycansize.cpp b/kolourpaint/widgets/kptoolwidgetspraycansize.cpp
new file mode 100644
index 00000000..161e5015
--- /dev/null
+++ b/kolourpaint/widgets/kptoolwidgetspraycansize.cpp
@@ -0,0 +1,119 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define DEBUG_KP_TOOL_WIDGET_SPRAYCAN_SIZE 0
+
+
+#include <kptoolwidgetspraycansize.h>
+
+#include <qbitmap.h>
+#include <qimage.h>
+#include <qpainter.h>
+
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <klocale.h>
+
+#include <kppixmapfx.h>
+
+
+static int spraycanSizes [] = {9, 17, 29};
+
+kpToolWidgetSpraycanSize::kpToolWidgetSpraycanSize (QWidget *parent, const char *name)
+ : kpToolWidgetBase (parent, name)
+{
+#if DEBUG_KP_TOOL_WIDGET_SPRAYCAN_SIZE
+ kdDebug () << "kpToolWidgetSpraycanSize::kpToolWidgetSpraycanSize() CALLED!" << endl;
+#endif
+
+ for (int i = 0; i < int (sizeof (spraycanSizes) / sizeof (spraycanSizes [0])); i++)
+ {
+ int s = spraycanSizes [i];
+ QString iconName = QString ("tool_spraycan_%1x%1").arg (s).arg(s);
+
+ #if DEBUG_KP_TOOL_WIDGET_SPRAYCAN_SIZE
+ kdDebug () << "\ticonName=" << iconName << endl;
+ #endif
+
+ QPixmap pixmap (s, s);
+ pixmap.fill (Qt::white);
+
+ QPainter painter (&pixmap);
+ painter.drawPixmap (0, 0, UserIcon (iconName));
+ painter.end ();
+
+ QImage image = kpPixmapFX::convertToImage (pixmap);
+
+ QBitmap mask (pixmap.width (), pixmap.height ());
+ mask.fill (Qt::color0);
+
+ painter.begin (&mask);
+ painter.setPen (Qt::color1);
+
+ for (int y = 0; y < image.height (); y++)
+ {
+ for (int x = 0; x < image.width (); x++)
+ {
+ if ((image.pixel (x, y) & RGB_MASK) == 0/*black*/)
+ painter.drawPoint (x, y); // mark as opaque
+ }
+ }
+
+ painter.end ();
+
+ pixmap.setMask (mask);
+
+ addOption (pixmap, i18n ("%1x%2").arg (s).arg (s)/*tooltip*/);
+ if (i == 1)
+ startNewOptionRow ();
+ }
+
+ finishConstruction (0, 0);
+}
+
+kpToolWidgetSpraycanSize::~kpToolWidgetSpraycanSize ()
+{
+}
+
+
+// public
+int kpToolWidgetSpraycanSize::spraycanSize () const
+{
+ return spraycanSizes [selected ()];
+}
+
+// protected slot virtual [base kpToolWidgetBase]
+bool kpToolWidgetSpraycanSize::setSelected (int row, int col, bool saveAsDefault)
+{
+ const bool ret = kpToolWidgetBase::setSelected (row, col, saveAsDefault);
+ if (ret)
+ emit spraycanSizeChanged (spraycanSize ());
+ return ret;
+}
+
+#include <kptoolwidgetspraycansize.moc>
diff --git a/kolourpaint/widgets/kptoolwidgetspraycansize.h b/kolourpaint/widgets/kptoolwidgetspraycansize.h
new file mode 100644
index 00000000..b4233a80
--- /dev/null
+++ b/kolourpaint/widgets/kptoolwidgetspraycansize.h
@@ -0,0 +1,51 @@
+
+/*
+ Copyright (c) 2003,2004,2005 Clarence Dang <dang@kde.org>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef __kptoolwidgetspraycansize_h__
+#define __kptoolwidgetspraycansize_h__
+
+#include <kptoolwidgetbase.h>
+
+class kpToolWidgetSpraycanSize : public kpToolWidgetBase
+{
+Q_OBJECT
+
+public:
+ kpToolWidgetSpraycanSize (QWidget *parent, const char *name);
+ virtual ~kpToolWidgetSpraycanSize ();
+
+ int spraycanSize () const;
+
+signals:
+ void spraycanSizeChanged (int size);
+
+protected slots:
+ virtual bool setSelected (int row, int col, bool saveAsDefault);
+};
+
+#endif // __kptoolwidgetspraycansize_h__