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;
+
+priv