diff options
Diffstat (limited to 'kooka')
77 files changed, 14070 insertions, 0 deletions
diff --git a/kooka/AUTHORS b/kooka/AUTHORS new file mode 100644 index 00000000..41f6d7f2 --- /dev/null +++ b/kooka/AUTHORS @@ -0,0 +1,2 @@ +Ivan Shvedunov <ivan_iv@rf-hp.npi.msu.su> +Klaas Freitag <freitag@SuSE.de> diff --git a/kooka/CHANGES b/kooka/CHANGES new file mode 100644 index 00000000..0b384de6 --- /dev/null +++ b/kooka/CHANGES @@ -0,0 +1,158 @@ +-> Nov. 2000, Klaas Freitag <freitag@suse.de>: +initial version + +- December 2000, Klaas Freitag <freitag@suse.de>: + +Added support for calling the external ocr program gocr of Joerg +Schulenburg and friends. See http://jocr.sourceforge.net for +details. Mind that still everything is under construction, even if +there could be some interesting screen hots around. + +OCR works best with smaller parts of text scanned black-and-white +scanned with about 150 dpi. + +-> Released version 0.2 on kde.org + +- December 2000, Klaas Freitag <freitag@suse.de>: + +* Reworked the ocr integration: Nice start- and finish-dialog, + animated status image, opening a text editor with the result text + via mime mechanism of KDE (KRun). OCR-Parameter get stored via KConfig. + OCR may be performed on the entire image or the selection. + +* Reworked the save assistant: New layout and fully KDE2-Compliant. + Still no new Image format help texts. + +* Reworked the startup dialog: Allows to select the scan device from a + list of available devices, possible to set the startup scan device + 'forever'. + +* Resizing of the scan parameters. The scan parameters shell should + take as much as space as it needs. It is not longer fix sized now. + +* Mirroring in three kinds of the displayed image with toolbar and menubar + entries. That needed that the packager is able to handle changes on already + saved images. + +* Creation of new images from the selection on the image canvas. + +- Jan 2001, Klaas Freitag <freitag@suse.de>: + +* Rotating of images. + +* Opening images in a kde graphic app. + +* Proper error messages if a changed image cant be saved. + +* drag and drop support: Pull an image from konqueror on the scanpackager ! + The image wil be importet. + +* print support (basic) + +* basic configuration support + + ... and losts of other, small bug fixes, unfortunately not mentionend very + well here. Sorry for that, I will try to do better. + +/* ================================================================================ */ + +-> Released version 0.3 in kdegraphics/kooka for KDE 2.2 +The released version does actually not have too many new + +- July 2001, Klaas Freitag <freitag@suse.de> + +* fixed the img_saver-bug that the 'Do not ask again' is not honored. + Now it should be, if it is checked, the format-Dialog will not be + displayed again. + +* added a new section Image saver to the properties dialog + +* some changes to the image saver to display the image type (Lineart etc. ) + +* bugfixes in the image Saver + +/* ================================================================================ */ +Nov 2001: +Added a combo box below kooka's gallery widget. Since the gallery and the +the preview window are in a tabwidget together, user complained that they +do not see the directory they are scanning to while working on the preview +page. The combobox shows the currently selected directory and is always +visible. The user can use the combo to change the target dir without leaving +the preview page. + +Nov 2001: +Replaced the old ScanPackager object by a new one based on the new KFileTreeView +Widget in kdelibs/kfile. Hopefully soon full KURL support and more than one image +repository. + +Nov 2001: +Moved preview image to a hidden directory under $KDEHOME/share/apps/ScanImages/.previews +That makes the preview images invisible in the Packager. + +/* ================================================================================ */ + +many bugfixes for KDE 3.0 -> Released Kooka 0.35 for KDE 3.0 + +Released Kooka 0.36 for KDE 3.0.1 + +Mai 2002: + Added Thumbnail View with Preference Dialog => Kooka Release 0.37 + Completed About dialog and added the Webpage http://kooka.kde.org + +/* ================================================================================ */ + +For Kooka 0.38: Introduced KDockWidgets to make the GUI configurable to everybodies +needs. + +/* ================================================================================ */ + +For Kooka 0.39: + +* Fixed problem with gocr: gocr always resulted in (PICTURE) - fixed + it by changing the save format for images to gocr. This bug happens + only with gocr 0.3.5 (and probably higher). + +* Renaming images inline reimplemented again. + +* exported Gallery Actions to appear in the menu bar again. + +* added a progress bar and bugfixes to the thumbview + +* added halftoning to the scanservice and thus kooka + +* fixed bugs in the resolving of dependencies of scanner parameters. + +/* ================================================================================ */ + +For Kooka 0.40: + +* Added Kadmos-Check to configure.in.in + +* Changed the Mainwindow to be KParts-Mainwindow,neccessary for using Parts in Kooka + +* OCR: Code massive reorganised. More ability to use different ocr engines. + +* OCR: Preparation to use Kadmos OCR engine. + +* OCR: Result not longer in a separat dialog, but in a editor part. + +* Scanpackager uses KookaImage more consequently. That provides meta data where needed + +* Improved image viewer from libkscan: Holds zoom settings over images + +* image information in status bar. + +/* ================================================================================ */ + +For Kooka 0.41: + +* Added support for ocrad OCR software. + +* Added image printing tab to the tab dialog, good printing support. + +* fixed some bugs with OCR result image handling. + +* libkscan: Fixed a cast bug with scan source selection + +* Improved support for tiff images, handle images with different x- and y resolution + correctly. diff --git a/kooka/COPYING b/kooka/COPYING new file mode 100644 index 00000000..0b84a43f --- /dev/null +++ b/kooka/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/kooka/CREDITS b/kooka/CREDITS new file mode 100644 index 00000000..cbb0bf8b --- /dev/null +++ b/kooka/CREDITS @@ -0,0 +1,4 @@ +Kooka and KScan has been derived from the the 1997 work of Ivan +Shvedunov <ivan_iv@rf-hp.npi.msu.su>, his homepage can be found at +http://rf-hp.npi.msu.su. + diff --git a/kooka/INSTALL b/kooka/INSTALL new file mode 100644 index 00000000..28fadaa7 --- /dev/null +++ b/kooka/INSTALL @@ -0,0 +1,181 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. + diff --git a/kooka/Makefile.am b/kooka/Makefile.am new file mode 100644 index 00000000..6fec098e --- /dev/null +++ b/kooka/Makefile.am @@ -0,0 +1,46 @@ +## Makefile.am for kooka + +SUBDIRS = pics + +bin_PROGRAMS = kooka +METASOURCES = AUTO + +kooka_SOURCES = main.cpp kooka.cpp kookaview.cpp kookapref.cpp \ + img_saver.cpp ksaneocr.cpp \ + kookaimage.cpp kookaimagemeta.cpp scanpackager.cpp \ + imgnamecombo.cpp imageselectline.cpp \ + thumbview.cpp thumbviewitem.cpp \ + dwmenuaction.cpp kocrbase.cpp \ + kocrgocr.cpp kocrkadmos.cpp kadmosocr.cpp ocrword.cpp \ + ocrresedit.cpp kookaprint.cpp imgprintdialog.cpp \ + kocrocrad.cpp +# pagesetup.cpp + +kooka_LDADD = $(LIB_KFILE) -lkdeprint -lktexteditor $(LIBTIFF) $(top_builddir)/libkscan/libkscan.la $(KADMOS_LIB) $(LIB_KSPELL) +kooka_LDFLAGS = $(KDE_RPATH) $(all_libraries) + +INCLUDES = -I$(top_srcdir)/libkscan $(all_includes) $(LIBSANE_INCLUDES) $(KADMOS_INC) + +noinst_HEADERS = \ +kookaview.h scanpackager.h \ +img_saver.h ksaneocr.h \ +formathelp.h kooka.h \ +kookaiface.h thumbview.h thumbviewitem.h \ +kookapref.h resource.h \ +imgnamecombo.h imageselectline.h kookaimage.h kookaimagemeta.h \ +kocrbase.h kocrgocr.h kocrkadmos.h \ +kadmosocr.h ocrword.h ocrresedit.h kookaprint.h imgprintdialog.h \ +kocrocrad.h + +# pagesetup.h + +appdatadir = $(kde_datadir)/kooka +appdata_DATA = kookaui.rc + +kde_conf_DATA = kookarc + +xdg_apps_DATA = kooka.desktop + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kooka.pot + diff --git a/kooka/README b/kooka/README new file mode 100644 index 00000000..8ae6fa54 --- /dev/null +++ b/kooka/README @@ -0,0 +1,66 @@ +Kooka +===== + +Kooka is a raster image scan program for the KDE system. + + PLEASE READ THE FILE "WARNING" FIRST ! + +It uses the SANE-lib (http://www.sane-project.org/) and the the +KScan-library which is a KDE module for scanner access. + +KScan and Kooka are under construction. Don't expect everything to work +fine. If you want to help, please send patches to freitag@suse.de. + +Features: +========= + +Kooka's main features are: + +Scanner Support using SANE: +- Scanner support using SANE. Kooka _DOES_NOT_ support all features that SANE + and its backends offer. It takes a small subset of the available options. +- Kooka offers a GUI to change the most important scanner options like resolution, + mode, threshold etc. These options are generated on the fly, depending on the + scanner capabilities. +- Kooka offers a preview-function and the user can select the scan area interactively + or automatically. + +Image Storage: +- Kooka provides an assistant to save your acquired images. +- Filenames are generated automatically to support multiple scans. +- Kooka manages the scanned images in a tree view where the user can delete and + export images. + +Image Manipulation: +- Kooka provides basic image manipulation functions like rotation, mirroring. +- Cut images to fit size. + +Image Viewing: +- Scanned images can be viewed by clicking them in the tree view. +- The viewer has a zoom function. + +OCR: +- Kooka supports Joerg Schulenburg's gocr, an open source program + for optical character recognition (OCR). Kooka starts the OCR program + and displays its output. Best results with bw-images scanned with ~150 DPI +- Support for the commercial OCR/ICR package KADMOS of the reRecognition + GmbH Kreuzlingen. Please read README.KADMOS for more information. + +Problems: +========= + +* Kooka does not yet support all options SANE offers. That will +improve in the future. However, I don't know if it makes sense to +support all, even not very common options, some scanners offer. Lets +see what is necessary and makes sense for the purpose of Kooka. + +* Kooka does not yet have a strategy for very large images :(. It uses +the Qt QImage/QPixmap as is. On some displays, that causes problems. + +* Automatic document feeder (ADF) support is not yet working correctly. + +---------------------------------------------------------------------- +Klaas Freitag <freitag@suse.de> + +$Id$ + diff --git a/kooka/README.KADMOS b/kooka/README.KADMOS new file mode 100644 index 00000000..a6242155 --- /dev/null +++ b/kooka/README.KADMOS @@ -0,0 +1,73 @@ +Kooka and KADMOS integration +============================ + +This file describes how to make Kooka working with the KADMOS OCR/ICR +engine. + +KAMDOS is commercial OCR/ICR software component of the company + + reRecognition GmbH + Hafenstr. 50B + 8280 Kreuzlingen + Switzerland + Tel.: +41 71 6780000 + Fax: +41 71 6780099 + www.reRecognition.com + +and Kooka can be build using the linux version of the component in +order to achive very good ocr results. + +Please contact re Recognition directly if you like to obtain or test +KADMOS. Note that if you are linking against KADMOS, you loose the +permission to use the GPL Qt version, so you need a commercial Qt +version as well. + +Configuration +------------- + +As Kooka does not require KADMOS, it is neccessary to configure +Kooka to use the KADMOS library. This could be done by calling the +configure script of Kooka's source distribution with following +parameter: +./configure --with-kadmos-dir=/where/kadmos.h/resides/ + +The configure script tries to locate the file kadmos.h in the +directory that was specified. The KADMOS library is expected in the +same directory. Build the source after configuring with KADMOS. Kooka +enables the code to work with KADMOS in the source and links the +library. + +Installation +------------ + +KADMOS is linked statically and thus there is no need for special +installation of the KADMOS library. + +Installation of the Classifier Files: + +KADMOS needs classifier files for the ocr process which come with +the KADMOS developer's toolkit. The classifier files need to be installed +in the KDE application data directory for Kooka in a subdirectory named +classifiers. If your KDE installation goes to /opt/kde3/, this is for +example /opt/kde3/share/apps/kooka/classifiers. Kooka picks the +available classifiers up automatically. + +The classifiers are named in the following way: + +[fontkind][country/region].rec, +where fontkinds are +ttf -> machine print font +hand -> handprint (isolated) +norm -> OCR norm font + + +For example the following classifier names are used: + +ttfus.rec US machineprint classifiers +handus.rec US handwriting classifiers +norm.rec Special OCR character sets, not localized + + +---------------------------------------------------------------------- +Klaas Freitag <freitag@suse.de> +$Id$ diff --git a/kooka/TODO b/kooka/TODO new file mode 100644 index 00000000..ce5f3967 --- /dev/null +++ b/kooka/TODO @@ -0,0 +1,26 @@ +To be continued: + +Object ScanParams: +- Resizing. On startup, the object comes up in a nearly fixed size, suiting the + mustek-backend very well ;) - thats the one I use at home. The cool solution + would be if the object 'knows' and tells what size (especially height) it wants + to have. + + -> This should be done now. Please check, if it works for other than mustek + backends. + +Startup: + +- Changing the 'Dont ask me on startup'-decision. If the user checks the button + in the startup dialog never to ask which scan device to use, it is not possible + to revert this decision. Need a page in the Preferences dialog. + +OCR: + +- The ocr result window does not appear properly on very large result images + provided by gocr. Kooka should resize the result image to a reasonable size. + +Scan Packager: + +- The scan packager needs major rework. Enhancements like metadata in XML etc + should be included. diff --git a/kooka/WARNING b/kooka/WARNING new file mode 100644 index 00000000..bee996d5 --- /dev/null +++ b/kooka/WARNING @@ -0,0 +1,28 @@ + +Scanning with Kooka and KScan +============================= + +Kooka and KScan use the SANE library to attach to the scanner +hardware. It cannot be avoided by any library that a program sends +commands to the scanner which can damage it. Especially very cheap +scanners sometimes do not have a hardware protection against driving +the bed to far, which can cause terrible noise and damage :-( + +That is why you should be warned running Kooka or the KDE scanservice +the first time. Even if there were no reportings that something bad +happens to any scanner models using Kooka yet, but it can not be +garanteed. + +Starting to scan the first time, be sure to sit next to your scanner +having your hand on the power-off switch. Switch off immediately if +you hear unexpected noises or if something strange happens. + +If you find errors, please dont ask the SANE-people without having +made sure that your error is _really_ a SANE error. Most probably, you +found an KScan or Kooka-Error, which should be reported at +http://bugs.kde.org + + +- Klaas Freitag <freitag@suse.de> + +$Id$ diff --git a/kooka/configure.in.in b/kooka/configure.in.in new file mode 100644 index 00000000..df590f5d --- /dev/null +++ b/kooka/configure.in.in @@ -0,0 +1,33 @@ +dnl AC_SEARCH_LIBS(pgm2asc,Pgm2asc) +dnl AC_CHECK_LIB(Pgm2asc,pgm2asc) +dnl should define HAVE_LIBPGM2ASC if available + +AC_ARG_WITH([kadmos], + [AC_HELP_STRING([--with-kadmos], + [Enable the kadmos OCR engine @<:@default=check@:>@])], + [], with_kadmos=check) + +AC_ARG_WITH([kadmos-dir], + AC_HELP_STRING([--with-kadmos-dir], + [sets the path to the kadmos engine @<:@default=/usr/local@:>@]), + [ac_kadmos_value=$withval], [ac_kadmos_value=/usr/local]) + +KADMOS_INC= +KADMOS_LIB= + +if test "x$with_kadmos" != xno; then + if test -r "$ac_kadmos_value/kadmos.h"; then + KADMOS_INC="-I$ac_kadmos_value" + KADMOS_LIB="$ac_kadmos_value/librep.a" + AC_DEFINE_UNQUOTED(HAVE_KADMOS, 1, [Defines if your system has the kadmos libraries]) + else + AC_MSG_WARN([couldn't find kadmos engine header file under $ac_kadmos_value/kadmos.h]) + fi + + if test "x$with_kadmos" != xcheck && test -z "$KADMOS_LIB"; then + AC_MSG_ERROR([--with-kadmos was given, but test for kadmos failed]) + fi +fi + +AC_SUBST(KADMOS_LIB) +AC_SUBST(KADMOS_INC) diff --git a/kooka/dwmenuaction.cpp b/kooka/dwmenuaction.cpp new file mode 100644 index 00000000..abe57f23 --- /dev/null +++ b/kooka/dwmenuaction.cpp @@ -0,0 +1,72 @@ +/*************************************************************************** + dwmenuaction.cpp - dockwidget visibility switches to actions + ------------------- + begin : 16.07.2002 + copyright : (C) 1999 by Klaas Freitag + email : freitag@suse.de + + $Id$ + Based on code from the from Joseph Wenninger (kate project) +***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#include "dwmenuaction.h" +#include "dwmenuaction.moc" +//------------------------------------- + +dwMenuAction::dwMenuAction( const QString& text, const KShortcut& cut, + KDockWidget *dw,QObject* parent, + KDockMainWindow *mw, const char* name ) + :KToggleAction(text,cut,parent,name),m_dw(dw),m_mw(mw) +{ + connect(this,SIGNAL(toggled(bool)),this,SLOT(slotToggled(bool))); + connect(m_dw->dockManager(),SIGNAL(change()),this,SLOT(anDWChanged())); + connect(m_dw,SIGNAL(destroyed()),this,SLOT(slotWidgetDestroyed())); + setChecked(m_dw->mayBeHide()); +} + + +dwMenuAction::~dwMenuAction(){;} + +void dwMenuAction::anDWChanged() +{ + if (isChecked() && m_dw->mayBeShow()) setChecked(false); + else if ((!isChecked()) && m_dw->mayBeHide()) setChecked(true); +} + + +void dwMenuAction::slotToggled(bool t) +{ + + if ((!t) && m_dw->mayBeHide() ) m_dw->undock(); + else + if ( t && m_dw->mayBeShow() ) m_mw->makeDockVisible(m_dw); + +} + + +void dwMenuAction::slotWidgetDestroyed() +{ + unplugAll(); + deleteLater(); +} + + +/* END */ diff --git a/kooka/dwmenuaction.h b/kooka/dwmenuaction.h new file mode 100644 index 00000000..9b1698ed --- /dev/null +++ b/kooka/dwmenuaction.h @@ -0,0 +1,62 @@ +/*************************************************************************** + dwmenuaction.h - dockwidget visibility switches to actions + ------------------- + begin : 16.07.2002 + copyright : (C) 1999 by Klaas Freitag + email : freitag@suse.de + + $Id$ + Based on code from the from Joseph Wenninger (kate project) +***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#ifndef __DW_MENU_ACTION +#define __DW_MENU_ACTION +#include <kdockwidget.h> +#include <qstring.h> +#include <kaction.h> + +/** + * This class is just a helper class since the KDockWidget classes do not yet + * export KActions but only a QPopup-Pointer, which is quite useless in case + * you have a xml-file driven gui. + * This class provides Actions for show and hide parts of the GUI (dockwidgets) + * Maybe that classes can be removed as soon the DockWidget know Actions + */ +class dwMenuAction:public KToggleAction +{ + Q_OBJECT +public: + dwMenuAction( const QString& text, + const KShortcut& cut = KShortcut(), + KDockWidget *dw=0, QObject* parent = 0, + KDockMainWindow * mw=0, const char* name = 0 ); + virtual ~dwMenuAction(); + +private: + KDockWidget *m_dw; + KDockMainWindow *m_mw; +protected slots: + void slotToggled(bool); + void anDWChanged(); + void slotWidgetDestroyed(); +}; + +#endif diff --git a/kooka/formathelp.h b/kooka/formathelp.h new file mode 100644 index 00000000..7887a240 --- /dev/null +++ b/kooka/formathelp.h @@ -0,0 +1,43 @@ +/*************************************************************************** + formathelp.h - description + ------------------- + begin : Mon Dec 27 1999 + copyright : (C) 1999 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#define HELP_BMP i18n("The <big>bitmap-format</big> is a well known format,\n" \ + "often used for 256 color images under " \ + "MS Windows.\n Suitable for color and " \ + "<bold>lineart-pictures</bold>\n" ) +#define HELP_PNM i18n("Portable Anymap\n" \ + ""\ + "" ) +#define HELP_JPG i18n("Jpeg is a high compression,\nquality " \ + "losing format for color\npictures " \ + "with many different colors." \ + "" ) + +#define HELP_EPS i18n("EPS is Encapsulated Postscript.\n " \ + "Initially it's a printer definition\n " \ + "language. Use this format if you\n" \ + "want to print the image or use\n" \ + "it with e.g. TeX" ) diff --git a/kooka/imageselectline.cpp b/kooka/imageselectline.cpp new file mode 100644 index 00000000..f8677f1f --- /dev/null +++ b/kooka/imageselectline.cpp @@ -0,0 +1,105 @@ +/*************************************************************************** + imageselectline.cpp - select a background image. + ------------------- + begin : Fri Dec 17 1999 + copyright : (C) 1999 by Klaas Freitag + email : freitag@suse.de + + $Id$ + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ +#include <qhbox.h> +#include <qvbox.h> +#include <qbutton.h> +#include <qpushbutton.h> +#include <qlabel.h> + +#include <kdebug.h> +#include <klocale.h> +#include <kurl.h> +#include <kurlcombobox.h> +#include <kfiledialog.h> +#include <kiconloader.h> + +#include "imageselectline.h" + +/* ############################################################################## */ + +/* + * This widget just combines a label, a combobox holding a path and a select button + * together in a row. The button opens a file selector box to pick a file. + */ + +ImageSelectLine::ImageSelectLine( QWidget *parent, const QString& text ) + : QHBox( parent ) +{ + setSpacing( 5 ); + (void) new QLabel( text, this ); + m_urlCombo = new KURLComboBox( KURLComboBox::Files, this ); + m_buttFileSelect = new QPushButton( this ); + m_buttFileSelect->setPixmap( SmallIcon( "fileopen" ) ); + + m_urlCombo->setMaxItems(5); + + connect( m_urlCombo, SIGNAL( urlActivated( const KURL& )), + this, SLOT( slUrlActivated( const KURL& ))); + + connect( m_buttFileSelect, SIGNAL( clicked() ), + this, SLOT( slSelectFile())); +} + +void ImageSelectLine::slSelectFile() +{ + KURL newUrl; + newUrl = KFileDialog::getImageOpenURL(); + + QStringList l = m_urlCombo->urls(); + + if( ! newUrl.isEmpty()) + { + l.prepend( newUrl.url() ); + m_urlCombo->setURLs( l ); + m_currUrl = newUrl; + } +} + +void ImageSelectLine::slUrlActivated( const KURL& url ) +{ + kdDebug(28000) << "Activating url: " << url.url() << endl; + m_currUrl = url; +} + +KURL ImageSelectLine::selectedURL() const +{ + return m_currUrl; +} + +void ImageSelectLine::setURL( const KURL& url ) +{ + if( m_urlCombo ) m_urlCombo->setURL( url ); + m_currUrl = url; +} + +void ImageSelectLine::setURLs( const QStringList& list ) +{ + if( m_urlCombo ) m_urlCombo->setURLs( list ); +} + +#include "imageselectline.moc" diff --git a/kooka/imageselectline.h b/kooka/imageselectline.h new file mode 100644 index 00000000..991cb3fd --- /dev/null +++ b/kooka/imageselectline.h @@ -0,0 +1,66 @@ +/*************************************************************************** + imageselectline.h - select a background image. + ------------------- + begin : ? + copyright : (C) 2002 by Klaas Freitag + email : freitag@suse.de + + $Id$ +***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#ifndef __IMGSELECTLINE_H__ +#define __IMGSELECTLINE_H__ + +#include <qhbox.h> + +/** + * + */ + +class KURL; +class KURLComboBox; +class QPushButton; +class QStringList; + +class ImageSelectLine:public QHBox +{ + Q_OBJECT +public: + ImageSelectLine( QWidget *parent, const QString& text ); + + KURL selectedURL() const; + void setURL( const KURL& ); + void setURLs( const QStringList& ); + +protected slots: + void slSelectFile(); + void slUrlActivated( const KURL& ); + +private: + + KURL m_currUrl; + KURLComboBox *m_urlCombo; + QPushButton *m_buttFileSelect; + +}; + + +#endif diff --git a/kooka/img_saver.cpp b/kooka/img_saver.cpp new file mode 100644 index 00000000..1d7cf1d7 --- /dev/null +++ b/kooka/img_saver.cpp @@ -0,0 +1,897 @@ +/*************************************************************************** + img_saver.cpp - description + ------------------- + begin : Mon Dec 27 1999 + copyright : (C) 1999 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#include <sys/stat.h> +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> + + +#include <kglobal.h> +#include <kconfig.h> +#include <kdialog.h> +#include <kimageio.h> +#include <kseparator.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kdebug.h> +#include <kio/jobclasses.h> +#include <kio/file.h> +#include <kio/job.h> +#include <kio/netaccess.h> +#include <ktempfile.h> +#include <kinputdialog.h> + +#include <qdir.h> +#include <qlayout.h> +#include <qfileinfo.h> +#include <qimage.h> +#include <qmessagebox.h> +#include <qvbox.h> +#include <qbuttongroup.h> + +#include "resource.h" +#include "img_saver.h" +#include "previewer.h" +#include "kookaimage.h" + +FormatDialog::FormatDialog( QWidget *parent, const QString&, const char *name ) + :KDialogBase( parent, name, true, + /* Tabbed,*/ i18n( "Kooka Save Assistant" ), + Ok|Cancel, Ok ) + +{ + buildHelp(); + // readConfig(); + // QFrame *page = addPage( QString( "Save the image") ); + QFrame *page = new QFrame( this ); + page->setFrameStyle( QFrame::Box | QFrame::Sunken ); + Q_CHECK_PTR( page ); + setMainWidget( page ); + + QVBoxLayout *bigdad = new QVBoxLayout( page, marginHint(), spacingHint()); + Q_CHECK_PTR(bigdad); + + // some nice words + QLabel *l0 = new QLabel( page ); + Q_CHECK_PTR(l0); + l0->setText( i18n( "<B>Save Assistant</B><P>Select an image format to save the scanned image." )); + bigdad->addWidget( l0 ); + + KSeparator* sep = new KSeparator( KSeparator::HLine, page); + bigdad->addWidget( sep ); + + // Layout-Boxes + // QHBoxLayout *hl1= new QHBoxLayout( ); // Caption + QHBoxLayout *lhBigMiddle = new QHBoxLayout( spacingHint() ); // Big middle + Q_CHECK_PTR(lhBigMiddle); + bigdad->addLayout( lhBigMiddle ); + QVBoxLayout *lvFormatSel = new QVBoxLayout( spacingHint() ); // Selection List + Q_CHECK_PTR(lvFormatSel); + lhBigMiddle->addLayout( lvFormatSel ); + + // Insert Scrolled List for formats + QLabel *l1 = new QLabel( page ); + Q_CHECK_PTR(l1); + l1->setText( i18n( "Available image formats:" )); + + lb_format = new QListBox( page, "ListBoxFormats" ); + Q_CHECK_PTR(lb_format); + +#ifdef USE_KIMAGEIO + QStringList fo = KImageIO::types(); +#else + QStringList fo = QImage::outputFormatList(); +#endif + kdDebug(28000) << "#### have " << fo.count() << " image types" << endl; + lb_format->insertStringList( fo ); + connect( lb_format, SIGNAL( highlighted(const QString&)), + SLOT( showHelp(const QString&))); + + // Insert label for helptext + l_help = new QLabel( page ); + Q_CHECK_PTR(l_help); + l_help->setFrameStyle( QFrame::Panel|QFrame::Sunken ); + l_help->setText( i18n("-No format selected-" )); + l_help->setAlignment( AlignVCenter | AlignHCenter ); + l_help->setMinimumWidth(230); + + // Insert Selbox for subformat + l2 = new QLabel( page ); + Q_CHECK_PTR(l2); + l2->setText( i18n( "Select the image sub-format" )); + cb_subf = new QComboBox( page, "ComboSubFormat" ); + Q_CHECK_PTR( cb_subf ); + + // Checkbox to store setting + cbDontAsk = new QCheckBox(i18n("Don't ask again for the save format if it is defined."), + page ); + Q_CHECK_PTR( cbDontAsk ); + + QFrame *hl = new QFrame(page); + Q_CHECK_PTR( hl ); + hl->setFrameStyle( QFrame::HLine|QFrame::Sunken ); + + // bigdad->addWidget( l_caption, 1 ); + lvFormatSel->addWidget( l1, 1 ); + lvFormatSel->addWidget( lb_format, 6 ); + lvFormatSel->addWidget( l2, 1 ); + lvFormatSel->addWidget( cb_subf, 1 ); + + lhBigMiddle->addWidget( l_help, 2 ); + //bigdad->addStretch(1); + bigdad->addWidget( hl, 1 ); + bigdad->addWidget( cbDontAsk , 2 ); + + bigdad->activate(); + +} + +void FormatDialog::showHelp( const QString& item ) +{ + QString helptxt = format_help[ item ]; + + if( !helptxt.isEmpty() ) { + // Set the hint + l_help->setText( helptxt ); + + // and check subformats + check_subformat( helptxt ); + } else { + l_help->setText( i18n("-no hint available-" )); + } +} + +void FormatDialog::check_subformat( const QString & format ) +{ + // not yet implemented + kdDebug(28000) << "This is format in check_subformat: " << format << endl; + cb_subf->setEnabled( false ); + // l2 = Label "select subformat" ->bad name :-| + l2->setEnabled( false ); +} + +void FormatDialog::setSelectedFormat( QString fo ) +{ + QListBoxItem *item = lb_format->findItem( fo ); + + if( item ) + { + // Select it. + lb_format->setSelected( lb_format->index(item), true ); + } +} + + +QString FormatDialog::getFormat( ) const +{ + int item = lb_format->currentItem(); + + if( item > -1 ) + { + const QString f = lb_format->text( item ); + return( f ); + } + return( "BMP" ); +} + + +QCString FormatDialog::getSubFormat( ) const +{ + // Not yet... + return( "" ); +} + +#include "formathelp.h" +void FormatDialog::buildHelp( void ) +{ + format_help.insert( QString::fromLatin1("BMP"), HELP_BMP ); + format_help.insert( QString::fromLatin1("PNM"), HELP_PNM ); + format_help.insert( QString::fromLatin1("JPEG"), HELP_JPG ); + format_help.insert( QString::fromLatin1("JPG"), HELP_JPG ); + format_help.insert( QString::fromLatin1("EPS"), HELP_EPS ); +} + + +/* ********************************************************************** */ + +ImgSaver::ImgSaver( QWidget *parent, const KURL dir_name ) + : QObject( parent ) +{ + + if( dir_name.isEmpty() || dir_name.protocol() != "file" ) + { + kdDebug(28000) << "ImageServer initialised with wrong dir " << dir_name.url() << endl; + directory = Previewer::galleryRoot(); + } + else + { + /* A path was given */ + if( dir_name.protocol() != "file" ) + { + kdDebug(28000) << "ImgSaver: Can only save local image, sorry !" << endl; + } + else + { + directory = dir_name.directory(true, false); + } + } + + kdDebug(28000) << "ImageSaver uses dir <" << directory << endl; + createDir( directory ); + readConfig(); + + last_file = ""; + last_format =""; + +} + + +ImgSaver::ImgSaver( QWidget *parent ) + :QObject( parent ) +{ + directory = Previewer::galleryRoot(); + createDir( directory ); + + readConfig(); + + last_file = ""; + last_format =""; + +} + + +/* Needs a full qualified directory name */ +void ImgSaver::createDir( const QString& dir ) +{ + KURL url( dir ); + + if( ! KIO::NetAccess::exists(url, false, 0) ) + { + kdDebug(28000) << "Wrn: Directory <" << dir << "> does not exist -> try to create !" << endl; + // if( mkdir( QFile::encodeName( dir ), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH ) != 0 ) + if( KIO::mkdir( KURL(dir))) + { + KMessageBox::sorry(0, i18n("The folder\n%1\n does not exist and could not be created;\n" + "please check the permissions.").arg(dir)); + } + } +#if 0 + if( ! fi.isWritable() ) + { + KMessageBox::sorry(0, i18n("The directory\n%1\n is not writeable;\nplease check the permissions.") + .arg(dir)); + } +#endif +} + +/** + * This function asks the user for a filename or creates + * one by itself, depending on the settings + **/ +ImgSaveStat ImgSaver::saveImage( QImage *image ) +{ + ImgSaveStat stat; + picType imgType; + + if( !image ) return( ISS_ERR_PARAM ); + + /* Find out what kind of image it is */ + if( image->depth() > 8 ) + { + imgType = PT_HICOLOR_IMAGE; + } + else + { + if( image->depth() == 1 || image->numColors() == 2 ) + { + kdDebug(28000) << "This is black And White!" << endl; + imgType = PT_BW_IMAGE; + } + else + { + imgType = PT_COLOR_IMAGE; + if( image->allGray() ) + { + imgType = PT_GRAY_IMAGE; + } + } + } + + + QString format = findFormat( imgType ); + QString subformat = findSubFormat( format ); + // Call save-Function with this params + + if( format.isEmpty() ) + { + kdDebug(28000) << "Save canceled by user -> no save !" << endl; + return( ISS_SAVE_CANCELED ); + } + + kdDebug(28000) << "saveImage: Directory is " << directory << endl; + QString filename = createFilename( format ); + + KConfig *konf = KGlobal::config (); + konf->setGroup( OP_FILE_GROUP ); + + if( konf->readBoolEntry( OP_ASK_FILENAME, false ) ) + { + bool ok; + QString text = KInputDialog::getText( i18n( "Filename" ), i18n("Enter filename:"), + filename, &ok ); + + if(ok) + { + filename = text; + } + } + + QString fi = directory + "/" + filename; + + if( extension(fi).isEmpty() ) + { + if( ! fi.endsWith( "." ) ) + { + fi+= "."; + } + fi+=format.lower(); + } + + + kdDebug(28000) << "saveImage: saving file <" << fi << ">" << endl; + stat = save( image, fi, format, subformat ); + + return( stat ); + +} + +/** + * This member creates a filename for the image to save. + * This is done by numbering all existing files and adding + * one + **/ +QString ImgSaver::createFilename( QString format ) +{ + if( format.isNull() || format.isEmpty() ) return( 0 ); + + QString s = "kscan_*." + format.lower(); + QDir files( directory, s ); + long c = 1; + + QString num; + num.setNum(c); + QString fname = "kscan_" + num.rightJustify(4, '0') + "." + format.lower(); + + while( files.exists( fname ) ) { + num.setNum(++c); + fname = "kscan_" + num.rightJustify(4, '0') + "." + format.lower(); + } + + return( fname ); +} + +/** + * This function gets a filename from the parent. The filename must not be relative. + **/ +ImgSaveStat ImgSaver::saveImage( QImage *image, const KURL& filename, const QString& imgFormat ) +{ + QString format = imgFormat; + + /* Check if the filename is local */ + if( !filename.isLocalFile()) + { + kdDebug(29000) << "ImgSaver: Can only save local image, sorry !" << endl; + return( ISS_ERR_PROTOCOL ); + } + + QString localFilename; + localFilename = filename.directory( false, true) + filename.fileName(); + + kdDebug(28000) << "saveImage: Saving "<< localFilename << " in format " << format << endl; + if( format.isEmpty() ) + format = "BMP"; + + return( save( image, localFilename, format, "" ) ); +} + + +/* + * findFormat does all the stuff with the dialog. + */ +QString ImgSaver::findFormat( picType type ) +{ + QString format; + KConfig *konf = KGlobal::config (); + konf->setGroup( OP_FILE_GROUP ); + + if( type == PT_THUMBNAIL ) + { + return( "BMP" ); + } + + // real images + switch( type ) + { + case PT_THUMBNAIL: + format = konf->readEntry( OP_FORMAT_THUMBNAIL, "BMP" ); + kdDebug( 28000) << "Format for Thumbnails: " << format << endl; + break; + case PT_PREVIEW: + format = konf->readEntry( OP_PREVIEW_FORMAT, "BMP" ); + kdDebug( 28000) << "Format for Preview: " << format << endl; + break; + case PT_COLOR_IMAGE: + format = konf->readEntry( OP_FORMAT_COLOR, "nothing" ); + kdDebug( 28000 ) << "Format for Color: " << format << endl; + break; + case PT_GRAY_IMAGE: + format = konf->readEntry( OP_FORMAT_GRAY, "nothing" ); + kdDebug( 28000 ) << "Format for Gray: " << format << endl; + break; + case PT_BW_IMAGE: + format = konf->readEntry( OP_FORMAT_BW, "nothing" ); + kdDebug( 28000 ) << "Format for BlackAndWhite: " << format << endl; + break; + case PT_HICOLOR_IMAGE: + format = konf->readEntry( OP_FORMAT_HICOLOR, "nothing" ); + kdDebug( 28000 ) << "Format for HiColorImage: " << format << endl; + break; + default: + format = "nothing"; + kdDebug( 28000 ) << "ERR: Could not find image type !" << endl; + + break; + } + + if( type != PT_PREVIEW ) /* Use always bmp-Default for preview scans */ + { + if( format == "nothing" || ask_for_format ) + { + format = startFormatDialog( type ); + } + } + return( format ); + +} + +QString ImgSaver::picTypeAsString( picType type ) const +{ + QString res; + + switch( type ) + { + case PT_COLOR_IMAGE: + res = i18n( "palleted color image (16 or 24 bit depth)" ); + break; + case PT_GRAY_IMAGE: + res = i18n( "palleted gray scale image (16 bit depth)" ); + break; + case PT_BW_IMAGE: + res = i18n( "lineart image (black and white, 1 bit depth)" ); + break; + case PT_HICOLOR_IMAGE: + res = i18n( "high (or true-) color image, not palleted" ); + break; + default: + res = i18n( "Unknown image type" ); + break; + } + return( res ); +} + + +QString ImgSaver::startFormatDialog( picType type) +{ + + FormatDialog fd( 0, picTypeAsString( type ), "FormatDialog" ); + + // set default values + if( type != PT_PREVIEW ) + { + QString defFormat = getFormatForType( type ); + fd.setSelectedFormat( defFormat ); + } + + QString format; + if( fd.exec() ) + { + format = fd.getFormat(); + kdDebug(28000) << "Storing to format <" << format << ">" << endl; + bool ask = fd.askForFormat(); + kdDebug(28000)<< "Store askFor is " << ask << endl; + storeFormatForType( type, format, ask ); + subformat = fd.getSubFormat(); + } + return( format ); +} + + +/* + * This method returns true if the image format given in format is remembered + * for that image type. + */ +bool ImgSaver::isRememberedFormat( picType type, QString format ) const +{ + if( getFormatForType( type ) == format ) + { + return( true ); + } + else + { + return( false ); + } + +} + + + + +QString ImgSaver::getFormatForType( picType type ) const +{ + KConfig *konf = KGlobal::config (); + Q_CHECK_PTR( konf ); + konf->setGroup( OP_FILE_GROUP ); + + QString f; + + switch( type ) + { + case PT_COLOR_IMAGE: + f = konf->readEntry( OP_FORMAT_COLOR, "BMP" ); + break; + case PT_GRAY_IMAGE: + f = konf->readEntry( OP_FORMAT_GRAY, "BMP" ); + break; + case PT_BW_IMAGE: + f = konf->readEntry( OP_FORMAT_BW, "BMP" ); + break; + case PT_HICOLOR_IMAGE: + f = konf->readEntry( OP_FORMAT_HICOLOR, "BMP" ); + break; + default: + f = "BMP"; + break; + } + return( f ); +} + + +void ImgSaver::storeFormatForType( picType type, QString format, bool ask ) +{ + KConfig *konf = KGlobal::config (); + Q_CHECK_PTR( konf ); + konf->setGroup( OP_FILE_GROUP ); + + konf->writeEntry( OP_FILE_ASK_FORMAT, ask ); + ask_for_format = ask; + + switch( type ) + { + case PT_COLOR_IMAGE: + konf->writeEntry( OP_FORMAT_COLOR, format ); + break; + case PT_GRAY_IMAGE: + konf->writeEntry( OP_FORMAT_GRAY, format ); + break; + case PT_BW_IMAGE: + konf->writeEntry( OP_FORMAT_BW, format ); + break; + case PT_HICOLOR_IMAGE: + konf->writeEntry( OP_FORMAT_HICOLOR, format ); + break; + default: + kdDebug(28000) << "Wrong Type - cant store format setting" << endl; + break; + } + konf->sync(); +} + + +QString ImgSaver::findSubFormat( QString format ) +{ + kdDebug(28000) << "Searching Subformat for " << format << endl; + return( subformat ); + +} + +/** + private save() does the work to save the image. + the filename must be complete and local. +**/ +ImgSaveStat ImgSaver::save( QImage *image, const QString &filename, + const QString &format, + const QString &subformat ) +{ + + bool result = false; + kdDebug(28000) << "in ImgSaver::save: saving " << filename << endl; + if( ! format || !image ) + { + kdDebug(28000) << "ImgSaver ERROR: Wrong parameter Format <" << format << "> or image" << endl; + return( ISS_ERR_PARAM ); + } + + if( image ) + { + // remember the last processed file - only the filename - no path + QFileInfo fi( filename ); + QString dirPath = fi.dirPath(); + QDir dir = QDir( dirPath ); + + if( ! dir.exists() ) + { + /* The dir to save in always should exist, except in the first preview save */ + kdDebug(28000) << "Creating dir " << dirPath << endl; + if( !dir.mkdir( dirPath ) ) + { + kdDebug(28000) << "ERR: Could not create directory" << endl; + } + } + + if( fi.exists() && !fi.isWritable() ) + { + kdDebug(28000) << "Cant write to file <" << filename << ">, cant save !" << endl; + result = false; + return( ISS_ERR_PERM ); + } + + /* Check the format, is it writable ? */ +#ifdef USE_KIMAGEIO + if( ! KImageIO::canWrite( format ) ) + { + kdDebug(28000) << "Cant write format <" << format << ">" << endl; + result = false; + return( ISS_ERR_FORMAT_NO_WRITE ); + } +#endif + kdDebug(28000) << "ImgSaver: saving image to <" << filename << "> as <" << format << "/" << subformat <<">" << endl; + + result = image->save( filename, format.latin1() ); + + + last_file = fi.absFilePath(); + last_format = format.latin1(); + } + + if( result ) + return( ISS_OK ); + else { + last_file = ""; + last_format = ""; + return( ISS_ERR_UNKNOWN ); + } + +} + + +void ImgSaver::readConfig( void ) +{ + + KConfig *konf = KGlobal::config (); + Q_CHECK_PTR( konf ); + konf->setGroup( OP_FILE_GROUP ); + ask_for_format = konf->readBoolEntry( OP_FILE_ASK_FORMAT, true ); + + QDir home = QDir::home(); +} + + + + + +QString ImgSaver::errorString( ImgSaveStat stat ) +{ + QString re; + + switch( stat ) { + case ISS_OK: re = i18n( " image save OK " ); break; + case ISS_ERR_PERM: re = i18n( " permission error " ); break; + case ISS_ERR_FILENAME: re = i18n( " bad filename " ); break; + case ISS_ERR_NO_SPACE: re = i18n( " no space on device " ); break; + case ISS_ERR_FORMAT_NO_WRITE: re = i18n( " could not write image format " ); break; + case ISS_ERR_PROTOCOL: re = i18n( " can not write file using that protocol "); break; + case ISS_SAVE_CANCELED: re = i18n( " user canceled saving " ); break; + case ISS_ERR_UNKNOWN: re = i18n( " unknown error " ); break; + case ISS_ERR_PARAM: re = i18n( " parameter wrong " ); break; + + default: re = ""; + } + return( re ); + +} + +QString ImgSaver::extension( const KURL& url ) +{ + QString extension = url.fileName(); + + int dotPos = extension.findRev( '.' ); + if( dotPos > 0 ) + { + int len = extension.length(); + extension = extension.right( len - dotPos -1 ); + } + else + { + /* No extension was supplied */ + extension = QString(); + } + return extension; +} + + +bool ImgSaver::renameImage( const KURL& fromUrl, KURL& toUrl, bool askExt, QWidget *overWidget ) +{ + /* Check if the provided filename has a extension */ + QString extTo = extension( toUrl ); + QString extFrom = extension( fromUrl ); + KURL targetUrl( toUrl ); + + if( extTo.isEmpty() && !extFrom.isEmpty() ) + { + /* Ask if the extension should be added */ + int result = KMessageBox::Yes; + QString fName = toUrl.fileName(); + if( ! fName.endsWith( "." ) ) + { + fName += "."; + } + fName += extFrom; + + if( askExt ) + { + + QString s; + s = i18n("The filename you supplied has no file extension.\nShould the correct one be added automatically? "); + s += i18n( "That would result in the new filename: %1" ).arg( fName); + + result = KMessageBox::questionYesNo(overWidget, s, i18n( "Extension Missing"), + i18n("Add Extension"), i18n("Do Not Add"), + "AutoAddExtensions" ); + } + + if( result == KMessageBox::Yes ) + { + targetUrl.setFileName( fName ); + kdDebug(28000) << "Rename file to " << targetUrl.prettyURL() << endl; + } + } + else if( !extFrom.isEmpty() && extFrom != extTo ) + { + if( ! ((extFrom.lower() == "jpeg" && extTo.lower() == "jpg") || + (extFrom.lower() == "jpg" && extTo.lower() == "jpeg" ))) + { + /* extensions differ -> TODO */ + KMessageBox::error( overWidget, + i18n("Format changes of images are currently not supported."), + i18n("Wrong Extension Found" )); + return(false); + } + } + + bool success = false; + + if( KIO::NetAccess::exists( targetUrl, false,0 ) ) + { + kdDebug(28000)<< "Target already exists - can not copy" << endl; + } + else + { + if( KIO::file_move(fromUrl, targetUrl) ) + { + success = true; + } + } + return( success ); +} + + +QString ImgSaver::tempSaveImage( KookaImage *img, const QString& format, int colors ) +{ + + KTempFile *tmpFile = new KTempFile( QString(), "."+format.lower()); + tmpFile->setAutoDelete( false ); + tmpFile->close(); + + KookaImage tmpImg; + + if( colors != -1 && img->numColors() != colors ) + { + // Need to convert image + if( colors == 1 || colors == 8 || colors == 24 || colors == 32 ) + { + tmpImg = img->convertDepth( colors ); + img = &tmpImg; + } + else + { + kdDebug(29000) << "ERROR: Wrong color depth requested: " << colors << endl; + img = 0; + } + } + + QString name; + if( img ) + { + name = tmpFile->name(); + + if( ! img->save( name, format.latin1() ) ) name = QString(); + } + delete tmpFile; + return name; +} + +bool ImgSaver::copyImage( const KURL& fromUrl, const KURL& toUrl, QWidget *overWidget ) +{ + + /* Check if the provided filename has a extension */ + QString extTo = extension( toUrl ); + QString extFrom = extension( fromUrl ); + KURL targetUrl( toUrl ); + + if( extTo.isEmpty() && !extFrom.isEmpty()) + { + /* Ask if the extension should be added */ + int result = KMessageBox::Yes; + QString fName = toUrl.fileName(); + if( ! fName.endsWith( "." )) + fName += "."; + fName += extFrom; + + QString s; + s = i18n("The filename you supplied has no file extension.\nShould the correct one be added automatically? "); + s += i18n( "That would result in the new filename: %1" ).arg( fName); + + result = KMessageBox::questionYesNo(overWidget, s, i18n( "Extension Missing"), + i18n("Add Extension"), i18n("Do Not Add"), + "AutoAddExtensions" ); + + if( result == KMessageBox::Yes ) + { + targetUrl.setFileName( fName ); + } + } + else if( !extFrom.isEmpty() && extFrom != extTo ) + { + /* extensions differ -> TODO */ + if( ! ((extFrom.lower() == "jpeg" && extTo.lower() == "jpg") || + (extFrom.lower() == "jpg" && extTo.lower() == "jpeg" ))) + { + KMessageBox::error( overWidget, i18n("Format changes of images are currently not supported."), + i18n("Wrong Extension Found" )); + return(false); + } + } + + KIO::Job *copyjob = KIO::copy( fromUrl, targetUrl, false ); + + return( copyjob ? true : false ); +} + + +/* extension needs to be added */ + +#include "img_saver.moc" diff --git a/kooka/img_saver.h b/kooka/img_saver.h new file mode 100644 index 00000000..f9a29898 --- /dev/null +++ b/kooka/img_saver.h @@ -0,0 +1,208 @@ +/*************************************************************************** + img_saver.h - description + ------------------- + begin : Mon Dec 27 1999 + copyright : (C) 1999 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#ifndef __IMG_SAVER_H__ +#define __IMG_SAVER_H__ +#include <qobject.h> +#include <qwidget.h> +#include <qlabel.h> +#include <qmemarray.h> +#include <qstring.h> +#include <qimage.h> +#include <stdlib.h> +#include <qdialog.h> +#include <qpushbutton.h> +#include <qcombobox.h> +#include <qcheckbox.h> +#include <qlistbox.h> +#include <qmap.h> +#include <kdialogbase.h> +#include <kurl.h> + + +#define OP_FILE_ASK_FORMAT "AskForSaveFormat" +#define OP_ASK_FILENAME "AskForFilename" +#define OP_FORMAT_HICOLOR "HiColorSaveFormat" +#define OP_FORMAT_COLOR "ColorSaveFormat" +#define OP_FORMAT_GRAY "GraySaveFormat" +#define OP_FORMAT_BW "BWSaveFormat" +#define OP_FORMAT_THUMBNAIL "ThumbnailFormat" +#define OP_PREVIEW_GROUP "ScanPreview" +#define OP_PREVIEW_FILE "PreviewFile" +#define OP_PREVIEW_FORMAT "PreviewFormat" +#define OP_FILE_GROUP "Files" + + +/** + * enum ImgSaveStat: + * Errorflags for the save. These enums are returned by the + * all image save operations and the calling object my display + * a human readable Error-Message on this information + **/ +typedef enum { + ISS_OK, /* Image save OK */ + ISS_ERR_PERM, /* permission Error */ + ISS_ERR_FILENAME, /* bad filename */ + ISS_ERR_NO_SPACE, /* no space on device */ + ISS_ERR_FORMAT_NO_WRITE, /* Image format can not be written */ + ISS_ERR_UNKNOWN, + ISS_ERR_PARAM, /* Parameter wrong */ + ISS_ERR_PROTOCOL, + ISS_SAVE_CANCELED + +} ImgSaveStat; + +/** + * enum picType: + * Specifies the type of the image to save. This is important for + * getting the format. + **/ +typedef enum { + PT_PREVIEW, + PT_THUMBNAIL, + PT_HICOLOR_IMAGE, + PT_COLOR_IMAGE, + PT_GRAY_IMAGE, + PT_BW_IMAGE, + PT_FINISHED +} picType; + + +class KookaImage; +/** + * Class FormatDialog: + * Asks the user for the image-Format and gives help for + * selecting it. + **/ + +class FormatDialog:public KDialogBase +{ + Q_OBJECT +public: + FormatDialog( QWidget *parent, const QString&, const char * ); + + + QString getFormat( ) const; + QCString getSubFormat( ) const; + QString errorString( ImgSaveStat stat ); + + bool askForFormat( ) const + { return( ! cbDontAsk->isChecked()); } + +public slots: + void setSelectedFormat( QString ); + + +protected slots: + void showHelp( const QString& item ); + +private: + + void check_subformat( const QString & format ); + void buildHelp( void ); + void readConfig( void ); + + QMap<QString, QString> format_help; + QComboBox *cb_subf; + QListBox *lb_format; + QLabel *l_help; + QLabel *l2; + QCheckBox *cbRemember; + QCheckBox *cbDontAsk; +}; + +/** + * Class ImgSaver: + * The main class of this module. It manages all saving of images + * in kooka + * It asks the user for the img-format if desired, creates thumbnails + * and cares for database entries (later ;) + **/ + +class ImgSaver:public QObject { + Q_OBJECT +public: + /** + * constructor of the image-saver object. + * name is the name of a subdirectory of the save directory, + * which can be given in dir. If no dir is given, an + * dir ~/.ksane is created. + * @param dir Name of the save root directory + * @param name Name of a subdirectory in the saveroot. + **/ + ImgSaver( QWidget *parent, const KURL ); + ImgSaver( QWidget *parent ); + + QString errorString( ImgSaveStat ); + /** + * returns the name of the last file that was saved by ImgSaver. + */ + QString lastFilename() const { return( last_file ); } + KURL lastFileUrl() const { return( KURL(last_file )); } + /** + * returns the image format of the last saved image. + */ + QCString lastSaveFormat( void ) const { return( last_format ); } + + QString getFormatForType( picType ) const; + void storeFormatForType( picType, QString, bool ); + bool isRememberedFormat( picType type, QString format ) const; + + /* static function that exports a file */ + static bool copyImage( const KURL& fromUrl, const KURL& toUrl, QWidget *overWidget=0 ); + static bool renameImage( const KURL& fromUrl, KURL& toUrl, bool askExt=false, QWidget *overWidget=0 ); + static QString tempSaveImage( KookaImage *img, const QString& format, int colors = -1 ); + + /* static function that returns the extension of an url */ + static QString extension( const KURL& ); + +public slots: + ImgSaveStat saveImage( QImage *image ); + ImgSaveStat saveImage( QImage *image, const KURL& filename, const QString& imgFormat ); + +private: + QString picTypeAsString( picType type ) const; + QString findFormat( picType type ); + QString findSubFormat( QString format ); + void createDir( const QString& ); + + ImgSaveStat save( QImage *image, const QString &filename, const QString &format, + const QString &subformat ); + QString createFilename( QString format ); + void readConfig( void ); + QString startFormatDialog( picType ); + + // QStrList all_formats; + QString directory; // dir where the image should be saved + QString last_file; + QCString subformat; + QCString last_format; + bool ask_for_format; + + // QDict<QString> formats; +}; + +#endif diff --git a/kooka/imgnamecombo.cpp b/kooka/imgnamecombo.cpp new file mode 100644 index 00000000..77b59a0d --- /dev/null +++ b/kooka/imgnamecombo.cpp @@ -0,0 +1,93 @@ +/*************************************************************************** + imgnamecombo.cpp - combobox for image names + ------------------- + begin : Tue Nov 13 2001 + copyright : (C) 2001 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#include <qlayout.h> +#include <qlabel.h> +#include <qlistview.h> + +#include <kcombobox.h> + +#include <kdebug.h> +#include <klocale.h> +#include <kfiletreebranch.h> + +#include "imgnamecombo.h" +#include "img_saver.h" + +ImageNameCombo::ImageNameCombo( QWidget *parent ) + : KComboBox( parent ) +{ + setInsertionPolicy( QComboBox::AtTop ); +} + +ImageNameCombo::~ImageNameCombo() +{ + +} + +void ImageNameCombo::slotPathRemove( KFileTreeBranch *branch, const QString& relPath ) +{ + QString path = branch->name() + QString::fromLatin1(" - ") + relPath; + + kdDebug(28000) << "ImageNameCombo: Removing " << path << endl; + QString select = currentText(); + + if( items.contains( path )) + { + kdDebug(28000) << "ImageNameCombo: Item exists-> deleting" << endl; + items.remove( path ); + } + + /* */ + rewriteList( branch, select ); +} + +void ImageNameCombo::rewriteList( KFileTreeBranch *branch, const QString& selText ) +{ + clear(); + for ( QStringList::Iterator it = items.begin(); it != items.end(); ++it ) + { + insertItem( branch->pixmap(), *it ); + } + + int index = items.findIndex( selText ); + setCurrentItem( index ); +} + +void ImageNameCombo::slotGalleryPathChanged( KFileTreeBranch* branch, const QString& relativPath ) +{ + QString newPath; + + newPath = branch->name() + QString::fromLatin1(" - ") + relativPath; + + kdDebug( 28000) << "Inserting " << newPath << " to combobox" << endl; + + setCurrentItem( newPath, true /* insert if missing */ ); +} + +/* The End */ +#include "imgnamecombo.moc" diff --git a/kooka/imgnamecombo.h b/kooka/imgnamecombo.h new file mode 100644 index 00000000..a577929e --- /dev/null +++ b/kooka/imgnamecombo.h @@ -0,0 +1,57 @@ +/*************************************************************************** + imgnamecombo.h - combobox for image names + ------------------- + begin : Tue Nov 13 2001 + copyright : (C) 2001 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + + +#ifndef IMGNAMECOMBO_H +#define IMGNAMECOMBO_H + + +#include <kcombobox.h> + +/** + *@author Klaas Freitag +*/ + +class QListViewItem; +class KFileBranch; + +class ImageNameCombo: public KComboBox +{ + Q_OBJECT +public: + ImageNameCombo( QWidget* ); + ~ImageNameCombo(); + +public slots: + + void slotGalleryPathChanged( KFileTreeBranch* branch, const QString& relativPath ); + void slotPathRemove( KFileTreeBranch *branch, const QString& relPath ); +private: + void rewriteList( KFileTreeBranch *, const QString& selText ); + QStringList items; +}; + +#endif diff --git a/kooka/imgprintdialog.cpp b/kooka/imgprintdialog.cpp new file mode 100644 index 00000000..f9aa3930 --- /dev/null +++ b/kooka/imgprintdialog.cpp @@ -0,0 +1,302 @@ +/*************************************************************************** + imgprintdialog.h - Kooka's Image Printing + ------------------- + begin : May 2003 + copyright : (C) 1999 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ +#include "imgprintdialog.h" + +#include <klocale.h> +#include <knuminput.h> +#include <kdialog.h> + +#include <qstring.h> +#include <qmap.h> +#include <qlayout.h> +#include <qvbuttongroup.h> +#include <qcheckbox.h> +#include <qradiobutton.h> +#include "kookaimage.h" +#include <qvgroupbox.h> +#include <qpaintdevicemetrics.h> +#include <qlabel.h> +#include <qtooltip.h> +#include <kdebug.h> + +#define ID_SCREEN 0 +#define ID_ORIG 1 +#define ID_CUSTOM 2 +#define ID_FIT_PAGE 3 + +ImgPrintDialog::ImgPrintDialog( KookaImage *img, QWidget *parent, const char* name ) + : KPrintDialogPage( parent, name ), + m_image(img), + m_ignoreSignal(false) +{ + setTitle(i18n("Image Printing")); + QVBoxLayout *layout = new QVBoxLayout( this ); + // layout->setMargin( KDialog::marginHint() ); + // layout->setSpacing( KDialog::spacingHint() ); + + m_scaleRadios = new QButtonGroup( 2, Qt::Vertical, i18n("Image Print Size"), this ); + m_scaleRadios->setRadioButtonExclusive(true); + connect( m_scaleRadios, SIGNAL(clicked(int)), SLOT(slScaleChanged(int))); + + m_rbScreen = new QRadioButton( i18n("Scale to same size as on screen"), + m_scaleRadios ); + QToolTip::add( m_rbScreen, i18n("Screen scaling. That prints according to the screen resolution.")); + + m_scaleRadios->insert( m_rbScreen, ID_SCREEN ); + + m_rbOrigSize = new QRadioButton( i18n("Original size (calculate from scan resolution)"), + m_scaleRadios ); + QToolTip::add( m_rbOrigSize, + i18n("Calculates the print size from the scan resolution. Enter the scan resolution in the dialog field below." )); + m_scaleRadios->insert( m_rbOrigSize, ID_ORIG ); + + + m_rbScale = new QRadioButton( i18n("Scale image to custom dimension"), m_scaleRadios ); + QToolTip::add( m_rbScale, + i18n("Set the print size yourself in the dialog below. The image is centered on the paper.")); + + m_scaleRadios->insert( m_rbScale, ID_CUSTOM ); + + m_rbFitPage = new QRadioButton( i18n("Scale image to fit to page"), m_scaleRadios ); + QToolTip::add( m_rbFitPage, i18n("Printout uses maximum space on the selected pager. Aspect ratio is maintained.")); + m_scaleRadios->insert( m_rbFitPage, ID_FIT_PAGE ); + + layout->addWidget( m_scaleRadios ); + + + QHBoxLayout *hbox = new QHBoxLayout( this ); + layout->addLayout( hbox ); + + /** Box for Image Resolutions **/ + QVGroupBox *group1 = new QVGroupBox( i18n("Resolutions"), this ); + hbox->addWidget( group1 ); + + /* Postscript generation resolution */ + m_psDraft = new QCheckBox( i18n("Generate low resolution PostScript (fast draft print)"), + group1, "cbPostScriptRes" ); + m_psDraft->setChecked( false ); + + + /* Scan resolution of the image */ + m_dpi = new KIntNumInput( group1 ); + m_dpi->setLabel( i18n("Scan resolution (dpi) " ), AlignVCenter ); + m_dpi->setValue( 300 ); + m_dpi->setSuffix( i18n(" dpi")); + + /* Label for displaying the screen Resolution */ + m_screenRes = new QLabel( group1 ); + + /** Box for Image Print Size **/ + QVGroupBox *group = new QVGroupBox( i18n("Image Print Size"), this ); + hbox->addWidget( group ); + + m_sizeW = new KIntNumInput( group ); + m_sizeW->setLabel( i18n("Image width:"), AlignVCenter ); + m_sizeW->setSuffix( i18n(" mm")); + connect( m_sizeW, SIGNAL(valueChanged(int)), SLOT(slCustomWidthChanged(int))); + m_sizeH = new KIntNumInput( m_sizeW, AlignVCenter, group ); + m_sizeH->setLabel( i18n("Image height:"), AlignVCenter); + m_sizeH->setSuffix( i18n(" mm")); + connect( m_sizeH, SIGNAL(valueChanged(int)), SLOT(slCustomHeightChanged(int))); + + m_ratio = new QCheckBox( i18n("Maintain aspect ratio"), group, "cbAspectRatio" ); + m_ratio->setChecked(true); + + + QWidget *spaceEater = new QWidget( this ); + spaceEater->setSizePolicy( QSizePolicy( QSizePolicy::Ignored, QSizePolicy::Ignored )); + layout->addWidget( spaceEater ); + + /* Set start values */ + m_rbScreen->setChecked(true); + slScaleChanged( ID_SCREEN ); +} + +void ImgPrintDialog::setImage( KookaImage *img ) +{ + if( ! img ) return; + + // TODO: get scan resolution out of the image + +} + +void ImgPrintDialog::setOptions(const QMap<QString,QString>& opts) +{ + // m_autofit->setChecked(opts["app-img-autofit"] == "1"); + QString scale = opts[OPT_SCALING]; + + kdDebug(28000) << "In setOption" << endl; + + if( scale == "scan" ) + m_rbOrigSize->setChecked(true); + else if( scale == "custom" ) + m_rbScale->setChecked(true); + else + m_rbScreen->setChecked(true); + + int help = opts[OPT_SCAN_RES].toInt(); + m_dpi->setValue( help ); + + help = opts[OPT_WIDTH].toInt(); + m_sizeW->setValue( help ); + + help = opts[OPT_HEIGHT].toInt(); + m_sizeH->setValue( help ); + + help = opts[OPT_SCREEN_RES].toInt(); + m_screenRes->setText(i18n( "Screen resolution: %1 dpi").arg(help)); + + help = opts[OPT_PSGEN_DRAFT].toInt(); + m_psDraft->setChecked( help == 1 ); + + help = opts[OPT_RATIO].toInt(); + m_ratio->setChecked( help == 1 ); + +} + + +void ImgPrintDialog::getOptions(QMap<QString,QString>& opts, bool ) +{ + // TODO: Check for meaning of include_def ! + // kdDebug(28000) << "In getOption with include_def: " << include_def << endl; + + QString scale = "screen"; + if( m_rbOrigSize->isChecked() ) + scale = "scan"; + else if( m_rbScale->isChecked() ) + scale = "custom"; + else if( m_rbFitPage->isChecked() ) + scale = "fitpage"; + + opts[OPT_SCALING] = scale; + + opts[OPT_SCAN_RES] = QString::number( m_dpi->value() ); + opts[OPT_WIDTH] = QString::number( m_sizeW->value() ); + opts[OPT_HEIGHT] = QString::number( m_sizeH->value() ); + opts[OPT_PSGEN_DRAFT] = QString::number( m_psDraft->isChecked() ); + opts[OPT_RATIO] = QString::number( m_ratio->isChecked() ); + + { + QPaintDeviceMetrics metric( this ); + opts[OPT_SCREEN_RES] = QString::number( metric.logicalDpiX()); + } +} + +bool ImgPrintDialog::isValid(QString& msg) +{ + /* check if scan reso is higher than 0 in case its needed */ + int id = m_scaleRadios->id( m_scaleRadios->selected()); + if( id == ID_ORIG && m_dpi->value() == 0 ) + { + msg = i18n("Please specify a scan resolution larger than 0"); + return false; + } + else if( id == ID_CUSTOM && (m_sizeW->value() == 0 || m_sizeH->value() == 0 ) ) + { + msg = i18n("For custom printing, a valid size should be specified.\n" + "At least one dimension is zero."); + } + + return true; +} + +void ImgPrintDialog::slScaleChanged( int id ) +{ + if( id == ID_SCREEN ) + { + /* disalbe size, scan res. */ + m_dpi->setEnabled(false); + m_ratio->setEnabled(false); + m_sizeW->setEnabled(false); + m_sizeH->setEnabled(false); + } + else if( id == ID_ORIG ) + { + /* disable size */ + m_dpi->setEnabled(true); + m_ratio->setEnabled(false); + m_sizeW->setEnabled(false); + m_sizeH->setEnabled(false); + } + else if( id == ID_CUSTOM ) + { + m_dpi->setEnabled(false); + m_ratio->setEnabled(true); + m_sizeW->setEnabled(true); + m_sizeH->setEnabled(true); + } + else if( id == ID_FIT_PAGE ) + { + m_dpi->setEnabled(false); + m_ratio->setEnabled(true); + m_sizeW->setEnabled(false); + m_sizeH->setEnabled(false); + } +} + +void ImgPrintDialog::slCustomWidthChanged( int val ) +{ + if( m_ignoreSignal ) + { + m_ignoreSignal = false; + return; + } + + /* go out here if scaling is not custom */ + if( m_scaleRadios->id( m_scaleRadios->selected()) != ID_CUSTOM ) return; + + /* go out here if maintain aspect ration is off */ + if( ! m_ratio->isChecked() ) return; + + m_ignoreSignal = true; + kdDebug(28000) << "Setting value to horizontal size" << endl; + m_sizeH->setValue( int( double(val) * + double(m_image->height())/double(m_image->width()) ) ); + +} + +void ImgPrintDialog::slCustomHeightChanged( int val ) +{ + if( m_ignoreSignal ) + { + m_ignoreSignal = false; + return; + } + + /* go out here if scaling is not custom */ + if( m_scaleRadios->id( m_scaleRadios->selected()) != ID_CUSTOM ) return; + + /* go out here if maintain aspect ration is off */ + if( ! m_ratio->isChecked() ) return; + + m_ignoreSignal = true; + kdDebug(28000) << "Setting value to vertical size" << endl; + m_sizeW->setValue( int( double(val) * + double(m_image->width())/double(m_image->height()) ) ); + +} + +#include "imgprintdialog.moc" diff --git a/kooka/imgprintdialog.h b/kooka/imgprintdialog.h new file mode 100644 index 00000000..845cc038 --- /dev/null +++ b/kooka/imgprintdialog.h @@ -0,0 +1,89 @@ +/*************************************************************************** + imgprintdialog.h - Kooka's Image Printing + ------------------- + begin : May 2003 + copyright : (C) 1999 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#ifndef __IMGPRINTDIALOG_H__ +#define __IMGPRINTDIALOG_H__ + +#include <qmap.h> +#include <qcheckbox.h> +#include <kdeprint/kprintdialogpage.h> + +#include "kookaimage.h" + +#define OPT_SCALING "kde-kooka-scaling" +#define OPT_SCAN_RES "kde-kooka-scanres" +#define OPT_SCREEN_RES "kde-kooka-screenres" +#define OPT_WIDTH "kde-kooka-width" +#define OPT_HEIGHT "kde-kooka-height" +#define OPT_PSGEN_DRAFT "kde-kooka-psdraft" +#define OPT_RATIO "kde-kooka-ratio" +#define OPT_FITPAGE "kde-kooka-fitpage" +class QWidget; +class QString; +class QLabel; +class KIntNumInput; +class KookaImage; +class QVButtonGroup; +class QRadioButton; +class QCheckBox; + +class ImgPrintDialog: public KPrintDialogPage +{ + Q_OBJECT +public: + ImgPrintDialog( KookaImage *img, QWidget *parent=0L, const char* name=0L ); + + void setOptions(const QMap<QString,QString>& opts); + void getOptions(QMap<QString,QString>& opts, bool include_def = false); + bool isValid(QString& msg); + + void setImage( KookaImage *img ); + +protected slots: + void slScaleChanged( int id ); + void slCustomWidthChanged(int); + void slCustomHeightChanged(int); + +private: + QButtonGroup *m_scaleRadios; + QRadioButton *m_rbOrigSize; + QRadioButton *m_rbScale; + QRadioButton *m_rbScreen; + QRadioButton *m_rbFitPage; + + KIntNumInput *m_sizeW; + KIntNumInput *m_sizeH; + KIntNumInput *m_dpi; + + QCheckBox *m_psDraft; + QCheckBox *m_ratio; + + KookaImage *m_image; + QLabel *m_screenRes; + bool m_ignoreSignal; +}; + +#endif diff --git a/kooka/kadmosocr.cpp b/kooka/kadmosocr.cpp new file mode 100644 index 00000000..72f9324b --- /dev/null +++ b/kooka/kadmosocr.cpp @@ -0,0 +1,432 @@ +/*************************************************************************** + kadmosocr.cpp - Kadmos cpp interface + ------------------- + begin : Fri Jun 30 2000 + + (c) 2002 re Recognition AG Hafenstrasse 50b CH-8280 Kreuzlingen + Switzerland Phone: +41 (0)71 6780000 Fax: +41 (0)71 6780099 + Website: www.reRecognition.com E-mail: info@reRecognition.com + + Author: Tamas Nagy (nagy@rerecognition.com) + Klaas Freitag <freitag@suse.de> + Heike Stuerzenhofecker <heike@freisturz.de> + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +/* Kadmos CPP object oriented interface */ + +#include <qimage.h> +#include <qpainter.h> +#include <qstring.h> +#include <qrect.h> +#include <qstringlist.h> + +#include <assert.h> + +#include <string.h> +#include <stdlib.h> +#include <memory.h> +#include <kdebug.h> + +#include "kadmosocr.h" +#include "ocrword.h" + +#ifdef HAVE_KADMOS + +using namespace Kadmos; + +/* -------------------- CRep -------------------- */ +CRep::CRep() + :QObject() +{ + memset(&m_RepData, 0, sizeof(m_RepData)); + m_Error = RE_SUCCESS; + m_undetectChar = QChar('_'); +} + +CRep::~CRep() +{ +} + +RelGraph* CRep::getGraphKnode(int line, int offset ) +{ + Kadmos::RepResult *res = getRepResult(line); + if( res ) + return ( &(getRepResult(line)->rel_graph[0])+offset); + else + return 0L; + +} + + +RepResult* CRep::getRepResult(int line) +{ + if( line<0 || line >= m_RepData.rep_result_len ) return 0L; + return &(m_RepData.rep_result[line]); +} + +RelResult* CRep::getRelResult(int line, RelGraph* graph, int alternative) +{ + if( ! ( graph && getRepResult(line))) return 0L; + int offset = graph->result_number[alternative]; + return( &(getRepResult(line)->rel_result[0]) + offset ); +} + + +KADMOS_ERROR CRep::Init(const char* ClassifierFilename) +{ + /* prepare RepData structure */ + m_RepData.init.rel_grid_maxlen = GRID_MAX_LEN; + m_RepData.init.rel_graph_maxlen = GRAPH_MAX_LEN; + m_RepData.init.rel_result_maxlen = CHAR_MAX_LEN; + m_RepData.init.rep_memory_size = LINE_MAX_LEN * sizeof(RepResult) + + (long)LINE_MAX_LEN * CHAR_MAX_LEN * (sizeof(RelGraph)+ + sizeof(RelResult)); + m_RepData.init.rep_memory = malloc( m_RepData.init.rep_memory_size ); + if (!m_RepData.init.rep_memory) { + CheckError(); + return m_Error; + } + strcpy(m_RepData.init.version, INC_KADMOS); + + m_Error = rep_init(&m_RepData, (char*)ClassifierFilename); + CheckError(); + return m_Error; +} + +void CRep::run() // KADMOS_ERROR CRep::Recognize() +{ + kdDebug(28000) << "ooo Locked and ocr!" << endl; + m_Error = rep_do(&m_RepData); + CheckError(); +} + +KADMOS_ERROR CRep::End() +{ + m_Error = rep_end(&m_RepData); + CheckError(); + return m_Error; +} + +int CRep::GetMaxLine() +{ + return m_RepData.rep_result_len; +} + +const char* CRep::RepTextLine(int nLine, unsigned char RejectLevel, int RejectChar, long Format) +{ + m_Error = rep_textline(&m_RepData, nLine, m_Line, + 2*CHAR_MAX_LEN, RejectLevel, RejectChar, Format); + CheckError(); + return m_Line; +} + +/** + * This method handles the given line. It takes repRes and goes through the + * kadmos result tree structures recursivly. + */ +ocrWordList CRep::getLineWords( int line ) +{ + ocrWordList repWords; + bool ok = true; + + Kadmos::RepResult *repRes = getRepResult(line); + + if( ! repRes ) + { + kdDebug(28000) << "repRes-Pointer is null" << endl; + ok = false; + } + + if( ok ) + { + int nextKnode=0; + + do + { + QString resultWord; + QRect boundingRect; + + int newNextKnode = nextBestWord( line, nextKnode, resultWord, boundingRect ); + boundingRect.moveBy( repRes->left, repRes->top ); + + ocrWord newWord; + newWord = resultWord; + newWord.setKnode(nextKnode); + newWord.setLine(line); + newWord.setRect(boundingRect); + repWords.push_back(newWord); + + /* set nextKnode to the next Knode */ + nextKnode = newNextKnode; + + + // Alternativen: + // partStrings( line, nextKnode, QString()); // fills m_parts - list with alternative words + // nextKnode = newNextKnode; + // kdDebug(28000) << "NextKnodeWord: " << resultWord << endl; + } + while( nextKnode > 0 ); + } + return repWords; +} + + +/* This fills theWord with the next best word and returns the + * next knode or 0 if there is no next node + */ +int CRep::nextBestWord( int line, int knode, QString& theWord, QRect& brect ) +{ + + Kadmos::RelGraph *relg = getGraphKnode( line, knode ); + // kdDebug(28000) << "GraphKnode is " << knode << endl; + int nextKnode = knode; + + while( relg ) + { + Kadmos::RelResult *relr = getRelResult( line, relg, 0 ); // best alternative + if( relr ) + { + // kdDebug(28000) << "Leading Blanks: " << relg->leading_blanks << + // " und Knode " << knode << endl; + char c = relr->rec_char[0][0]; + QChar newChar = c; + if( c == 0 ) + { + kdDebug(28000) << "Undetected char found !" << endl; + newChar = m_undetectChar; + } + + if ( (nextKnode != knode) && (relg->leading_blanks > 0)) + { + /* this means the word ends here. */ + // kdDebug(28000) << "----" << theWord << endl; + relg = 0L; /* Leave the loop. */ + } + else + { + /* append the character */ + theWord.append(newChar); + + /* save the bounding rect */ + // kdDebug(28000) << "LEFT: " << relr->left << " TOP: " << relr->top << endl; + QRect r( relr->left, relr->top, relr->width, relr->height ); + + if( brect.isNull() ) + { + brect = r; + } + else + { + brect = brect.unite( r ); + } + + /* next knode */ + if( relg->next[0] > 0 ) + { + nextKnode = relg->next[0]; + relg = getGraphKnode( line, nextKnode ); + } + else + { + /* end of the line */ + nextKnode = 0; + relg = 0L; + } + } + } + } + return( nextKnode ); +} + + + +void CRep::partStrings( int line, int graphKnode, QString soFar ) +{ + /* The following knodes after a word break */ + Kadmos::RelGraph *relg = getGraphKnode( line, graphKnode ); + // kdDebug(28000) << "GraphKnode is " << graphKnode << endl; + + QString theWord=""; + for( int resNo=0; resNo < SEG_ALT; resNo++ ) + { + // kdDebug(28000) << "Alternative " << resNo << " is " << relg->result_number[resNo] << endl; + if( relg->result_number[resNo] == -1 ) + { + /* This means that there is no other alternative. Go out here. */ + break; + } + + Kadmos::RelResult *relr = getRelResult( line, relg, resNo ); + theWord = QChar(relr->rec_char[0][0]); + + if ( !soFar.isEmpty() && relg->leading_blanks ) + { + /* this means the previous words end. */ + // TODO: This forgets the alternatives of _this_ first character of the new word. + + kdDebug(28000) << "---- " << soFar << endl; + m_parts << soFar; + break; + } + else + { + /* make a QString from this single char and append it. */ + soFar += theWord; + } + + if( relg->next[resNo] > 0 ) + { + /* There is a follower to this knode. Combine the result list from a recursive call + * to this function with the follower knode. + */ + partStrings( line, relg->next[resNo], soFar ); + } + else + { + /* There is no follower */ + kdDebug(28000) << "No followers - theWord is " << soFar << endl; + m_parts<<soFar; + break; + } + } +} + + + +void CRep::drawCharBox( QPixmap *pix, const QRect& r ) +{ + drawBox( pix, r, QColor( Qt::red )); +} + +void CRep::drawLineBox( QPixmap* pix, const QRect& r ) +{ + drawBox( pix, r, QColor( Qt::blue )); +} + +void CRep::drawBox( QPixmap* pix, const QRect& r, const QColor& color ) +{ + QPainter p; + p.begin(pix); + + p.setPen( color ); + p.drawRect(r); +} + + + +KADMOS_ERROR CRep::SetImage( const QString file ) +{ + ReImageHandle image_handle; + image_handle = re_readimage(file.latin1(), &m_RepData.image); + if( ! image_handle ) + { + kdDebug(28000) << "Can not load input file" << endl; + } + CheckError(); + return RE_SUCCESS; + +} + +KADMOS_ERROR CRep::SetImage(QImage *Image) +{ + // memcpy(&m_RepData.image, Image.bits(), Image.numBytes()); + if( !Image ) return RE_PARAMETERERROR; + + kdDebug(28000) << "Setting image manually." << endl; + m_RepData.image.data = (void*)Image->bits(); + m_RepData.image.imgtype = IMGTYPE_PIXELARRAY; + m_RepData.image.width = Image->width(); + m_RepData.image.height = Image->height(); + m_RepData.image.bitsperpixel = Image->depth(); + m_RepData.image.alignment = 1; + m_RepData.image.fillorder = FILLORDER_MSB2LSB; + // color + if( Image->depth() == 1 || (Image->numColors()==2 && Image->depth() == 8) ) + { + m_RepData.image.color=COLOR_BINARY; + kdDebug(28000) << "Setting Binary" << endl; + } else if( Image->isGrayscale() ) { + m_RepData.image.color = COLOR_GRAY; + kdDebug(28000) << "Setting GRAY" << endl; + + } else { + m_RepData.image.color = COLOR_RGB; + kdDebug(28000) << "Setting Color RGB" << endl; + } + // orientation + m_RepData.image.orientation = ORIENTATION_TOPLEFT; + m_RepData.image.photometric = PHOTOMETRIC_MINISWHITE; + m_RepData.image.resunit = RESUNIT_INCH; + m_RepData.image.xresolution = 200; + m_RepData.image.yresolution = 200; + + CheckError(); + + return RE_SUCCESS; +} + +void CRep::SetNoiseReduction(bool bNoiseReduction) +{ + if (bNoiseReduction) { + m_RepData.parm.prep |= PREP_AUTO_NOISEREDUCTION; + } + else { + m_RepData.parm.prep &= !PREP_AUTO_NOISEREDUCTION; + } +} + +void CRep::SetScaling(bool bScaling) +{ + if (bScaling) { + m_RepData.parm.prep |= PREP_SCALING; + } + else { + m_RepData.parm.prep &= !PREP_SCALING; + } +} + +void CRep::CheckError() +{ + if ( kadmosError() ) + { + kdDebug(28000) << "KADMOS ERROR: " << getErrorText() << endl; + } +} + +/* returns a QString containing the string describing the kadmos error */ +QString CRep::getErrorText() const +{ + re_ErrorText Err; + re_GetErrorText(&Err); + return QString::fromLocal8Bit( Err.text ); +} + +bool CRep::kadmosError() +{ + return m_Error != RE_SUCCESS; +} + +#include "kadmosocr.moc" + +#endif /* HAVE_KADMOS */ + + +// } /* End of Kadmos namespace */ diff --git a/kooka/kadmosocr.h b/kooka/kadmosocr.h new file mode 100644 index 00000000..12056209 --- /dev/null +++ b/kooka/kadmosocr.h @@ -0,0 +1,143 @@ +/*************************************************************************** + kadmosocr.h - Kadmos cpp interface + ------------------- + begin : Fri Jun 30 2000 + + (c) 2002 re Recognition AG Hafenstrasse 50b CH-8280 Kreuzlingen + Switzerland Phone: +41 (0)71 6780000 Fax: +41 (0)71 6780099 + Website: www.reRecognition.com E-mail: info@reRecognition.com + + Author: Tamas Nagy (nagy@rerecognition.com) + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#ifndef __KADMOS_OCR_ +#define __KADMOS_OCR_ + +#include <qobject.h> +#include <qstring.h> + +#include "config.h" + +#ifdef HAVE_KADMOS +/* class declarations */ +class QImage; +class QPixmap; +class QColor; +class QStringList; +class QRect; + + +class ocrWord; +class ocrWordList; + +namespace Kadmos { + +/* include files */ + +#include "kadmos.h" +#include <qptrlist.h> + +/* ---------------------------------------- REP ---------------------------------------- */ +//! Maximum number of lines in a paragraph + const int LINE_MAX_LEN = 100; + const int GRID_MAX_LEN = 50; //!< Maximum number of grid elements in a line + const int GRAPH_MAX_LEN = 500; //!< Maximum number of graph elements in a line + const int CHAR_MAX_LEN = 500; //!< Maximum number of characters in a line + + /* Error handling */ + const char CPP_ERROR[] = "Kadmos CPP interface error"; + + /* ==== CRep ========================================= */ + class CRep : public QObject + { + Q_OBJECT + public: + CRep(); + virtual ~CRep(); + + RepResult* getRepResult(int line=0); + RelGraph* getGraphKnode(int line, int offset=0); + RelResult* getRelResult(int line, Kadmos::RelGraph* graph, int alternative=0); + + + /** + @param ClassifierFilename is a name of a classifier file (*.rec) + */ + KADMOS_ERROR Init(const char* ClassifierFile); + + virtual void run(); + virtual bool finished() { return true; } + // KADMOS_ERROR Recognize(); + KADMOS_ERROR End(); + + /** + @param Image is an image object + */ + KADMOS_ERROR SetImage(QImage* Image); + KADMOS_ERROR SetImage( const QString ); + int GetMaxLine(); + + ocrWordList getLineWords( int line ); + + const char* RepTextLine(int Line, unsigned char RejectLevel=128, + int RejectChar='~', long Format=TEXT_FORMAT_ANSI); + + void analyseLine(int, QPixmap* ); + /** Enable/disable noise reduction + @param TRUE(enable)/FALSE(disable) noise reduction + */ + void SetNoiseReduction(bool bNoiseReduction); + + /** Enable/disable scaling (size normalization) + @param TRUE(enable)/FALSE(disable) scaling (size normalization) + */ + void SetScaling(bool bScaling); + + /* draw graphic visualiser into the pixmap pointed to */ + virtual void drawLineBox( QPixmap*, const QRect& ); + virtual void drawCharBox( QPixmap*, const QRect& ); + virtual void drawBox( QPixmap*, const QRect&, const QColor& ); + + int nextBestWord( int line, int knode, QString& theWord, QRect& brect ); + + /* Error text in QString */ + QString getErrorText() const; + bool kadmosError(); + private: + void partStrings( int line, int graphKnode, QString soFar ); + void CheckError(); + + RepData m_RepData; + KADMOS_ERROR m_Error; + char m_Line[2*CHAR_MAX_LEN]; + int m_currLine; + QStringList m_parts; + QString m_theWord; + int m_recurse; + + QChar m_undetectChar; + }; + +} /* End of Kadmos namespace */ + +#endif /* HAVE KADMOS */ + +#endif /* header tagging */ diff --git a/kooka/kocrbase.cpp b/kooka/kocrbase.cpp new file mode 100644 index 00000000..889739e7 --- /dev/null +++ b/kooka/kocrbase.cpp @@ -0,0 +1,368 @@ +/*************************************************************************** + kocrbase.cpp - base dialog for ocr + ------------------- + begin : Fri Now 10 2000 + copyright : (C) 2000 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#include <qlayout.h> +#include <qlabel.h> +#include <qfileinfo.h> +#include <qtooltip.h> +#include <kio/job.h> +#include <kio/previewjob.h> + +#include <kapplication.h> +#include <kconfig.h> +#include <kglobal.h> +#include <kdebug.h> +#include <klocale.h> +#include <kanimwidget.h> +#include <kseparator.h> +#include <kmessagebox.h> +#include <kactivelabel.h> +#include <qhbox.h> +#include <qvbox.h> + +#include "resource.h" +#include "kocrbase.h" +#include "ksaneocr.h" +#include "kookaimage.h" + +#include <kscanslider.h> +#include <kstandarddirs.h> +#include <kfilemetainfo.h> +#include <ksconfig.h> +#include <qstringlist.h> +#include <qcolor.h> +#include <qgrid.h> +#include <qsizepolicy.h> +#include <qgroupbox.h> +#include <qcheckbox.h> + +KOCRBase::KOCRBase( QWidget *parent, KSpellConfig *spellConfig, + KDialogBase::DialogType face ) + :KDialogBase( face, i18n("Optical Character Recognition"), + User2|Close|User1, User1, parent,0, false, true, + KGuiItem( i18n("Start OCR" ), "launch", + i18n("Start the Optical Character Recognition process" )), + KGuiItem( i18n("Cancel" ), "stopocr", + i18n("Stop the OCR Process" ))), + m_animation(0L), + m_metaBox(0L), + m_imgHBox(0L), + m_previewPix(0L), + m_currImg(0L), + m_spellConfig(spellConfig), + m_wantSpellCfg(true), + m_userWantsSpellCheck(true), + m_cbWantCheck(0L), + m_gbSpellOpts(0L) +{ + kdDebug(28000) << "OCR Base Dialog!" << endl; + // Layout-Boxes + + KConfig *konf = KGlobal::config (); + KConfigGroupSaver gs( konf, CFG_OCR_KSPELL ); + m_userWantsSpellCheck = konf->readBoolEntry(CFG_WANT_KSPELL, true); + + /* Connect signals which disable the fields and store the configuration */ + connect( this, SIGNAL( user1Clicked()), this, SLOT( writeConfig())); + connect( this, SIGNAL( user1Clicked()), this, SLOT( startOCR() )); + connect( this, SIGNAL( user2Clicked()), this, SLOT( stopOCR() )); + m_previewSize.setWidth(200); + m_previewSize.setHeight(300); + + enableButton( User1, true ); /* start ocr */ + enableButton( User2, false ); /* Cancel */ + enableButton( Close, true ); +} + + +KAnimWidget* KOCRBase::getAnimation(QWidget *parent) +{ + if( ! m_animation ) + { + m_animation = new KAnimWidget( QString("kde"), 48, parent, "ANIMATION" ); + } + return( m_animation ); +} + +EngineError KOCRBase::setupGui() +{ + ocrIntro(); + imgIntro(); + if( m_wantSpellCfg ) spellCheckIntro(); + + return ENG_OK; +} + +void KOCRBase::imgIntro() +{ + m_imgPage = addVBoxPage( i18n("Image") ); + (void) new QLabel( i18n("Image Information"), m_imgPage ); + + // Caption - Label and image + m_imgHBox = new QHBox( m_imgPage ); + + m_imgHBox->setSpacing( KDialog::spacingHint()); + + m_previewPix = new QLabel( m_imgHBox ); + m_previewPix->setPixmap(QPixmap()); + m_previewPix->setFixedSize(m_previewSize); + m_previewPix->setAlignment( Qt::AlignCenter ); + m_previewPix->setFrameStyle( QFrame::Panel | QFrame::Sunken ); + // m_previewPix->resize(m_previewSize); + + /* See introduceImage where the meta box is filled with data from the + * incoming widget. + */ + m_metaBox = new QVBox( m_imgHBox ); +} + +/* + * This creates a Tab OCR + */ +void KOCRBase::ocrIntro( ) +{ + m_ocrPage = addVBoxPage( i18n("OCR") ); + + // Caption - Label and image + /* labelstring */ + (void) new QLabel( i18n("<b>Starting Optical Character Recognition with %1</b><p>"). + arg( ocrEngineName() ), m_ocrPage ); + // Find the kadmos logo and display if available + KStandardDirs stdDir; + QString logo = stdDir.findResource( "data", "kooka/pics/" + ocrEngineLogo() ); + + kdDebug(28000)<< "Reading logo " << logo << endl; + QPixmap pix; + QWidget *pa = m_ocrPage; + + if( pix.load( logo )) + { + QHBox *hb_cap = new QHBox( m_ocrPage ); + hb_cap->setSpacing( KDialog::spacingHint()); + + QLabel *imgLab = new QLabel( hb_cap ); + imgLab->setAlignment( Qt::AlignHCenter | Qt::AlignTop ); + imgLab->setPixmap( pix ); + pa = hb_cap; + } + + (void) new KActiveLabel( ocrEngineDesc(), pa ); +} + + +void KOCRBase::spellCheckIntro() +{ + m_spellchkPage = addVBoxPage( i18n("Spell-checking") ); + + /* Want the spell checking at all? Checkbox here */ + QGroupBox *gb1 = new QGroupBox( 1, Qt::Horizontal, i18n("OCR Post Processing"), m_spellchkPage ); + m_cbWantCheck = new QCheckBox( i18n("Enable spell-checking for validation of the OCR result"), + gb1 ); + /* Spellcheck options */ + m_gbSpellOpts = new QGroupBox( 1, Qt::Horizontal, i18n("Spell-Check Options"), + m_spellchkPage ); + + KSpellConfig *sCfg = new KSpellConfig( m_gbSpellOpts, "SPELLCHK", m_spellConfig, false ); + /* A space eater */ + QWidget *spaceEater = new QWidget(m_spellchkPage); + spaceEater->setSizePolicy( QSizePolicy( QSizePolicy::Ignored, QSizePolicy::Ignored )); + + /* connect toggle button */ + connect( m_cbWantCheck, SIGNAL(toggled(bool)), this, SLOT(slWantSpellcheck(bool))); + m_cbWantCheck->setChecked( m_userWantsSpellCheck ); + m_gbSpellOpts->setEnabled( m_userWantsSpellCheck ); + m_spellConfig = sCfg; + + connect( sCfg, SIGNAL(configChanged()), + this, SLOT(slSpellConfigChanged())); +} + +void KOCRBase::slSpellConfigChanged() +{ + kdDebug(28000) << "Spellcheck config changed" << endl; +} + + + +void KOCRBase::stopAnimation() +{ + if( m_animation ) + m_animation->stop(); +} + +void KOCRBase::startAnimation() +{ + if( m_animation ) + m_animation->start(); +} + +KOCRBase::~KOCRBase() +{ + +} + +void KOCRBase::introduceImage( KookaImage* img) +{ + if( ! (img && img->isFileBound()) ) return; + KFileMetaInfo info = img->fileMetaInfo(); + QStringList groups; + if ( info.isValid() ) + groups = info.preferredGroups(); + + delete m_metaBox; + m_metaBox = new QVBox( m_imgHBox ); + + /* Start to create a preview job for the thumb */ + KURL::List li(img->url()); + KIO::Job *m_job = KIO::filePreview(li, m_previewSize.width(), + m_previewSize.height()); + + if( m_job ) + { + connect( m_job, SIGNAL( result( KIO::Job * )), + this, SLOT( slPreviewResult( KIO::Job * ))); + connect( m_job, SIGNAL( gotPreview( const KFileItem*, const QPixmap& )), + SLOT( slGotPreview( const KFileItem*, const QPixmap& ) )); + /* KIO::Jo result is called in any way: Success, Failed, Error, + * thus connecting the failed is not really necessary. + */ + } + + for ( QStringList::Iterator it = groups.begin(); it != groups.end(); ++it ) + { + QString theGroup(*it); + + kdDebug(29000) << "handling the group " << theGroup << endl; + + QStringList keys = info.group(theGroup).supportedKeys(); + + if( keys.count() > 0 ) + { + // info.groupInfo( theGroup )->translatedName() + // FIXME: howto get the translated group name? + QLabel *lGroup = new QLabel( theGroup, m_metaBox ); + lGroup->setBackgroundColor( QColor(gray)); + lGroup->setMargin( KDialog::spacingHint()); + + QGrid *nGrid = new QGrid( 2, m_metaBox ); + nGrid->setSpacing( KDialog::spacingHint()); + for ( QStringList::Iterator keyIt = keys.begin(); keyIt != keys.end(); ++keyIt ) + { + KFileMetaInfoItem item = info.item(*keyIt); + QString itKey = item.translatedKey(); + if( itKey.isEmpty() ) + itKey = item.key(); + if( ! itKey.isEmpty() ) + { + (void) new QLabel( item.translatedKey() + ": ", nGrid ); + (void) new QLabel( item.string(), nGrid ); + kdDebug(29000) << "hasKey " << *keyIt << endl; + } + } + } + } + QWidget *spaceEater = new QWidget( m_metaBox ); + spaceEater->setSizePolicy( QSizePolicy( QSizePolicy::Ignored, QSizePolicy::Ignored )); + m_metaBox->show(); +} + +void KOCRBase::slPreviewResult(KIO::Job *job ) +{ + // nothing + if( job && job->error() > 0 ) + { + kdDebug(28000) << "Thumbnail Creation ERROR: " << job->errorString() << endl; + job->showErrorDialog( 0 ); + } +} + +void KOCRBase::slGotPreview( const KFileItem*, const QPixmap& newPix ) +{ + kdDebug(28000) << "Got the preview" << endl; + m_previewPix->setPixmap(newPix); + + if( m_previewPix && m_currImg ) + { + m_previewPix->setPixmap(newPix); + } +} + + +void KOCRBase::writeConfig() +{ + +} + +bool KOCRBase::wantSpellCheck() +{ + return m_userWantsSpellCheck; +} + +void KOCRBase::startOCR() +{ + /* en- and disable the buttons */ + kdDebug(28000) << "Base: Starting OCR" << endl; + + enableFields(false); + enableButton( User1, false ); /* Start OCR */ + enableButton( User2, true ); /* Stop OCR */ + enableButton( Close, true ); + + startAnimation(); +} + +void KOCRBase::stopOCR() +{ + enableFields(true); + + enableButton( User1, true ); /* start ocr */ + enableButton( User2, false ); /* Cancel */ + enableButton( Close, true ); + + stopAnimation(); + +} + +void KOCRBase::enableFields(bool) +{ + +} + +void KOCRBase::slWantSpellcheck( bool wantIt ) +{ + if( m_gbSpellOpts ) + { + m_gbSpellOpts->setEnabled( wantIt ); + } + m_userWantsSpellCheck = wantIt; + + KConfig *konf = KGlobal::config (); + KConfigGroupSaver gs( konf, CFG_OCR_KSPELL ); + konf->writeEntry( CFG_WANT_KSPELL, wantIt ); +} + +/* The End ;) */ +#include "kocrbase.moc" diff --git a/kooka/kocrbase.h b/kooka/kocrbase.h new file mode 100644 index 00000000..05987b1d --- /dev/null +++ b/kooka/kocrbase.h @@ -0,0 +1,158 @@ +/*************************************************************************** + kocrbase.h - base dialog for OCR + ------------------- + begin : Sun Jun 11 2000 + copyright : (C) 2000 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#ifndef KOCRBASE_H +#define KOCRBASE_H + +#include <kdialogbase.h> +#include <kio/previewjob.h> +#include <qimage.h> +#include <qstring.h> + +#include <kscanslider.h> +#include <kanimwidget.h> +#include <ksconfig.h> + +#include "ksaneocr.h" +/** + *@author Klaas Freitag + */ + + +class KookaImage; +class QHBox; +class QVBox; +class QLabel; +class QSize; +class KSpellConfig; +class QCheckBox; +class QGroupBox; + +class KOCRBase: public KDialogBase +{ + Q_OBJECT +public: + KOCRBase( QWidget *, KSpellConfig *spellConfig, + KDialogBase::DialogType face = KDialogBase::Plain ); + ~KOCRBase(); + + virtual EngineError setupGui(); + + /** + * @return the name of the ocr engine + */ + virtual QString ocrEngineName() const { return QString(); } + + /** + * @return the filename (without path) of the logo of the ocr engine. + * the logo needs to be installed in $KDEDIR/share/apps/kooka/pics + */ + virtual QString ocrEngineLogo() const { return QString(); } + + /** + * @return a description string of the ocr engine + */ + virtual QString ocrEngineDesc() const { return QString(); } + + QVBox* ocrPage() const { return m_ocrPage; } + QVBox* imagePage() const { return m_imgPage; } + + KSpellConfig* spellConfig() const + { return m_spellConfig; } + + bool wantSpellCheck(); + +public slots: + virtual void stopAnimation(); + virtual void startAnimation(); + + virtual void introduceImage( KookaImage* ); + + virtual void startOCR(); + virtual void stopOCR(); + /** + * enable or disable dialog fields. This slot is called when the ocr process starts + * with parameter state=false and called again if the gui should accept user input + * again after ocr finished with parameter true. + */ + virtual void enableFields(bool state); + +protected: + /** + * This creates a a tab OCR in the dialog and creates a small intro about the + * ocr engine used. + * It calls the virtual subs ocrEngineName, ocrEngineLogo and ocrEngineDesc which + * must return the approbiate values for the engines. + * @return a pointer to a VBox in which further elements can be layouted + */ + virtual void ocrIntro(); + + /** + * This creates a a tab Image Info in the dialog and creates a image description + * about the current image to ocr. + */ + virtual void imgIntro(); + + /** + * This sets up the spellchecking configuration + */ + virtual void spellCheckIntro(); + + +protected slots: + virtual KAnimWidget* getAnimation(QWidget*); + virtual void writeConfig(); + virtual void slSpellConfigChanged(); + + /** + * hit if the user toggles the want-spellcheck checkbox + */ + virtual void slWantSpellcheck( bool wantIt ); + +private slots: + virtual void slPreviewResult( KIO::Job* ); + virtual void slGotPreview( const KFileItem*, const QPixmap& ); + +private: + KAnimWidget *m_animation; + QVBox *m_ocrPage; + QVBox *m_imgPage; + QVBox *m_spellchkPage; + QVBox *m_metaBox; + QHBox *m_imgHBox; + QLabel *m_previewPix; + KookaImage *m_currImg; + + KSpellConfig *m_spellConfig; + bool m_wantSpellCfg; /* show the spellcheck options? */ + bool m_userWantsSpellCheck; /* user has enabled/disabled spellcheck */ + QSize m_previewSize; + + QCheckBox *m_cbWantCheck; + QGroupBox *m_gbSpellOpts; +}; + +#endif diff --git a/kooka/kocrgocr.cpp b/kooka/kocrgocr.cpp new file mode 100644 index 00000000..cfc4c92c --- /dev/null +++ b/kooka/kocrgocr.cpp @@ -0,0 +1,201 @@ +/*************************************************************************** + kocrgocr.cpp - GOCR ocr dialog + ------------------- + begin : Fri Now 10 2000 + copyright : (C) 2000 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +/* $Id$ */ + +#include <qlayout.h> +#include <qlabel.h> +#include <qfileinfo.h> +#include <qtooltip.h> + +#include <kapplication.h> +#include <kconfig.h> +#include <kglobal.h> +#include <kdebug.h> +#include <klocale.h> +#include <kanimwidget.h> +#include <kseparator.h> +#include <kmessagebox.h> + +#include "resource.h" +#include "ksaneocr.h" // TODO: Really needed? +#include "kocrgocr.h" +#include "kocrgocr.moc" +#include <kscanslider.h> +#include "kookaimage.h" +#include "kookapref.h" +#include <qvbox.h> +#include <qhbox.h> + +/* defines for konfig-reading */ + +#define CFG_GOCR_DUSTSIZE "gocrDustSize" +#define CFG_GOCR_GRAYLEVEL "gocrGrayLevel" +#define CFG_GOCR_SPACEWIDTH "gocrSpaceWidth" + + + +KGOCRDialog::KGOCRDialog( QWidget *parent, KSpellConfig *spellConfig ) + :KOCRBase( parent, spellConfig, KDialogBase::Tabbed ), + m_ocrCmd( QString()) +{ + kdDebug(28000) << "Starting KOCR-Start-Dialog!" << endl; + // Layout-Boxes +} + +QString KGOCRDialog::ocrEngineLogo() const +{ + return "gocr.png"; +} + +QString KGOCRDialog::ocrEngineName() const +{ + return i18n("GOCR" ); +} + +QString KGOCRDialog::ocrEngineDesc() const +{ + return i18n("GOCR is an Open Source project " + "for optical character recognition.<P>" + "The author of gocr is <B>Joerg Schulenburg</B><BR>" + "For more information about gocr see " + "<A HREF=http://jocr.sourceforge.net>" + "http://jocr.sourceforge.net</A>"); +} + +EngineError KGOCRDialog::setupGui() +{ + KOCRBase::setupGui(); + + QVBox *page = ocrPage(); + Q_CHECK_PTR( page ); + + KConfig *conf = KGlobal::config (); + conf->setGroup( CFG_GROUP_OCR_DIA ); + + // Horizontal line + // (void) new KSeparator( KSeparator::HLine, page); + + // Entry-Field. + QString res = conf->readPathEntry( CFG_GOCR_BINARY, "notFound" ); + if( res == "notFound" ) + { + res = KookaPreferences::tryFindGocr(); + if( res.isEmpty() ) + { + /* Popup here telling that the config needs to be called */ + KMessageBox::sorry( this, i18n( "The path to the gocr binary is not configured yet.\n" + "Please go to the Kooka configuration and enter the path manually."), + i18n("OCR Software Not Found") ); + } + } + + if( res.isEmpty() ) + res = i18n("Not found"); + else + m_ocrCmd = res; + + (void) new QLabel( i18n("Using GOCR binary: ") + res, page ); + (void) new KSeparator( KSeparator::HLine, page); + + QHBox *hb = new QHBox(page); + hb->setSpacing( KDialog::spacingHint()); + QVBox *innerBox = new QVBox( hb ); + innerBox->setSpacing( KDialog::spacingHint()); + /* This is for a 'work-in-progress'-Animation */ + getAnimation(hb); + + /* Slider for OCR-Options */ + sliderGrayLevel = new KScanSlider( innerBox , i18n("&Gray level"), 0, 254, true, 160 ); + int numdefault = conf->readNumEntry( CFG_GOCR_GRAYLEVEL, 160 ); + sliderGrayLevel->slSetSlider( numdefault ); + QToolTip::add( sliderGrayLevel, + i18n( "The numeric value gray pixels are \nconsidered to be black.\n\nDefault is 160")); + + sliderDustSize = new KScanSlider( innerBox, i18n("&Dust size" ), 0, 60, true, 10 ); + numdefault = conf->readNumEntry( CFG_GOCR_DUSTSIZE, 10 ); + sliderDustSize->slSetSlider( numdefault ); + QToolTip::add( sliderDustSize, + i18n( "Clusters smaller than this value\nwill be considered to be dust and \nremoved from the image.\n\nDefault is 10")); + + sliderSpace = new KScanSlider( innerBox, i18n( "&Space width" ), 0, 60, true, 0 ); + numdefault = conf->readNumEntry( CFG_GOCR_SPACEWIDTH, 0 ); + sliderSpace->slSetSlider( numdefault ); + QToolTip::add( sliderSpace, i18n("Spacing between characters.\n\nDefault is 0 what means autodetection")); + + return ENG_OK; +} + +void KGOCRDialog::introduceImage( KookaImage *img ) +{ + if( !img ) return; + + KOCRBase::introduceImage( img ); + + + bool isOn = true; + + if( img->numColors() > 0 && img->numColors() <3 ) + { + kdDebug(29000) << "introduceImage: Have " << img->numColors() << " colors on depth " << img->depth() << endl; + + /* that means it is a black-and-white image. Thus we do not need the GrayLevel slider */ + isOn = false; + } + + if( sliderGrayLevel ) + sliderGrayLevel->setEnabled( isOn ); + +} + + +KGOCRDialog::~KGOCRDialog() +{ + +} + +void KGOCRDialog::writeConfig( void ) +{ + KConfig *conf = KGlobal::config (); + conf->setGroup( CFG_GROUP_OCR_DIA ); + + conf->writeEntry( CFG_GOCR_BINARY, QString(getOCRCmd())); + conf->writeEntry( CFG_GOCR_GRAYLEVEL, getGraylevel()); + conf->writeEntry( CFG_GOCR_DUSTSIZE, getDustsize()); + conf->writeEntry( CFG_GOCR_SPACEWIDTH, getSpaceWidth()); +} + + +void KGOCRDialog::enableFields(bool b) +{ + kdDebug(28000) << "About to disable the entry fields" << endl; + sliderGrayLevel->setEnabled( b ); + sliderDustSize->setEnabled( b ); + sliderSpace->setEnabled( b ); +} + +/* The End ;) */ + diff --git a/kooka/kocrgocr.h b/kooka/kocrgocr.h new file mode 100644 index 00000000..619cfedd --- /dev/null +++ b/kooka/kocrgocr.h @@ -0,0 +1,86 @@ +/*************************************************************************** + kocrgocr.h - ocr dialog for GOCR + ------------------- + begin : Sun Jun 11 2000 + copyright : (C) 2000 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + + +#ifndef KOCRGOCR_H +#define KOCRGOCR_H + +#include <kdialogbase.h> +#include <qimage.h> +#include <qstring.h> + +#include <kscanslider.h> +#include <kanimwidget.h> + +#include "kocrbase.h" +/** + *@author Klaas Freitag + */ + +class KSpellConfig; + +class KGOCRDialog: public KOCRBase +{ + Q_OBJECT +public: + KGOCRDialog( QWidget*, KSpellConfig* ); + ~KGOCRDialog(); + + QString getOCRCmd( void ) const + { return m_ocrCmd;} + + int getGraylevel( void ) const + { return( sliderGrayLevel->value());} + int getDustsize( void ) const + { return( sliderDustSize->value());} + int getSpaceWidth( void ) const + { return( sliderSpace->value());} + + EngineError setupGui(); + + QString ocrEngineName() const; + QString ocrEngineDesc() const; + QString ocrEngineLogo() const; + +public slots: + void enableFields(bool); + void introduceImage( KookaImage* ); + +protected: + void writeConfig(); + + +private: + + + KScanSlider *sliderGrayLevel; + KScanSlider *sliderDustSize; + KScanSlider *sliderSpace; + + QString m_ocrCmd; +}; + +#endif diff --git a/kooka/kocrkadmos.cpp b/kooka/kocrkadmos.cpp new file mode 100644 index 00000000..b4d58244 --- /dev/null +++ b/kooka/kocrkadmos.cpp @@ -0,0 +1,521 @@ +/*************************************************************************** + kocrstartdia.cpp - description + ------------------- + begin : Fri Now 10 2000 + copyright : (C) 2000 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#include <qlayout.h> +#include <qlabel.h> +#include <qfileinfo.h> +#include <qtooltip.h> +#include <qvbox.h> +#include <qdict.h> +#include <qdir.h> +#include <qmap.h> +#include <qbuttongroup.h> +#include <qradiobutton.h> + +#include <kapplication.h> +#include <kconfig.h> +#include <kglobal.h> +#include <kdebug.h> +#include <klocale.h> +#include <kanimwidget.h> +#include <kseparator.h> +#include <kmessagebox.h> + +#include "resource.h" +#include "ksaneocr.h" // TODO: Really needed? +#include "kocrkadmos.h" +#include "kocrkadmos.moc" + +#include <kscanslider.h> +#include <qcheckbox.h> +#include <kstandarddirs.h> +#include <qstringlist.h> + + +/* defines for konfig-reading */ +#define CFG_GROUP_KADMOS "Kadmos" +#define CFG_KADMOS_CLASSIFIER_PATH "classifierPath" +#define CFG_KADMOS_CLASSIFIER "classifier" + + +#define CNTRY_CZ i18n( "Czech Republic, Slovakia") +#define CNTRY_GB i18n( "Great Britain, USA" ) + +KadmosDialog::KadmosDialog( QWidget *parent, KSpellConfig *spellConfig ) + :KOCRBase( parent, spellConfig, KDialogBase::Tabbed ), + m_cbNoise(0), + m_cbAutoscale(0), + m_haveNorm(false) +{ + kdDebug(28000) << "Starting KOCR-Start-Dialog!" << endl; + // Layout-Boxes + findClassifiers(); +} + +QString KadmosDialog::ocrEngineLogo() const +{ + return "kadmoslogo.png"; +} + +QString KadmosDialog::ocrEngineName() const +{ + return i18n("KADMOS OCR/ICR"); +} + +QString KadmosDialog::ocrEngineDesc() const +{ + return i18n("This version of Kooka was linked with the <I>KADMOS OCR/ICR engine</I>, a " + "commercial engine for optical character recognition.<P>" + "Kadmos is a product of <B>re Recognition AG</B><BR>" + "For more information about Kadmos OCR see " + "<A HREF=http://www.rerecognition.com>" + "http://www.rerecognition.com</A>"); +} + + +EngineError KadmosDialog::findClassifiers() +{ + findClassifierPath(); + + KLocale *locale = KGlobal::locale(); + QStringList allCountries = locale->allLanguagesTwoAlpha (); + for ( QStringList::Iterator it = allCountries.begin(); + it != allCountries.end(); ++it ) + { + m_longCountry2short[locale->twoAlphaToCountryName(*it)] = *it; + } + m_longCountry2short[i18n("European Countries")] = "eu"; + m_longCountry2short[ CNTRY_CZ ] = "cz"; + m_longCountry2short[ CNTRY_GB ] = "us"; + + QStringList lst; + + /* custom Path */ + if( ! m_customClassifierPath.isEmpty() ) + { + QDir dir( m_customClassifierPath ); + + QStringList lst1 = dir.entryList( "ttf*.rec" ); + + for ( QStringList::Iterator it = lst1.begin(); it != lst1.end(); ++it ) + { + lst << m_customClassifierPath + *it; + } + + lst1 = dir.entryList( "hand*.rec" ); + + for ( QStringList::Iterator it = lst1.begin(); it != lst1.end(); ++it ) + { + lst << m_customClassifierPath + *it; + } + + lst1 = dir.entryList( "norm*.rec" ); + + for ( QStringList::Iterator it = lst1.begin(); it != lst1.end(); ++it ) + { + lst << m_customClassifierPath + *it; + } + } + else + { + /* standard location */ + KStandardDirs stdDir; + kdDebug(28000) << "Starting to read resource" << endl; + + lst = stdDir.findAllResources( "data", + "kooka/classifiers/*.rec", + true, /* recursive */ + true ); /* uniqu */ + } + + + /* no go through lst and sort out hand-, ttf- and norm classifier */ + for ( QStringList::Iterator it = lst.begin(); it != lst.end(); ++it ) + { + QFileInfo fi( *it); + QString name = fi.fileName().lower(); + + kdDebug(28000) << "Checking file " << *it << endl; + + if( name.startsWith( "ttf" ) ) + { + QString lang = name.mid(3,2); + if( allCountries.contains(lang) ) + { + QString lngCountry = locale->twoAlphaToCountryName(lang); + if( lngCountry.isEmpty() ) + lngCountry = name; + m_ttfClassifier << lngCountry; + kdDebug(28000) << "ttf: Insert country " << lngCountry << endl; + } + else if( lang == "cz" ) + { + m_ttfClassifier << CNTRY_CZ; + } + else if( lang == "us" ) + { + m_ttfClassifier << CNTRY_GB; + } + else + { + m_ttfClassifier << name; + kdDebug(28000) << "ttf: Unknown country" << endl; + } + } + else if( name.startsWith( "hand" ) ) + { + QString lang = name.mid(4,2); + if( allCountries.contains(lang) ) + { + QString lngCountry = locale->twoAlphaToCountryName(lang); + if( lngCountry.isEmpty() ) + lngCountry = name; + m_handClassifier << lngCountry; + } + else if( lang == "cz" ) + { + m_handClassifier << i18n( "Czech Republic, Slovakia"); + } + else if( lang == "us" ) + { + m_handClassifier << i18n( "Great Britain, USA" ); + } + else + { + kdDebug(28000) << "Hand: Unknown country " << lang << endl; + m_handClassifier << name; + } + } + else if( name.startsWith( "norm" )) + { + m_haveNorm = true; + } + + kdDebug(28000) << "Found classifier: " << *it << endl; + m_classifierPath << *it; + } + + if( m_handClassifier.count()+m_ttfClassifier.count()>0 ) + { + /* There are classifiers */ + return ENG_OK; + } + else + { + /* Classifier are missing */ + return ENG_DATA_MISSING; + } +} + + +EngineError KadmosDialog::findClassifierPath() +{ + KStandardDirs stdDir; + EngineError err = ENG_OK; + + KConfig *conf = KGlobal::config (); + KConfigGroupSaver gs( conf, CFG_GROUP_KADMOS ); + + m_customClassifierPath = conf->readPathEntry( CFG_KADMOS_CLASSIFIER_PATH ); +#if 0 + if( m_customClassifierPath == "NotFound" ) + { + /* Wants the classifiers from the standard kde paths */ + KMessageBox::error(0, i18n("The classifier files for KADMOS could not be found.\n" + "OCR with KADMOS will not be possible!\n\n" + "Change the OCR engine in the preferences dialog."), + i18n("Installation Error") ); + } + else + { + m_classifierPath = customPath; + } +#endif + return err; + +} + + +EngineError KadmosDialog::setupGui() +{ + + EngineError err = KOCRBase::setupGui(); + + // setupPreprocessing( addVBoxPage( i18n("Preprocessing"))); + // setupSegmentation( addVBoxPage( i18n("Segmentation"))); + // setupClassification( addVBoxPage( i18n("Classification"))); + + /* continue page setup on the first page */ + QVBox *page = ocrPage(); + + // Horizontal line + (void) new KSeparator( KSeparator::HLine, page); + + // FIXME: dynamic classifier reading. + + (void) new QLabel( i18n("Please classify the font type and language of the text on the image:"), + page ); + QHBox *locBox = new QHBox( page ); + m_bbFont = new QButtonGroup(1, Qt::Horizontal, i18n("Font Type Selection"), locBox); + + m_rbMachine = new QRadioButton( i18n("Machine print"), m_bbFont ); + m_rbHand = new QRadioButton( i18n("Hand writing"), m_bbFont ); + m_rbNorm = new QRadioButton( i18n("Norm font"), m_bbFont ); + + m_gbLang = new QGroupBox(1, Qt::Horizontal, i18n("Country"), locBox); + + + m_cbLang = new QComboBox( m_gbLang ); + m_cbLang->setCurrentText( KLocale::defaultCountry() ); + + connect( m_bbFont, SIGNAL(clicked(int)), this, SLOT(slFontChanged(int) )); + m_rbMachine->setChecked(true); + + /* --- */ + QHBox *innerBox = new QHBox( page ); + innerBox->setSpacing( KDialog::spacingHint()); + + QButtonGroup *cbGroup = new QButtonGroup( 1, Qt::Horizontal, i18n("OCR Modifier"), innerBox ); + Q_CHECK_PTR(cbGroup); + + m_cbNoise = new QCheckBox( i18n( "Enable automatic noise reduction" ), cbGroup ); + m_cbAutoscale = new QCheckBox( i18n( "Enable automatic scaling"), cbGroup ); + + getAnimation(innerBox); + // (void) new QWidget ( page ); + + if( err != ENG_OK ) + { + enableFields(false); + enableButton(User1, false ); + } + + if( m_ttfClassifier.count() == 0 ) + { + m_rbMachine->setEnabled(false); + } + if( m_handClassifier.count() == 0 ) + { + m_rbHand->setEnabled(false); + } + if( !m_haveNorm ) + m_rbNorm->setEnabled(false); + + if( (m_ttfClassifier.count() + m_handClassifier.count()) == 0 && ! m_haveNorm ) + { + KMessageBox::error(0, i18n("The classifier files for KADMOS could not be found.\n" + "OCR with KADMOS will not be possible!\n\n" + "Change the OCR engine in the preferences dialog."), + i18n("Installation Error") ); + err = ENG_BAD_SETUP; + } + else + slFontChanged( 0 ); // Load machine print font language list + return err; +} + +void KadmosDialog::slFontChanged( int id ) +{ + m_cbLang->clear(); + + KConfig *conf = KGlobal::config (); + KConfigGroupSaver gs( conf, CFG_GROUP_KADMOS ); + + + + m_customClassifierPath = conf->readPathEntry( CFG_KADMOS_CLASSIFIER_PATH ); + + bool enable = true; + + if( id == 0 ) /* Machine Print */ + { + m_cbLang->insertStringList( m_ttfClassifier ); + } + else if( id == 1 ) /* Hand Writing */ + { + m_cbLang->insertStringList( m_handClassifier ); + } + else if( id == 2 ) /* Norm Font */ + { + enable = false; + } + m_cbLang->setEnabled( enable ); +} + + +void KadmosDialog::setupPreprocessing( QVBox* ) +{ + +} + +void KadmosDialog::setupSegmentation( QVBox* ) +{ + +} + +void KadmosDialog::setupClassification( QVBox* ) +{ + +} +/* + * returns the complete path of the classifier selected in the + * GUI in the parameter path. The result value indicates if there + * was one found. + */ + +bool KadmosDialog::getSelClassifier( QString& path ) const +{ + QString classifier = getSelClassifierName(); + + QString cmplPath; + /* + * Search the complete path for the classifier file name + * returned from the getSelClassifierName method + */ + for ( QStringList::ConstIterator it = m_classifierPath.begin(); + it != m_classifierPath.end(); ++it ) + { + QFileInfo fi( *it ); + if( fi.fileName() == classifier ) + { + cmplPath = *it; + break; + } + } + + bool res = true; + + if( cmplPath.isEmpty() ) + { + /* hm, no path was found */ + kdDebug(28000) << "ERR; The entire path is empty, joking?" << endl; + res = false; + } + else + { + /* Check if the classifier exists on the HD. If not, return an empty string */ + QFileInfo fi(cmplPath); + + if( res && ! fi.exists() ) + { + kdDebug(28000) << "Classifier file does not exist" << endl; + path = i18n("Classifier file %1 does not exist").arg(classifier); + res = false; + } + + if( res && ! fi.isReadable() ) + { + kdDebug(28000) << "Classifier file could not be read" << endl; + path = i18n("Classifier file %1 is not readable").arg(classifier); + res = false; + } + + if( res ) + path = cmplPath; + } + return res; +} + +QString KadmosDialog::getSelClassifierName() const +{ + QButton *butt = m_bbFont->selected(); + + QString fType, rType; + + if( butt ) + { + int fontTypeID = m_bbFont->id(butt); + if( fontTypeID == 0 ) + fType = "ttf"; + else if( fontTypeID == 1 ) + fType = "hand"; + else if( fontTypeID == 2 ) + fType = "norm"; + else + kdDebug(28000) << "ERR: Wrong Font Type ID" << endl; + } + + /* Get the long text from the combo box */ + QString selLang = m_cbLang->currentText(); + QString trans; + if( fType != "norm" && m_longCountry2short.contains( selLang )) + { + QString langType = m_longCountry2short[selLang]; + trans = fType+langType+".rec"; + } + else + { + if( selLang.endsWith( ".rec" )) + { + /* can be a undetected */ + trans = selLang; + } + else if( fType == "norm" ) + { + trans = "norm.rec"; + } + else + kdDebug(28000) << "ERROR: Not a valid classifier" << endl; + } + kdDebug(28000) << "Returning trans. "<< trans << endl; + return( trans ); +} + +bool KadmosDialog::getAutoScale() +{ + return( m_cbAutoscale ? m_cbAutoscale->isChecked() : false ); +} + +bool KadmosDialog::getNoiseReduction() +{ + return( m_cbNoise ? m_cbNoise->isChecked() : false ); + +} + +KadmosDialog::~KadmosDialog() +{ + +} + +void KadmosDialog::writeConfig( void ) +{ + +} + + +void KadmosDialog::enableFields( bool state ) +{ + kdDebug(28000) << "About to disable the entry fields" << endl; + m_cbNoise->setEnabled( state ); + m_cbAutoscale->setEnabled( state ); + + m_bbFont->setEnabled( state ); + m_gbLang->setEnabled( state ); +} + + +/* The End ;) */ + diff --git a/kooka/kocrkadmos.h b/kooka/kocrkadmos.h new file mode 100644 index 00000000..38247cc9 --- /dev/null +++ b/kooka/kocrkadmos.h @@ -0,0 +1,128 @@ +/*************************************************************************** + kocrkadmos.h - ocr dialog for KADMOS ocr engine + ------------------- + begin : Sun Jun 11 2000 + copyright : (C) 2000 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + + +#ifndef KOCRKADMOS_H +#define KOCRKADMOS_H + +#include <kdialogbase.h> +#include <qmap.h> + +#include "kocrbase.h" +/** + *@author Klaas Freitag + */ + + + +class KScanCombo; +class QWidget; +class QButtonGroup; +class KConfig; +class QCheckBox; +class KSpellConfig; +class QRadioButton; + +class KadmosClassifier /* Not yet used FIXME */ +{ +public: + KadmosClassifier( QString lang, QString filename ); + QString getCmplFilename() const { return path+filename; } + QString getFilename() const { return filename; } + QString language() const { return languagesName; } + + void setPath( const QString& p ) { path=p; } +private: + + QString filename; + QString path; + QString languagesName; +}; + + +class KadmosDialog: public KOCRBase +{ + Q_OBJECT +public: + KadmosDialog( QWidget *, KSpellConfig *spellConfig ); + ~KadmosDialog(); + + typedef QMap<QString, QString> StrMap; + + EngineError setupGui(); + bool getAutoScale(); + bool getNoiseReduction(); + bool getSelClassifier(QString&) const; + QString getSelClassifierName() const; + + QString ocrEngineName() const; + QString ocrEngineDesc() const; + QString ocrEngineLogo() const; + +public slots: + void enableFields(bool); + +protected: + void writeConfig(); + + void setupPreprocessing( QVBox *box ); + void setupSegmentation( QVBox *box ); + void setupClassification( QVBox *box ); + + EngineError findClassifiers(); + EngineError findClassifierPath(); +private slots: + + void slFontChanged( int id ); + +private: + StrMap m_classifierTranslate; + + QCheckBox *m_cbNoise; + QCheckBox *m_cbAutoscale; + QString m_customClassifierPath; + + QButtonGroup *m_bbFont; + + QRadioButton *m_rbMachine; + QRadioButton *m_rbHand; + QRadioButton *m_rbNorm; + + QGroupBox *m_gbLang; + + QComboBox *m_cbLang; + + QStringList m_ttfClassifier; + QStringList m_handClassifier; + QStringList m_classifierPath; + + bool m_haveNorm; + + typedef QMap<QString, QString> StringMap; + StringMap m_longCountry2short; +}; + +#endif diff --git a/kooka/kocrocrad.cpp b/kooka/kocrocrad.cpp new file mode 100644 index 00000000..1ce94f65 --- /dev/null +++ b/kooka/kocrocrad.cpp @@ -0,0 +1,263 @@ +/*************************************************************************** + kocrocrad.cpp - ocrad dialog + ------------------- + begin : Tue Jul 15 2003 + copyright : (C) 2003 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +/* $Id$ */ + +#include <qlayout.h> +#include <qlabel.h> +#include <qfileinfo.h> +#include <qtooltip.h> +#include <qregexp.h> + +#include <kapplication.h> +#include <kconfig.h> +#include <kglobal.h> +#include <kdebug.h> +#include <klocale.h> +#include <kanimwidget.h> +#include <kseparator.h> +#include <kmessagebox.h> +#include <kurlrequester.h> +#include <kprocess.h> + +#include "resource.h" +#include "kocrocrad.h" +#include <kscanslider.h> +#include "kookaimage.h" +#include "kookapref.h" +#include <qvbox.h> +#include <qhbox.h> +#include <qcombobox.h> + + + +ocradDialog::ocradDialog( QWidget *parent, KSpellConfig *spellConfig ) + :KOCRBase( parent, spellConfig, KDialogBase::Tabbed ), + m_ocrCmd( QString()), + m_orfUrlRequester(0L), + m_layoutMode(0), + m_binaryLabel(0), + m_proc(0), + m_version(0) +{ + kdDebug(28000) << "Starting ocrad-Start-Dialog!" << endl; + // Layout-Boxes +} + +QString ocradDialog::ocrEngineLogo() const +{ + return "ocrad.png"; +} + +QString ocradDialog::ocrEngineName() const +{ + return i18n("ocrad" ); +} + +QString ocradDialog::ocrEngineDesc() const +{ + return i18n("ocrad is a Free Software project " + "for optical character recognition.<p>" + "The author of ocrad is <b>Antonio Diaz</b><br>" + "For more information about ocrad see " + "<A HREF=\"http://www.gnu.org/software/ocrad/ocrad.html\">" + "http://www.gnu.org/software/ocrad/ocrad.html</A><p>" + "Images should be scanned in black/white mode for ocrad.<br>" + "Best results are achieved if the characters are at least 20 pixels high.<p>" + "Problems arise, as usual, with very bold or very light or broken characters, " + "the same with merged character groups."); +} + + +int ocradDialog::layoutDetectionMode() const +{ + return m_layoutMode->currentItem(); +} + +EngineError ocradDialog::setupGui() +{ + KOCRBase::setupGui(); + + QVBox *page = ocrPage(); + Q_CHECK_PTR( page ); + + KConfig *conf = KGlobal::config (); + conf->setGroup( CFG_GROUP_OCR_DIA ); + + // Horizontal line + // (void) new KSeparator( KSeparator::HLine, page); + + // Entry-Field. + QString res = conf->readPathEntry( CFG_OCRAD_BINARY, "notFound" ); + if( res == "notFound" ) + { + res = KookaPreferences::tryFindBinary("ocrad", CFG_OCRAD_BINARY); + if( res.isEmpty() ) + { + /* Popup here telling that the config needs to be called */ + KMessageBox::sorry( this, i18n( "The path to the ocrad binary is not configured yet.\n" + "Please go to the Kooka configuration and enter the path manually."), + i18n("OCR Software Not Found") ); + } + } + + if( res.isEmpty() ) + res = i18n("Not found"); + else + m_ocrCmd = res; + + /** layout detection button **/ + conf->setGroup( CFG_GROUP_OCRAD ); + int layoutDetect = conf->readNumEntry( CFG_OCRAD_LAYOUT_DETECTION, 0 ); + kdDebug(28000) << "Layout detection from config: " << layoutDetect << endl; + + (void) new KSeparator( KSeparator::HLine, page); + QHBox *hb1 = new QHBox(page); + hb1->setSpacing( KDialog::spacingHint() ); + (void) new QLabel( i18n("OCRAD layout analysis mode: "), hb1); + m_layoutMode = new QComboBox(hb1); + m_layoutMode->insertItem(i18n("No Layout Detection"), 0 ); + m_layoutMode->insertItem(i18n("Column Detection"), 1 ); + m_layoutMode->insertItem(i18n("Full Layout Detection"), 2); + m_layoutMode->setCurrentItem(layoutDetect); + + /** stating the ocrad binary **/ + (void) new KSeparator( KSeparator::HLine, page); + QHBox *hb = new QHBox(page); + hb->setSpacing( KDialog::spacingHint()); + + m_binaryLabel = new QLabel( i18n("Using ocrad binary: ") + res, hb ); + + // retrieve Program version and display + version(res); + + getAnimation(hb); + + /* This is for a 'work-in-progress'-Animation */ + + return ENG_OK; +} + +void ocradDialog::introduceImage( KookaImage *img ) +{ + if( !img ) return; + + KOCRBase::introduceImage( img ); +} + + +ocradDialog::~ocradDialog() +{ + if( m_proc ) + delete m_proc; +} + +void ocradDialog::writeConfig( void ) +{ + KConfig *conf = KGlobal::config (); + conf->setGroup( CFG_GROUP_OCR_DIA ); + + conf->writeEntry( CFG_OCRAD_BINARY, QString(getOCRCmd())); + + conf->setGroup( CFG_GROUP_OCRAD ); + conf->writeEntry( CFG_OCRAD_LAYOUT_DETECTION, m_layoutMode->currentItem()); +} + + +void ocradDialog::enableFields(bool ) +{ + kdDebug(28000) << "About to disable the entry fields" << endl; +} + +/* Later: Allow interactive loading of orf files + * for now, return emty string + */ +QString ocradDialog::orfUrl() const +{ + if( m_orfUrlRequester ) + return m_orfUrlRequester->url(); + else + return QString(); +} + +void ocradDialog::version( const QString& exe ) +{ + if( m_proc ) delete m_proc; + + m_proc = new KProcess; + + kdDebug(28000) << "Using " << exe << " as command" << endl; + *m_proc << exe; + *m_proc << QString("-V"); + + connect( m_proc, SIGNAL(receivedStdout(KProcess *, char *, int )), + this, SLOT(slReceiveStdIn(KProcess *, char *, int ))); + + if( ! m_proc->start( KProcess::NotifyOnExit, KProcess::Stdout ) ) + { + slReceiveStdIn( 0, (char*) "unknown", 7 ); + } +} + +void ocradDialog::slReceiveStdIn( KProcess*, char *buffer, int buflen) +{ + QString vstr = QString::fromUtf8(buffer, buflen); + + kdDebug(28000) << "Got input: "<< buffer << endl; + + QRegExp rx; + rx.setPattern("GNU Ocrad version ([\\d\\.]+)"); + if( rx.search( vstr ) > -1 ) + { + QString vStr = rx.cap(1); + vStr.remove(0,2); + + m_version = vStr.toInt(); + QString v = i18n("Version: ") + rx.cap(1); + + if( m_binaryLabel ) + { + m_binaryLabel->setText(m_binaryLabel->text() + "\n" + v ); + m_binaryLabel->update(); + } + } +} + +/* + * returns the numeric version of the ocrad program. It is queried in the slot + * slReceiveStdIn, which parses the output of the ocrad -V call. + * + * Attention: This method returns 10 for ocrad v. 0.10 and 8 for ocrad-0.8 + */ +int ocradDialog::getNumVersion() +{ + return m_version; +} + +#include "kocrocrad.moc" + +/* The End ;) */ + diff --git a/kooka/kocrocrad.h b/kooka/kocrocrad.h new file mode 100644 index 00000000..d268f403 --- /dev/null +++ b/kooka/kocrocrad.h @@ -0,0 +1,106 @@ +/*************************************************************************** + kocrocrad.h - ocr dialog for ocrad + ------------------- + begin : Tue Jul 15 2003 + copyright : (C) 2003 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + + +#ifndef KOCROCRAD_H +#define KOCROCRAD_H + +#include <kdialogbase.h> +#include <qimage.h> +#include <qstring.h> + +#include <kscanslider.h> +#include <kanimwidget.h> + +#include "kocrbase.h" + +#define CFG_GROUP_OCRAD "ocrad" +#define CFG_OCRAD_LAYOUT_DETECTION "layoutDetection" +#define CFG_OCRAD_EXTRA_ARGUMENTS "extraArguments" +#define CFG_OCRAD_FORMAT "format" +#define CFG_OCRAD_CHARSET "charset" +/** + *@author Klaas Freitag + */ + +class KSpellConfig; +class KURLRequester; +class KProcess; +class QLabel; +class QComboBox; + +class ocradDialog: public KOCRBase +{ + Q_OBJECT +public: + ocradDialog( QWidget*, KSpellConfig* ); + ~ocradDialog(); + + QString getOCRCmd( void ) const + { return m_ocrCmd;} + + EngineError setupGui(); + + QString ocrEngineName() const; + QString ocrEngineDesc() const; + QString ocrEngineLogo() const; + + QString orfUrl() const; + + int layoutDetectionMode() const; + + /** + * returns the numeric version of the ocrad program. + * + * Attention: This method returns 10 for ocrad v. 0.10 and 8 for ocrad-0.8 + */ + int getNumVersion(); + +public slots: + void enableFields(bool); + void introduceImage( KookaImage* ); + +protected: + void writeConfig(); + + +private: + void version( const QString& exe ); + +private slots: + void slReceiveStdIn( KProcess *proc, char *buffer, int buflen); + +private: + + QString m_ocrCmd; + KURLRequester *m_orfUrlRequester; + QComboBox *m_layoutMode; + QLabel *m_binaryLabel; + KProcess *m_proc; + int m_version; +}; + +#endif diff --git a/kooka/kooka.cpp b/kooka/kooka.cpp new file mode 100644 index 00000000..3e3d660d --- /dev/null +++ b/kooka/kooka.cpp @@ -0,0 +1,465 @@ +/************************************************************************** + kooka.cpp - Main program class + ------------------- + begin : Sun Jan 16 2000 + copyright : (C) 2000 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ +#include "kooka.h" +#include "kookaview.h" +#include "resource.h" + +#include "kookapref.h" +#include "imgprintdialog.h" + +#include <qlineedit.h> +#include <qprinter.h> +#include <qprintdialog.h> +#include <qpainter.h> +#include <qpaintdevicemetrics.h> + +#include <kglobal.h> +#include <klocale.h> +#include <kdebug.h> +#include <kiconloader.h> +#include <kmenubar.h> +#include <kaccel.h> +#include <kio/netaccess.h> +#include <kfiledialog.h> +#include <kconfig.h> +#include <kprinter.h> +#include <kstatusbar.h> +#include <kurl.h> +#include <kurlrequesterdlg.h> +#include <qstrlist.h> +#include <kedittoolbar.h> +#include <kmessagebox.h> +#include <kdockwidget.h> +#include <kparts/partmanager.h> +#include <kstdaccel.h> +#include <kaction.h> +#include <kstdaction.h> +#include <qiconset.h> +#include <kurldrag.h> + +#define DOCK_SIZES "DockSizes" + + +Kooka::Kooka( const QCString& deviceToUse) + : KParts::DockMainWindow( 0, "Kooka" ), + m_printer(0), + m_prefDialogIndex(0) +{ + /* Start to create the main view framework */ + m_view = new KookaView( this, deviceToUse); + + /* Call createGUI on the ocr-result view */ + setXMLFile( "kookaui.rc", true ); + + setAcceptDrops(false); // Waba: Not (yet?) supported + KConfig *konf = KGlobal::config (); + readDockConfig ( konf, DOCK_SIZES ); + + // then, setup our actions + setupActions(); + + createGUI(0L); // m_view->ocrResultPart()); + // and a status bar + statusBar()->insertItem( QString(), KookaView::StatusTemp ); + statusBar()->show(); + + // allow the view to change the statusbar and caption + connect(m_view, SIGNAL(signalChangeStatusbar(const QString&)), + this, SLOT(changeStatusbar(const QString&))); + connect(m_view, SIGNAL(signalCleanStatusbar(void)), + this, SLOT(cleanStatusbar())); + connect(m_view, SIGNAL(signalChangeCaption(const QString&)), + this, SLOT(changeCaption(const QString&))); + + changeCaption( i18n( "KDE Scanning" )); + + setAutoSaveSettings( QString::fromLatin1("General Options"), + true ); +} + +void Kooka::createMyGUI( KParts::Part *part ) +{ + kdDebug(28000) << "Part changed, Creating gui" << endl; + createGUI(part); + +} + +Kooka::~Kooka() +{ + KConfig *konf = KGlobal::config (); + m_view->slCloseScanDevice(); + writeDockConfig ( konf, DOCK_SIZES ); + delete m_printer; +} + +void Kooka::startup( void ) +{ + kdDebug(29000) << "Starting startup !" << endl; + if( m_view ) m_view->loadStartupImage(); +} + + +void Kooka::setupActions() +{ + + KStdAction::print(this, SLOT(filePrint()), actionCollection()); + KStdAction::quit(this , SLOT(close()), actionCollection()); + + KStdAction::keyBindings(guiFactory(), SLOT(configureShortcuts()), +actionCollection()); + KStdAction::configureToolbars(this, SLOT(optionsConfigureToolbars()), + actionCollection()); + KStdAction::preferences(this, SLOT(optionsPreferences()), actionCollection()); + + m_view->createDockMenu(actionCollection(), this, "settings_show_docks" ); + + /* Image Viewer action Toolbar - OCR, Scaling etc. */ + (void) new KAction(i18n("&OCR Image..."), "ocr", CTRL+Key_O, + m_view, SLOT(doOCR()), + actionCollection(), "ocrImage" ); + + (void) new KAction(i18n("O&CR on Selection..."), "ocr-select", CTRL+Key_C, + m_view, SLOT(doOCRonSelection()), + actionCollection(), "ocrImageSelect" ); + + KAction *act; + act = new KAction(i18n("Scale to W&idth"), "scaletowidth", CTRL+Key_I, + m_view, SLOT( slIVScaleToWidth()), + actionCollection(), "scaleToWidth" ); + m_view->connectViewerAction( act ); + + act = new KAction(i18n("Scale to &Height"), "scaletoheight", CTRL+Key_H, + m_view, SLOT( slIVScaleToHeight()), + actionCollection(), "scaleToHeight" ); + m_view->connectViewerAction( act ); + + act = new KAction(i18n("Original &Size"), "scaleorig", CTRL+Key_S, + m_view, SLOT( slIVScaleOriginal()), + actionCollection(), "scaleOriginal" ); + m_view->connectViewerAction( act ); + +#ifdef QICONSET_HONOUR_ON_OFF + /* The Toggleaction does not seem to handle the on/off icon from QIconSet */ + QIconSet lockSet; + lockSet.setPixmap(BarIcon("lock") , QIconSet::Automatic, QIconSet::Normal, QIconSet::On ); + lockSet.setPixmap(BarIcon("unlock"), QIconSet::Automatic, QIconSet::Normal, QIconSet::Off); + act = new KToggleAction ( i18n("Keep &Zoom Setting"), lockSet, CTRL+Key_Z, + actionCollection(), "keepZoom" ); +#else + act = new KToggleAction( i18n("Keep &Zoom Setting"), BarIcon("lockzoom"), CTRL+Key_Z, + actionCollection(), "keepZoom" ); +#endif + + connect( act, SIGNAL( toggled( bool ) ), m_view->getImageViewer(), + SLOT(setKeepZoom(bool))); + + m_view->connectViewerAction( act ); + + /* thumbview and gallery actions */ + act = new KAction(i18n("Set Zoom..."), "viewmag", 0, + m_view, SLOT( slIVShowZoomDialog()), + actionCollection(), "showZoomDialog" ); + m_view->connectViewerAction( act ); + + (void) new KAction(i18n("Create From Selectio&n"), "crop", CTRL+Key_N, + m_view, SLOT( slCreateNewImgFromSelection() ), + actionCollection(), "createFromSelection" ); + + (void) new KAction(i18n("Mirror Image &Vertically"), "mirror-vert", CTRL+Key_V, + this, SLOT( slMirrorVertical() ), + actionCollection(), "mirrorVertical" ); + + (void) new KAction(i18n("&Mirror Image Horizontally"), "mirror-horiz", CTRL+Key_M, + this, SLOT( slMirrorHorizontal() ), + actionCollection(), "mirrorHorizontal" ); + + (void) new KAction(i18n("Mirror Image &Both Directions"), "mirror-both", CTRL+Key_B, + this, SLOT( slMirrorBoth() ), + actionCollection(), "mirrorBoth" ); + + (void) new KAction(i18n("Open Image in &Graphic Application..."), "fileopen", CTRL+Key_G, + m_view, SLOT( slOpenCurrInGraphApp() ), + actionCollection(), "openInGraphApp" ); + + act = new KAction(i18n("&Rotate Image Clockwise"), "rotate_cw", CTRL+Key_R, + this, SLOT( slRotateClockWise() ), + actionCollection(), "rotateClockwise" ); + m_view->connectViewerAction( act ); + + act = new KAction(i18n("Rotate Image Counter-Clock&wise"), "rotate_ccw", CTRL+Key_W, + this, SLOT( slRotateCounterClockWise() ), + actionCollection(), "rotateCounterClockwise" ); + m_view->connectViewerAction( act ); + + act = new KAction(i18n("Rotate Image 180 &Degrees"), "rotate", CTRL+Key_D, + this, SLOT( slRotate180() ), + actionCollection(), "upsitedown" ); + m_view->connectViewerAction( act ); + + /* Gallery actions */ + act = new KAction(i18n("&Create Folder..."), "folder_new", 0, + m_view->gallery(), SLOT( slotCreateFolder() ), + actionCollection(), "foldernew" ); + m_view->connectGalleryAction( act ); + + act = new KAction(i18n("&Save Image..."), "filesave", 0, + m_view->gallery(), SLOT( slotExportFile() ), + actionCollection(), "saveImage" ); + m_view->connectGalleryAction( act ); + + act = new KAction(i18n("&Import Image..."), "inline_image", 0, + m_view->gallery(), SLOT( slotImportFile() ), + actionCollection(), "importImage" ); + m_view->connectGalleryAction( act ); + + act = new KAction(i18n("&Delete Image"), "edittrash", 0, + m_view->gallery(), SLOT( slotDeleteItems() ), + actionCollection(), "deleteImage" ); + m_view->connectGalleryAction( act ); + + act = new KAction(i18n("&Unload Image"), "fileclose", 0, + m_view->gallery(), SLOT( slotUnloadItems() ), + actionCollection(), "unloadImage" ); + m_view->connectGalleryAction( act ); + +#if 0 + /* not yet supported actions - coming post 3.1 */ + (void) new KAction(i18n("&Load Scan Parameters"), "bookmark_add", CTRL+Key_L, + m_view, SLOT(slLoadScanParams()), + actionCollection(), "loadscanparam" ); + + (void) new KAction(i18n("Save &Scan Parameters"), "bookmark_add", CTRL+Key_S, + m_view, SLOT(slSaveScanParams()), + actionCollection(), "savescanparam" ); +#endif + + (void) new KAction(i18n("Select Scan Device"), "scanner", 0, + m_view, SLOT( slSelectDevice()), + actionCollection(), "selectsource" ); + + (void) new KAction( i18n("Enable All Warnings && Messages"), 0, + this, SLOT(slEnableWarnings()), + actionCollection(), "enable_msgs"); + + + m_saveOCRTextAction = new KAction( i18n("Save OCR Res&ult Text"), "filesaveas", CTRL+Key_U, + m_view, SLOT(slSaveOCRResult()), + actionCollection(), "saveOCRResult"); +} + + +void Kooka::saveProperties(KConfig *config) +{ + // the 'config' object points to the session managed + // config file. anything you write here will be available + // later when this app is restored + + //if (!m_view->currentURL().isNull()) + // config->writePathEntry("lastURL", m_view->currentURL()); + kdDebug(28000) << "In kooka's saveProperties !" << endl; + config->setGroup( KOOKA_STATE_GROUP ); + config->writeEntry( PREFERENCE_DIA_TAB, m_prefDialogIndex ); + m_view->saveProperties( config ); +} + +void Kooka::readProperties(KConfig *config) +{ + (void) config; + // the 'config' object points to the session managed + // config file. this function is automatically called whenever + // the app is being restored. read in here whatever you wrote + // in 'saveProperties' + config->setGroup( KOOKA_STATE_GROUP ); + m_prefDialogIndex = config->readNumEntry( PREFERENCE_DIA_TAB, 0 ); + // QString url = config->readPathEntry("lastURL"); + +} + +void Kooka::dragEnterEvent(QDragEnterEvent *event) +{ + // accept uri drops only + event->accept(KURLDrag::canDecode(event)); +} + +#if 0 +void Kooka::dropEvent(QDropEvent *event) +{ + // this is a very simplistic implementation of a drop event. we + // will only accept a dropped URL. the Qt dnd code can do *much* + // much more, so please read the docs there + KURL::List uri; + + // see if we can decode a URI.. if not, just ignore it + if (KURLDrag::decode(event, uri) && !uri.isEmpty()) + { + // okay, we have a URI.. process it + const KURL &url = uri.first(); + kdDebug(29000) << "Importing URI " << url.url() << endl; + + // TODO: Do something with url + // Waba: See also setAcceptDrops() above + } +} + +void Kooka::fileNew() +{ + // this slot is called whenever the File->New menu is selected, + // the New shortcut is pressed (usually CTRL+N) or the New toolbar + // button is clicked + + // create a new window + (new Kooka)->show(); +} + +void Kooka::fileOpen() +{ + // this slot is called whenever the File->Open menu is selected, + // the Open shortcut is pressed (usually CTRL+O) or the Open toolbar + // button is clicked +} + +void Kooka::fileSave() +{ + // this slot is called whenever the File->Save menu is selected, + // the Save shortcut is pressed (usually CTRL+S) or the Save toolbar + // button is clicked + + // save the current file +} + + +void Kooka::fileSaveAs() +{ + // this slot is called whenever the File->Save As menu is selected, + QStrList strlist; + strlist.append( "BMP" ); + strlist.append( "JPEG" ); + FormatDialog fd( 0, "FormatDialog", &strlist ); + fd.exec(); + +} +#endif + +void Kooka::filePrint() +{ + // this slot is called whenever the File->Print menu is selected, + // the Print shortcut is pressed (usually CTRL+P) or the Print toolbar + // button is clicked + m_view->print(); + +} + +void Kooka::optionsShowScanParams() +{ + m_view->slSetScanParamsVisible( m_scanParamsAction->isChecked() ); +} + +void Kooka::optionsShowPreviewer() +{ + m_view->slSetTabWVisible( m_previewerAction->isChecked()); +} + +void Kooka::optionsConfigureToolbars() +{ + // use the standard toolbar editor + saveMainWindowSettings(KGlobal::config(), autoSaveGroup()); + KEditToolbar dlg(factory()); + connect(&dlg, SIGNAL(newToolbarConfig()), SLOT(newToolbarConfig())); + dlg.exec(); +} + +void Kooka::newToolbarConfig() +{ + // OK/Apply pressed in the toolbar editor + applyMainWindowSettings(KGlobal::config(), autoSaveGroup()); +} + +void Kooka::optionsPreferences() +{ + // popup some sort of preference dialog, here + KookaPreferences dlg; + dlg.showPage( m_prefDialogIndex ); + connect( &dlg, SIGNAL( dataSaved() ), m_view, SLOT(slFreshUpThumbView())); + + if (dlg.exec()) + { + // redo your settings + m_prefDialogIndex = dlg.activePageIndex(); + // m_view->slFreshUpThumbView(); + } +} + +void Kooka::changeStatusbar(const QString& text) +{ + // display the text on the statusbar + statusBar()->changeItem( text, KookaView::StatusTemp ); +} + +void Kooka::changeCaption(const QString& text) +{ + // display the text on the caption + setCaption(text); +} + +void Kooka::slMirrorVertical( void ) +{ + m_view->slMirrorImage( KookaView::MirrorVertical ); +} + +void Kooka::slMirrorHorizontal( void ) +{ + m_view->slMirrorImage( KookaView::MirrorHorizontal ); +} + +void Kooka::slMirrorBoth( void ) +{ + m_view->slMirrorImage( KookaView::MirrorBoth ); +} + +void Kooka::slRotateClockWise( void ) +{ + m_view->slRotateImage( 90 ); +} + +void Kooka::slRotateCounterClockWise( void ) +{ + m_view->slRotateImage( -90 ); + +} + +void Kooka::slRotate180( void ) +{ + m_view->slRotateImage( 180 ); +} + +void Kooka::slEnableWarnings( ) +{ + KMessageBox::information (this, i18n("All messages and warnings will now be shown.")); + KMessageBox::enableAllMessages(); + kapp->config()->reparseConfiguration(); +} + +#include "kooka.moc" diff --git a/kooka/kooka.desktop b/kooka/kooka.desktop new file mode 100644 index 00000000..19cd3f35 --- /dev/null +++ b/kooka/kooka.desktop @@ -0,0 +1,78 @@ +[Desktop Entry] +Type=Application +Exec=kooka %i %m %U +Icon=scanner +Path= +Terminal=false +DocPath=kooka/index.html +GenericName=Scan & OCR Program +GenericName[af]=Skandeer & Optiese karakter herkenning Program +GenericName[ar]=برنامج Ù„Ù„Ù…Ø³Ø Ø§Ù„Ø¶ÙˆØ¦ÙŠ +GenericName[bg]=Сканиране +GenericName[bs]=Program za skeniranje i OCR +GenericName[ca]=Programa d'escaneig i OCR +GenericName[cs]=Program pro skenovánà a OCR +GenericName[cy]=Rhaglen Sganio ac OCR +GenericName[da]=Skanne- & OCR-program +GenericName[de]=Scan- und OCR-Programm +GenericName[el]=Î ÏόγÏαμμα ΣάÏωσης & OCR +GenericName[eo]=Bildbitiga programo kaj tekstrekono +GenericName[es]=OCR y explorador con un escáner +GenericName[et]=Skaneerimise ja OMT rakendus +GenericName[eu]=Eskaneatzeko eta OCR programa +GenericName[fa]=پویش Ùˆ برنامۀ OCR +GenericName[fi]=Skannaus- ja tekstintunnistusohjelma +GenericName[fr]=Numérisation et reconnaissance de caractères +GenericName[gl]=Programa para escanear e facer OCR +GenericName[he]=×ª×•×›× ×™×ª סריקה וזיהוי ×ª×•×•×™× ×ופטי +GenericName[hi]=सà¥à¤•à¥ˆà¤¨ व ऑपà¥à¤Ÿà¤¿à¤•à¤² कैरेकà¥à¤Ÿà¤° रिकॉगà¥à¤¨à¥€à¤¶à¤¨ पà¥à¤°à¥‹à¤—à¥à¤°à¤¾à¤® (OCR) +GenericName[hr]=Program za skaniranje i OCR +GenericName[hu]=Lapolvasó +GenericName[is]=Forrit til að skanna inn myndir +GenericName[it]=Programma di scansione e OCR +GenericName[ja]=スã‚ャン & OCR プãƒã‚°ãƒ©ãƒ +GenericName[kk]=Сканерге Ñ‚Ò¯Ñіру және танып-талдау +GenericName[km]=កម្មវិធី​ស្កáŸáž“ & OCR +GenericName[lt]=Skanavimo ir teksto atpažinimo programa +GenericName[lv]=SkanÄ“Å¡anas un OCR Programma +GenericName[ms]=Program Imbas & OCR +GenericName[nb]=Et skanne-og OCR-program +GenericName[nds]=Inlees- un OTR-Programm +GenericName[ne]=सà¥à¤•à¥à¤¯à¤¾à¤¨ र OCR कारà¥à¤¯à¤•à¥à¤°à¤® +GenericName[nl]=Scan- en OCR-programma +GenericName[nn]=Skanne- og tekstattkjenningsprogram +GenericName[pl]=Program do skanowania i rozpoznawania pisma +GenericName[pt]=Programa de Digitalização e OCR +GenericName[pt_BR]=Um programa de Digitalização & OCR +GenericName[ro]=Scanare imagini ÅŸi OCR +GenericName[ru]=Сканирование и раÑпознавание текÑта +GenericName[sk]=Skenovacà program s OCR +GenericName[sl]=Program za skeniranje in prepoznavanje znakov +GenericName[sr]=Програм за Ñкенирање и препознавање текÑта +GenericName[sr@Latn]=Program za skeniranje i prepoznavanje teksta +GenericName[sv]=Bildläsar- och OCR-program +GenericName[ta]=வரà¯à®Ÿà¯ & OCR நிரலி +GenericName[tg]=Барномаи Ñканеронӣ ва шиноÑоии матн +GenericName[th]=โปรà¹à¸à¸£à¸¡à¸ªà¹à¸à¸™à¸ าพà¹à¸¥à¸° OCR +GenericName[tr]=Tarayıcı ve karakter tanıma programı +GenericName[uk]=Програма ÑÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‚Ð° Ñ€Ð¾Ð·Ð¿Ñ–Ð·Ð½Ð°Ð²Ð°Ð½Ð½Ñ Ñимволів +GenericName[ven]=U nanga & Mbekanyamushumo ya OCR +GenericName[wa]=Programe di scanaedje eyet di ricnoxhance di tecse +GenericName[xh]=Udweliso Lwenkqubo Yemita Yovavanyo +GenericName[zh_CN]=扫æ和文å—è¯†åˆ«ç¨‹åº +GenericName[zh_HK]=掃æ和文å—辦è˜ç¨‹å¼ +GenericName[zh_TW]=掃æ和文å—辦è˜ç¨‹å¼ +GenericName[zu]=Scan & OCR Iprogremu +Name=Kooka +Name[ar]=برنامج Kooka +Name[eo]=Kokao +Name[hi]=कूका +Name[is]=Skanni +Name[ko]=ì¿ ì¹´ +Name[ne]=कोओका +Name[pa]=ਕੋਕਾ +Name[ta]=கூகà¯à®•à®¾ +Name[zh_TW]=Kooka 掃æ器 + +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Graphics; diff --git a/kooka/kooka.h b/kooka/kooka.h new file mode 100644 index 00000000..66f59e0c --- /dev/null +++ b/kooka/kooka.h @@ -0,0 +1,141 @@ +/************************************************************************** + kooka.h - Main program class + ------------------- + begin : Sun Jan 16 2000 + copyright : (C) 2000 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#ifndef KOOKA_H +#define KOOKA_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <kapplication.h> +#include <kmainwindow.h> +#include <kdockwidget.h> +#include <kparts/dockmainwindow.h> + +#define KOOKA_STATE_GROUP "State" +#define PREFERENCE_DIA_TAB "PreferencesTab" + +class KPrinter; +class KToggleAction; +class KActionMenu; +class KookaView; + +/** + * This class serves as the main window for Kooka. It handles the + * menus, toolbars, and status bars. + * + * @short Main window class + * @author Klaas Freitag <freitag@suse.de> + * @version 0.1 + */ +class Kooka : public KParts::DockMainWindow +{ + Q_OBJECT +public: + /** + * Default Constructor + */ + Kooka(const QCString& deviceToUse); + + /** + * Default Destructor + */ + ~Kooka(); + + /** + * Startup, loads (at the moment) only the last displayed image + **/ + void startup( void ); + + +protected: + /** + * Overridden virtuals for Qt drag 'n drop (XDND) + */ + virtual void dragEnterEvent(QDragEnterEvent *event); + // virtual void dropEvent(QDropEvent *event); + + /** + * This function is called when it is time for the app to save its + * properties for session management purposes. + */ + void saveProperties(KConfig *); + + /** + * This function is called when this app is restored. The KConfig + * object points to the session management config file that was saved + * with @ref saveProperties + */ + void readProperties(KConfig *); + + +private slots: + + void createMyGUI( KParts::Part* ); + + void filePrint(); + /* ImageViewer-Actions */ + + void optionsShowScanParams(); + void optionsShowPreviewer(); + void optionsConfigureToolbars(); + void optionsPreferences(); + + void changeStatusbar(const QString& text); + void cleanStatusbar(void) { changeStatusbar(""); } + void changeCaption(const QString& text); + void newToolbarConfig(); + + // void fileSaveAs(); + + void slMirrorVertical( void ); + void slMirrorHorizontal( void ); + void slMirrorBoth( void ); + + void slRotateClockWise( void ); + void slRotateCounterClockWise( void ); + void slRotate180( void ); + + void slEnableWarnings(); + +private: + void setupAccel(); + void setupActions(); + +private: + KookaView *m_view; + + KPrinter *m_printer; + KToggleAction *m_scanParamsAction; + KToggleAction *m_previewerAction; + KActionMenu *m_settingsShowDocks; + + KAction *m_saveOCRTextAction; + int m_prefDialogIndex; +}; + +#endif // KOOKA_H diff --git a/kooka/kookaiface.h b/kooka/kookaiface.h new file mode 100644 index 00000000..ab6fcbee --- /dev/null +++ b/kooka/kookaiface.h @@ -0,0 +1,13 @@ +#ifndef KOOKAIFACE_H +#define KOOKAIFACE_H + +#include <dcopobject.h> + +class KookaIface : virtual public DCOPObject +{ + K_DCOP +public: + +}; + +#endif // KOOKAIFACE_H diff --git a/kooka/kookaimage.cpp b/kooka/kookaimage.cpp new file mode 100644 index 00000000..4db87728 --- /dev/null +++ b/kooka/kookaimage.cpp @@ -0,0 +1,413 @@ +/*************************************************************************** + kookaimage.cpp - Kooka's Image + ------------------- + begin : Thu Nov 20 2001 + copyright : (C) 1999 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#include <kdebug.h> +#include <kurl.h> +#include <kfileitem.h> + +#include "kookaimage.h" +#include "config.h" +#ifdef HAVE_LIBTIFF +#include <tiffio.h> +#include <tiff.h> +#endif +/** + *@author Klaas Freitag + */ + + +KookaImage::KookaImage( ) + : QImage(), + m_subImages(-1), + m_subNo(0), + m_parent(0), + m_fileBound(false), + m_tileCols(0) +{ + +} + +/* constructor for subimages */ +KookaImage::KookaImage( int subNo, KookaImage *p ) + : QImage(), + m_subImages(-1), + m_subNo(subNo), + m_parent( p ), + m_fileItem(0L), + m_fileBound(false), + m_tileCols(0) +{ + kdDebug(28000) << "Setting subimageNo to " << subNo << endl; +} + +KookaImage& KookaImage::operator=(const KookaImage& img) +{ + QImage::operator=(img); + + m_subImages = img.subImagesCount(); + m_subNo = img.m_subNo; + m_parent = img.m_parent; + m_url = img.m_url; + m_fileItem = img.m_fileItem; + + return *this; +} + +KookaImage& KookaImage::operator=(const QImage& img) +{ + QImage::operator=(img); + return *this; +} + +KFileItem* KookaImage::fileItem() const +{ + return m_fileItem; +} + +void KookaImage::setFileItem( KFileItem* it ) +{ + m_fileItem = it; +} + +const KFileMetaInfo KookaImage::fileMetaInfo( ) +{ + QString filename = localFileName( ); + if( ! filename.isEmpty() ) + { + kdDebug(28000) << "Fetching metainfo for " << filename << endl; + const KFileMetaInfo info( filename ); + return info; + } + else + return KFileMetaInfo(); +} + +QString KookaImage::localFileName( ) const +{ + + if( ! m_url.isEmpty() ) + return( m_url.directory() + "/" + m_url.fileName()); + else + return QString(); +} + +bool KookaImage::loadFromUrl( const KURL& url ) +{ + bool ret = true; + m_url = url; + QString filename = localFileName( ); + QString format ( imageFormat( filename )); + + /* if the format was not recogniseable, check the extension, if it is tif, try to read it by + * tifflib */ + if( format.isNull() ) + { + if( filename.endsWith( "tif" ) || filename.endsWith( "tiff" ) || + filename.endsWith( "TIF" ) || filename.endsWith( "TIFF" ) ) + { + format = "tif"; + kdDebug(28000) << "Setting format to tif by extension" << endl; + } + } + + kdDebug(28000) << "Image format to load: <" << format << "> from file <" << filename << ">" << endl; + bool haveTiff = false; + + if( !m_url.isLocalFile() ) + { + kdDebug(28000)<<"ERROR: Can not laod non-local images -> not yet implemented!" << endl; + return false; + } + +#ifdef HAVE_LIBTIFF + TIFF* tif = 0; + m_subImages = 0; + + if( format == "tif" || + format == "TIF" || + format == "TIFF" || + format == "tiff" ) + { + /* if it is tiff, check with Tifflib if it is multiple sided */ + kdDebug(28000) << "Trying to load TIFF!" << endl; + tif = TIFFOpen(filename.latin1(), "r"); + if (tif) + { + do { + m_subImages++; + } while (TIFFReadDirectory(tif)); + kdDebug(28000) << m_subImages << " TIFF-directories found" << endl; + + haveTiff = true; + } + } +#endif + if( !haveTiff ) + { + /* Qt can only read one image */ + ret = load(filename); + if( ret ) + { + m_subImages = 0; + m_subNo = 0; + } + } +#ifdef HAVE_LIBTIFF + else + { + loadTiffDir( filename, 0); + /* its a tiff, read by tifflib directly */ + // Find the width and height of the image + } +#endif + + m_fileBound = ret; + return( ret ); +} + + +KookaImage::KookaImage( const QImage& img ) + : QImage( img ) + /* m_subImages( 1 ) */ +{ + m_subImages = 0; + + /* Load one QImage, can not be Tiff yet. */ + kdDebug(28000) << "constructor from other image here " << endl; +} + + +/* loads the number stored in m_subNo */ +void KookaImage::extractNow() +{ + kdDebug(28000) << "extracting a subimage number " << m_subNo << endl; + + KookaImage *parent = parentImage(); + + if( parent ) + { + loadTiffDir( parent->localFileName(), m_subNo ); + } + else + { + kdDebug(28000) << "ERR: No parent defined - can not laod subimage" << endl; + } +} + +KURL KookaImage::url() const +{ + return m_url; +} + +bool KookaImage::loadTiffDir( const QString& filename, int no ) +{ +#ifdef HAVE_LIBTIFF + int imgWidth, imgHeight; + TIFF* tif = 0; + /* if it is tiff, check with Tifflib if it is multiple sided */ + kdDebug(28000) << "Trying to load TIFF, subimage number "<< no << endl; + tif = TIFFOpen(filename.latin1(), "r"); + if (!tif) + return false; + + if( ! TIFFSetDirectory( tif, no ) ) + { + kdDebug(28000) << "ERR: could not set Directory " << no << endl; + TIFFClose(tif); + return false; + } + + TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &imgWidth); + TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &imgHeight); + + + /* TODO: load bw-image correctly only 2 bit */ + // KookaImage tmpImg; + create( imgWidth, imgHeight, 32 ); + if (TIFFReadRGBAImage(tif, imgWidth, imgHeight, (uint32*) bits(),0)) + { + // successfully read. now convert. + // reverse red and blue + uint32 *data; + data = (uint32 *)bits(); + for( unsigned i = 0; i < unsigned(imgWidth * imgHeight); ++i ) + { + uint32 red = ( 0x00FF0000 & data[i] ) >> 16; + uint32 blue = ( 0x000000FF & data[i] ) << 16; + data[i] &= 0xFF00FF00; + data[i] += red + blue; + } + + // reverse image (it's upside down) + unsigned h = unsigned(imgHeight); + for( unsigned ctr = 0; ctr < h>>1; ) + { + unsigned *line1 = (unsigned *)scanLine( ctr ); + unsigned *line2 = (unsigned *)scanLine( imgHeight + - ( ++ctr ) ); + + unsigned w = unsigned(imgWidth); + for( unsigned x = 0; x < w; x++ ) + { + int temp = *line1; + *line1 = *line2; + *line2 = temp; + line1++; + line2++; + } + } + } + + /* fetch the x- and y-resolutions to adjust images */ + float xReso, yReso; + bool resosFound; + resosFound = TIFFGetField(tif, TIFFTAG_XRESOLUTION, &xReso ); + resosFound &= TIFFGetField(tif, TIFFTAG_YRESOLUTION, &yReso ); + kdDebug(28000)<< "Tiff image: X-Resol.: " << xReso << " and Y-Resol.: " << yReso << endl; + + TIFFClose(tif); + + /* Check now if resolution in x- and y-direction differ. If so, stretch the image + * accordingly. + */ + if( resosFound && xReso != yReso ) + { + if( xReso > yReso ) + { + float yScalefactor = xReso / yReso; + kdDebug(28000) << "Different resolution x/y, rescaling with factor " << yScalefactor << endl; + /* rescale the image */ + *this = smoothScale( imgWidth, int(imgHeight*yScalefactor), QImage::ScaleFree ); + } + else + { + /* yReso > xReso */ + float scalefactor = yReso / xReso; + kdDebug(28000) << "Different resolution x/y, rescaling x with factor " << scalefactor << endl; + /* rescale the image */ + *this = smoothScale( int(imgWidth*scalefactor), imgHeight, QImage::ScaleFree ); + + } + } + +#endif + return true; +} + + +int KookaImage::subImagesCount() const +{ + return( m_subImages ); +} + +KookaImage::~KookaImage() +{ + +} + +KookaImage* KookaImage::parentImage() const +{ + return( m_parent ); +} + +bool KookaImage::isSubImage() const +{ + return( subImagesCount() ); +} + +/* + * tiling + */ +int KookaImage::cutToTiles( const QSize maxSize, int& rows, int& cols, TileMode ) +{ + QSize imgSize = size(); + + int w = imgSize.width(); + if( w > maxSize.width() ) + { + // image is wider than paper + w = maxSize.width(); + } + int h = imgSize.height(); + if( h > maxSize.height() ) + { + // image is wider than paper + h = maxSize.height(); + } + + int absX = 0; // absolute x position from where to start print + int absY = 0; // on the image, left top corner of the part to print + rows = 0; + + while( h ) // Loop over height, cut in vertical direction + { + rows++; + cols = 0; + while( w ) // Loop over width, cut in horizontal direction + { + cols++; + m_tileVector.append( QRect( absX, absY, w, h )); + + absX += w+1; + w = imgSize.width() - absX; + + // if w < 0, this was the last loop, set w to zero to stop loop + if( w < 0 ) w = 0; + + // if > 0 here, a new page is required + if( w > 0 ) + { + if( w > maxSize.width() ) w = maxSize.width(); + } + } + // Reset the X-values to start on the left border again + absX = 0; + // start with full width again + w = imgSize.width(); + if( w > maxSize.width() ) + w = maxSize.width(); + + absY += h+1; + h = imgSize.height() - absY; + + if( h < 0 ) h = 0; // be sure to meet the break condition + if( h > maxSize.height()) h = maxSize.height(); // limit to page height + } + m_tileCols = cols; + + return m_tileVector.count(); +} + + + +QRect KookaImage::getTileRect( int rowPos, int colPos ) const +{ + int indx = rowPos*m_tileCols+colPos; + kdDebug(28000) << "Tile Index: " << indx << endl; + const QRect r = m_tileVector[(rowPos)*m_tileCols + colPos]; + + return r; +} diff --git a/kooka/kookaimage.h b/kooka/kookaimage.h new file mode 100644 index 00000000..84018d4d --- /dev/null +++ b/kooka/kookaimage.h @@ -0,0 +1,170 @@ +/*************************************************************************** + kookaimage.h - Kooka's Image + ------------------- + begin : Thu Nov 20 2001 + copyright : (C) 1999 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + + + +#ifndef KOOKAIMAGE_H +#define KOOKAIMAGE_H +#include <kurl.h> +#include <qimage.h> +#include <qptrlist.h> +#include <qvaluevector.h> +#include <qrect.h> + +#include <kfilemetainfo.h> + +class KFileItem; + +/** + * @author Klaas Freitag + * + * class that represents an image, very much as QImage. But this one can contain + * multiple pages. + */ + +typedef enum { MaxCut, MediumCut } TileMode; + +class KookaImage: public QImage +{ +public: + + KookaImage( ); + /** + * creating a subimage for a parent image. + * @param subNo contains the sequence number of subimages to create. + * @param p is the parent image. + */ + KookaImage( int subNo, KookaImage *p ); + KookaImage( const QImage& img ); + + KookaImage& operator=(const KookaImage& ); + KookaImage& operator=(const QImage& ); + /** + * load an image from a KURL. This method reads the entire file and sets + * the values for subimage count. + */ + bool loadFromUrl( const KURL& ); + + ~KookaImage(); + + /** + * the amount of subimages. This is 0 if there are no subimages. + */ + int subImagesCount() const; + + /** + * the parent image. + */ + KookaImage* parentImage() const; + + /** + * returns true if this is a subimage. + */ + bool isSubImage() const; + + /** + * extracts the correct subimage according to the number given in the constructor. + */ + void extractNow(); + + KURL url() const; + QString localFileName( ) const; + + /** + * Set and get the KFileItem of the image. Note that the KFileItem pointer returned + * may be zero. + */ + KFileItem* fileItem() const; + void setFileItem( KFileItem* ); + + /** + * @return the KFileMetaInfo + **/ + const KFileMetaInfo fileMetaInfo( ); + + /** + * set the url of the kooka image. Note that loadFromUrl sets this + * url automatically. + */ + void setUrl( const KURL& url ) + { m_url = url; } + + /** + * checks if the image is file bound ie. was loaded from file. If this + * method returns false, fileMetaInfo and FileItem are undefined. + */ + bool isFileBound()const { return m_fileBound; } + + /** + * Create tiles on the given image. That is just cut the image in parts + * while non of the parts is larger than maxSize and store the rect list. + * The parameters rows and cols contain the number of rows and cols after + * tiling. If both are one, the image is smaller than maxSize, thus the + * left-top tile is index 1,1. + * Use getTile() to read the QRect list. + */ + int cutToTiles( const QSize maxSize, int& rows, int& cols, TileMode mode = MaxCut ); + + /** + * read tiles from the tile list. The image needs to be tiled by method + * cutToTiles before. + */ + QRect getTileRect( int rowPos, int colPos ) const; + + /** + * retrieve the sub number of this image. + */ + int subNumber() const { return m_subNo; } + +private: + int m_subImages; + bool loadTiffDir( const QString&, int ); + + /* if subNo is 0, the image is the one and only. If it is larger than 0, the + * parent contains the filename */ + int m_subNo; + + /* In case being a subimage */ + KookaImage *m_parent; + KURL m_url; + /* Fileitem if available */ + KFileItem *m_fileItem; + bool m_fileBound; + + QValueVector<QRect> m_tileVector; + int m_tileCols; /* number of tile columns */ +}; + + +class KookaImageList: public QPtrList<KookaImage> +{ +public: + KookaImageList() {} + ~KookaImageList() {} +}; + + +#endif diff --git a/kooka/kookaimagemeta.cpp b/kooka/kookaimagemeta.cpp new file mode 100644 index 00000000..7ba1963d --- /dev/null +++ b/kooka/kookaimagemeta.cpp @@ -0,0 +1,51 @@ +/*************************************************************************** + kookaimage.cpp - Kooka's Image + ------------------- + begin : Thu Nov 20 2001 + copyright : (C) 1999 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#include "kookaimagemeta.h" + +KookaImageMeta::KookaImageMeta( ) : + m_scanResolution(-1), + m_scanResolutionY(-1) +{ + +} + +void KookaImageMeta::setScanResolution( int x, int y) +{ + m_scanResolutionY = y; + m_scanResolution = x; + +} + +int KookaImageMeta::getScanResolutionX() const +{ + return m_scanResolution; +} + +int KookaImageMeta::getScanResolutionY() const +{ + return m_scanResolutionY; +} diff --git a/kooka/kookaimagemeta.h b/kooka/kookaimagemeta.h new file mode 100644 index 00000000..fd269ddd --- /dev/null +++ b/kooka/kookaimagemeta.h @@ -0,0 +1,54 @@ +/*************************************************************************** + kookaimagemeta.h - Kooka's Image Meta Data + ------------------- + begin : Thu Nov 20 2001 + copyright : (C) 1999 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#ifndef KOOKAIMAGEMETA_H +#define KOOKAIMAGEMETA_H + + +/** + * @author Klaas Freitag + * + */ + + +class KookaImageMeta +{ +public: + + KookaImageMeta( ); + ~KookaImageMeta() { ;} + + void setScanResolution( int x, int y=-1); + int getScanResolutionX() const; + int getScanResolutionY() const; + +private: + int m_scanResolution; + int m_scanResolutionY; + +}; + +#endif diff --git a/kooka/kookapref.cpp b/kooka/kookapref.cpp new file mode 100644 index 00000000..c5996275 --- /dev/null +++ b/kooka/kookapref.cpp @@ -0,0 +1,547 @@ +/*************************************************************************** + kookapref.cpp - Kookas preferences dialog + ------------------- + begin : Wed Jan 5 2000 + copyright : (C) 2000 by Klaas Freitag + email : + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + + +#include "kookapref.h" +#include "img_saver.h" + +#include <klocale.h> +#include <kiconloader.h> +#include <kconfig.h> +#include <kdebug.h> +#include <knuminput.h> +#include <kcolorbutton.h> +#include <kstandarddirs.h> + +#include <qlayout.h> +#include <qtooltip.h> +#include <qvgroupbox.h> +#include <qgrid.h> +#include <qcheckbox.h> +#include <qstringlist.h> + +#include <devselector.h> +#include "config.h" +#include "thumbview.h" +#include "imageselectline.h" +#include "kscanslider.h" +#include "ksaneocr.h" + +#include <kmessagebox.h> +#include <qbuttongroup.h> +#include <qradiobutton.h> +#include <kurlrequester.h> + +KookaPreferences::KookaPreferences() + : KDialogBase(IconList, i18n("Preferences"), + Help|Default|Ok|Apply|Cancel, Ok ) +{ + // this is the base class for your preferences dialog. it is now + // a Treelist dialog.. but there are a number of other + // possibilities (including Tab, Swallow, and just Plain) + konf = KGlobal::config (); + + setupStartupPage(); + setupSaveFormatPage(); + setupThumbnailPage(); + setupOCRPage(); +} + +void KookaPreferences::setupOCRPage() +{ + konf->setGroup( CFG_GROUP_OCR_DIA ); + + QFrame *page = addPage( i18n("OCR"), i18n("Optical Character Recognition" ), + BarIcon("ocrImage", KIcon::SizeMedium ) ); + + QVBoxLayout *top = new QVBoxLayout( page, 0, spacingHint() ); + + bool haveGocr = false; + bool haveOcrad = false; + bool haveKadmos = false; + + /* + * Switch ocr engines + */ + QButtonGroup *engGroup = new QButtonGroup( 1, Qt::Horizontal, i18n("OCR Engine to Use"), page ); + m_gocrBut = new QRadioButton( i18n("GOCR engine") , engGroup ); + m_kadmosBut = new QRadioButton( i18n("KADMOS engine"), engGroup ); + m_ocradBut = new QRadioButton( i18n("OCRAD engine"), engGroup ); + m_kadmosBut->setChecked(false); + m_gocrBut->setChecked(false); + m_ocradBut->setChecked(false); + top->addWidget( engGroup ); + + /* + * GOCR Option Box + */ + QVGroupBox *gp = new QVGroupBox( i18n("GOCR OCR"), page ); + m_urlReqGocr = binaryCheckBox( gp, "gocr" ); + connect( m_urlReqGocr, SIGNAL( textChanged( const QString& )), + this, SLOT( slCheckOnGOCR( const QString& ))); + QString cmdGocr = tryFindBinary( "gocr", CFG_GOCR_BINARY ); + kdDebug(28000) << "Found gocr command: " << cmdGocr << endl; + m_gocrBut->setEnabled(false); + if( !cmdGocr.isEmpty() ) + { + /* Found the command */ + m_urlReqGocr->setURL( cmdGocr ); + m_gocrBut->setEnabled(true); + haveGocr = true; + } + top->addWidget( gp ); + + /* + * OCRAD Option Box + */ + gp = new QVGroupBox( i18n("OCRAD OCR"), page ); + m_urlReqOcrad = binaryCheckBox( gp, "ocrad" ); + connect( m_urlReqOcrad, SIGNAL( textChanged( const QString& )), + this, SLOT( slCheckOnOCRAD( const QString& ))); + QString cmdOcrad = tryFindBinary( "ocrad", CFG_OCRAD_BINARY ); + kdDebug(28000) << "Found ocrad command: " << cmdOcrad << endl; + m_ocradBut->setEnabled(false); + if( !cmdOcrad.isEmpty() ) + { + /* Found the command */ + m_urlReqOcrad->setURL( cmdOcrad ); + m_ocradBut->setEnabled(true); + haveOcrad = true; + } + top->addWidget( gp ); + + /* + * Global Kadmos Options + */ + QVGroupBox *kgp = new QVGroupBox( i18n("KADMOS OCR"), page ); + +#ifdef HAVE_KADMOS + (void) new QLabel( i18n("The KADMOS OCR engine is available"), kgp); + m_kadmosBut->setChecked(true); + m_kadmosBut->setEnabled(true); + haveKadmos = true; +#else + (void) new QLabel( i18n("The KADMOS OCR engine is not available in this version of Kooka"), kgp ); + m_kadmosBut->setEnabled(false); +#endif + top->addWidget( kgp ); + QWidget *spaceEater = new QWidget( page ); + spaceEater->setSizePolicy( QSizePolicy( QSizePolicy::Ignored, QSizePolicy::Ignored )); + top->addWidget( spaceEater ); + + /* + * Now read the config value CFG_OCR_ENGINE and set the radios to the value if available + */ + QString useEngine = konf->readEntry( CFG_OCR_ENGINE, "ocrad" ); + if( useEngine != "notFound" ) + { + if( useEngine == "gocr" && haveGocr ) + { + m_gocrBut->setChecked(true); + m_prevOCREngine = "gocr"; + } + else if( useEngine == "ocrad" && haveOcrad ) + { + m_ocradBut->setChecked(true); + m_prevOCREngine = "ocrad"; + } + else if( useEngine == "kadmos" && haveKadmos ) + { + m_kadmosBut->setChecked(true); + m_prevOCREngine = "kadmos"; + } + } +} + +KURLRequester* KookaPreferences::binaryCheckBox( QWidget *parent, const QString& program ) +{ + QHBox *hbox = new QHBox( parent ); + + (void) new QLabel( i18n("Select the %1 binary to use:").arg( program ), hbox ); + KURLRequester* urlRequester = new KURLRequester( parent ); + urlRequester->setMode( KFile::File | KFile::ExistingOnly | KFile::LocalOnly ); + + QToolTip::add( urlRequester, + i18n( "Enter the path to %1, the optical-character-recognition " + "command line tool.").arg(program)); + return urlRequester; +} + + +QString KookaPreferences::tryFindGocr( void ) +{ + return( tryFindBinary( "gocr", CFG_GOCR_BINARY ) ); +} + +QString KookaPreferences::tryFindBinary( const QString& bin, const QString& configKey ) +{ + + /* First check the config files for an entry */ + KConfig *cfg = KGlobal::config(); + cfg->setGroup(CFG_GROUP_OCR_DIA); + QString res = cfg->readPathEntry( configKey /* CFG_GOCR_BINARY */, "notFound" ); + + if( res != "notFound" ) + { + QFileInfo fi( res ); + if( fi.exists() && fi.isExecutable() && !fi.isDir() && res.contains(bin) ) + { + return res; + } + } + + res = QString(); + + QStringList locations; + locations.append( "/usr/bin/" + bin ); + locations.append( "/bin/" + bin ); + locations.append( "/usr/X11R6/bin/"+bin ); + locations.append( "/usr/local/bin/"+bin ); + + for ( QStringList::Iterator it = locations.begin(); it != locations.end(); ++it ) + { + QString cmd = *it; + kdDebug(28000) << "checking command " << cmd << endl; + QFileInfo fi( cmd ); + if( fi.exists() && fi.isExecutable() && !fi.isDir()) + { + res = cmd; + kdDebug(28000) << "found command " << res << endl; + break; + } + } + + return( res ); +} + + +void KookaPreferences::slCheckOnGOCR( const QString& cmd ) +{ + if( checkOCRBinIntern( cmd, "gocr", false )) + { + // cmd exists and is executable + m_gocrBut->setEnabled( true ); + } + else + { + m_gocrBut->setEnabled( false ); + } +} + +void KookaPreferences::slCheckOnOCRAD( const QString& cmd ) +{ + if( checkOCRBinIntern( cmd, "ocrad", false )) + { + // cmd exists and is executable + m_ocradBut->setEnabled( true ); + } + else + { + m_ocradBut->setEnabled( false ); + } +} + +#if 0 +void KookaPreferences::checkOCRBinarySilent( const QString& cmd ) +{ + // checkOCRBinIntern( cmd, this->sender(), false); +} +#endif +bool KookaPreferences::checkOCRBinIntern( const QString& cmd, const QString& tool, bool show_msg ) +{ + if( ! cmd.contains( tool )) return false; + + bool ret = true; + QFileInfo fi( cmd ); + if( ! fi.exists() ) + { + if( show_msg ) + KMessageBox::sorry( this, i18n( "The path does not lead to a valid binary.\n" + "Please check your installation and/or install the program."), + i18n("OCR Software Not Found") ); + ret = false; + } + else + { + /* File exists, check if not dir and executable */ + if( fi.isDir() || (! fi.isExecutable()) ) + { + if( show_msg ) + KMessageBox::sorry( this, i18n( "The program exists, but is not executable.\n" + "Please check your installation and/or install the binary properly."), + i18n("OCR Software Not Executable") ); + ret = false; + } + } + + return ret; +} + + + +void KookaPreferences::setupStartupPage() +{ + + /* startup options */ + konf->setGroup( GROUP_STARTUP ); + + QFrame *page = addPage( i18n("Startup"), i18n("Kooka Startup Preferences" ), + BarIcon("gear", KIcon::SizeMedium ) ); + QVBoxLayout *top = new QVBoxLayout( page, 0, spacingHint() ); + /* Description-Label */ + top->addWidget( new QLabel( i18n("Note that changing these options will affect Kooka's next start!"), page )); + + /* Query for network scanner (Checkbox) */ + cbNetQuery = new QCheckBox( i18n("Query network for available scanners"), + page, "CB_NET_QUERY" ); + QToolTip::add( cbNetQuery, + i18n( "Check this if you want a network query for available scanners.\nNote that this does not mean a query over the entire network but only the stations configured for SANE!" )); + cbNetQuery->setChecked( ! (konf->readBoolEntry( STARTUP_ONLY_LOCAL, false )) ); + + + /* Show scanner selection box on startup (Checkbox) */ + cbShowScannerSelection = new QCheckBox( i18n("Show the scanner selection box on next startup"), + page, "CB_SHOW_SELECTION" ); + QToolTip::add( cbShowScannerSelection, + i18n( "Check this if you once checked 'do not show the scanner selection on startup',\nbut you want to see it again." )); + + cbShowScannerSelection->setChecked( !konf->readBoolEntry( STARTUP_SKIP_ASK, false )); + + /* Read startup image on startup (Checkbox) */ + cbReadStartupImage = new QCheckBox( i18n("Load the last image into the viewer on startup"), + page, "CB_LOAD_ON_START" ); + QToolTip::add( cbReadStartupImage, + i18n( "Check this if you want Kooka to load the last selected image into the viewer on startup.\nIf your images are large, that might slow down Kooka's start." )); + cbReadStartupImage->setChecked( konf->readBoolEntry( STARTUP_READ_IMAGE, true)); + + /* -- */ + + top->addWidget( cbNetQuery ); + top->addWidget( cbShowScannerSelection ); + top->addWidget( cbReadStartupImage ); + + top->addStretch(10); + +} + +void KookaPreferences::setupSaveFormatPage( ) +{ + konf->setGroup( OP_FILE_GROUP ); + QFrame *page = addPage( i18n("Image Saving"), i18n("Configure Image Save Assistant" ), + BarIcon("filesave", KIcon::SizeMedium ) ); + QVBoxLayout *top = new QVBoxLayout( page, 0, spacingHint() ); + + /* Skip the format asking if a format entry exists */ + cbSkipFormatAsk = new QCheckBox( i18n("Always display image save assistant"), + page, "CB_IMGASSIST_QUERY" ); + cbSkipFormatAsk->setChecked( konf->readBoolEntry( OP_FILE_ASK_FORMAT, true )); + QToolTip::add( cbSkipFormatAsk, i18n("Check this if you want to see the image save assistant even if there is a default format for the image type." )); + top->addWidget( cbSkipFormatAsk ); + + cbFilenameAsk = new QCheckBox( i18n("Ask for filename when saving file"), + page, "CB_ASK_FILENAME" ); + cbFilenameAsk->setChecked( konf->readBoolEntry( OP_ASK_FILENAME, false)); + QToolTip::add( cbFilenameAsk, i18n("Check this if you want to enter a filename when an image has been scanned." )); + top->addWidget( cbFilenameAsk ); + + + + top->addStretch(10); +} + +void KookaPreferences::setupThumbnailPage() +{ + konf->setGroup( THUMB_GROUP ); + + QFrame *page = addPage( i18n("Thumbnail View"), i18n("Thumbnail Gallery View" ), + BarIcon("thumbnail", KIcon::SizeMedium ) ); + QVBoxLayout *top = new QVBoxLayout( page, 0, spacingHint() ); + + top->addWidget( new QLabel( i18n("Here you can configure the appearance of the thumbnail view of your scan picture gallery."),page )); + + /* Backgroundimage */ + KStandardDirs stdDir; + QString bgImg = konf->readPathEntry( BG_WALLPAPER ); + if( bgImg.isEmpty() ) + bgImg = stdDir.findResource( "data", STD_TILE_IMG ); + + /* image file selector */ + QVGroupBox *hgb1 = new QVGroupBox( i18n("Thumbview Background" ), page ); + m_tileSelector = new ImageSelectLine( hgb1, i18n("Select background image:")); + kdDebug(28000) << "Setting tile url " << bgImg << endl; + m_tileSelector->setURL( KURL(bgImg) ); + + top->addWidget( hgb1 ); + + /* Add the Boxes to configure size, framestyle and background */ + QVGroupBox *hgb2 = new QVGroupBox( i18n("Thumbnail Size" ), page ); + QVGroupBox *hgb3 = new QVGroupBox( i18n("Thumbnail Frame" ), page ); + + /* Thumbnail size */ + int w = konf->readNumEntry( PIXMAP_WIDTH, 100); + int h = konf->readNumEntry( PIXMAP_HEIGHT, 120 ); + QGrid *lGrid = new QGrid( 2, hgb2 ); + lGrid->setSpacing( 2 ); + QLabel *l1 = new QLabel( i18n("Thumbnail maximum &width:"), lGrid ); + m_thumbWidth = new KIntNumInput( w, lGrid ); + m_thumbWidth->setMinValue(1); + l1->setBuddy( m_thumbWidth ); + + lGrid->setSpacing( 4 ); + l1 = new QLabel( i18n("Thumbnail maximum &height:"), lGrid ); + m_thumbHeight = new KIntNumInput( m_thumbWidth, h, lGrid ); + m_thumbHeight->setMinValue(1); + l1->setBuddy( m_thumbHeight ); + + /* Frame Stuff */ + int frameWidth = konf->readNumEntry( THUMB_MARGIN, 3 ); + QColor col1 = konf->readColorEntry( MARGIN_COLOR1, &(colorGroup().base())); + QColor col2 = konf->readColorEntry( MARGIN_COLOR2, &(colorGroup().foreground())); + + QGrid *fGrid = new QGrid( 2, hgb3 ); + fGrid->setSpacing( 2 ); + l1 = new QLabel(i18n("Thumbnail &frame width:"), fGrid ); + m_frameWidth = new KIntNumInput( frameWidth, fGrid ); + m_frameWidth->setMinValue(0); + l1->setBuddy( m_frameWidth ); + + l1 = new QLabel(i18n("Frame color &1: "), fGrid ); + m_colButt1 = new KColorButton( col1, fGrid ); + l1->setBuddy( m_colButt1 ); + + l1 = new QLabel(i18n("Frame color &2: "), fGrid ); + m_colButt2 = new KColorButton( col2, fGrid ); + l1->setBuddy( m_colButt2 ); + /* TODO: Gradient type */ + + top->addWidget( hgb2, 10); + top->addWidget( hgb3, 10); + top->addStretch(10); + +} + + +void KookaPreferences::slotOk( void ) +{ + slotApply(); + accept(); + +} + + +void KookaPreferences::slotApply( void ) +{ + /* ** startup options ** */ + + /** write the global one, to read from libkscan also */ + konf->setGroup(QString::fromLatin1(GROUP_STARTUP)); + bool cbVal = !(cbShowScannerSelection->isChecked()); + kdDebug(28000) << "Writing for " << STARTUP_SKIP_ASK << ": " << cbVal << endl; + konf->writeEntry( STARTUP_SKIP_ASK, cbVal, true, true ); /* global flag goes to kdeglobals */ + + /* only search for local (=non-net) scanners ? */ + konf->writeEntry( STARTUP_ONLY_LOCAL, !cbNetQuery->isChecked(), true, true ); /* global */ + + /* Should kooka open the last displayed image in the viewer ? */ + if( cbReadStartupImage ) + konf->writeEntry( STARTUP_READ_IMAGE, cbReadStartupImage->isChecked()); + + /* ** Image saver option(s) ** */ + konf->setGroup( OP_FILE_GROUP ); + bool showFormatAssist = cbSkipFormatAsk->isChecked(); + konf->writeEntry( OP_FILE_ASK_FORMAT, showFormatAssist ); + konf->writeEntry( OP_ASK_FILENAME, cbFilenameAsk->isChecked() ); + + /* ** Thumbnail options ** */ + konf->setGroup( THUMB_GROUP ); + konf->writeEntry( PIXMAP_WIDTH, m_thumbWidth->value() ); + konf->writeEntry( PIXMAP_HEIGHT, m_thumbHeight->value() ); + konf->writeEntry( THUMB_MARGIN, m_frameWidth->value() ); + konf->writeEntry( MARGIN_COLOR1, m_colButt1->color()); + konf->writeEntry( MARGIN_COLOR2, m_colButt2->color()); + + KURL bgUrl = m_tileSelector->selectedURL().url(); + bgUrl.setProtocol(""); + kdDebug(28000) << "Writing tile-pixmap " << bgUrl.prettyURL() << endl; + konf->writePathEntry( BG_WALLPAPER, bgUrl.url() ); + + /* ** OCR Options ** */ + konf->setGroup( CFG_GROUP_OCR_DIA ); + QString eng( "gocr" ); + + if( m_ocradBut->isChecked() ) + eng = "ocrad"; + + if( m_kadmosBut && m_kadmosBut->isChecked() ) + eng = "kadmos"; + + if( eng != m_prevOCREngine ) + { + // selection of the ocr engine has changed. Popup button. + KMessageBox::sorry( this, i18n( "The OCR engine settings were changed.\n" + "Note that Kooka needs to be restarted to change the OCR engine."), + i18n("OCR Engine Change") ); + } + + konf->writeEntry(CFG_OCR_ENGINE, eng ); + + QString path = m_urlReqGocr->url(); + if( ! path.isEmpty() ) + konf->writePathEntry( CFG_GOCR_BINARY, path ); + + path = m_urlReqOcrad->url(); + if( ! path.isEmpty() ) + konf->writePathEntry( CFG_OCRAD_BINARY, path ); + + konf->sync(); + + emit dataSaved(); +} + +void KookaPreferences::slotDefault( void ) +{ + cbNetQuery->setChecked( true ); + cbShowScannerSelection->setChecked( true); + cbReadStartupImage->setChecked( true); + cbSkipFormatAsk->setChecked( true ); + KStandardDirs stdDir; + QString bgImg = stdDir.findResource( "data", STD_TILE_IMG ); + m_tileSelector->setURL( KURL(bgImg) ); + m_thumbWidth->setValue( 100 ); + m_thumbHeight->setValue( 120 ); + QColor col1 = QColor( colorGroup().base()); + QColor col2 = QColor( colorGroup().foreground()); + + m_frameWidth->setValue( 3 ); + m_colButt1->setColor( col1 ); + m_colButt2->setColor( col2 ); + m_gocrBut->setChecked(true); +} + + + +#include "kookapref.moc" + diff --git a/kooka/kookapref.h b/kooka/kookapref.h new file mode 100644 index 00000000..c5ab34c0 --- /dev/null +++ b/kooka/kookapref.h @@ -0,0 +1,100 @@ +/*************************************************************************** + kookapref.h - Preferences + ------------------- + begin : Sun Jan 16 2000 + copyright : (C) 2000 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ +#ifndef KOOKAPREF_H +#define KOOKAPREF_H + +#include <kdialogbase.h> +#include <qframe.h> + +class KConfig; +class QLabel; +class KIntNumInput; +class KColorButton; +class ImageSelectLine; +class KScanEntry; +class QRadioButton; +class KURLRequester; +class QCheckBox; + +#define STARTUP_READ_IMAGE "ReadImageOnStart" +#define CFG_GROUP_OCR_DIA "ocrDialog" +#define CFG_OCRAD_BINARY "ocradBinary" +#define CFG_GOCR_BINARY "gocrBinary" + +class KookaPreferences : public KDialogBase +{ + Q_OBJECT +public: + KookaPreferences(); + static QString tryFindGocr( void ); + static QString tryFindBinary( const QString&, const QString& ); + +public slots: + void slotOk( void ); + void slotApply( void ); + void slotDefault( void ); + +private slots: + bool checkOCRBinIntern( const QString&, const QString&, bool ); + + void slCheckOnGOCR( const QString& ); + void slCheckOnOCRAD( const QString& ); + +signals: + void dataSaved(); + +private: + void setupStartupPage(); + void setupSaveFormatPage(); + void setupThumbnailPage(); + void setupOCRPage(); + KURLRequester* binaryCheckBox( QWidget *, const QString& ); + + QCheckBox *cbNetQuery; + QCheckBox *cbSkipFormatAsk; + QCheckBox *cbFilenameAsk; + QCheckBox *cbShowScannerSelection; + KConfig *konf; + QCheckBox *cbReadStartupImage; + + KIntNumInput *m_thumbWidth; + KIntNumInput *m_thumbHeight; + KIntNumInput *m_frameWidth; + ImageSelectLine *m_tileSelector; + KColorButton *m_colButt1; + KColorButton *m_colButt2; + + KURLRequester *m_urlReqGocr; + KURLRequester *m_urlReqOcrad; + + QRadioButton *m_gocrBut; + QRadioButton *m_kadmosBut; + QRadioButton *m_ocradBut; + QString m_prevOCREngine; +}; + + +#endif // KOOKAPREF_H diff --git a/kooka/kookaprint.cpp b/kooka/kookaprint.cpp new file mode 100644 index 00000000..6e0554e9 --- /dev/null +++ b/kooka/kookaprint.cpp @@ -0,0 +1,410 @@ +/*************************************************************************** + kookaprint.cpp - Printing from the gallery + ------------------- + begin : Tue May 13 2003 + copyright : (C) 1999 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#include "kookaprint.h" +#include "kookaimage.h" +#include <kprinter.h> +#include <qpainter.h> +#include <qpaintdevicemetrics.h> +#include <qfontmetrics.h> +#include "imgprintdialog.h" +#include <kdebug.h> +#include <klocale.h> + +KookaPrint::KookaPrint( KPrinter *printer ) + :QObject(), + m_printer(printer), + m_extraMarginPercent(10) +{ + +} + +bool KookaPrint::printImage( KookaImage *img ) +{ + bool result = true; + if( ! m_printer || !img) return false; + + QString psMode = m_printer->option( OPT_PSGEN_DRAFT ); + kdDebug(28000) << "User setting for quality: " << psMode << endl; + +#if 0 + if( psMode == "1" ) + m_printer->setResolution( 75 ); + else + m_printer->setResolution( 600 ); +#endif + + /* Create painter _after_ setting Resolution */ + QPainter painter(m_printer); + m_painter = &painter; + KookaImage tmpImg; + QPoint pt(0, 0); // the top-left corner (image will be centered) + + // We use a QPaintDeviceMetrics to know the actual page size in pixel, + // this gives the real painting area + QPaintDeviceMetrics printermetrics( m_painter->device() ); + + int screenRes = m_printer->option( OPT_SCREEN_RES ).toInt(); + // int printerRes = printermetrics.logicalDpiX(); + int printerRes = m_printer->resolution(); + + QString scale = m_printer->option( OPT_SCALING ); + + int reso = screenRes; + + if( scale == "scan" ) + { + /* Scale to original size */ + reso = m_printer->option( OPT_SCAN_RES ).toInt(); + } + else if( scale == "custom" ) + { + // kdDebug(28000) << "Not yet implemented: Custom scale" << endl; + double userWidthInch = (m_printer->option( OPT_WIDTH ).toDouble() / 25.4 ); + reso = int( double(img->width()) / userWidthInch ); + + kdDebug(28000) << "Custom resolution: " << reso << endl; + + } + else if( scale == "fitpage" ) + { + kdDebug(28000) << "Printing using maximum space on page" << endl; + printFittingToPage( img ); + reso = 0; // to skip the printing on this page. + } + + /* Scale the image for printing */ + kdDebug(28000) << "Printer-Resolution: " << printerRes << " and scale-Reso: " << reso << endl; + QSize margins = m_printer->margins(); + kdDebug(28000) << "Printer-Margins left: " << margins.width() << " and top " << margins.height() + << endl; + if( reso > 0) + { + double sizeInch = double(img->width()) / double(reso); + int newWidth = int(sizeInch * printerRes); + + printerRes = printermetrics.logicalDpiY(); + sizeInch = double(img->height()) / double(reso); + int newHeight = int(sizeInch * printerRes ); + + kdDebug(28000) << "Scaling to printer size " << newWidth << " x " << newHeight << endl; + + tmpImg = img->smoothScale(newWidth, newHeight, QImage::ScaleFree); + + QSize sz = tmpImg.size(); // the current image size + QSize maxOnPage = maxPageSize(); // the maximum space on one side + + int maxRows, maxCols; + int subpagesCnt = tmpImg.cutToTiles( maxOnPage, maxRows, maxCols ); + + kdDebug(28000) << "Subpages count: " << subpagesCnt << + " Columns:" << maxCols << " Rows:" << maxRows << endl; + + int cnt = 0; + + for( int row = 0; row < maxRows; row++ ) + { + for( int col = 0; col < maxCols; col++ ) + { + const QRect part = tmpImg.getTileRect( row, col ); + const QSize imgSize = part.size(); + + kdDebug(28000) << "Printing part from " << part.x() << "/" << part.y() + << " width:"<< part.width() << " and height " << part.height() << endl; + QImage tileImg = tmpImg.copy( part ); + + m_painter->drawImage( printPosTopLeft(imgSize), tileImg ); + drawCornerMarker( imgSize, row, col, maxRows, maxCols ); + cnt++; + if( cnt < subpagesCnt ) + m_printer->newPage(); + } + } + } + + m_painter = 0; // no, this is not a memory leak. + return result; +} + +void KookaPrint::printFittingToPage(KookaImage *img) +{ + if( ! img || ! m_painter ) return; + + KookaImage tmpImg; + + QString psMode = m_printer->option( OPT_RATIO ); + bool maintainAspect = (psMode == "1"); + + QSize s = maxPageSize(); + + double wAspect = double(s.width()) / double(img->width()); + double hAspect = double(s.height()) / double(img->height()); + + // take the smaller one. + double aspect = wAspect; + if( hAspect < wAspect ) aspect = hAspect; + + // default: maintain aspect ratio. + int newWidth = int( double( img->width() ) * aspect ); + int newHeight = int( double( img->height()) * aspect ); + + if( ! maintainAspect ) + { + newWidth = int( double( img->width() ) * wAspect ); + newHeight = int( double( img->height() ) * hAspect ); + } + + tmpImg = img->smoothScale(newWidth, newHeight, QImage::ScaleFree); + + m_painter->drawImage( 0,0, tmpImg ); + +} + + +void KookaPrint::drawMarkerAroundPoint( const QPoint& p ) +{ + if( ! m_painter ) return; + const int len = 10; + + m_painter->drawLine( p-QPoint(len,0), p+QPoint(len,0)); + m_painter->drawLine( p-QPoint(0,len), p+QPoint(0,len)); + +} + + +void KookaPrint::drawCutSign( const QPoint& p, int num, MarkerDirection dir ) +{ + QBrush saveB = m_painter->brush(); + int start = 0; + const int radius=20; + + QColor brushColor( Qt::red ); + int toffX=0; + int toffY=0; + QString numStr = QString::number(num); + + QFontMetrics fm = m_painter->fontMetrics(); + int textWidth = fm.width( numStr )/2; + int textHeight = fm.width( numStr )/2; + int textYOff = 0; + int textXOff = 0; + switch( dir ) + { + case SW: + start = -90; + brushColor = Qt::green; + toffX =-1; + toffY = 1; + textXOff = -1*textWidth; + textYOff = textHeight; + break; + case NW: + start = -180; + brushColor = Qt::blue; + toffX =-1; + toffY =-1; + textXOff = -1*textWidth; + textYOff = textHeight; + break; + case NO: + start = -270; + brushColor = Qt::yellow; + toffX = 1; + toffY = -1; + textXOff = -1*textWidth; + textYOff = textHeight; + + break; + case SO: + start = 0; + brushColor = Qt::magenta; + toffX = 1; + toffY = 1; + textXOff = -1*textWidth; + textYOff = textHeight; + break; + default: + start = 0; + } + + /* to draw around the point p, subtraction of the half radius is needed */ + int x = p.x()-radius/2; + int y = p.y()-radius/2; + + // m_painter->drawRect( x, y, radius, radius ); /* debug !!! */ + const int tAway = radius*3/4; + + QRect bRect = fm.boundingRect( QString::number(num)); + int textX = p.x()+ tAway * toffX + textXOff; + int textY = p.y()+ tAway * toffY + textYOff; + + // m_painter->drawRect( textX, textY, bRect.width(), bRect.height() ); + kdDebug(28000) << "Drawing to position " << textX << "/" << textY << endl; + m_painter->drawText( textX, + textY, + QString::number(num)); + QBrush b( brushColor, NoBrush /* remove this to get debug color*/ ); + + + m_painter->setBrush( b ); + m_painter->drawPie( x, y, radius, radius, 16*start, -16*90 ); + + m_painter->setBrush( saveB ); +} + + +/* + * draws the circle and the numbers that indicate the pages to glue to the side + */ +void KookaPrint::drawCornerMarker( const QSize& imgSize, int row, int col, int maxRows, int maxCols ) +{ + QPoint p; + + kdDebug(28000) << "Marker: Row: " << row << " and col " << col <<" from max " + << maxRows << "x" << maxCols << endl; + + // Top left. + p = printPosTopLeft( imgSize ); + drawMarkerAroundPoint( p ); + int indx = maxCols*row+col+1; + if( maxRows > 1 || maxCols > 1 ) + { + if( col > 0 ) + drawCutSign( p, indx-1, SW ); + if( row > 0 ) + drawCutSign( p, indx-maxCols, NO ); + + if( row > 0 && col > 0 ) + drawCutSign( p, indx-maxCols-1, NW ); + } + + // Top Right + p = printPosTopRight( imgSize ); + drawMarkerAroundPoint( p ); + if( maxRows > 1 || maxCols > 1 ) + { + if( col < maxCols-1 ) + drawCutSign( p, indx+1, SO ); + if( row > 0 ) + drawCutSign( p, indx-maxCols, NW ); + if( row > 0 && col < maxCols-1 ) + drawCutSign( p, indx-maxCols+1, NO ); + } + + // Bottom Right + p = printPosBottomRight( imgSize ); + if( maxRows > 1 || maxCols > 1 ) + { + if( col < maxCols-1 ) + drawCutSign( p, indx+1, NO ); + if( row < maxRows-1 ) + drawCutSign( p, indx+maxCols, SW ); + if( row < maxRows -1 && col < maxCols-1 ) + drawCutSign( p, indx+maxCols, SO ); + } + + // p += QPoint( 1, 1 ); + drawMarkerAroundPoint( p ); /* at bottom right */ + + /* Bottom left */ + p = printPosBottomLeft( imgSize ); + // p += QPoint( -1, 1 ); + if( maxRows > 1 || maxCols > 1 ) + { + if( col > 0 ) + drawCutSign( p, indx-1, NW ); + if( row < maxRows-1 ) + drawCutSign( p, indx+maxCols, SO ); + if( row < maxRows -1 && col > 0 ) + drawCutSign( p, indx+maxCols-1, SW ); + } + drawMarkerAroundPoint( p ); /* at bottom left */ +} + +QSize KookaPrint::maxPageSize( int extraShrinkPercent ) const +{ + if( ! m_painter ) return QSize(); + QPaintDeviceMetrics printermetrics( m_painter->device() ); + + double extraShrink = double(100-extraShrinkPercent)/100.0; + + QSize retSize( printermetrics.width(), printermetrics.height() ); + + if( extraShrinkPercent > 0 ) + retSize = QSize( int(double(printermetrics.width())* extraShrink) , + int(double(printermetrics.height())* extraShrink )); + return retSize; +} + +int KookaPrint::extraMarginPix() const +{ + QSize max = maxPageSize(); + /* take the half extra margin */ + return int(double(max.width())*double(m_extraMarginPercent) / 100.0 / 2.0); +} + +QPoint KookaPrint::printPosTopLeft( const QSize& imgSize ) const +{ + QSize max = maxPageSize(); + /* take the half extra margin */ + int eMargin = extraMarginPix(); + + return QPoint( eMargin + (max.width() - imgSize.width())/2, + eMargin + (max.height() - imgSize.height())/2 ); +} + +QPoint KookaPrint::printPosTopRight(const QSize& imgSize) const +{ + QSize max = maxPageSize(); + /* take the half extra margin */ + int eMargin = extraMarginPix(); + + return QPoint( eMargin + (max.width() - imgSize.width())/2+imgSize.width(), + eMargin + (max.height() - imgSize.height())/2 ); +} + +QPoint KookaPrint::printPosBottomLeft(const QSize& imgSize) const +{ + QSize max = maxPageSize(); + int eMargin = extraMarginPix(); + /* take the half extra margin */ + return QPoint( eMargin+(max.width() - imgSize.width())/2, + eMargin+(max.height() - imgSize.height())/2 + imgSize.height() ); +} + +QPoint KookaPrint::printPosBottomRight(const QSize& imgSize) const +{ + QSize max = maxPageSize(); + /* take the half extra margin */ + int eMargin = extraMarginPix(); + + return QPoint( eMargin+(max.width() - imgSize.width())/2 + imgSize.width(), + eMargin+(max.height() - imgSize.height())/2 + imgSize.height() ); +} + + + +#include "kookaprint.moc" diff --git a/kooka/kookaprint.h b/kooka/kookaprint.h new file mode 100644 index 00000000..5f87d973 --- /dev/null +++ b/kooka/kookaprint.h @@ -0,0 +1,95 @@ +/*************************************************************************** + kookaprint.h - Printing from the gallery + ------------------- + begin : Tue May 13 2003 + copyright : (C) 1999 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#ifndef __KOOKA_PRINT_H__ +#define __KOOKA_PRINT_H__ + +#include <qobject.h> +#include <qmap.h> +#include <qstring.h> +#include <kprinter.h> +#include <kdeprint/kprintdialogpage.h> + +class KookaImage; +class KPrinter; +class QPainter; +class KLineEdit; + + +class ImageSettings : public KPrintDialogPage +{ +public: + void setOptions( const QMap<QString, QString>& opts ); + void getOptions( QMap<QString, QString>& opts, bool include_def = false ); + bool isValid( QString& msg ); + +private: + KLineEdit *m_width, *m_height; + +}; + + +class KookaPrint:public QObject +{ + Q_OBJECT +public: + KookaPrint(KPrinter*); + + /** + * The top left edge of the required print position + */ + virtual QPoint printPosTopLeft(const QSize&) const; + virtual QPoint printPosTopRight(const QSize&) const; + virtual QPoint printPosBottomLeft(const QSize&) const; + virtual QPoint printPosBottomRight(const QSize&) const; + + virtual int extraMarginPix() const; + + /** + * The maximum pixel size of the image (or imagepart) on + * the current page + */ + virtual QSize maxPageSize( int extraShrinkPercent = 0 ) const; + +public slots: + + bool printImage( KookaImage* ); + void printFittingToPage(KookaImage *img); +protected: + typedef enum { SW, NW, NO, SO } MarkerDirection; + + virtual void drawMarkerAroundPoint( const QPoint& ); + virtual void drawCutSign( const QPoint&, int, MarkerDirection ); + virtual void drawCornerMarker( const QSize&, int, int, int, int ); + +private: + + KPrinter *m_printer; + QPainter *m_painter; + int m_extraMarginPercent; +}; + +#endif diff --git a/kooka/kookarc b/kooka/kookarc new file mode 100644 index 00000000..13875fff --- /dev/null +++ b/kooka/kookarc @@ -0,0 +1,121 @@ +[DockSizes] +Kookas MainDock,Preview ,Thumbs:first_name=Kookas MainDock,Preview\s +Kookas MainDock,Preview ,Thumbs:last_name=Thumbs +Kookas MainDock,Preview ,Thumbs:orientation=0 +Kookas MainDock,Preview ,Thumbs:parent=yes +Kookas MainDock,Preview ,Thumbs:sepPos=72 +Kookas MainDock,Preview ,Thumbs:stayButton=false +Kookas MainDock,Preview ,Thumbs:type=GROUP +Kookas MainDock,Preview :curTab=0 +Kookas MainDock,Preview :parent=yes +Kookas MainDock,Preview :stayButton=false +Kookas MainDock,Preview :tabNames=Kookas MainDock,Preview\s +Kookas MainDock,Preview :type=TAB_GROUP +Kookas MainDock,Thumbs,Preview :curTab=1 +Kookas MainDock,Thumbs,Preview :parent=yes +Kookas MainDock,Thumbs,Preview :stayButton=false +Kookas MainDock,Thumbs,Preview :tabNames=Kookas MainDock,Thumbs,Preview\s +Kookas MainDock,Thumbs,Preview :type=TAB_GROUP +Kookas MainDock,Thumbs:curTab=1 +Kookas MainDock,Thumbs:first_name=Kookas MainDock +Kookas MainDock,Thumbs:last_name=Thumbs +Kookas MainDock,Thumbs:orientation=0 +Kookas MainDock,Thumbs:parent=yes +Kookas MainDock,Thumbs:sepPos=54 +Kookas MainDock,Thumbs:stayButton=false +Kookas MainDock,Thumbs:tabNames=Kookas MainDock,Thumbs +Kookas MainDock,Thumbs:type=TAB_GROUP +Kookas MainDock:stayButton=false +Kookas MainDock:type=DOCK +Main:Geometry=40,60,980,760 +Main:dock=Kookas MainDock +Main:view=Scanpackager,Recent,Scan Parameter,Kookas MainDock,Preview ,Thumbs +Main:visible=false +NameList=Kookas MainDock,Thumbs,Scanpackager,Recent,Scan Parameter,Preview ,Kookas MainDock\\,Preview ,Kookas MainDock\\,Preview \\,Thumbs,Recent\\,Scan Parameter,Scanpackager\\,Recent\\,Scan Parameter,Scanpackager\\,Recent\\,Scan Parameter\\,Kookas MainDock\\,Preview \\,Thumbs +Preview ,Scanpackager,Recent,Scan Parameter,Kookas MainDock,Thumbs:first_name=Preview ,Scanpackager,Recent,Scan Parameter +Preview ,Scanpackager,Recent,Scan Parameter,Kookas MainDock,Thumbs:last_name=Kookas MainDock,Thumbs +Preview ,Scanpackager,Recent,Scan Parameter,Kookas MainDock,Thumbs:orientation=1 +Preview ,Scanpackager,Recent,Scan Parameter,Kookas MainDock,Thumbs:parent=yes +Preview ,Scanpackager,Recent,Scan Parameter,Kookas MainDock,Thumbs:sepPos=38 +Preview ,Scanpackager,Recent,Scan Parameter,Kookas MainDock,Thumbs:stayButton=false +Preview ,Scanpackager,Recent,Scan Parameter,Kookas MainDock,Thumbs:type=GROUP +Preview ,Scanpackager,Recent,Scan Parameter:first_name=Preview ,Scanpackager,Recent +Preview ,Scanpackager,Recent,Scan Parameter:last_name=Scan Parameter +Preview ,Scanpackager,Recent,Scan Parameter:orientation=0 +Preview ,Scanpackager,Recent,Scan Parameter:parent=yes +Preview ,Scanpackager,Recent,Scan Parameter:sepPos=50 +Preview ,Scanpackager,Recent,Scan Parameter:stayButton=false +Preview ,Scanpackager,Recent,Scan Parameter:type=GROUP +Preview ,Scanpackager,Recent:first_name=Preview ,Scanpackager +Preview ,Scanpackager,Recent:last_name=Recent +Preview ,Scanpackager,Recent:orientation=0 +Preview ,Scanpackager,Recent:parent=yes +Preview ,Scanpackager,Recent:sepPos=84 +Preview ,Scanpackager,Recent:stayButton=false +Preview ,Scanpackager,Recent:type=GROUP +Preview ,Scanpackager:first_name=Preview\s +Preview ,Scanpackager:last_name=Scanpackager +Preview ,Scanpackager:orientation=0 +Preview ,Scanpackager:parent=yes +Preview ,Scanpackager:sepPos=50 +Preview ,Scanpackager:stayButton=false +Preview ,Scanpackager:type=GROUP +Preview :stayButton=false +Preview :type=DOCK +Recent,Scan Parameter:first_name=Recent +Recent,Scan Parameter:last_name=Scan Parameter +Recent,Scan Parameter:orientation=0 +Recent,Scan Parameter:parent=yes +Recent,Scan Parameter:sepPos=14 +Recent,Scan Parameter:stayButton=false +Recent,Scan Parameter:type=GROUP +Recent:stayButton=false +Recent:type=DOCK +Scan Parameter:stayButton=false +Scan Parameter:type=DOCK +Scanpackager,Preview ,Recent,Scan Parameter,Kookas MainDock,Thumbs:first_name=Scanpackager,Preview ,Recent,Scan Parameter +Scanpackager,Preview ,Recent,Scan Parameter,Kookas MainDock,Thumbs:last_name=Kookas MainDock,Thumbs +Scanpackager,Preview ,Recent,Scan Parameter,Kookas MainDock,Thumbs:orientation=1 +Scanpackager,Preview ,Recent,Scan Parameter,Kookas MainDock,Thumbs:parent=yes +Scanpackager,Preview ,Recent,Scan Parameter,Kookas MainDock,Thumbs:sepPos=38 +Scanpackager,Preview ,Recent,Scan Parameter,Kookas MainDock,Thumbs:stayButton=false +Scanpackager,Preview ,Recent,Scan Parameter,Kookas MainDock,Thumbs:type=GROUP +Scanpackager,Preview ,Recent,Scan Parameter:first_name=Scanpackager,Preview\s +Scanpackager,Preview ,Recent,Scan Parameter:last_name=Recent,Scan Parameter +Scanpackager,Preview ,Recent,Scan Parameter:orientation=0 +Scanpackager,Preview ,Recent,Scan Parameter:parent=yes +Scanpackager,Preview ,Recent,Scan Parameter:sepPos=48 +Scanpackager,Preview ,Recent,Scan Parameter:stayButton=false +Scanpackager,Preview ,Recent,Scan Parameter:type=GROUP +Scanpackager,Preview :curTab=0 +Scanpackager,Preview :parent=yes +Scanpackager,Preview :stayButton=false +Scanpackager,Preview :tabNames=Scanpackager,Preview\s +Scanpackager,Preview :type=TAB_GROUP +Scanpackager,Recent,Scan Parameter,Kookas MainDock,Preview ,Thumbs:first_name=Scanpackager,Recent,Scan Parameter +Scanpackager,Recent,Scan Parameter,Kookas MainDock,Preview ,Thumbs:last_name=Kookas MainDock,Preview ,Thumbs +Scanpackager,Recent,Scan Parameter,Kookas MainDock,Preview ,Thumbs:orientation=1 +Scanpackager,Recent,Scan Parameter,Kookas MainDock,Preview ,Thumbs:parent=yes +Scanpackager,Recent,Scan Parameter,Kookas MainDock,Preview ,Thumbs:sepPos=41 +Scanpackager,Recent,Scan Parameter,Kookas MainDock,Preview ,Thumbs:stayButton=false +Scanpackager,Recent,Scan Parameter,Kookas MainDock,Preview ,Thumbs:type=GROUP +Scanpackager,Recent,Scan Parameter,Kookas MainDock,Thumbs,Preview :first_name=Scanpackager,Recent,Scan Parameter +Scanpackager,Recent,Scan Parameter,Kookas MainDock,Thumbs,Preview :last_name=Kookas MainDock,Thumbs,Preview\s +Scanpackager,Recent,Scan Parameter,Kookas MainDock,Thumbs,Preview :orientation=1 +Scanpackager,Recent,Scan Parameter,Kookas MainDock,Thumbs,Preview :parent=yes +Scanpackager,Recent,Scan Parameter,Kookas MainDock,Thumbs,Preview :sepPos=38 +Scanpackager,Recent,Scan Parameter,Kookas MainDock,Thumbs,Preview :stayButton=false +Scanpackager,Recent,Scan Parameter,Kookas MainDock,Thumbs,Preview :type=GROUP +Scanpackager,Recent,Scan Parameter:first_name=Scanpackager +Scanpackager,Recent,Scan Parameter:last_name=Recent,Scan Parameter +Scanpackager,Recent,Scan Parameter:orientation=0 +Scanpackager,Recent,Scan Parameter:parent=yes +Scanpackager,Recent,Scan Parameter:sepPos=42 +Scanpackager,Recent,Scan Parameter:stayButton=false +Scanpackager,Recent,Scan Parameter:type=GROUP +Scanpackager:stayButton=false +Scanpackager:type=DOCK +Thumbs:stayButton=false +Thumbs:type=DOCK +Version=0.0.5 + diff --git a/kooka/kookaui.rc b/kooka/kookaui.rc new file mode 100644 index 00000000..77d3cf4e --- /dev/null +++ b/kooka/kookaui.rc @@ -0,0 +1,62 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="Kooka" version="3"> +<MenuBar> + <Menu name="file"><text>&File</text> + <Action name="foldernew"/> + <Action name="saveImage"/> + <Action name="importImage"/> + <Action name="deleteImage"/> + <Action name="unloadImage"/> + <Action name="saveOCRResult"/> + </Menu> + <Menu name="imgCanvas"><text>&Image</text> + <Action name="openInGraphApp"/> + <Separator/> + <Action name="ocrImage"/> + <Action name="ocrImageSelect"/> + <Separator/> + <Action name="scaleToWidth"/> + <Action name="scaleToHeight"/> + <Action name="scaleOriginal"/> + <Separator/> + <Action name="createFromSelection"/> + <Separator/> + <Action name="mirrorVertical"/> + <Action name="mirrorHorizontal"/> + <Action name="mirrorBoth"/> + <Separator/> + <Action name="rotateClockwise"/> + <Action name="rotateCounterClockwise"/> + <Action name="upsitedown"/> + </Menu> + <Menu name="settings"><text>&Settings</text> + <Action name="settings_show_docks" append="show_merge"/> + <Action name="enable_msgs" append="save_merge"/> + <Separator append="save_merge"/> + <Action name="selectsource" append="save_merge"/> + <Separator append="save_merge"/> + <Action name="loadscanparam" append="save_merge"/> + <Action name="savescanparam" append="save_merge"/> + </Menu> +</MenuBar> + <ToolBar name="mainToolBar" fullWidth="true"> + <text>Image Viewer Toolbar</text> + <Action name="ocrImage"/> + <Action name="ocrImageSelect"/> + <Separator/> + <Action name="scaleToWidth"/> + <Action name="scaleToHeight"/> + <Action name="scaleOriginal"/> + <Action name="keepZoom"/> + <Separator/> + <Action name="createFromSelection"/> + <Separator/> + <Action name="mirrorVertical"/> + <Action name="mirrorHorizontal"/> + <Action name="mirrorBoth"/> + <Separator/> + <Action name="rotateClockwise"/> + <Action name="rotateCounterClockwise"/> + <Action name="upsitedown"/> + </ToolBar> +</kpartgui> diff --git a/kooka/kookaview.cpp b/kooka/kookaview.cpp new file mode 100644 index 00000000..f1c1d8d0 --- /dev/null +++ b/kooka/kookaview.cpp @@ -0,0 +1,1083 @@ +/*************************************************************************** + kookaview.cpp - kookas visible stuff + ------------------- + begin : ? + copyright : (C) 1999 by Klaas Freitag + email : freitag@suse.de + + $Id$ + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#include "kookaview.h" +#include "resource.h" +#include "kscandevice.h" +#include "imgscaninfo.h" +#include "devselector.h" +#include "ksaneocr.h" +#include "img_saver.h" +#include "kookapref.h" +#include "imgnamecombo.h" +#include "thumbview.h" +#include "dwmenuaction.h" +#include "kookaimage.h" +#include "kookaimagemeta.h" +#include "ocrresedit.h" +#include "kookaprint.h" +#include "imgprintdialog.h" +#if 0 +#include "paramsetdialogs.h" +#endif +#include <qlabel.h> +#include <qpainter.h> +#include <qlayout.h> +#include <qsplitter.h> +#include <qstrlist.h> +#include <qpaintdevice.h> +#include <qpaintdevicemetrics.h> +#include <qpopupmenu.h> +#include <qwidgetstack.h> + +#include <kurl.h> +#include <krun.h> +#include <kapplication.h> +#include <kstatusbar.h> +#include <kconfig.h> +#include <kdebug.h> +#include <ktrader.h> +#include <klibloader.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <keditcl.h> +#include <kled.h> +#include <kcombobox.h> +#include <kaction.h> +#include <kiconloader.h> +#include <kshortcut.h> +#include <kdockwidget.h> +#include <qobject.h> + +#include <kparts/componentfactory.h> +#include <qimage.h> +#include <kpopupmenu.h> + + +#define STARTUP_IMG_SELECTION "SelectedImageOnStartup" + + +KookaView::KookaView( KParts::DockMainWindow *parent, const QCString& deviceToUse) + : QObject(), + m_ocrResultImg(0), + ocrFabric(0), + m_mainDock(0), + m_dockScanParam(0), + m_dockThumbs(0), + m_dockPackager(0), + m_dockRecent(0), + m_dockPreview(0), + m_dockOCRText(0), + m_mainWindow(parent), + m_ocrResEdit(0) +{ + KIconLoader *loader = KGlobal::iconLoader(); + scan_params = 0L; + preview_canvas = 0L; + + m_mainDock = parent->createDockWidget( "Kookas MainDock", + loader->loadIcon( "folder_image", KIcon::Small ), + 0L, i18n("Image Viewer")); + m_mainDock->setEnableDocking(KDockWidget::DockNone ); + m_mainDock->setDockSite( KDockWidget::DockFullSite ); + + parent->setView( m_mainDock); + parent->setMainDockWidget( m_mainDock); + + img_canvas = new ImageCanvas( m_mainDock ); + img_canvas->setMinimumSize(100,200); + img_canvas->enableContextMenu(true); + connect( img_canvas, SIGNAL( imageReadOnly(bool)), + this, SLOT(slViewerReadOnly(bool))); + + KPopupMenu *ctxtmenu = static_cast<KPopupMenu*>(img_canvas->contextMenu()); + if( ctxtmenu ) + ctxtmenu->insertTitle(i18n("Image View")); + m_mainDock->setWidget( img_canvas ); + + /** Thumbview **/ + m_dockThumbs = parent->createDockWidget( "Thumbs", + loader->loadIcon( "thumbnail", KIcon::Small ), + 0L, i18n("Thumbnails")); + m_dockThumbs->setDockSite(KDockWidget::DockFullSite ); + + /* thumbnail viewer widget */ + m_thumbview = new ThumbView( m_dockThumbs); + m_dockThumbs->setWidget( m_thumbview ); + + m_dockThumbs->manualDock( m_mainDock, // dock target + KDockWidget::DockBottom, // dock site + 20 ); // relation target/this (in percent) + + /** Packager Dock **/ + /* A new packager to contain the already scanned images */ + m_dockPackager = parent->createDockWidget( "Scanpackager", + loader->loadIcon( "palette_color", KIcon::Small ), + 0L, i18n("Gallery")); + m_dockPackager->setDockSite(KDockWidget::DockFullSite); + packager = new ScanPackager( m_dockPackager ); + m_dockPackager->setWidget( packager ); + m_dockPackager->manualDock( m_mainDock, // dock target + KDockWidget::DockLeft, // dock site + 30 ); // relation target/this (in percent) + + + connect( packager, SIGNAL(showThumbnails( KFileTreeViewItem* )), + this, SLOT( slShowThumbnails( KFileTreeViewItem* ))); + connect( m_thumbview, SIGNAL( selectFromThumbnail( const KURL& )), + packager, SLOT( slSelectImage(const KURL&))); + + /* + * Create a Kombobox that holds the last folders visible even on the preview page + */ + m_dockRecent = parent->createDockWidget( "Recent", + loader->loadIcon( "image", KIcon::Small ), + 0L, i18n("Gallery Folders")); + + m_dockRecent->setDockSite(KDockWidget::DockFullSite); + + QHBox *recentBox = new QHBox( m_dockRecent ); + recentBox->setMargin(KDialog::marginHint()); + QLabel *lab = new QLabel( i18n("Gallery:"), recentBox ); + lab->setSizePolicy( QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed) ); + recentFolder = new ImageNameCombo( recentBox ); + + m_dockRecent->setWidget( recentBox ); + m_dockRecent->manualDock( m_dockPackager, // dock target + KDockWidget::DockBottom, // dock site + 5 ); // relation target/this (in percent) + + + + connect( packager, SIGNAL( galleryPathSelected( KFileTreeBranch*, const QString&)), + recentFolder, SLOT( slotGalleryPathChanged( KFileTreeBranch*, const QString& ))); + + connect( packager, SIGNAL( directoryToRemove( KFileTreeBranch*, const QString&)), + recentFolder, SLOT( slotPathRemove( KFileTreeBranch*, const QString& ))); + + connect( recentFolder, SIGNAL(activated( const QString& )), + packager, SLOT(slotSelectDirectory( const QString& ))); + + /* the object from the kscan lib to handle low level scanning */ + m_dockScanParam = parent->createDockWidget( "Scan Parameter", + loader->loadIcon( "folder", KIcon::Small ), + 0L, i18n("Scan Parameter")); + // + m_dockScanParam->setDockSite(KDockWidget::DockFullSite); + + m_dockScanParam->setWidget( 0 ); // later + sane = new KScanDevice( m_dockScanParam ); + Q_CHECK_PTR(sane); + + m_dockScanParam->manualDock( m_dockRecent, // dock target + KDockWidget::DockBottom, // dock site + 20 ); // relation target/this (in percent) + m_dockScanParam->hide(); + + /* select the scan device, either user or from config, this creates and assembles + * the complete scanner options dialog + * scan_params must be zero for that */ + + m_dockPreview = parent->createDockWidget( "Preview ", + loader->loadIcon( "viewmag", KIcon::Small ), + 0L, i18n("Scan Preview")); + + preview_canvas = new Previewer( m_dockPreview ); + { + preview_canvas->setMinimumSize( 100,100); + + /* since the scan_params will be created in slSelectDevice, do the + * connections later + */ + } + m_dockPreview->setWidget( preview_canvas ); + m_dockPreview->manualDock( m_mainDock, // dock target + KDockWidget::DockCenter, // dock site + 100 ); // relation target/this (in percent) + + /* Create a text editor part for ocr results */ + + m_dockOCRText = parent->createDockWidget( "OCRResults", + loader->loadIcon("edit", KIcon::Small ), + 0L, i18n("OCR Result Text")); + // m_textEdit + m_ocrResEdit = new ocrResEdit( m_dockOCRText ); + + if( m_ocrResEdit ) + { + m_dockOCRText->setWidget( m_ocrResEdit ); // m_textEdit->widget() ); + m_dockOCRText->manualDock( m_dockThumbs, // dock target + KDockWidget::DockCenter, // dock site + 100 ); // relation target/this (in percent) + + m_ocrResEdit->setTextFormat( Qt::PlainText ); + m_ocrResEdit->setWordWrap( QTextEdit::NoWrap ); + // m_dockOCRText->hide(); + } + + if( slSelectDevice(deviceToUse)) + { + /* Load from config which tab page was selected last time */ + } + + /* New image created after scanning */ + connect(sane, SIGNAL(sigNewImage(QImage*,ImgScanInfo*)), this, SLOT(slNewImageScanned(QImage*,ImgScanInfo*))); + /* New preview image */ + connect(sane, SIGNAL(sigNewPreview(QImage*,ImgScanInfo *)), this, SLOT( slNewPreview(QImage*,ImgScanInfo *))); + + connect( sane, SIGNAL( sigScanStart() ), this, SLOT( slScanStart())); + connect( sane, SIGNAL( sigScanFinished(KScanStat)), this, SLOT(slScanFinished(KScanStat))); + connect( sane, SIGNAL( sigAcquireStart()), this, SLOT( slAcquireStart())); + /* Image canvas should show a new document */ + connect( packager, SIGNAL( showImage( KookaImage* )), + this, SLOT( slShowAImage( KookaImage*))); + + connect( packager, SIGNAL( aboutToShowImage(const KURL&)), + this, SLOT( slStartLoading( const KURL& ))); + + /* Packager unloads the image */ + connect( packager, SIGNAL( unloadImage( KookaImage* )), + this, SLOT( slUnloadAImage( KookaImage*))); + + /* a image changed mostly through a image manipulation method like rotate */ + connect( packager, SIGNAL( fileChanged( KFileItem* )), + m_thumbview, SLOT( slImageChanged( KFileItem* ))); + + connect( packager, SIGNAL( fileRenamed( KFileItem*, const KURL& )), + m_thumbview, SLOT( slImageRenamed( KFileItem*, const KURL& ))); + + connect( packager, SIGNAL( fileDeleted( KFileItem* )), + m_thumbview, SLOT( slImageDeleted( KFileItem* ))); + + + packager->openRoots(); + + /* Status Bar */ + KStatusBar *statBar = m_mainWindow->statusBar(); + + // statBar->insertItem(QString("1"), SBAR_ZOOM, 0, true ); + statBar->insertItem( QString("-"), StatusImage, 0, true ); + + /* Set a large enough size */ + int w = statBar->fontMetrics(). + width(img_canvas->imageInfoString(2000, 2000, 48)); + kdDebug(28000) << "Fixed size for status bar: " << w << " from string " << img_canvas->imageInfoString(2000, 2000, 48) << endl; + statBar->setItemFixed( StatusImage, w ); + +} + + +KookaView::~KookaView() +{ + saveProperties( KGlobal::config () ); + delete preview_canvas; + + kdDebug(28000)<< "Finished saving config data" << endl; +} + +void KookaView::slViewerReadOnly( bool ) +{ + /* retrieve actions that could change the image */ +} + + +bool KookaView::slSelectDevice( const QCString& useDevice ) +{ + + kdDebug(28000) << "Kookaview: select a device!" << endl; + bool haveConnection = false; + + QCString selDevice; + /* in case useDevice is the term 'gallery', the user does not want to + * connect to a scanner, but only work in gallery mode. Otherwise, try + * to read the device to use from config or from a user dialog */ + if( useDevice != "gallery" ) + { + selDevice = useDevice; + if( selDevice.isEmpty()) + { + selDevice = userDeviceSelection(); + } + } + + if( !selDevice.isEmpty() ) + { + kdDebug(28000) << "Opening device " << selDevice << endl; + + if( connectedDevice == selDevice ) { + kdDebug( 28000) << "Device " << selDevice << " is already selected!" << endl; + return( true ); + } + + if( scan_params ) + { + /* This deletes the existing scan_params^-object */ + slCloseScanDevice(); + } + + /* This connects to the selected scanner */ + scan_params = new ScanParams( m_dockScanParam ); + Q_CHECK_PTR(scan_params); + + if( sane->openDevice( selDevice ) == KSCAN_OK ) + { + connect( scan_params, SIGNAL( scanResolutionChanged( int, int )), + preview_canvas, SLOT( slNewScanResolutions( int, int ))); + + if( ! scan_params->connectDevice( sane ) ) + { + kdDebug(28000) << "Connecting to the scanner failed :( ->TODO" << endl; + } + else + { + haveConnection = true; + connectedDevice = selDevice; + + /* New Rectangle selection in the preview, now scanimge exists */ + ImageCanvas *previewCanvas = preview_canvas->getImageCanvas(); + connect( previewCanvas , SIGNAL( newRect(QRect)), + scan_params, SLOT(slCustomScanSize(QRect))); + connect( previewCanvas, SIGNAL( noRect()), + scan_params, SLOT(slMaximalScanSize())); + // connect( scan_params, SIGNAL( scanResolutionChanged( int, int )), + // preview_canvas, SLOT( slNewScanResolutions( int, int ))); + /* load the preview image */ + if( preview_canvas ) + { + preview_canvas->setPreviewImage( sane->loadPreviewImage() ); + + /* Call this after the devic is actually open */ + preview_canvas->slConnectScanner( sane ); + } + } + } + else + { + kdDebug(28000) << "Could not open device <" << selDevice << ">" << endl; + scan_params->connectDevice(0); + } + + /* show the widget again */ + + m_dockScanParam->setWidget( scan_params ); + + m_dockScanParam->show(); + } + else + { + // no devices available or starting in gallery mode + if( scan_params ) + scan_params->connectDevice( 0L ); + } + return( haveConnection ); +} + +QCString KookaView::userDeviceSelection( ) const +{ + /* Human readable scanner descriptions */ + QStringList hrbackends; + + /* a list of backends the scan backend knows */ + QStrList backends = sane->getDevices(); + QStrListIterator it( backends ); + + QCString selDevice; + if( backends.count() > 0 ) + { + while( it ) + { + kdDebug( 28000 ) << "Found backend: " << it.current() << endl; + hrbackends.append( sane->getScannerName( it.current() )); + ++it; + } + + /* allow the user to select one */ + DeviceSelector ds( 0, backends, hrbackends ); + selDevice = ds.getDeviceFromConfig( ); + + if( selDevice.isEmpty() || selDevice.isNull() ) + { + kdDebug(29000) << "selDevice not found - starting selector!" << selDevice << endl; + if ( ds.exec() == QDialog::Accepted ) + { + selDevice = ds.getSelectedDevice(); + } + } + } + return( selDevice ); +} + + +void KookaView::loadStartupImage( void ) +{ + kdDebug( 28000) << "Starting to load startup image" << endl; + + /* Now set the configured stuff */ + KConfig *konf = KGlobal::config (); + if( konf ) + { + konf->setGroup(GROUP_STARTUP); + bool wantReadOnStart = konf->readBoolEntry( STARTUP_READ_IMAGE, true ); + + if( wantReadOnStart ) + { + QString startup = konf->readPathEntry( STARTUP_IMG_SELECTION ); + + if( !startup.isEmpty() ) + { + kdDebug(28000) << "Loading startup image !" << endl; + packager->slSelectImage( KURL(startup) ); + } + } + else + { + kdDebug(28000) << "Do not load startup image due to config value" << endl; + } + } +} + + +void KookaView::print() +{ + /* For now, print a single file. Later, print multiple images to one page */ + KookaImage *img = packager->getCurrImage(); + if ( !img ) + return; + KPrinter printer; // ( true, pMode ); + printer.setUsePrinterResolution(true); + printer.addDialogPage( new ImgPrintDialog( img )); + + if( printer.setup( m_mainWindow, i18n("Print %1").arg(img->localFileName().section('/', -1)) )) + { + KookaPrint kookaprint( &printer ); + kookaprint.printImage(img); + } +} + +void KookaView::slNewPreview( QImage *new_img, ImgScanInfo * ) +{ + if( new_img ) + { + if( ! new_img->isNull() ) + { + /* flip preview to front */ + m_dockPreview->makeDockVisible(); + } + preview_canvas->newImage( new_img ); + } +} + + +bool KookaView::ToggleVisibility( int item ) +{ + QWidget *w = 0; + bool ret = false; + + switch( item ) + { + case ID_VIEW_SCANPARAMS: + w = scan_params; + break; + case ID_VIEW_POOL: + w = preview_canvas; + break; + default: + w = 0; + } + + if( w ) + { + if( w->isVisible() ) + { + w->hide(); + ret = false; + } + else + { + w->show(); + ret = true; + } + } + return ret; +} + + +void KookaView::doOCRonSelection( void ) +{ + emit( signalChangeStatusbar( i18n("Starting OCR on selection" ))); + + KookaImage img; + + if( img_canvas->selectedImage(&img) ) + { + startOCR( &img ); + } + emit( signalCleanStatusbar() ); +} + +/* Does OCR on the entire picture */ +void KookaView::doOCR( void ) +{ + emit( signalChangeStatusbar( i18n("Starting OCR on the entire image" ))); + KookaImage *img = packager->getCurrImage(); + startOCR( img ); + emit( signalCleanStatusbar( )); +} + +void KookaView::startOCR( KookaImage *img ) +{ + if( img && ! img->isNull() ) + { + if( ocrFabric == 0L ) + { + ocrFabric = new KSANEOCR( m_mainDock, KGlobal::config() ); + ocrFabric->setImageCanvas( img_canvas ); + + connect( ocrFabric, SIGNAL( newOCRResultText( const QString& )), + m_ocrResEdit, SLOT(setText( const QString& ))); + + connect( ocrFabric, SIGNAL( newOCRResultText( const QString& )), + m_dockOCRText, SLOT( show() )); + + connect( ocrFabric, SIGNAL( repaintOCRResImage( )), + img_canvas, SLOT(repaint())); + + connect( ocrFabric, SIGNAL( clearOCRResultText()), + m_ocrResEdit, SLOT(clear())); + + connect( ocrFabric, SIGNAL( updateWord(int, const QString&, const QString& )), + m_ocrResEdit, SLOT( slUpdateOCRResult( int, const QString&, const QString& ))); + + connect( ocrFabric, SIGNAL( ignoreWord(int, const ocrWord&)), + m_ocrResEdit, SLOT( slIgnoreWrongWord( int, const ocrWord& ))); + + connect( ocrFabric, SIGNAL( markWordWrong(int, const ocrWord& )), + m_ocrResEdit, SLOT( slMarkWordWrong( int, const ocrWord& ))); + + connect( ocrFabric, SIGNAL( readOnlyEditor( bool )), + m_ocrResEdit, SLOT( setReadOnly( bool ))); + + connect( ocrFabric, SIGNAL( selectWord( int, const ocrWord& )), + m_ocrResEdit, SLOT( slSelectWord( int, const ocrWord& ))); + + } + + Q_CHECK_PTR( ocrFabric ); + ocrFabric->slSetImage( img ); + + if( !ocrFabric->startOCRVisible(m_mainDock) ) + { + KMessageBox::sorry(0, i18n("Could not start OCR-Process.\n" + "Probably there is already one running." )); + + } + } +} + + +void KookaView::slOCRResultImage( const QPixmap& pix ) +{ + kdDebug(28000) << "Showing OCR Result Image" << endl; + if( ! img_canvas ) return; + + if( m_ocrResultImg ) + { + img_canvas->newImage(0L); + delete m_ocrResultImg; + } + + m_ocrResultImg = new QImage(); + *m_ocrResultImg = pix; + img_canvas->newImage( m_ocrResultImg ); + img_canvas->setReadOnly(true); // ocr result images should be read only. +} + +void KookaView::slScanStart( ) +{ + kdDebug(28000) << "Scan starts " << endl; + if( scan_params ) + { + scan_params->setEnabled( false ); + KLed *led = scan_params->operationLED(); + if( led ) + { + led->setColor( Qt::red ); + led->setState( KLed::On ); + } + } +} + +void KookaView::slAcquireStart( ) +{ + kdDebug(28000) << "Acquire starts " << endl; + if( scan_params ) + { + KLed *led = scan_params->operationLED(); + if( led ) + { + led->setColor( Qt::green ); + } + } +} + +void KookaView::slNewImageScanned( QImage* img, ImgScanInfo* si ) +{ + KookaImageMeta *meta = new KookaImageMeta; + meta->setScanResolution(si->getXResolution(), si->getYResolution()); + packager->slAddImage(img, meta); +} + + + +void KookaView::slScanFinished( KScanStat stat ) +{ + kdDebug(28000) << "Scan finished with status " << stat << endl; + if( scan_params ) + { + scan_params->setEnabled( true ); + KLed *led = scan_params->operationLED(); + if( led ) + { + led->setColor( Qt::green ); + led->setState( KLed::Off ); + } + } +} + + +void KookaView::slCloseScanDevice( ) +{ + kdDebug(28000) << "Scanner Device closes down !" << endl; + if( scan_params ) { + delete scan_params; + scan_params = 0; + m_dockScanParam->setWidget(0L); + m_dockScanParam->hide(); + } + + sane->slCloseDevice(); +} + +void KookaView::slCreateNewImgFromSelection() +{ + if( img_canvas->rootImage() ) + { + emit( signalChangeStatusbar( i18n("Create new image from selection" ))); + QImage img; + if( img_canvas->selectedImage( &img ) ) + { + packager->slAddImage( &img ); + } + emit( signalCleanStatusbar( )); + } + +} + + +void KookaView::slRotateImage(int angle) +{ + // QImage *img = (QImage*) img_canvas->rootImage(); + KookaImage *img = packager->getCurrImage(); + bool doUpdate = true; + + if( img ) + { + QImage resImg; + + QApplication::setOverrideCursor(waitCursor); + switch( angle ) + { + case 90: + emit( signalChangeStatusbar( i18n("Rotate image 90 degrees" ))); + resImg = rotateRight( img ); + break; + case 180: + emit( signalChangeStatusbar( i18n("Rotate image 180 degrees" ))); + resImg = rotate180( img ); + break; + case 270: + case -90: + emit( signalChangeStatusbar( i18n("Rotate image -90 degrees" ))); + resImg = rotateLeft( img ); + + break; + default: + kdDebug(28000) << "Not supported yet !" << endl; + doUpdate = false; + + break; + } + QApplication::restoreOverrideCursor(); + + /* updateCurrImage does the status-bar cleanup */ + if( doUpdate ) + updateCurrImage( resImg ); + else + emit(signalCleanStatusbar()); + } + +} + + + +void KookaView::slMirrorImage( MirrorType m ) +{ + const QImage *img = img_canvas->rootImage(); + bool doUpdate = true; + + if( img ) + { + QImage resImg; + + QApplication::setOverrideCursor(waitCursor); + switch( m ) + { + case MirrorVertical: + emit( signalChangeStatusbar( i18n("Mirroring image vertically" ))); + resImg = img->mirror(); + break; + case MirrorHorizontal: + emit( signalChangeStatusbar( i18n("Mirroring image horizontally" ))); + resImg = img->mirror( true, false ); + break; + case MirrorBoth: + emit( signalChangeStatusbar( i18n("Mirroring image in both directions" ))); + resImg = img->mirror( true, true ); + break; + default: + kdDebug(28000) << "Mirroring: no way ;)" << endl; + doUpdate = false; + } + QApplication::restoreOverrideCursor(); + + /* updateCurrImage does the status-bar cleanup */ + if( doUpdate ) + updateCurrImage( resImg ); + else + emit(signalCleanStatusbar()); + + // img_canvas->newImage( ); + } +} + + +void KookaView::slSaveOCRResult() +{ + if( ! m_ocrResEdit ) return; + m_ocrResEdit->slSaveText(); + +} + + +void KookaView::slLoadScanParams( ) +{ + if( ! sane ) return; +#if 0 + /* not yet cooked */ + LoadSetDialog loadDialog( m_mainDock, sane->shortScannerName(), sane ); + if( loadDialog.exec()) + { + kdDebug(28000)<< "Executed successfully" << endl; + } +#endif +} + +void KookaView::slSaveScanParams( ) +{ + if( !sane ) return; + + /* not yet cooked */ +#if 0 + KScanOptSet optSet( "SaveSet" ); + + sane->getCurrentOptions( &optSet ); + SaveSetDialog dialog( m_mainDock /* this */ , &optSet ); + if( dialog.exec()) + { + kdDebug(28000)<< "Executed successfully" << endl; + QString name = dialog.paramSetName(); + QString desc = dialog.paramSetDescription(); + sane->slSaveScanConfigSet( name, desc ); + } +#endif +} + +void KookaView::slShowAImage( KookaImage *img ) +{ + kdDebug(28000) << "Show new Image" << endl; + if( img_canvas ) + { + img_canvas->newImage( img ); + img_canvas->setReadOnly(false); + } + + /* tell ocr about */ + if( ocrFabric ) + { + ocrFabric->slSetImage( img ); + } + + /* Status Bar */ + KStatusBar *statBar = m_mainWindow->statusBar(); + if( img_canvas ) + statBar->changeItem( img_canvas->imageInfoString(), StatusImage ); +} + +void KookaView::slUnloadAImage( KookaImage * ) +{ + kdDebug(28000) << "Unloading Image" << endl; + if( img_canvas ) + { + img_canvas->newImage( 0L ); + } +} + + +void KookaView::slShowThumbnails(KFileTreeViewItem *dirKfi, bool forceRedraw ) +{ + /* If no item is specified, use the current one */ + if( ! dirKfi ) + { + /* do on the current visible dir */ + KFileTreeViewItem *kftvi = packager->currentKFileTreeViewItem(); + if ( !kftvi ) + { + return; + } + + if( kftvi->isDir()) + { + dirKfi = kftvi; + } + else + { + kftvi = static_cast<KFileTreeViewItem*>(static_cast<QListViewItem*>(kftvi)->parent()); + dirKfi = kftvi; + forceRedraw = true; + packager->setSelected( static_cast<QListViewItem*>(dirKfi), true ); + } + } + + kdDebug(28000) << "Showing thumbs for " << dirKfi->url().prettyURL() << endl; + + /* Only do the new thumbview if the old is on another dir */ + if( m_thumbview && (forceRedraw || m_thumbview->currentDir() != dirKfi->url()) ) + { + m_thumbview->clear(); + /* Find a list of child KFileItems */ + if( forceRedraw ) m_thumbview->readSettings(); + + KFileItemList fileItemsList; + + QListViewItem * myChild = dirKfi->firstChild(); + while( myChild ) + { + fileItemsList.append( static_cast<KFileTreeViewItem*>(myChild)->fileItem()); + myChild = myChild->nextSibling(); + } + + m_thumbview->slNewFileItems( fileItemsList ); + m_thumbview->setCurrentDir( dirKfi->url()); + // m_thumbview->arrangeItemsInGrid(); + } + +} + +/* this slot is called when the user clicks on an image in the packager + * and loading of the image starts + */ +void KookaView::slStartLoading( const KURL& url ) +{ + emit( signalChangeStatusbar( i18n("Loading %1" ).arg( url.prettyURL() ) )); + + // if( m_stack->visibleWidget() != img_canvas ) + // { + // m_stack->raiseWidget( img_canvas ); + // } + +} + + +void KookaView::updateCurrImage( QImage& img ) +{ + if( ! img_canvas->readOnly() ) + { + emit( signalChangeStatusbar( i18n("Storing image changes" ))); + packager->slotCurrentImageChanged( &img ); + emit( signalCleanStatusbar()); + } + else + { + emit( signalChangeStatusbar( i18n("Can not save image, it is write protected!"))); + kdDebug(28000) << "Image is write protected, no saving!" << endl; + } +} + + +void KookaView::saveProperties(KConfig *config) +{ + kdDebug(28000) << "Saving Properties for KookaView !" << endl; + config->setGroup( GROUP_STARTUP ); + /* Get with path */ + config->writePathEntry( STARTUP_IMG_SELECTION, packager->getCurrImageFileName(true)); + +} + + +void KookaView::slOpenCurrInGraphApp( void ) +{ + QString file; + + if( packager ) + { + KFileTreeViewItem *ftvi = packager->currentKFileTreeViewItem(); + + if( ! ftvi ) return; + + kdDebug(28000) << "Trying to open <" << ftvi->url().prettyURL()<< ">" << endl; + KURL::List urllist; + + urllist.append( ftvi->url()); + + KRun::displayOpenWithDialog( urllist ); + } +} + + +QImage KookaView::rotateLeft( QImage *m_img ) +{ + QImage rot; + + if( m_img ) + { + QWMatrix m; + + m.rotate(-90); + rot = m_img->xForm(m); + } + return( rot ); +} + +QImage KookaView::rotateRight( QImage *m_img ) +{ + QImage rot; + + if( m_img ) + { + QWMatrix m; + + m.rotate(+90); + rot = m_img->xForm(m); + } + return( rot ); +} + +QImage KookaView::rotate180( QImage *m_img ) +{ + QImage rot; + + if( m_img ) + { + QWMatrix m; + + m.rotate(+180); + rot = m_img->xForm(m); + } + return( rot ); +} + + + +void KookaView::connectViewerAction( KAction *action ) +{ + QPopupMenu *popup = img_canvas->contextMenu(); + kdDebug(29000) << "This is the popup: " << popup << endl; + if( popup && action ) + { + action->plug( popup ); + } +} + +void KookaView::connectGalleryAction( KAction *action ) +{ + QPopupMenu *popup = packager->contextMenu(); + + if( popup && action ) + { + action->plug( popup ); + } +} + +void KookaView::slFreshUpThumbView() +{ + if( m_thumbview ) + { + /* readSettings returns true if something changes */ + if( m_thumbview->readSettings() ) + { + kdDebug(28000) << "Thumbview-Settings changed, readraw thumbs" << endl; + /* new settings */ + slShowThumbnails(0, true); + } + } +} + +void KookaView::createDockMenu( KActionCollection *col, KDockMainWindow *mainWin, const char * name ) +{ + KActionMenu *actionMenu = new KActionMenu( i18n("Tool Views"), "view_icon", col, name ); + + actionMenu->insert( new dwMenuAction( i18n("Show Image Viewer"), + KShortcut(), m_mainDock, col, + mainWin, "dock_viewer" )); + + actionMenu->insert( new dwMenuAction( i18n("Show Preview"), + KShortcut(), m_dockPreview, col, + mainWin, "dock_preview" )); + + actionMenu->insert( new dwMenuAction( i18n("Show Recent Gallery Folders"), + KShortcut(), m_dockRecent, col, + mainWin, "dock_recent" )); + actionMenu->insert( new dwMenuAction( i18n("Show Gallery"), + KShortcut(), m_dockPackager, col, + mainWin, "dock_gallery" )); + + actionMenu->insert( new dwMenuAction( i18n("Show Thumbnail Window"), + KShortcut(), m_dockThumbs, col, + mainWin, "dock_thumbs" )); + + actionMenu->insert( new dwMenuAction( i18n("Show Scan Parameters"), + KShortcut(), m_dockScanParam, col, + mainWin, "dock_scanparams" )); + + actionMenu->insert( new dwMenuAction( i18n("Show OCR Results"), + KShortcut(), m_dockOCRText, col, + mainWin, "dock_ocrResults" )); +} + + +#include "kookaview.moc" diff --git a/kooka/kookaview.h b/kooka/kookaview.h new file mode 100644 index 00000000..a1f7898a --- /dev/null +++ b/kooka/kookaview.h @@ -0,0 +1,241 @@ +/*************************************************************************** + kookaview.h - Main view + ------------------- + begin : Sun Jan 16 2000 + copyright : (C) 2000 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ +#ifndef KOOKAVIEW_H +#define KOOKAVIEW_H + +#include <qwidget.h> +#include <kopenwith.h> +#include "kookaiface.h" +#include <kdockwidget.h> +#include <qtabwidget.h> +#include <qlayout.h> +#include <qimage.h> +#include <qsplitter.h> + +#include <kparts/dockmainwindow.h> +#include <kparts/part.h> + +// application specific includes +#include "kscandevice.h" +#include "previewer.h" +#include "scanpackager.h" +#include "scanparams.h" +#include "img_canvas.h" + +class KDockWidget; +class QPainter; +class KSANEOCR; +class KConfig; +class KPrinter; +class KComboBox; +class KAction; +class KActionCollection; +class ThumbView; +class KookaImage; +class QPixmap; +class ocrResEdit; +/** + * This is the main view class for Kooka. Most of the non-menu, + * non-toolbar, and non-statusbar (e.g., non frame) GUI code should go + * here. + * + * @short Main view + * @author Klaas Freitag <freitag@suse.de> + * @version 0.1 + */ +class KookaView : public QObject +{ + Q_OBJECT +public: + typedef enum { MirrorVertical, MirrorHorizontal, MirrorBoth } MirrorType; + typedef enum { StatusTemp, StatusImage } StatusBarIDs; + + /** + * Default constructor + */ + KookaView(KParts::DockMainWindow *parent, const QCString& deviceToUse); + + /** + * Destructor + */ + virtual ~KookaView(); + + /** + * Print this view to any medium -- paper or not + */ + void print( ); + + bool ToggleVisibility( int ); + void loadStartupImage( void ); + KDockWidget *mainDockWidget( ) { return m_mainDock; } + + void createDockMenu( KActionCollection*, KDockMainWindow *, const char *); + + ScanPackager *gallery() { return packager; } + + // KParts::Part* ocrResultPart() { return m_textEdit; } + + ImageCanvas *getImageViewer() { return img_canvas; } +public slots: + void slShowPreview() { } + void slShowPackager() { } + void slNewPreview( QImage *, ImgScanInfo * ); + + void slSetScanParamsVisible( bool v ) + { if( v ) scan_params->show(); else scan_params->hide(); } + void slSetTabWVisible( bool v ) + { if( v ) preview_canvas->show(); else preview_canvas->hide(); } + + void doOCR( void ); + void doOCRonSelection( void ); + + void slStartPreview() { if( scan_params ) scan_params->slAcquirePreview(); } + void slStartFinalScan() { if( scan_params ) scan_params->slStartScan(); } + + void slCreateNewImgFromSelection( void ); + + void slRotateImage( int ); + + void slMirrorImage( MirrorType ); + + void slIVScaleToWidth( void ) + { if( img_canvas ) img_canvas->handle_popup(ImageCanvas::ID_FIT_WIDTH );} + void slIVScaleToHeight( void ) + { if( img_canvas ) img_canvas->handle_popup(ImageCanvas::ID_FIT_HEIGHT );} + void slIVScaleOriginal( void ) + { if( img_canvas ) img_canvas->handle_popup(ImageCanvas::ID_ORIG_SIZE ); } + void slIVShowZoomDialog( ) + { if( img_canvas ) img_canvas->handle_popup(ImageCanvas::ID_POP_ZOOM ); } + + void slOpenCurrInGraphApp( void ); + + void slSaveOCRResult(); + + void slLoadScanParams( ); + void slSaveScanParams( ); + + void slOCRResultImage( const QPixmap& ); + + void slShowThumbnails( KFileTreeViewItem *dirKfi = 0, bool forceRedraw=false); + void slFreshUpThumbView(); + + /** + * slot that show the image viewer + */ + void slStartLoading( const KURL& url ); + /** + * starts ocr on the image the parameter is pointing to + **/ + void startOCR( KookaImage* ); + + void slCloseScanDevice(); + void saveProperties( KConfig* ); + + /** + * slot to select the scanner device. Does all the work with selection + * of scanner, disconnection of the old device and connecting the new. + */ + bool slSelectDevice(const QCString& useDevice=QCString()); + + void connectViewerAction( KAction *action ); + void connectGalleryAction( KAction *action ); + + void slScanStart(); + void slScanFinished( KScanStat stat ); + void slAcquireStart(); + + +protected slots: + + void slShowAImage( KookaImage* ); + void slUnloadAImage( KookaImage* ); + + /** + * called from the scandevice if a new Image was successfully scanned. + * Needs to convert the one-page-QImage to a KookaImage + */ + void slNewImageScanned(QImage*, ImgScanInfo*); + + /** + * called if an viewer image was set to read only or back to read write state. + */ + void slViewerReadOnly( bool ro ); +signals: + /** + * Use this signal to change the content of the statusbar + */ + void signalChangeStatusbar(const QString& text); + + /** + * Use this signal to clean up the statusbar + */ + void signalCleanStatusbar( void ); + + /** + * Use this signal to change the content of the caption + */ + void signalChangeCaption(const QString& text); + +private: + QImage rotateRight( QImage* ); + QImage rotateLeft ( QImage* ); + QImage rotate180 ( QImage* ); + QCString userDeviceSelection( ) const; + + void updateCurrImage( QImage& ) ; + + ImageCanvas *img_canvas; + ThumbView *m_thumbview; + + Previewer *preview_canvas; + ScanPackager *packager; + ScanParams *scan_params; + + KScanDevice *sane; + KComboBox *recentFolder; + + QCString connectedDevice; + + QImage *m_ocrResultImg; + int image_pool_id; + int preview_id; + + KSANEOCR *ocrFabric; + + KDockWidget *m_mainDock; + KDockWidget *m_dockScanParam; + KDockWidget *m_dockThumbs; + KDockWidget *m_dockPackager; + KDockWidget *m_dockRecent; + KDockWidget *m_dockPreview; + KDockWidget *m_dockOCRText; + + KMainWindow *m_mainWindow; + + ocrResEdit *m_ocrResEdit; +}; + +#endif // KOOKAVIEW_H diff --git a/kooka/ksaneocr.cpp b/kooka/ksaneocr.cpp new file mode 100644 index 00000000..cf10d682 --- /dev/null +++ b/kooka/ksaneocr.cpp @@ -0,0 +1,1493 @@ +/*************************************************************************** + ksaneocr.cpp - generic ocr + ------------------- + begin : Fri Jun 30 2000 + copyright : (C) 2000 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +/* $Id$ */ + +#include <kdebug.h> +#include <kmessagebox.h> +#include <kconfig.h> +#include <kapplication.h> +#include <ktempfile.h> +#include <kprocess.h> +#include <stdlib.h> +#include <kspell.h> +#include <kspelldlg.h> +#include <qfile.h> +#include <qcolor.h> +#include <stdio.h> +#include <unistd.h> + +#include <img_canvas.h> + +#include "img_saver.h" +#include "kadmosocr.h" +#include "kocrbase.h" +#include "kocrkadmos.h" +#include "kocrocrad.h" +#include "config.h" +#include "ksaneocr.h" +#include "kocrgocr.h" +#include "kookaimage.h" +#include "kookapref.h" +#include "ocrword.h" + +#include <qtimer.h> +#include <qregexp.h> +#include <klocale.h> +#include <qpaintdevice.h> +#include <qpainter.h> +#include <qpen.h> +#include <qbrush.h> +#include <qfileinfo.h> + +/* + * Thread support is disabled here because the kadmos lib seems not to be + * thread save unfortunately. See slotKadmosResult-comments for more information + */ + +KSANEOCR::KSANEOCR( QWidget*, KConfig *cfg ): + m_ocrProcessDia(0L), + daemon(0L), + visibleOCRRunning(false), + m_resultImage(0), + m_imgCanvas(0L), + m_spell(0L), + m_wantKSpell(true), + m_kspellVisible(true), + m_hideDiaWhileSpellcheck(true), + m_spellInitialConfig(0L), + m_parent(0L), + m_ocrCurrLine(0), + m_currHighlight(-1), + m_applyFilter(false), + m_unlinkORF(true) +{ + KConfig *konf = KGlobal::config (); + m_ocrEngine = OCRAD; + m_img = 0L; + m_tmpFile = 0L; + + if( cfg ) + m_hideDiaWhileSpellcheck = cfg->readBoolEntry( HIDE_BASE_DIALOG, true ); + /* + * a initial config is needed as a starting point for the config dialog + * but also for ocr without visible dialog. + */ + m_spellInitialConfig = new KSpellConfig( 0L, 0L ,0L, false ); + + if( konf ) + { + /* -- ocr dialog information -- */ + konf->setGroup( CFG_GROUP_OCR_DIA ); + QString eng = konf->readEntry(CFG_OCR_ENGINE, "ocrad"); + + if( eng == "ocrad" ) + { + m_ocrEngine = OCRAD; + } + else if( eng == "gocr" ) + { + m_ocrEngine = GOCR; + } +#ifdef HAVE_KADMOS + else if( eng == QString("kadmos") ) m_ocrEngine = KADMOS; +#endif + kdDebug(28000) << "OCR engine is " << eng << endl; + + m_unlinkORF = konf->readBoolEntry( CFG_OCR_CLEANUP, true ); + } + + /* resize m_blocks to size 1 since there is at least one block */ + m_blocks.resize(1); + +} + + +KSANEOCR::~KSANEOCR() +{ + if( daemon ) { + delete( daemon ); + daemon = 0; + } + if ( m_tmpFile ) + { + m_tmpFile->setAutoDelete( true ); + delete m_tmpFile; + } + + if( m_resultImage ) + { + delete m_resultImage; + m_resultImage = 0; + } + + if( m_img ) delete m_img; + if( m_spellInitialConfig ) delete m_spellInitialConfig; +} + +/* + * This slot is called to introduce a new image, usually if the user clicks on a + * new image either in the gallery or on the thumbnailview. + */ +void KSANEOCR::slSetImage(KookaImage *img ) +{ + if( ! img ) return ; + + if( m_img ) + delete m_img; + + // FIXME: copy all the image is bad. + m_img = new KookaImage(*img); + + if( m_ocrProcessDia ) + { + m_ocrProcessDia->introduceImage( m_img ); + } + + m_applyFilter = false; +} + +/* + * Request to visualise a line-box in the source image, KADMOS Engine + */ +void KSANEOCR::slLineBox( const QRect& ) +{ + if( ! m_img ) return; +} + + +/* + * starts visual ocr process. Depending on the ocr engine, this function creates + * a new dialog, and shows it. + */ +bool KSANEOCR::startOCRVisible( QWidget *parent ) +{ + if( visibleOCRRunning ) return( false ); + bool res = true; + + m_parent = parent; + + if( m_ocrEngine == GOCR ) + { + m_ocrProcessDia = new KGOCRDialog ( parent, m_spellInitialConfig ); + } + else if( m_ocrEngine == OCRAD ) + { + m_ocrProcessDia = new ocradDialog( parent, m_spellInitialConfig ); + } + else if( m_ocrEngine == KADMOS ) + { +#ifdef HAVE_KADMOS +/*** Kadmos Engine OCR ***/ + m_ocrProcessDia = new KadmosDialog( parent, m_spellInitialConfig ); +#else + KMessageBox::sorry(0, i18n("This version of Kooka was not compiled with KADMOS support.\n" + "Please select another OCR engine in Kooka's options dialog.")); + kdDebug(28000) << "Sorry, this version of Kooka has no KADMOS support" << endl; +#endif /* HAVE_KADMOS */ + } + else + { + kdDebug(28000) << "ERR Unknown OCR engine requested!" << endl; + } + + /* + * this part is independant from the engine again + */ + if( m_ocrProcessDia ) + { + m_ocrProcessDia->setupGui(); + + m_ocrProcessDia->introduceImage( m_img ); + visibleOCRRunning = true; + + connect( m_ocrProcessDia, SIGNAL( user1Clicked()), this, SLOT( startOCRProcess() )); + connect( m_ocrProcessDia, SIGNAL( closeClicked()), this, SLOT( slotClose() )); + connect( m_ocrProcessDia, SIGNAL( user2Clicked()), this, SLOT( slotStopOCR() )); + m_ocrProcessDia->show(); + + } + return( res ); +} + +/** + * This method should be called by the engine specific finish slots. + * It does the not engine dependant cleanups like re-enabling buttons etc. + */ + +void KSANEOCR::finishedOCRVisible( bool success ) +{ + bool doSpellcheck = m_wantKSpell; + + if( m_ocrProcessDia ) + { + m_ocrProcessDia->stopOCR(); + doSpellcheck = m_ocrProcessDia->wantSpellCheck(); + } + + if( success ) + { + QString goof = ocrResultText(); + + emit newOCRResultText(goof); + + if( m_imgCanvas ) + { + if( m_resultImage != 0 ) delete m_resultImage; + kdDebug(28000) << "Result image name: " << m_ocrResultImage << endl; + m_resultImage = new QImage( m_ocrResultImage, "BMP" ); + kdDebug(28000) << "New result image has dimensions: " << m_resultImage->width() << "x" << m_resultImage->height()<< endl; + /* The image canvas is non-zero. Set it to our image */ + m_imgCanvas->newImageHoldZoom( m_resultImage ); + m_imgCanvas->setReadOnly(true); + + /* now handle double clicks to jump to the word */ + m_applyFilter=true; + } + + /** now it is time to invoke the dictionary if required **/ + emit readOnlyEditor( false ); + + if( doSpellcheck ) + { + m_ocrCurrLine = 0; + /* + * create a new kspell object, based on the config of the base dialog + */ + + connect( new KSpell( m_parent, i18n("Kooka OCR Dictionary Check"), + this, SLOT( slSpellReady(KSpell*)), + m_ocrProcessDia->spellConfig() ), + SIGNAL( death()), this, SLOT(slSpellDead())); + } + + delete m_ocrProcessDia; + m_ocrProcessDia = 0L; + + } + + visibleOCRRunning = false; + cleanUpFiles(); + + + kdDebug(28000) << "# ocr finished #" << endl; +} + +/* + * starting the spell check on line m_ocrCurrLine if the line exists. + * If not, the function returns. + */ +void KSANEOCR::startLineSpellCheck() +{ + if( m_ocrCurrLine < m_ocrPage.size() ) + { + m_checkStrings = (m_ocrPage[m_ocrCurrLine]).stringList(); + + /* In case the checklist is empty, call the result slot immediately */ + if( m_checkStrings.count() == 0 ) + { + slCheckListDone(false); + return; + } + + kdDebug(28000)<< "Wordlist (size " << m_ocrPage[m_ocrCurrLine].count() << ", line " << m_ocrCurrLine << "):" << m_checkStrings.join(", ") << endl; + + // if( list.count() > 0 ) + + m_spell->checkList( &m_checkStrings, m_kspellVisible ); + kdDebug(28000)<< "Started!" << endl; + /** + * This call ends in three slots: + * 1. slMisspelling: Hit _before_ the dialog (if any) appears. Time to + * mark the wrong word. + * 2. slSpellCorrected: Hit if the user decided which word to use. + * 3. slCheckListDone: The line is finished. The global counter needs to be + * increased and this function needs to be called again. + **/ + + } + else + { + kdDebug(28000) << k_funcinfo <<" -- no more lines !" << endl; + m_spell->cleanUp(); + } +} + + + +/* User Cancel is called when the user does not really start the + * ocr but uses the cancel-Button to come out of the Dialog */ +void KSANEOCR::slotClose() +{ + kdDebug(28000) << "closing ocr Dialog" << endl; + if( daemon && daemon->isRunning() ) + { + kdDebug(28000) << "Still running - Killing daemon with Sig. 9" << endl; + daemon->kill(9); + } + finishedOCRVisible(false); +} + +void KSANEOCR::slotStopOCR() +{ + kdDebug(28000) << "closing ocr Dialog" << endl; + if( daemon && daemon->isRunning() ) + { + kdDebug(28000) << "Killing daemon with Sig. 9" << endl; + daemon->kill(9); + // that leads to the process being destroyed. + KMessageBox::error(0, i18n("The OCR-process was stopped.") ); + } + +} + +void KSANEOCR::startOCRAD( ) +{ + ocradDialog *ocrDia = static_cast<ocradDialog*>(m_ocrProcessDia); + + m_ocrResultImage = ocrDia->orfUrl(); + const QString cmd = ocrDia->getOCRCmd(); + + // if( m_ocrResultImage.isEmpty() ) + { + /* The url is empty. Start the program to fill up a temp file */ + m_ocrResultImage = ImgSaver::tempSaveImage( m_img, "BMP", 8 ); // m_tmpFile->name(); + kdDebug(28000) << "The new image name is <" << m_ocrResultImage << ">" << endl; + } + + m_ocrImagePBM = ImgSaver::tempSaveImage( m_img, "PBM", 1 ); + + /* temporar file for orf result */ + KTempFile *tmpOrf = new KTempFile( QString(), ".orf" ); + tmpOrf->setAutoDelete( false ); + tmpOrf->close(); + m_tmpOrfName = QFile::encodeName(tmpOrf->name()); + + + if( daemon ) + { + delete( daemon ); + daemon = 0; + } + + daemon = new KProcess; + Q_CHECK_PTR(daemon); + + *daemon << cmd; + *daemon << QString("-x"); + *daemon << m_tmpOrfName; // the orf result file + *daemon << QFile::encodeName( m_ocrImagePBM ); // The name of the image + *daemon << QString("-l"); + *daemon << QString::number( ocrDia->layoutDetectionMode()); + + KConfig *konf = KGlobal::config (); + KConfigGroupSaver( konf, CFG_GROUP_OCRAD ); + + QString format = konf->readEntry( CFG_OCRAD_FORMAT, "utf8"); + *daemon << QString("-F"); + *daemon << format; + + QString charset = konf->readEntry( CFG_OCRAD_CHARSET, "iso-8859-15"); + *daemon << QString("-c"); + *daemon << charset; + + + QString addArgs = konf->readEntry( CFG_OCRAD_EXTRA_ARGUMENTS, QString() ); + + if( !addArgs.isEmpty() ) + { + kdDebug(28000) << "Setting additional args from config for ocrad: " << addArgs << endl; + *daemon << addArgs; + } + + m_ocrResultText = ""; + + connect(daemon, SIGNAL(processExited(KProcess *)), + this, SLOT( ocradExited(KProcess*))); + connect(daemon, SIGNAL(receivedStdout(KProcess *, char*, int)), + this, SLOT( ocradStdIn(KProcess*, char*, int))); + connect(daemon, SIGNAL(receivedStderr(KProcess *, char*, int)), + this, SLOT( ocradStdErr(KProcess*, char*, int))); + + if (!daemon->start(KProcess::NotifyOnExit, KProcess::All)) + { + kdDebug(28000) << "Error starting ocrad-daemon!" << endl; + } + else + { + kdDebug(28000) << "Start OK" << endl; + + } + delete tmpOrf; +} + + +void KSANEOCR::ocradExited(KProcess* ) +{ + kdDebug(28000) << "ocrad exit " << endl; + QString err; + bool parseRes = true; + + if( ! readORF(m_tmpOrfName, err) ) + { + KMessageBox::error( m_parent, + i18n("Parsing of the OCR Result File failed:") + err, + i18n("Parse Problem")); + parseRes = false; + } + finishedOCRVisible( parseRes ); + +} + +void KSANEOCR::ocradStdErr(KProcess*, char* buffer, int buflen) +{ + QString errorBuffer = QString::fromLocal8Bit(buffer, buflen); + kdDebug(28000) << "ocrad says on stderr: " << errorBuffer << endl; + +} + +void KSANEOCR::ocradStdIn(KProcess*, char* buffer, int buflen) +{ + QString errorBuffer = QString::fromLocal8Bit(buffer, buflen); + kdDebug(28000) << "ocrad says on stdin: " << errorBuffer << endl; +} + + + + +/* + * This slot is fired if the user clicks on the 'Start' button of the GUI, no + * difference which engine is active. + */ +void KSANEOCR::startOCRProcess( void ) +{ + if( ! m_ocrProcessDia ) return; + + /* starting the animation, setting fields disabled */ + m_ocrProcessDia->startOCR(); + + kapp->processEvents(); + if( m_ocrEngine == OCRAD ) + { + startOCRAD(); + } + + if( m_ocrEngine == GOCR ) + { + /* + * Starting a gocr process + */ + + KGOCRDialog *gocrDia = static_cast<KGOCRDialog*>(m_ocrProcessDia); + + const QString cmd = gocrDia->getOCRCmd(); + + /* Save the image to a temp file */ + + /** + * Save images formats: + * Black&White: PBM + * Gray: PGM + * Bunt: PPM + */ + QString format; + if( m_img->depth() == 1 ) + format = "PBM"; + else if( m_img->isGrayscale() ) + format = "PGM"; + else + format = "PPM"; + + QString tmpFile = ImgSaver::tempSaveImage( m_img, format ); // m_tmpFile->name(); + + kdDebug(28000) << "Starting GOCR-Command: " << cmd << " on file " << tmpFile + << ", format " << format << endl; + + if( daemon ) { + delete( daemon ); + daemon = 0; + } + + daemon = new KProcess; + Q_CHECK_PTR(daemon); + m_ocrResultText = ""; + + connect(daemon, SIGNAL(processExited(KProcess *)), + this, SLOT( gocrExited(KProcess*))); + connect(daemon, SIGNAL(receivedStdout(KProcess *, char*, int)), + this, SLOT( gocrStdIn(KProcess*, char*, int))); + connect(daemon, SIGNAL(receivedStderr(KProcess *, char*, int)), + this, SLOT( gocrStdErr(KProcess*, char*, int))); + + QString opt; + *daemon << QFile::encodeName(cmd); + *daemon << "-x"; + *daemon << "-"; + if( !( m_img->numColors() > 0 && m_img->numColors() <3 )) /* not a bw-image */ + { + *daemon << "-l"; + opt.setNum(gocrDia->getGraylevel()); + *daemon << opt; + } + *daemon << "-s"; + opt.setNum(gocrDia->getSpaceWidth()); + *daemon << opt; + *daemon << "-d"; + opt.setNum(gocrDia->getDustsize()); + *daemon << opt; + + // Write an result image + *daemon << "-v"; + *daemon << "32"; + + // Unfortunately this is fixed by gocr. + m_ocrResultImage = "out30.bmp"; + + *daemon << QFile::encodeName(tmpFile); + + m_ocrCurrLine = 0; // Important in gocrStdIn to store the results + + if (!daemon->start(KProcess::NotifyOnExit, KProcess::All)) + { + kdDebug(28000) << "Error starting daemon!" << endl; + } + else + { + kdDebug(28000) << "Start OK" << endl; + + } + } +#ifdef HAVE_KADMOS + if( m_ocrEngine == KADMOS ) + { + KadmosDialog *kadDia = static_cast<KadmosDialog*>(m_ocrProcessDia); + + kdDebug(28000) << "Starting Kadmos OCR Engine" << endl; + + QString clasPath; /* target where the clasPath is written in */ + if( ! kadDia->getSelClassifier( clasPath ) ) + { + KMessageBox::error( m_parent, + i18n("The classifier file necessary for OCR cannot be loaded: %1;\n" + "OCR with the KADMOS engine is not possible." ). + arg(clasPath), i18n("KADMOS Installation Problem")); + finishedOCRVisible(false); + return; + } + QCString c = clasPath.latin1(); + + kdDebug(28000) << "Using classifier " << c << endl; + m_rep.Init( c ); + if( m_rep.kadmosError() ) /* check if kadmos initialised OK */ + { + KMessageBox::error( m_parent, + i18n("The KADMOS OCR system could not be started:\n") + + m_rep.getErrorText()+ + i18n("\nPlease check the configuration." ), + i18n("KADMOS Failure") ); + } + else + { + /** Since initialising succeeded, we start the ocr here **/ + m_rep.SetNoiseReduction( kadDia->getNoiseReduction() ); + m_rep.SetScaling( kadDia->getAutoScale() ); + kdDebug(28000) << "Image size " << m_img->width() << " x " << m_img->height() << endl; + kdDebug(28000) << "Image depth " << m_img->depth() << ", colors: " << m_img->numColors() << endl; +#define USE_KADMOS_FILEOP /* use a save-file for OCR instead of filling the reImage struct manually */ +#ifdef USE_KADMOS_FILEOP + m_tmpFile = new KTempFile( QString(), QString("bmp")); + m_tmpFile->setAutoDelete( false ); + m_tmpFile->close(); + QString tmpFile = m_tmpFile->name(); + kdDebug() << "Saving to file " << tmpFile << endl; + m_img->save( tmpFile, "BMP" ); + m_rep.SetImage(tmpFile); +#else + m_rep.SetImage(m_img); +#endif + // rep.Recognize(); + m_rep.run(); + + /* Dealing with threads or no threads (using QT_THREAD_SUPPORT to distinguish) + * If threads are here, the recognition task is started in its own thread. The gui thread + * needs to wait until the recognition thread is finished. Therefore, a timer is fired once + * that calls slotKadmosResult and checks if the recognition task is finished. If it is not, + * a new one-shot-timer is fired in slotKadmosResult. If it is, the OCR result can be + * processed. + * In case the system has no threads, the method start of the recognition engine does not + * return until it is ready, the user has to live with a non responsive gui while + * recognition is performed. The start()-method is implemented as a wrapper to the run() + * method of CRep, which does the recognition job. Instead of pulling up a timer, simply + * the result slot is called if start()=run() has finished. In the result slot, finished() + * is only a dummy always returning true to avoid more preprocessor tags here. + * Hope that works ... + * It does not :( That is why it is not used here. Maybe some day... + */ + } +#ifdef QT_THREAD_SUPPORT + /* start a timer and wait until it fires. */ + QTimer::singleShot( 500, this, SLOT( slotKadmosResult() )); +#else + slotKadmosResult(); +#endif + + } +#endif /* HAVE_KADMOS */ +} + +/* + * This method is called to check if the kadmos process was already finished, if + * thread support is enabled (check for preprocessor variable QT_THREAD_SUPPORT) + * The problem is that the kadmos library seems not to be thread stable so thread + * support should not be enabled by default. In case threads are enabled, this slot + * checks if the KADMOS engine is finished already and if not it fires a timer. + */ + +void KSANEOCR::slotKadmosResult() +{ +#ifdef HAVE_KADMOS + kdDebug(28000) << "check for Recognition finished" << endl; + + + if( m_rep.finished() ) + { + /* The recognition thread is finished. */ + kdDebug(28000) << "kadmos is finished." << endl; + + m_ocrResultText = ""; + if( ! m_rep.kadmosError() ) + { + int lines = m_rep.GetMaxLine(); + kdDebug(28000) << "Count lines: " << lines << endl; + m_ocrPage.clear(); + m_ocrPage.resize( lines ); + + for( int line = 0; line < m_rep.GetMaxLine(); line++ ) + { + // ocrWordList wordList = m_rep.getLineWords(line); + /* call an ocr engine independent method to use the spellbook */ + ocrWordList words = m_rep.getLineWords(line); + kdDebug(28000) << "Have " << words.count() << " entries in list" << endl; + m_ocrPage[line]=words; + } + + /* show results of ocr */ + m_rep.End(); + } + finishedOCRVisible( !m_rep.kadmosError() ); + + } + else + { + /* recognition thread is not yet finished. Wait another half a second. */ + QTimer::singleShot( 500, this, SLOT( slotKadmosResult() )); + /* Never comes here if no threads exist on the system */ + } +#endif /* HAVE_KADMOS */ +} + + + + +/* + * + */ +void KSANEOCR::gocrExited(KProcess* d) +{ + kdDebug(28000) << "daemonExited start !" << endl; + + /* Now all the text of gocr is in the member m_ocrResultText. This one must + * be split up now to m_ocrPage. First break up the lines, resize m_ocrPage + * accordingly and than go through every line and create ocrwords for every + * word. + */ + QStringList lines = QStringList::split( '\n', m_ocrResultText, true ); + + m_ocrPage.clear(); + m_ocrPage.resize( lines.count() ); + + kdDebug(28000) << "RESULT " << m_ocrResultText << " was splitted to lines " << lines.count() << endl; + + unsigned int lineCnt = 0; + + for ( QStringList::Iterator it = lines.begin(); it != lines.end(); ++it ) + { + kdDebug(28000) << "Splitting up line " << *it << endl; + ocrWordList ocrLine; + + QStringList words = QStringList::split( QRegExp( "\\s+" ), *it, false ); + for ( QStringList::Iterator itWord = words.begin(); itWord != words.end(); ++itWord ) + { + kdDebug(28000) << "Appending to results: " << *itWord << endl; + ocrLine.append( ocrWord( *itWord )); + } + m_ocrPage[lineCnt] = ocrLine; + lineCnt++; + } + kdDebug(28000) << "Finished to split!" << endl; + /* set the result pixmap to the result pix of gocr */ + if( ! m_resPixmap.load( m_ocrResultImage ) ) + { + kdDebug(28000) << "Can not load result image!" << endl; + } + + /* load the gocr result image */ + if( m_img ) delete m_img; + m_img = new KookaImage(); + m_img->load( "out30.bmp" ); + + finishedOCRVisible( d->normalExit() ); +} + +/* + * A sample orf snippet: + * + * # Ocr Results File. Created by GNU ocrad version 0.3pre1 + * total blocks 2 + * block 1 0 0 560 344 + * lines 5 + * line 1 chars 10 height 26 + * 71 109 17 26;2,'0'1,'o'0 + * 93 109 15 26;2,'1'1,'l'0 + * 110 109 18 26;1,'2'0 + * 131 109 18 26;1,'3'0 + * 151 109 19 26;1,'4'0 + * 172 109 17 26;1,'5'0 + * 193 109 17 26;1,'6'0 + * 213 108 17 27;1,'7'0 + * 232 109 18 26;1,'8'0 + * 253 109 17 26;1,'9'0 + * line 2 chars 14 height 27 + * + */ + +bool KSANEOCR::readORF( const QString& fileName, QString& errStr ) +{ + QFile file( fileName ); + QRegExp rx; + bool error = false; + + /* use a global line number counter here, not the one from the orf. The orf one + * starts at 0 for every block, but we want line-no counting page global here. + */ + unsigned int lineNo = 0; + int blockCnt = 0; + int currBlock = -1; + + + /* Fetch the numeric version of ocrad */ + ocradDialog *ocrDia = static_cast<ocradDialog*>(m_ocrProcessDia); + int ocradVersion = 0; + if( ocrDia ) + { + ocradVersion = ocrDia->getNumVersion(); + } + + /* clear the ocr result page */ + m_ocrPage.clear(); + kdDebug(28000) << "***** starting to analyse orf at " << fileName << " *****" << endl; + + /* some checks on the orf */ + QFileInfo fi( fileName ); + if( ! fi.exists() ) { + error = true; + errStr = i18n("The orf %1 does not exist.").arg(fileName); + } + if( ! error && ! fi.isReadable() ) { + error = true; + errStr = i18n("Permission denied on file %1.").arg(fileName); + } + + + if ( !error && file.open( IO_ReadOnly ) ) + { + QTextStream stream( &file ); + QString line; + QString recLine; // recognised line + + while ( !stream.atEnd() ) + { + line = stream.readLine().stripWhiteSpace(); // line of text excluding '\n' + int len = line.length(); + + if( ! line.startsWith( "#" )) // Comments + { + kdDebug(28000) << "# Line check |" << line << "|" << endl; + if( line.startsWith( "total blocks " ) ) // total count fo blocks, must be first line + { + blockCnt = line.right( len - 13 /* QString("total blocks ").length() */ ).toInt(); + kdDebug(28000) << "Amount of blocks: " << blockCnt << endl; + m_blocks.resize(blockCnt); + } + else if( line.startsWith( "total text blocks " )) + { + blockCnt = line.right( len - 18 /* QString("total text blocks ").length() */ ).toInt(); + kdDebug(28000) << "Amount of blocks (V. 10): " << blockCnt << endl; + m_blocks.resize(blockCnt); + } + else if( line.startsWith( "block ") || line.startsWith( "text block ") ) + { + rx.setPattern("^.*block\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)"); + if( rx.search( line ) > -1) + { + currBlock = (rx.cap(1).toInt())-1; + kdDebug(28000) << "Setting current block " << currBlock << endl; + QRect r( rx.cap(2).toInt(), rx.cap(3).toInt(), rx.cap(4).toInt(), rx.cap(5).toInt()); + m_blocks[currBlock] = r; + } + else + { + kdDebug(28000) << "WRN: Unknown block line: " << line << endl; + // Not a killing bug + } + } + else if( line.startsWith( "lines " )) + { + int lineCnt = line.right( len - 6 /* QString("lines ").length() */).toInt(); + m_ocrPage.resize(m_ocrPage.size()+lineCnt); + kdDebug(28000) << "Resized ocrPage to linecount " << lineCnt << endl; + } + else if( line.startsWith( "line" )) + { + // line 5 chars 13 height 20 + rx.setPattern("^line\\s+(\\d+)\\s+chars\\s+(\\d+)\\s+height\\s+\\d+" ); + if( rx.search( line )>-1 ) + { + kdDebug(28000) << "RegExp-Result: " << rx.cap(1) << " : " << rx.cap(2) << endl; + int charCount = rx.cap(2).toInt(); + ocrWord word; + QRect brect; + ocrWordList ocrLine; + ocrLine.setBlock(currBlock); + /* Loop over all characters in the line. Every char has it's own line + * defined in the orf file */ + kdDebug(28000) << "Found " << charCount << " chars for line " << lineNo << endl; + + for( int c=0; c < charCount && !stream.atEnd(); c++ ) + { + /* Read one line per character */ + QString charLine = stream.readLine(); + int semiPos = charLine.find(';'); + if( semiPos == -1 ) + { + kdDebug(28000) << "invalid line: " << charLine << endl; + } + else + { + QString rectStr = charLine.left( semiPos ); + QString results = charLine.remove(0, semiPos+1 ); + bool lineErr = false; + + // rectStr contains the rectangle info of for the character + // results contains the real result caracter + + // find the amount of alternatives. + int altCount = 0; + int h = results.find(','); // search the first comma + if( h > -1 ) { + // kdDebug(28000) << "Results of count search: " << results.left(h) << endl; + altCount = results.left(h).toInt(); + results = results.remove( 0, h+1 ).stripWhiteSpace(); + } else { + lineErr = true; + } + // kdDebug(28000) << "Results-line after cutting the alter: " << results << endl; + QChar detectedChar = UndetectedChar; + if( !lineErr ) + { + /* take the first alternative only FIXME */ + if( altCount > 0 ) + detectedChar = results[1]; + // kdDebug(28000) << "Found " << altCount << " alternatives for " + // << QString(detectedChar) << endl; + } + + /* Analyse the rectangle */ + if( ! lineErr && detectedChar != ' ' ) + { + // kdDebug(28000) << "STRING: " << rectStr << "<" << endl; + rx.setPattern( "(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)"); + if( rx.search( rectStr ) != -1 ) + { + /* unite the rectangles */ + QRect privRect( rx.cap(1).toInt(), rx.cap(2).toInt(), + rx.cap(3).toInt(), rx.cap(4).toInt() ); + word.setRect( word.rect() | privRect ); + } + else + { + kdDebug(28000) << "ERR: Unable to read rect info for char!" << endl; + lineErr = true; + } + } + + if( ! lineErr ) + { + /* store word if finished by a space */ + if( detectedChar == ' ' ) + { + /* add the block offset to the rect of the word */ + QRect r = word.rect(); + if( ocradVersion < 10 ) + { + QRect blockRect = m_blocks[currBlock]; + r.moveBy( blockRect.x(), blockRect.y()); + } + + word.setRect( r ); + ocrLine.append( word ); + word = ocrWord(); + } + else + { + word.append( detectedChar ); + } + } + } + } + if( !word.isEmpty() ) + { + /* add the block offset to the rect of the word */ + QRect r = word.rect(); + if( ocradVersion < 10 ) + { + QRect blockRect = m_blocks[currBlock]; + r.moveBy( blockRect.x(), blockRect.y()); + } + word.setRect( r ); + + ocrLine.append( word ); + } + if( lineNo < m_ocrPage.size() ) + { + kdDebug(29000) << "Store result line no " << lineNo << "=\"" << + ocrLine.first() << "..." << endl; + m_ocrPage[lineNo] = ocrLine; + lineNo++; + } + else + { + kdDebug(28000) << "ERR: line index overflow: " << lineNo << endl; + } + } + else + { + kdDebug(28000) << "ERR: Unknown line found: " << line << endl; + } + } + else + { + kdDebug(29000) << "Unknown line: " << line << endl; + } + } /* is a comment? */ + + } + file.close(); + } + return !error; +} + + +void KSANEOCR::cleanUpFiles( void ) +{ + if( m_tmpFile ) + { + delete m_tmpFile; + m_tmpFile = 0L; + } + + if( ! m_ocrResultImage.isEmpty()) + { + kdDebug(28000) << "Unlinking OCR Result image file!" << endl; + unlink(QFile::encodeName(m_ocrResultImage)); + m_ocrResultImage = QString(); + } + + if( ! m_ocrImagePBM.isEmpty()) + { + kdDebug(28000) << "Unlinking OCR PBM file!" << endl; + unlink( QFile::encodeName(m_ocrImagePBM)); + m_ocrImagePBM = QString(); + } + + if( ! m_tmpOrfName.isEmpty() ) + { + if( m_unlinkORF ) + { + unlink(QFile::encodeName(m_tmpOrfName)); + m_tmpOrfName = QString(); + } + else + { + kdDebug(28000) << "Do NOT unlink temp orf file " << m_tmpOrfName << endl; + } + } + + /* Delete the debug images of gocr ;) */ + unlink( "out20.bmp" ); +} + + +void KSANEOCR::gocrStdErr(KProcess*, char* buffer, int buflen) +{ + QString errorBuffer = QString::fromLocal8Bit(buffer, buflen); + kdDebug(28000) << "gocr says: " << errorBuffer << endl; + +} + + +void KSANEOCR::gocrStdIn(KProcess*, char* buffer, int buflen) +{ + QString aux = QString::fromLocal8Bit(buffer, buflen); + + QRegExp rx( "^\\s*\\d+\\s+\\d+"); + if( rx.search( aux ) > -1 ) + { + /* calculate ocr progress for gocr */ + int progress = rx.capturedTexts()[0].toInt(); + int subProgress = rx.capturedTexts()[1].toInt(); + // kdDebug(28000) << "Emitting progress: " << progress << endl; + emit ocrProgress( progress, subProgress ); + } + else + { + m_ocrResultText += aux; + } + + // kdDebug(28000) << aux << endl; + +} + +/* + * Assemble the result text + */ +QString KSANEOCR::ocrResultText() +{ + QString res; + const QString space(" "); + + /* start from the back and search the original word to replace it */ + QValueVector<ocrWordList>::iterator pageIt; + + for( pageIt = m_ocrPage.begin(); pageIt != m_ocrPage.end(); ++pageIt ) + { + /* thats goes over all lines */ + QValueList<ocrWord>::iterator lineIt; + for( lineIt = (*pageIt).begin(); lineIt != (*pageIt).end(); ++lineIt ) + { + res += space + *lineIt; + } + res += "\n"; + } + kdDebug(28000) << "Returning result String " << res << endl; + return res; +} + + +/* -------------------------------------------------------------------------------- + * event filter to filter the mouse events to the image viewer + */ + +void KSANEOCR::setImageCanvas( ImageCanvas *canvas ) +{ + m_imgCanvas = canvas; + + m_imgCanvas->installEventFilter( this ); +} + + +bool KSANEOCR::eventFilter( QObject *object, QEvent *event ) +{ + QWidget *w = (QWidget*) object; + + if( m_applyFilter && m_imgCanvas && w == m_imgCanvas ) + { + if( event->type() == QEvent::MouseButtonDblClick ) + { + QMouseEvent *mev = static_cast<QMouseEvent*>(event); + + int x = mev->x(); + int y = mev->y(); + int scale = m_imgCanvas->getScaleFactor(); + + m_imgCanvas->viewportToContents( mev->x(), mev->y(), + x, y ); + + kdDebug(28000) << "Clicked to " << x << "/" << y << ", scale " << scale << endl; + if( scale != 100 ) + { + // Scale is e.g. 50 that means tha the image is only half of size. + // thus the clicked coords must be multiplied with 2 + y = int(double(y)*100/scale); + x = int(double(x)*100/scale); + } + /* now search the word that was clicked on */ + QValueVector<ocrWordList>::iterator pageIt; + + int line = 0; + bool valid = false; + ocrWord wordToFind; + + for( pageIt = m_ocrPage.begin(); pageIt != m_ocrPage.end(); ++pageIt ) + { + QRect r = (*pageIt).wordListRect(); + + if( y > r.top() && y < r.bottom() ) + { + kdDebug(28000)<< "It is in between " << r.top() << "/" << r.bottom() + << ", line " << line << endl; + valid = true; + break; + } + line++; + } + + /* + * If valid, we have the line into which the user clicked. Now we + * have to find out the actual word + */ + if( valid ) + { + valid = false; + /* find the word in the line and mark it */ + ocrWordList words = *pageIt; + ocrWordList::iterator wordIt; + + for( wordIt = words.begin(); wordIt != words.end() && !valid; ++wordIt ) + { + QRect r = (*wordIt).rect(); + if( x > r.left() && x < r.right() ) + { + wordToFind = *wordIt; + valid = true; + } + } + + } + + /* + * if valid, the wordToFind contains the correct word now. + */ + if( valid ) + { + kdDebug(28000) << "Found the clicked word " << wordToFind << endl; + emit selectWord( line, wordToFind ); + } + + return true; + } + } + return false; +} + + + +/* -------------------------------------------------------------------------------- + * Spellbook support + */ + + +/* + * This slot is hit when the checkWord method of KSpell thinks a word is wrong. + * KSpell detects the correction by itself and delivers it in newword here. + * To see all alternatives KSpell proposes, slMissspelling must be used. + */ +void KSANEOCR::slSpellCorrected( const QString& originalword, + const QString& newword, + unsigned int pos ) +{ + kdDebug(28000) << "Corrected: Original Word " << originalword << " was corrected to " + << newword << ", pos ist " << pos << endl; + + kdDebug(28000) << "Dialog state is " << m_spell->dlgResult() << endl; + + if( slUpdateWord( m_ocrCurrLine, pos, originalword, newword ) ) + { + if( m_imgCanvas && m_currHighlight > -1 ) + { + if( m_applyFilter ) + m_imgCanvas->removeHighlight( m_currHighlight ); + } + else + { + kdDebug(28000) << "No highlighting to remove!" << endl; + } + } + +} + + +void KSANEOCR::slSpellIgnoreWord( const QString& word ) +{ + ocrWord ignoreOCRWord; + + ignoreOCRWord = ocrWordFromKSpellWord( m_ocrCurrLine, word ); + if( ! ignoreOCRWord.isEmpty() ) + { + emit ignoreWord( m_ocrCurrLine, ignoreOCRWord ); + + if( m_imgCanvas && m_currHighlight > -1 ) + { + m_imgCanvas->removeHighlight( m_currHighlight ); + + /* create a new highlight. That will never be removed */ + QBrush brush; + QPen pen( gray, 1 ); + QRect r = ignoreOCRWord.rect(); + r.moveBy(0,2); // a bit offset to the top + + if( m_applyFilter ) + m_imgCanvas->highlight( r, pen, brush ); + } + } +} + +ocrWord KSANEOCR::ocrWordFromKSpellWord( int line, const QString& word ) +{ + ocrWord resWord; + if( lineValid(line) ) + { + ocrWordList words = m_ocrPage[line]; + + words.findFuzzyIndex( word, resWord ); + } + + return resWord; +} + + +bool KSANEOCR::lineValid( int line ) +{ + bool ret = false; + + if( line >= 0 && (uint)line < m_ocrPage.count() ) + ret = true; + + return ret; +} + +void KSANEOCR::slMisspelling( const QString& originalword, const QStringList& suggestions, + unsigned int pos ) +{ + /* for the first try, use the first suggestion */ + ocrWord s( suggestions.first()); + kdDebug(28000) << "Misspelled: " << originalword << " at position " << pos << endl; + + int line = m_ocrCurrLine; + m_currHighlight = -1; + + // ocrWord resWord = ocrWordFromKSpellWord( line, originalword ); + ocrWordList words = m_ocrPage[line]; + ocrWord resWord; + kdDebug(28000) << "Size of wordlist (line " << line << "): " << words.count() << endl; + + if( pos < words.count() ) + { + resWord = words[pos]; + } + + if( ! resWord.isEmpty() ) + { + QBrush brush; + brush.setColor( QColor(red)); // , "Dense4Pattern" ); + brush.setStyle( Qt::Dense4Pattern ); + QPen pen( red, 2 ); + QRect r = resWord.rect(); + + r.moveBy(0,2); // a bit offset to the top + + if( m_applyFilter ) + m_currHighlight = m_imgCanvas->highlight( r, pen, brush, true ); + + kdDebug(28000) << "Position ist " << r.x() << ", " << r.y() << ", width: " + << r.width() << ", height: " << r.height() << endl; + + /* draw a line under the word to check */ + + /* copy the source */ + emit repaintOCRResImage(); + } + else + { + kdDebug(28000) << "Could not find the ocrword for " << originalword << endl; + } + + emit markWordWrong( line, resWord ); +} + +/* + * This is the global starting point for spell checking of the ocr result. + * After the KSpell object was created in method finishedOCRVisible, this + * slot is called if the KSpell-object feels itself ready for operation. + * Coming into this slot, the spelling starts in a line by line manner + */ +void KSANEOCR::slSpellReady( KSpell *spell ) +{ + m_spell = spell; + connect ( m_spell, SIGNAL( misspelling( const QString&, const QStringList&, + unsigned int )), + this, SLOT( slMisspelling(const QString& , + const QStringList& , + unsigned int ))); + connect( m_spell, SIGNAL( corrected ( const QString&, const QString&, unsigned int )), + this, SLOT( slSpellCorrected( const QString&, const QString&, unsigned int ))); + + connect( m_spell, SIGNAL( ignoreword( const QString& )), + this, SLOT( slSpellIgnoreWord( const QString& ))); + + connect( m_spell, SIGNAL( done(bool)), this, SLOT(slCheckListDone(bool))); + + kdDebug(28000) << "Spellcheck available" << endl; + + if( m_ocrProcessDia && m_hideDiaWhileSpellcheck ) + m_ocrProcessDia->hide(); + emit readOnlyEditor( true ); + startLineSpellCheck(); +} + +/** + * slot called after either the spellcheck finished or the KSpell object found + * out that it does not want to run because of whatever problems came up. + * If it is an KSpell-init problem, the m_spell variable is still zero and + * Kooka pops up a warning. + */ +void KSANEOCR::slSpellDead() +{ + if( ! m_spell ) + { + kdDebug(28000) << "Spellcheck NOT available" << endl; + /* Spellchecking has not yet been existing, thus there is a base problem with + * spellcheck on this system. + */ + KMessageBox::error( m_parent, + i18n("Spell-checking cannot be started on this system.\n" + "Please check the configuration" ), + i18n("Spell-Check") ); + + } + else + { + if( m_spell->status() == KSpell::Cleaning ) + { + kdDebug(28000) << "KSpell cleans up" << endl; + } + else if( m_spell->status() == KSpell::Finished ) + { + kdDebug(28000) << "KSpell finished" << endl; + } + else if( m_spell->status() == KSpell::Error ) + { + kdDebug(28000) << "KSpell finished with Errors" << endl; + } + else if( m_spell->status() == KSpell::Crashed ) + { + kdDebug(28000) << "KSpell Chrashed" << endl; + } + else + { + kdDebug(28000) << "KSpell finished with unknown state!" << endl; + } + + /* save the current config */ + delete m_spell; + m_spell = 0L; + + /* reset values */ + m_checkStrings.clear(); + m_ocrCurrLine = 0; + if( m_imgCanvas && m_currHighlight > -1 ) + m_imgCanvas->removeHighlight( m_currHighlight ); + + } + if( m_ocrProcessDia ) + m_ocrProcessDia->show(); + emit readOnlyEditor( false ); +} + + +/** + * This slot reads the current line from the member m_ocrCurrLine and + * writes the corrected wordlist to the member page word lists + */ +void KSANEOCR::slCheckListDone(bool) +{ + + /* + * nothing needs to be updated here in the texts, because it is already done + * in the slSpellCorrected slot + */ + + /* Check the dialog state here */ + if( m_spell->dlgResult() == KS_CANCEL || + m_spell->dlgResult() == KS_STOP ) + { + /* stop processing */ + m_spell->cleanUp(); + } + else + { + m_ocrCurrLine++; + kdDebug(28000) << "Starting spellcheck from CheckListDone" << endl; + startLineSpellCheck(); + } +} + +/** + * updates the word at position spellWordIndx in line line to the new word newWord. + * The original word was origWord. This slot is called from slSpellCorrected + * + */ +bool KSANEOCR::slUpdateWord( int line, int spellWordIndx, const QString& origWord, + const QString& newWord ) +{ + bool result = false; + + if( lineValid( line )) + { + ocrWordList words = m_ocrPage[line]; + kdDebug(28000) << "Updating word " << origWord << " to " << newWord << endl; + + if( words.updateOCRWord( words[spellWordIndx] /* origWord */, newWord ) ) // searches for the word and updates + { + result = true; + emit updateWord( line, origWord, newWord ); + } + else + kdDebug(28000) << "WRN: Update from " << origWord << " to " << newWord << " failed" << endl; + + } + else + { + kdDebug(28000) << "WRN: Line " << line << " no not valid!" << endl; + } + return result; +} + + +char KSANEOCR::UndetectedChar = '_'; + +/* -- */ +#include "ksaneocr.moc" diff --git a/kooka/ksaneocr.h b/kooka/ksaneocr.h new file mode 100644 index 00000000..425718dc --- /dev/null +++ b/kooka/ksaneocr.h @@ -0,0 +1,285 @@ +/*************************************************************************** + ksaneocr.h - ocr-engine class + ------------------- + begin : Fri Jun 30 2000 + copyright : (C) 2000 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#ifndef KSANEOCR_H +#define KSANEOCR_H +#include <qwidget.h> +#include <qobject.h> + +#include "ocrword.h" + +#define CFG_OCR_ENGINE "ocrEngine" +#define CFG_OCR_CLEANUP "unlinkORF" /* delete orf file? */ + +#define CFG_OCR_KSPELL "ocrSpellSettings" +#define CFG_WANT_KSPELL "ocrKSpellEnabled" +#define CFG_KS_NOROOTAFFIX "KSpell_NoRootAffix" +#define CFG_KS_RUNTOGETHER "KSpell_RunTogether" +#define CFG_KS_DICTIONARY "KSpell_Dictionary" +#define CFG_KS_DICTFROMLIST "KSpell_DictFromList" +#define CFG_KS_ENCODING "KSpell_Encoding" +#define CFG_KS_CLIENT "KSpell_Client" + + +#define HIDE_BASE_DIALOG "hideOCRDialogWhileSpellCheck" +/** + *@author Klaas Freitag + */ + +class KOCRBase; +class KookaImage; +class KTempFile; +class KProcess; +class QRect; +class QPixmap; +class QStringList; +class KSpell; +class KSpellConfig; +class ImageCanvas; +class KConfig; +// class ocrWord; +// class ocrPage; + +#ifdef HAVE_KADMOS +#include "kadmosocr.h" +#endif + +/* + * Error Classifier the report errors on bad engine setup + */ +typedef enum{ ENG_ERROR, ENG_OK, ENG_DATA_MISSING, ENG_BAD_SETUP } EngineError; + +class KSANEOCR : public QObject +{ + Q_OBJECT +public: + enum OCREngines{ GOCR, OCRAD, KADMOS }; + + KSANEOCR( QWidget*, KConfig *); + ~KSANEOCR(); + + bool startOCRVisible( QWidget* parent=0); + + void finishedOCRVisible( bool ); + + /** + * checks after a ocr run if the line number exists in the result + */ + bool lineValid( int line ); + +#ifdef HAVE_KADMOS + bool startKadmosOCR(); +#endif + + /** + * return the final ocr result + */ + + QString ocrResultText(); + + /** + * @return the current spell config. + */ + KSpellConfig* ocrSpellConfig() const + { return m_spellInitialConfig; } + + + /** + * Sets an image Canvas that displays the result image of ocr. If this + * is set to zero (or never set) no result image is displayed. + * The ocr fabric passes a new image to the canvas which is a copy of + * the image to ocr. + */ + void setImageCanvas( ImageCanvas* canvas ); + +signals: + void newOCRResultText( const QString& ); + void clearOCRResultText(); + void newOCRResultPixmap( const QPixmap& ); + + /** + * progress of the ocr process. The first integer is the main progress, + * the second the sub progress. If there is only on progress, it is the + * first parameter, the second is always -1 than. + * Both have a range from 0..100. + * Note that this signal may not be emitted if the engine does not support + * progress. + */ + void ocrProgress(int, int); + + /** + * select a word in the editor in line line. + */ + void selectWord( int line, const ocrWord& word ); + + /** + * signal to indicate that a ocr text must be updated due to better results + * retrieved from spell check. The internal ocr data structure is already + * updated when this signal is fired. + * + * @param line the line in which the word must be changed (start at 0) + * @param wordFrom the original word + * @param wordTo the new word(s). + */ + void updateWord( int line, const QString& wordFrom, const QString& wordTo ); + + /** + * signal to indicate that word word was ignored by the user. This should result + * in a special coloring in the editor. + */ + void ignoreWord( int, const ocrWord& ); + + /** + * signal that comes if a word is considered to be wrong in the editor. + * The word should be marked in any way, e.g. with a signal color. + **/ + void markWordWrong( int, const ocrWord& ); + + /** + * signal the tells that the result image was modified. + */ + void repaintOCRResImage( ); + + /** + * indicates that the text editor holding the text that came through + * newOCRResultText should be set to readonly or not. Can be connected + * to QTextEdit::setReadOnly directly. + */ + void readOnlyEditor( bool ); + +public slots: + void slSetImage( KookaImage* ); + + void slLineBox( const QRect& ); + +protected: + /** + * Start spell checking on a specific line that is stored in m_ocrCurrLine. + * This method starts the spell checking. + **/ + void startLineSpellCheck(); + ocrWord ocrWordFromKSpellWord( int line, const QString& word ); + + /** + * Eventhandler to handle the mouse events to the image viewer showing the + * ocr result image + */ + bool eventFilter( QObject *object, QEvent *event ); + + void startOCRAD(); +protected slots: + void slotClose (); + void slotStopOCR(); + + void slSpellReady( KSpell* ); + void slSpellDead( ); + /** + * a new list of ocr results of the current ocr process arrived and is available + * in the member m_ocrPage[line] + */ + // void gotOCRLine( int line ); + + void slMisspelling( const QString& originalword, + const QStringList& suggestions, + unsigned int pos ); + void slSpellCorrected( const QString& originalword, + const QString& newword, + unsigned int pos ); + + void slSpellIgnoreWord( const QString& word ); + + void slCheckListDone( bool ); + + bool slUpdateWord( int line, int spellWordIndx, + const QString& origWord, + const QString& newWord ); + +private slots: + + void slotKadmosResult(); + void startOCRProcess( void ); + void gocrStdIn(KProcess*, char* buffer, int buflen); + void gocrStdErr(KProcess*, char* buffer, int buflen); + void gocrExited(KProcess*); + + void ocradStdIn(KProcess*, char* buffer, int buflen); + void ocradStdErr(KProcess*, char* buffer, int buflen); + void ocradExited(KProcess*); + + /* + * reads orf files from a file and fills the result structures + * accordingly. + */ + bool readORF( const QString&, QString& ); + +private: + void cleanUpFiles( void ); + + + KOCRBase *m_ocrProcessDia; + KProcess *daemon; + bool visibleOCRRunning; + KTempFile *m_tmpFile; + + KookaImage *m_img; + QString m_ocrResultText; + QString m_ocrResultImage; + QString m_ocrImagePBM; + QString m_tmpOrfName; + QImage *m_resultImage; + + OCREngines m_ocrEngine; + QPixmap m_resPixmap; + QPixmap m_storePixmap; + + ImageCanvas *m_imgCanvas; + + KSpell *m_spell; + bool m_wantKSpell; + bool m_kspellVisible; + bool m_hideDiaWhileSpellcheck; + KSpellConfig *m_spellInitialConfig; + + /* ValueVector of wordLists for every line of ocr results */ + ocrBlock m_ocrPage; /* one block contains all lines of the page */ + QWidget *m_parent; + /* current processed line to speed kspell correction */ + unsigned m_ocrCurrLine; + QStringList m_checkStrings; + + int m_currHighlight; + bool m_applyFilter; + + bool m_unlinkORF; + rectList m_blocks; // dimensions of blocks + + static char UndetectedChar; +#ifdef HAVE_KADMOS + Kadmos::CRep m_rep; +#endif +}; + +#endif diff --git a/kooka/main.cpp b/kooka/main.cpp new file mode 100644 index 00000000..086d3ddf --- /dev/null +++ b/kooka/main.cpp @@ -0,0 +1,121 @@ +/*************************************************************************** + main.cpp - description + ------------------- + begin : Thu Dec 9 20:16:54 MET 1999 + + copyright : (C) 1999 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#include <qdict.h> +#include <qpixmap.h> + +#include <kapplication.h> +#include <dcopclient.h> +#include <kaboutdata.h> +#include <kcmdlineargs.h> +#include <klocale.h> +#include <kglobal.h> +#include <kimageio.h> +#include <kiconloader.h> +#include <kdebug.h> +#include <kwin.h> + +#include "kooka.h" +#include "version.h" + +static const char description[] = + "Kooka is a KDE application which provides access to scanner hardware\n" + "using the SANE library.\n" + "Kooka helps you scan, save your image in the correct image format\n" + "and perform Optical Character Recognition on it, using gocr, Joerg\n" + "Schulenburg's and friends' Open Source ocr program."; + +static const char license[] = +"This program is distributed under the terms of the GPL v2 as publishec by\n" +"the Free Software Foundation\n\n" +"As a special exception, permission is given to link this program\n" +"with any version of the KADMOS ocr/icr engine of reRecognition GmbH,\n" +"Kreuzlingen and distribute the resulting executable without\n" +"including the source code for KADMOS in the source distribution.\n\n" +"As a special exception, permission is given to link this program\n" +"with any edition of Qt, and distribute the resulting executable,\n" +"without including the source code for Qt in the source distribution.\n"; + + +static KCmdLineOptions options[] = +{ + { "d ", I18N_NOOP("The SANE compatible device specification (e.g. umax:/dev/sg0)"), "" }, + { "g", I18N_NOOP("Gallery mode - do not connect to scanner"), "" }, + KCmdLineLastOption +}; + + + +int main( int argc, char *argv[] ) +{ + KAboutData about("kooka", I18N_NOOP("Kooka"), KOOKA_VERSION, I18N_NOOP(description), + KAboutData::License_GPL_V2, "(C) 2000 Klaas Freitag", 0, + I18N_NOOP("http://kooka.kde.org")); + + about.addAuthor( "Klaas Freitag", I18N_NOOP("developer"), "freitag@suse.de" ); + about.addAuthor( "Mat Colton", I18N_NOOP("graphics, web"), "mat@colton.de" ); + about.setLicenseText( license ); + + KCmdLineArgs::init(argc, argv, &about); + KCmdLineArgs::addCmdLineOptions( options ); // Add my own options. + + KApplication app; + KGlobal::locale()->insertCatalogue("libkscan"); + KImageIO::registerFormats(); + KIconLoader *loader = KGlobal::iconLoader(); + + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + QCString devToUse = args->getOption( "d" ); + if( args->isSet("g") ) + { + devToUse = "gallery"; + } + kdDebug( 29000) << "DevToUse is " << devToUse << endl; + + if (args->count() == 1) + { + args->usage(); + // exit(-1); + } + + + Kooka *kooka = new Kooka(devToUse); + app.setMainWidget( kooka ); + + KWin::setIcons(kooka->winId(), loader->loadIcon( "scanner", KIcon::Desktop ), + loader->loadIcon("scanner", KIcon::Small) ); + + kooka->show(); + app.processEvents(); + kooka->startup(); + args->clear(); + int ret = app.exec(); + + return ret; + +} diff --git a/kooka/ocrresedit.cpp b/kooka/ocrresedit.cpp new file mode 100644 index 00000000..a289a079 --- /dev/null +++ b/kooka/ocrresedit.cpp @@ -0,0 +1,148 @@ +/*************************************************************************** + ocrresedit.cpp - ocr result editor widget + ------------------- + begin : Tue 12 Feb 2003 + copyright : (C) 2003 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ +#include <qcolor.h> + +#include "ocrresedit.h" +#include "ocrword.h" +#include <kdebug.h> +#include <kfiledialog.h> +#include <klocale.h> + +#include <qfile.h> +#include <qtextstream.h> + +/* -------------------- ocrResEdit -------------------- */ + +ocrResEdit::ocrResEdit( QWidget *parent ) + : QTextEdit(parent) +{ + m_updateColor.setNamedColor( "SeaGreen"); + m_ignoreColor.setNamedColor( "CadetBlue4" ); + m_wrnColor.setNamedColor( "firebrick2" ); +} + + +void ocrResEdit::slMarkWordWrong( int line, const ocrWord& word ) +{ + // m_textEdit->setSelection( line, + slReplaceWord( line, word, word, m_wrnColor ); +} + + +void ocrResEdit::slUpdateOCRResult( int line, const QString& wordFrom, + const QString& wordTo ) +{ + /* the index is quite useless here, since the text could have had been + * changed by corrections before. Thus better search the word and update + * it. + */ + slReplaceWord( line, wordFrom, wordTo, m_updateColor ); + +} + + +void ocrResEdit::slIgnoreWrongWord( int line, const ocrWord& word ) +{ + slReplaceWord( line, word, word, m_ignoreColor ); +} + + +void ocrResEdit::slSelectWord( int line, const ocrWord& word ) +{ + if( line < paragraphs() ) + { + QString editLine = text(line); + int cnt = editLine.contains( word); + + if( cnt > 0 ) + { + int pos = editLine.find(word); + setCursorPosition( line, pos ); + setSelection( line, pos, line, pos + word.length()); + } + } +} + +void ocrResEdit::slReplaceWord( int line, const QString& wordFrom, + const QString& wordTo, const QColor& color ) +{ + kdDebug(28000) << "Updating word " << wordFrom << " in line " << line << endl; + + bool isRO = isReadOnly(); + + if( line < paragraphs() ) + { + QString editLine = text(line); + int cnt = editLine.contains( wordFrom ); + + if( cnt > 0 ) + { + int pos = editLine.find(wordFrom); + setSelection( line, pos, line, pos+wordFrom.length()); + + QColor saveCol = this->color(); + setColor( color ); + if( isRO ) { + setReadOnly(false); + } + insert( wordTo, unsigned (4) ); + if( isRO ) { + setReadOnly( true ); + } + setColor(saveCol); + } + else + { + kdDebug(28000) << "WRN: Paragraph does not contain word " << wordFrom << endl; + } + + } + else + { + kdDebug(28000) << "WRN: editor does not have line " << line << endl; + } +} + + +void ocrResEdit::slSaveText() +{ + QString fileName = KFileDialog::getSaveFileName( (QDir::home()).path(), + "*.txt", + this, + i18n("Save OCR Result Text") ); + if( fileName.isEmpty() ) + return; + QFile file( fileName ); + if ( file.open( IO_WriteOnly ) ) + { + QTextStream stream( &file ); + stream << text(); + file.close(); + } +} + +#include "ocrresedit.moc" +/* */ diff --git a/kooka/ocrresedit.h b/kooka/ocrresedit.h new file mode 100644 index 00000000..6c483db4 --- /dev/null +++ b/kooka/ocrresedit.h @@ -0,0 +1,65 @@ +/*************************************************************************** + ocrresedit.h - ocr-result edit widget + ------------------- + begin : Fri 12 Feb 2003 + copyright : (C) 2003 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#ifndef _OCR_RESEDIT_ +#define _OCR_RESEDIT_ + +#include <qtextedit.h> + +class QString; +class QColor; +class ocrWord; + +class ocrResEdit : public QTextEdit +{ + Q_OBJECT +public: + ocrResEdit( QWidget *parent ); + +public slots: + void slUpdateOCRResult( int line, const QString& wordFrom, + const QString& wordTo ); + + void slMarkWordWrong( int line, const ocrWord& word ); + + void slIgnoreWrongWord( int line, const ocrWord& word ); + + void slSelectWord( int line, const ocrWord& word ); + + void slSaveText(); + +protected slots: + void slReplaceWord( int line, const QString& wordFrom, + const QString& wordTo, const QColor& color ); + +private: + QColor m_updateColor; + QColor m_ignoreColor; + QColor m_wrnColor; + +}; + +#endif diff --git a/kooka/ocrword.cpp b/kooka/ocrword.cpp new file mode 100644 index 00000000..1bd29f3e --- /dev/null +++ b/kooka/ocrword.cpp @@ -0,0 +1,157 @@ +/*************************************************************************** + ocrword.cpp - ocr-result word and wordlist + ------------------- + begin : Fri Jan 10 2003 + copyright : (C) 2003 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#include <qstring.h> +#include "ocrword.h" +#include <qrect.h> +#include <qptrlist.h> +#include <kdebug.h> +#include <qregexp.h> + +/* -------------------- ocrWord -------------------- */ +ocrWord::ocrWord( const QString& s ) + : QString(s) +{ + +} + +ocrWord::ocrWord() : QString() +{ + +} + +#if 0 +QRect ocrWord::boundingRect() +{ + QRect r; + + return r; +} +#endif + +/* -------------------- CocrWordList ------------------ */ +ocrWordList::ocrWordList() + :QValueList<ocrWord>(), + m_block(0) +{ + // setAutoDelete( true ); +} + +QStringList ocrWordList::stringList() +{ + QStringList res; + QRegExp rx("[,\\.-]"); + ocrWordList::iterator it; + + for ( it = begin(); it != end(); ++it ) + { +#if 0 + /* Uncommented this to prevent an error that occurs if the lenght of the + * spellchecked stringlist and the ocr_page wordlist are not the same length. + * For the ocrpage words connected with a dash are one word while the code + * below parts them into two. That confuses the replacement code if the user + * decided. Solution: KSpell should treat dash-linked words correctly. + * We live with the problem here that dashes bring confusion ;-) + */ + if( (*it).contains( rx ) ) + res += QStringList::split( rx, (*it) ); + else +#endif + res << *it; + } + return res; + +} + +bool ocrWordList::updateOCRWord( const QString& from, const QString& to ) +{ + ocrWordList::iterator it; + bool res = false; + + for( it = begin(); it != end(); ++it ) + { + QString word = (*it); + kdDebug(28000) << "updateOCRWord in list: Comparing word " << word << endl; + if( word.contains( from, true ) ) // case sensitive search + { + word.replace( from, to ); + *it = ocrWord( word ); + res = true; + break; + } + } + return res; +} + +QRect ocrWordList::wordListRect() +{ + QRect rect; + + ocrWordList::iterator it; + + for( it = begin(); it != end(); ++it ) + { + rect = rect.unite( (*it).rect() ); + } + return rect; +} + + +/* + * since kspell removes , - | / etc. from words while they remain in the words + * in the ocr wordlist. + * This search goes through the wordlist and tries to find the words without caring + * for special chars. It simply removes all chars from the words that are not alphanumeric. + */ +bool ocrWordList::findFuzzyIndex( const QString& word, ocrWord& resWord ) +{ + ocrWordList::iterator it; + bool res = false; + + for( it = begin(); it != end() && !res; ++it ) + { + QString fuzzyword = (*it); + fuzzyword.remove( QRegExp( "\\W" )); // Remove all non-word characters. + fuzzyword.remove( '_' ); + + // kdDebug(28000) << "findFuzzy: Comparing word " << fuzzyword << " which was " + // << (*it) << " with " << word << endl; + if( fuzzyword == word ) + { + resWord = *it; + res = true; + } + } + return res; + +} + +void ocrWordList::setBlock( int b ) +{ + m_block = b; +} + +/* */ diff --git a/kooka/ocrword.h b/kooka/ocrword.h new file mode 100644 index 00000000..606acb9f --- /dev/null +++ b/kooka/ocrword.h @@ -0,0 +1,111 @@ +/*************************************************************************** + ocrword.h - ocr-result word and wordlist + ------------------- + begin : Fri 10 Jan 2003 + copyright : (C) 2003 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#ifndef _OCR_WORD_ +#define _OCR_WORD_ + +#include <qstringlist.h> +#include <qvaluevector.h> +#include <qvaluelist.h> +#include <qrect.h> + +class QString; +class QRect; + + +/* ==== ocrWord ====================================== */ +class ocrWord : public QString +{ +public: + ocrWord(const QString& s); + ocrWord(); + QStringList getAlternatives() + { return m_alternatives; } + + void setAlternatives( const QString& s ) + { m_alternatives.append(s); } + + // QRect boundingRect(); + + void setKnode( int k ) + { m_startKnode = k; } + void setLine( int l ) + { m_line = l; } + + int getLine() const { return m_line; } + int getKnode() const { return m_startKnode; } + + void setRect( const QRect& r ) + { m_position = r; } + QRect rect() + { return m_position; } + +private: + QStringList m_alternatives; + int m_startKnode; + int m_line; + QRect m_position; +}; + +/* ==== ocrWordList ====================================== */ + +/** + * This represents a line of words in an ocr'ed document + */ +class ocrWordList : public QValueList<ocrWord> +{ +public: + ocrWordList(); + QStringList stringList(); + + bool updateOCRWord( const QString& from, const QString& to ); + + bool findFuzzyIndex( const QString& word, ocrWord& resWord ); + + QRect wordListRect( ); + + void setBlock( int b ); + int block() const { return m_block; } + +private: + int m_block; +}; + +/** + * All lines of a block: A value vector containing as much as entries + * as lines are available in a block. Needs to be resized acordingly. + */ +typedef QValueVector<ocrWordList> ocrBlock; + +/** + * Blocks taken together form the page. + * Attention: Needs to be resized to the amount of blocks !! + */ +typedef QValueVector<ocrBlock> ocrBlockPage; + +typedef QValueVector<QRect> rectList; + +#endif diff --git a/kooka/pics/Makefile.am b/kooka/pics/Makefile.am new file mode 100644 index 00000000..f8f4440b --- /dev/null +++ b/kooka/pics/Makefile.am @@ -0,0 +1,8 @@ +# Add all of your pixmaps here +pics_DATA = mirror-both.png mirror-horiz.png mirror-vert.png scaletoheight.png \ + scaletowidth.png scaleorig.png ocr.png ocr-select.png newfromselect.png \ + thumbviewtile.png gocr.png ocrad.png lockzoom.png + + +# This is where it will all be installed +picsdir = $(kde_datadir)/kooka/pics diff --git a/kooka/pics/gocr.png b/kooka/pics/gocr.png Binary files differnew file mode 100644 index 00000000..575ebea9 --- /dev/null +++ b/kooka/pics/gocr.png diff --git a/kooka/pics/lockzoom.png b/kooka/pics/lockzoom.png Binary files differnew file mode 100644 index 00000000..dbfa1a7e --- /dev/null +++ b/kooka/pics/lockzoom.png diff --git a/kooka/pics/mirror-both.png b/kooka/pics/mirror-both.png Binary files differnew file mode 100644 index 00000000..e275689b --- /dev/null +++ b/kooka/pics/mirror-both.png diff --git a/kooka/pics/mirror-horiz.png b/kooka/pics/mirror-horiz.png Binary files differnew file mode 100644 index 00000000..eb537f40 --- /dev/null +++ b/kooka/pics/mirror-horiz.png diff --git a/kooka/pics/mirror-vert.png b/kooka/pics/mirror-vert.png Binary files differnew file mode 100644 index 00000000..d3c29462 --- /dev/null +++ b/kooka/pics/mirror-vert.png diff --git a/kooka/pics/newfromselect.png b/kooka/pics/newfromselect.png Binary files differnew file mode 100644 index 00000000..93a75ec8 --- /dev/null +++ b/kooka/pics/newfromselect.png diff --git a/kooka/pics/ocr-select.png b/kooka/pics/ocr-select.png Binary files differnew file mode 100644 index 00000000..db076898 --- /dev/null +++ b/kooka/pics/ocr-select.png diff --git a/kooka/pics/ocr.png b/kooka/pics/ocr.png Binary files differnew file mode 100644 index 00000000..c68f0616 --- /dev/null +++ b/kooka/pics/ocr.png diff --git a/kooka/pics/ocrad.png b/kooka/pics/ocrad.png Binary files differnew file mode 100644 index 00000000..01e41184 --- /dev/null +++ b/kooka/pics/ocrad.png diff --git a/kooka/pics/scaleorig.png b/kooka/pics/scaleorig.png Binary files differnew file mode 100644 index 00000000..8c696f48 --- /dev/null +++ b/kooka/pics/scaleorig.png diff --git a/kooka/pics/scaletoheight.png b/kooka/pics/scaletoheight.png Binary files differnew file mode 100644 index 00000000..b84d971b --- /dev/null +++ b/kooka/pics/scaletoheight.png diff --git a/kooka/pics/scaletowidth.png b/kooka/pics/scaletowidth.png Binary files differnew file mode 100644 index 00000000..90e33617 --- /dev/null +++ b/kooka/pics/scaletowidth.png diff --git a/kooka/pics/thumbviewtile.png b/kooka/pics/thumbviewtile.png Binary files differnew file mode 100644 index 00000000..6f806851 --- /dev/null +++ b/kooka/pics/thumbviewtile.png diff --git a/kooka/resource.h b/kooka/resource.h new file mode 100644 index 00000000..51c9488f --- /dev/null +++ b/kooka/resource.h @@ -0,0 +1,94 @@ +/*************************************************************************** + resource.h - description + ------------------- + begin : Thu Dec 9 20:16:54 MET 1999 + + copyright : (C) 1999 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#ifndef RESSOURCE_H +#define RESSOURCE_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + + +/////////////////////////////////////////////////////////////////// +// resource.h -- contains macros used for commands + + +/////////////////////////////////////////////////////////////////// +// COMMAND VALUES FOR MENUBAR AND TOOLBAR ENTRIES + + +/////////////////////////////////////////////////////////////////// +// File-menu entries +#define ID_FILE_NEW 10020 +#define ID_FILE_OPEN 10030 + +#define ID_FILE_SAVE 10050 +#define ID_FILE_SAVE_AS 10060 +#define ID_FILE_CLOSE 10070 + +#define ID_FILE_PRINT 10080 + +#define ID_FILE_QUIT 10100 + + +/////////////////////////////////////////////////////////////////// +// Edit-menu entries +#define ID_EDIT_UNDO 11010 +#define ID_EDIT_REDO 11020 +#define ID_EDIT_COPY 11030 +#define ID_EDIT_CUT 11040 +#define ID_EDIT_PASTE 11050 +#define ID_EDIT_SELECT_ALL 11060 + + +/////////////////////////////////////////////////////////////////// +// View-menu entries +#define ID_VIEW_TOOLBAR 12010 +#define ID_VIEW_STATUSBAR 12020 +#define ID_VIEW_PREVIEW 12021 +#define ID_VIEW_POOL 12022 +#define ID_VIEW_SCANPARAMS 12023 + + +/////////////////////////////////////////////////////////////////// +// View-menu entries +#define ID_SCAN_PREVIEW 13010 +#define ID_SCAN_FINAL 13020 + +/////////////////////////////////////////////////////////////////// +// Help-menu entries +#define ID_HELP_ABOUT 1002 + +/////////////////////////////////////////////////////////////////// +// General application values +#define IDS_APP_ABOUT "Ksanetest\n Version " VERSION + +#define IDS_DEFAULT "Ready." + +#endif // RESOURCE_H + + diff --git a/kooka/scanpackager.cpp b/kooka/scanpackager.cpp new file mode 100644 index 00000000..7af7f151 --- /dev/null +++ b/kooka/scanpackager.cpp @@ -0,0 +1,1261 @@ +/*************************************************************************** + scanpackager.cpp - description + ------------------- + begin : Fri Dec 17 1999 + copyright : (C) 1999 by Klaas Freitag + email : freitag@suse.de + + $Id$ + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#include "scanpackager.h" +#include "resource.h" +#include "img_saver.h" +#include "kookaimage.h" +#include "kookaimagemeta.h" +#include "previewer.h" +#include "devselector.h" + +#include <qapplication.h> +#include <qdir.h> +#include <qfile.h> +#include <qpopupmenu.h> +#include <qdict.h> +#include <qpixmap.h> +#include <kmessagebox.h> +#include <qfiledialog.h> +#include <qstringlist.h> +#include <qheader.h> + +#include <kfiletreeview.h> +#include <kfiletreeviewitem.h> +#include <kfiletreebranch.h> + +#include <kurldrag.h> +#include <kpopupmenu.h> +#include <kaction.h> +#include <kinputdialog.h> +#include <kiconloader.h> +#include <kfiledialog.h> +#include <kurl.h> +#include <kdebug.h> +#include <klocale.h> +#include <kglobal.h> +#include <kio/global.h> +#include <kio/progressbase.h> +#include <kio/netaccess.h> +#include <kio/jobclasses.h> +#include <kio/file.h> +#include <kio/job.h> + +#define STARTUP_FIRST_START "firstStart" + + +/* ----------------------------------------------------------------------- */ +/* Constructor Scan Packager */ +ScanPackager::ScanPackager( QWidget *parent ) : KFileTreeView( parent ) +{ + // TODO: + setItemsRenameable (true ); + setDefaultRenameAction( QListView::Reject ); + addColumn( i18n("Image Name" )); + setColumnAlignment( 0, AlignLeft ); + + addColumn( i18n("Size") ); + setColumnAlignment( 1, AlignRight ); + setColumnAlignment( 2, AlignRight ); + + addColumn( i18n("Format" )); setColumnAlignment( 3, AlignRight ); + + /* Drag and Drop */ + setDragEnabled( true ); + setDropVisualizer(true); + setAcceptDrops(true); + + connect( this, SIGNAL(dropped( QWidget*, QDropEvent*, KURL::List&, KURL& )), + this, SLOT( slotUrlsDropped( QWidget*, QDropEvent*, KURL::List&, KURL& ))); + + kdDebug(28000) << "connected Drop-Signal" << endl; + setRenameable ( 0, true ); + setRenameable ( 1, false ); + setRenameable ( 2, false ); + setRenameable ( 3, false ); + + setRootIsDecorated( false ); + + connect( this, SIGNAL( clicked( QListViewItem*)), + SLOT( slClicked(QListViewItem*))); + + connect( this, SIGNAL( rightButtonPressed( QListViewItem *, const QPoint &, int )), + SLOT( slShowContextMenue(QListViewItem *, const QPoint &, int ))); + + connect( this, SIGNAL(itemRenamed (QListViewItem*, const QString &, int ) ), this, + SLOT(slFileRename( QListViewItem*, const QString&, int))); + + + img_counter = 1; + /* Set the current export dir to home */ + m_currCopyDir = QDir::home().absPath(); + m_currImportDir = m_currCopyDir; + + /* Preload frequently used icons */ + KIconLoader *loader = KGlobal::iconLoader(); + m_floppyPixmap = loader->loadIcon( "3floppy_unmount", KIcon::Small ); + m_grayPixmap = loader->loadIcon( "palette_gray", KIcon::Small ); + m_bwPixmap = loader->loadIcon( "palette_lineart", KIcon::Small ); + m_colorPixmap = loader->loadIcon( "palette_color", KIcon::Small ); + + m_startup = true; + + /* create a context menu and set the title */ + m_contextMenu = new KPopupMenu(); + static_cast<KPopupMenu*>(m_contextMenu)->insertTitle( i18n( "Gallery" )); + +} + +void ScanPackager::openRoots() +{ + /* standard root always exists, ImgRoot creates it */ + KURL rootUrl(Previewer::galleryRoot()); + kdDebug(28000) << "Open standard root " << rootUrl.url() << endl; + + openRoot( rootUrl, true ); + m_defaultBranch->setOpen(true); + + /* open more configurable image repositories TODO */ +} + +KFileTreeBranch* ScanPackager::openRoot( const KURL& root, bool ) +{ + KIconLoader *loader = KGlobal::iconLoader(); + + /* working on the global branch. FIXME */ + m_defaultBranch = addBranch( root, i18n("Kooka Gallery"), + loader->loadIcon( "folder_image", KIcon::Small ), + false /* do not showHidden */ ); + + // Q_CHECK_PTR( m_defaultBranch ); + m_defaultBranch->setOpenPixmap( loader->loadIcon( "folder_blue_open", KIcon::Small )); + + setDirOnlyMode( m_defaultBranch, false ); + m_defaultBranch->setShowExtensions( true ); // false ); + + connect( m_defaultBranch, SIGNAL( newTreeViewItems( KFileTreeBranch*, const KFileTreeViewItemList& )), + this, SLOT( slotDecorate(KFileTreeBranch*, const KFileTreeViewItemList& ))); + + connect( m_defaultBranch, SIGNAL( directoryChildCount( KFileTreeViewItem* , int )), + this, SLOT( slotDirCount( KFileTreeViewItem *, int ))); + + connect( m_defaultBranch, SIGNAL( deleteItem( KFileItem* )), + this, SLOT( slotDeleteFromBranch(KFileItem*))); + + connect( m_defaultBranch, SIGNAL( populateFinished( KFileTreeViewItem * )), + this, SLOT( slotStartupFinished( KFileTreeViewItem * ))); + + + return( m_defaultBranch ); +} + +void ScanPackager::slotStartupFinished( KFileTreeViewItem *it ) +{ + if( m_startup && (it == m_defaultBranch->root()) ) + { + kdDebug(28000) << "Slot population finished hit!" << endl; + + /* If nothing is selected, select the root. */ + if( ! currentKFileTreeViewItem() ) + { + (m_defaultBranch->root())->setSelected( true ); + } + + m_startup = false; + } +} + +void ScanPackager::slotDirCount( KFileTreeViewItem* item, int cnt ) +{ + if( item && item->isDir() ) + { + QString cc = i18n( "one item", "%n items", cnt); + item->setText( 1, cc ); + } + else + { + kdDebug(28000) << "Item is NOT directory - do not set child count!" << endl; + } +} + +void ScanPackager::slotDecorate( KFileTreeViewItem* item ) +{ + if( !item ) return; + if( item->isDir()) + { + // done in extra slot. + kdDebug(28000) << "Decorating directory!" << endl; + } + else + + { + KFileItem *kfi = item->fileItem(); + + KookaImage *img = 0L; + + if( kfi ) + { + img = static_cast<KookaImage*>(kfi->extraData( this )); + } + + if( img ) + { + + /* The image appears to be loaded to memory. */ + if( img->depth() == 1 ) + { + /* a bw-image */ + item->setPixmap( 0, m_bwPixmap ); + } + else + { + if( img->isGrayscale() ) + { + item->setPixmap( 0, m_grayPixmap ); + } + else + { + item->setPixmap( 0, m_colorPixmap ); + } + } + + /* set image size in pixels */ + QString t = i18n( "%1 x %2" ).arg( img->width()).arg(img->height()); + item->setText( 1, t ); + kdDebug( 28000) << "Image loaded and decorated!" << endl; + } + else + { + /* Item is not yet loaded. Display file information */ + item->setPixmap( 0, m_floppyPixmap ); + if ( kfi ) + { + item->setText(1, KIO::convertSize( kfi->size() )); + } + } + + /* Image format */ + QString format = getImgFormat( item ); + item->setText( 2, format ); + } + + // This code is quite similar to m_nextUrlToSelect in KFileTreeView::slotNewTreeViewItems + // When scanning a new image, we wait for the KDirLister to notice the new file, + // and then we have the KFileTreeViewItem that we need to display the image. + if ( ! m_nextUrlToShow.isEmpty() ) + { + if( m_nextUrlToShow.equals(item->url(), true )) + { + m_nextUrlToShow = KURL(); // do this first to prevent recursion + slClicked( item ); + setCurrentItem(item); // neccessary in case of new file from D&D + } + } +} + + + + +void ScanPackager::slotDecorate( KFileTreeBranch* branch, const KFileTreeViewItemList& list ) +{ + (void) branch; + kdDebug(28000) << "decorating slot for list !" << endl; + + KFileTreeViewItemListIterator it( list ); + + bool end = false; + for( ; !end && it.current(); ++it ) + { + KFileTreeViewItem *kftvi = *it; + slotDecorate( kftvi ); + emit fileChanged( kftvi->fileItem() ); + } +} + + + +void ScanPackager::slFileRename( QListViewItem* it, const QString& newStr, int ) +{ + + bool success = true; + if( !it ) return; + + if( newStr.isEmpty() ) + success = false; + + KFileTreeViewItem *item = static_cast<KFileTreeViewItem*>(it); + + /* Free memory and imform everybody who is interested. */ + KURL urlFrom = item->url(); + KURL urlTo( urlFrom ); + + /* clean filename and apply new name */ + urlTo.setFileName(""); + urlTo.setFileName(newStr); + + if( success ) + { + if( urlFrom == urlTo ) + { + kdDebug(28000) << "Renaming to same url does not make sense!" << endl; + success = false; + } + else + { + /* clear selection, because the renamed image comes in through + * kdirlister again + */ + slotUnloadItem( item ); + + kdDebug(28000) << "Renaming to " << urlTo.prettyURL() << + " from " << urlFrom.prettyURL() << endl; + + /* to urlTo the really used filename is written */ + setSelected( item, false ); + + if( ImgSaver::renameImage( urlFrom, urlTo, false, this ) ) + { + kdDebug(28000) << "renaming OK" << endl; + emit fileRenamed( item->fileItem(), urlTo ); + success=true; + } + else + { + success = false; + } + } + } + + if( !success ) + { + kdDebug(28000) << "renaming failed" << endl; + /* restore the name */ + item->setText(0, urlFrom.fileName() ); + setSelected( item, true ); + + } + +} + + +/* ----------------------------------------------------------------------- */ +/* + * Method that checks if the new filename a user enters while renaming an image is valid. + * It checks for a proper extension. + */ +QString ScanPackager::buildNewFilename( QString cmplFilename, QString currFormat ) const +{ + /* cmplFilename = new name the user wishes. + * currFormat = the current format of the image. + * if the new filename has a valid extension, which is the same as the + * format of the current, fine. A ''-String has to be returned. + */ + QFileInfo fiNew( cmplFilename ); + QString base = fiNew.baseName(); + QString newExt = fiNew.extension( false ).lower(); + QString nowExt = currFormat.lower(); + QString ext = ""; + + kdDebug(28000) << "Filename wanted: "<< cmplFilename << " <"<<newExt<<"> <" << nowExt<<">" <<endl; + + if( newExt.isEmpty() ) + { + /* ok, fine -> return the currFormat-Extension */ + ext = base + "." + currFormat; + } + else if( newExt == nowExt ) + { + /* also good, no reason to put another extension */ + ext = cmplFilename; + } + else + { + /* new Ext. differs from the current extension. Later. */ + KMessageBox::sorry( 0L, i18n( "You entered a file extension that differs from the existing one. That is not yet possible. Converting 'on the fly' is planned for a future release.\n" + "Kooka corrects the extension."), + i18n("On the Fly Conversion")); + ext = base + "." + currFormat; + } + return( ext ); +} + +/* ----------------------------------------------------------------------- */ +/* This method returns the directory of an image or directory. + */ +QString ScanPackager::itemDirectory( const KFileTreeViewItem* item, bool relativ ) const +{ + if( ! item ) + { + kdDebug(28000) << "ERR: itemDirectory without item" << endl; + return QString::null; + } + + QString relativUrl= (item->url()).prettyURL(); + + if( ! item->isDir() ) + { + // Cut off the filename in case it is not a dir + relativUrl.truncate( relativUrl.findRev( '/' )+1); + } + else + { + /* add a "/" to the directory if not there */ + if( ! relativUrl.endsWith( "/" ) ) + relativUrl.append( "/" ); + } + + if( relativ ) + { + KFileTreeBranch *branch = item->branch(); + if( branch ) + { + kdDebug(28000) << "Relativ URL of the file " << relativUrl << endl; + QString rootUrl = (branch->rootUrl()).prettyURL(); // directory of branch root + + if( relativUrl.startsWith( rootUrl )) + { + relativUrl.remove( 0, rootUrl.length() ); + + if( relativUrl.isEmpty() ) relativUrl = "/"; // The root + } + else + { + kdDebug(28000) << "ERR: Item-URL does not start with root url " << rootUrl << endl; + } + } + } + return( relativUrl ); +} +/* ----------------------------------------------------------------------- */ +/* This slot receives a string from the gallery-path combobox shown under the + * image gallery. The form of the string coming in here is <branch-name> - < + * relativ directory under the branch. Now it is to assemble a complete path + * from the data, find out which KFileTreeViewItem is associated with it and + * call slClicked with it. + */ + +void ScanPackager::slotSelectDirectory( const QString & dirString ) +{ + kdDebug(28000) << "Trying to decode directory string " << dirString << endl; + + QString searchFor = QString::fromLatin1(" - "); + int pos = dirString.find( searchFor ); + + if( pos > -1 ) + { + /* Splitting up the string coming in */ + QString branchName = dirString.left( pos ); + QString relPath( dirString ); + + relPath = relPath.remove( 0, pos + searchFor.length()); + + kdDebug(28000) << "Splitted up to branch <" << branchName << "> and <" << relPath << endl; + + KFileTreeViewItem *kfi = findItem( branchName, relPath ); + + if( kfi ) + { + kdDebug(28000) << "got a new item to select !" << endl; + ensureItemVisible(kfi); + setCurrentItem(kfi); + slClicked(kfi); // load thumbnails for this dir etc. + } + } +} + +/* ----------------------------------------------------------------------- */ +/* This slot is called when clicking on an item. */ +void ScanPackager::slClicked( QListViewItem *newItem ) +{ + KFileTreeViewItem *item = static_cast<KFileTreeViewItem*>(newItem); + + if( item ) // can be 0, when clicking where no item is present + { + kdDebug(28000) << "Clicked - newItem !" << endl; + /* Check if directory, hide image for now, later show a thumb view */ + if( item->isDir()) + { + kdDebug(28000) << "clicked: Is a directory !" << endl; + emit( showImage( 0L )); + kdDebug(28000) << "emitting showThumbnails" << endl; + } + else + { + /* if not a dir, load the image if necessary. This is done by loadImageForItem, + * which is async( TODO ). The image finally arrives in slotImageArrived */ + QApplication::setOverrideCursor(waitCursor); + emit( aboutToShowImage( item->url())); + loadImageForItem( item ); + QApplication::restoreOverrideCursor(); + } + + /* emit a signal indicating the new directory if there is a new one */ + QString wholeDir = itemDirectory( item, false ); /* not relativ to root */ + + if( currSelectedDir != wholeDir ) + { + currSelectedDir = wholeDir; + QString relativUrl = itemDirectory( item, true ); + kdDebug(28000) << "Emitting " << relativUrl << " as new relative Url" << endl; + /* Emit the signal with branch and the relative path */ + emit( galleryPathSelected( item->branch(), relativUrl )); + + if( item->isDir() ) + { + emit( showThumbnails( item )); + } + else + { + emit( showThumbnails( static_cast<KFileTreeViewItem*>(item->parent()))); + } + } + else + { + // kdDebug(28000) << "directory is not new: " << currSelectedDir << endl; + } + } +} + +void ScanPackager::loadImageForItem( KFileTreeViewItem *item ) +{ + + if( ! item ) return; + bool result = true; + + KFileItem *kfi = item->fileItem(); + if( ! kfi ) return; + + KookaImage *img = static_cast<KookaImage*>( kfi->extraData(this)); + + if( img ) + { + kdDebug(28000) << "Image already loaded." << endl; + /* result is still true, image must be shown. */ + } + else + { + /* The image needs to be loaded. Possibly it is a multi-page image. + * If it is, the kookaImage has a subImageCount larger than one. We + * create an subimage-item for every subimage, but do not yet load + * them. + */ + KURL url = item->url(); + + img = new KookaImage( ); + if( !img || !img->loadFromUrl( url ) ) + { + kdDebug(28000) << "Loading KookaImage from File failed!" << endl; + result = false; + } + else + { + /* store the fileitem */ + img->setFileItem( kfi ); + + /* care for subimages, create items for them */ + kdDebug(28000) << "subImage-count: " << img->subImagesCount() << endl; + if( img->subImagesCount() > 1 ) + { + KIconLoader *loader = KGlobal::iconLoader(); + kdDebug(28000) << "SubImages existing!" << endl; + + /* Start at the image with index 1, that makes one less than are actually in the + * image. But image 0 was already created above. */ + KFileTreeViewItem *prevItem=0; + for( int i = 1; i < img->subImagesCount(); i++ ) + { + kdDebug(28000) << "Creating subimage no " << i << endl; + KFileItem *newKfi = new KFileItem( *kfi ); + KFileTreeViewItem *subImgItem = new KFileTreeViewItem( item, newKfi, item->branch()); + + if( prevItem ) + { + subImgItem->moveItem( prevItem ); + } + prevItem = subImgItem; + + subImgItem->setPixmap( 0, loader->loadIcon( "editcopy", KIcon::Small )); + subImgItem->setText( 0, i18n("Sub-image %1").arg( i ) ); + KookaImage *subImgImg = new KookaImage( i, img ); + subImgImg->setFileItem( newKfi ); + newKfi->setExtraData( (void*) this, (void*) subImgImg ); + } + } + } + } + + + if( result && img ) + { + if( img->isSubImage() ) + { + kdDebug(28000) << "it _is_ a subimage" << endl; + /* load if not loaded */ + if( img->isNull()) + { + kdDebug(28000) << "extracting subimage" << endl; + img->extractNow(); + } + else + { + kdDebug(28000) << "Is not a null image" << endl; + } + } + slImageArrived( item, img ); + } +} + +/* Hit this slot with a file for a kfiletreeviewitem. */ +void ScanPackager::slImageArrived( KFileTreeViewItem *item, KookaImage* image ) +{ + if( item && image ) + { + /* Associate the image for the Scanpackager-Object. */ + KFileItem *kfi = item->fileItem(); + if( kfi ) + { + kfi->setExtraData( (void*) this, (void*) image ); + } + slotDecorate( item ); + emit( showImage( image )); + } +} + +KookaImage* ScanPackager::getCurrImage() const +{ + KFileTreeViewItem *curr = currentKFileTreeViewItem(); + KookaImage *img = 0L; + + if( curr ) + { + KFileItem *kfi = curr->fileItem(); + if( kfi ) + { + img = static_cast<KookaImage*>(kfi->extraData( this )); + } + } + return(img); +} + + +QString ScanPackager::getCurrImageFileName( bool withPath = true ) const +{ + QString result = ""; + + KFileTreeViewItem *curr = currentKFileTreeViewItem(); + if( ! curr ) + { + kdDebug( 28000) << "getCurrImageFileName: nothing selected !"<< endl; + } + else + { + if( withPath ) + { + result = localFileName(curr); + } + else + { + KURL url( localFileName(curr)); + url = curr->url(); + result = url.fileName(); + } + } + return( result ); +} + +/* ----------------------------------------------------------------------- */ +QCString ScanPackager::getImgFormat( KFileTreeViewItem* item ) const +{ + + QCString cstr; + + if( !item ) return( cstr ); +#if 0 + KFileItem *kfi = item->fileItem(); + + QString mime = kfi->mimetype(); +#endif + + // TODO find the real extension for use with the filename ! + // temporarely: + QString f = localFileName( item ); + + return( QImage::imageFormat( f )); + +} + +QString ScanPackager::localFileName( KFileTreeViewItem *it ) const +{ + if( ! it ) return( QString::null ); + + KURL url = it->url(); + + QString res; + + if( url.isLocalFile()) + { + res = url.directory( false, true ) + url.fileName(); + } + + return( res ); +} + +/* Called if the image exists but was changed by image manipulation func */ +void ScanPackager::slotCurrentImageChanged( QImage *img ) +{ + KFileTreeViewItem *curr = currentKFileTreeViewItem(); + if( ! curr ) + { + kdDebug(28000) << "ImageChanged: nothing selected !" << endl; + return; + } + + /* Do not save directories */ + if( curr->isDir() ) return; + + /* unload image and free memory */ + slotUnloadItem( curr ); + + const QString filename = localFileName( curr ); + const QCString format = getImgFormat( curr ); + ImgSaver saver( this ); + ImgSaveStat is_stat = ISS_OK; + is_stat = saver.saveImage( img, filename, format ); + + if( is_stat == ISS_ERR_FORMAT_NO_WRITE ) + { + KMessageBox::error( this, i18n( "Cannot write this image format.\nImage will not be saved!"), + i18n("Save Error") ); + } + else if( is_stat == ISS_ERR_PERM ) + { + KMessageBox::error( this, i18n( "Image file is write protected.\nImage will not be saved!"), + i18n("Save Error") ); + + } + else if( is_stat == ISS_ERR_PROTOCOL ) + { + KMessageBox::sorry( this, i18n( "Cannot save the image, because the file is local.\n" + "Kooka will support other protocols later."), + i18n("Save Error") ); + + } + else if( is_stat != ISS_OK ) + { + kdDebug(28000) << "Error while saving existing image !" << endl; + } + + if( img && !img->isNull()) + { + emit( imageChanged( curr->fileItem())); + KookaImage *newImage = new KookaImage(*img); + slImageArrived( curr, newImage ); + } +} + + +/* ----------------------------------------------------------------------- */ +/* This slot takes a new scanned Picture and saves it. + * It urgently needs to make a deep copy of the image ! + */ +void ScanPackager::slAddImage( QImage *img, KookaImageMeta* ) +{ + ImgSaveStat is_stat = ISS_OK; + /* Save the image with the help of the ImgSaver */ + if( ! img ) return; + + /* currently selected item is the directory or a file item */ + KFileTreeViewItem *curr = currentKFileTreeViewItem(); + + /* Use root if nothing is selected */ + if( ! curr ) + { + KFileTreeBranch *b = branches().at(0); /* There should be at least one */ + + if( b ) + { + curr = findItem( b, i18n( "Incoming/" ) ); + if( ! curr ) curr = b->root(); + } + + /* If curr is still undefined, something very tough has happend. Go away here */ + if( !curr ) return; + + setSelected( curr, true ); + } + + /* find the directory above the current one */ + + KURL dir(itemDirectory( curr )); + + /* Path of curr sel item */ + ImgSaver img_saver( this, dir ); + + is_stat = img_saver.saveImage( img ); + if( is_stat == ISS_ERR_FORMAT_NO_WRITE ) + { + KMessageBox::error( this, i18n( "Cannot write this image format.\nImage will not be saved!"), + i18n("Save Error") ); + } + else if( is_stat == ISS_ERR_PERM ) + { + KMessageBox::error( this, i18n( "Image file is write protected.\nImage will not be saved!"), + i18n("Save Error") ); + + } + else if( is_stat != ISS_OK ) + { + if( is_stat == ISS_SAVE_CANCELED ) + { + return; + } + kdDebug(28000) << "ERROR: Saving failed: " << img_saver.errorString( is_stat ) << endl; + /* And now ?? */ + } + + /* Add the new image to the list of new images */ + KURL lurl = img_saver.lastFileUrl(); + + KFileTreeBranchList branchlist = branches(); + KFileTreeBranch *kookaBranch = branchlist.at(0); + + QString strdir = itemDirectory(curr); + if(strdir.endsWith(QString("/"))) strdir.truncate( strdir.length() - 1 ); + kdDebug(28000) << "Updating directory with " << strdir << endl; + + if( kookaBranch ) kookaBranch->updateDirectory( KURL(strdir) ); + slotSetNextUrlToSelect( lurl ); + m_nextUrlToShow = lurl; + + QString s; + /* Count amount of children of the father */ + QListViewItem *paps = curr->parent(); + if( curr->isDir() ) /* take only father if the is no directory */ + paps = curr; + + if( paps ) + { + int childcount = paps->childCount(); + s = i18n("%1 images").arg(childcount); + paps->setText( 1, s); + setOpen( paps, true ); + } + +} + +/* ----------------------------------------------------------------------- */ +/* selects and opens the file with the given name. This is used to restore the + * last displayed image by its name. + */ +void ScanPackager::slSelectImage( const KURL& name ) +{ + + KFileTreeViewItem *found = spFindItem( UrlSearch, name.url() ); + + if( found ) + { + kdDebug(28000) << "slSelectImage: Found an item !" << endl; + ensureItemVisible( found ); + setCurrentItem( found ); + slClicked( found ); + } + +} + + +KFileTreeViewItem *ScanPackager::spFindItem( SearchType type, const QString name, const KFileTreeBranch *branch ) +{ + /* Prepare a list of branches to go through. If the parameter branch is set, search + * only in the parameter branch. If it is zero, search all branches returned by + * kfiletreeview.branches() + */ + KFileTreeBranchList branchList; + + if( branch ) + { + branchList.append( branch ); + } + else + { + branchList = branches(); + } + + + KFileTreeBranchIterator it( branchList ); + KFileItem *kfi = 0L; + KFileTreeViewItem *foundItem = 0L; + + /* Leave the loop in case kfi is defined */ + KFileTreeBranch *branchloop = 0L; + for( ; !kfi && it.current(); ++it ) + { + branchloop = *it; + KURL url(name); + switch( type ) + { + case Dummy: + kdDebug(28000) << "Dummy search skipped !" << endl; + break; + case NameSearch: + kdDebug(28000) << "ScanPackager: searching for " << name << endl; + kfi = branchloop->findByName( name ); + break; + case UrlSearch: + kdDebug(28000) << "ScanPackager: URL search for " << name << endl; + kfi = branchloop->find( url ); + break; + default: + kdDebug(28000) << "Scanpackager: Wrong search type !" << endl; + break; + } + + } + if( kfi ) + { + foundItem = static_cast<KFileTreeViewItem*>(kfi->extraData(branchloop)); + kdDebug(28000) << "spFindItem: Success !" << foundItem << endl; + } + return( foundItem ); +} + +/* ----------------------------------------------------------------------- */ +void ScanPackager::slShowContextMenue(QListViewItem *lvi, const QPoint &p, int col ) +{ + kdDebug(28000) << "Showing Context Menue" << endl; + (void) col; + + KFileTreeViewItem *curr = 0; + + if( lvi ) + { + curr = currentKFileTreeViewItem(); + if( curr->isDir() ) + setSelected( curr, true ); + } + + if( m_contextMenu ) + { + m_contextMenu->exec( p ); + } + +} + +/* ----------------------------------------------------------------------- */ + +void ScanPackager::slotExportFile( ) +{ + KFileTreeViewItem *curr = currentKFileTreeViewItem(); + if( ! curr ) return; + + if( curr->isDir() ) + { + kdDebug(28000) << "Not yet implemented!" << endl; + } + else + { + KURL fromUrl( curr->url()); + QString filter = "*." + getImgFormat(curr).lower(); + filter += "\n*|" + i18n( "All Files" ); + + // initial += fromUrl.filename(false); + QString initial = m_currCopyDir + "/"; + initial += fromUrl.filename(false); + KURL fileName = KFileDialog::getSaveURL ( initial, + filter, this ); + + if ( fileName.isValid() ) // got a file name + { + if( fromUrl == fileName ) return; + + /* Since it is asynchron, we will never get if it succeeded. */ + ImgSaver::copyImage( fromUrl, fileName ); + + /* remember the filename for the next export */ + fileName.setFileName( QString()); + m_currCopyDir = fileName.url( ); + } + } +} + + +void ScanPackager::slotImportFile() +{ + KFileTreeViewItem *curr = currentKFileTreeViewItem(); + if( ! curr ) return; + + KURL impTarget = curr->url(); + + if( ! curr->isDir() ) + { + KFileTreeViewItem *pa = static_cast<KFileTreeViewItem*>(curr->parent()); + impTarget = pa->url(); + } + kdDebug(28000) << "Importing to " << impTarget.url() << endl; + + KURL impUrl = KFileDialog::getImageOpenURL ( m_currImportDir, this, i18n("Import Image File to Gallery")); + + if( ! impUrl.isEmpty() ) + { + m_currImportDir = impUrl.url(); + impTarget.addPath( impUrl.fileName()); // append the name of the sourcefile to the path + m_nextUrlToShow = impTarget; + ImgSaver::copyImage( impUrl, impTarget ); + } +} + + + +void ScanPackager::slotUrlsDropped( QWidget*, QDropEvent* ev, KURL::List& urls, KURL& copyTo ) +{ + if( !urls.isEmpty() ) + { + kdDebug(28000) << "Kooka drop event!" << endl; + // kdDebug(28000) << "Kooka drop event. First src url=" << urls.first() << " copyTo=" << copyTo + // << " move=" << ( ev->action() == QDropEvent::Move ) << endl; + + /* first make the last url to copy to the one to select next */ + if( ! urls.empty() ) + { + KURL nextSel = copyTo; + nextSel.addPath( urls.back().fileName(false)); + + kdDebug(28000) << "Selecting next url: " << nextSel.url() << endl; + m_nextUrlToShow = nextSel; + // slotSetNextUrlToSelect( nextSel ); + } + + if ( ev->action() == QDropEvent::Move ) + copyjob = KIO::move( urls, copyTo, true ); + else + copyjob = KIO::copy( urls, copyTo, true ); + } +} + +void ScanPackager::slotCanceled( KIO::Job* ) +{ + kdDebug(28000) << i18n("Canceled by user") << endl; +} + + +/* ----------------------------------------------------------------------- */ +void ScanPackager::slotUnloadItems( ) +{ + KFileTreeViewItem *curr = currentKFileTreeViewItem(); + emit( showImage( 0L )); + slotUnloadItem( curr ); +} + +void ScanPackager::slotUnloadItem( KFileTreeViewItem *curr ) +{ + if( ! curr ) return; + + if( curr->isDir()) + { + KFileTreeViewItem *child = static_cast<KFileTreeViewItem*>(curr->firstChild()); + while( child ) + { + kdDebug(28000) << "Unloading item " << child << endl; + slotUnloadItem( child ); + child = static_cast<KFileTreeViewItem*> (child->nextSibling()); + } + } + else + { + KFileItem *kfi = curr->fileItem(); + KookaImage *image = static_cast<KookaImage*>(kfi->extraData( this )); + + /* If image is zero, ok, than there is nothing to unload :) */ + if( image ) + { + if( image->subImagesCount() > 0 ) + { + KFileTreeViewItem *child = static_cast<KFileTreeViewItem*>(curr->firstChild()); + + while( child ) + { + KFileTreeViewItem *nextChild = 0; + kdDebug(28000) << "Unloading subimage item " << child << endl; + slotUnloadItem( child ); + nextChild = static_cast<KFileTreeViewItem*> (child->nextSibling()); + delete child; + child = nextChild; + } + } + + emit( unloadImage( image )); + delete image; + kfi->removeExtraData( this ); + slotDecorate( curr ); + } + } +} + +/* ----------------------------------------------------------------------- */ +void ScanPackager::slotDeleteItems( ) +{ + KFileTreeViewItem *curr = currentKFileTreeViewItem(); + if( ! curr ) return; + + KURL urlToDel = curr->url(); + QListViewItem *nextToSelect = curr->nextSibling(); + + kdDebug(28000) << "Deleting: " << urlToDel.prettyURL() << endl; + bool ask = true; /* for later use */ + + int result = KMessageBox::Yes; + + KFileItem *item = curr->fileItem(); + if( ask ) + { + QString s; + s = i18n("Do you really want to delete this image?\nIt cannot be restored!" ); + if( item->isDir() ) + { + s = i18n("Do you really want to delete the folder %1\nand all the images inside?").arg(""); + } + result = KMessageBox::warningContinueCancel(this, s, i18n( "Delete Collection Item"), + KStdGuiItem::del(), "AskForDeleteFiles" ); + } + + /* Since we are currently talking about local files here, NetAccess is OK */ + if( result == KMessageBox::Continue ) + { + if( KIO::NetAccess::del( urlToDel, 0 )) + { + if( nextToSelect ) + setSelected( nextToSelect, true ); + /* TODO: remove the directory from the imageNameCombobox */ + if( curr && item->isDir() ) + { + /* The directory needs to be removed from the name combo */ + emit(directoryToRemove( curr->branch(), itemDirectory( curr, true ) )); + } + + } + else + kdDebug(28000) << "Deleting files failed" << endl; + + } +} + +/* ----------------------------------------------------------------------- */ +void ScanPackager::slotCreateFolder( ) +{ + bool ok; + QString folder = KInputDialog::getText( i18n( "New Folder" ), + i18n( "Please enter a name for the new folder:" ), QString::null, + &ok, this ); + + if( ok ) + { + /* KIO create folder goes here */ + + KFileTreeViewItem *it = currentKFileTreeViewItem(); + if( it ) + { + KURL url = it->url(); + + /* If a directory is selected, the filename needs not to be deleted */ + if( ! it->isDir()) + url.setFileName( "" ); + /* add the folder name from user input */ + url.addPath( folder ); + kdDebug(28000) << "Creating folder " << url.prettyURL() << endl; + + /* Since the new directory arrives in the packager in the newItems-slot, we set a + * variable urlToSelectOnArrive here. The newItems-slot will honor it and select + * the treeviewitem with that url. + */ + slotSetNextUrlToSelect( url ); + + if( ! KIO::NetAccess::mkdir( url, 0, -1 )) + { + kdDebug(28000) << "ERR: creation of " << url.prettyURL() << " failed !" << endl; + } + else + { + /* created successfully */ + /* open the branch if necessary and select the new folder */ + + } + } + } +} + + +/* ----------------------------------------------------------------------- */ +QString ScanPackager::getImgName( QString name_on_disk ) +{ + QString s; + (void) name_on_disk; + + s = i18n("image %1").arg(img_counter++); + return( s ); +} + +/* ----------------------------------------------------------------------- */ +ScanPackager::~ScanPackager(){ + kdDebug(29000) << "Destructor of ScanPackager" << endl; + +} + +/* called whenever one branch detects a deleted file */ +void ScanPackager::slotDeleteFromBranch( KFileItem* kfi ) +{ + emit fileDeleted( kfi ); +} + +void ScanPackager::contentsDragMoveEvent( QDragMoveEvent *e ) +{ + if( ! acceptDrag( e ) ) + { + e->ignore(); + return; + } + + QListViewItem *afterme = 0; + QListViewItem *parent = 0; + + findDrop( e->pos(), parent, afterme ); + + // "afterme" is 0 when aiming at a directory itself + QListViewItem *item = afterme ? afterme : parent; + + if( item ) + { + bool isDir = static_cast<KFileTreeViewItem*> (item)->isDir(); + if( isDir ) { + KFileTreeView::contentsDragMoveEvent( e ); // for the autoopen code + return; + } + } + e->acceptAction(); +} + + +#include "scanpackager.moc" diff --git a/kooka/scanpackager.h b/kooka/scanpackager.h new file mode 100644 index 00000000..13173050 --- /dev/null +++ b/kooka/scanpackager.h @@ -0,0 +1,167 @@ +/*************************************************************************** + scanpackager.h - description + ------------------- + begin : Fri Dec 17 1999 + copyright : (C) 1999 by Klaas Freitag + email : freitag@suse.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + + +#ifndef SCANPACKAGER_H +#define SCANPACKAGER_H + +#include <qlistview.h> +#include <qimage.h> +#include <qpixmap.h> +#include <qdragobject.h> +#include <qmap.h> +#include <klistview.h> +#include <kio/job.h> +#include <kio/global.h> +#include <kio/file.h> +#include <kfiletreeview.h> + + +/** + *@author Klaas Freitag + */ + +class KURL; +class QPopupMenu; +class KFileTreeViewItem; +class KookaImage; +class KookaImageMeta; +class KFileTreeBranch; + + +typedef enum{ Dummy, NameSearch, UrlSearch } SearchType; + +class JobDescription +{ +public: + enum JobType { NoJob, ImportJob, RenameJob, ExportJob }; + JobDescription():jobType( NoJob ), kioJob(0L), pitem(0L) {} + JobDescription( KIO::Job* kiojob, KFileTreeViewItem *new_item, JobType type ) : + jobType(type), kioJob(kiojob), pitem(new_item) {} + + JobType type( void ) { return( jobType ); } + KFileTreeViewItem *item( void ) { return( pitem ); } + KIO::Job* job( void ){ return( kioJob ); } +private: + JobType jobType; + KIO::Job* kioJob; + KFileTreeViewItem* pitem; +}; + +class ScanPackager : public KFileTreeView +{ + Q_OBJECT +public: + ScanPackager( QWidget *parent); + ~ScanPackager(); + virtual QString getImgName( QString name_on_disk ); + + QString getCurrImageFileName( bool ) const; + KookaImage* getCurrImage() const; + + KFileTreeBranch* openRoot( const KURL&, bool open=false ); + + QPopupMenu *contextMenu() const { return m_contextMenu; } + void openRoots(); + +public slots: + void slSelectImage( const KURL& ); + void slAddImage( QImage *img, KookaImageMeta* meta = 0 ); + void slShowContextMenue(QListViewItem *, const QPoint &, int ); + + void slotExportFile( ); + void slotImportFile(); + void slotCanceled(KIO::Job*); + void slotCurrentImageChanged( QImage* ); + + void slotDecorate( KFileTreeViewItem* ); + void slotDecorate( KFileTreeBranch*, const KFileTreeViewItemList& ); + + void slotSelectDirectory( const QString& ); + +protected: + virtual void contentsDragMoveEvent( QDragMoveEvent *e ); + +protected slots: + void slClicked( QListViewItem * ); + void slFileRename( QListViewItem*, const QString&, int ); + // void slFilenameChanged( KFileTreeViewItem*, const KURL & ); + void slImageArrived( KFileTreeViewItem *item, KookaImage* image ); + void slotCreateFolder( ); + void slotDeleteItems( ); + void slotUnloadItems( ); + void slotUnloadItem( KFileTreeViewItem *curr ); + void slotDirCount( KFileTreeViewItem *item, int cnt ); + void slotUrlsDropped( QWidget*, QDropEvent*, KURL::List& urls, KURL& copyTo ); + void slotDeleteFromBranch( KFileItem* ); + void slotStartupFinished( KFileTreeViewItem * ); +signals: + void showImage ( KookaImage* ); + void deleteImage( KookaImage* ); + void unloadImage( KookaImage* ); + void galleryPathSelected( KFileTreeBranch* branch, const QString& relativPath ); + void directoryToRemove( KFileTreeBranch *branch, const QString& relativPath ); + void showThumbnails( KFileTreeViewItem* ); + + void aboutToShowImage( const KURL& ); /* starting to load image */ + void imageChanged( KFileItem* ); /* the image has changed */ + + void fileDeleted( KFileItem* ); + void fileChanged( KFileItem* ); + void fileRenamed( KFileItem*, const KURL& ); + +private: + QString localFileName( KFileTreeViewItem* it ) const; + void loadImageForItem( KFileTreeViewItem* item ); + QCString getImgFormat( KFileTreeViewItem* item ) const; + + QString buildNewFilename( QString cmplFilename, QString currFormat ) const; + KFileTreeViewItem *spFindItem( SearchType type, const QString name, const KFileTreeBranch* branch = 0 ); + QString itemDirectory( const KFileTreeViewItem*, bool relativ = false ) const; + + // int readDir( QListViewItem *parent, QString dir_to_read ); + void showContextMenu( QPoint p, bool show_folder = true ); + + QString m_currImportDir; + QString m_currCopyDir; + QString currSelectedDir; + KIO::Job *copyjob; + int img_counter; + QPopupMenu *m_contextMenu; + + // like m_nextUrlToSelect in KFileTreeView but for our own purposes (showing the image) + KURL m_nextUrlToShow; + + QPixmap m_floppyPixmap; + QPixmap m_grayPixmap; + QPixmap m_bwPixmap; + QPixmap m_colorPixmap; + + KFileTreeBranch *m_defaultBranch; + bool m_startup; +}; + +#endif diff --git a/kooka/thumbview.cpp b/kooka/thumbview.cpp new file mode 100644 index 00000000..5dfa93b6 --- /dev/null +++ b/kooka/thumbview.cpp @@ -0,0 +1,494 @@ +/*************************************************************************** + thumbview.cpp - Class to display thumbnailed images + ------------------- + begin : Tue Apr 18 2002 + copyright : (C) 2002 by Klaas Freitag + email : freitag@suse.de + + $Id$ + ***************************************************************************/ + + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#include <qpixmap.h> +#include <qpainter.h> + +#include <kio/previewjob.h> +#include <kdebug.h> +#include <kfileitem.h> +#include <kfileiconview.h> +#include <kfiletreeviewitem.h> +#include <kimageeffect.h> +#include <kglobal.h> +#include <kstandarddirs.h> +#include <kprogress.h> + +#include "thumbview.h" +#include "thumbview.moc" + +#include "thumbviewitem.h" + + + +ThumbView::ThumbView( QWidget *parent, const char *name ) + : QVBox( parent ), + m_iconView(0), + m_job(0) +{ + setMargin(3); + m_pixWidth = 0; + m_pixHeight = 0; + m_thumbMargin = 5; + m_iconView = new KIconView( this, name ); + m_progress = new KProgress( this ); + m_progress->hide(); + + m_pixWidth = 100; + m_pixHeight = 100; + + readSettings(); + + m_basePix.resize( QSize( m_pixWidth, m_pixHeight ) ); + m_basePix.fill(); // fills white per default TODO + + + m_iconView->setItemsMovable( false ); + + slSetBackGround(); + + connect( m_iconView, SIGNAL( executed( QIconViewItem* )), + this, SLOT( slDoubleClicked( QIconViewItem* ))); + + m_pendingJobs.setAutoDelete(false); +} + +ThumbView::~ThumbView() +{ + saveConfig(); +} + +bool ThumbView::readSettings() +{ + KConfig *cfg = KGlobal::config(); + cfg->setGroup( THUMB_GROUP ); + bool dirty = false; + + QColor color; + color = cfg->readColorEntry( MARGIN_COLOR1, &(colorGroup().base())); + if( color != m_marginColor1 ) + { + dirty = true; + m_marginColor1 = color; + } + + color = cfg->readColorEntry( MARGIN_COLOR2, &(colorGroup().foreground())); + if( color != m_marginColor2 ) + { + dirty = true; + m_marginColor2 = color; + } + + int value; + bool sizeDirty = false; + value = cfg->readNumEntry( THUMB_MARGIN, 5 ); + if( value != m_thumbMargin ) + { + sizeDirty = true; + m_thumbMargin = value; + } + + value = cfg->readNumEntry( PIXMAP_WIDTH, 100 ); + if( value != m_pixWidth || m_pixWidth == 0 ) + { + sizeDirty = true; + m_pixWidth = value; + } + + value = cfg->readNumEntry( PIXMAP_HEIGHT, 120 ); + if( value != m_pixHeight || m_pixHeight == 0 ) + { + sizeDirty = true; + m_pixHeight = value; + } + + if( sizeDirty ) + { + int gX = 2*m_thumbMargin+m_pixWidth+10; + int gY = 2*m_thumbMargin+m_pixHeight+10; + m_iconView->setGridX(gX); + m_iconView->setGridY(gY); + kdDebug(28000) << "Setting Grid " << gX << " - " << gY << endl; + } + + KStandardDirs stdDir; + QString newBgImg = cfg->readEntry( BG_WALLPAPER, stdDir.findResource( "data", STD_TILE_IMG ) ); + + if( m_bgImg != newBgImg ) + { + m_bgImg = newBgImg; + slSetBackGround(); + } + + return (sizeDirty || dirty); +} + +void ThumbView::slDoubleClicked( QIconViewItem *qIt ) +{ + ThumbViewItem *it = static_cast<ThumbViewItem*>( qIt ); + + if( it ) + { + const KURL url = it->itemUrl(); + + emit( selectFromThumbnail( url )); + } +} + +void ThumbView::slSetBackGround( ) +{ + QPixmap bgPix; + if( m_bgImg.isEmpty()) + { + bgPix.resize( QSize(16, 16)); + bgPix.fill( QPixmap::blue ); + } + else + { + bgPix.load( m_bgImg ); + } + + m_iconView->setPaletteBackgroundPixmap ( bgPix ); + setPaletteBackgroundPixmap ( bgPix ); + +} + +void ThumbView::slImageChanged( KFileItem *kfit ) +{ + if( ! kfit ) return; + // kdDebug(28000) << "changes to one thumbnail!" << endl; + + KURL thumbDir = currentDir(); + KURL itemUrl = kfit->url(); + + /* delete filename */ + itemUrl.setFileName( QString()); + if( !itemUrl.equals( thumbDir, true )) + { + // kdDebug(28000) << "returning, because directory does not match: " << itemUrl.prettyURL() << endl; + // kdDebug(28000) << "and my URL: " << thumbDir.prettyURL() << endl; + return; + } + + if( deleteImage( kfit )) + { + kdDebug(28000) << "was changed, deleted first!" << endl; + } + /* Trigger a new reading */ + KFileItemList li; + li.append( kfit ); + slNewFileItems( li ); +} + +void ThumbView::slImageRenamed( KFileItem *kfit, const KURL& newUrl ) +{ + const KURL url = kfit->url(); + + if( kfit->isDir() ) { + clear(); + } + + for ( QIconViewItem *item = m_iconView->firstItem(); item; item = item->nextItem() ) + { + ThumbViewItem *it=static_cast<ThumbViewItem*>( item ); + + if( url == it->itemUrl() ) + { + it->setItemUrl( newUrl ); + + break; + } + } +} + + +void ThumbView::slCheckForUpdate( KFileItem *kfit ) +{ + if( ! kfit ) return; + + kdDebug(28000) << "Checking for update of thumbview!" << endl; + + KURL searchUrl = kfit->url(); + bool haveItem = false; + + /* iterate over all icon items and compare urls. + * TODO: Check the parent url to avoid iteration over all */ + for ( QIconViewItem *item = m_iconView->firstItem(); item && !haveItem; + item = item->nextItem() ) + { + if( searchUrl == static_cast<ThumbViewItem*>(item)->itemUrl() ) + { + haveItem = true; + } + } + + /* if we still do not have the item, it is not in the thumbview. */ + if( ! haveItem ) + { + KFileItemList kfiList; + + kfiList.append( kfit ); + slNewFileItems( kfiList ); + } + +} + + +bool ThumbView::deleteImage( KFileItem *kfit ) +{ + if( ! kfit ) return false; + + + KURL searchUrl = kfit->url(); + bool haveItem = false; + + /* iterate over all icon items and compare urls. + * TODO: Check the parent url to avoid iteration over all */ + for ( QIconViewItem *item = m_iconView->firstItem(); item && !haveItem; item = item->nextItem() ) + { + if( searchUrl == static_cast<ThumbViewItem*>(item)->itemUrl() ) + { + m_iconView->takeItem( item ); + haveItem = true; + } + } + kdDebug(28000) << "Deleting image from thumbview, result is " << haveItem << endl; + return( haveItem ); +} + +void ThumbView::slImageDeleted( KFileItem *kfit ) +{ + deleteImage( kfit ); + + + /* + From a mail from Waldo pointing out two probs in Thumbview: + + 1) KDirLister is the owner of the KFileItems it emits, this means + that you must watch it's deleteItem() signal vigourously, + otherwise you may end up with KFileItems that are already + deleted. This burden is propagated to classes that use + KDirLister, such as KFileIconView. + + This has a tendency to go wrong in combination with PreviewJob, + because it stores a list of KFileItems while running. This has + the potential to crash if the fileitems are being deleted + during this time. The remedy is to make sure to remove + fileitems that get deleted from the PreviewJob with + PreviewJob::removeItem. + + */ + if( m_job ) /* is a job running? Remove the item from it if existing. */ + { + m_job->removeItem( kfit ); + } + + /* check if it is in the pending list */ + m_pendingJobs.removeRef(kfit); +} + + +void ThumbView::slNewFileItems( const KFileItemList& items ) +{ + kdDebug(28000) << "Creating thumbnails for fileItemList" << endl; + + /* Fill the pending jobs list. */ + KFileItemListIterator it( items ); + KFileItem *item = 0; + for ( ; (item = it.current()); ++it ) + { + QString filename = item->url().prettyURL(); + if( item->isDir() ) + { + /* create a dir pixmap */ + } + else + { + QPixmap p(m_basePix) ; + QPixmap mime( item->pixmap(0) ); + + if( p.width() > mime.width() && p.height() > mime.height() ) + { + QPainter paint( &p ); + paint.drawPixmap( (p.width()-mime.width())/2, + (p.height()-mime.height())/2, + mime ); + paint.flush(); + } + + /* Create a new empty preview pixmap and store the pointer to it */ + ThumbViewItem *newIconViewIt = new ThumbViewItem( m_iconView, + item->url().filename(), + createPixmap( p ), + item ); + + newIconViewIt->setItemUrl( item->url() ); + + /* tell the file item about the iconView-representation */ + item->setExtraData( this, newIconViewIt ); + + m_pendingJobs.append( item ); + } + } + + /* + From a mail from Waldo Bastian pointing out problems with thumbview: + + 2) I think you may end up creating two PreviewJob's in parallel + when the slNewFileItems() function is called two times in + quick succession. The current code doesn't seem to expect + that, given the comment in slPreviewResult(). In the light of + 1) it might become fatal since you will not be able to call + PreviewJob::removeItem on the proper job. I suggest to queue + new items when a job is already running and start a new job + once the first one is finished when there are any items left + in the queue. Don't forget to delete items from the queue if + they get deleted in the mean time. + + The strategy is as follows: In the global list m_pendingJobs + the jobs to start are appended. Only if m_job is zero (no job + is running) a job is started on the current m_pendingJobs list. + The m_pendingJobs list is clear afterwords. + */ + + if( ! m_job && m_pendingJobs.count() > 0 ) + { + /* Progress-Bar */ + m_progress->show(); + m_progress->setTotalSteps(m_pendingJobs.count()); + m_cntJobsStarted = 0; + + /* start a preview-job */ + m_job = KIO::filePreview(m_pendingJobs, m_pixWidth, m_pixHeight ); + + if( m_job ) + { + connect( m_job, SIGNAL( result( KIO::Job * )), + this, SLOT( slPreviewResult( KIO::Job * ))); + connect( m_job, SIGNAL( gotPreview( const KFileItem*, const QPixmap& )), + SLOT( slGotPreview( const KFileItem*, const QPixmap& ) )); + + m_pendingJobs.clear(); + + /* KIO::Jo result is called in any way: Success, Failed, Error, + * thus connecting the failed is not really necessary. + */ + // connect( job, SIGNAL( failed( const KFileItem* )), + // this, SLOT( slotFailed( const KFileItem* ) )); + + } + } +} + + + +void ThumbView::slGotPreview( const KFileItem* newFileItem, const QPixmap& newPix ) +{ + if( ! newFileItem ) return; + KFileIconViewItem *item = static_cast<KFileIconViewItem*>(const_cast<void*>(newFileItem->extraData( this ))); + + if( ! item ) return; + + item->setPixmap( createPixmap(newPix) ); + m_cntJobsStarted+=1; + + m_progress->setProgress(m_cntJobsStarted); + + // kdDebug(28000)<< "jobs-Counter: " << m_cntJobsStarted << endl; + +} + +void ThumbView::slPreviewResult( KIO::Job *job ) +{ + if( job && job->error() > 0 ) + { + kdDebug(28000) << "Thumbnail Creation ERROR: " << job->errorString() << endl; + job->showErrorDialog( 0 ); + } + + if( job != m_job ) + { + kdDebug(28000) << "Very obscure: Job finished is not mine!" << endl; + } + /* finished */ + kdDebug(28000) << "Thumbnail job finished." << endl; + m_cntJobsStarted = 0; + m_progress->reset(); + m_progress->hide(); + m_job = 0L; + + /* maybe there is a new job to start because of pending items? */ + if( m_pendingJobs.count() > 0 ) + { + slNewFileItems( KFileItemList() ); /* Call with an empty list */ + } +} + + +QPixmap ThumbView::createPixmap( const QPixmap& preview ) const +{ + QImage ires = KImageEffect::unbalancedGradient( QSize( 2*m_thumbMargin+ preview.width(), + 2*m_thumbMargin+ preview.height()), + m_marginColor1, m_marginColor2, + KImageEffect::DiagonalGradient ); + + + QPixmap pixRet; + pixRet.convertFromImage( ires ); + QPainter p( &pixRet ); + + p.drawPixmap( m_thumbMargin, m_thumbMargin, preview ); + p.flush(); + // draw on pixmap + + return( pixRet ); +} + + +void ThumbView::clear() +{ + if( m_job ) + m_job->kill( false /* not silently to get result-signal */ ); + m_iconView->clear(); +} + + +void ThumbView::saveConfig() +{ + KConfig *cfg = KGlobal::config(); + cfg->setGroup( THUMB_GROUP ); + + cfg->writeEntry( MARGIN_COLOR1, m_marginColor1 ); + cfg->writeEntry( MARGIN_COLOR2, m_marginColor2 ); + cfg->writeEntry( PIXMAP_WIDTH, m_pixWidth ); + cfg->writeEntry( PIXMAP_HEIGHT, m_pixHeight ); + cfg->writeEntry( THUMB_MARGIN, m_thumbMargin ); + + +} diff --git a/kooka/thumbview.h b/kooka/thumbview.h new file mode 100644 index 00000000..c16afa84 --- /dev/null +++ b/kooka/thumbview.h @@ -0,0 +1,153 @@ +/*************************************************************************** + thumbview.h - Class to display thumbnailed images + ------------------- + begin : Tue Apr 18 2002 + copyright : (C) 2002 by Klaas Freitag + email : freitag@suse.de + + $Id$ + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#ifndef __THUMBVIEW_H__ +#define __THUMBVIEW_H__ + +#include <qwidget.h> +#include <qimage.h> +#include <qpixmap.h> +#include <qcolor.h> +#include <qvbox.h> + +#include <kiconview.h> +#include <kurl.h> +#include <kio/previewjob.h> +#include <kfileitem.h> +#include <kfileiconview.h> + +/* KConfig group definitions */ +#define MARGIN_COLOR1 "MarginColor1" +#define MARGIN_COLOR2 "MarginColor2" +#define PIXMAP_WIDTH "pixmapWidth" +#define PIXMAP_HEIGHT "pixmapHeight" +#define THUMB_MARGIN "thumbnailMargin" +#define THUMB_GROUP "thumbnailView" +#define BG_WALLPAPER "BackGroundTile" +#define STD_TILE_IMG "kooka/pics/thumbviewtile.png" + +class QPixmap; +class QListViewItem; +class KProgress; +class KIO::PreviewJob; + +class ThumbView: public QVBox /* KIconView */ +{ + Q_OBJECT + +public: + + ThumbView( QWidget *parent, const char *name=0 ); + ~ThumbView(); + + void setCurrentDir( const KURL& s) + { m_currentDir = s; } + KURL currentDir( ) const + { return m_currentDir; } + + QSize tumbSize( ) const + { + return( QSize( m_pixWidth, m_pixHeight )); + } + + int thumbMargin() const + { + return m_thumbMargin; + } +public slots: + void slSetThumbSize( int w, int h ) + { + m_pixWidth = w; + m_pixHeight = h; + } + void slSetThumbSize( const QSize& s ) + { + m_pixWidth = s.width(); + m_pixHeight = s.height(); + } + + void slSetThumbMargin( int m ) + { + m_thumbMargin = m; + } + + void slNewFileItems( const KFileItemList& ); + void slGotPreview( const KFileItem*, const QPixmap& ); + void slPreviewResult( KIO::Job* ); + + /** + * This connects to the IconView's executed signal and tells the packager + * to select the image + */ + void slDoubleClicked( QIconViewItem* ); + + /** + * indication that a image changed, needs to be reloaded. + */ + void slImageChanged( KFileItem * ); + void slImageDeleted( KFileItem * ); + void slSetBackGround( ); + void slCheckForUpdate( KFileItem* ); + bool readSettings(); + void clear(); + + void slImageRenamed( KFileItem*, const KURL& ); + +protected: + + void saveConfig(); + +signals: + /** + * selects a QListViewItem from the thumbnail. This signal only makes + * sense if connected to a ScanPackager. + */ + void selectFromThumbnail( const KURL& ); + +private: + QPixmap createPixmap( const QPixmap& ) const; + + bool deleteImage( KFileItem* ); + KIconView *m_iconView; + KProgress *m_progress; + + KURL m_currentDir; + QPixmap m_basePix; + int m_pixWidth; + int m_pixHeight; + int m_thumbMargin; + QColor m_marginColor1; + QColor m_marginColor2; + QString m_bgImg; + int m_cntJobsStarted; + KIO::PreviewJob *m_job; + + KFileItemList m_pendingJobs; +}; + +#endif diff --git a/kooka/thumbviewitem.cpp b/kooka/thumbviewitem.cpp new file mode 100644 index 00000000..7f2d01f6 --- /dev/null +++ b/kooka/thumbviewitem.cpp @@ -0,0 +1,49 @@ +/*************************************************************************** + thumbviewitem.cpp - Thumbview item class + ------------------- + begin : Tue Apr 24 2002 + copyright : (C) 2002 by Klaas Freitag + email : freitag@suse.de + + $Id$ + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#include <kfileitem.h> +#include <kfileiconview.h> + +#include "thumbview.h" +#include "thumbviewitem.h" + +ThumbViewItem::ThumbViewItem(QIconView *parent, const QString &text, + const QPixmap &pixmap, + KFileItem *fi ) + :KFileIconViewItem( parent, text, pixmap,fi ) +{ + +} + +void ThumbViewItem:: setItemUrl( const KURL& u ) +{ + m_url = u; + setText( m_url.fileName()); +} + + diff --git a/kooka/thumbviewitem.h b/kooka/thumbviewitem.h new file mode 100644 index 00000000..745c2b25 --- /dev/null +++ b/kooka/thumbviewitem.h @@ -0,0 +1,60 @@ +/*************************************************************************** + thumbviewitem.h - Thumbnailview items + ------------------- + begin : Tue Apr 24 2002 + copyright : (C) 2002 by Klaas Freitag + email : freitag@suse.de + + $Id$ + ***************************************************************************/ + +/*************************************************************************** + * * + * This file may be distributed and/or modified under the terms of the * + * GNU General Public License version 2 as published by the Free Software * + * Foundation and appearing in the file COPYING included in the * + * packaging of this file. * + * + * As a special exception, permission is given to link this program * + * with any version of the KADMOS ocr/icr engine of reRecognition GmbH, * + * Kreuzlingen and distribute the resulting executable without * + * including the source code for KADMOS in the source distribution. * + * + * As a special exception, permission is given to link this program * + * with any edition of Qt, and distribute the resulting executable, * + * without including the source code for Qt in the source distribution. * + * * + ***************************************************************************/ + +#ifndef __THUMBVIEWITEM_H__ +#define __THUMBVIEWITEM_H__ + +#include <kiconview.h> +#include <kurl.h> +#include <kio/previewjob.h> +#include <kfileitem.h> +#include <kfileiconview.h> + +class KFileTreeViewItem; + + +class ThumbViewItem: public KFileIconViewItem +{ +public: + ThumbViewItem( QIconView *parent, + const QString &text, + const QPixmap &pixmap, + KFileItem *fi ); + + void setItemUrl( const KURL& u ); + + KURL itemUrl() const + { return m_url; } + +private: + KURL m_url; + + +}; + +#endif diff --git a/kooka/version.h b/kooka/version.h new file mode 100644 index 00000000..2d9da5f7 --- /dev/null +++ b/kooka/version.h @@ -0,0 +1,4 @@ +#ifndef KOOKA_VERSION +#define KOOKA_VERSION "0.44" +#endif + |