diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | 47d455dd55be855e4cc691c32f687f723d9247ee (patch) | |
tree | 52e236aaa2576bdb3840ebede26619692fed6d7d /libkscan | |
download | tdegraphics-47d455dd55be855e4cc691c32f687f723d9247ee.tar.gz tdegraphics-47d455dd55be855e4cc691c32f687f723d9247ee.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdegraphics@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'libkscan')
49 files changed, 11301 insertions, 0 deletions
diff --git a/libkscan/AUTHORS b/libkscan/AUTHORS new file mode 100644 index 00000000..9d4c1dab --- /dev/null +++ b/libkscan/AUTHORS @@ -0,0 +1,2 @@ +Ivan Shvedunov <ivan_iv@rf-hp.npi.msu.su> +Klaas Freitag <freitag@suse.de> diff --git a/libkscan/COPYING.LIB b/libkscan/COPYING.LIB new file mode 100644 index 00000000..01148ab6 --- /dev/null +++ b/libkscan/COPYING.LIB @@ -0,0 +1,486 @@ +NOTE! The LGPL below is copyrighted by the Free Software Foundation, but +the instance of code that it refers to (the kde libraries) are copyrighted +by the authors who actually wrote it. + +--------------------------------------------------------------------------- + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 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. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, 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 library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, 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 companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, 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 library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete 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 distribute a copy of this License along with the +Library. + + 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 Library or any portion +of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +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 Library, 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 Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you 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. + + If distribution of 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 satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. 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. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library 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. + + 9. 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 Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +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. + + 11. 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 Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library 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 Library. + +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. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library 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. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +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 Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +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 + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. 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 LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. 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 library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; 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. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/libkscan/INSTALL b/libkscan/INSTALL new file mode 100644 index 00000000..02a4a074 --- /dev/null +++ b/libkscan/INSTALL @@ -0,0 +1,167 @@ +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 a while. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Type `make install' to install the programs and any data files and + documentation. + + 4. You can remove the program binaries and object files from the + source code directory by typing `make clean'. + +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. + + 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/libkscan/Makefile.am b/libkscan/Makefile.am new file mode 100644 index 00000000..b0c3be45 --- /dev/null +++ b/libkscan/Makefile.am @@ -0,0 +1,32 @@ +INCLUDES = -I$(top_srcdir)/libkscan $(all_includes) $(LIBSANE_INCLUDES) + +lib_LTLIBRARIES = libkscan.la + +noinst_HEADERS = kscandevice.h kscanslider.h kgammatable.h \ + kscanoption.h kscanoptset.h \ + gammadialog.h dispgamma.h \ + scansourcedialog.h scanparams.h massscandialog.h devselector.h \ + img_canvas.h previewer.h imgscaledialog.h sizeindicator.h \ + imgscaninfo.h + +libkscan_la_SOURCES = kscandevice.cpp kscanslider.cpp kgammatable.cpp\ + kscanoption.cpp kscanoptset.cpp \ + gammadialog.cpp dispgamma.cpp \ + scansourcedialog.cpp scanparams.cpp massscandialog.cpp \ + devselector.cpp scandialog.cpp \ + img_canvas.cpp previewer.cpp imgscaledialog.cpp sizeindicator.cpp \ + imgscaninfo.cpp + + +libkscan_la_LDFLAGS = $(all_libraries) $(LIBSANE_LDFLAGS) -no-undefined -version-info 1:0 +libkscan_la_LIBADD = $(LIBSANE_LIBS) $(LIB_KFILE) + +kde_services_DATA = scanservice.desktop + +METASOURCES = AUTO + +SUBDIRS = pics + +messages: + $(XGETTEXT) *.cpp -o $(podir)/libkscan.pot + diff --git a/libkscan/README b/libkscan/README new file mode 100644 index 00000000..d947ac58 --- /dev/null +++ b/libkscan/README @@ -0,0 +1,12 @@ +KScan is KDE's Scanner Library. +It's used by kooka and by koffice. +It provides an easy-to-use library, which +allows you to acces your scanner (camera - as long as it's +sane compatible). + +It's based on SANE. +You can find SANE under http://www.sane-project.org/ + + +Nikolas Zimmermann +<wildfox@kde.org> diff --git a/libkscan/TODO b/libkscan/TODO new file mode 100644 index 00000000..5dd0a652 --- /dev/null +++ b/libkscan/TODO @@ -0,0 +1,59 @@ + +Our TODO List: +2. Tue Oct 31 18:12:25 CET 2000 - Klaas Freitag <freitag@suse.de> + + OK, trying to find smaller portions of work to do ;) + Trying to write down the status of the classes here. + + Probably all classes need: + - a cleanup of the code. Lots of unused stuff may be deleted. + - a debug concept. By now, the function debug() is used, but probably that not state of the art? + - a rewrite/cleaning to adept to Qt 2.x + - the gui functionality KDE 2.x - features. + - KDE-features at all ! The whole stuff does not use any KDE-funcitonality, only Qt. + What about having a Version which compiles without KDE ? .o0( Good idea to think about ? ) + - attention to drop QString where possible, and I think, its possible where SANE-stuff is + used. + - I18N-functions. In resource.h, I defined a macro I18N which does not much by now. I tried + to place it around strings to prepare for real I18N treatment. How is that done ? + - C++ - cleanup. Nearly no class has proper default and copy constructor and stuff... + + KGammaTable: an abstrct data type + - seems to work more or less for 255-color images. Does not work for palettes with more entries. + + - needs documentation + + KScanDevice: logic scanner device. + - has mixed code for the widget-factory (getGuiElement) and the logic scanner. Dont know if + that is cool, or if we should participate that. + - Has the sane scanner handle as a modul global variable. Thus, only one scanner handle can + be used in one application. + - No net device yet. I never had a look for it, and if it works or not. I only disabled it to + cut down complexity. + - ... much more + - should be able to load and apply option sets (see there). + + - needs documentation + + KScanOption: + - has no possibility to be applied immediatly after being set. That might be a real problem + in some cases. Good ideas are welcome. + By now, the Option can be set and needs to be applied (means sent to the scanner device) + by a call to the apply method in KScanDevice. After that, it could be checked, if other + options changed thereby. Thats fine under the aspect of a good OO design, but might cause + problems in case a option is changed and there is no possibility to call apply. + + - needs documentation + + KScanOptSet: + - needs possibility to be stored and loaded in/from KDE-configuration files. Example: + Option set (including resolution, gammatables etc.) for photos, for ocr, bla... + + + KScanSlider: + - needs a neutral button in the widget ! + + KScanEntry : + KScanCombo : + + ... much more. diff --git a/libkscan/configure.in.bot b/libkscan/configure.in.bot new file mode 100644 index 00000000..64c4c234 --- /dev/null +++ b/libkscan/configure.in.bot @@ -0,0 +1,9 @@ +if test -z "$SANE_CONFIG"; then + echo "" + echo "You're missing development files for libsane - " + echo "libkscan/kooka won't be compiled without libsane." + echo "You can download it from" + echo "http://www.sane-project.org/" + echo "" + all_tests=bad +fi diff --git a/libkscan/configure.in.in b/libkscan/configure.in.in new file mode 100644 index 00000000..09929e92 --- /dev/null +++ b/libkscan/configure.in.in @@ -0,0 +1,22 @@ + +KDE_FIND_PATH( sane-config, SANE_CONFIG, [ ${prefix}/bin, ${exec_prefix}/bin, + /usr/local/bin, /opt/local/bin, + /usr/bin ] ) +if test -n "$SANE_CONFIG"; then + dnl ### version check. Klaas, which version is minimum? + + LIBSANE_LIBS="`$SANE_CONFIG --libs`" + LIBSANE_LDFLAGS="`$SANE_CONFIG --ldflags`" + dnl Bah, --cflags is -I lines only (according to the sane-config.in source + dnl and _INCLUDES looks nicer :) + LIBSANE_INCLUDES="`$SANE_CONFIG --cflags`" + + AC_DEFINE_UNQUOTED(HAVE_SANE, 1, [Defines if your system has the sane libraries]) +else + DO_NOT_COMPILE="$DO_NOT_COMPILE libkscan kooka" +fi + +AM_CONDITIONAL(include_kooka, test -n "$SANE_CONFIG") +AC_SUBST(LIBSANE_LIBS) +AC_SUBST(LIBSANE_LDFLAGS) +AC_SUBST(LIBSANE_INCLUDES) diff --git a/libkscan/devselector.cpp b/libkscan/devselector.cpp new file mode 100644 index 00000000..8d1a90ad --- /dev/null +++ b/libkscan/devselector.cpp @@ -0,0 +1,183 @@ +/* This file is part of the KDE Project + Copyright (C) 2000 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <stdlib.h> + +#include <qbuttongroup.h> +#include <qcheckbox.h> +#include <qcstring.h> +#include <qlayout.h> +#include <qlabel.h> +#include <qfile.h> +#include <qpushbutton.h> +#include <qradiobutton.h> +#include <qstrlist.h> +#include <qstringlist.h> + +#include <kapplication.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kglobal.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <ksimpleconfig.h> + +#include "devselector.h" + + +DeviceSelector::DeviceSelector( QWidget *parent, QStrList& devList, + const QStringList& hrdevList ) + : KDialogBase( parent, "DeviceSel", true, i18n("Welcome to Kooka"), + Ok|Cancel, Ok, true ) +{ + kdDebug(29000) << "Starting DevSelector!" << endl; + // Layout-Boxes + QWidget *page = new QWidget( this ); + Q_CHECK_PTR( page ); + setMainWidget( page ); + + QVBoxLayout *topLayout = new QVBoxLayout( page, marginHint(), spacingHint() ); + QLabel *label = new QLabel( page, "captionImage" ); + Q_CHECK_PTR( label ); + label->setPixmap( QPixmap( "kookalogo.png" )); + label->resize( 100, 350 ); + topLayout->addWidget( label ); + + selectBox = new QButtonGroup( 1, Horizontal, i18n( "Select Scan Device" ), + page, "ButtonBox"); + Q_CHECK_PTR( selectBox ); + selectBox->setExclusive( true ); + topLayout->addWidget( selectBox ); + setScanSources( devList, hrdevList ); + + cbSkipDialog = new QCheckBox( i18n("&Do not ask on startup again, always use this device"), + page, "CBOX_SKIP_ON_START" ); + + KConfig *gcfg = KGlobal::config(); + gcfg->setGroup(QString::fromLatin1(GROUP_STARTUP)); + bool skipDialog = gcfg->readBoolEntry( STARTUP_SKIP_ASK, false ); + cbSkipDialog->setChecked( skipDialog ); + + topLayout->addWidget(cbSkipDialog); + +} + +QCString DeviceSelector::getDeviceFromConfig( void ) const +{ + KConfig *gcfg = KGlobal::config(); + gcfg->setGroup(QString::fromLatin1(GROUP_STARTUP)); + bool skipDialog = gcfg->readBoolEntry( STARTUP_SKIP_ASK, false ); + + QCString result; + + /* in this case, the user has checked 'Do not ask me again' and does not + * want to be bothered any more. + */ + result = QFile::encodeName(gcfg->readEntry( STARTUP_SCANDEV, "" )); + kdDebug(29000) << "Got scanner from config file to use: " << result << endl; + + /* Now check if the scanner read from the config file is available ! + * if not, ask the user ! + */ + if( skipDialog && devices.find( result ) > -1 ) + { + kdDebug(29000) << "Scanner from Config file is available - fine." << endl; + } + else + { + kdDebug(29000) << "Scanner from Config file is _not_ available" << endl; + result = QCString(); + } + + return( result ); +} + +bool DeviceSelector::getShouldSkip( void ) const +{ + return( cbSkipDialog->isChecked()); +} + +QCString DeviceSelector::getSelectedDevice( void ) const +{ + unsigned int selID = selectBox->id( selectBox->selected() ); + + int dcount = devices.count(); + kdDebug(29000) << "The Selected ID is <" << selID << ">/" << dcount << endl; + + const char * dev = devices.at( selID ); + + kdDebug(29000) << "The selected device: <" << dev << ">" << endl; + + /* Store scanner selection settings */ + KConfig *c = KGlobal::config(); + c->setGroup(QString::fromLatin1(GROUP_STARTUP)); + /* Write both the scan device and the skip-start-dialog flag global. */ + c->writeEntry( STARTUP_SCANDEV, dev, true, true ); + c->writeEntry( STARTUP_SKIP_ASK, getShouldSkip(), true, true ); + c->sync(); + + return dev; +} + + +void DeviceSelector::setScanSources( const QStrList& sources, + const QStringList& hrSources ) +{ + bool default_ok = false; + KConfig *gcfg = KGlobal::config(); + gcfg->setGroup(QString::fromLatin1(GROUP_STARTUP)); + QCString defstr = gcfg->readEntry( STARTUP_SCANDEV, "" ).local8Bit(); + + /* Selector-Stuff*/ + uint nr = 0; + int checkDefNo = 0; + + QStrListIterator it( sources ); + QStringList::ConstIterator it2 = hrSources.begin(); + for ( ; it.current(); ++it, ++it2 ) + { + QString text = QString::fromLatin1("&%1. %2\n%3").arg(1+nr).arg( QString::fromLocal8Bit(*it) ).arg( *it2 ); + QRadioButton *rb = new QRadioButton( text, selectBox ); + selectBox->insert( rb ); + + devices.append( *it ); + + if( *it == defstr ) + checkDefNo = nr; + + nr++; + } + + /* Default still needs to be set */ + if( ! default_ok ) + { + /* if no default found, set the first */ + QRadioButton *rb = (QRadioButton*) selectBox->find( checkDefNo ); + if ( rb ) + rb->setChecked( true ); + } +} + +DeviceSelector::~DeviceSelector() +{ + +} + +/* The End ;) */ +#include "devselector.moc" diff --git a/libkscan/devselector.h b/libkscan/devselector.h new file mode 100644 index 00000000..9fa80469 --- /dev/null +++ b/libkscan/devselector.h @@ -0,0 +1,104 @@ +/* This file is part of the KDE Project + Copyright (C) 2000 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef DEVSELECTOR_H +#define DEVSELECTOR_H + + +#include <kdialogbase.h> + +class QButtonGroup; +class QStrList; +class QStringList; +class QCheckBox; + +/** + *@author Klaas Freitag + */ + +/* Configuration-file definitions */ +#define GROUP_STARTUP "Scan Settings" +#define STARTUP_SCANDEV "ScanDevice" +#define STARTUP_SKIP_ASK "SkipStartupAsk" +#define STARTUP_ONLY_LOCAL "QueryLocalOnly" + + +/** + * A utitlity class that lets the user select a scan device. + * + * This class provides functions to get the scan device to use for an + * application with scan support. + * + * Its main purpose is to display a selection dialog, but it also reads + * and writes config entries to store the users selection and handles + * a field 'do not ask me again'. + * + */ + +class DeviceSelector: public KDialogBase +{ + Q_OBJECT +public: + /** + * constructs the dialog class + * @param QWidget *parent - the parent + * @param QStrList backends - a list of device names retrieved from the scan device + * @param QStrList scannerNames - a list of corresponding human readable sanner names. + */ + DeviceSelector( QWidget *parent, QStrList&, const QStringList& ); + ~DeviceSelector(); + + /** + * returns the device the user selected. + * @return a CString containing the technical name of the selected device (taken from + * the backends-list from the constructor) + */ + QCString getSelectedDevice( void ) const; + + /** + * returns the users selection if the dialog should be skipped in future. + * @return true for should be skipped. + */ + bool getShouldSkip( void ) const; + + /** + * retrieval to get the device from the config file. The function reads the applications + * config file, cares for the 'do not ask me again'-settings and checks if the scandevice + * specified in the config file is present at the current configuration. + * If this function returns null, the DeviceSelector should be opened for a user selection. + * @return a string containing the device to open or null if no device is specified or the + * one specified is not valid. + */ + QCString getDeviceFromConfig( void ) const; + +public slots: + void setScanSources( const QStrList&, const QStringList& ); + +private: + QButtonGroup *selectBox; + mutable QStrList devices; + QCheckBox *cbSkipDialog; + bool configDevValid; + + class DeviceSelectorPrivate; + DeviceSelectorPrivate *d; + +}; + +#endif diff --git a/libkscan/dispgamma.cpp b/libkscan/dispgamma.cpp new file mode 100644 index 00000000..2892d563 --- /dev/null +++ b/libkscan/dispgamma.cpp @@ -0,0 +1,85 @@ +/* This file is part of the KDE Project + Copyright (C) 2000 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qpainter.h> +#include <qpixmap.h> + +#include "dispgamma.h" + +DispGamma::DispGamma( QWidget *parent ) : QWidget( parent ) +{ + vals = 0; + margin = 10; +} + +DispGamma::~DispGamma() +{ + +} + +void DispGamma::resizeEvent (QResizeEvent* ) +{ + repaint(); +} + +void DispGamma::paintEvent( QPaintEvent *ev ) +{ + QPainter p(this); + int w = vals->size() +1; + + // Viewport auf margin setzen. + p.setViewport( margin, margin, width() - margin, height() - margin ); + p.setWindow( 0, 255, w, -256 ); + + p.setClipRect( ev->rect()); + + p.setPen( colorGroup().highlight() ); + p.setBrush( colorGroup().base() ); + // Backgrond + p.drawRect( 0,0, w, 256 ); + p.setPen( QPen(colorGroup().midlight(), 1, DotLine)); + // horizontal Grid + for( int l = 1; l < 5; l++ ) + p.drawLine( 1, l*51, 255, l*51 ); + + // vertical Grid + for( int l = 1; l < 5; l++ ) + p.drawLine( l*51, 2, l*51, 255 ); + + // draw gamma-Line + p.setPen( colorGroup().highlight() ); + p.moveTo( 1, vals->at(1) ); + for( int i = 2; i < w-1; i++ ) + { + p.lineTo( i, vals->at(i) ); + } + p.flush(); +} + + +QSize DispGamma::sizeHint( void ) +{ + return QSize( 256 + 2*margin,256 + 2 * margin ); +} + +QSizePolicy DispGamma::sizePolicy( void ) +{ + return QSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding ); +} +#include "dispgamma.moc" diff --git a/libkscan/dispgamma.h b/libkscan/dispgamma.h new file mode 100644 index 00000000..86c8c1b4 --- /dev/null +++ b/libkscan/dispgamma.h @@ -0,0 +1,62 @@ +/* This file is part of the KDE Project + Copyright (C) 2000 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef DISPGAMMA_H +#define DISPGAMMA_H + +#include <qwidget.h> +#include <qsizepolicy.h> +#include <qsize.h> +#include <qmemarray.h> + +extern "C"{ +#include <sane/sane.h> +#include <sane/saneopts.h> +} +/** + *@author Klaas Freitag + */ + +class DispGamma : public QWidget { + Q_OBJECT +public: + DispGamma( QWidget *parent ); + ~DispGamma(); + + QSize sizeHint( void ); + QSizePolicy sizePolicy( void ); + + void setValueRef( QMemArray<SANE_Word> *newVals ) + { + vals = newVals; + } +protected: + void paintEvent (QPaintEvent *ev ); + void resizeEvent( QResizeEvent* ); + +private: + + QMemArray<SANE_Word> *vals; + int margin; + + class DispGammaPrivate; + DispGammaPrivate *d; +}; + +#endif diff --git a/libkscan/gammadialog.cpp b/libkscan/gammadialog.cpp new file mode 100644 index 00000000..4a8b3302 --- /dev/null +++ b/libkscan/gammadialog.cpp @@ -0,0 +1,114 @@ +/* This file is part of the KDE Project + Copyright (C) 2000 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qlabel.h> +#include <qpushbutton.h> +#include <qslider.h> +#include <qlineedit.h> +#include <qcombobox.h> + +#include <kscanslider.h> +#include <klocale.h> +#include <kdebug.h> + +#include "gammadialog.h" + +GammaDialog::GammaDialog( QWidget *parent ) : + KDialogBase( parent, "GammaDialog", true, i18n("Custom Gamma Tables"), + Ok|Cancel|Apply, Ok, true ) +{ + gt = new KGammaTable(); + QWidget *page = new QWidget( this ); + + Q_CHECK_PTR( page ); + setMainWidget( page ); + + /* This connect is for recalculating the table every time a new + * Bright., Contrast or Gamma-Value is set */ + connect( gt, SIGNAL(tableChanged()), gt, SLOT(getTable())); + + gtDisp = new DispGamma( page ); + gtDisp->setValueRef( gt->getArrayPtr() ); + gtDisp->resize( 280, 280 ); + + connect( gt, SIGNAL(tableChanged()), gtDisp, SLOT( repaint())); + + // setCaption( i18n( "Gamma Table" )); + + // Layout-Boxes + QVBoxLayout *bigdad = new QVBoxLayout( page, 10 ); + QHBoxLayout *lhMiddle = new QHBoxLayout( 5 ); + QVBoxLayout *lvSliders = new QVBoxLayout( 10 ); + + QLabel *l_top = new QLabel( i18n( "<B>Edit the custom gamma table</B><BR>This gamma table is passed to the scanner hardware." ), page ); + bigdad->addWidget( l_top, 1 ); + bigdad->addLayout( lhMiddle, 6 ); + + lhMiddle->addLayout( lvSliders, 3); + lhMiddle->addWidget( gtDisp, 2 ); + + /* Slider Widgets for gamma, brightness, contrast */ + wBright = new KScanSlider ( page, i18n("Brightness"), -50.0, 50.0 ); + Q_CHECK_PTR(wBright); + wBright->slSetSlider( 0 ); + connect( wBright, SIGNAL(valueChanged(int)), gt, SLOT(setBrightness(int))); + + wContrast = new KScanSlider ( page, i18n("Contrast") , -50.0, 50.0 ); + Q_CHECK_PTR(wContrast); + wContrast->slSetSlider( 0 ); + connect( wContrast, SIGNAL(valueChanged(int)), gt, SLOT(setContrast(int))); + + wGamma = new KScanSlider ( page, i18n("Gamma"), 30.0, 300.0 ); + Q_CHECK_PTR(wGamma); + wGamma->slSetSlider(100); + connect( wGamma, SIGNAL(valueChanged(int)), gt, SLOT(setGamma(int))); + + /* and add the Sliders */ + lvSliders->addWidget( wBright, 1 ); + lvSliders->addWidget( wContrast, 1 ); + lvSliders->addWidget( wGamma, 1 ); + + // Finished and Activate ! + bigdad->activate(); + resize( 480, 300 ); +} + + +void GammaDialog::setGt(KGammaTable& ngt) +{ + *gt = ngt; + + if( wBright ) wBright->slSetSlider( gt->getBrightness() ); + if( wContrast ) wContrast->slSetSlider( gt->getContrast() ); + if( wGamma ) wGamma->slSetSlider( gt->getGamma() ); + +} + +void GammaDialog::slotApply() +{ + /* and call a signal */ + KGammaTable *myTable = getGt(); + emit( gammaToApply( myTable )); +} + +GammaDialog::~GammaDialog() +{ + +} +#include "gammadialog.moc" diff --git a/libkscan/gammadialog.h b/libkscan/gammadialog.h new file mode 100644 index 00000000..8ba648a3 --- /dev/null +++ b/libkscan/gammadialog.h @@ -0,0 +1,75 @@ +/* This file is part of the KDE Project + Copyright (C) 2000 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef GAMMADIALOG_H +#define GAMMADIALOG_H + +#include <qwidget.h> +#include <qlayout.h> + +#include <kgammatable.h> +#include <kdialogbase.h> + + + +#include "dispgamma.h" + + +/** + *@author Klaas Freitag + */ + +class KScanSlider; +class KGammaTable; + +class GammaDialog : public KDialogBase +{ + Q_OBJECT +// FIXME: Doesn't compile with Qt 3 (malte) +// Q_PROPERTY( KGammaTable *gt READ getGt WRITE setGt ) + +public: + GammaDialog ( QWidget *parent ); + ~GammaDialog( ); + + KGammaTable *getGt( ) const { return gt; } + void setGt( KGammaTable& ngt); + +public slots: + virtual void slotApply(); + +signals: +void gammaToApply( KGammaTable* ); + +private: + KGammaTable *gt; + DispGamma *gtDisp; + + QHBoxLayout *lhMiddle; + QVBoxLayout *lvSliders; + + KScanSlider *wGamma; + KScanSlider *wBright; + KScanSlider *wContrast; + + class GammaDialogPrivate; + GammaDialogPrivate *d; +}; + +#endif diff --git a/libkscan/img_canvas.cpp b/libkscan/img_canvas.cpp new file mode 100644 index 00000000..eccaa412 --- /dev/null +++ b/libkscan/img_canvas.cpp @@ -0,0 +1,1115 @@ +/* This file is part of the KDE Project + Copyright (C) 1999 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qapplication.h> +#include <qfiledialog.h> +#include <qstring.h> +#include <qmessagebox.h> +#include <qscrollview.h> +#include <kpopupmenu.h> +#include <qlabel.h> +#include <qdict.h> +#include <qimage.h> +#include <qpainter.h> + +#include <klocale.h> +#include <kstyle.h> +#include <kapplication.h> + +#include <kpixmapio.h> +#include <kdebug.h> +#include <kiconloader.h> +#include <kcmenumngr.h> +#include <qpixmap.h> + +#define __IMG_CANVAS_CPP__ + +#include "imgscaledialog.h" +#include "img_canvas.h" + + + +#define MIN(x,y) (x<y?x:y) + + +inline void debug_rect( const char *name, QRect *r ) +{ + kdDebug(29000) << (name ? name: "NONAME") << ": " << r->x() << ", " << r->y() << ", " << r->width() << ", " << r->height() << endl; +} + + +class ImageCanvas::ImageCanvasPrivate +{ +public: + ImageCanvasPrivate() + : keepZoom(false), + readOnly(false), + scaleKind( UNSPEC ), + defaultScaleKind( FIT_ORIG ) + {} + + bool keepZoom; /* keep the zoom settings if images change */ + bool readOnly; + ScaleKinds scaleKind; + ScaleKinds defaultScaleKind; + + QValueList<QRect> highlightRects; +}; + +ImageCanvas::ImageCanvas(QWidget *parent, + const QImage *start_image, + const char *name ): + QScrollView( parent, name ), + m_contextMenu(0) +{ + d = new ImageCanvasPrivate(); + + scale_factor = 100; // means original size + maintain_aspect = true; + selected = new QRect; + selected->setWidth(0); + selected->setHeight(0); + + timer_id = 0; + pmScaled = 0; + + image = start_image; + moving = MOVE_NONE; + + QSize img_size; + + if( image && ! image->isNull() ) + { + img_size = image->size(); + pmScaled = new QPixmap( img_size ); + +#ifdef USE_KPIXMAPIO + *pmScaled = pixIO.convertToPixmap(*image); +#else + pmScaled->convertFromImage( *image ); +#endif + + acquired = true; + } else { + img_size = size(); + } + + + update_scaled_pixmap(); + + // timer-Start and stop + connect( this, SIGNAL( newRect()), SLOT( newRectSlot())); + connect( this, SIGNAL( noRect()), SLOT( noRectSlot())); + + //zoomOut();scrollview/scrollview + viewport()->setCursor( crossCursor ); + cr1 = 0; + cr2 = 0; + viewport()->setMouseTracking(TRUE); + viewport()->setBackgroundMode(PaletteBackground); + show(); + +} + +ImageCanvas::~ImageCanvas() +{ + kdDebug(29000) << "Destructor of ImageCanvas" << endl; + noRectSlot(); + if( selected ) delete selected; + selected = 0; + if( pmScaled ) delete pmScaled; + pmScaled = 0; + delete d; +} + +void ImageCanvas::deleteView( QImage *delimage ) +{ + const QImage *img = rootImage(); + + if( delimage == img ) + { + kdDebug(29000) << "ImageCanvas -> emiting newImage(0L)" << endl; + newImage( 0L ); + noRectSlot(); + } + +} + +void ImageCanvas::newImageHoldZoom( QImage *new_image ) +{ + bool holdZ = d->keepZoom; + + d->keepZoom = true; + newImage( new_image ); + d->keepZoom = holdZ; +} + +void ImageCanvas::newImage( QImage *new_image ) +{ + + /** do cleanups **/ + // dont free old image -> not yours + image = new_image; + + if( ! image || image->isNull()) + { + kdDebug(29000) << "newImage: Got Empty image !" << endl; + } + + if( pmScaled ) + { + delete pmScaled; + pmScaled = 0L; + } + + if( selected ) + { + noRectSlot(); + } + + /* throw away all highlights */ + d->highlightRects.clear(); + + /** handle the new image **/ + if( image ) + { + if( image->depth() == 1 ) { + pmScaled = new QPixmap( image->size(), 1 ); + } else { + int i = QPixmap::defaultDepth(); + pmScaled = new QPixmap( image->size(), i); + } + + // image->convertDepth(32); +#ifdef USE_KPIXMAPIO + + *pmScaled = pixIO.convertToPixmap(*image); +#else + pmScaled->convertFromImage( *image ); + // *pmScaled = image->convertToPixmap( ); +#endif + + acquired = true; + + if( d->keepZoom ) + { + kdDebug(29000) << "Preserving Zoom settings!" << endl; + } + else + { + kdDebug(29000) << "Resetting Zoom to original size!" << endl; + setScaleKind( defaultScaleKind() ); + } + + update_scaled_pixmap(); + setContentsPos(0,0); + } else { + kdDebug(29000) << "New image called without image => deleting!" << endl; + acquired = false; + resizeContents( 0,0 ); + } + + + kdDebug(29000) << "going to repaint!" << endl; + repaint( true ); + kdDebug(29000) << "repaint ok" << endl; +} + +QSize ImageCanvas::sizeHint() const +{ + return( QSize( 2, 2 )); +} + +void ImageCanvas::enableContextMenu( bool wantContextMenu ) +{ + if( wantContextMenu ) + { + if( ! m_contextMenu ) + { + m_contextMenu = new KPopupMenu(this, "IMG_CANVAS"); + + KContextMenuManager::insert( viewport(), m_contextMenu ); + } + } + else + { + /* remove all items */ + if( m_contextMenu ) m_contextMenu->clear(); + /* contextMenu is not deleted because there is no way to remove + * it from the contextMenuManager + */ + } + +} + +void ImageCanvas::handle_popup( int item ) +{ + if( item < ID_POP_ZOOM || item > ID_ORIG_SIZE ) return; + + if( ! image ) return; + ImgScaleDialog *zoomDia = 0; + + switch( item ) + { + case ID_POP_ZOOM: + + zoomDia = new ImgScaleDialog( this, getScaleFactor() ); + if( zoomDia->exec() ) + { + int sf = zoomDia->getSelected(); + setScaleKind(ZOOM); + setScaleFactor( sf ); + } + delete zoomDia; + zoomDia = 0; + break; + case ID_ORIG_SIZE: + setScaleKind( FIT_ORIG ); + break; + case ID_FIT_WIDTH: + setScaleKind( FIT_WIDTH ); + break; + case ID_FIT_HEIGHT: + setScaleKind( FIT_HEIGHT ); + break; + case ID_POP_CLOSE: + emit( closingRequested()); + break; + + default: break; + } + update_scaled_pixmap(); + repaint(); +} + + +/** + * Returns the selected rect in tenth of percent, not in absolute + * sizes, eg. 500 means 50.0 % of original width/height. + * That makes it easier to work with different scales and units + */ +QRect ImageCanvas::sel( void ) +{ + QRect retval; + retval.setCoords(0, 0, 0, 0); + + if( selected && image && selected->width()>MIN_AREA_WIDTH + && selected->height()>MIN_AREA_HEIGHT ) + { + /* Get the size in real image pixels */ + + // debug_rect( "PRE map", selected ); + QRect mapped = inv_scale_matrix.map( (const QRect) *selected ); + // debug_rect( "Postmap", &mapped ); + if( mapped.x() > 0 ) + retval.setLeft((int) (1000.0/( (double)image->width() / (double)mapped.x()))); + + if( mapped.y() > 0 ) + retval.setTop((int) (1000.0/( (double)image->height() / (double)mapped.y()))); + + if( mapped.width() > 0 ) + retval.setWidth((int) (1000.0/( (double)image->width() / (double)mapped.width()))); + + if( mapped.height() > 0 ) + retval.setHeight((int)(1000.0/( (double)image->height() / (double)mapped.height()))); + + } + // debug_rect( "sel() return", &retval ); + return( retval ); + +} + +bool ImageCanvas::selectedImage( QImage *retImg ) +{ + QRect r = sel(); + bool result = false; + + const QImage* entireImg = this->rootImage(); + + if( entireImg ) + { + QSize s = entireImg->size(); + + int x = (s.width() * r.x())/1000; + int y = (s.height() * r.y())/1000; + int w = (s.width() * r.width())/1000; + int h = (s.height() * r.height())/1000; + + if( w > 0 && h > 0 ) { + *retImg = entireImg->copy( x, y, w, h ); + result = true; + } + } + return(result); + +} + + +void ImageCanvas::drawContents( QPainter * p, int clipx, int clipy, int clipw, int cliph ) +{ + if( !pmScaled ) return; + int x1 = 0; + int y1 = 0; + + int x2 = pmScaled->width(); + int y2 = pmScaled->height(); + + if (x1 < clipx) x1=clipx; + if (y1 < clipy) y1=clipy; + if (x2 > clipx+clipw-1) x2=clipx+clipw-1; + if (y2 > clipy+cliph-1) y2=clipy+cliph-1; + + // Paint using the small coordinates... + // p->scale( used_xscaler, used_yscaler ); + // p->scale( used_xscaler, used_yscaler ); + if ( x2 >= x1 && y2 >= y1 ) { + p->drawPixmap( x1, y1, *pmScaled, x1, y1 ); //, clipw, cliph); + // p->setBrush( red ); + // p->drawRect( x1, y1, clipw, cliph ); + } + + // p->fillRect(x1, y1, x2-x1+1, y2-y1+1, red); + +} + +void ImageCanvas::timerEvent(QTimerEvent *) +{ + if(moving!=MOVE_NONE || !acquired ) return; + cr1++; + QPainter p(viewport()); + // p.setWindow( contentsX(), contentsY(), contentsWidth(), contentsHeight()); + drawAreaBorder(&p); +} + +void ImageCanvas::newRectSlot( QRect newSel ) +{ + QRect to_map; + QPainter p(viewport()); + drawAreaBorder(&p,TRUE); + selected->setWidth(0); + selected->setHeight(0); + + emit( noRect() ); + + if ( image ) + { + int rx, ry, rw, rh; + int w = image->width(); + int h = image->height(); + + kdDebug(29000) << "ImageCanvas: Image size is " << w << "x" << h << endl; + kdDebug(29000) << "ImageCanvas got selection Rect: W=" << newSel.width() << ", H=" << newSel.height() << endl; + // to_map.setWidth(static_cast<int>(w * newSel.width() / 1000.0)); + rw = static_cast<int>(w * newSel.width() / 1000.0); + rx = static_cast<int>(w * newSel.x() / 1000.0); + ry = static_cast<int>(h * newSel.y() / 1000.0); + rh = static_cast<int>(h * newSel.height() / 1000.0); + kdDebug(29000) << "ImageCanvas: scaled Height is " << rh << endl; + + to_map.setRect( rx, ry, rw, rh ); + + kdDebug(29000) << "ImageCanvas Selection: W=" << to_map.width() << " H=" << to_map.height() << endl; + *selected = scale_matrix.map( to_map ); + kdDebug(29000) << "ImageCanvas Selection: W=" << selected->width() << " H=" << selected->height() << endl; + emit( newRect( sel() )); + newRectSlot(); + } +} + + + +void ImageCanvas::newRectSlot( ) +{ + // printf( "Timer switched on !\n" ); + if( timer_id == 0 ) + timer_id = startTimer( 100 ); +} + +void ImageCanvas::noRectSlot( void ) +{ + // printf( "Timer switched off !\n" ); + if( timer_id ) { + killTimer( timer_id ); + timer_id = 0; + } + + if( selected ) + selected->setCoords( 0,0,0,0 ); +} + +void ImageCanvas::viewportMousePressEvent(QMouseEvent *ev) +{ + if( ! acquired || ! image ) return; + + if(ev->button()==LeftButton ) + { + + int cx = contentsX(), cy = contentsY(); + int x = lx = ev->x(),y = ly = ev->y(); + + int ix, iy; + scale_matrix.map( image->width(), image->height(), &ix, &iy ); + if( x > ix-cx || y > iy-cy ) return; + + if( moving == MOVE_NONE ) + { + QPainter p( viewport()); + drawAreaBorder(&p,TRUE); + moving = classifyPoint( x+cx ,y+cy); + + if(moving == MOVE_NONE) + { //Create new area + selected->setCoords( x+cx, y+cy, x+cx, y+cy ); + moving = MOVE_BOTTOM_RIGHT; + } + drawAreaBorder(&p); + } + } +} + + +void ImageCanvas::viewportMouseReleaseEvent(QMouseEvent *ev) +{ + if(ev->button()!=LeftButton || !acquired ) return; + + //// debug( "Mouse Release at %d/%d", ev->x(), ev->y()); + if(moving!=MOVE_NONE) { + QPainter p(viewport()); + drawAreaBorder(&p,TRUE); + moving = MOVE_NONE; + *selected = selected->normalize(); + + if(selected->width () < MIN_AREA_WIDTH || + selected->height() < MIN_AREA_HEIGHT) + { + selected->setWidth(0); + selected->setHeight(0); + emit noRect(); + return; + } + + drawAreaBorder(&p); + emit newRect( sel() ); + emit newRect( ); + } +} + + +void ImageCanvas::viewportMouseMoveEvent(QMouseEvent *ev) +{ + if( ! acquired || !image) return; + + static cursor_type ps = HREN; + int x = ev->x(), y = ev->y(); + int cx = contentsX(), cy = contentsY(); + + // debug("Mouse Coord x: %d, y: %d | cx: %d, cy: %d", x,y, cx, cy ); + // dont draw out of the scaled Pixmap + if( x < 0 ) x = 0; + int ix, iy; + scale_matrix.map( image->width(), image->height(), &ix, &iy ); + if( x >= ix ) return; + + if( y < 0 ) y = 0; + if( y >= iy ) return; + + // debug( "cx, cy: %dx%d, x,y: %d, %d", + // cx, cy, x, y ); + +#if 0 + if( moving == MOVE_NONE ) + moving = classifyPoint(x, y); +#endif + switch( moving!=MOVE_NONE ? moving:classifyPoint(x+cx,y+cy) ) { + case MOVE_NONE: + if(ps!=CROSS) { + viewport()->setCursor(crossCursor); + ps = CROSS; + } + break; + case MOVE_LEFT: + case MOVE_RIGHT: + if(ps!=HSIZE) { + viewport()->setCursor(sizeHorCursor); + ps = HSIZE; + } + break; + case MOVE_TOP: + case MOVE_BOTTOM: + if(ps!=VSIZE) { + viewport()->setCursor(sizeVerCursor); + ps = VSIZE; + } + break; + case MOVE_TOP_LEFT: + case MOVE_BOTTOM_RIGHT: + if(ps!=FDIAG) { + viewport()->setCursor(sizeFDiagCursor); + ps = FDIAG; + } + break; + case MOVE_TOP_RIGHT: + case MOVE_BOTTOM_LEFT: + if(ps!=BDIAG) { + viewport()->setCursor(sizeBDiagCursor); + ps = BDIAG; + } + break; + case MOVE_WHOLE: + if(ps!=ALL) { + viewport()->setCursor(sizeAllCursor); + ps = ALL; + } + } + //At ButtonRelease : normalize + clip + if( moving!=MOVE_NONE ) { + int mx, my; + QPainter p(viewport()); + drawAreaBorder(&p,TRUE); + switch(moving) { + case MOVE_NONE: //Just to make compiler happy + break; + case MOVE_TOP_LEFT: + selected->setLeft( x + cx ); + case MOVE_TOP: // fall through + selected->setTop( y + cy ); + break; + case MOVE_TOP_RIGHT: + selected->setTop( y + cy ); + case MOVE_RIGHT: // fall through + selected->setRight( x + cx ); + break; + case MOVE_BOTTOM_LEFT: + selected->setBottom( y + cy ); + case MOVE_LEFT: // fall through + selected->setLeft( x + cx ); + break; + case MOVE_BOTTOM_RIGHT: + selected->setRight( x + cx ); + case MOVE_BOTTOM: // fall through + selected->setBottom( y + cy ); + break; + case MOVE_WHOLE: + if( selected ) + { + // lx is the Last x-Koord from the run before (global) + mx = x-lx; my = y-ly; + /* Check if rectangle would run out of the image on right and bottom */ + if( selected->x()+ selected->width()+mx >= ix-cx ) + { + mx = ix -cx - selected->width() - selected->x(); + kdDebug(29000) << "runs out !" << endl; + } + if( selected->y()+ selected->height()+my >= iy-cy ) + { + my = iy -cy - selected->height() - selected->y(); + kdDebug(29000) << "runs out !" << endl; + } + + /* Check if rectangle would run out of the image on left and top */ + if( selected->x() +mx < 0 ) + mx = -selected->x(); + if( selected->y()+ +my < 0 ) + my = -selected->y(); + + x = mx+lx; y = my+ly; + + selected->moveBy( mx, my ); + + } + } + drawAreaBorder(&p); + lx = x; + ly = y; + } +} + + +void ImageCanvas::setScaleFactor( int i ) +{ + kdDebug(29000) << "Setting Scalefactor to " << i << endl; + scale_factor = i; + if( i == 0 ){ + kdDebug(29000) << "Setting Dynamic Scaling!" << endl; + setScaleKind(DYNAMIC); + } + update_scaled_pixmap(); +} + + +void ImageCanvas::resizeEvent( QResizeEvent * event ) +{ + QScrollView::resizeEvent( event ); + update_scaled_pixmap(); + +} + +void ImageCanvas::update_scaled_pixmap( void ) +{ + resizeContents( 0,0 ); + updateScrollBars(); + if( !pmScaled || !image) + { // debug( "Pixmap px is null in Update_scaled" ); + return; + } + + QApplication::setOverrideCursor(waitCursor); + + kdDebug(28000) << "Updating scaled_pixmap" << endl; + if( scaleKind() == DYNAMIC ) + kdDebug(28000) << "Scaling DYNAMIC" << endl; + QSize noSBSize( visibleWidth(), visibleHeight()); + const int sbWidth = kapp->style().pixelMetric( QStyle::PM_ScrollBarExtent ); + + // if( verticalScrollBar()->visible() ) noSBSize.width()+=sbWidth; + // if( horizontalScrollBar()->visible() ) noSBSize.height()+=sbWidth; + + switch( scaleKind() ) + { + case DYNAMIC: + // do scaling to window-size + used_yscaler = ((double)viewport()-> height()) / ((double)image->height()); + used_xscaler = ((double)viewport()-> width()) / ((double)image->width()); + scale_factor = 0; + break; + case FIT_ORIG: + used_yscaler = used_xscaler = 1.0; + scale_factor = 100; + break; + case FIT_WIDTH: + used_xscaler = used_yscaler = double(noSBSize.width()) / double(image->width()); + if( used_xscaler * image->height() >= noSBSize.height() ) + { + /* substract for scrollbar */ + used_xscaler = used_yscaler = double(noSBSize.width() - sbWidth) / + double(image->width()); + kdDebug(29000) << "FIT WIDTH scrollbar to substract: " << sbWidth << endl; + } + scale_factor = 100*used_xscaler; + break; + case FIT_HEIGHT: + used_yscaler = used_xscaler = double(noSBSize.height())/double(image->height()); + + // scale = int(100.0 * noSBSize.height() / image->height()); + if( used_xscaler * image->width() >= noSBSize.width() ) + { + /* substract for scrollbar */ + used_xscaler = used_yscaler = double(noSBSize.height() - sbWidth) / + double(image->height()); + + kdDebug(29000) << "FIT HEIGHT scrollbar to substract: " << sbWidth << endl; + // scale = int(100.0*(noSBSize.height() -sbWidth) / image->height()); + } + scale_factor = 100*used_xscaler; + + break; + case ZOOM: + used_xscaler = used_yscaler = double(getScaleFactor())/100.0; + scale_factor = 100*used_xscaler; + break; + default: + break; + } + + // reconvert the selection to orig size + if( selected ) { + *selected = inv_scale_matrix.map( (const QRect) *selected ); + } + + scale_matrix.reset(); // transformation matrix + inv_scale_matrix.reset(); + + + if( scaleKind() == DYNAMIC && maintain_aspect ) { + // printf( "Skaler: x: %f, y: %f\n", x_scaler, y_scaler ); + used_xscaler = used_yscaler < used_xscaler ? + used_yscaler : used_xscaler; + used_yscaler = used_xscaler; + } + + scale_matrix.scale( used_xscaler, used_yscaler ); // define scale factors + inv_scale_matrix = scale_matrix.invert(); // for redraw of selection + + if( selected ) { + *selected = scale_matrix.map( (const QRect )*selected ); + } + +#ifdef USE_KPIXMAPIO + *pmScaled = pixIO.convertToPixmap(*image); +#else + pmScaled->convertFromImage( *image ); +#endif + + *pmScaled = pmScaled->xForm( scale_matrix ); // create scaled pixmap + + /* Resizing to 0,0 never may be dropped, otherwise there are problems + * with redrawing of new images. + */ + resizeContents( static_cast<int>(image->width() * used_xscaler), + static_cast<int>(image->height() * used_yscaler ) ); + + QApplication::restoreOverrideCursor(); +} + + +void ImageCanvas::drawHAreaBorder(QPainter &p,int x1,int x2,int y,int r) +{ + if( ! acquired || !image ) return; + + if(moving!=MOVE_NONE) cr2 = 0; + int inc = 1; + int cx = contentsX(), cy = contentsY(); + if(x2 < x1) inc = -1; + + if(!r) { + if(cr2 & 4) p.setPen(black); + else p.setPen(white); + } else if(!acquired) p.setPen(QPen(QColor(150,150,150))); + + for(;;) { + if(rect().contains(QPoint(x1,y))) { + if( r && acquired ) { + int re_x1, re_y; + inv_scale_matrix.map( x1+cx, y+cy, &re_x1, &re_y ); + re_x1 = MIN( image->width()-1, re_x1 ); + re_y = MIN( image->height()-1, re_y ); + + p.setPen( QPen( QColor( image->pixel(re_x1, re_y)))); + } + p.drawPoint(x1,y); + } + if(!r) { + cr2++; + cr2 &= 7; + if(!(cr2&3)) { + if(cr2&4) p.setPen(black); + else p.setPen(white); + } + } + if(x1==x2) break; + x1 += inc; + } + +} + +void ImageCanvas::drawVAreaBorder(QPainter &p, int x, int y1, int y2, int r ) +{ + if( ! acquired || !image ) return; + if( moving!=MOVE_NONE ) cr2 = 0; + int inc = 1; + if( y2 < y1 ) inc = -1; + + int cx = contentsX(), cy = contentsY(); + + + if( !r ) { + if( cr2 & 4 ) p.setPen(black); + else + p.setPen(white); + } else + if( !acquired ) p.setPen( QPen( QColor(150,150,150) ) ); + + for(;;) { + if(rect().contains( QPoint(x,y1) )) { + if( r && acquired ) { + int re_y1, re_x; + inv_scale_matrix.map( x+cx, y1+cy, &re_x, &re_y1 ); + re_x = MIN( image->width()-1, re_x ); + re_y1 = MIN( image->height()-1, re_y1 ); + + p.setPen( QPen( QColor( image->pixel( re_x, re_y1) ))); + } + p.drawPoint(x,y1); + } + + if(!r) { + cr2++; + cr2 &= 7; + if(!(cr2&3)) { + if(cr2&4) p.setPen(black); + else p.setPen(white); + } + } + if(y1==y2) break; + y1 += inc; + } + +} + +void ImageCanvas::drawAreaBorder(QPainter *p,int r ) +{ + if(selected->isNull()) return; + + cr2 = cr1; + int xinc = 1; + if( selected->right() < selected->left()) xinc = -1; + int yinc = 1; + if( selected->bottom() < selected->top()) yinc = -1; + + if( selected->width() ) + drawHAreaBorder(*p, + selected->left() - contentsX(), + selected->right()- contentsX(), + selected->top() - contentsY(), r ); + if( selected->height() ) { + drawVAreaBorder(*p, + selected->right() - contentsX(), + selected->top()- contentsY()+yinc, + selected->bottom()- contentsY(),r); + if(selected->width()) { + drawHAreaBorder(*p, + selected->right()-xinc- contentsX(), + selected->left()- contentsX(), + selected->bottom()- contentsY(),r); + drawVAreaBorder(*p, + selected->left()- contentsX(), + selected->bottom()-yinc- contentsY(), + selected->top()- contentsY()+yinc,r); + } + } +} + +// x and y come as real pixmap-coords, not contents-coords +preview_state ImageCanvas::classifyPoint(int x,int y) +{ + if(selected->isEmpty()) return MOVE_NONE; + + QRect a = selected->normalize(); + + int top = 0,left = 0,right = 0,bottom = 0; + int lx = a.left()-x, rx = x-a.right(); + int ty = a.top()-y, by = y-a.bottom(); + + if( a.width() > delta*2+2 ) + lx = abs(lx), rx = abs(rx); + + if(a.height()>delta*2+2) + ty = abs(ty), by = abs(by); + + if( lx>=0 && lx<=delta ) left++; + if( rx>=0 && rx<=delta ) right++; + if( ty>=0 && ty<=delta ) top++; + if( by>=0 && by<=delta ) bottom++; + if( y>=a.top() &&y<=a.bottom() ) { + if(left) { + if(top) return MOVE_TOP_LEFT; + if(bottom) return MOVE_BOTTOM_LEFT; + return MOVE_LEFT; + } + if(right) { + if(top) return MOVE_TOP_RIGHT; + if(bottom) return MOVE_BOTTOM_RIGHT; + return MOVE_RIGHT; + } + } + if(x>=a.left()&&x<=a.right()) { + if(top) return MOVE_TOP; + if(bottom) return MOVE_BOTTOM; + if(selected->contains(QPoint(x,y))) return MOVE_WHOLE; + } + return MOVE_NONE; +} + + +int ImageCanvas::getBrightness() const +{ + return brightness; +} + +int ImageCanvas::getContrast() const +{ + return contrast; +} + +int ImageCanvas::getGamma() const +{ + return gamma; +} + +int ImageCanvas::getScaleFactor() const +{ + return( scale_factor ); +} + +const QImage *ImageCanvas::rootImage( ) +{ + return( image ); +} + +void ImageCanvas::setReadOnly( bool ro ) +{ + d->readOnly = ro; + emit( imageReadOnly(ro) ); +} + +bool ImageCanvas::readOnly() +{ + return d->readOnly; +} + +void ImageCanvas::setBrightness(int b) +{ + brightness = b; +} + +void ImageCanvas::setContrast(int c) +{ + contrast = c; +} + +void ImageCanvas::setGamma(int c) +{ + gamma = c; +} + +void ImageCanvas::setKeepZoom( bool k ) +{ + d->keepZoom = k; +} + +ImageCanvas::ScaleKinds ImageCanvas::scaleKind() +{ + if( d->scaleKind == UNSPEC ) + return defaultScaleKind(); + else + return d->scaleKind; +} + +ImageCanvas::ScaleKinds ImageCanvas::defaultScaleKind() +{ + return d->defaultScaleKind; +} + +void ImageCanvas::setScaleKind( ScaleKinds k ) +{ + if( k == d->scaleKind ) return; // no change, return + d->scaleKind = k; + + emit scalingChanged(scaleKindString()); +} + +void ImageCanvas::setDefaultScaleKind( ScaleKinds k ) +{ + d->defaultScaleKind = k; +} + +const QString ImageCanvas::imageInfoString( int w, int h, int d ) +{ + if( w == 0 && h == 0 && d == 0 ) + { + if( image ) + { + w = image->width(); + h = image->height(); + d = image->depth(); + } + else + return QString("-"); + } + return i18n("%1x%2 pixel, %3 bit").arg(w).arg(h).arg(d); +} + + +const QString ImageCanvas::scaleKindString() +{ + switch( scaleKind() ) + { + case DYNAMIC: + return i18n("Fit window best"); + break; + case FIT_ORIG: + return i18n("Original size"); + break; + case FIT_WIDTH: + return i18n("Fit Width"); + break; + case FIT_HEIGHT: + return i18n("Fit Height"); + break; + case ZOOM: + return i18n("Zoom to %1 %%").arg( QString::number(getScaleFactor())); + break; + default: + return i18n("Unknown scaling!"); + break; + } +} + + +int ImageCanvas::highlight( const QRect& rect, const QPen& pen, const QBrush&, bool ensureVis ) +{ + QRect saveRect; + saveRect.setRect( rect.x()-2, rect.y()-2, rect.width()+4, rect.height()+4 ); + d->highlightRects.append( saveRect ); + + int idx = d->highlightRects.findIndex(saveRect); + + QRect targetRect = scale_matrix.map( rect ); + + QPainter p( pmScaled ); + + p.setPen(pen); + p.drawLine( targetRect.x(), targetRect.y()+targetRect.height(), + targetRect.x()+targetRect.width(), targetRect.y()+targetRect.height() ); + + p.flush(); + updateContents(targetRect.x()-1, targetRect.y()-1, + targetRect.width()+2, targetRect.height()+2 ); + + if( ensureVis ) + { + QPoint p = targetRect.center(); + ensureVisible( p.x(), p.y(), 10+targetRect.width()/2, 10+targetRect.height()/2 ); + } + + return idx; +} + +void ImageCanvas::removeHighlight( int idx ) +{ + if( (unsigned) idx >= d->highlightRects.count() ) + { + kdDebug(28000) << "removeHighlight: Not a valid index" << endl; + return; + } + + /* take the rectangle from the stored highlight rects and map it to the viewer scaling */ + QRect r = d->highlightRects[idx]; + d->highlightRects.remove(r); + QRect targetRect = scale_matrix.map( r ); + + /* create a small pixmap with a copy of the region in question of the original image */ + QPixmap origPix; + origPix.convertFromImage( image->copy(r) ); + /* and scale it */ + QPixmap scaledPix = origPix.xForm( scale_matrix ); + /* and finally draw it */ + QPainter p( pmScaled ); + p.drawPixmap( targetRect, scaledPix ); + p.flush(); + + /* update the viewers contents */ + updateContents(targetRect.x()-1, targetRect.y()-1, + targetRect.width()+2, targetRect.height()+2 ); + + +} + + +#include "img_canvas.moc" diff --git a/libkscan/img_canvas.h b/libkscan/img_canvas.h new file mode 100644 index 00000000..5abf267c --- /dev/null +++ b/libkscan/img_canvas.h @@ -0,0 +1,221 @@ +/* This file is part of the KDE Project + Copyright (C) 1999 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __IMG_CANVAS_H__ +#define __IMG_CANVAS_H__ + +#include <qwidget.h> +#include <qrect.h> +#include <stdlib.h> +#include <qsize.h> +#include <qwmatrix.h> +#include <qscrollview.h> +#include <qstrlist.h> + +#ifdef USE_KPIXMAPIO +#include <kpixmapio.h> +#endif + +class QPopupMenu; +class QPixmap; +class QImage; +class QPainter; + +enum preview_state { + MOVE_NONE, + MOVE_TOP_LEFT, + MOVE_TOP_RIGHT, + MOVE_BOTTOM_LEFT, + MOVE_BOTTOM_RIGHT, + MOVE_LEFT, + MOVE_RIGHT, + MOVE_TOP, + MOVE_BOTTOM, + MOVE_WHOLE +}; + +enum cursor_type { + CROSS, + VSIZE, + HSIZE, + BDIAG, + FDIAG, + ALL, + HREN +}; + +const int MIN_AREA_WIDTH = 3; +const int MIN_AREA_HEIGHT = 3; +const int delta = 3; +#ifdef __PREVIEW_CPP__ +int max_dpi = 600; +#else +extern int max_dpi; +#endif + + + +class ImageCanvas: public QScrollView +{ + Q_OBJECT + Q_ENUMS( PopupIDs ) + Q_PROPERTY( int brightness READ getBrightness WRITE setBrightness ) + Q_PROPERTY( int contrast READ getContrast WRITE setContrast ) + Q_PROPERTY( int gamma READ getGamma WRITE setGamma ) + Q_PROPERTY( int scale_factor READ getScaleFactor WRITE setScaleFactor ) + +public: + ImageCanvas( QWidget *parent = 0, + const QImage *start_image = 0, + const char *name = 0); + ~ImageCanvas( ); + + int getBrightness() const; + int getContrast() const; + int getGamma() const; + + int getScaleFactor() const; + + const QImage *rootImage(); + + bool hasImage( void ) { return acquired; } + QPopupMenu* contextMenu() { return m_contextMenu; } + QRect sel( void ); + + enum ScaleKinds { UNSPEC, DYNAMIC, FIT_ORIG, FIT_WIDTH, FIT_HEIGHT, ZOOM }; + + enum PopupIDs { ID_POP_ZOOM, ID_POP_CLOSE, ID_FIT_WIDTH, + ID_FIT_HEIGHT, ID_ORIG_SIZE }; + + bool selectedImage( QImage* ); + + ScaleKinds scaleKind(); + const QString scaleKindString(); + + ScaleKinds defaultScaleKind(); + + const QString imageInfoString( int w=0, int h=0, int d=0 ); + +public slots: + void setBrightness(int); + void setContrast(int ); + void setGamma(int ); + + void toggleAspect( int aspect_in_mind ) + { + maintain_aspect = aspect_in_mind; + repaint(); + } + virtual QSize sizeHint() const; + void newImage( QImage* ); + void newImageHoldZoom( QImage* ); + void deleteView( QImage *); + void newRectSlot(); + void newRectSlot( QRect newSel ); + void noRectSlot( void ); + void setScaleFactor( int i ); + void handle_popup(int item ); + void enableContextMenu( bool wantContextMenu ); + void setKeepZoom( bool k ); + void setScaleKind( ScaleKinds k ); + void setDefaultScaleKind( ScaleKinds k ); + + /** + * Highlight a rectangular area on the current image using the given brush + * and pen. + * The function returns a id that needs to be given to the remove method. + */ + int highlight( const QRect&, const QPen&, const QBrush&, bool ensureVis=false ); + + /** + * reverts the highlighted region back to normal view. + */ + void removeHighlight( int idx = -1 ); + + /** + * permit to do changes to the image that are saved back to the file + */ + void setReadOnly( bool ); + + bool readOnly(); + +signals: + void noRect( void ); + void newRect( void ); + void newRect( QRect ); + void scalingRequested(); + void closingRequested(); + void scalingChanged( const QString& ); + /** + * signal emitted if the permission of the currently displayed image changed, + * ie. if it goes from writeable to readable. + * @param shows if the image is now read only (true) or not. + */ + void imageReadOnly( bool isRO ); + +protected: + void drawContents( QPainter * p, int clipx, int clipy, int clipw, int cliph ); + + void timerEvent(QTimerEvent *); + void viewportMousePressEvent(QMouseEvent *); + void viewportMouseReleaseEvent(QMouseEvent *); + void viewportMouseMoveEvent(QMouseEvent *); + + void resizeEvent( QResizeEvent * event ); + +private: + QStrList urls; + + int scale_factor; + const QImage *image; + int brightness, contrast, gamma; + + +#ifdef USE_KPIXMAPIO + KPixmapIO pixIO; +#endif + + QWMatrix scale_matrix; + QWMatrix inv_scale_matrix; + QPixmap *pmScaled; + float used_yscaler; + float used_xscaler; + QPopupMenu *m_contextMenu; + bool maintain_aspect; + + int timer_id; + QRect *selected; + preview_state moving; + int cr1,cr2; + int lx,ly; + bool acquired; + + /* private functions for the running ant */ + void drawHAreaBorder(QPainter &p,int x1,int x2,int y,int r = FALSE); + void drawVAreaBorder(QPainter &p,int x,int y1,int y2,int r = FALSE); + void drawAreaBorder(QPainter *p,int r = FALSE); + void update_scaled_pixmap( void ); + + preview_state classifyPoint(int x,int y); + + class ImageCanvasPrivate; + ImageCanvasPrivate *d; +}; + +#endif diff --git a/libkscan/imgscaledialog.cpp b/libkscan/imgscaledialog.cpp new file mode 100644 index 00000000..535d3ffd --- /dev/null +++ b/libkscan/imgscaledialog.cpp @@ -0,0 +1,178 @@ +/* This file is part of the KDE Project + Copyright (C) 1999 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <klocale.h> +#include <kdebug.h> +#include <qbuttongroup.h> +#include <qradiobutton.h> +#include <knumvalidator.h> +#include "imgscaledialog.h" + +/* ############################################################################## */ + + +ImgScaleDialog::ImgScaleDialog( QWidget *parent, int curr_sel, + const char *name ) + :KDialogBase( parent, name , true, i18n("Zoom"), + Ok|Cancel, Ok, true ) +{ + // setCaption (i18n ("Image Zoom")); + selected = curr_sel; + int one_is_selected = false; + enableButtonSeparator( false ); + + // (void) new QLabel( , main, "Page"); + // + // makeMainWidget(); + QButtonGroup *radios = new QButtonGroup ( 2, Qt::Horizontal, this ); + setMainWidget(radios); + Q_CHECK_PTR(radios); + radios->setTitle( i18n("Select Image Zoom") ); + + connect( radios, SIGNAL( clicked( int )), + this, SLOT( setSelValue( int ))); + + // left gap: smaller Image + QRadioButton *rb25 = new QRadioButton (i18n ("25 %"), radios); + if( curr_sel == 25 ){ + rb25->setChecked( true ); + one_is_selected = true; + } + + QRadioButton *rb50 = new QRadioButton (i18n ("50 %"), radios ); + if( curr_sel == 50 ){ + rb50->setChecked( true ); + one_is_selected = true; + } + + QRadioButton *rb75 = new QRadioButton (i18n ("75 %"), radios ); + if( curr_sel == 75 ) { + rb75->setChecked( true ); + one_is_selected = true; + } + + QRadioButton *rb100 = new QRadioButton (i18n ("100 %"), radios); + if( curr_sel == 100 ) { + rb100->setChecked( true ); + one_is_selected = true; + } + + QRadioButton *rb150 = new QRadioButton (i18n ("150 %"), radios); + if( curr_sel == 150 ) { + rb150->setChecked( true ); + one_is_selected = true; + } + + QRadioButton *rb200 = new QRadioButton (i18n ("200 %"), radios ); + if( curr_sel == 200 ) { + rb200->setChecked( true ); + one_is_selected = true; + } + + QRadioButton *rb300 = new QRadioButton (i18n ("300 %"), radios ); + if( curr_sel == 300 ) { + rb300->setChecked( true ); + one_is_selected = true; + } + + QRadioButton *rb400 = new QRadioButton (i18n ("400 %"), radios); + if( curr_sel == 400 ) { + rb400->setChecked( true ); + one_is_selected = true; + } + + // Custom Scaler at the bottom + QRadioButton *rbCust = new QRadioButton (i18n ("Custom scale factor:"), + radios); + if( ! one_is_selected ) + rbCust->setChecked( true ); + + + leCust = new QLineEdit( radios ); + QString sn; + sn.setNum(curr_sel ); + leCust->setValidator( new KIntValidator( leCust ) ); + leCust->setText(sn ); + connect( leCust, SIGNAL( textChanged( const QString& )), + this, SLOT( customChanged( const QString& ))); + connect( rbCust, SIGNAL( toggled( bool )), + this, SLOT(enableAndFocus(bool))); + leCust->setEnabled( rbCust->isChecked()); + + +} + +void ImgScaleDialog::customChanged( const QString& s ) +{ + bool ok; + int okval = s.toInt( &ok ); + if( ok && okval > 5 && okval < 1000 ) { + selected = okval; + emit( customScaleChange( okval )); + } else { + kdDebug(29000) << "ERR: To large, to smale, or whatever shitty !" << endl; + } +} + +// This slot is called, when the user changes the Scale-Selection +// in the button group. The value val is the index of the active +// button which is translated to the Scale-Size in percent. +// If custom size is selected, the ScaleSize is read from the +// QLineedit. +// +void ImgScaleDialog::setSelValue( int val ) +{ + const int translator[]={ 25, 50, 75, 100, 150,200,300, 400, -1 }; + const size_t translator_size = sizeof( translator ) / sizeof(int); + int old_sel = selected; + + // Check if value is in Range + if( val >= 0 && val < (int) translator_size ) { + selected = translator[val]; + + // Custom size selected + if( selected == -1 ) { + QString s = leCust->text(); + + bool ok; + int okval = s.toInt( &ok ); + if( ok ) { + selected = okval; + emit( customScaleChange( okval )); + } else { + selected = old_sel; + } + } // Selection is not custom + } else { + kdDebug(29000) << "ERR: Invalid size selected !" << endl; + } + // debug( "SetValue: Selected Scale is %d", selected ); +} + + +int ImgScaleDialog::getSelected() const +{ + return( selected ); +} + + +/* ############################################################################## */ + + +#include "imgscaledialog.moc" diff --git a/libkscan/imgscaledialog.h b/libkscan/imgscaledialog.h new file mode 100644 index 00000000..8bbe1963 --- /dev/null +++ b/libkscan/imgscaledialog.h @@ -0,0 +1,62 @@ +/* This file is part of the KDE Project + Copyright (C) 1999 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __IMGSCALEDIALOG_H__ +#define __IMGSCALEDIALOG_H__ + +#include <qlineedit.h> +#include <kdialogbase.h> + + +/* ---------------------------------------------------------------------- + * The ImgScaleDialg is a small dialog to be used by the image canvas. It + * allows the user to select a zoom factor in percent, either in steps + * or as a custom value. + */ +class ImgScaleDialog : public KDialogBase +{ + Q_OBJECT + Q_PROPERTY( int selected READ getSelected WRITE setSelValue ) + +public: + ImgScaleDialog( QWidget *parent, int curr_sel = 100, + const char *name = 0 ); + +public slots: + void enableAndFocus( bool b ) + { + leCust->setEnabled( b ); leCust->setFocus(); + } + + void setSelValue( int val ); + int getSelected() const; + +signals: + void customScaleChange( int ); +public slots: + void customChanged( const QString& ); +private: + QLineEdit *leCust; + int selected; + + class ImgScaleDialogPrivate; + ImgScaleDialogPrivate *d; +}; + +#endif diff --git a/libkscan/imgscaninfo.cpp b/libkscan/imgscaninfo.cpp new file mode 100644 index 00000000..a3287a5b --- /dev/null +++ b/libkscan/imgscaninfo.cpp @@ -0,0 +1,75 @@ +/* This file is part of the KDE Project + Copyright (C) 2004 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "imgscaninfo.h" + +#include <klocale.h> +#include <kdebug.h> + + +/* ############################################################################## */ + +ImgScanInfo::ImgScanInfo() + :m_xRes(0), + m_yRes(0), + d(0) +{ + + +} + +void ImgScanInfo::setXResolution( int xres ) +{ + m_xRes = xres; +} + +int ImgScanInfo::getXResolution() +{ + return m_xRes; +} + +void ImgScanInfo::setYResolution( int yres ) +{ + m_yRes = yres; +} + +int ImgScanInfo::getYResolution() +{ + return m_yRes; +} + +void ImgScanInfo::setMode( const QString& smode ) +{ + m_mode = smode; +} + +QString ImgScanInfo::getMode() +{ + return m_mode; +} + +void ImgScanInfo::setScannerName( const QString& name ) +{ + m_scanner = name; +} + +QString ImgScanInfo::getScannerName() +{ + return m_scanner; +} diff --git a/libkscan/imgscaninfo.h b/libkscan/imgscaninfo.h new file mode 100644 index 00000000..05ff63e2 --- /dev/null +++ b/libkscan/imgscaninfo.h @@ -0,0 +1,52 @@ +/* This file is part of the KDE Project + Copyright (C) 1999 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __IMGSCANINFO_H__ +#define __IMGSCANINFO_H__ + +#include <qstring.h> + +/* ---------------------------------------------------------------------- + * + */ +class ImgScanInfo +{ +public: + ImgScanInfo(); + + int getXResolution(); + int getYResolution(); + QString getMode(); + QString getScannerName(); + + void setXResolution( int ); + void setYResolution( int ); + void setMode( const QString& ); + void setScannerName( const QString& ); +private: + int m_xRes; + int m_yRes; + QString m_mode; + QString m_scanner; + + class ImgScanInfoPrivate; + ImgScanInfoPrivate *d; +}; + +#endif diff --git a/libkscan/kgammatable.cpp b/libkscan/kgammatable.cpp new file mode 100644 index 00000000..4f1ef64f --- /dev/null +++ b/libkscan/kgammatable.cpp @@ -0,0 +1,93 @@ +/* This file is part of the KDE Project + Copyright (C) 2002 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <math.h> + +#include <kdebug.h> +#include "kgammatable.h" + +KGammaTable::KGammaTable( int gamma, int brightness, int contrast ) + : QObject() +{ + g = gamma < 1 ? 1 : gamma; + b = brightness; + c = contrast; + gt.resize(256); + calcTable(); +} + +const KGammaTable& KGammaTable::operator=(const KGammaTable& gt) +{ + if( this != > ) + { + g = gt.g; + b = gt.b; + c = gt.c; + + calcTable(); + } + + return( *this ); +} + +inline SANE_Word adjust( SANE_Word x, int gl,int bl,int cl) +{ + //printf("x : %d, x/256 : %lg, g : %d, 100/g : %lg", + // x,(double)x/256.0,g,100.0/(double)g); + x = ( SANE_Int )(256*pow((double)x/256.0,100.0/(double)gl)); + x = ((65536/(128-cl)-256)*(x-128)>>8)+128+bl; + if(x<0) x = 0; else if(x>255) x = 255; + //printf(" -> %d\n",x); + return x; +} + +void KGammaTable::calcTable( ) +{ + int br = (b<<8)/(128-c); + int gr = g; + int cr = c; + + if( gr == 0 ) + { + kdDebug(29000) << "Cant calc table -> would raise div. by zero !" << endl; + return; + } + + for( SANE_Word i = 0; i<256; i++) + gt[i] = adjust(i, gr, br, cr ); + + dirty = false; +} + +void KGammaTable::setAll( int gamma, int brightness, int contrast ) +{ + g = gamma < 1 ? 1 : gamma; + b = brightness; + c = contrast; + + dirty = true; +} + + +SANE_Word* KGammaTable::getTable() +{ + if( dirty ) calcTable(); + return( gt.data()); +} +#include "kgammatable.moc" diff --git a/libkscan/kgammatable.h b/libkscan/kgammatable.h new file mode 100644 index 00000000..05e71ddd --- /dev/null +++ b/libkscan/kgammatable.h @@ -0,0 +1,71 @@ +/* This file is part of the KDE Project + Copyright (C) 2002 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KGAMMATABLE_H +#define KGAMMATABLE_H + +#include <qmemarray.h> +#include <qobject.h> + +extern "C" { +#include <sane/sane.h> +} + +class KGammaTable: public QObject +{ + Q_OBJECT + + Q_PROPERTY( int g READ getGamma WRITE setGamma ) + Q_PROPERTY( int c READ getContrast WRITE setContrast ) + Q_PROPERTY( int b READ getBrightness WRITE setBrightness ) + +public: + KGammaTable ( int gamma = 100, int brightness = 0, + int contrast = 0 ); + void setAll ( int gamma, int brightness, int contrast ); + QMemArray<SANE_Word> *getArrayPtr( void ) { return > } + + int getGamma( ) const { return g; } + int getBrightness( ) const { return b; } + int getContrast( ) const { return c; } + + const KGammaTable& operator=(const KGammaTable&); + +public slots: + void setContrast ( int con ) { c = con; dirty = true; emit( tableChanged() );} + void setBrightness ( int bri ) { b = bri; dirty = true; emit( tableChanged() );} + void setGamma ( int gam ) { g = gam; dirty = true; emit( tableChanged() );} + + int tableSize() { return gt.size(); } + SANE_Word *getTable(); + +signals: + void tableChanged(void); + +private: + void calcTable( ); + int g, b, c; + bool dirty; + QMemArray<SANE_Word> gt; + + class KGammaTablePrivate; + KGammaTablePrivate *d; +}; + +#endif // KGAMMATABLE_H diff --git a/libkscan/kscandevice.cpp b/libkscan/kscandevice.cpp new file mode 100644 index 00000000..ca678eac --- /dev/null +++ b/libkscan/kscandevice.cpp @@ -0,0 +1,1541 @@ +/* This file is part of the KDE Project + Copyright (C) 1999 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <stdlib.h> +#include <qwidget.h> +#include <qobject.h> +#include <qasciidict.h> +#include <qcombobox.h> +#include <qslider.h> +#include <qcheckbox.h> +#include <qlineedit.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qtooltip.h> +#include <qimage.h> +#include <qfileinfo.h> +#include <qapplication.h> +#include <kdebug.h> +#include <klocale.h> +#include <kglobal.h> +#include <kconfig.h> +#include <kstandarddirs.h> + +#include <unistd.h> +#include "kgammatable.h" +#include "kscandevice.h" +#include "kscanslider.h" +#include "kscanoption.h" +#include "kscanoptset.h" +#include "devselector.h" +#include "imgscaninfo.h" + +#include <ksimpleconfig.h> + +#define MIN_PREVIEW_DPI 75 +#define UNDEF_SCANNERNAME I18N_NOOP( "undefined" ) +#define MAX_PROGRESS 100 + +/* --------------------------------------------------------------------------- + Private class for KScanDevice + ------------------------------------------------------------------------- */ +class KScanDevice::KScanDevicePrivate + +{ +public: + KScanDevicePrivate() + : currScanResolutionX(0), + currScanResolutionY(0) + { + + } + + int currScanResolutionX, currScanResolutionY; + +}; + + +/* --------------------------------------------------------------------------- + + ------------------------------------------------------------------------- */ +void KScanDevice::guiSetEnabled( const QCString& name, bool state ) +{ + KScanOption *so = getExistingGuiElement( name ); + + if( so ) + { + QWidget *w = so->widget(); + + if( w ) + w->setEnabled( state ); + } +} + + +/* --------------------------------------------------------------------------- + + ------------------------------------------------------------------------- */ +KScanOption *KScanDevice::getExistingGuiElement( const QCString& name ) +{ + KScanOption *ret = 0L; + + QCString alias = aliasName( name ); + + /* gui_elements is a QList<KScanOption> */ + for( ret = gui_elements.first(); ret != 0; ret = gui_elements.next()) + { + if( ret->getName() == alias ) break; + } + + return( ret ); +} +/* --------------------------------------------------------------------------- + + ------------------------------------------------------------------------- */ + +KScanOption *KScanDevice::getGuiElement( const QCString& name, QWidget *parent, + const QString& desc, + const QString& tooltip ) +{ + if( name.isEmpty() ) return(0); + QWidget *w = 0; + KScanOption *so = 0; + + QCString alias = aliasName( name ); + + /* Check if already exists */ + so = getExistingGuiElement( name ); + + if( so ) return( so ); + + /* ...else create a new one */ + so = new KScanOption( alias ); + + if( so->valid() && so->softwareSetable()) + { + /** store new gui-elem in list of all gui-elements */ + gui_elements.append( so ); + + w = so->createWidget( parent, desc, tooltip ); + if( w ) + { + connect( so, SIGNAL( optionChanged( KScanOption* ) ), + this, SLOT( slOptChanged( KScanOption* ))); + w->setEnabled( so->active() ); + } + else + { + kdDebug(29000) << "ERROR: No widget created for " << name << endl; + } + } + else + { + if( !so->valid()) + kdDebug(29000) << "getGuiElem: no option <" << alias << ">" << endl; + else + if( !so->softwareSetable()) + kdDebug(29000) << "getGuiElem: option <" << alias << "> is not software Setable" << endl; + + delete so; + so = 0; + } + + return( so ); +} +// --------------------------------------------------------------------------- + +// --------------------------------------------------------------------------- + + +KScanDevice::KScanDevice( QObject *parent ) + : QObject( parent ) +{ + SANE_Status sane_stat = sane_init(NULL, NULL ); + + d = new KScanDevicePrivate(); + + option_dic = new QAsciiDict<int>; + option_dic->setAutoDelete( true ); + gui_elements.setAutoDelete( true ); + + scanner_initialised = false; /* stays false until openDevice. */ + scanStatus = SSTAT_SILENT; + + data = 0; /* temporary image data buffer while scanning */ + sn = 0; /* socket notifier for async scanning */ + img = 0; /* temporary image to scan in */ + storeOptions = 0; /* list of options to store during preview */ + overall_bytes = 0; + rest_bytes = 0; + pixel_x = 0; + pixel_y = 0; + scanner_name = 0L; + + KConfig *konf = KGlobal::config (); + konf->setGroup( GROUP_STARTUP ); + bool netaccess = konf->readBoolEntry( STARTUP_ONLY_LOCAL, false ); + kdDebug(29000) << "Query for network scanners " << (netaccess ? "Not enabled" : "Enabled") << endl; + if( sane_stat == SANE_STATUS_GOOD ) + { + sane_stat = sane_get_devices( &dev_list, netaccess ? SANE_TRUE : SANE_FALSE ); + + // NO network devices yet + + // Store all available Scanner to Stringlist + for( int devno = 0; sane_stat == SANE_STATUS_GOOD && + dev_list[ devno ]; ++devno ) + { + if( dev_list[devno] ) + { + scanner_avail.append( dev_list[devno]->name ); + scannerDevices.insert( dev_list[devno]->name, dev_list[devno] ); + kdDebug(29000) << "Found Scanner: " << dev_list[devno]->name << endl; + } + } +#if 0 + connect( this, SIGNAL(sigOptionsChanged()), SLOT(slReloadAll())); +#endif + gammaTables = new KScanOptSet( "GammaTables" ); + } + else + { + kdDebug(29000) << "ERROR: sane_init failed -> SANE installed ?" << endl; + } + + connect( this, SIGNAL( sigScanFinished( KScanStat )), SLOT( slScanFinished( KScanStat ))); + +} + + +KScanDevice::~KScanDevice() +{ + if( storeOptions ) delete (storeOptions ); + kdDebug(29000) << "Calling sane_exit to finish sane!" << endl; + sane_exit(); + delete d; +} + + +KScanStat KScanDevice::openDevice( const QCString& backend ) +{ + KScanStat stat = KSCAN_OK; + SANE_Status sane_stat = SANE_STATUS_GOOD; + + if( backend.isEmpty() ) return KSCAN_ERR_PARAM; + + // search for scanner + int indx = scanner_avail.find( backend ); + + if( indx < 0 ) { + stat = KSCAN_ERR_NO_DEVICE; + } + + // if available, build lists of properties + if( stat == KSCAN_OK ) + { + sane_stat = sane_open( backend, &scanner_handle ); + + + if( sane_stat == SANE_STATUS_GOOD ) + { + // fill description dic with names and numbers + stat = find_options(); + scanner_name = backend; + + } else { + stat = KSCAN_ERR_OPEN_DEV; + scanner_name = UNDEF_SCANNERNAME; + } + } + + if( stat == KSCAN_OK ) + scanner_initialised = true; + + return( stat ); +} + +void KScanDevice::slCloseDevice( ) +{ + /* First of all, send a signal to close down the scanner dev. */ + emit( sigCloseDevice( )); + + kdDebug(29000) << "Saving scan settings" << endl; + slSaveScanConfigSet( DEFAULT_OPTIONSET, i18n("the default startup setup")); + + /* After return, delete all related stuff */ + scanner_name = UNDEF_SCANNERNAME; + if( scanner_handle ) + { + if( scanStatus != SSTAT_SILENT ) + { + kdDebug(29000) << "Scanner is still active, calling cancel !" << endl; + sane_cancel( scanner_handle ); + } + sane_close( scanner_handle ); + scanner_handle = 0; + } + + gui_elements.clear(); + + option_dic->clear(); + scanner_initialised = false; + +} + + +QString KScanDevice::getScannerName(const QCString& name) const +{ + QString ret = i18n("No scanner selected"); + SANE_Device *scanner = 0L; + + if( scanner_name && scanner_initialised && name.isEmpty()) + { + scanner = scannerDevices[ scanner_name ]; + } + else if ( ! name.isEmpty() ) + { + scanner = scannerDevices[ name ]; + ret = QString::null; + } + + if( scanner ) { + // ret.sprintf( "%s %s %s", scanner->vendor, scanner->model, scanner->type ); + ret.sprintf( "%s %s", scanner->vendor, scanner->model ); + } + + kdDebug(29000) << "getScannerName returns <" << ret << ">" << endl; + return ( ret ); +} + + +QSize KScanDevice::getMaxScanSize( void ) const +{ + QSize s; + double min, max, q; + + KScanOption so_w( SANE_NAME_SCAN_BR_X ); + so_w.getRange( &min, &max, &q ); + + s.setWidth( (int) max ); + + KScanOption so_h( SANE_NAME_SCAN_BR_Y ); + so_h.getRange( &min, &max, &q ); + + s.setHeight( (int) max ); + + return( s ); +} + + +KScanStat KScanDevice::find_options() +{ + KScanStat stat = KSCAN_OK; + SANE_Int n; + SANE_Int opt; + int *new_opt; + + SANE_Option_Descriptor *d; + + if( sane_control_option(scanner_handle, 0,SANE_ACTION_GET_VALUE, &n, &opt) + != SANE_STATUS_GOOD ) + stat = KSCAN_ERR_CONTROL; + + // printf("find_options(): Found %d options\n", n ); + + // resize the Array which hold the descriptions + if( stat == KSCAN_OK ) + { + + option_dic->clear(); + + for(int i = 1; i<n; i++) + { + d = (SANE_Option_Descriptor*) + sane_get_option_descriptor( scanner_handle, i); + + if( d ) + { + // logOptions( d ); + if(d->name ) + { + // Die Option anhand des Namen in den Dict + + if( strlen( d->name ) > 0 ) + { + new_opt = new int; + *new_opt = i; + kdDebug(29000) << "Inserting <" << d->name << "> as " << *new_opt << endl; + /* create a new option in the set. */ + option_dic->insert ( (const char*)d->name, new_opt ); + option_list.append( (const char*) d->name ); +#if 0 + KScanOption *newOpt = new KScanOption( d->name ); + const QString qq = newOpt->get(); + qDebug( "INIT: <%s> = <%s>", d->name, qq ); + allOptionSet->insert( d->name, newOpt ); +#endif + } + else if( d->type == SANE_TYPE_GROUP ) + { + // qDebug( "######### Group found: %s ########", d->title ); + } + else + kdDebug(29000) << "Unable to detect option " << endl; + } + } + } + } + return stat; +} + + +QStrList KScanDevice::getAllOptions() +{ + return( option_list ); +} + +QStrList KScanDevice::getCommonOptions() +{ + QStrList com_opt; + + QCString s = option_list.first(); + + while( !s.isEmpty() ) + { + KScanOption opt( s ); + if( opt.commonOption() ) + com_opt.append( s ); + s = option_list.next(); + } + return( com_opt ); +} + +QStrList KScanDevice::getAdvancedOptions() +{ + QStrList com_opt; + + QCString s = option_list.first(); + + while( !s.isEmpty() ) + { + KScanOption opt( s ); + if( !opt.commonOption() ) + com_opt.append( s ); + s = option_list.next(); + } + return( com_opt ); +} + +KScanStat KScanDevice::apply( KScanOption *opt, bool isGammaTable ) +{ + KScanStat stat = KSCAN_OK; + if( !opt ) return( KSCAN_ERR_PARAM ); + int sane_result = 0; + + int *num = (*option_dic)[ opt->getName() ]; + SANE_Status sane_stat = SANE_STATUS_GOOD; + const QCString& oname = opt->getName(); + + if ( oname == "preview" || oname == "mode" ) { + sane_stat = sane_control_option( scanner_handle, *num, + SANE_ACTION_SET_AUTO, 0, + &sane_result ); + /* No return here, please ! Carsten, does it still work than for you ? */ + } + + + if( ! opt->initialised() || opt->getBuffer() == 0 ) + { + kdDebug(29000) << "Attempt to set Zero buffer of " << oname << " -> skipping !" << endl; + + if( opt->autoSetable() ) + { + kdDebug(29000) << "Setting option automatic !" << endl; + sane_stat = sane_control_option( scanner_handle, *num, + SANE_ACTION_SET_AUTO, 0, + &sane_result ); + } + else + { + sane_stat = SANE_STATUS_INVAL; + } + stat = KSCAN_ERR_PARAM; + } + else + { + if( ! opt->active() ) + { + kdDebug(29000) << "Option " << oname << " is not active now!" << endl; + stat = KSCAN_OPT_NOT_ACTIVE; + } + else if( ! opt->softwareSetable() ) + { + kdDebug(29000) << "Option " << oname << " is not software Setable!" << endl; + stat = KSCAN_OPT_NOT_ACTIVE; + } + else + { + + sane_stat = sane_control_option( scanner_handle, *num, + SANE_ACTION_SET_VALUE, + opt->getBuffer(), + &sane_result ); + } + } + + if( stat == KSCAN_OK ) + { + if( sane_stat == SANE_STATUS_GOOD ) + { + kdDebug(29000) << "Applied <" << oname << "> successfully" << endl; + + if( sane_result & SANE_INFO_RELOAD_OPTIONS ) + { + kdDebug(29000) << "* Setting status to reload options" << endl; + stat = KSCAN_RELOAD; +#if 0 + qDebug( "Emitting sigOptionChanged()" ); + emit( sigOptionsChanged() ); +#endif + } + +#if 0 + if( sane_result & SANE_INFO_RELOAD_PARAMS ) + emit( sigScanParamsChanged() ); +#endif + if( sane_result & SANE_INFO_INEXACT ) + { + kdDebug(29000) << "Option <" << oname << "> was set inexact !" << endl; + } + + /* if it is a gamma table, the gamma values must be stored */ + if( isGammaTable ) + { + gammaTables->backupOption( *opt ); + kdDebug(29000) << "GammaTable stored: " << opt->getName() << endl; + } + } + else + { + kdDebug(29000) << "Status of sane is bad: " << sane_strstatus( sane_stat ) + << " for option " << oname << endl; + + } + } + else + { + kdDebug(29000) << "Setting of <" << oname << "> failed -> kscanerror." << endl; + } + + if( stat == KSCAN_OK ) + { + slSetDirty( oname ); + } + + return( stat ); +} + +bool KScanDevice::optionExists( const QCString& name ) +{ + if( name.isEmpty() ) return false; + int *i = 0L; + + QCString altname = aliasName( name ); + + if( ! altname.isNull() ) + i = (*option_dic)[ altname ]; + + if( !i ) + return( false ); + return( *i > -1 ); +} + +void KScanDevice::slSetDirty( const QCString& name ) +{ + if( optionExists( name ) ) + { + if( dirtyList.find( name ) == -1 ) + { + kdDebug(29000)<< "Setting dirty <" << name << ">" << endl; + /* item not found */ + dirtyList.append( name ); + } + } +} + + +/* This function tries to find name aliases which appear from backend to backend. + * Example: Custom-Gamma is for epson backends 'gamma-correction' - not a really + * cool thing :-| + * Maybe this helps us out ? + */ +QCString KScanDevice::aliasName( const QCString& name ) +{ + int *i = (*option_dic)[ name ]; + QCString ret; + + if( i ) return( name ); + ret = name; + + if( name == SANE_NAME_CUSTOM_GAMMA ) + { + if((*option_dic)["gamma-correction"]) + ret = "gamma-correction"; + + } + + if( ret != name ) + kdDebug( 29000) << "Found alias for <" << name << "> which is <" << ret << ">" << endl; + + return( ret ); +} + + + +/* Nothing to do yet. This Slot may get active to do same user Widget changes */ +void KScanDevice::slOptChanged( KScanOption *opt ) +{ + kdDebug(29000) << "Slot Option Changed for Option " << opt->getName() << endl; + apply( opt ); +} + +/* This might result in a endless recursion ! */ +void KScanDevice::slReloadAllBut( KScanOption *not_opt ) +{ + if( ! not_opt ) + { + kdDebug(29000) << "ReloadAllBut called with invalid argument" << endl; + return; + } + + /* Make sure its applied */ + apply( not_opt ); + + kdDebug(29000) << "*** Reload of all except <" << not_opt->getName() << "> forced ! ***" << endl; + + for( KScanOption *so = gui_elements.first(); so; so = gui_elements.next()) + { + if( so != not_opt ) + { + kdDebug(29000) << "Reloading <" << so->getName() << ">" << endl; + so->slReload(); + so->slRedrawWidget(so); + } + } + kdDebug(29000) << "*** Reload of all finished ! ***" << endl; + +} + + +/* This might result in a endless recursion ! */ +void KScanDevice::slReloadAll( ) +{ + kdDebug(29000) << "*** Reload of all forced ! ***" << endl; + + for( KScanOption *so = gui_elements.first(); so; so = gui_elements.next()) + { + so->slReload(); + so->slRedrawWidget(so); + } +} + + +void KScanDevice::slStopScanning( void ) +{ + kdDebug(29000) << "Attempt to stop scanning" << endl; + if( scanStatus == SSTAT_IN_PROGRESS ) + { + emit( sigScanFinished( KSCAN_CANCELLED )); + } + scanStatus = SSTAT_STOP_NOW; +} + + +const QString KScanDevice::previewFile() +{ + QString dir = (KGlobal::dirs())->saveLocation( "data", "ScanImages", true ); + if( !dir.endsWith("/") ) + dir += "/"; + + QString fname = dir + QString::fromLatin1(".previews/"); + QString sname( getScannerName(shortScannerName()) ); + sname.replace( '/', "_"); + + return fname+sname; +} + +QImage KScanDevice::loadPreviewImage() +{ + const QString prevFile = previewFile(); + kdDebug(29000) << "Loading preview from file " << prevFile << endl; + + QImage image; + image.load( prevFile ); + + return image; +} + +bool KScanDevice::savePreviewImage( const QImage &image ) +{ + if( image.isNull() ) + return false; + + const QString prevFile = previewFile(); + kdDebug(29000) << "Saving preview to file " << prevFile << endl; + + return image.save( prevFile, "BMP" ); +} + + +KScanStat KScanDevice::acquirePreview( bool forceGray, int dpi ) +{ + if( ! scanner_handle ) + return KSCAN_ERR_NO_DEVICE; + + double min, max, q; + + (void) forceGray; + + if( storeOptions ) + storeOptions->clear(); + else + storeOptions = new KScanOptSet( "TempStore" ); + + + /* set Preview = ON if exists */ + if( optionExists( SANE_NAME_PREVIEW ) ) + { + KScanOption prev( aliasName(SANE_NAME_PREVIEW) ); + + prev.set( true ); + apply( &prev ); + + /* after having applied, save set to false to switch preview mode off after + scanning */ + prev.set( false ); + storeOptions->backupOption( prev ); + } + + /* Gray-Preview only done by widget ? */ + if( optionExists( SANE_NAME_GRAY_PREVIEW )) + { + KScanOption *so = getExistingGuiElement( SANE_NAME_GRAY_PREVIEW ); + if( so ) + { + if( so->get() == "true" ) + { + /* Gray preview on */ + so->set( true ); + kdDebug(29000) << "Setting GrayPreview ON" << endl; + } + else + { + so->set(false ); + kdDebug(29000) << "Setting GrayPreview OFF" << endl; + } + } + apply( so ); + } + + + if( optionExists( SANE_NAME_SCAN_MODE ) ) + { + KScanOption mode( SANE_NAME_SCAN_MODE ); + const QString kk = mode.get(); + kdDebug(29000) << "Mode is <" << kk << ">" << endl; + storeOptions->backupOption( mode ); + /* apply if it has a widget, or apply always ? */ + if( mode.widget() ) apply( &mode ); + } + + /** Scan Resolution should always exist. **/ + KScanOption res ( SANE_NAME_SCAN_RESOLUTION ); + const QString p = res.get(); + + kdDebug(29000) << "Scan Resolution pre Preview is " << p << endl; + storeOptions->backupOption( res ); + + int set_dpi = dpi; + + if( dpi == 0 ) + { + /* No resolution argument */ + if( ! res.getRange( &min, &max, &q ) ) + { + if( ! res.getRangeFromList ( &min, &max, &q ) ) + { + kdDebug(29000) << "Could not retrieve resolution range!" << endl; + min = 75.0; // Hope that every scanner can 75 + } + } + kdDebug(29000) << "Minimum Range: " << min << ", Maximum Range: " << max << endl; + + if( min > MIN_PREVIEW_DPI ) + set_dpi = (int) min; + else + set_dpi = MIN_PREVIEW_DPI; + } + + /* Set scan resolution for preview. */ + if( !optionExists( SANE_NAME_SCAN_Y_RESOLUTION ) ) + d->currScanResolutionY = 0; + else + { + KScanOption yres ( SANE_NAME_SCAN_Y_RESOLUTION ); + /* if active ? */ + storeOptions->backupOption( yres ); + yres.set( set_dpi ); + apply( &yres ); + yres.get( &d->currScanResolutionY ); + + /* Resolution bind switch ? */ + if( optionExists( SANE_NAME_RESOLUTION_BIND ) ) + { + KScanOption bind_so( SANE_NAME_RESOLUTION_BIND ); + /* Switch binding on if available */ + storeOptions->backupOption( bind_so ); + bind_so.set( true ); + apply( &bind_so ); + } + } + + res.set( set_dpi ); + apply( &res ); + + /* Store the resulting preview for additional image information */ + res.get( &d->currScanResolutionX ); + + if ( d->currScanResolutionY == 0 ) + d->currScanResolutionY = d->currScanResolutionX; + + /* Start scanning */ + KScanStat stat = acquire_data( true ); + + /* Restauration of the previous values must take place in the scanfinished slot, + * because scanning works asynchron now. + */ + + return( stat ); +} + + + +/** + * prepareScan tries to set as much as parameters as possible. + * + **/ +#define NOTIFIER(X) optionNotifyString(X) + +QString KScanDevice::optionNotifyString( int i ) const +{ + const QString sOff = " |"; + const QString sOn = " X |"; + if( i > 0 ) + { + return sOn; + } + return sOff; +} + + +void KScanDevice::prepareScan( void ) +{ + QAsciiDictIterator<int> it( *option_dic ); // iterator for dict + + kdDebug(29000) << "########################################################################################################" << endl; + kdDebug(29000) << "Scanner: " << scanner_name << endl; + kdDebug(29000) << " " << getScannerName() << endl; + kdDebug(29000) << "----------------------------------+--------+--------+--------+--------+--------+--------+--------+" << endl; + kdDebug(29000) << " Option-Name |SOFT_SEL|HARD_SEL|SOFT_DET|EMULATED|AUTOMATI|INACTIVE|ADVANCED|" << endl; + kdDebug(29000) << "----------------------------------+--------+--------+--------+--------+--------+--------+--------+" << endl; + + while ( it.current() ) + { + // qDebug( "%s -> %d", it.currentKey().latin1(), *it.current() ); + int descriptor = *it.current(); + + const SANE_Option_Descriptor *d = sane_get_option_descriptor( scanner_handle, descriptor ); + + if( d ) + { + int cap = d->cap; + + QString s = QString(it.currentKey()).leftJustify(32, ' '); + kdDebug(29000) << " " << s << " |" << + NOTIFIER( ((cap) & SANE_CAP_SOFT_SELECT)) << + NOTIFIER( ((cap) & SANE_CAP_HARD_SELECT)) << + NOTIFIER( ((cap) & SANE_CAP_SOFT_DETECT)) << + NOTIFIER( ((cap) & SANE_CAP_EMULATED) ) << + NOTIFIER( ((cap) & SANE_CAP_AUTOMATIC) ) << + NOTIFIER( ((cap) & SANE_CAP_INACTIVE) ) << + NOTIFIER( ((cap) & SANE_CAP_ADVANCED ) ) << endl; + + } + ++it; + } + kdDebug(29000) << "----------------------------------+--------+--------+--------+--------+--------+--------+--------+" << endl; + + KScanOption pso( SANE_NAME_PREVIEW ); + const QString q = pso.get(); + + kdDebug(29000) << "Preview-Switch is at the moment: " << q << endl; + + +} + +/** Starts scanning + * depending on if a filename is given or not, the function tries to open + * the file using the Qt-Image-IO or really scans the image. + **/ +KScanStat KScanDevice::acquire( const QString& filename ) +{ + if( ! scanner_handle ) + return KSCAN_ERR_NO_DEVICE; + + KScanOption *so = 0; + + if( filename.isEmpty() ) + { + /* *real* scanning - apply all Options and go for it */ + prepareScan(); + + for( so = gui_elements.first(); so; so = gui_elements.next() ) + { + if( so->active() ) + { + kdDebug(29000) << "apply <" << so->getName() << ">" << endl; + apply( so ); + } + else + { + kdDebug(29000) << "Option <" << so->getName() << "> is not active !" << endl; + } + } + + /** Scan Resolution should always exist. **/ + KScanOption res( SANE_NAME_SCAN_RESOLUTION ); + res.get( &d->currScanResolutionX ); + if ( !optionExists( SANE_NAME_SCAN_Y_RESOLUTION ) ) + d->currScanResolutionY = d->currScanResolutionX; + else + { + KScanOption yres( SANE_NAME_SCAN_Y_RESOLUTION ); + yres.get( &d->currScanResolutionY ); + } + + return( acquire_data( false )); + } + else + { + /* a filename is in the parameter */ + QFileInfo file( filename ); + if( file.exists() ) + { + QImage i; + ImgScanInfo info; + if( i.load( filename )) + { + info.setXResolution(i.dotsPerMeterX()); // TODO: *2.54/100 + info.setYResolution(i.dotsPerMeterY()); // TODO: *2.54/100 + info.setScannerName(filename); + emit( sigNewImage( &i, &info )); + } + } + } + + return KSCAN_OK; +} + + +/** + * Creates a new QImage from the retrieved Image Options + **/ +KScanStat KScanDevice::createNewImage( SANE_Parameters *p ) +{ + if( !p ) return( KSCAN_ERR_PARAM ); + KScanStat stat = KSCAN_OK; + + if( img ) { + delete( img ); + img = 0; + } + + if( p->depth == 1 ) // Lineart !! + { + img = new QImage( p->pixels_per_line, p->lines, 8 ); + if( img ) + { + img->setNumColors( 2 ); + img->setColor( 0, qRgb( 0,0,0)); + img->setColor( 1, qRgb( 255,255,255) ); + } + } + else if( p->depth == 8 ) + { + // 8 bit rgb-Picture + if( p->format == SANE_FRAME_GRAY ) + { + /* Grayscale */ + img = new QImage( p->pixels_per_line, p->lines, 8 ); + if( img ) + { + img->setNumColors( 256 ); + + for(int i = 0; i<256; i++) + img->setColor(i, qRgb(i,i,i)); + } + } + else + { + /* true color image */ + img = new QImage( p->pixels_per_line, p->lines, 32 ); + if( img ) + img->setAlphaBuffer( false ); + } + } + else + { + /* ERROR: NO OTHER DEPTHS supported */ + kdDebug(29000) << "KScan supports only bit dephts 1 and 8 yet!" << endl; + } + + if( ! img ) stat = KSCAN_ERR_MEMORY; + return( stat ); +} + + +KScanStat KScanDevice::acquire_data( bool isPreview ) +{ + SANE_Status sane_stat = SANE_STATUS_GOOD; + KScanStat stat = KSCAN_OK; + + scanningPreview = isPreview; + + emit sigScanStart(); + + sane_stat = sane_start( scanner_handle ); + if( sane_stat == SANE_STATUS_GOOD ) + { + sane_stat = sane_get_parameters( scanner_handle, &sane_scan_param ); + + if( sane_stat == SANE_STATUS_GOOD ) + { + kdDebug(29000) << "--Pre-Loop" << endl; + kdDebug(29000) << "format : " << sane_scan_param.format << endl; + kdDebug(29000) << "last_frame : " << sane_scan_param.last_frame << endl; + kdDebug(29000) << "lines : " << sane_scan_param.lines << endl; + kdDebug(29000) << "depth : " << sane_scan_param.depth << endl; + kdDebug(29000) << "pixels_per_line : " << sane_scan_param.pixels_per_line << endl; + kdDebug(29000) << "bytes_per_line : " << sane_scan_param.bytes_per_line << endl; + } + else + { + stat = KSCAN_ERR_OPEN_DEV; + kdDebug(29000) << "sane-get-parameters-Error: " << sane_strstatus( sane_stat ) << endl; + } + } + else + { + stat = KSCAN_ERR_OPEN_DEV; + kdDebug(29000) << "sane-start-Error: " << sane_strstatus( sane_stat ) << endl; + } + + + if( sane_scan_param.pixels_per_line == 0 || sane_scan_param.lines < 1 ) + { + kdDebug(29000) << "ERROR: Acquiring empty picture !" << endl; + stat = KSCAN_ERR_EMPTY_PIC; + } + + /* Create new Image from SANE-Parameters */ + if( stat == KSCAN_OK ) + { + stat = createNewImage( &sane_scan_param ); + } + + if( stat == KSCAN_OK ) + { + /* new buffer for scanning one line */ + if(data) delete [] data; + data = new SANE_Byte[ sane_scan_param.bytes_per_line +4 ]; + if( ! data ) stat = KSCAN_ERR_MEMORY; + } + + /* Signal for a progress dialog */ + emit( sigScanProgress( 0 ) ); + emit( sigAcquireStart() ); + + if( stat == KSCAN_OK ) + { + /* initiates Redraw of the Progress-Window */ + qApp->processEvents(); + } + + if( stat == KSCAN_OK ) + { + overall_bytes = 0; + scanStatus = SSTAT_IN_PROGRESS; + pixel_x = 0; + pixel_y = 0; + overall_bytes = 0; + rest_bytes = 0; + + /* internal status to indicate Scanning in progress + * this status might be changed by pressing Stop on a GUI-Dialog displayed during scan */ + if( sane_set_io_mode( scanner_handle, SANE_TRUE ) == SANE_STATUS_GOOD ) + { + + int fd = 0; + if( sane_get_select_fd( scanner_handle, &fd ) == SANE_STATUS_GOOD ) + { + sn = new QSocketNotifier( fd, QSocketNotifier::Read, this ); + QObject::connect( sn, SIGNAL(activated(int)), + this, SLOT( doProcessABlock() ) ); + + } + } + else + { + do + { + doProcessABlock(); + if( scanStatus != SSTAT_SILENT ) + { + sane_stat = sane_get_parameters( scanner_handle, &sane_scan_param ); + kdDebug(29000) << "--ProcessABlock-Loop" << endl; + kdDebug(29000) << "format : " << sane_scan_param.format << endl; + kdDebug(29000) << "last_frame : " << sane_scan_param.last_frame << endl; + kdDebug(29000) << "lines : " << sane_scan_param.lines << endl; + kdDebug(29000) << "depth : " << sane_scan_param.depth << endl; + kdDebug(29000) << "pixels_per_line : " << sane_scan_param.pixels_per_line << endl; + kdDebug(29000) << "bytes_per_line : " << sane_scan_param.bytes_per_line << endl; + } + } while ( scanStatus != SSTAT_SILENT ); + } + } + + if( stat != KSCAN_OK ) + { + /* Scanning was disturbed in any way - end it */ + kdDebug(29000) << "Scanning was disturbed - clean up" << endl; + emit( sigScanFinished( stat )); + } + return( stat ); +} + +void KScanDevice::loadOptionSet( KScanOptSet *optSet ) +{ + if( !optSet ) + { + return; + } + + // kdDebug(29000) << "Loading Option set: " << optSet->optSetName() << endl; + QAsciiDictIterator<KScanOption> it(*optSet); + + kdDebug(29000) << "Postinstalling " << optSet->count() << " options" << endl; + while( it.current() ) + { + KScanOption *so = it.current(); + if( ! so->initialised() ) + kdDebug(29000) << so->getName() << " is not initialised" << endl; + + if( ! so->active() ) + kdDebug(29000) << so->getName() << " is not active" << endl; + + if( so && so->active() && so->initialised()) + { + const QString qq = so->get(); + + kdDebug(29000) << "Post-Scan-apply <" << it.currentKey() << ">: " << qq << endl; + apply( so ); + } + ++it; + } + +} + + +void KScanDevice::slScanFinished( KScanStat status ) +{ + // clean up + if( sn ) { + sn->setEnabled( false ); + delete sn; + sn = 0; + } + + emit( sigScanProgress( MAX_PROGRESS )); + + kdDebug(29000) << "Slot ScanFinished hit with status " << status << endl; + + if( data ) + { + delete[] data; + data = 0; + } + + if( status == KSCAN_OK && img ) + { + ImgScanInfo info; + info.setXResolution(d->currScanResolutionX); + info.setYResolution(d->currScanResolutionY); + info.setScannerName(shortScannerName()); + + // put the resolution also into the image itself + img->setDotsPerMeterX(static_cast<int>(d->currScanResolutionX / 0.0254 + 0.5)); + img->setDotsPerMeterY(static_cast<int>(d->currScanResolutionY / 0.0254 + 0.5)); + kdDebug(29000) << "resolutionX:" << d->currScanResolutionX << " resolutionY:" << d->currScanResolutionY << endl; + + if( scanningPreview ) + { + kdDebug(29000) << "Scanning a preview !" << endl; + savePreviewImage(*img); + emit( sigNewPreview( img, &info )); + + /* The old settings need to be redefined */ + loadOptionSet( storeOptions ); + } + else + { + emit( sigNewImage( img, &info )); + } + } + + + sane_cancel(scanner_handle); + + /* This follows after sending the signal */ + if( img ) + { + delete img; + img = 0; + } + + /* delete the socket notifier */ + if( sn ) { + delete( sn ); + sn = 0; + } + +} + + +/* This function calls at least sane_read and converts the read data from the scanner + * to the qimage. + * The function needs: + * QImage img valid + * the data-buffer set to a appropriate size + **/ + +void KScanDevice::doProcessABlock( void ) +{ + int val,i; + QRgb col, newCol; + + SANE_Byte *rptr = 0; + SANE_Int bytes_written = 0; + int chan = 0; + SANE_Status sane_stat = SANE_STATUS_GOOD; + uchar eight_pix = 0; + bool goOn = true; + + // int rest_bytes = 0; + while( goOn && data ) + { + sane_stat = + sane_read( scanner_handle, data + rest_bytes, sane_scan_param.bytes_per_line, &bytes_written); + int red = 0; + int green = 0; + int blue = 0; + + if( sane_stat != SANE_STATUS_GOOD ) + { + /** any other error **/ + kdDebug(29000) << "sane_read returned with error <" << sane_strstatus( sane_stat ) << ">: " << bytes_written << " bytes left" << endl; + goOn = false; + } + + if( goOn && bytes_written < 1 ) + { + // qDebug( "No bytes written -> leaving out !" ); + goOn = false; /** No more data -> leave ! **/ + } + + if( goOn ) + { + overall_bytes += bytes_written; + // qDebug( "Bytes read: %d, bytes written: %d", bytes_written, rest_bytes ); + + rptr = data; // + rest_bytes; + + switch( sane_scan_param.format ) + { + case SANE_FRAME_RGB: + if( sane_scan_param.lines < 1 ) break; + bytes_written += rest_bytes; // die bergebliebenen Bytes dazu + rest_bytes = bytes_written % 3; + + for( val = 0; val < ((bytes_written-rest_bytes) / 3); val++ ) + { + red = *rptr++; + green = *rptr++; + blue = *rptr++; + + // kdDebug(29000) << "RGB: %d, %d, %d\n", red, green, blue" << endl; + if( pixel_x == sane_scan_param.pixels_per_line ) + { pixel_x = 0; pixel_y++; } + if( pixel_y < img->height()) + img->setPixel( pixel_x, pixel_y, qRgb( red, green,blue )); + + pixel_x++; + } + /* copy the remaining bytes to the beginning of the block :-/ */ + for( val = 0; val < rest_bytes; val++ ) + { + *(data+val) = *rptr++; + } + + break; + case SANE_FRAME_GRAY: + for( val = 0; val < bytes_written ; val++ ) + { + if( pixel_y >= sane_scan_param.lines ) break; + if( sane_scan_param.depth == 8 ) + { + if( pixel_x == sane_scan_param.pixels_per_line ) { pixel_x = 0; pixel_y++; } + img->setPixel( pixel_x, pixel_y, *rptr++ ); + pixel_x++; + } + else + { // Lineart + /* Lineart needs to be converted to byte */ + eight_pix = *rptr++; + for( i = 0; i < 8; i++ ) + { + if( pixel_y < sane_scan_param.lines ) + { + chan = (eight_pix & 0x80)> 0 ? 0:1; + eight_pix = eight_pix << 1; + //qDebug( "Setting on %d, %d: %d", pixel_x, pixel_y, chan ); + img->setPixel( pixel_x, pixel_y, chan ); + pixel_x++; + if( pixel_x >= sane_scan_param.pixels_per_line ) + { + pixel_x = 0; pixel_y++; + break; + } + } + } + } + } + + break; + case SANE_FRAME_RED: + case SANE_FRAME_GREEN: + case SANE_FRAME_BLUE: + kdDebug(29000) << "Scanning Single color Frame: " << bytes_written << " Bytes!" << endl; + for( val = 0; val < bytes_written ; val++ ) + { + if( pixel_x >= sane_scan_param.pixels_per_line ) + { + pixel_y++; pixel_x = 0; + } + + if( pixel_y < sane_scan_param.lines ) + { + + col = img->pixel( pixel_x, pixel_y ); + + red = qRed( col ); + green = qGreen( col ); + blue = qBlue( col ); + chan = *rptr++; + + switch( sane_scan_param.format ) + { + case SANE_FRAME_RED : + newCol = qRgba( chan, green, blue, 0xFF ); + break; + case SANE_FRAME_GREEN : + newCol = qRgba( red, chan, blue, 0xFF ); + break; + case SANE_FRAME_BLUE : + newCol = qRgba( red , green, chan, 0xFF ); + break; + default: + kdDebug(29000) << "Undefined format !" << endl; + newCol = qRgba( 0xFF, 0xFF, 0xFF, 0xFF ); + break; + } + img->setPixel( pixel_x, pixel_y, newCol ); + pixel_x++; + } + } + break; + default: + kdDebug(29000) << "Unexpected ERROR: No Format type" << endl; + break; + } /* switch */ + + + if( (sane_scan_param.lines > 0) && (sane_scan_param.lines * pixel_y> 0) ) + { + int progress = (int)(((double)MAX_PROGRESS) / sane_scan_param.lines * + pixel_y); + if( progress < MAX_PROGRESS) + emit( sigScanProgress( progress)); + } + + if( bytes_written == 0 || sane_stat == SANE_STATUS_EOF ) + { + kdDebug(29000) << "Down under sane_stat not OK" << endl; + goOn = false; + } + } + + if( goOn && scanStatus == SSTAT_STOP_NOW ) + { + /* scanStatus is set to SSTAT_STOP_NOW due to hitting slStopScanning */ + /* Mostly that one is fired by the STOP-Button in the progress dialog. */ + + /* This is also hit after the normal finish of the scan. Most probably, + * the QSocketnotifier fires for a few times after the scan has been + * cancelled. Does it matter ? To see it, just uncomment the qDebug msg. + */ + kdDebug(29000) << "Stopping the scan progress !" << endl; + goOn = false; + scanStatus = SSTAT_SILENT; + emit( sigScanFinished( KSCAN_OK )); + } + + } /* while( 1 ) */ + + + /** Comes here if scanning is finished or has an error **/ + if( sane_stat == SANE_STATUS_EOF) + { + if ( sane_scan_param.last_frame) + { + /** Everythings okay, the picture is ready **/ + kdDebug(29000) << "last frame reached - scan successful" << endl; + scanStatus = SSTAT_SILENT; + emit( sigScanFinished( KSCAN_OK )); + } + else + { + /** EOF und nicht letzter Frame -> Parameter neu belegen und neu starten **/ + scanStatus = SSTAT_NEXT_FRAME; + kdDebug(29000) << "EOF, but another frame to scan" << endl; + + } + } + + if( sane_stat == SANE_STATUS_CANCELLED ) + { + scanStatus = SSTAT_STOP_NOW; + kdDebug(29000) << "Scan was cancelled" << endl; + + // stat = KSCAN_CANCELLED; + // emit( sigScanFinished( stat )); + + /* hmmm - how could this happen ? */ + } +} /* end of fkt */ + + +void KScanDevice::slSaveScanConfigSet( const QString& setName, const QString& descr ) +{ + if( setName.isEmpty() || setName.isNull()) return; + + kdDebug(29000) << "Saving Scan Configuration" << setName << endl; + + KScanOptSet optSet( DEFAULT_OPTIONSET ); + getCurrentOptions( &optSet ); + + optSet.saveConfig( scanner_name , setName, descr ); + +} + + +void KScanDevice::getCurrentOptions( KScanOptSet *optSet ) +{ + if( ! optSet ) return; + + for( KScanOption *so = gui_elements.first(); so; so = gui_elements.next()) + { + kdDebug(29000) << "Storing <" << so->getName() << ">" << endl; + if( so && so->active()) + { + apply(so); + optSet->backupOption( *so ); + } + + /* drop the thing from the dirty-list */ + dirtyList.removeRef( so->getName()); + } + + QStrListIterator it( dirtyList ); + while( it.current()) + { + KScanOption so( it.current() ); + optSet->backupOption( so ); + ++it; + } +} + +QString KScanDevice::getConfig( const QString& key, const QString& def ) const +{ + QString confFile = SCANNER_DB_FILE; + + KSimpleConfig scanConfig( confFile, true ); + scanConfig.setGroup( shortScannerName() ); + + return scanConfig.readEntry( key, def ); +} + +void KScanDevice::slStoreConfig( const QString& key, const QString& val ) +{ + QString confFile = SCANNER_DB_FILE; + QString scannerName = shortScannerName(); + + if( scannerName.isEmpty() || scannerName == UNDEF_SCANNERNAME ) + { + kdDebug(29000) << "Skipping config write, scanner name is empty!" << endl; + } + else + { + kdDebug(29000) << "Storing config " << key << " in Group " << scannerName << endl; + + KSimpleConfig scanConfig( confFile ); + scanConfig.setGroup( scannerName ); + scanConfig.writeEntry( key, val ); + scanConfig.sync(); + } +} + + +bool KScanDevice::scanner_initialised = false; +SANE_Handle KScanDevice::scanner_handle = 0L; +SANE_Device const **KScanDevice::dev_list = 0L; +QAsciiDict<int> *KScanDevice::option_dic = 0L; +KScanOptSet *KScanDevice::gammaTables = 0L; + + +#include "kscandevice.moc" diff --git a/libkscan/kscandevice.h b/libkscan/kscandevice.h new file mode 100644 index 00000000..dd849566 --- /dev/null +++ b/libkscan/kscandevice.h @@ -0,0 +1,459 @@ +/* This file is part of the KDE Project + Copyright (C) 1999 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _KSCANDEV_H_ +#define _KSCANDEV_H_ + +#include <qwidget.h> +#include <qasciidict.h> +#include <qsize.h> +#include <qobject.h> +#include <qstrlist.h> +#include <qsocketnotifier.h> + + +#include "kscanoption.h" +#include "kscanoptset.h" + +#define DEFAULT_OPTIONSET "saveSet" +#define SCANNER_DB_FILE "scannerrc" + +class ImgScanInfo; + +extern "C" { +#include <sane/sane.h> +#include <sane/saneopts.h> +} + +/** + * This is KScanDevice, a class for accessing the SANE scanning functions under KDE + * + * @short KScanDevice + * @author Klaas Freitag + * @version 0.1alpha + * + */ + +typedef enum { + SSTAT_SILENT, SSTAT_IN_PROGRESS, SSTAT_NEXT_FRAME, SSTAT_STOP_NOW, STTAT_STOP_ADF_FINISHED +} SCANSTATUS; + + +/** + * KScanStat + * shows the status of a KScan operation + **/ + + +class KScanDevice : public QObject +{ + Q_OBJECT + + /* Hmmm - No Q_PROPS ? */ +public: + /** + * KScanDevice - the KDE Scanner Device + * constructor. Does not much. + * + * @see openDevice + * + */ + + KScanDevice( QObject *parent = 0 ); + + /** + * Destructor + * + * releases internal allocated memory. + */ + ~KScanDevice( ); + + /** + * opens the device named backend. + * @return the state of the operation + * @param backend: the name of the backend to open + */ + KScanStat openDevice( const QCString& backend ); + + /** + * returns the names of all existing Scan Devices in the system. + * @param enable_net: allow net devices. + * @return a QStrList of available Scanners in the system + * @see KScanDevice + */ + QStrList getDevices( ) const + { return( scanner_avail ); } + + /** + * returns the short, technical name of the currently attached backend. + * It is in the form 'umax:/dev/sg1'. + */ + QCString shortScannerName() const { return scanner_name; } + + /** + * returns a long, human readable name of the scanner, like + * 'Mustek SP1200 Flatbed scanner'. The parameter name takes + * the name of a backend, what means something like "/dev/scanner". + * If the name of the backend is skipped, the selected backend is + * returned. + * + * @param a QString with a backend string + * @return a QString containing a human readable scanner name + **/ + QString getScannerName( const QCString& name = 0 ) const; + + /* + * ========= Preview Functions ========== + */ + + /** + * QImage * acquirePreview( bool forceGray = 0, int dpi = 0 ) + * + * acquires a preview on the selected device. KScanDevice allocs + * memory for the qimage returned. This qimage remains valid + * until the next call to acquirePreview or until KScanDevice is + * destructed. + * + * @return a pointer to a qimage or null on error + * @param bool forceGray if true, acquire a preview in gray + + * @param int dpi dpi setting. If 0, the dpi setting is + * elected by the object, e.g. is the smallest possible + * resolution. + + */ + KScanStat acquirePreview( bool forceGray = 0, int dpi = 0 ); + + /** acquires a qimage on the given settings. + * the Parameter image needs to point to a object qimage, + * which is filled by the SANE-Dev. After the function returns, + * the qimage is valid, if the returnvalue is OK. + * @param QImage *image - Pointer to a reserved image + * @return the state of the operation in KScanStat + */ + KScanStat acquire( const QString& filename = QString::null ); + + /** + * returns the default filename of the preview image of this scanner. + * @return default filename + **/ + const QString previewFile(); + + /** + * loads the last saved previewed image on this device from the default file + * @return a bitmap as QImage + **/ + QImage loadPreviewImage(); + + /** + * saves current previewed image from this device to the default file + * @param image current preview image which should be saved + * @return true if the saving was sucessful + **/ + bool savePreviewImage( const QImage& image ); + + + /* + * ========= List Functions ========== + */ + + /** + * returns all existing options of the selected device. + * @see openDevice + * @return a QStrList with the names of all options + **/ + QStrList getAllOptions(); + + /** + * returns the common options of the device. A frontend should + * display the returned options in a convinient way and easy to + * use. + * @see getAllOptions + * @see getAdvancedOptions + * @return a QStrList with with names of the common options. + */ + QStrList getCommonOptions(); + + /** + * returns a list of advanced scan options. A frontend should + * display these options in a special dialog. + * @see getAllOptions + * @see getCommonOptions + * @return a QStrList with names of advanced scan options. + */ + QStrList getAdvancedOptions(); + + /** + * retrieves a set of the current scan options and writes them + * to the object pointed to by parameter optSet + * @see KScanOptSet + * @param optSet, a pointer to the option set object + */ + void getCurrentOptions( KScanOptSet *optSet ); + + /** + * load scanner parameter sets. All options stored in @param optSet + * are loaded into the scan device. + */ + void loadOptionSet( KScanOptSet *optSet ); + + /** + * applys the values in an scan-option object. The value to + * set must be set in the KScanOption object prior. However, + * it takes effect after being applied with this function. + * @see KScanOption + * @return the status of the operation + * @param a scan-option object with the scanner option to set and + * an optional boolean parameter if it is a gamma table. + **/ + + KScanStat apply( KScanOption *opt, bool=false ); + + /** + * returns true if the option exists in that device. + * @param name: the name of a option from a returned option-List + * @return true, if the option exists + */ + bool optionExists( const QCString& name ); + + /** + * checks if the backend knows the option with the required name under + * a different name, like some backends do. If it does, the known name + * is returned, otherwise a null QCString. + */ + + QCString aliasName( const QCString& name ); + + /** + * returns a Widget suitable for the selected Option and creates the + * KScanOption. It is internally connected to the scan device, every + * change to the widget is automaticly considered by the scan device. + * @param name: Name of the SANE Option + * @param parent: pointer to the parent widget + * @param desc: pointer to the text appearing as widget text + * @param tooltip: tooltip text. If zero, the SANE text will be used. + **/ + KScanOption *getGuiElement( const QCString& name, QWidget *parent, + const QString& desc = QString::null, + const QString& tooltip = QString::null ); + + /** + * returns the pointer to an already created Scanoption from the + * gui element list. Cares for option name aliases. + */ + KScanOption *getExistingGuiElement( const QCString& name ); + + /** + * sets an widget of the named option enabled/disabled + **/ + void guiSetEnabled( const QCString& name, bool state ); + + /** + * returns the maximum scan size. This is interesting e.g. for the + * preview widget etc. + * The unit of the return value is millimeter, if the sane backend + * returns millimeter. (TODO) + **/ + QSize getMaxScanSize( void ) const; + + /** + * returns the information bit stored for the currently attached scanner + * identified by the key. + */ + QString getConfig( const QString& key, const QString& def = QString() ) const; + + static bool scanner_initialised; + static SANE_Handle scanner_handle; + static QAsciiDict<int>* option_dic; + static SANE_Device const **dev_list; + static KScanOptSet *gammaTables; + +public slots: + void slOptChanged( KScanOption* ); + + /** + * The setting of some options require a complete reload of all + * options, because they might have changed. In that case, slot + * slReloadAll() is called. + **/ + void slReloadAll( ); + + /** + * This slot does the same as ReloadAll, but it does not reload + * the scanner option given as parameter. This is useful to make + * sure that no recursion takes places during reload of options. + **/ + void slReloadAllBut( KScanOption*); + + /** + * Notifies the scan device to stop the current scanning. + * This slot only makes sense, if a scan is in progress. + * Note that if the slot is called, it can take a few moments + * to stop the scanning + */ + void slStopScanning( void ); + + /** + * Image ready-slot in asynchronous scanning + */ + void slScanFinished( KScanStat ); + + /** + * Save the current configuration parameter set. Only changed options are saved. + **/ + void slSaveScanConfigSet( const QString&, const QString& ); + + + void slSetDirty( const QCString& name ); + + /** + * Closes the scan device and frees all related data, makes + * the system ready to open a new, other scanner. + */ + void slCloseDevice( ); + + /** + * stores the info bit in a config file for the currently connected + * scanner. For this, the config file $KDEHOME/.kde/share/config/scannerrc + * is opened, a group is created that identifies the scanner and the + * device where it is connected. The information is stored into that group. + */ + void slStoreConfig( const QString&, const QString& ); + +signals: + /** + * emitted, if an option change was applied, which made other + * options changed. The application may connect a slot to this + * signal to see, if other options became active/inactive. + **/ + void sigOptionsChanged(); + + /** + * emitted, if an option change was applied, which made the + * scan parameters change. The parameters need to be reread + * by sane_get_parameters(). + **/ + void sigScanParamsChanged(); + + /** + * emitted if a new image was acquired. The image has to be + * copied in the signal handler. + */ + void sigNewImage( QImage*, ImgScanInfo* ); + + /** + * emitted if a new preview was acquired. The image has to be + * copied in the signal handler. + */ + void sigNewPreview( QImage*, ImgScanInfo* ); + + /** + * emitted to tell the application the start of scanning. That is + * before the enquiry of the scanner starts. Between this signal + * and @see sigScanProgress(0) can be some time consumed by a scanner + * warming up etc. + */ + void sigScanStart(); + + /** + * signal to indicate the start of acquireing data. It is equal + * to @see sigScanProgress emitted with value 0 and thus it is sent + * after sigScanStart. + * The time between sigScanStart and sigAcquireStart differs very much + * from scanner to scanner. + */ + void sigAcquireStart(); + + /** + * emitted to tell the application the progress of the scanning + * of one page. The final value is always 1000. The signal is + * emitted for zero to give the change to initialize the progress + * dialog. + **/ + void sigScanProgress( int ); + + /** + * emitted to show that a scan finished and provides a status how + * the scan finished. + */ + void sigScanFinished( KScanStat ); + + + /** + * emitted to give all objects using this device to free all dependencies + * on the scan device, because this signal means, that the scan device is + * going to be closed. The can not be stopped. All receiving objects have + * to free their stuff using the device and pray. While handling the signal, + * the device is still open and valid. + */ + void sigCloseDevice( ); + +private slots: + /** + * Slot to acquire a whole image */ + void doProcessABlock( void ); + + +private: + /** + * Function which applies all Options which need to be applied. + * See SANE-Documentation Table 4.5, description for SANE_CAP_SOFT_DETECT. + * The function sets the options which have SANE_CAP_AUTOMATIC set, + * to autmatic adjust. + **/ + void prepareScan( void ); + + KScanStat createNewImage( SANE_Parameters *p ); + +// not implemented +// QWidget *entryField( QWidget *parent, const QString& text, +// const QString& tooltip ); + KScanStat find_options(); // help fct. to process options + KScanStat acquire_data( bool isPreview = false ); + QStrList scanner_avail; // list of names of all scan dev. + QStrList option_list; // list of names of all options + QStrList dirtyList; // option changes + + inline QString optionNotifyString(int) const; + + QPtrList<KScanOption> gui_elements; + QAsciiDict<SANE_Device> scannerDevices; + + QSocketNotifier *sn; + + SCANSTATUS scanStatus; + + /* Data for the scan process */ + /* This could/should go to a small help object */ + QCString scanner_name; + SANE_Byte *data; + QImage *img; + SANE_Parameters sane_scan_param; + long overall_bytes; + int rest_bytes; + int pixel_x, pixel_y; + bool scanningPreview; + + KScanOptSet *storeOptions; + + class KScanDevicePrivate; + KScanDevicePrivate *d; +}; + +#endif diff --git a/libkscan/kscandoc.h b/libkscan/kscandoc.h new file mode 100644 index 00000000..14f1f2c8 --- /dev/null +++ b/libkscan/kscandoc.h @@ -0,0 +1,116 @@ +/* This file is part of the KDE Project + Copyright (C) 2002 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/** +\mainpage The KScan Library. + + The KScan Library provides an interface for the SANE-lib (see http://www.sane-project.org for more information) for KDE2 applications. + +\section intro Introduction + +The KScan Library furnishes each KDE2 application with an object which can connect to a scanner set up by SANE, as well as read out and manage the scanner's parameters. The difficulty with this is that SANE scanners do not have a uniform set of options. The scanners support various scan options. An interface for establishing contact to the scanners has to be set up dynamically after the decision is made as to which available device in the system should be used (dynamic GUI). + +\section usage How to use libkscan + +Libkscan provides a dialog for you to include scan functionality to your application. This includes on the main page of the dialog + - The scanner setting parts with dynamically generated controls of scan parameters and the buttons to start scanning + - A preview window to select the scan area + - An option tab to edit basically scan options. +The scan dialog works completely as KPart and allows the user to scan a preview, select the interesting part on the preview scan, and than start the final scan. Your application will be notified by a signal finalImage that informs you that a new image is available from the scanner. + + + + + +\section objectOverview Abstract KScan Objects + +The KScan Library defines the following classes which are responsible for managing the scanner's parameters: + +- KGammaTable\n +This is a base class which implements a gamma table and carries out the calculations etc. internally. + +- KScanOption\n +The object KScanOption implements exactly one scanning option, such as the scanning resolution. A scanner supports several options which are, in part, independent of one another. This means that if option A is modified, option B could be modified along with it. The KScanLib supports the handling of these dependencies. + +- KScanDevice\n +The object KScanDevice maps the scanners available in the system. This object assists the detection of reachable devices and the options they support. +\n +Once a device is decided upon and this has been opened, the KScanDevice will represent the scanning device. By way of this class, the hardware will actually utilize the options (KScanOption). Furthermore, scanned image data is supplied by KScanDevice. + +- KScanOptSet\n +The object KScanOptSet represents a container for the KScanOption options, apart from a specific device. Up until now, this was made possible by saving several options during the course of the program, e.g. previously configured scanning parameters if a preview scan were to be carried out so that this can be restored following the preview scan.\n +Furthermore, option sets can be saved to disk. + +\section helpers Helper Classes + +There are some helper widgets which simplify the dynamic setup of the scanning interface. These objects provide simple combinations of base widgets to make their usage easier. + +Itemized, these are: + +- KScanEntry\n(defined in kscanslider.h)\n +Provides an entry field preceded by text. + +- KScanSlider \n(defined in kscanslider.h)\n +Slider preceded by text. Range values are transferred with the data type double. + +- KScanCombo \n(defined in kscanslider.h)\n +Combobox widget which precedes description text and can represent icons. + +- DispGamma \n(defined in dispgamma.h)\n +Widget for displaying gamma tables. + +\section guiElements Interface Objects + +The KScan Library offers some ready-made objects which can be used as a sort of pre-fabricated dialog in order to integrate the scanning functionality into an application. This results in the availability of scanning functionality in all applications over the same interface. + +Currently, there are the following interface elements: + +- DeviceSelector\n +is a class which represents a dialog for scanner selection. + +- GammaDialog\n +is a dialog where a gamma table can be edited. To represent the gamma tables, the gamma dialog uses KGammaTable internally. This class is returned in such as way that it can set the relevant scanner options directly, once the dialog has been completed. + +- MassScanDialog\n +is a mass scanning dialog which informs the user about how the scanning is progressing when the automatic document feeder is used. \e very \e beta \e ! + +- ScanSourceDialog\n +Small dialog which enables scanning source selection, e.g. Flatbed, automatic document feeder... + +- ScanParams\n +The ScanParams class is the actual core of the + KScan Library in terms of interface layout. The ScanParams class provides a ready-to-use interface for the selected scanner.\n +\n + The scanner device is analyzed in this class and dynamically generates an interface, according to the device's properties, containing the most important operational elements. These are currently + + <ul> + <li> Setting options for scanning resolution + <li> Scanning source selection + <li> Scanning mode + <li> Brightness and contrast settings + <li> Gamma table + <li> Preview scanning + </ul> + + + +\author Klaas Freitag <freitag@suse.de> + +*/ + diff --git a/libkscan/kscanoption.cpp b/libkscan/kscanoption.cpp new file mode 100644 index 00000000..1aae1602 --- /dev/null +++ b/libkscan/kscanoption.cpp @@ -0,0 +1,1221 @@ +/* This file is part of the KDE Project + Copyright (C) 2000 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <stdlib.h> +#include <qwidget.h> +#include <qobject.h> +#include <qasciidict.h> +#include <qcombobox.h> +#include <qslider.h> +#include <qcheckbox.h> +#include <qlineedit.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qtooltip.h> +#include <qimage.h> +#include <qregexp.h> + +#include <kdebug.h> + +#include <unistd.h> +#include "kgammatable.h" +#include "kscandevice.h" +#include "kscanslider.h" +#include "kscanoptset.h" + + +// #define MIN_PREVIEW_DPI 20 +// this is nowhere used!? Additionally, it's defined to 75 in kscandevice.cpp + +/* switch to show some from time to time useful alloc-messages */ +#undef MEM_DEBUG + +#undef APPLY_IN_SITU + + + +/** inline-access to the option descriptor, object to changes with global vars. **/ +inline const SANE_Option_Descriptor *getOptionDesc( const QCString& name ) +{ + int *idx = (*KScanDevice::option_dic)[ name ]; + + const SANE_Option_Descriptor *d = 0; + // debug( "<< for option %s >>", name ); + if ( idx && *idx > 0 ) + { + d = sane_get_option_descriptor( KScanDevice::scanner_handle, *idx ); + // debug( "retrieving Option %s", d->name ); + } + else + { + kdDebug(29000) << "no option descriptor for <" << name << ">" << endl; + // debug( "Name survived !" ); + } + // debug( "<< leaving option %s >>", name ); + + return( d ); +} + + + +/* ************************************************************************ */ +/* KScan Option */ +/* ************************************************************************ */ +KScanOption::KScanOption( const QCString& new_name ) : + QObject() +{ + if( initOption( new_name ) ) + { + int *num = (*KScanDevice::option_dic)[ getName() ]; + if( !num || !buffer ) + return; + + SANE_Status sane_stat = sane_control_option( KScanDevice::scanner_handle, *num, + SANE_ACTION_GET_VALUE, + buffer, 0 ); + + if( sane_stat == SANE_STATUS_GOOD ) + { + buffer_untouched = false; + } + } + else + { + kdDebug(29000) << "Had problems to create KScanOption - initOption failed !" << endl; + } +} + + +bool KScanOption::initOption( const QCString& new_name ) +{ + desc = 0; + if( new_name.isEmpty() ) return( false ); + + name = new_name; + desc = getOptionDesc( name ); + buffer = 0; + internal_widget = 0; + buffer_untouched = true; + buffer_size = 0; + + if( desc ) + { + + /* Gamma-Table - initial values */ + gamma = 0; /* marks as unvalid */ + brightness = 0; + contrast = 0; + gamma = 100; + + // allocate memory + switch( desc->type ) + { + case SANE_TYPE_INT: + case SANE_TYPE_FIXED: + case SANE_TYPE_STRING: + buffer = allocBuffer( desc->size ); + break; + case SANE_TYPE_BOOL: + buffer = allocBuffer( sizeof( SANE_Word ) ); + break; + default: + buffer_size = 0; + buffer = 0; + } + + KScanOption *gtOption = (*KScanDevice::gammaTables)[ new_name ]; + if( gtOption ) + { + kdDebug(29000) << "Is older GammaTable!" << endl; + KGammaTable gt; + gtOption->get( > ); + + gamma = gt.getGamma(); + contrast = gt.getContrast(); + brightness = gt.getBrightness(); + } + else + { + // kdDebug(29000) << "Is NOT older GammaTable!" << endl; + } + } + + return desc; +} + +KScanOption::KScanOption( const KScanOption &so ) : + QObject() +{ + /* desc is stored by sane-lib and may be copied */ + desc = so.desc; + name = so.name; + buffer_untouched = so.buffer_untouched; + gamma = so.gamma; + brightness = so.brightness; + contrast = so.contrast; + + /* intialise */ + buffer = 0; + buffer_size = 0; + + /* the widget is not copied ! */ + internal_widget = 0; + + if( ! ( desc && name ) ) + { + kdWarning( 29000) << "Trying to copy a not healthy option (no name nor desc)" << endl; + return; + } + + if( so.buffer_untouched ) + kdDebug(29000) << "Buffer of source is untouched!" << endl; + + switch( desc->type ) + { + case SANE_TYPE_INT: + case SANE_TYPE_FIXED: + case SANE_TYPE_STRING: + buffer = allocBuffer( desc->size ); + // desc->size / sizeof( SANE_Word ) + memcpy( buffer, so.buffer, buffer_size ); + break; + case SANE_TYPE_BOOL: + buffer = allocBuffer( sizeof( SANE_Word ) ); + memcpy( buffer, so.buffer, buffer_size ); + break; + default: + kdWarning( 29000 ) << "unknown option type in copy constructor" << endl; + break; + } +} + + +const QString KScanOption::configLine( void ) +{ + QCString strval = this->get(); + kdDebug(29000) << "configLine returns <" << strval << ">" << endl; + return( strval ); +} + + +const KScanOption& KScanOption::operator= (const KScanOption& so ) +{ + /* desc is stored by sane-lib and may be copied */ + if( this == &so ) return( *this ); + + desc = so.desc; + name = so.name; + buffer_untouched = so.buffer_untouched; + gamma = so.gamma; + brightness = so.brightness; + contrast = so.contrast; + + delete internal_widget; + internal_widget = so.internal_widget; + + if( buffer ) { + delete [] buffer; + buffer = 0; + } + + switch( desc->type ) + { + case SANE_TYPE_INT: + case SANE_TYPE_FIXED: + case SANE_TYPE_STRING: + buffer = allocBuffer( desc->size ); + memcpy( buffer, so.buffer, buffer_size ); + break; + case SANE_TYPE_BOOL: + buffer = allocBuffer( sizeof( SANE_Word ) ); + memcpy( buffer, so.buffer, buffer_size ); + break; + default: + buffer = 0; + buffer_size = 0; + } + return( *this ); +} + +void KScanOption::slWidgetChange( const QCString& t ) +{ + kdDebug(29000) << "Received WidgetChange for " << getName() << " (const QCString&)" << endl; + set( t ); + emit( guiChange( this ) ); + // emit( optionChanged( this )); +} + +void KScanOption::slWidgetChange( void ) +{ + kdDebug(29000) << "Received WidgetChange for " << getName() << " (void)" << endl; + /* If Type is bool, the widget is a checkbox. */ + if( type() == BOOL ) + { + bool b = ((QCheckBox*) internal_widget)->isChecked(); + kdDebug(29000) << "Setting bool: " << b << endl; + set( b ); + } + emit( guiChange( this ) ); + // emit( optionChanged( this )); + +} + +void KScanOption::slWidgetChange( int i ) +{ + kdDebug(29000) << "Received WidgetChange for " << getName() << " (int)" << endl; + set( i ); + emit( guiChange( this ) ); + // emit( optionChanged( this )); +} + +/* this slot is called on a widget change, if a widget was created. + * In normal case, it is internally connected, so the param so and + * this are equal ! + */ +void KScanOption::slRedrawWidget( KScanOption *so ) +{ + // qDebug( "Checking widget %s", (const char*) so->getName()); + int help = 0; + QString string; + + QWidget *w = so->widget(); + + if( so->valid() && w && so->getBuffer() ) + { + switch( so->type( ) ) + { + case BOOL: + if( so->get( &help )) + ((QCheckBox*) w)->setChecked( (bool) help ); + /* Widget Type is ToggleButton */ + break; + case SINGLE_VAL: + /* Widget Type is Entry-Field - not implemented yet */ + + break; + case RANGE: + /* Widget Type is Slider */ + if( so->get( &help )) + ((KScanSlider*)w)->slSetSlider( help ); + + break; + case GAMMA_TABLE: + /* Widget Type is GammaTable */ + // w = new QSlider( parent, "AUTO_GAMMA" ); + break; + case STR_LIST: + // w = comboBox( parent, text ); + ((KScanCombo*)w)->slSetEntry( so->get() ); + /* Widget Type is Selection Box */ + break; + case STRING: + // w = entryField( parent, text ); + ((KScanEntry*)w)->slSetEntry( so->get() ); + /* Widget Type is Selection Box */ + break; + default: + // w = 0; + break; + } + } +} + +/* In this slot, the option queries the scanner for current values. */ + +void KScanOption::slReload( void ) +{ + int *num = (*KScanDevice::option_dic)[ getName() ]; + desc = getOptionDesc( getName() ); + + if( !desc || !num ) + return; + + if( widget() ) + { + kdDebug(29000) << "constraint is " << desc->cap << endl; + if( !active() ) + kdDebug(29000) << desc->name << " is not active now" << endl; + + if( !softwareSetable() ) + kdDebug(29000) << desc->name << " is not software setable" << endl; + + if( !active() || !softwareSetable() ) + { + kdDebug(29000) << "Disabling widget " << getName() << " !" << endl; + widget()->setEnabled( false ); + } + else + widget()->setEnabled( true ); + } + + /* first get mem if nothing is approbiate */ + if( !buffer ) + { + kdDebug(29000) << " *********** getting without space **********" << endl; + // allocate memory + switch( desc->type ) + { + case SANE_TYPE_INT: + case SANE_TYPE_FIXED: + case SANE_TYPE_STRING: + buffer = allocBuffer( desc->size ); + break; + case SANE_TYPE_BOOL: + buffer = allocBuffer( sizeof( SANE_Word ) ); + break; + default: + if( desc->size > 0 ) + { + buffer = allocBuffer( desc->size ); + } + } + } + + if( active()) + { + if( (size_t) desc->size > buffer_size ) + { + kdDebug(29000) << "ERROR: Buffer to small" << endl; + } + else + { + SANE_Status sane_stat = sane_control_option( KScanDevice::scanner_handle, *num, + SANE_ACTION_GET_VALUE, buffer, 0 ); + + if( sane_stat != SANE_STATUS_GOOD ) + { + kdDebug(29000) << "ERROR: Cant get value for " << getName() << ": " << sane_strstatus( sane_stat ) << endl; + } else { + buffer_untouched = false; + kdDebug(29000) << "Setting buffer untouched to FALSE" << endl; + } + } + } +} + + +KScanOption::~KScanOption() +{ +#ifdef MEM_DEBUG + qDebug( "M: FREEing %d byte of option <%s>", buffer_size, (const char*) getName()); +#endif +} + +bool KScanOption::valid( void ) const +{ + return( desc && 1 ); +} + +bool KScanOption::autoSetable( void ) +{ + /* Refresh description */ + desc = getOptionDesc( name ); + + return( desc && ((desc->cap & SANE_CAP_AUTOMATIC) > 0 ) ); +} + +bool KScanOption::commonOption( void ) +{ + /* Refresh description */ + desc = getOptionDesc( name ); + + return( desc && ((desc->cap & SANE_CAP_ADVANCED) == 0) ); +} + + +bool KScanOption::active( void ) +{ + bool ret = false; + /* Refresh description */ + desc = getOptionDesc( name ); + if( desc ) + ret = SANE_OPTION_IS_ACTIVE( desc->cap ); + + return( ret ); +} + +bool KScanOption::softwareSetable( void ) +{ + /* Refresh description */ + desc = getOptionDesc( name ); + if( desc ) + { + if( SANE_OPTION_IS_SETTABLE(desc->cap) == SANE_TRUE ) + return( true ); + } + return( false ); +} + + +KSANE_Type KScanOption::type( void ) const +{ + KSANE_Type ret = INVALID_TYPE; + + if( valid() ) + { + switch( desc->type ) + { + case SANE_TYPE_BOOL: + ret = BOOL; + break; + case SANE_TYPE_INT: + case SANE_TYPE_FIXED: + if( desc->constraint_type == SANE_CONSTRAINT_RANGE ) + { + /* FIXME ! Dies scheint nicht wirklich so zu sein */ + if( desc->size == sizeof( SANE_Word )) + ret = RANGE; + else + ret = GAMMA_TABLE; + } + else if( desc->constraint_type == SANE_CONSTRAINT_NONE ) + { + ret = SINGLE_VAL; + } + else if( desc->constraint_type == SANE_CONSTRAINT_WORD_LIST ) + { + ret = STR_LIST; + // ret = GAMMA_TABLE; + } + else + { + ret = INVALID_TYPE; + } + break; + case SANE_TYPE_STRING: + if( desc->constraint_type == SANE_CONSTRAINT_STRING_LIST ) + ret = STR_LIST; + else + ret = STRING; + break; + default: + ret = INVALID_TYPE; + break; + } + } + return( ret ); +} + + +bool KScanOption::set( int val ) +{ + if( ! desc ) return( false ); + bool ret = false; + + int word_size = 0; + QMemArray<SANE_Word> qa; + SANE_Word sw = SANE_TRUE; + const SANE_Word sw1 = val; + const SANE_Word sw2 = SANE_FIX( (double) val ); + + switch( desc->type ) + { + case SANE_TYPE_BOOL: + + if( ! val ) + sw = SANE_FALSE; + if( buffer ) { + memcpy( buffer, &sw, sizeof( SANE_Word )); + ret = true; + } + break; + // Type int: Fill the whole buffer with that value + case SANE_TYPE_INT: + word_size = desc->size / sizeof( SANE_Word ); + + qa.resize( word_size ); + qa.fill( sw1 ); + + if( buffer ) { + memcpy( buffer, qa.data(), desc->size ); + ret = true; + } + break; + + // Type fixed: Fill the whole buffer with that value + case SANE_TYPE_FIXED: + word_size = desc->size / sizeof( SANE_Word ); + + qa.resize( word_size ); + qa.fill( sw2 ); + + if( buffer ) { + memcpy( buffer, qa.data(), desc->size ); + ret = true; + } + break; + default: + kdDebug(29000) << "Cant set " << name << " with type int" << endl; + } + if( ret ) + { + buffer_untouched = false; +#ifdef APPLY_IN_SITU + applyVal(); +#endif + +#if 0 + emit( optionChanged( this )); +#endif + } + + return( ret ); +} + + + +bool KScanOption::set( double val ) +{ + if( ! desc ) return( false ); + bool ret = false; + int word_size = 0; + QMemArray<SANE_Word> qa; + SANE_Word sw = SANE_FALSE; + + switch( desc->type ) + { + case SANE_TYPE_BOOL: + + if( val > 0 ) + sw = SANE_TRUE; + if( buffer ) { + memcpy( buffer, &sw, sizeof( SANE_Word )); + ret = true; + } + break; + // Type int: Fill the whole buffer with that value + case SANE_TYPE_INT: + sw = (SANE_Word) val; + word_size = desc->size / sizeof( SANE_Word ); + + qa.resize( word_size ); + qa.fill( sw ); + + if( buffer ) { + memcpy( buffer, qa.data(), desc->size ); + ret = true; + } + break; + + // Type fixed: Fill the whole buffer with that value + case SANE_TYPE_FIXED: + sw = SANE_FIX( val ); + word_size = desc->size / sizeof( SANE_Word ); + + qa.resize( word_size ); + qa.fill( sw ); + + if( buffer ) { + memcpy( buffer, qa.data(), desc->size ); + ret = true; + } + break; + default: + kdDebug(29000) << "Cant set " << name << " with type double" << endl; + } + + if( ret ) + { + buffer_untouched = false; +#ifdef APPLY_IN_SITU + applyVal(); +#endif +#if 0 + emit( optionChanged( this )); +#endif + } + + return( ret ); +} + + + +bool KScanOption::set( int *val, int size ) +{ + if( ! desc || ! val ) return( false ); + bool ret = false; + int offset = 0; + + int word_size = desc->size / sizeof( SANE_Word ); /* add 1 in case offset is needed */ + QMemArray<SANE_Word> qa( 1+word_size ); +#if 0 + if( desc->constraint_type == SANE_CONSTRAINT_WORD_LIST ) + { + /* That means that the first entry must contain the size */ + kdDebug(29000) << "Size: " << size << ", word_size: " << word_size << ", descr-size: "<< desc->size << endl; + qa[0] = (SANE_Word) 1+size; + kdDebug(29000) << "set length field to " << qa[0] <<endl; + offset = 1; + } +#endif + + switch( desc->type ) + { + case SANE_TYPE_INT: + for( int i = 0; i < word_size; i++ ) + { + if( i < size ) + qa[offset+i] = (SANE_Word) *(val++); + else + qa[offset+i] = (SANE_Word) *val; + } + ret = true; + break; + + // Type fixed: Fill the whole buffer with that value + case SANE_TYPE_FIXED: + for( int i = 0; i < word_size; i++ ) + { + if( i < size ) + qa[offset+i] = SANE_FIX((double)*(val++)); + else + qa[offset+i] = SANE_FIX((double) *val ); + } + ret = true; + break; + default: + kdDebug(29000) << "Cant set " << name << " with type int*" << endl; + + } + + if( ret && buffer ) { + int copybyte = desc->size; + + if( offset ) + copybyte += sizeof( SANE_Word ); + + kdDebug(29000) << "Copying " << copybyte << " byte to options buffer" << endl; + + memcpy( buffer, qa.data(), copybyte ); + } + + if( ret ) + { + buffer_untouched = false; +#ifdef APPLY_IN_SITU + applyVal(); +#endif +#if 0 + emit( optionChanged( this )); +#endif + } + + return( ret ); +} + +bool KScanOption::set( const QCString& c_string ) +{ + bool ret = false; + int val = 0; + + if( ! desc ) return( false ); + + /* Check if it is a gammatable. If it is, convert to KGammaTable and call + * the approbiate set method. + */ + QRegExp re( "\\d+, \\d+, \\d+" ); + re.setMinimal(true); + + if( QString(c_string).contains( re )) + { + QStringList relist = QStringList::split( ", ", QString(c_string) ); + + int brig = (relist[0]).toInt(); + int contr = (relist[1]).toInt(); + int gamm = (relist[2]).toInt(); + + KGammaTable gt( brig, contr, gamm ); + ret = set( > ); + kdDebug(29000) << "Setting GammaTable with int vals " << brig << "|" << contr << "|" << gamm << endl; + + return( ret ); + } + + + /* On String-type the buffer gets malloced in Constructor */ + switch( desc->type ) + { + case SANE_TYPE_STRING: + kdDebug(29000) << "Setting " << c_string << " as String" << endl; + + if( buffer_size >= c_string.length() ) + { + memset( buffer, 0, buffer_size ); + qstrncpy( (char*) buffer, (const char*) c_string, buffer_size ); + ret = true; + } + else + { + kdDebug(29000) << "ERROR: Buffer for String " << c_string << " too small: " << buffer_size << " < " << c_string.length() << endl; + } + break; + case SANE_TYPE_INT: + case SANE_TYPE_FIXED: + kdDebug(29000) << "Type is INT or FIXED, try to set value <" << c_string << ">" << endl; + val = c_string.toInt( &ret ); + if( ret ) + set( &val, 1 ); + else + kdDebug(29000) << "Conversion of string value failed!" << endl; + + break; + case SANE_TYPE_BOOL: + kdDebug(29000) << "Type is BOOL, setting value <" << c_string << ">" << endl; + val = 0; + if( c_string == "true" ) val = 1; + set( val ); + break; + default: + kdDebug(29000) << "Type of " << name << " is " << desc->type << endl; + kdDebug(29000) << "Cant set " << name << " with type string" << endl; + break; + } + + if( ret ) + { + buffer_untouched = false; +#ifdef APPLY_IN_SITU + applyVal(); +#endif +#if 0 + emit( optionChanged( this )); +#endif + } + kdDebug(29000) << "Returning " << ret << endl; + return( ret ); +} + + +bool KScanOption::set( KGammaTable *gt ) +{ + if( ! desc ) return( false ); + bool ret = true; + int size = gt->tableSize(); + SANE_Word *run = gt->getTable(); + + int word_size = desc->size / sizeof( SANE_Word ); + QMemArray<SANE_Word> qa( word_size ); + kdDebug(29000) << "KScanOption::set for Gammatable !" << endl; + switch( desc->type ) + { + case SANE_TYPE_INT: + for( int i = 0; i < word_size; i++ ){ + if( i < size ) + qa[i] = *run++; + else + qa[i] = *run; + } + break; + + // Type fixed: Fill the whole buffer with that value + case SANE_TYPE_FIXED: + for( int i = 0; i < word_size; i++ ){ + if( i < size ) + qa[i] = SANE_FIX((double)*(run++)); + else + qa[i] = SANE_FIX((double) *run ); + } + break; + default: + kdDebug(29000) << "Cant set " << name << " with type GammaTable" << endl; + ret = false; + + } + + if( ret && buffer ) + { + /* remember raw vals */ + gamma = gt->getGamma(); + brightness = gt->getBrightness(); + contrast = gt->getContrast(); + + memcpy( buffer, qa.data(), desc->size ); + buffer_untouched = false; +#ifdef APPLY_IN_SITU + applyVal(); +#endif +#if 0 + emit( optionChanged( this )); +#endif + } + + return( ret ); +} + +bool KScanOption::get( int *val ) const +{ + SANE_Word sane_word; + double d; + if( !valid() || !getBuffer()) + return( false ); + + switch( desc->type ) + { + case SANE_TYPE_BOOL: + /* Buffer has a SANE_Word */ + sane_word = *((SANE_Word*)buffer); + if( sane_word == SANE_TRUE ) + *val = 1; + else + *val = 0; + break; + /* Type int: Fill the whole buffer with that value */ + /* reading the first is OK */ + case SANE_TYPE_INT: + sane_word = *((SANE_Word*)buffer); + *val = sane_word; + break; + + // Type fixed: whole buffer filled with the same value + case SANE_TYPE_FIXED: + d = SANE_UNFIX(*(SANE_Word*)buffer); + *val = (int)d; + break; + default: + kdDebug(29000) << "Cant get " << name << " to type int" << endl; + return( false ); + } + + // qDebug( "option::get returns %d", *val ); + return( true ); +} + + + +QCString KScanOption::get( void ) const +{ + QCString retstr; + + SANE_Word sane_word; + + + if( !valid() || !getBuffer()) + return( "parametererror" ); + + switch( desc->type ) + { + case SANE_TYPE_BOOL: + sane_word = *((SANE_Word*)buffer); + if( sane_word == SANE_TRUE ) + retstr = "true"; + else + retstr = "false"; + break; + case SANE_TYPE_STRING: + retstr = (const char*) getBuffer(); + // retstr.sprintf( "%s", cc ); + break; + case SANE_TYPE_INT: + sane_word = *((SANE_Word*)buffer); + retstr.setNum( sane_word ); + break; + case SANE_TYPE_FIXED: + sane_word = (SANE_Word) SANE_UNFIX(*(SANE_Word*)buffer); + retstr.setNum( sane_word ); + break; + + default: + kdDebug(29000) << "Cant get " << getName() << " to type String !" << endl; + retstr = "unknown"; + } + + /* Handle gamma-table correctly */ + ; + if( type() == GAMMA_TABLE ) + { + retstr.sprintf( "%d, %d, %d", gamma, brightness, contrast ); + } + + kdDebug(29000) << "option::get returns " << retstr << endl; + return( retstr ); +} + + +/* Caller needs to have the space ;) */ +bool KScanOption::get( KGammaTable *gt ) const +{ + if( gt ) + { + gt->setAll( gamma, brightness, contrast ); + // gt->calcTable(); + return( true ); + } + return( false ); +} + + +QStrList KScanOption::getList( ) const +{ + if( ! desc ) return( false ); + const char **sstring = 0; + QStrList strList; + + if( desc->constraint_type == SANE_CONSTRAINT_STRING_LIST ) { + sstring = (const char**) desc->constraint.string_list; + + while( *sstring ) + { + // qDebug( "This is in the stringlist: %s", *sstring ); + strList.append( *sstring ); + sstring++; + } + } + if( desc->constraint_type == SANE_CONSTRAINT_WORD_LIST ) { + const SANE_Int *sint = desc->constraint.word_list; + int amount_vals = *sint; sint ++; + QString s; + + for( int i=0; i < amount_vals; i++ ) { + if( desc->type == SANE_TYPE_FIXED ) + s.sprintf( "%f", SANE_UNFIX(*sint) ); + else + s.sprintf( "%d", *sint ); + sint++; + strList.append( s.local8Bit() ); + } + } + return( strList ); +} + + + +bool KScanOption::getRangeFromList( double *min, double *max, double *q ) const +{ + if( !desc ) return( false ); + bool ret = true; + + if ( desc->constraint_type == SANE_CONSTRAINT_WORD_LIST ) { + // Try to read resolutions from word list + kdDebug(29000) << "Resolutions are in a word list" << endl; + const SANE_Int *sint = desc->constraint.word_list; + int amount_vals = *sint; sint ++; + double value; + *min = 0; + *max = 0; + *q = -1; // What is q? + + for( int i=0; i < amount_vals; i++ ) { + if( desc->type == SANE_TYPE_FIXED ) { + value = (double) SANE_UNFIX( *sint ); + } else { + value = *sint; + } + if ((*min > value) || (*min == 0)) *min = value; + if ((*max < value) || (*max == 0)) *max = value; + + if( min != 0 && max != 0 && max > min ) { + double newq = max - min; + *q = newq; + } + sint++; + } + } else { + kdDebug(29000) << "getRangeFromList: No list type " << desc->name << endl; + ret = false; + } + return( ret ); +} + + + +bool KScanOption::getRange( double *min, double *max, double *q ) const +{ + if( !desc ) return( false ); + bool ret = true; + + if( desc->constraint_type == SANE_CONSTRAINT_RANGE || + desc->constraint_type == SANE_CONSTRAINT_WORD_LIST ) { + const SANE_Range *r = desc->constraint.range; + + if( desc->type == SANE_TYPE_FIXED ) { + *min = (double) SANE_UNFIX( r->min ); + *max = (double) SANE_UNFIX( r->max ); + *q = (double) SANE_UNFIX( r->quant ); + } else { + *min = r->min ; + *max = r->max ; + *q = r->quant ; + } + } else { + kdDebug(29000) << "getRange: No range type " << desc->name << endl; + ret = false; + } + return( ret ); +} + +QWidget *KScanOption::createWidget( QWidget *parent, const QString& w_desc, + const QString& tooltip ) +{ + QStrList list; + if( ! valid() ) + { + kdDebug(29000) << "The option is not valid!" << endl; + return( 0 ); + } + QWidget *w = 0; + /* free the old widget */ + delete internal_widget; + internal_widget = 0; + + /* check for text */ + QString text = w_desc; + if( text.isEmpty() && desc ) { + text = QString::fromLocal8Bit( desc->title ); + } + + + switch( type( ) ) + { + case BOOL: + /* Widget Type is ToggleButton */ + w = new QCheckBox( text, parent, "AUTO_TOGGLE_BUTTON" ); + connect( w, SIGNAL(clicked()), this, + SLOT(slWidgetChange())); + break; + case SINGLE_VAL: + /* Widget Type is Entry-Field */ + w = 0; // new QEntryField( + kdDebug(29000) << "can not create widget for SINGLE_VAL!" << endl; + break; + case RANGE: + /* Widget Type is Slider */ + w = KSaneSlider( parent, text ); + break; + case GAMMA_TABLE: + /* Widget Type is Slider */ + // w = KSaneSlider( parent, text ); + kdDebug(29000) << "can not create widget for GAMMA_TABLE!" << endl; + w = 0; // No widget, needs to be a set ! + break; + case STR_LIST: + w = comboBox( parent, text ); + /* Widget Type is Selection Box */ + break; + case STRING: + w = entryField( parent, text ); + /* Widget Type is Selection Box */ + break; + default: + kdDebug(29000) << "returning zero for default widget creation!" << endl; + w = 0; + break; + } + + if( w ) + { + internal_widget = w; + connect( this, SIGNAL( optionChanged( KScanOption*)), + SLOT( slRedrawWidget( KScanOption* ))); + QString tt = tooltip; + if( tt.isEmpty() && desc ) + tt = QString::fromLocal8Bit( desc->desc ); + + if( !tt.isEmpty() ) + QToolTip::add( internal_widget, tt ); + } + + /* Check if option is active, setEnabled etc. */ + slReload(); + if( w ) slRedrawWidget( this ); + return( w ); +} + + +QWidget *KScanOption::comboBox( QWidget *parent, const QString& text ) +{ + QStrList list = getList(); + + KScanCombo *cb = new KScanCombo( parent, text, list); + + connect( cb, SIGNAL( valueChanged( const QCString& )), this, + SLOT( slWidgetChange( const QCString& ))); + + return( cb ); +} + + +QWidget *KScanOption::entryField( QWidget *parent, const QString& text ) +{ + KScanEntry *ent = new KScanEntry( parent, text ); + connect( ent, SIGNAL( valueChanged( QCString )), this, + SLOT( slWidgetChange( QCString ))); + + return( ent ); +} + + +QWidget *KScanOption::KSaneSlider( QWidget *parent, const QString& text ) +{ + double min, max, quant; + getRange( &min, &max, &quant ); + + KScanSlider *slider = new KScanSlider( parent, text, min, max ); + /* Connect to the options change Slot */ + connect( slider, SIGNAL( valueChanged(int)), this, + SLOT( slWidgetChange(int))); + + return( slider ); +} + + + + +void *KScanOption::allocBuffer( long size ) +{ + if( size < 1 ) return( 0 ); + +#ifdef MEM_DEBUG + qDebug( "M: Reserving %ld bytes of mem for <%s>", size, (const char*) getName() ); +#endif + + void *r = new char[ size ]; + buffer_size = size; + + if( r ) memset( r, 0, size ); + + return( r ); + +} + + + +bool KScanOption::applyVal( void ) +{ + bool res = true; + int *idx = (*KScanDevice::option_dic)[ name ]; + + if( *idx == 0 ) return( false ); + if( ! buffer ) return( false ); + + SANE_Status stat = sane_control_option ( KScanDevice::scanner_handle, *idx, + SANE_ACTION_SET_VALUE, buffer, + 0 ); + if( stat != SANE_STATUS_GOOD ) + { + kdDebug(29000) << "Error in in situ appliance " << getName() << ": " << sane_strstatus( stat ) << endl; + res = false; + } + else + { + kdDebug(29000) << "IN SITU appliance " << getName() << ": OK" << endl; + + } + return( res ); +} +#include "kscanoption.moc" diff --git a/libkscan/kscanoption.h b/libkscan/kscanoption.h new file mode 100644 index 00000000..616ece23 --- /dev/null +++ b/libkscan/kscanoption.h @@ -0,0 +1,260 @@ +/* This file is part of the KDE Project + Copyright (C) 2000 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _KSCANOPTION_H_ +#define _KSCANOPTION_H_ + +#include <qwidget.h> +#include <qdict.h> +#include <qobject.h> +#include <qstrlist.h> +#include <qsocketnotifier.h> +#include <qdatastream.h> + +extern "C" { +#include <sane/sane.h> +#include <sane/saneopts.h> +} + + +typedef enum { KSCAN_OK, KSCAN_ERROR, KSCAN_ERR_NO_DEVICE, + KSCAN_ERR_BLOCKED, KSCAN_ERR_NO_DOC, KSCAN_ERR_PARAM, + KSCAN_ERR_OPEN_DEV, KSCAN_ERR_CONTROL, KSCAN_ERR_EMPTY_PIC, + KSCAN_ERR_MEMORY, KSCAN_ERR_SCAN, KSCAN_UNSUPPORTED, + KSCAN_RELOAD, KSCAN_CANCELLED, KSCAN_OPT_NOT_ACTIVE } +KScanStat; + +typedef enum { INVALID_TYPE, BOOL, SINGLE_VAL, RANGE, GAMMA_TABLE, STR_LIST, STRING } +KSANE_Type; + +class KGammaTable; + + +/** + * This is KScanOption, a class which holds a single scanner option. + * + * @short KScanOption + * @author Klaas Freitag + * @version 0.1alpha + * + * is a help class for accessing the scanner options. + **/ + + +class KScanOption : public QObject +{ + Q_OBJECT + +public: + /** + * creates a new option object for the named option. After that, the + * option is valid and contains the correct value retrieved from the + * scanner. + **/ + KScanOption( const QCString& new_name ); + + /** + * creates a KScanOption from another + **/ + KScanOption( const KScanOption& so ); + ~KScanOption(); + + /** + * tests if the option is initialised. It is initialised, if the value + * for the option was once read from the scanner + **/ + bool initialised( void ){ return( ! buffer_untouched );} + + /** + * checks if the option is valid, means that the option is known by the scanner + **/ + bool valid( void ) const; + + /** + * checks if the option is auto setable, what means, the scanner can cope + * with the option automatically. + **/ + bool autoSetable( void ); + + /** + * checks if the option is a so called common option. All frontends should + * provide gui elements to change them. + **/ + bool commonOption( void ); + + /** + * checks if the option is active at the moment. Note that this changes + * on runtime due to the settings of mode, resolutions etc. + **/ + bool active( void ); + + /** + * checks if the option is setable by software. Some scanner options can not + * be set by software. + **/ + bool softwareSetable(); + + /** + * returns the type the option is. Type is one of + * INVALID_TYPE, BOOL, SINGLE_VAL, RANGE, GAMMA_TABLE, STR_LIST, STRING + **/ + KSANE_Type type( void ) const; + + /** + * set the option depending on the type + **/ + bool set( int val ); + bool set( double val ); + bool set( int *p_val, int size ); + bool set( const QCString& ); + bool set( bool b ){ if( b ) return(set( (int)(1==1) )); else return( set( (int) (1==0) )); } + bool set( KGammaTable *gt ); + + /** + * retrieves the option value, depending on the type. + **/ + bool get( int* ) const; + bool get( KGammaTable* ) const; + QCString get( void ) const; + + /** + * This function creates a widget for the scanner option depending + * on the type of the option. + * + * For boolean options, a checkbox is generated. For ranges, a KSaneSlider + * is generated. + * + * For a String list such as mode etc., a KScanCombo is generated. + * + * For option type string and gamma table, no widget is generated yet. + * + * The widget is maintained completely by the kscanoption object. + * + **/ + + QWidget *createWidget( QWidget *parent, + const QString& w_desc = QString::null, + const QString& tooltip = QString::null ); + + /* Operators */ + const KScanOption& operator= (const KScanOption& so ); + + const QString configLine( void ); + + + // Possible Values + QStrList getList() const; + bool getRangeFromList( double*, double*, double* ) const; + bool getRange( double*, double*, double* ) const; + + QCString getName() const { return( name ); } + void * getBuffer() const { return( buffer ); } + QWidget *widget( ) const { return( internal_widget ); } + /** + * returns the type of the selected option. + * This might be SINGLE_VAL, VAL_LIST, STR_LIST, GAMMA_TABLE, + * RANGE or BOOL + * + * You may use the information returned to decide, in which way + * the option is to set. + * + * A SINGLE_VAL is returned in case the value is represented by a + * single number, e.g. the resoltion. + * + * A VAL_LIST is returned in case the value needs to be set by + * a list of numbers. You need to check the size to find out, + * how many numbers you have to + * @param name: the name of a option from a returned option-List + * return a option type. + + ### not implemented at all? + **/ + KSANE_Type typeToSet( const QCString& name ); + + /** + * returns a string describing the unit of given the option. + * @return the unit description, e.g. mm + * @param name: the name of a option from a returned option-List + + ### not implemented at all? + **/ + QString unitOf( const QCString& name ); + +public slots: + void slRedrawWidget( KScanOption *so ); + /** + * that slot makes the option to reload the buffer from the scanner. + */ + void slReload( void ); + +protected slots: + /** + * this slot is called if an option has a gui element (not all have) + * and if the value of the widget is changed. + * This is an internal slot. + **/ + void slWidgetChange( void ); + void slWidgetChange( const QCString& ); + void slWidgetChange( int ); + + signals: + /** + * Signal emitted if a option changed for different reasons. + * The signal should be connected by outside objects. + **/ + void optionChanged( KScanOption*); + /** + * Signal emitted if the option is set by a call to the set() + * member of the option. In the slot needs to be checked, if + * a widget exists, it needs to be set to the new value. + * This is a more internal signal + **/ + void optionSet( void ); + + /** + * Signal called when user changes a gui - element + */ + void guiChange( KScanOption* ); + +private: + bool applyVal( void ); + bool initOption( const QCString& new_name ); + void *allocBuffer( long ); + + QWidget *entryField ( QWidget *parent, const QString& text ); + QWidget *KSaneSlider( QWidget *parent, const QString& text ); + QWidget *comboBox ( QWidget *parent, const QString& text ); + + const SANE_Option_Descriptor *desc; + QCString name; + void *buffer; + QWidget *internal_widget; + bool buffer_untouched; + size_t buffer_size; + + /* For gamma-Tables remember gamma, brightness, contrast */ + int gamma, brightness, contrast; + + class KScanOptionPrivate; + KScanOptionPrivate *d; +}; + + + +#endif diff --git a/libkscan/kscanoptset.cpp b/libkscan/kscanoptset.cpp new file mode 100644 index 00000000..ac30ae40 --- /dev/null +++ b/libkscan/kscanoptset.cpp @@ -0,0 +1,221 @@ +/* This file is part of the KDE Project + Copyright (C) 2000 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qstring.h> +#include <qasciidict.h> +#include <qmap.h> +#include <qdict.h> +#include <kdebug.h> +#include <kconfig.h> + +#include "kscandevice.h" +#include "kscanoption.h" +#include "kscanoptset.h" + +KScanOptSet::KScanOptSet( const QCString& setName ) +{ + name = setName; + + setAutoDelete( false ); + + description = ""; + + strayCatsList.setAutoDelete( true ); +} + + + +KScanOptSet::~KScanOptSet() +{ + /* removes all deep copies from backupOption */ + strayCatsList.clear(); +} + + + +KScanOption *KScanOptSet::get( const QCString name ) const +{ + KScanOption *ret = 0; + + ret = (*this) [name]; + + return( ret ); +} + +QCString KScanOptSet::getValue( const QCString name ) const +{ + KScanOption *re = get( name ); + QCString retStr = ""; + + if( re ) + { + retStr = re->get(); + } + else + { + kdDebug(29000) << "option " << name << " from OptionSet is not available" << endl; + } + return( retStr ); +} + + +bool KScanOptSet::backupOption( const KScanOption& opt ) +{ + bool retval = true; + + /** Allocate a new option and store it **/ + const QCString& optName = opt.getName(); + if( !optName ) + retval = false; + + if( retval ) + { + KScanOption *newopt = find( optName ); + + if( newopt ) + { + /** The option already exists **/ + /* Copy the new one into the old one. TODO: checken Zuweisungoperatoren OK ? */ + *newopt = opt; + } + else + { + const QCString& qq = opt.get(); + kdDebug(29000) << "Value is now: <" << qq << ">" << endl; + const KScanOption *newopt = new KScanOption( opt ); + + strayCatsList.append( newopt ); + + if( newopt ) + { + insert( optName, newopt ); + } else { + retval = false; + } + } + } + + return( retval ); + +} + +QString KScanOptSet::getDescription() const +{ + return description; +} + +void KScanOptSet::slSetDescription( const QString& str ) +{ + description = str; +} + +void KScanOptSet::backupOptionDict( const QAsciiDict<KScanOption>& optDict ) +{ + QAsciiDictIterator<KScanOption> it( optDict ); + + while ( it.current() ) + { + kdDebug(29000) << "Dict-Backup of Option <" << it.currentKey() << ">" << endl; + backupOption( *(it.current())); + ++it; + } + + +} + +/* */ +void KScanOptSet::saveConfig( const QString& scannerName, const QString& configName, + const QString& descr ) +{ + QString confFile = SCANNER_DB_FILE; + kdDebug( 29000) << "Creating scan configuration file <" << confFile << ">" << endl; + + KConfig *scanConfig = new KConfig( confFile ); + QString cfgName = configName; + + if( configName.isNull() || configName.isEmpty() ) + cfgName = "default"; + + scanConfig->setGroup( cfgName ); + + scanConfig->writeEntry( "description", descr ); + scanConfig->writeEntry( "scannerName", scannerName ); + QAsciiDictIterator<KScanOption> it( *this); + + while ( it.current() ) + { + const QString line = it.current() -> configLine(); + const QString name = it.current()->getName(); + + kdDebug(29000) << "writing " << name << " = <" << line << ">" << endl; + + scanConfig->writeEntry( name, line ); + + ++it; + } + + scanConfig->sync(); + delete( scanConfig ); +} + +bool KScanOptSet::load( const QString& /*scannerName*/ ) +{ + QString confFile = SCANNER_DB_FILE; + kdDebug( 29000) << "** Reading from scan configuration file <" << confFile << ">" << endl; + bool ret = true; + + KConfig *scanConfig = new KConfig( confFile, true ); + QString cfgName = name; /* of the KScanOptSet, given in constructor */ + + if( cfgName.isNull() || cfgName.isEmpty() ) + cfgName = "default"; + + if( ! scanConfig->hasGroup( name ) ) + { + kdDebug(29000) << "Group " << name << " does not exist in configuration !" << endl; + ret = false; + } + else + { + scanConfig->setGroup( name ); + + typedef QMap<QString, QString> StringMap; + + StringMap strMap = scanConfig->entryMap( name ); + + StringMap::Iterator it; + for( it = strMap.begin(); it != strMap.end(); ++it ) + { + QCString optName = it.key().latin1(); + KScanOption optset( optName ); + + QCString val = it.data().latin1(); + kdDebug(29000) << "Reading for " << optName << " value " << val << endl; + + optset.set( val ); + + backupOption( optset ); + } + } + delete( scanConfig ); + + return( ret ); +} + +/* END */ diff --git a/libkscan/kscanoptset.h b/libkscan/kscanoptset.h new file mode 100644 index 00000000..042dbe86 --- /dev/null +++ b/libkscan/kscanoptset.h @@ -0,0 +1,113 @@ +/* This file is part of the KDE Project + Copyright (C) 2000 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KSCANOPTSET_H +#define KSCANOPTSET_H + +#include <qobject.h> +#include <qstring.h> +#include <qptrlist.h> +#include <qasciidict.h> + + +#include "kscanoption.h" + +/** + * This is a container class for KScanOption-objects, which contain information + * about single scanner dependant options. It allows you to store a bunch + * of options and accessing them via a iterator. + * + * The class which is inherited from QAsciiDict does no deep copy of the options + * to store by with the standard method insert. + * @see backupOption to get a deep copy. + * + * Note that the destructor of the KScanOptSet only clears the options created + * by backupOption. + * + * @author Klaas Freitag@SuSE.de + * @version 0.1 + */ + + + +class KScanOptSet: public QAsciiDict<KScanOption> +{ + +public: + /** + * Constructor to create a new Container. Takes a string as a name, which + * has no special meaning yet ;) + */ + KScanOptSet( const QCString& ); + ~KScanOptSet(); + + /** + * function to store a deep copy of an option. Note that this class is inherited + * from QAsciiDict and thus does no deep copies. This method does. + * @see insert + */ + bool backupOption( const KScanOption& ); + + /** + * returns a pointer to a stored option given by name. + */ + KScanOption *get( const QCString name ) const; + QCString getValue( const QCString name ) const; + + void backupOptionDict( const QAsciiDict<KScanOption>& ); + + /** + * saves a configuration set to the configuration file 'ScanSettings' + * in the default dir config (@see KDir). It uses the group given + * in configName and stores the entire option set in that group. + * additionally, a description is also saved. + * + * @param scannerName : the name of the scanner + * @param configName: The name of the config, e.g. Black and White + * @param descr : A description for the config. + */ + void saveConfig( const QString&, const QString&, const QString&); + + /** + * allows to load a configuration. Simple create a optionSet with the + * approbiate name the config was called ( @see saveConfig ) and call + * load for the scanner you want. + * @param scannerName: A scanner's name + */ + bool load( const QString& scannerName ); + + QString getDescription() const; + +public slots: + + void slSetDescription( const QString& ); + +private: + QCString name; + + /* List to collect objects for which memory was allocated and must be freed */ + QPtrList<KScanOption> strayCatsList; + + class KScanOptSetPrivate; + KScanOptSetPrivate *d; + + QString description; +}; + +#endif // KScanOptSet diff --git a/libkscan/kscanslider.cpp b/libkscan/kscanslider.cpp new file mode 100644 index 00000000..3bf50395 --- /dev/null +++ b/libkscan/kscanslider.cpp @@ -0,0 +1,319 @@ +/* This file is part of the KDE Project + Copyright (C) 2000 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qlayout.h> +#include <qpushbutton.h> +#include <qspinbox.h> +#include <qtooltip.h> +#include <qcombobox.h> +#include <qlabel.h> +#include <qslider.h> +#include <qlineedit.h> + +#include <kiconloader.h> +#include <klocale.h> +#include <kdebug.h> +#include "kscanslider.h" + +KScanSlider::KScanSlider( QWidget *parent, const QString& text, + double min, double max, bool haveStdButt, + int stdValue ) + : QFrame( parent ), + m_stdValue( stdValue ), + m_stdButt(0) +{ + QHBoxLayout *hb = new QHBoxLayout( this ); + l1 = new QLabel( text, this, "AUTO_SLIDER_LABEL" ); + hb->addWidget( l1,20 ); + + if( haveStdButt ) + { + KIconLoader *loader = KGlobal::iconLoader(); + m_stdButt = new QPushButton( this ); + m_stdButt->setPixmap( loader->loadIcon( "undo",KIcon::Small )); + + /* connect the button click to setting the value */ + connect( m_stdButt, SIGNAL(clicked()), + this, SLOT(slRevertValue())); + + QToolTip::add( m_stdButt, + i18n( "Revert value back to its standard value %1" ).arg( stdValue )); + hb->addWidget( m_stdButt, 0 ); + hb->addSpacing( 4 ); + } + + slider = new QSlider( (int) min, (int)max, 1, (int)min, QSlider::Horizontal, this, "AUTO_SLIDER_" ); + slider->setTickmarks( QSlider::Below ); + slider->setTickInterval( int(QMAX( (max-min)/10, 1 )) ); + slider->setSteps( int(QMAX( (max-min)/20, 1) ), int(QMAX( (max-min)/10, 1) ) ); + slider->setMinimumWidth( 140 ); + /* set a buddy */ + l1->setBuddy( slider ); + + /* create a spinbox for displaying the values */ + m_spin = new QSpinBox( (int) min, (int) max, + 1, // step + this ); + + + /* make spin box changes change the slider */ + connect( m_spin, SIGNAL(valueChanged(int)), this, SLOT(slSliderChange(int))); + + /* Handle internal number display */ + // connect(slider, SIGNAL(valueChanged(int)), numdisp, SLOT( setNum(int) )); + connect(slider, SIGNAL(valueChanged(int)), this, SLOT( slSliderChange(int) )); + + /* set Value 0 to the widget */ + slider->setValue( (int) min -1 ); + + /* Add to layout widget and activate */ + hb->addWidget( slider, 36 ); + hb->addSpacing( 4 ); + hb->addWidget( m_spin, 0 ); + + hb->activate(); + +} + +void KScanSlider::setEnabled( bool b ) +{ + if( slider ) + slider->setEnabled( b ); + if( l1 ) + l1->setEnabled( b ); + if( m_spin ) + m_spin->setEnabled( b ); + if( m_stdButt ) + m_stdButt->setEnabled( b ); +} + +void KScanSlider::slSetSlider( int value ) +{ + /* Important to check value to avoid recursive signals ;) */ + // debug( "Slider val: %d -> %d", value, slider_val ); + kdDebug(29000) << "Setting Slider with " << value << endl; + + if( value == slider->value() ) + { + kdDebug(29000) << "Returning because slider value is already == " << value << endl; + return; + } + slider->setValue( value ); + slSliderChange( value ); + +} + +void KScanSlider::slSliderChange( int v ) +{ + // kdDebug(29000) << "Got slider val: " << v << endl; + // slider_val = v; + int spin = m_spin->value(); + if( v != spin ) + m_spin->setValue(v); + int slid = slider->value(); + if( v != slid ) + slider->setValue(v); + + emit( valueChanged( v )); +} + +void KScanSlider::slRevertValue() +{ + if( m_stdButt ) + { + /* Only if stdButt is non-zero, the default value is valid */ + slSetSlider( m_stdValue ); + } +} + + +KScanSlider::~KScanSlider() +{ +} + +/* ====================================================================== */ + +KScanEntry::KScanEntry( QWidget *parent, const QString& text ) + : QFrame( parent ) +{ + QHBoxLayout *hb = new QHBoxLayout( this ); + + QLabel *l1 = new QLabel( text, this, "AUTO_ENTRYFIELD" ); + hb->addWidget( l1,1 ); + + entry = new QLineEdit( this, "AUTO_ENTRYFIELD_E" ); + l1->setBuddy( entry ); + connect( entry, SIGNAL( textChanged(const QString& )), + this, SLOT( slEntryChange(const QString&))); + connect( entry, SIGNAL( returnPressed()), + this, SLOT( slReturnPressed())); + + hb->addWidget( entry,3 ); + hb->activate(); +} + +QString KScanEntry::text( void ) const +{ + QString str = QString::null; + // kdDebug(29000) << "entry is "<< entry << endl; + if(entry) + { + str = entry->text(); + if( ! str.isNull() && ! str.isEmpty()) + { + kdDebug(29000) << "KScanEntry returns <" << str << ">" << endl; + } + else + { + kdDebug(29000) << "KScanEntry: nothing entered !" << endl; + } + } + else + { + kdDebug(29000) << "KScanEntry ERR: member var entry not defined!" << endl; + } + return ( str ); +} + +void KScanEntry::slSetEntry( const QString& t ) +{ + if( t == entry->text() ) + return; + /* Important to check value to avoid recursive signals ;) */ + + entry->setText( t ); +} + +void KScanEntry::slEntryChange( const QString& t ) +{ + emit valueChanged( QCString( t.latin1() ) ); +} + +void KScanEntry::slReturnPressed( void ) +{ + QString t = text(); + emit returnPressed( QCString( t.latin1())); +} + + + +KScanCombo::KScanCombo( QWidget *parent, const QString& text, + const QStrList& list ) + : QHBox( parent ), + combo(0) +{ + createCombo( text ); + if( combo ) + combo->insertStrList( list); + combolist = list; +} + +KScanCombo::KScanCombo( QWidget *parent, const QString& text, + const QStringList& list ) + : QHBox( parent ), + combo(0) +{ + createCombo( text ); + if( combo ) + combo->insertStringList( list ); + + for ( QStringList::ConstIterator it = list.begin(); it != list.end(); ++it ) { + combolist.append( (*it).local8Bit() ); + } +} + + +void KScanCombo::createCombo( const QString& text ) +{ + setSpacing( 12 ); + setMargin( 2 ); + + + (void) new QLabel( text, this, "AUTO_COMBOLABEL" ); + + combo = new QComboBox( this, "AUTO_COMBO" ); + + connect( combo, SIGNAL(activated( const QString &)), this, + SLOT( slComboChange( const QString &))); + connect( combo, SIGNAL(activated( int )), + this, SLOT(slFireActivated(int))); + +} + + +void KScanCombo::slSetEntry( const QString &t ) +{ + if( t.isNull() ) return; + int i = combolist.find( t.local8Bit() ); + + /* Important to check value to avoid recursive signals ;) */ + if( i == combo->currentItem() ) + return; + + if( i > -1 ) + combo->setCurrentItem( i ); + else + kdDebug(29000) << "Combo item not in list !" << endl; +} + +void KScanCombo::slComboChange( const QString &t ) +{ + emit valueChanged( QCString( t.latin1() ) ); + kdDebug(29000) << "Combo: valueChanged emitted!" << endl; +} + +void KScanCombo::slSetIcon( const QPixmap& pix, const QString& str) +{ + for( int i=0; i < combo->count(); i++ ) + { + if( combo->text(i) == str ) + { + combo->changeItem( pix, str, i ); + break; + } + } +} + +QString KScanCombo::currentText( void ) const +{ + return( combo->currentText() ); +} + + +QString KScanCombo::text( int i ) const +{ + return( combo->text(i) ); +} + +void KScanCombo::setCurrentItem( int i ) +{ + combo->setCurrentItem( i ); +} + +int KScanCombo::count( void ) const +{ + return( combo->count() ); +} + +void KScanCombo::slFireActivated( int i ) +{ + emit( activated( i )); +} + +#include "kscanslider.moc" diff --git a/libkscan/kscanslider.h b/libkscan/kscanslider.h new file mode 100644 index 00000000..7502ecad --- /dev/null +++ b/libkscan/kscanslider.h @@ -0,0 +1,244 @@ +/* This file is part of the KDE Project + Copyright (C) 2000 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KSCANSLIDER_H +#define KSCANSLIDER_H + +#include <qframe.h> +#include <qstrlist.h> +#include <qhbox.h> +#include <qcombobox.h> +#include <qslider.h> +#include <qlineedit.h> +/** + *@author Klaas Freitag + */ + +class QPushButton; +class QSpinBox; +class QLabel; + +/** + * a kind of extended slider which has a spinbox beside the slider offering + * the possibility to enter an exact numeric value to the slider. If + * desired, the slider has a neutral button by the side. A descriptional + * text is handled automatically. + * + * @author Klaas Freitag <freitag@suse.de> + */ +class KScanSlider : public QFrame +{ + Q_OBJECT + Q_PROPERTY( int slider_val READ value WRITE slSetSlider ) + +public: + /** + * Create the slider. + * + * @param parent parent widget + * @param text is the text describing the the slider value. If the text + * contains a '&', a buddy for the slider will be created. + * @param min minimum slider value + * @param max maximum slider value + * @param haveStdButt make a 'set to standard'-button visible. The button + * appears on the left of the slider. + * @param stdValue the value to which the standard button resets the slider. + */ + KScanSlider( QWidget *parent, const QString& text, + double min, double max, bool haveStdButt=false, + int stdValue=0); + /** + * Destructor + */ + ~KScanSlider(); + + /** + * @return the current slider value + */ + int value( ) const + { return( slider->value()); } + +public slots: + /** + * sets the slider value + */ + void slSetSlider( int ); + + /** + * enables the complete slider. + */ + void setEnabled( bool b ); + +protected slots: + /** + * reverts the slider back to the standard value given in the constructor + */ + void slRevertValue(); + + signals: + /** + * emitted if the slider value changes + */ + void valueChanged( int ); + +private slots: + void slSliderChange( int ); + +private: + QSlider *slider; + QLabel *l1, *numdisp; + QSpinBox *m_spin; + int m_stdValue; + QPushButton *m_stdButt; + class KScanSliderPrivate; + KScanSliderPrivate *d; + +}; + +/** + * a entry field with a prefix text for description. + */ +class KScanEntry : public QFrame +{ + Q_OBJECT + Q_PROPERTY( QString text READ text WRITE slSetEntry ) + +public: + /** + * create a new entry field prepended by text. + * @param parent the parent widget + * @text the prefix text + */ + KScanEntry( QWidget *parent, const QString& text ); + // ~KScanEntry(); + + /** + * @return the current entry field contents. + */ + QString text( ) const; + +public slots: + /** + * set the current text + * @param t the new string + */ + void slSetEntry( const QString& t ); + /** + * enable or disable the text entry. + * @param b set enabled if true, else disabled. + */ + void setEnabled( bool b ){ if( entry) entry->setEnabled( b ); } + +protected slots: + void slReturnPressed( void ); + +signals: + void valueChanged( const QCString& ); + void returnPressed( const QCString& ); + +private slots: + void slEntryChange( const QString& ); + +private: + QLineEdit *entry; + + class KScanEntryPrivate; + KScanEntryPrivate *d; + +}; + + +/** + * a combobox filled with a decriptional text. + */ +class KScanCombo : public QHBox +{ + Q_OBJECT + Q_PROPERTY( QString cbEntry READ currentText WRITE slSetEntry ) + +public: + /** + * create a combobox with prepended text. + * + * @param parent parent widget + * @param text the text the combobox is prepended by + * @param list a stringlist with values the list should contain. + */ + KScanCombo( QWidget *parent, const QString& text, const QStrList& list ); + KScanCombo( QWidget *parent, const QString& text, const QStringList& list ); + // ~KScanCombo(); + + /** + * @return the current selected text + */ + QString currentText( ) const; + + /** + * @return the text a position i + */ + QString text( int i ) const; + + /** + * @return the amount of list entries. + */ + int count( ) const; + +public slots: + /** + * set the current entry to the given string if it is member of the list. + * if not, the entry is not changed. + */ + void slSetEntry( const QString &); + + /** + * enable or disable the combobox. + * @param b enables the combobox if true. + */ + void setEnabled( bool b){ if(combo) combo->setEnabled( b ); }; + + /** + * set an icon for a string in the combobox + * @param pix the pixmap to set. + * @param str the string for which the pixmap should be set. + */ + void slSetIcon( const QPixmap& pix, const QString& str); + + /** + * set the current item of the combobox. + */ + void setCurrentItem( int i ); + +private slots: + void slFireActivated( int); + void slComboChange( const QString & ); + +signals: + void valueChanged( const QCString& ); + void activated(int); + +private: + void createCombo( const QString& text ); + QComboBox *combo; + QStrList combolist; + + class KScanComboPrivate; + KScanComboPrivate *d; +}; + +#endif diff --git a/libkscan/massscandialog.cpp b/libkscan/massscandialog.cpp new file mode 100644 index 00000000..c86cd4e5 --- /dev/null +++ b/libkscan/massscandialog.cpp @@ -0,0 +1,123 @@ +/* This file is part of the KDE Project + Copyright (C) 2000 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qlayout.h> +#include <qlabel.h> +#include <qprogressbar.h> +#include <qgroupbox.h> +#include <qframe.h> + +#include <klocale.h> +#include <kdebug.h> +#include <kpushbutton.h> +#include <kstdguiitem.h> + +#include "massscandialog.h" + +MassScanDialog::MassScanDialog( QWidget *parent ) + :QDialog( parent, "MASS_SCAN", true ) +{ + setCaption( i18n( "ADF Scanning" )); + kdDebug(29000) << "Starting MassScanDialog!" << endl; + // Layout-Boxes + QVBoxLayout *bigdad = new QVBoxLayout( this, 5 ); + // QHBoxLayout *hl1= new QHBoxLayout( ); // Caption + QHBoxLayout *l_but = new QHBoxLayout( 10 ); // Buttons + + /* Caption */ + QLabel *l1 = new QLabel( i18n( "<B>Mass Scanning</B>" ), this); + bigdad->addWidget( l1, 1); + + /* Scan parameter information */ + QGroupBox *f1 = new QGroupBox( i18n("Scan Parameter"), this ); + f1->setFrameStyle( QFrame::Box | QFrame::Sunken ); + f1->setMargin(5); + f1->setLineWidth( 1 ); + QVBoxLayout *l_main = new QVBoxLayout( f1, f1->frameWidth()+3, 3 ); + bigdad->addWidget( f1, 6 ); + + scanopts = i18n("Scanning <B>%s</B> with <B>%d</B> dpi"); + l_scanopts = new QLabel( scanopts, f1 ); + l_main->addWidget( l_scanopts ); + + tofolder = i18n("Storing new images in folder <B>%s</B>"); + l_tofolder = new QLabel( tofolder, f1 ); + l_main->addWidget( l_tofolder ); + + /* Scan Progress information */ + QGroupBox *f2 = new QGroupBox( i18n("Scan Progress"), this ); + f2->setFrameStyle( QFrame::Box | QFrame::Sunken ); + f2->setMargin(15); + f2->setLineWidth( 1 ); + QVBoxLayout *l_pro = new QVBoxLayout( f2, f2->frameWidth()+3, 3 ); + bigdad->addWidget( f2, 6 ); + + QHBoxLayout *l_scanp = new QHBoxLayout( ); + l_pro->addLayout( l_scanp, 5 ); + progress = i18n("Scanning page %1"); + l_progress = new QLabel( progress, f2 ); + l_scanp->addWidget( l_progress, 3 ); + l_scanp->addStretch( 1 ); + QPushButton *pb_cancel_scan = new QPushButton( i18n("Cancel Scan"), f2); + l_scanp->addWidget( pb_cancel_scan,3 ); + + progressbar = new QProgressBar( 1000, f2 ); + l_pro->addWidget( progressbar, 3 ); + + /* Buttons to start scanning and close the Window */ + bigdad->addLayout( l_but ); + + QPushButton *b_start = new QPushButton( i18n("Start Scan"), this, "ButtOK" ); + connect( b_start, SIGNAL(clicked()), this, SLOT( slStartScan()) ); + + QPushButton *b_cancel = new QPushButton( i18n("Stop"), this, "ButtCancel" ); + connect( b_cancel, SIGNAL(clicked()), this, SLOT(slStopScan()) ); + + QPushButton *b_finish = new KPushButton( KStdGuiItem::close(), this, "ButtFinish" ); + connect( b_finish, SIGNAL(clicked()), this, SLOT(slFinished()) ); + + l_but->addWidget( b_start ); + l_but->addWidget( b_cancel ); + l_but->addWidget( b_finish ); + + bigdad->activate(); + show(); +} + +MassScanDialog::~MassScanDialog() +{ + +} + +void MassScanDialog::slStartScan( void ) +{ + +} + +void MassScanDialog::slStopScan( void ) +{ + +} + +void MassScanDialog::slFinished( void ) +{ + delete this; +} + +#include "massscandialog.moc" diff --git a/libkscan/massscandialog.h b/libkscan/massscandialog.h new file mode 100644 index 00000000..6e57b098 --- /dev/null +++ b/libkscan/massscandialog.h @@ -0,0 +1,67 @@ +/* This file is part of the KDE Project + Copyright (C) 2000 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef MASSSCANDIALOG_H +#define MASSSCANDIALOG_H + +#include <qstring.h> +#include <qdialog.h> + +class QProgressBar; +class QLabel; + +/** + *@author Klaas Freitag + */ + +class MassScanDialog : public QDialog +{ + Q_OBJECT +public: + MassScanDialog( QWidget *parent); + ~MassScanDialog(); + +public slots: + + void slStartScan( void ); + void slStopScan( void ); + void slFinished( void ); + + void setPageProgress( int p ) + { + progressbar->setProgress( p ); + } + +private: + QString scanopts; + QLabel *l_scanopts; + + QString tofolder; + QLabel *l_tofolder; + + QString progress; + QLabel *l_progress; + + QProgressBar *progressbar; + + class MassScanDialogPrivate; + MassScanDialogPrivate *d; +}; + +#endif diff --git a/libkscan/pics/Makefile.am b/libkscan/pics/Makefile.am new file mode 100644 index 00000000..56681824 --- /dev/null +++ b/libkscan/pics/Makefile.am @@ -0,0 +1 @@ +KDE_ICON=AUTO
\ No newline at end of file diff --git a/libkscan/pics/cr16-action-palette_color.png b/libkscan/pics/cr16-action-palette_color.png Binary files differnew file mode 100644 index 00000000..f41ed604 --- /dev/null +++ b/libkscan/pics/cr16-action-palette_color.png diff --git a/libkscan/pics/cr16-action-palette_gray.png b/libkscan/pics/cr16-action-palette_gray.png Binary files differnew file mode 100644 index 00000000..fe2a423c --- /dev/null +++ b/libkscan/pics/cr16-action-palette_gray.png diff --git a/libkscan/pics/cr16-action-palette_halftone.png b/libkscan/pics/cr16-action-palette_halftone.png Binary files differnew file mode 100644 index 00000000..3d410fc9 --- /dev/null +++ b/libkscan/pics/cr16-action-palette_halftone.png diff --git a/libkscan/pics/cr16-action-palette_lineart.png b/libkscan/pics/cr16-action-palette_lineart.png Binary files differnew file mode 100644 index 00000000..3d410fc9 --- /dev/null +++ b/libkscan/pics/cr16-action-palette_lineart.png diff --git a/libkscan/previewer.cpp b/libkscan/previewer.cpp new file mode 100644 index 00000000..f4133ee9 --- /dev/null +++ b/libkscan/previewer.cpp @@ -0,0 +1,871 @@ + +/* This file is part of the KDE Project + Copyright (C) 2000 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qlabel.h> +#include <qfontmetrics.h> +#include <qhbox.h> +#include <qtooltip.h> +#include <qpopupmenu.h> +#include <qfile.h> +#include <qtextstream.h> +#include <qcombobox.h> +#include <qradiobutton.h> +#include <qgroupbox.h> +#include <qlayout.h> + +#include <kdebug.h> +#include <klocale.h> +#include <kcombobox.h> +#include <kaction.h> +#include <kstandarddirs.h> + +#include "previewer.h" +#include "img_canvas.h" +#include "sizeindicator.h" +#include "devselector.h" /* for definition of config key :( */ +#include "kscandevice.h" +#include <qslider.h> +#include <qcheckbox.h> +#include <kconfig.h> +#include <qbuttongroup.h> +#include <qvbuttongroup.h> +#include <kmessagebox.h> +#include <qvaluevector.h> + +#define ID_CUSTOM 0 +#define ID_A4 1 +#define ID_A5 2 +#define ID_A6 3 +#define ID_9_13 4 +#define ID_10_15 5 +#define ID_LETTER 6 + +/** Config tags for autoselection **/ +#define CFG_AUTOSEL_DO "doAutoselection" /* do it or not */ +#define CFG_AUTOSEL_THRESH "autoselThreshold" /* threshold */ +#define CFG_AUTOSEL_DUSTSIZE "autoselDustsize" /* dust size */ +/* tag if a scan of the empty scanner results in black or white image */ +#define CFG_SCANNER_EMPTY_BG "scannerBackgroundWhite" + +/* Defaultvalues for the threshold for the autodetection */ +#define DEF_THRESH_BLACK "45" +#define DEF_THRESH_WHITE "240" + +/* Items for the combobox to set the color of an empty scan */ +#define BG_ITEM_BLACK 0 +#define BG_ITEM_WHITE 1 + + +class Previewer::PreviewerPrivate +{ +public: + PreviewerPrivate(): + m_doAutoSelection(false), + m_autoSelThresh(0), + m_dustsize(5), + m_bgIsWhite(false), + m_sliderThresh(0), + m_sliderDust(0), + m_cbAutoSel(0), + m_cbBackground(0), + m_autoSelGroup(0), + m_scanner(0) + { + } + bool m_doAutoSelection; /* switch auto-selection on and off */ + int m_autoSelThresh; /* threshold for auto selection */ + int m_dustsize; /* dustsize for auto selection */ + + bool m_bgIsWhite; /* indicates if a scan without paper + * results in black or white */ + QSlider *m_sliderThresh; + QSlider *m_sliderDust; + QCheckBox *m_cbAutoSel; + QComboBox *m_cbBackground; + QGroupBox *m_autoSelGroup; + KScanDevice *m_scanner; + + QMemArray<long> m_heightSum; + QMemArray<long> m_widthSum; +}; + +Previewer::Previewer(QWidget *parent, const char *name ) + : QWidget(parent,name) +{ + d = new PreviewerPrivate(); + + // beautification to look like the left scanparams widget in the dialog + QHBoxLayout *htop = new QHBoxLayout( this ); + QFrame *frame = new QFrame( this ); + frame->setFrameStyle( QFrame::Panel | QFrame::Raised ); + frame->setLineWidth( 1 ); + htop->addWidget( frame ); + + QVBoxLayout *top = new QVBoxLayout( frame, KDialog::marginHint(), KDialog::spacingHint() ); + layout = new QHBoxLayout( KDialog::spacingHint() ); + top->addLayout( layout, 9 ); + QVBoxLayout *left = new QVBoxLayout( KDialog::spacingHint() ); + layout->addLayout( left, 2 ); + + /* Load autoselection values from Config file */ + KConfig *cfg = KGlobal::config(); + cfg->setGroup( GROUP_STARTUP ); + + /* Units etc. TODO: get from Config */ + sizeUnit = KRuler::Millimetres; + displayUnit = sizeUnit; + d->m_autoSelThresh = 240; + + overallHeight = 295; /* Default DIN A4 */ + overallWidth = 210; + kdDebug(29000) << "Previewer: got Overallsize: " << + overallWidth << " x " << overallHeight << endl; + img_canvas = new ImageCanvas( frame ); + + img_canvas->setDefaultScaleKind( ImageCanvas::DYNAMIC ); + img_canvas->enableContextMenu(true); + img_canvas->repaint(); + layout->addWidget( img_canvas, 6 ); + + /* Actions for the previewer zoom */ + KAction *act; + act = new KAction(i18n("Scale to W&idth"), "scaletowidth", CTRL+Key_I, + this, SLOT( slScaleToWidth()), this, "preview_scaletowidth" ); + act->plug( img_canvas->contextMenu()); + + act = new KAction(i18n("Scale to &Height"), "scaletoheight", CTRL+Key_H, + this, SLOT( slScaleToHeight()), this, "preview_scaletoheight" ); + act->plug( img_canvas->contextMenu()); + + /*Signals: Control the custom-field and show size of selection */ + connect( img_canvas, SIGNAL(newRect()), this, SLOT(slCustomChange())); + connect( img_canvas, SIGNAL(newRect(QRect)), this, SLOT(slNewDimen(QRect))); + + /* Stuff for the preview-Notification */ + left->addWidget( new QLabel( i18n("<B>Preview</B>"), frame ), 1); + + // Create a button group to contain buttons for Portrait/Landscape + bgroup = new QVButtonGroup( i18n("Scan Size"), frame ); + + // ----- + pre_format_combo = new QComboBox( frame, "PREVIEWFORMATCOMBO" ); + pre_format_combo->insertItem( i18n( "Custom" ), ID_CUSTOM); + pre_format_combo->insertItem( i18n( "DIN A4" ), ID_A4); + pre_format_combo->insertItem( i18n( "DIN A5" ), ID_A5); + pre_format_combo->insertItem( i18n( "DIN A6" ), ID_A6); + pre_format_combo->insertItem( i18n( "9x13 cm" ), ID_9_13 ); + pre_format_combo->insertItem( i18n( "10x15 cm" ), ID_10_15 ); + pre_format_combo->insertItem( i18n( "Letter" ), ID_LETTER); + + connect( pre_format_combo, SIGNAL(activated (int)), + this, SLOT( slFormatChange(int))); + + left->addWidget( pre_format_combo, 1 ); + + /** Potrait- and Landscape Selector **/ + QFontMetrics fm = bgroup->fontMetrics(); + int w = fm.width( (const QString)i18n(" Landscape " ) ); + int h = fm.height( ); + + rb1 = new QRadioButton( i18n("&Landscape"), bgroup ); + landscape_id = bgroup->id( rb1 ); + rb2 = new QRadioButton( i18n("P&ortrait"), bgroup ); + portrait_id = bgroup->id( rb2 ); + bgroup->setButton( portrait_id ); + + connect(bgroup, SIGNAL(clicked(int)), this, SLOT(slOrientChange(int))); + + int rblen = 5+w+12; // 12 for the button? + rb1->setGeometry( 5, 6, rblen, h ); + rb2->setGeometry( 5, 1+h/2+h, rblen, h ); + + left->addWidget( bgroup, 2 ); + + + /** Autoselection Box **/ + d->m_autoSelGroup = new QGroupBox( 1, Horizontal, i18n("Auto-Selection"), frame); + + QHBox *hbox = new QHBox(d->m_autoSelGroup); + d->m_cbAutoSel = new QCheckBox( i18n("Active on"), hbox ); + QToolTip::add( d->m_cbAutoSel, i18n("Check here if you want autodetection\n" + "of the document on the preview.")); + + /* combobox to select if black or white background */ + d->m_cbBackground = new QComboBox( hbox ); + d->m_cbBackground->insertItem(i18n("Black"), BG_ITEM_BLACK ); + d->m_cbBackground->insertItem(i18n("White"), BG_ITEM_WHITE ); + connect( d->m_cbBackground, SIGNAL(activated(int) ), + this, SLOT( slScanBackgroundChanged( int ))); + + + QToolTip::add( d->m_cbBackground, + i18n("Select whether a scan of the empty\n" + "scanner glass results in a\n" + "black or a white image.")); + connect( d->m_cbAutoSel, SIGNAL(toggled(bool) ), SLOT(slAutoSelToggled(bool))); + + (void) new QLabel( i18n("scanner background"), d->m_autoSelGroup ); + + QLabel *l1= new QLabel( i18n("Thresh&old:"), d->m_autoSelGroup ); + d->m_sliderThresh = new QSlider( 0, 254, 10, d->m_autoSelThresh, Qt::Horizontal, + d->m_autoSelGroup ); + connect( d->m_sliderThresh, SIGNAL(valueChanged(int)), SLOT(slSetAutoSelThresh(int))); + QToolTip::add( d->m_sliderThresh, + i18n("Threshold for autodetection.\n" + "All pixels higher (on black background)\n" + "or smaller (on white background)\n" + "than this are considered to be part of the image.")); + l1->setBuddy(d->m_sliderThresh); + +#if 0 /** Dustsize-Slider: No deep impact on result **/ + (void) new QLabel( i18n("Dust size:"), grBox ); + d->m_sliderDust = new QSlider( 0, 50, 5, d->m_dustsize, Qt::Horizontal, grBox ); + connect( d->m_sliderDust, SIGNAL(valueChanged(int)), SLOT(slSetAutoSelDustsize(int))); +#endif + + /* disable Autoselbox as long as no scanner is connected */ + d->m_autoSelGroup->setEnabled(false); + + left->addWidget(d->m_autoSelGroup); + + /* Labels for the dimension */ + QGroupBox *gbox = new QGroupBox( 1, Horizontal, i18n("Selection"), frame, "GROUPBOX" ); + + QLabel *l2 = new QLabel( i18n("width - mm" ), gbox ); + QLabel *l3 = new QLabel( i18n("height - mm" ), gbox ); + + connect( this, SIGNAL(setScanWidth(const QString&)), + l2, SLOT(setText(const QString&))); + connect( this, SIGNAL(setScanHeight(const QString&)), + l3, SLOT(setText(const QString&))); + + /* size indicator */ + QHBox *hb = new QHBox( gbox ); + (void) new QLabel( i18n( "Size:"), hb ); + SizeIndicator *indi = new SizeIndicator( hb ); + QToolTip::add( indi, i18n( "This size field shows how large the uncompressed image will be.\n" + "It tries to warn you, if you try to produce huge images by \n" + "changing its background color." )); + indi->setText( i18n("-") ); + + connect( this, SIGNAL( setSelectionSize(long)), + indi, SLOT( setSizeInByte (long)) ); + + left->addWidget( gbox, 1 ); + + left->addStretch( 6 ); + + top->activate(); + + /* Preset custom Cutting */ + pre_format_combo->setCurrentItem( ID_CUSTOM ); + slFormatChange( ID_CUSTOM); + + scanResX = -1; + scanResY = -1; + pix_per_byte = 1; + + selectionWidthMm = 0.0; + selectionHeightMm = 0.0; + recalcFileSize(); +} + +Previewer::~Previewer() +{ + delete d; +} + +bool Previewer::setPreviewImage( const QImage &image ) +{ + if ( image.isNull() ) + return false; + + m_previewImage = image; + img_canvas->newImage( &m_previewImage ); + + return true; +} + +QString Previewer::galleryRoot() +{ + QString dir = (KGlobal::dirs())->saveLocation( "data", "ScanImages", true ); + + if( !dir.endsWith("/") ) + dir += "/"; + + return( dir ); + +} + +void Previewer::newImage( QImage *ni ) +{ + /* image canvas does not copy the image, so we hold a copy here */ + m_previewImage = *ni; + + /* clear the auto detection arrays */ + d->m_heightSum.resize( 0 ); + d->m_widthSum.resize( 0 ); + + img_canvas->newImage( &m_previewImage ); + findSelection( ); +} + +void Previewer::setScanSize( int w, int h, KRuler::MetricStyle unit ) +{ + overallWidth = w; + overallHeight = h; + sizeUnit = unit; +} + + +void Previewer::slSetDisplayUnit( KRuler::MetricStyle unit ) +{ + displayUnit = unit; +} + + +void Previewer::slOrientChange( int id ) +{ + (void) id; + /* Gets either portrait or landscape-id */ + /* Just read the format-selection and call slFormatChange */ + slFormatChange( pre_format_combo->currentItem() ); +} + +/** Slot called whenever the format selection combo changes. **/ +void Previewer::slFormatChange( int id ) +{ + QPoint p(0,0); + bool lands_allowed; + bool portr_allowed; + bool setSelection = true; + int s_long = 0; + int s_short= 0; + + isCustom = false; + + switch( id ) + { + case ID_LETTER: + s_long = 294; + s_short = 210; + lands_allowed = false; + portr_allowed = true; + break; + case ID_CUSTOM: + lands_allowed = false; + portr_allowed = false; + setSelection = false; + isCustom = true; + break; + case ID_A4: + s_long = 297; + s_short = 210; + lands_allowed = false; + portr_allowed = true; + break; + case ID_A5: + s_long = 210; + s_short = 148; + lands_allowed = true; + portr_allowed = true; + break; + case ID_A6: + s_long = 148; + s_short = 105; + lands_allowed = true; + portr_allowed = true; + break; + case ID_9_13: + s_long = 130; + s_short = 90; + lands_allowed = true; + portr_allowed = true; + break; + case ID_10_15: + s_long = 150; + s_short = 100; + lands_allowed = true; + portr_allowed = true; + break; + default: + lands_allowed = true; + portr_allowed = true; + setSelection = false; + break; + } + + rb1->setEnabled( lands_allowed ); + rb2->setEnabled( portr_allowed ); + + int format_id = bgroup->id( bgroup->selected() ); + if( !lands_allowed && format_id == landscape_id ) + { + bgroup->setButton( portrait_id ); + format_id = portrait_id; + } + /* Convert the new dimension to a new QRect and call slot in canvas */ + if( setSelection ) + { + QRect newrect; + newrect.setRect( 0,0, p.y(), p.x() ); + + if( format_id == portrait_id ) + { /* Portrait Mode */ + p = calcPercent( s_short, s_long ); + kdDebug(29000) << "Now is portrait-mode" << endl; + } + else + { /* Landscape-Mode */ + p = calcPercent( s_long, s_short ); + } + + newrect.setWidth( p.x() ); + newrect.setHeight( p.y() ); + + img_canvas->newRectSlot( newrect ); + } +} + +/* This is called when the user fiddles around in the image. + * This makes the selection custom-sized immediately. + */ +void Previewer::slCustomChange( void ) +{ + if( isCustom )return; + pre_format_combo->setCurrentItem(ID_CUSTOM); + slFormatChange( ID_CUSTOM ); +} + + +void Previewer::slNewScanResolutions( int x, int y ) +{ + kdDebug(29000) << "got new Scan Resolutions: " << x << "|" << y << endl; + scanResX = x; + scanResY = y; + + recalcFileSize(); +} + + +/* This slot is called with the new dimension for the selection + * in values between 0..1000. It emits signals, that redraw the + * size labels. + */ +void Previewer::slNewDimen(QRect r) +{ + if( r.height() > 0) + selectionWidthMm = (overallWidth / 1000 * r.width()); + if( r.width() > 0) + selectionHeightMm = (overallHeight / 1000 * r.height()); + + QString s; + s = i18n("width %1 mm").arg( int(selectionWidthMm)); + emit(setScanWidth(s)); + + kdDebug(29000) << "Setting new Dimension " << s << endl; + s = i18n("height %1 mm").arg(int(selectionHeightMm)); + emit(setScanHeight(s)); + + recalcFileSize( ); + +} + +void Previewer::recalcFileSize( void ) +{ + /* Calculate file size */ + long size_in_byte = 0; + if( scanResY > -1 && scanResX > -1 ) + { + double w_inch = ((double) selectionWidthMm) / 25.4; + double h_inch = ((double) selectionHeightMm) / 25.4; + + int pix_w = int( w_inch * double( scanResX )); + int pix_h = int( h_inch * double( scanResY )); + + size_in_byte = pix_w * pix_h / pix_per_byte; + } + + emit( setSelectionSize( size_in_byte )); +} + + +QPoint Previewer::calcPercent( int w_mm, int h_mm ) +{ + QPoint p(0,0); + if( overallWidth < 1.0 || overallHeight < 1.0 ) return( p ); + + if( sizeUnit == KRuler::Millimetres ) { + p.setX( static_cast<int>(1000.0*w_mm / overallWidth) ); + p.setY( static_cast<int>(1000.0*h_mm / overallHeight) ); + } else { + kdDebug(29000) << "ERROR: Only mm supported yet !" << endl; + } + return( p ); + +} + +void Previewer::slScaleToWidth() +{ + if( img_canvas ) + { + img_canvas->handle_popup( ImageCanvas::ID_FIT_WIDTH ); + } +} + +void Previewer::slScaleToHeight() +{ + if( img_canvas ) + { + img_canvas->handle_popup( ImageCanvas::ID_FIT_HEIGHT); + } +} + +void Previewer::slConnectScanner( KScanDevice *scan ) +{ + kdDebug(29000) << "Connecting scan device!" << endl; + d->m_scanner = scan; + + if( scan ) + { + /* Enable the by-default disabled autoselection group */ + d->m_autoSelGroup->setEnabled(true); + QString h; + + h = scan->getConfig( CFG_AUTOSEL_DO, QString("unknown") ); + if( h == QString("on") ) + d->m_cbAutoSel->setChecked(true); + else + d->m_cbAutoSel->setChecked(false); + + QString isWhite = d->m_scanner->getConfig( CFG_SCANNER_EMPTY_BG, "unknown" ); + + h = scan->getConfig( CFG_AUTOSEL_DUSTSIZE, QString("5") ); + d->m_dustsize = h.toInt(); + + QString thresh = DEF_THRESH_BLACK; /* for black */ + if( isWhite.lower() == "yes" ) + thresh = DEF_THRESH_WHITE; + + h = scan->getConfig( CFG_AUTOSEL_THRESH, thresh ); + d->m_sliderThresh->setValue( h.toInt() ); + } +} + +void Previewer::slSetScannerBgIsWhite( bool b ) +{ + d->m_bgIsWhite = b; + + if( d->m_scanner ) + { + if( b ) // The background _is_ white + { + d->m_cbBackground->setCurrentItem( BG_ITEM_WHITE ); + } + else + { + d->m_cbBackground->setCurrentItem( BG_ITEM_BLACK ); + } + + d->m_scanner->slStoreConfig( CFG_SCANNER_EMPTY_BG, b ? QString("Yes") : QString("No")); + } +} + +/** + * reads the scanner dependant config file through the m_scanner pointer. + * If a value for the scanner is not yet known, the function starts up a + * popup and asks the user. The result is stored. + */ +void Previewer::checkForScannerBg() +{ + if( d->m_scanner ) /* Is the scan device already known? */ + { + QString isWhite = d->m_scanner->getConfig( CFG_SCANNER_EMPTY_BG, "unknown" ); + bool goWhite = false; + if( isWhite == "unknown" ) + { + /* not yet known, should ask the user. */ + kdDebug(29000) << "Dont know the scanner background yet!" << endl; + + goWhite = ( KMessageBox::questionYesNo( this, + i18n("The autodetection of images on the preview depends on the background color of the preview image (Think of a preview of an empty scanner).\nPlease select whether the background of the preview image is black or white"), + i18n("Image Autodetection"), + i18n("White"), i18n("Black") ) == KMessageBox::Yes ); + kdDebug(29000) << "User said " << isWhite << endl; + + } + else + { + if( isWhite.lower() == "yes" ) + goWhite = true; + } + + /* remember value */ + slSetScannerBgIsWhite( goWhite ); + } +} + +void Previewer::slScanBackgroundChanged( int indx ) +{ + slSetScannerBgIsWhite( indx == BG_ITEM_WHITE ); +} + +void Previewer::slAutoSelToggled(bool isOn ) +{ + if( isOn ) + checkForScannerBg(); + + if( d->m_cbAutoSel ) + { + QRect r = img_canvas->sel(); + + kdDebug(29000) << "The rect is " << r.width() << " x " << r.height() << endl; + d->m_doAutoSelection = isOn; + + /* Store configuration */ + if( d->m_scanner ) + { + d->m_scanner->slStoreConfig( CFG_AUTOSEL_DO, + isOn ? "on" : "off" ); + } + + if( isOn && r.width() < 2 && r.height() < 2) /* There is no selection yet */ + { + /* if there is already an image, check, if the bg-color is set already */ + if( img_canvas->rootImage() ) + { + kdDebug(29000) << "No selection -> try to find one!" << endl; + + findSelection(); + } + + } + } + if( d->m_sliderThresh ) + d->m_sliderThresh->setEnabled(isOn); + if( d->m_sliderDust ) + d->m_sliderDust->setEnabled(isOn); + if( d->m_cbBackground ) + d->m_cbBackground->setEnabled(isOn); + +} + + +void Previewer::slSetAutoSelThresh(int t) +{ + d->m_autoSelThresh = t; + kdDebug(29000) << "Setting threshold to " << t << endl; + if( d->m_scanner ) + d->m_scanner->slStoreConfig( CFG_AUTOSEL_THRESH, QString::number(t) ); + findSelection(); +} + +void Previewer::slSetAutoSelDustsize(int dSize) +{ + d->m_dustsize = dSize; + kdDebug(29000) << "Setting dustsize to " << dSize << endl; + findSelection(); +} + +/** + * This method tries to find a selection on the preview image automatically. + * It uses the image of the preview image canvas, the previewer global + * threshold setting and a dustsize. + **/ +void Previewer::findSelection( ) +{ + kdDebug(29000) << "Searching Selection" << endl; + + kdDebug(29000) << "Threshold: " << d->m_autoSelThresh << endl; + kdDebug(29000) << "dustsize: " << d->m_dustsize << endl; + kdDebug(29000) << "isWhite: " << d->m_bgIsWhite << endl; + + + if( ! d->m_doAutoSelection ) return; + int line; + int x; + const QImage *img = img_canvas->rootImage(); + if( ! img ) return; + + long iWidth = img->width(); + long iHeight = img->height(); + + QMemArray<long> heightSum; + QMemArray<long> widthSum; + + kdDebug(29000)<< "Preview size is " << iWidth << "x" << iHeight << endl; + + if( (d->m_heightSum).size() == 0 && (iHeight>0) ) + { + kdDebug(29000) << "Starting to fill Array " << endl; + QMemArray<long> heightSum(iHeight); + QMemArray<long> widthSum(iWidth); + heightSum.fill(0); + widthSum.fill(0); + + kdDebug(29000) << "filled Array with zero " << endl; + + for( line = 0; line < iHeight; line++ ) + { + + for( x = 0; x < iWidth; x++ ) + { + int gray = qGray( img->pixel( x, line )); + // kdDebug(29000) << "Gray-Value at line " << gray << endl; + Q_ASSERT( line < iHeight ); + Q_ASSERT( x < iWidth ); + int hsum = heightSum.at(line); + int wsum = widthSum.at(x); + + heightSum[line] = hsum+gray; + widthSum [x] = wsum+gray; + } + heightSum[line] = heightSum[line]/iWidth; + } + /* Divide by amount of pixels */ + kdDebug(29000) << "Resizing now" << endl; + for( x = 0; x < iWidth; x++ ) + widthSum[x] = widthSum[x]/iHeight; + + kdDebug(29000) << "Filled Arrays successfully" << endl; + d->m_widthSum = widthSum; + d->m_heightSum = heightSum; + } + /* Now try to find values in arrays that have grayAdds higher or lower + * than threshold */ +#if 0 + /* debug output */ + { + QFile fi( "/tmp/thheight.dat"); + if( fi.open( IO_ReadWrite ) ) { + QTextStream str( &fi ); + + str << "# height ##################" << endl; + for( x = 0; x < iHeight; x++ ) + str << x << '\t' << d->m_heightSum[x] << endl; + fi.close(); + } + } + QFile fi1( "/tmp/thwidth.dat"); + if( fi1.open( IO_ReadWrite )) + { + QTextStream str( &fi1 ); + str << "# width ##################" << endl; + str << "# " << iWidth << " points" << endl; + for( x = 0; x < iWidth; x++ ) + str << x << '\t' << d->m_widthSum[x] << endl; + + fi1.close(); + } +#endif + int start = 0; + int end = 0; + QRect r; + + /** scale to 0..1000 range **/ + start = 0; + end = 0; + imagePiece( d->m_heightSum, start, end ); // , d->m_threshold, d->m_dustsize, false ); + + r.setTop( 1000*start/iHeight ); + r.setBottom( 1000*end/iHeight); + // r.setTop( start ); + // r.setBottom( end ); + + start = 0; + end = 0; + imagePiece( d->m_widthSum, start, end ); // , d->m_threshold, d->m_dustsize, false ); + r.setLeft( 1000*start/iWidth ); + r.setRight( 1000*end/iWidth ); + // r.setLeft( start ); + // r.setRight( end ); + + kdDebug(29000) << " -- Autodetection -- " << endl; + kdDebug(29000) << "Area top " << r.top() << endl; + kdDebug(29000) << "Area left" << r.left() << endl; + kdDebug(29000) << "Area bottom " << r.bottom() << endl; + kdDebug(29000) << "Area right " << r.right() << endl; + kdDebug(29000) << "Area width " << r.width() << endl; + kdDebug(29000) << "Area height " << r.height() << endl; + + img_canvas->newRectSlot( r ); + slCustomChange(); +} + + +/* + * returns an Array containing the + */ +bool Previewer::imagePiece( QMemArray<long> src, int& start, int& end ) +{ + for( uint x = 0; x < src.size(); x++ ) + { + if( !d->m_bgIsWhite ) + { + /* pixelvalue needs to be higher than threshold, white background */ + if( src[x] > d->m_autoSelThresh ) + { + /* Ok this pixel could be the start */ + int iStart = x; + int iEnd = x; + x++; + while( x < src.size() && src[x] > d->m_autoSelThresh ) + { + x++; + } + iEnd = x; + + int delta = iEnd-iStart; + + if( delta > d->m_dustsize && end-start < delta ) + { + start = iStart; + end = iEnd; + } + } + } + else + { + /* pixelvalue needs to be lower than threshold, black background */ + if( src[x] < d->m_autoSelThresh ) + { + int iStart = x; + int iEnd = x; + x++; + while( x < src.size() && src[x] < d->m_autoSelThresh ) + { + x++; + } + iEnd = x; + + int delta = iEnd-iStart; + + if( delta > d->m_dustsize && end-start < delta ) + { + start = iStart; + end = iEnd; + } + } + } + } + return (end-start)>0; +} + +#include "previewer.moc" diff --git a/libkscan/previewer.h b/libkscan/previewer.h new file mode 100644 index 00000000..b2f993a0 --- /dev/null +++ b/libkscan/previewer.h @@ -0,0 +1,120 @@ +/* This file is part of the KDE Project + Copyright (C) 2000 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef PREVIEWER_H +#define PREVIEWER_H + +#include <qwidget.h> +#include <qimage.h> +#include <qrect.h> +#include <qbuttongroup.h> +#include <qpoint.h> + +#include <kruler.h> +#include <qmemarray.h> + +/** + *@author Klaas Freitag + */ +class ImageCanvas; +class QCheckBox; +class QSlider; +class KScanDevice; +class QComboBox; +class QRadioButton; +class QHBoxLayout; + +class Previewer : public QWidget +{ + Q_OBJECT +public: + Previewer(QWidget *parent=0, const char *name=0); + ~Previewer(); + + ImageCanvas *getImageCanvas( void ){ return( img_canvas ); } + + /** + * Static function that returns the image gallery base dir. + */ + static QString galleryRoot(); + bool setPreviewImage( const QImage &image ); + void findSelection(); + +public slots: + void newImage( QImage* ); + void slFormatChange( int id ); + void slOrientChange(int); + void slSetDisplayUnit( KRuler::MetricStyle unit ); + void setScanSize( int w, int h, KRuler::MetricStyle unit ); + void slCustomChange( void ); + void slNewDimen(QRect r); + void slNewScanResolutions( int, int ); + void recalcFileSize( void ); + void slSetAutoSelThresh(int); + void slSetAutoSelDustsize(int); + void slSetScannerBgIsWhite(bool b); + void slConnectScanner( KScanDevice *scan ); +protected slots: + void slScaleToWidth(); + void slScaleToHeight(); + void slAutoSelToggled(bool); + void slScanBackgroundChanged(int); + +signals: + void newRect( QRect ); + void noRect( void ); + void setScanWidth(const QString&); + void setScanHeight(const QString&); + void setSelectionSize( long ); + +private: + void checkForScannerBg(); + + QPoint calcPercent( int, int ); + + QHBoxLayout *layout; + ImageCanvas *img_canvas; + QComboBox *pre_format_combo; + QMemArray<QCString> format_ids; + QButtonGroup * bgroup; + QRadioButton * rb1; + QRadioButton * rb2; + QImage m_previewImage; + + bool imagePiece( QMemArray<long> src, + int& start, + int& end ); + + int landscape_id, portrait_id; + + double overallWidth, overallHeight; + KRuler::MetricStyle sizeUnit; + KRuler::MetricStyle displayUnit; + bool isCustom; + + int scanResX, scanResY; + int pix_per_byte; + double selectionWidthMm; + double selectionHeightMm; + + class PreviewerPrivate; + PreviewerPrivate *d; +}; + +#endif diff --git a/libkscan/scandialog.cpp b/libkscan/scandialog.cpp new file mode 100644 index 00000000..8a6689fa --- /dev/null +++ b/libkscan/scandialog.cpp @@ -0,0 +1,380 @@ +/* This file is part of the KDE Project + Copyright (C) 2001 Nikolas Zimmermann <wildfox@kde.org> + Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qlabel.h> +#include <qlayout.h> +#include <qstringlist.h> +#include <qstrlist.h> +#include <qtooltip.h> +#include <qsizepolicy.h> +#include <qapplication.h> +#include <qcheckbox.h> + +#include <kglobal.h> +#include <klocale.h> +#include <kdebug.h> +#include <kconfig.h> +#include <ksimpleconfig.h> +#include <kled.h> +#include <kglobalsettings.h> +#include <kscandevice.h> + +// libkscan stuff +#include "scanparams.h" +#include "devselector.h" +#include "img_canvas.h" +#include "previewer.h" +#include "scandialog.h" + +#define SCANDIA_SPLITTER_SIZES "ScanDialogSplitter %1" + +extern "C" { + void * init_libkscan() { + return new ScanDialogFactory; + } +} + +ScanDialogFactory::ScanDialogFactory( QObject *parent, const char *name ) + : KScanDialogFactory( parent, name ) +{ + setName( "ScanDialogFactory" ); + KGlobal::locale()->insertCatalogue( QString::fromLatin1("libkscan") ); +} + +KScanDialog * ScanDialogFactory::createDialog( QWidget *parent, + const char *name, bool modal) +{ + return new ScanDialog( parent, name, modal ); +} + + +/////////////////////////////////////////////////////////////////// + + +ScanDialog::ScanDialog( QWidget *parent, const char *name, bool modal ) + : KScanDialog( Tabbed, Close|Help, parent, name, modal ), + good_scan_connect(false) +{ + QVBox *page = addVBoxPage( i18n("&Scanning") ); + + splitter = new QSplitter( Horizontal, page, "splitter" ); + Q_CHECK_PTR( splitter ); + + m_scanParams = 0; + m_device = new KScanDevice( this ); + connect(m_device, SIGNAL(sigNewImage(QImage *, ImgScanInfo*)), + this, SLOT(slotFinalImage(QImage *, ImgScanInfo *))); + + connect( m_device, SIGNAL(sigScanStart()), this, SLOT(slotScanStart())); + connect( m_device, SIGNAL(sigScanFinished(KScanStat)), + this, SLOT(slotScanFinished(KScanStat))); + connect( m_device, SIGNAL(sigAcquireStart()), this, SLOT(slotAcquireStart())); + /* Create a preview widget to the right side of the splitter */ + m_previewer = new Previewer( splitter ); + Q_CHECK_PTR(m_previewer ); + + /* ... and connect to the selector-slots. They communicate user's + * selection to the scanner parameter engine */ + /* a new preview signal */ + connect( m_device, SIGNAL( sigNewPreview( QImage*, ImgScanInfo* )), + this, SLOT( slotNewPreview( QImage* ))); + + m_previewer->setEnabled( false ); // will be enabled in setup() + + /* Options-page */ + createOptionsTab( ); + +} + + +void ScanDialog::createOptionsTab( void ) +{ + + QVBox *page = addVBoxPage( i18n("&Options")); + setMainWidget(page); + + QGroupBox *gb = new QGroupBox( 1, Qt::Horizontal, i18n("Startup Options"), page, "GB_STARTUP" ); + QLabel *label = new QLabel( i18n( "Note: changing these options will affect the scan plugin on next start." ), + gb ); + label->setSizePolicy( QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed ) ); + + /* Checkbox for asking for scanner on startup */ + cb_askOnStart = new QCheckBox( i18n( "&Ask for the scan device on plugin startup"), gb ); + QToolTip::add( cb_askOnStart, + i18n("You can uncheck this if you do not want to be asked which scanner to use on startup.")); + Q_CHECK_PTR( cb_askOnStart ); + + /* Checkbox for network access */ + cb_network = new QCheckBox( i18n( "&Query the network for scan devices"), gb ); + QToolTip::add( cb_network, + i18n("Check this if you want to query for configured network scan stations.")); + Q_CHECK_PTR( cb_network ); + + + /* Read settings for startup behavior */ + KConfig *gcfg = KGlobal::config(); + gcfg->setGroup(QString::fromLatin1(GROUP_STARTUP)); + bool skipDialog = gcfg->readBoolEntry( STARTUP_SKIP_ASK, false ); + bool onlyLocal = gcfg->readBoolEntry( STARTUP_ONLY_LOCAL, false ); + + /* Note: flag must be inverted because of question is 'the other way round' */ + cb_askOnStart->setChecked( !skipDialog ); + connect( cb_askOnStart, SIGNAL(toggled(bool)), this, SLOT(slotAskOnStartToggle(bool))); + + cb_network->setChecked( !onlyLocal ); + connect( cb_network, SIGNAL(toggled(bool)), this, SLOT(slotNetworkToggle(bool))); + + + QWidget *spaceEater = new QWidget( page ); + Q_CHECK_PTR( spaceEater ); + spaceEater->setSizePolicy( QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding) ); + +} + +void ScanDialog::slotNetworkToggle( bool state) +{ + bool writestate = !state; + + kdDebug(29000) << "slotNetworkToggle: Writing state " << writestate << endl; + KConfig *c = KGlobal::config(); + c->setGroup(QString::fromLatin1(GROUP_STARTUP)); + c->writeEntry( STARTUP_ONLY_LOCAL, writestate, true, true ); +} + +void ScanDialog::slotAskOnStartToggle(bool state) +{ + bool writestate = !state; + + kdDebug(29000) << "slotAskOnStartToggle: Writing state " << writestate << endl; + KConfig *c = KGlobal::config(); + c->setGroup(QString::fromLatin1(GROUP_STARTUP)); + c->writeEntry( STARTUP_SKIP_ASK, writestate, true, true ); +} + +void ScanDialog::slotScanStart( ) +{ + if( m_scanParams ) + { + m_scanParams->setEnabled( false ); + KLed *led = m_scanParams->operationLED(); + if( led ) + { + led->setColor( Qt::red ); + led->setState( KLed::On ); + } + + } +} + +void ScanDialog::slotAcquireStart( ) +{ + if( m_scanParams ) + { + KLed *led = m_scanParams->operationLED(); + if( led ) + { + led->setColor( Qt::green ); + } + + } +} + +void ScanDialog::slotScanFinished( KScanStat status ) +{ + kdDebug(29000) << "Scan finished with status " << status << endl; + if( m_scanParams ) + { + m_scanParams->setEnabled( true ); + KLed *led = m_scanParams->operationLED(); + if( led ) + { + led->setColor( Qt::green ); + led->setState( KLed::Off ); + } + + } +} + +bool ScanDialog::setup() +{ + if( ! m_device ) + { + good_scan_connect = false; + return(false); + } + // The scan device is now closed on closing the scan dialog. That means + // that more work to open it needs to be done in the setup slot like opening + // the selector if necessary etc. + + if( m_scanParams ) + { + /* if m_scanParams exist it means, that the dialog is already open */ + return true; + } + + m_scanParams = new ScanParams( splitter ); + connect( m_previewer->getImageCanvas(), SIGNAL( newRect(QRect)), + m_scanParams, SLOT(slCustomScanSize(QRect))); + connect( m_previewer->getImageCanvas(), SIGNAL( noRect()), + m_scanParams, SLOT(slMaximalScanSize())); + + connect( m_scanParams, SIGNAL( scanResolutionChanged( int, int )), + m_previewer, SLOT( slNewScanResolutions( int, int ))); + + + /* continue to attach a real scanner */ + /* first, get the list of available devices from libkscan */ + QStringList scannerNames; + QStrList backends = m_device->getDevices();; + QStrListIterator it( backends ); + + while ( it.current() ) { + scannerNames.append( m_device->getScannerName( it.current() )); + ++it; + } + + /* ..if there are devices.. */ + QCString configDevice; + good_scan_connect = true; + if( scannerNames.count() > 0 ) + { + /* allow the user to select one */ + DeviceSelector ds( this, backends, scannerNames ); + configDevice = ds.getDeviceFromConfig( ); + + if( configDevice.isEmpty() || configDevice.isNull() ) + { + kdDebug(29000) << "configDevice is not valid - starting selector!" << configDevice << endl; + if ( ds.exec() == QDialog::Accepted ) + { + configDevice = ds.getSelectedDevice(); + } + } + + /* If a device was selected, */ + if( ! configDevice.isNull() ) + { + /* ..open it (init sane with that) */ + m_device->openDevice( configDevice ); + + /* ..and connect to the gui (create the gui) */ + if ( !m_scanParams->connectDevice( m_device ) ) + { + kdDebug(29000) << "ERR: Could not connect scan device" << endl; + good_scan_connect = false; + } + } + } + + if( configDevice.isNull() || configDevice.isEmpty() ) + { + /* No scanner found, open with information */ + m_scanParams->connectDevice( 0L ); + good_scan_connect = false; + /* Making the previewer gray */ + /* disabling is much better than hiding for 'advertising' ;) */ + } + + /* Move the scan params to the left, for backward compatibility. + * However, having it on the right looks a bit better IMHO */ + if( splitter && m_scanParams ) + splitter->moveToFirst( m_scanParams ); + + if( good_scan_connect ) + { + m_previewer->setEnabled( true ); + m_previewer->setPreviewImage( m_device->loadPreviewImage() ); + m_previewer->slConnectScanner( m_device ); + } + + /* set initial sizes */ + setInitialSize( configDialogSize( GROUP_STARTUP )); + + KConfig *kfg = KGlobal::config(); + if( kfg ) + { + QRect r = KGlobalSettings::desktopGeometry(this); + + kfg->setGroup( GROUP_STARTUP ); + /* Since this is a vertical splitter, only the width is important */ + QString key = QString::fromLatin1( SCANDIA_SPLITTER_SIZES ).arg( r.width()); + kdDebug(29000) << "Read Splitter-Sizes " << key << endl; + splitter->setSizes( kfg->readIntListEntry( key )); + } + + return true; +} + +void ScanDialog::slotClose() +{ + /* Save the dialog start size to global configuration */ + saveDialogSize( GROUP_STARTUP, true ); + + if( splitter ) + { + KConfig *kfg = KGlobal::config(); + if( kfg ) + { + QRect r = KGlobalSettings::desktopGeometry(this); + + kfg->setGroup( GROUP_STARTUP ); + /* Since this is a vertical splitter, only the width is important */ + QString key = QString::fromLatin1( SCANDIA_SPLITTER_SIZES ).arg( r.width()); + kfg->writeEntry( key, splitter->sizes(), true, true); + } + } + + if( m_scanParams ) + { + delete m_scanParams; + m_scanParams =0; + } + if( m_device ) + m_device->slCloseDevice(); + else + kdDebug(29000) << "ERR: no device exists :(" << endl; + // bullshit happend + accept(); +} + +void ScanDialog::slotNewPreview( QImage *image ) +{ + if( image ) + { + m_previewImage = *image; + // hmmm - dont know, if conversion of the bit-depth is necessary. + // m_previewImage.convertDepth(32); + + /* The previewer does not copy the image data ! */ + m_previewer->newImage( &m_previewImage ); + } + +} + +ScanDialog::~ScanDialog() +{ +} + +void ScanDialog::slotFinalImage(QImage *image, ImgScanInfo *) +{ + emit finalImage(*image, nextId()); +} + +#include "scandialog.moc" diff --git a/libkscan/scandialog.h b/libkscan/scandialog.h new file mode 100644 index 00000000..02aa48b1 --- /dev/null +++ b/libkscan/scandialog.h @@ -0,0 +1,83 @@ +/* This file is part of the KDE Project + Copyright (C)2001 Nikolas Zimmermann <wildfox@kde.org> + Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef SCAN_H +#define SCAN_H + +#include <qimage.h> +#include <kscan.h> + +class ScanParams; +class KScanDevice; +class Previewer; +class QSplitter; + +class ScanDialog : public KScanDialog +{ + Q_OBJECT + +public: + ScanDialog( QWidget *parent=0, const char *name=0, bool modal=false ); + ~ScanDialog(); + + virtual bool setup(); + +private: + void createOptionsTab( void ); + +protected slots: + void slotFinalImage( QImage *, ImgScanInfo * ); + void slotNewPreview( QImage * ); + void slotScanStart( ); + void slotScanFinished( KScanStat status ); + void slotAcquireStart(); + +private slots: + void slotAskOnStartToggle(bool state); + void slotNetworkToggle( bool state); + + + void slotClose(); +private: + ScanParams *m_scanParams; + KScanDevice *m_device; + Previewer *m_previewer; + QImage m_previewImage; + bool good_scan_connect; + QCheckBox *cb_askOnStart; + QCheckBox *cb_network; + QSplitter *splitter; + class ScanDialogPrivate; + ScanDialogPrivate *d; +}; + +class ScanDialogFactory : public KScanDialogFactory +{ +public: + ScanDialogFactory( QObject *parent=0, const char *name=0 ); + +protected: + virtual KScanDialog * createDialog( QWidget *parent=0, const char *name=0, + bool modal=false ); + + +}; + +#endif // SCAN_H diff --git a/libkscan/scanparams.cpp b/libkscan/scanparams.cpp new file mode 100644 index 00000000..d886d113 --- /dev/null +++ b/libkscan/scanparams.cpp @@ -0,0 +1,1140 @@ +/* This file is part of the KDE Project + Copyright (C) 1999 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <sane/saneopts.h> +#include <qcstring.h> +#include <qfile.h> +#include <qframe.h> +#include <qlabel.h> +#include <qpushbutton.h> +#include <qcombobox.h> +#include <qimage.h> +#include <qtooltip.h> +#include <qmessagebox.h> +#include <qlayout.h> +#include <qdict.h> +#include <qprogressdialog.h> +#include <qscrollview.h> +#include <qsizepolicy.h> +#include <qcheckbox.h> +#include <qbuttongroup.h> + + +#include <kfiledialog.h> +#include <klocale.h> +#include <kdebug.h> +#include <kbuttonbox.h> +#include <kiconloader.h> +#include <kled.h> +#include <kseparator.h> + +#include "scanparams.h" +#include <scansourcedialog.h> +#include "massscandialog.h" +#include <gammadialog.h> +#include "kscanslider.h" + + + + +ScanParams::ScanParams( QWidget *parent, const char *name ) + : QVBox( parent, name ), + m_firstGTEdit( true ) +{ + /* first some initialisation and debug messages */ + sane_device = 0; virt_filename = 0; + pb_edit_gtable = 0; + cb_gray_preview = 0; + pb_source_sel = 0; + bg_virt_scan_mode = 0; + xy_resolution_bind = 0; + progressDialog = 0; + + /* Preload icons */ + pixMiniFloppy = SmallIcon( "3floppy_unmount" ); + + pixColor = SmallIcon( "palette_color" ); + pixGray = SmallIcon( "palette_gray" ); + pixLineArt = SmallIcon( "palette_lineart" ); + pixHalftone = SmallIcon( "palette_halftone" ); + + /* intialise the default last save warnings */ + startupOptset = 0; + +} + +bool ScanParams::connectDevice( KScanDevice *newScanDevice ) +{ + setMargin( KDialog::marginHint() ); + setSpacing( KDialog::spacingHint() ); + /* Debug: dump common Options */ + + if( ! newScanDevice ) + { + kdDebug(29000) << "No scan device found !" << endl; + sane_device = 0L; + createNoScannerMsg(); + return( true ); + } + sane_device = newScanDevice; + + QStrList strl = sane_device->getCommonOptions(); + QString emp; + for ( emp=strl.first(); strl.current(); emp=strl.next() ) + kdDebug(29000) << "Common: " << strl.current() << endl; + + /* Start path for virual scanner */ + last_virt_scan_path = QDir::home(); + adf = ADF_OFF; + + /* Frame stuff for toplevel of scanparams - beautification */ + setFrameStyle( QFrame::Panel | QFrame::Raised ); + setLineWidth( 1 ); + + + /* initialise own widgets */ + cb_gray_preview = 0; + + /* A top layout box */ + // QVBoxLayout *top = new QVBoxLayout(this, 6); + QHBox *hb = new QHBox( this ); + hb->setSpacing( KDialog::spacingHint() ); + QString cap = i18n("<B>Scanner Settings</B>") + " : "; + cap += sane_device->getScannerName(); + (void ) new QLabel( cap, hb ); + m_led = new KLed( hb ); + m_led->setState( KLed::Off ); + m_led->setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed )); + + + (void) new KSeparator( KSeparator::HLine, this); + + /* Now create Widgets for the important scan settings */ + QScrollView *sv = 0; + + if( sane_device->optionExists( SANE_NAME_FILE ) ) + { + /* Its a virtual scanner */ + scan_mode = ID_SANE_DEBUG; + virtualScannerParams( ); + + } else { + scan_mode = ID_SCAN; + + /* load the startup scanoptions */ + startupOptset = new KScanOptSet( DEFAULT_OPTIONSET ); + Q_CHECK_PTR( startupOptset ); + + if( !startupOptset->load( "Startup" ) ) + { + kdDebug(29000) << "Could not load Startup-Options" << endl; + delete startupOptset; + startupOptset = 0; + } + sv = scannerParams( ); + } + + /* Reload all options to care for inactive options */ + sane_device->slReloadAll(); + + /* Create a Start-Scan-Button */ + (void) new KSeparator( KSeparator::HLine, this); + KButtonBox *kbb = new KButtonBox( this ); + QPushButton* pb = kbb->addButton( KGuiItem( i18n( "Final S&can" ), "scanner" ) ); + connect( pb, SIGNAL(clicked()), this, SLOT(slStartScan()) ); + pb = kbb->addButton( i18n( "&Preview Scan" )); + connect( pb, SIGNAL(clicked()), this, SLOT(slAcquirePreview()) ); + kbb->layout(); + + /* Initialise the progress dialog */ + progressDialog = new QProgressDialog( i18n("Scanning in progress"), + i18n("Stop"), 100, 0L, + "SCAN_PROGRESS", true, 0 ); + progressDialog->setAutoClose( true ); + progressDialog->setAutoReset( true ); + + connect( sane_device, SIGNAL(sigScanProgress(int)), + progressDialog, SLOT(setProgress(int))); + + /* Connect the Progress Dialogs cancel-Button */ + connect( progressDialog, SIGNAL( cancelled() ), sane_device, + SLOT( slStopScanning() ) ); + + return( true ); +} + + +ScanParams::~ScanParams() +{ + if( startupOptset ) + { + delete startupOptset; + startupOptset = 0; + } + + if( progressDialog ) + { + delete( progressDialog ); + progressDialog = 0; + } +} + +void ScanParams::initialise( KScanOption *so ) +{ + if( ! so ) return; + bool initialised = false; + + if( startupOptset ) + { + QCString name = so->getName(); + if( ! name.isEmpty() ){ + QCString val = startupOptset->getValue( name ); + kdDebug( 29000) << "Initialising <" << name << "> with value <" << val << ">" << endl; + so->set( val ); + sane_device->apply(so); + initialised = true; + } + } + + if( ! initialised ) + { + + } +} + +QScrollView *ScanParams::scannerParams( ) +{ + KScanOption *so = 0; + + /* Its a real scanner */ + QScrollView *sv = new QScrollView( this ); + sv->setHScrollBarMode( QScrollView::AlwaysOff ); + sv->setResizePolicy( QScrollView::AutoOneFit ); + QVBox *pbox = new QVBox( sv->viewport()); + pbox->setSpacing( KDialog::spacingHint() ); + sv->setFrameStyle( QFrame::NoFrame ); + + QHBox *hb = new QHBox(pbox); + + /* Mode setting */ + so = sane_device->getGuiElement( SANE_NAME_SCAN_MODE, hb, + SANE_TITLE_SCAN_MODE, + SANE_DESC_SCAN_MODE ); + if ( so ) + { + /* Pretty-Pixmap */ + + KScanCombo *cb = (KScanCombo*) so->widget(); + Q_CHECK_PTR(cb); + // the following strings are not translatable since the sane library does not translate them. + // so, if we want icons, we have to keep them non-translated for now. + cb->slSetIcon( pixLineArt, "Line art" ); + cb->slSetIcon( pixLineArt, "Lineart" ); + cb->slSetIcon( pixLineArt, "Binary" ); + cb->slSetIcon( pixGray, "Gray" ); + cb->slSetIcon( pixGray, "Gray" ); + cb->slSetIcon( pixColor, "Color" ); + cb->slSetIcon( pixHalftone, "Halftone" ); + + // hb->setMargin( KDialog::marginHint() ); + // hb->setSpacing( KDialog::spacingHint()); + hb->setMargin( 0 ); /// KDialog::marginHint() ); + hb->setSpacing( KDialog::spacingHint()); + + hb->setStretchFactor( cb, 5 ); + + initialise( so ); + connect( so, SIGNAL(guiChange(KScanOption*)), + this, SLOT(slReloadAllGui( KScanOption* ))); + } + + /* Add a button for Source-Selection */ + if( sane_device->optionExists( SANE_NAME_SCAN_SOURCE )) + { + KScanOption source( SANE_NAME_SCAN_SOURCE ); + QStrList l = source.getList(); + + QWidget *spacer = new QWidget(hb); + hb->setStretchFactor( spacer, 1 ); + kdDebug(29000) << "Source list size: " << l.count() << endl; + + if( l.count() > 1 ) + { + pb_source_sel = new QPushButton( i18n("Source..."), hb ); + connect( pb_source_sel, SIGNAL(clicked()), this, SLOT(slSourceSelect())); + initialise( &source ); + hb->setStretchFactor( pb_source_sel, 3 ); + +#if 0 /* Testing !! TODO: remove */ + if( ! source.active() ) { + pb_source_sel->setEnabled( false ); + } +#endif + } + else + { + kdDebug(29000) << "only one scan-source, do not show button." << endl; + } + } + + /* Halftoning */ + if( sane_device->optionExists( SANE_NAME_HALFTONE ) ) + { + so = sane_device->getGuiElement( SANE_NAME_HALFTONE, pbox, + SANE_TITLE_HALFTONE, + SANE_DESC_HALFTONE ); + if( so ) + { + initialise(so); + connect( so, SIGNAL(guiChange(KScanOption*)), + this, SLOT(slReloadAllGui( KScanOption* ))); + } + } + + if( sane_device->optionExists( SANE_NAME_HALFTONE_DIMENSION) ) + { + kdDebug(29000) << "Halftone-Dimen exists" << endl; + so = sane_device->getGuiElement( SANE_NAME_HALFTONE_DIMENSION, pbox, + SANE_TITLE_HALFTONE_DIMENSION, + SANE_DESC_HALFTONE_DIMENSION ); + if( so ) + { + initialise(so); + connect( so, SIGNAL(guiChange(KScanOption*)), + this, SLOT(slReloadAllGui( KScanOption* ))); + } + } + + if( sane_device->optionExists( SANE_NAME_HALFTONE_PATTERN) ) + { + kdDebug(29000) << "Halftone-Pattern exists" << endl; + so = sane_device->getGuiElement( SANE_NAME_HALFTONE_PATTERN, pbox, + SANE_TITLE_HALFTONE_PATTERN, + SANE_DESC_HALFTONE_PATTERN ); + if( so ) + { + initialise(so); + connect( so, SIGNAL(guiChange(KScanOption*)), + this, SLOT(slReloadAllGui( KScanOption* ))); + } + } + + + /* Resolution Setting -> X-Resolution Setting */ + so = sane_device->getGuiElement( SANE_NAME_SCAN_X_RESOLUTION, pbox /* this */, + i18n("Resolution"), + SANE_DESC_SCAN_X_RESOLUTION ); + + if ( so ) + { + initialise( so ); + int x_y_res; + so->get( &x_y_res ); + so->slRedrawWidget( so ); + + /* connect to slot that passes the resolution to the previewer */ + connect( so, SIGNAL(guiChange(KScanOption*)), + this, SLOT( slNewXResolution(KScanOption*))); + + connect( so, SIGNAL(guiChange(KScanOption*)), + this, SLOT(slReloadAllGui( KScanOption* ))); + + xy_resolution_bind = + sane_device->getGuiElement(SANE_NAME_RESOLUTION_BIND, pbox, + SANE_TITLE_RESOLUTION_BIND, + SANE_DESC_RESOLUTION_BIND ); + if( xy_resolution_bind ) + { + initialise( xy_resolution_bind ); + xy_resolution_bind->slRedrawWidget( xy_resolution_bind ); + /* Connect to Gui-change-Slot */ + connect( xy_resolution_bind, SIGNAL(guiChange(KScanOption*)), + this, SLOT(slReloadAllGui( KScanOption* ))); + } + + /* Resolution Setting -> Y-Resolution Setting */ + so = sane_device->getGuiElement( SANE_NAME_SCAN_Y_RESOLUTION, pbox, + SANE_TITLE_SCAN_Y_RESOLUTION, + SANE_DESC_SCAN_Y_RESOLUTION ); + int y_res = x_y_res; + if ( so ) + { + initialise( so ); + if( so->active() ) + so->get( &y_res ); + so->slRedrawWidget( so ); + } + + emit( scanResolutionChanged( x_y_res, y_res )); + } + else + { + /* If the SCAN_X_RES does not exists, perhaps just SCAN_RES does */ + so = sane_device->getGuiElement( SANE_NAME_SCAN_RESOLUTION, pbox, + SANE_TITLE_SCAN_Y_RESOLUTION, + SANE_DESC_SCAN_X_RESOLUTION ); + if( so ) + { + initialise( so ); + } + else + { + kdDebug(29000) << "SERIOUS: No Resolution setting possible !" << endl; + } + } + + /* Insert another beautification line */ + (void) new KSeparator( KSeparator::HLine, pbox ); + + /* Speed-Setting - show only if active */ + if( sane_device->optionExists( SANE_NAME_SCAN_SPEED )) + { + KScanOption kso_speed( SANE_NAME_SCAN_SPEED ); + if( kso_speed.valid() && kso_speed.softwareSetable() && kso_speed.active()) + { + so = sane_device->getGuiElement( SANE_NAME_SCAN_SPEED, pbox, + SANE_TITLE_SCAN_SPEED, + SANE_DESC_SCAN_SPEED ); + initialise( so ); + } + } + + /* Threshold-Setting */ + so = sane_device->getGuiElement( SANE_NAME_THRESHOLD, pbox, + SANE_TITLE_THRESHOLD, + SANE_DESC_THRESHOLD); + if( so ) + { + initialise( so ); + } + + /* Brightness-Setting */ + so = sane_device->getGuiElement( SANE_NAME_BRIGHTNESS, pbox, + SANE_TITLE_BRIGHTNESS, + SANE_DESC_BRIGHTNESS); + if( so ) initialise( so ); + + /* Contrast-Setting */ + so = sane_device->getGuiElement( SANE_NAME_CONTRAST, pbox, + SANE_TITLE_CONTRAST, + SANE_DESC_CONTRAST ); + if( so ) initialise( so ); + /* Custom Gamma */ + + /* Sharpness */ + so = sane_device->getGuiElement( "sharpness", pbox ); + if( so ) initialise( so ); + + + /* The gamma table can be used - add a button for editing */ + QHBox *hb1 = new QHBox(pbox); + + if( sane_device->optionExists( SANE_NAME_CUSTOM_GAMMA ) ) + { + so = sane_device->getGuiElement( SANE_NAME_CUSTOM_GAMMA, hb1, + SANE_TITLE_CUSTOM_GAMMA, + SANE_DESC_CUSTOM_GAMMA ); + initialise( so ); + connect( so, SIGNAL(guiChange(KScanOption*)), + this, SLOT(slReloadAllGui( KScanOption* ))); + } + else + { + (void) new QLabel( i18n("Custom Gamma Table"), hb1 ); + } + + /* Connect a signal to refresh activity of the gamma tables */ + (void) new QWidget( hb1 ); /* dummy widget to eat space */ + + pb_edit_gtable = new QPushButton( i18n("Edit..."), hb1 ); + Q_CHECK_PTR(pb_edit_gtable); + + connect( pb_edit_gtable, SIGNAL( clicked () ), + this, SLOT( slEditCustGamma () ) ); + setEditCustomGammaTableState(); + + /* This connection cares for enabling/disabling the edit-Button */ + if(so ) + connect( so, SIGNAL(guiChange(KScanOption*)), + this, SLOT(slOptionNotify(KScanOption*))); + + /* my Epson Perfection backends offer a list of user defined gamma values */ + + /* Insert another beautification line */ + if( sane_device->optionExists( SANE_NAME_GRAY_PREVIEW ) || + sane_device->optionExists( SANE_NAME_NEGATIVE ) ) + { + (void) new KSeparator( KSeparator::HLine, pbox ); + } + + so = sane_device->getGuiElement( SANE_NAME_NEGATIVE, pbox, + SANE_TITLE_NEGATIVE, + SANE_DESC_NEGATIVE ); + initialise( so ); + + /* PREVIEW-Switch */ + kdDebug(29000) << "Try to get Gray-Preview" << endl; + if( sane_device->optionExists( SANE_NAME_GRAY_PREVIEW )) + { + so = sane_device->getGuiElement( SANE_NAME_GRAY_PREVIEW, pbox, + SANE_TITLE_GRAY_PREVIEW, + SANE_DESC_GRAY_PREVIEW ); + initialise( so ); + cb_gray_preview = (QCheckBox*) so->widget(); + QToolTip::add( cb_gray_preview, i18n("Acquire a gray preview even in color mode (faster)") ); + } + + QWidget *spacer = new QWidget( pbox ); + spacer->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); + + pbox->setMinimumWidth( pbox->sizeHint().width() ); + sv->setMinimumWidth( pbox->minimumWidth() ); + sv->addChild( pbox ); + + return( sv ); +} + + +void ScanParams::createNoScannerMsg( void ) +{ + /* Mode setting */ + QString cap; + cap = i18n( "<B>Problem: No Scanner was found</B><P>Your system does not provide a SANE <I>(Scanner Access Now Easy)</I> installation, which is required by the KDE scan support.<P>Please install and configure SANE correctly on your system.<P>Visit the SANE homepage under http://www.sane-project.org to find out more about SANE installation and configuration. " ); + + (void) new QLabel( cap, this ); + +} + +/* This slot will be called if something changes with the option given as a param. + * This is useful if the parameter - Gui has widgets in his own space, which depend + * on widget controlled by the KScanOption. + */ +void ScanParams::slOptionNotify( KScanOption *kso ) +{ + if( !kso || !kso->valid()) return; + setEditCustomGammaTableState (); +} + + +void ScanParams::slSourceSelect( void ) +{ + kdDebug(29000) << "Open Window for source selection !" << endl; + KScanOption so( SANE_NAME_SCAN_SOURCE ); + ADF_BEHAVE adf = ADF_OFF; + + const QCString& currSource = so.get(); + kdDebug(29000) << "Current Source is <" << currSource << ">" << endl; + QStrList sources; + + if( so.valid() ) + { + sources = so.getList(); +#undef CHEAT_FOR_DEBUGGING +#ifdef CHEAT_FOR_DEBUGGING + if( sources.find( "Automatic Document Feeder" ) == -1) + sources.append( "Automatic Document Feeder" ); +#endif + + ScanSourceDialog d( this, sources, adf ); + d.slSetSource( currSource ); + + if( d.exec() == QDialog::Accepted ) + { + QString sel_source = d.getText(); + adf = d.getAdfBehave(); + + /* set the selected Document source, the behavior is stored in a membervar */ + so.set( QCString(sel_source.latin1()) ); // FIX in ScanSourceDialog, then here + sane_device->apply( &so ); + + kdDebug(29000) << "Dialog finished OK: " << sel_source << ", " << adf << endl; + + } + } +} + +/** slFileSelect + * this slot allows to select a file or directory for virtuell Scanning. + * If a dir is selected, the virt. Scanner crawls for all readable + * images, if a single file is selected, the virt Scanner just reads + * one file. + * If SANE Debug Mode is selected, only pnm can be loaded. + * on Qt mode, all available Qt-Formats are ok. + */ +void ScanParams::slFileSelect( void ) +{ + kdDebug(29000) << "File Selector" << endl; + QString filter; + QCString prefix = "\n*."; + + if( scan_mode == ID_QT_IMGIO ) + { + QStrList filterList = QImage::inputFormats(); + filter = i18n( "*|All Files (*)"); + for( QCString fi_item = filterList.first(); !fi_item.isEmpty(); + fi_item = filterList.next() ) + { + + filter.append( QString::fromLatin1( prefix + fi_item.lower()) ); + } + } + else + { + filter.append( i18n( "*.pnm|PNM Image Files (*.pnm)") ); + } + + + + KFileDialog fd(last_virt_scan_path.path(), filter, this, "FileDialog",true); + fd.setCaption( i18n("Select Input File") ); + /* Read the filename and remind it */ + QString fileName; + if ( fd.exec() == QDialog::Accepted ) { + fileName = fd.selectedFile(); + QFileInfo ppath( fileName ); + last_virt_scan_path = QDir(ppath.dirPath(true)); + } else { + return; + } + + if ( !fileName.isNull() && virt_filename ) { // got a file name + kdDebug(29000) << "Got fileName: " << fileName << endl; + // write Value to SANEOption, it updates itself. + virt_filename->set( QFile::encodeName( fileName ) ); + } +} + +/** Slot which is called if the user switches in the gui between + * the SANE-Debug-Mode and the qt image reading + */ +void ScanParams::slVirtScanModeSelect( int id ) +{ + if( id == 0 ) { + scan_mode = ID_SANE_DEBUG; /* , ID_QT_IMGIO */ + sane_device->guiSetEnabled( "three-pass", true ); + sane_device->guiSetEnabled( "grayify", true ); + sane_device->guiSetEnabled( SANE_NAME_CONTRAST, true ); + sane_device->guiSetEnabled( SANE_NAME_BRIGHTNESS, true ); + + /* Check if the entered filename to delete */ + if( virt_filename ) { + QString vf = virt_filename->get(); + kdDebug(29000) << "Found File in Filename-Option: " << vf << endl; + + QFileInfo fi( vf ); + if( fi.extension() != QString::fromLatin1("pnm") ) + virt_filename->set(QCString("")); + } + } else { + scan_mode = ID_QT_IMGIO; + sane_device->guiSetEnabled( "three-pass", false ); + sane_device->guiSetEnabled( "grayify", false ); + sane_device->guiSetEnabled( SANE_NAME_CONTRAST, false ); + sane_device->guiSetEnabled( SANE_NAME_BRIGHTNESS, false ); + } +} + + +/** + * virtualScannerParams builds the GUI for the virtual scanner, + * which allows the user to read images from the harddisk. + */ +void ScanParams::virtualScannerParams( void ) +{ +#if 0 + KScanOption *so = 0; + QWidget *w = 0; + + /* Selection if virt. Scanner or SANE Debug */ + bg_virt_scan_mode = new QButtonGroup( 2, Qt::Horizontal, + this, "GroupBoxVirtScanner" ); + connect( bg_virt_scan_mode, SIGNAL(clicked(int)), + this, SLOT( slVirtScanModeSelect(int))); + + QRadioButton *rb1 = new QRadioButton( i18n("SANE debug (pnm only)"), + bg_virt_scan_mode, "VirtScanSANEDebug" ); + + + + QRadioButton *rb2 = new QRadioButton( i18n("virt. Scan (all Qt modes)"), + bg_virt_scan_mode, "VirtScanQtModes" ); + + rb1->setChecked( true ); + rb2->setChecked( false ); + + if( scan_mode == ID_QT_IMGIO ) + { + rb2->setChecked( true ); + rb1->setChecked( false ); + } + + top->addWidget( bg_virt_scan_mode, 1 ); + + /* GUI-Element Filename */ + virt_filename = sane_device->getGuiElement( SANE_NAME_FILE, this, + SANE_TITLE_FILE, + SANE_DESC_FILE ); + if( virt_filename ) + { + QHBoxLayout *hb = new QHBoxLayout(); + top->addLayout( hb ); + w = virt_filename->widget(); + w->setMinimumHeight( (w->sizeHint()).height()); + connect( w, SIGNAL(returnPressed()), this, + SLOT( slCheckGlob())); + + hb->addWidget( w, 12 ); + + QPushButton *pb_file_sel = new QPushButton( this ); + pb_file_sel->setPixmap(miniFloppyPixmap); + //hb->addStretch( 1 ); + hb->addWidget( pb_file_sel, 1 ); + connect( pb_file_sel, SIGNAL(clicked()), this, SLOT(slFileSelect())); + + } + + /* GUI-Element Brightness */ + so = sane_device->getGuiElement( SANE_NAME_BRIGHTNESS, this, + SANE_TITLE_BRIGHTNESS, + SANE_DESC_BRIGHTNESS ); + if( so ) + { + top->addWidget( so->widget(), 1 ); + so->set( 0 ); + sane_device->apply( so ); + } + + /* GUI-Element Contrast */ + so = sane_device->getGuiElement( SANE_NAME_CONTRAST, this, + SANE_TITLE_CONTRAST, + SANE_DESC_CONTRAST ); + if ( so ) + { + top->addWidget( so->widget(), 1 ); + so->set( 0 ); + sane_device->apply( so ); + } + + + /* GUI-Element grayify on startup */ + so = sane_device->getGuiElement( "grayify", this, i18n("convert the image to gray on loading"), 0 ); + if ( so ) + { + top->addWidget( so->widget(), 1 ); + so->set( true ); + sane_device->apply( so ); + } + + /* GUI-Element three pass simulation */ + so = sane_device->getGuiElement( "three-pass", this, i18n("Simulate three-pass acquiring"), 0 ); + if ( so ) + { + top->addWidget( so->widget(), 1 ); + so->set( false ); + sane_device->apply( so ); + } +#endif +} + +/* This slot is called if the user changes the +void ScanParams::slCheckGlob( void ) +{ + +} +*/ +/* Slot called to start scanning */ +void ScanParams::slStartScan( void ) +{ + KScanStat stat = KSCAN_OK; + + kdDebug(29000) << "Called start-scan-Slot!" << endl; + QString q; + + if( scan_mode == ID_SANE_DEBUG || scan_mode == ID_QT_IMGIO ) + { + if( virt_filename ) + q = virt_filename->get(); + if( q.isEmpty() ) + { + QMessageBox::information( this, i18n("KSANE"), + i18n("The filename for virtual scanning is not set.\n" + "Please set the filename first.") ); + stat = KSCAN_ERR_PARAM; + } + } + /* if it is a virt Scanner in SANE Debug or real scanning is on */ + if( stat == KSCAN_OK ) + { + if( (scan_mode == ID_SANE_DEBUG || scan_mode == ID_SCAN) ) + { + if( adf == ADF_OFF ) + { + /* Progress-Dialog */ + progressDialog->setProgress(0); + if( progressDialog->isHidden()) + progressDialog->show(); + kdDebug(29000) << "* slStartScan: Start to acquire an image!" << endl; + stat = sane_device->acquire( ); + + } else { + kdDebug(29000) << "Not yet implemented :-/" << endl; + + // stat = performADFScan(); + } + } else { + kdDebug(29000) << "Reading dir by Qt-internal imagereading file " << q << endl; + sane_device->acquire( q ); + } + } +} + +/* Slot called to start the Custom Gamma Table Edit dialog */ + +void ScanParams::slEditCustGamma( void ) +{ + kdDebug(29000) << "Called EditCustGamma ;)" << endl; + KGammaTable old_gt; + + + /* Since gammatable options are not set in the default gui, it must be + * checked if it is the first edit. If it is, take from loaded default + * set if available there */ + if( m_firstGTEdit && startupOptset ) + { + m_firstGTEdit = false; + KScanOption *gt = startupOptset->get(SANE_NAME_GAMMA_VECTOR); + if( !gt ) + { + /* If it not gray, it should be one color. */ + gt = startupOptset->get( SANE_NAME_GAMMA_VECTOR_R ); + } + + if( gt ) + gt->get( &old_gt ); + } + else + { + /* it is not the first edit, use older values */ + if( sane_device->optionExists( SANE_NAME_GAMMA_VECTOR ) ) + { + KScanOption grayGt( SANE_NAME_GAMMA_VECTOR ); + /* This will be fine for all color gt's. */ + grayGt.get( &old_gt ); + kdDebug(29000) << "Gray Gamma Table is active " << endl; + } + else + { + /* Gray is not active, but at the current implementation without + * red/green/blue gammatables, but one for all, all gammatables + * are equally. So taking the red one should be fine. TODO + */ + if( sane_device->optionExists( SANE_NAME_GAMMA_VECTOR_R )) + { + KScanOption redGt( SANE_NAME_GAMMA_VECTOR_R ); + redGt.get( &old_gt ); + kdDebug(29000) << "Getting old gamma table from Red channel" << endl; + } + else + { + /* uh ! No current gammatable could be retrieved. Use the 100/0/0 gt + * created by KGammaTable's constructor. Nothing to do for that. + */ + kdDebug(29000) << "WRN: Could not retrieve a gamma table" << endl; + } + } + } + + kdDebug(29000) << "Old gamma table: " << old_gt.getGamma() << ", " << old_gt.getBrightness() << ", " << old_gt.getContrast() << endl; + + GammaDialog gdiag( this ); + connect( &gdiag, SIGNAL( gammaToApply(KGammaTable*) ), + this, SLOT( slApplyGamma(KGammaTable*) ) ); + + gdiag.setGt( old_gt ); + + if( gdiag.exec() == QDialog::Accepted ) + { + slApplyGamma( gdiag.getGt() ); + kdDebug(29000) << "Fine, applied new Gamma Table !" << endl; + } + else + { + /* reset to old values */ + slApplyGamma( &old_gt ); + kdDebug(29000) << "Cancel, reverted to old Gamma Table !" << endl; + } + +} + + +void ScanParams::slApplyGamma( KGammaTable* gt ) +{ + if( ! gt ) return; + + kdDebug(29000) << "Applying gamma table: " << gt->getGamma() << + ", " << gt->getBrightness() << ", " << gt->getContrast() << endl; + + + if( sane_device->optionExists( SANE_NAME_GAMMA_VECTOR ) ) + { + KScanOption grayGt( SANE_NAME_GAMMA_VECTOR ); + + /* Now find out, which gamma-Tables are active. */ + if( grayGt.active() ) + { + grayGt.set( gt ); + sane_device->apply( &grayGt, true ); + } + } + + if( sane_device->optionExists( SANE_NAME_GAMMA_VECTOR_R )) { + KScanOption rGt( SANE_NAME_GAMMA_VECTOR_R ); + if( rGt.active() ) + { + rGt.set( gt ); + sane_device->apply( &rGt, true ); + } + } + + if( sane_device->optionExists( SANE_NAME_GAMMA_VECTOR_G )) { + KScanOption gGt( SANE_NAME_GAMMA_VECTOR_G ); + if( gGt.active() ) + { + gGt.set( gt ); + sane_device->apply( &gGt, true ); + } + } + + if( sane_device->optionExists( SANE_NAME_GAMMA_VECTOR_B )) { + KScanOption bGt( SANE_NAME_GAMMA_VECTOR_B ); + if( bGt.active() ) + { + bGt.set( gt ); + sane_device->apply( &bGt, true ); + } + } +} + +/* Slot calls if a widget changes. Things to do: + * - Apply the option and reload all if the option affects all + */ + +void ScanParams::slReloadAllGui( KScanOption* t) +{ + if( !t || ! sane_device ) return; + kdDebug(29000) << "This is slReloadAllGui for widget <" << t->getName() << ">" << endl; + /* Need to reload all _except_ the one which was actually changed */ + + sane_device->slReloadAllBut( t ); + + /* Custom Gamma <- What happens if that does not exist for some scanner ? TODO */ + setEditCustomGammaTableState(); +} + +/* + * enable editing of the gamma tables if one of the gamma tables + * exists and is active at the moment + */ +void ScanParams::setEditCustomGammaTableState() +{ + if( !(sane_device && pb_edit_gtable) ) + return; + + bool butState = false; + kdDebug(29000) << "Checking state of edit custom gamma button !" << endl; + + if( sane_device->optionExists( SANE_NAME_CUSTOM_GAMMA ) ) + { + KScanOption kso( SANE_NAME_CUSTOM_GAMMA ); + butState = kso.active(); + // kdDebug(29000) << "CustomGamma is active: " << butState << endl; + } + + if( !butState && sane_device->optionExists( SANE_NAME_GAMMA_VECTOR_R ) ) + { + KScanOption kso( SANE_NAME_GAMMA_VECTOR_R ); + butState = kso.active(); + // kdDebug(29000) << "CustomGamma Red is active: " << butState << endl; + } + + if( !butState && sane_device->optionExists( SANE_NAME_GAMMA_VECTOR_G ) ) + { + KScanOption kso( SANE_NAME_GAMMA_VECTOR_G ); + butState = kso.active(); + // kdDebug(29000) << "CustomGamma Green is active: " << butState << endl; + } + + if( !butState && sane_device->optionExists( SANE_NAME_GAMMA_VECTOR_B ) ) + { + KScanOption kso( SANE_NAME_GAMMA_VECTOR_B ); + butState = kso.active(); + // kdDebug(29000) << "CustomGamma blue is active: " << butState << endl; + } + pb_edit_gtable->setEnabled( butState ); +} + + +/* Slot called to start acquirering a preview */ +void ScanParams::slAcquirePreview( void ) +{ + kdDebug(29000) << "Called acquirePreview-Slot!" << endl; + bool gray_preview = false; + if( cb_gray_preview ) + gray_preview = cb_gray_preview->isChecked(); + + + /* Maximal preview size */ + slMaximalScanSize(); + + if( ! sane_device ) kdDebug(29000) << "Aeetsch: sane_device is 0 !" << endl; + Q_CHECK_PTR( sane_device ); + KScanStat stat = sane_device->acquirePreview( gray_preview ); + + if( stat != KSCAN_OK ) + { + kdDebug(29000) << "Error in scanning !" << endl; + } +} + +/* Custom Scan size setting. + * The custom scan size is given in the QRect parameter. It must contain values + * from 0..1000 which are interpreted as tenth of percent of the overall dimension. + */ +void ScanParams::slCustomScanSize( QRect sel) +{ + kdDebug(29000) << "Custom-Size: " << sel.x() << ", " << sel.y() << " - " << sel.width() << "x" << sel.height() << endl; + + KScanOption tl_x( SANE_NAME_SCAN_TL_X ); + KScanOption tl_y( SANE_NAME_SCAN_TL_Y ); + KScanOption br_x( SANE_NAME_SCAN_BR_X ); + KScanOption br_y( SANE_NAME_SCAN_BR_Y ); + + double min1=0.0, max1=0.0, min2=0.0, max2=0.0, dummy1=0.0, dummy2=0.0; + tl_x.getRange( &min1, &max1, &dummy1 ); + br_x.getRange( &min2, &max2, &dummy2 ); + + /* overall width */ + double range = max2-min1; + double w = min1 + double(range * (double(sel.x()) / 1000.0) ); + tl_x.set( w ); + w = min1 + double(range * double(sel.x() + sel.width())/1000.0); + br_x.set( w ); + + + kdDebug(29000) << "set tl_x: " << min1 + double(range * (double(sel.x()) / 1000.0) ) << endl; + kdDebug(29000) << "set br_x: " << min1 + double(range * (double(sel.x() + sel.width())/1000.0)) << endl; + + /** Y-Value setting */ + tl_y.getRange( &min1, &max1, &dummy1 ); + br_y.getRange(&min2, &max2, &dummy2 ); + + /* overall width */ + range = max2-min1; + w = min1 + range * double(sel.y()) / 1000.0; + tl_y.set( w ); + w = min1 + range * double(sel.y() + sel.height())/1000.0; + br_y.set( w ); + + + kdDebug(29000) << "set tl_y: " << min1 + double(range * (double(sel.y()) / 1000.0) ) << endl; + kdDebug(29000) << "set br_y: " << min1 + double(range * (double(sel.y() + sel.height())/1000.0)) << endl; + + + sane_device->apply( &tl_x ); + sane_device->apply( &tl_y ); + sane_device->apply( &br_x ); + sane_device->apply( &br_y ); +} + + +/** + * sets the scan area to the default, which is the whole area. + */ +void ScanParams::slMaximalScanSize( void ) +{ + kdDebug(29000) << "Setting to default" << endl; + slCustomScanSize(QRect( 0,0,1000,1000)); +} + + +void ScanParams::slNewXResolution(KScanOption *opt) +{ + if(! opt ) return; + + kdDebug(29000) << "Got new X-Resolution !" << endl; + + int x_res = 0; + opt->get( &x_res ); + + int y_res = x_res; + + if( xy_resolution_bind && xy_resolution_bind->active() ) + { + /* That means, that x and y may be different */ + KScanOption opt_y( SANE_NAME_SCAN_Y_RESOLUTION ); + if( opt_y.valid () ) + { + opt_y.get( &y_res ); + } + } + + emit( scanResolutionChanged( x_res, y_res ) ); +} + +void ScanParams::slNewYResolution(KScanOption *opt) +{ + if( ! opt ) return; + + int y_res = 0; + opt->get( &y_res ); + + int x_res = y_res; + + if( xy_resolution_bind && xy_resolution_bind->active()) + { + /* That means, that x and y may be different */ + KScanOption opt_x( SANE_NAME_SCAN_X_RESOLUTION ); + if( opt_x.valid () ) + { + opt_x.get( &x_res ); + } + } + + emit( scanResolutionChanged( x_res, y_res ) ); + +} + + +KScanStat ScanParams::performADFScan( void ) +{ + KScanStat stat = KSCAN_OK; + bool scan_on = true; + + MassScanDialog *msd = new MassScanDialog( this ); + msd->show(); + + /* The scan source should be set to ADF by the SourceSelect-Dialog */ + + while( scan_on ) + { + scan_on = false; + } + return( stat ); +} +#include "scanparams.moc" diff --git a/libkscan/scanparams.h b/libkscan/scanparams.h new file mode 100644 index 00000000..c80e1ce6 --- /dev/null +++ b/libkscan/scanparams.h @@ -0,0 +1,182 @@ +/* This file is part of the KDE Project + Copyright (C) 1999 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef SCANPARAMS_H +#define SCANPARAMS_H + +#include "kscandevice.h" +#include "scansourcedialog.h" + +#include <qvbox.h> +#include <qhbox.h> + +#include <qdir.h> +#include <qpixmap.h> + +/** + *@author Klaas Freitag + */ + +class GammaDialog; +class KScanOptSet; +class QScrollView; +class KLed; +class QProgressDialog; +class QPushButton; +class QCheckBox; +class QButtonGroup; + +typedef enum { ID_SANE_DEBUG, ID_QT_IMGIO, ID_SCAN } ScanMode; + +class ScanParams : public QVBox +{ + Q_OBJECT +public: + ScanParams( QWidget *parent, const char *name = 0); + ~ScanParams(); +#if 0 + QSize sizeHint( ); +#endif + bool connectDevice( KScanDevice* ); + + KLed *operationLED() { return m_led; } + +public slots: +/** + * In this slot, a custom scan window can be set, e.g. through a preview + * image with a area selector. The QRect-param needs to contain values + * between 0 and 1000, which are interpreted as tenth of percent of the + * whole image dimensions. + **/ +void slCustomScanSize( QRect ); + + /** + * sets the scan area to the default, which is the whole area. + */ + void slMaximalScanSize( void ); + + /** + * starts acquireing a preview image. + * This ends up in a preview-signal of the scan-device object + */ + void slAcquirePreview( void ); + void slStartScan( void ); + + /** + * connect this slot to KScanOptions Signal optionChanged to be informed + * on a options change. + */ + void slOptionNotify( KScanOption *kso ); + +protected slots: +/** + * connected to the button which opens the source selection dialog + */ +void slSourceSelect( void ); + /** + * allows to select a file or directory for the virtuell scanner + */ + void slFileSelect ( void ); + + /** + * Slot to call if the virtual scanner mode is changed + */ + void slVirtScanModeSelect( int id ); + + /** + * Slot for result on an edit-Custom Gamma Table request. + * Starts a dialog. + */ + void slEditCustGamma( void ); + + /** + * Slot called if a Gui-Option changed due to user action, eg. the + * user selects another entry in a List. + * Action to do is to apply the new value and check, if it affects others. + */ + void slReloadAllGui( KScanOption* ); + + /** + * Slot called when the Edit Custom Gamma-Dialog has a new gamma table + * to apply. This is an internal slot. + */ + void slApplyGamma( KGammaTable* ); + + /** + * internal slot called when the slider for x resolution changes. + * In the slot, the signal scanResolutionChanged will be emitted, which + * is visible outside the scanparam-object to notify that the resolutions + * changed. + * + * That is e.g. useful for size calculations + */ + void slNewXResolution( KScanOption* ); + + /** + * the same slot as @see slNewXResolution but for y resolution changes. + */ + void slNewYResolution( KScanOption* ); + + + signals: + + /** + * emitted if the resolution to scan changes. This signal may be connected + * to slots calculating the size of the image size etc. + * + * As parameters the resolutions in x- and y-direction are coming. + */ + void scanResolutionChanged( int, int ); + +private: + + + QScrollView* scannerParams( ); + void virtualScannerParams( void ); + void createNoScannerMsg( void ); + void initialise( KScanOption* ); + void setEditCustomGammaTableState(); + + KScanStat performADFScan( void ); + + KScanDevice *sane_device; + KScanOption *virt_filename; + QCheckBox *cb_gray_preview; + QPushButton *pb_edit_gtable; + QPushButton *pb_source_sel; + ADF_BEHAVE adf; + QButtonGroup *bg_virt_scan_mode; + ScanMode scan_mode; + QDir last_virt_scan_path; + + KScanOption *xy_resolution_bind; + + KScanOptSet *startupOptset; + + QProgressDialog *progressDialog; + + QPixmap pixLineArt, pixGray, pixColor, pixHalftone, pixMiniFloppy; + KLed *m_led; + bool m_firstGTEdit; + + class ScanParamsPrivate; + ScanParamsPrivate *d; +}; + +#endif diff --git a/libkscan/scanservice.desktop b/libkscan/scanservice.desktop new file mode 100644 index 00000000..8330e0ae --- /dev/null +++ b/libkscan/scanservice.desktop @@ -0,0 +1,70 @@ +[Desktop Entry] +Type=Service +Name=KDE Scan Service +Name[af]=Kde Skandeer Diens +Name[ar]=خدمة KDE للمسح الضوئي +Name[az]=KDE Darama Servisi +Name[bg]=Услуга за сканиране +Name[br]=Damanterien skramm +Name[ca]=Servei d'escaneig del KDE +Name[cs]=Skenovací služba pro KDE +Name[cy]=Gwasanaeth Sganio KDE +Name[da]=KDE Skanneservice +Name[de]=Scan-Dienst von KDE +Name[el]=Υπηρεσία σάρωσης του KDE +Name[eo]=Bildcifereciga servo (skano) +Name[es]=Servicio de digitalización de KDE +Name[et]=KDE skaneerimisteenus +Name[eu]=KDE eskaneatze zerbitzua +Name[fa]=خدمت پویش KDE +Name[fi]=KDE :n skannauspalvelu +Name[fr]=Service de numérisation pour KDE +Name[gl]=Servicio de escáner de KDE +Name[he]=שירות הסריקה של KDE +Name[hi]=केडीई स्कैन सर्विस +Name[hr]=KDE skan servis +Name[hu]=KDE lapolvasó szolgáltatás +Name[is]=KDE myndlesaraþjónusta +Name[it]=Servizio di scansione di KDE +Name[ja]=KDE スキャンサービス +Name[kk]=KDE сканер қызметі +Name[km]=សេវាស្កេនរបស់ KDE +Name[ko]=KDE 스캔 서비스 +Name[lt]=KDE skanavimo tarnyba +Name[lv]=KDE Skanēšanas Serviss +Name[ms]=Perkhidmatan Imbas KDE +Name[mt]=Servizz KDE tal-iskannjar +Name[nb]=KDE skannetjeneste +Name[nds]=Bildinleesdeenst för KDE +Name[ne]=केडीई स्क्यान सेवा +Name[nl]=KDE Scannerdiensten +Name[nn]=KDE Skanningsteneste +Name[nso]=Tirelo ya Tebelelo ya KDE +Name[pl]=Usługa skanowania KDE +Name[pt]=Serviço de 'Scanner' do KDE +Name[pt_BR]=Serviço de Digitalização do KDE +Name[ro]=Serviciu de scanare KDE +Name[ru]=Служба сканирования KDE +Name[se]=KDE skánnenbálválus +Name[sk]=KDE skenovacia služba +Name[sl]=Storitev skeniranja za KDE +Name[sr]=KDE-ов сервис за скенирање +Name[sr@Latn]=KDE-ov servis za skeniranje +Name[sv]=KDE:s bildläsartjänst +Name[ta]=கேடிஇ வருடு சேவை +Name[tg]=Сканеронӣ дар KDE +Name[th]=บริการสแกนภาพ - K +Name[tr]=KDE Tarama Servisi +Name[uk]=Служба пошуку KDE +Name[uz]=KDE skan qilish xizmati +Name[uz@cyrillic]=KDE скан қилиш хизмати +Name[ven]=Tshishumiswa tshau naga tsha KDE +Name[wa]=Siervice di scanaedje di KDE +Name[xh]=Inkonzo yemita yovavanyo ye KDE +Name[zh_CN]=KDE 扫描服务 +Name[zh_HK]=KDE 掃描服務 +Name[zh_TW]=KDE 掃描服務 +Name[zu]=Umsebenzi Lokuhlola lwe-KDE +X-KDE-Library=libkscan +InitialPreference=2 +ServiceTypes=KScan/KScanDialog diff --git a/libkscan/scansourcedialog.cpp b/libkscan/scansourcedialog.cpp new file mode 100644 index 00000000..01d71dc1 --- /dev/null +++ b/libkscan/scansourcedialog.cpp @@ -0,0 +1,211 @@ +/* This file is part of the KDE Project + Copyright (C) 2000 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "scansourcedialog.h" +#include "kscanslider.h" + +#include <klocale.h> +#include <kdebug.h> + +#include <qlabel.h> +#include <qpushbutton.h> +#include <qlayout.h> +#include <qvbox.h> +#include <qhbox.h> +#include <qradiobutton.h> +#include <qslider.h> +#include <qlineedit.h> +#include <qcombobox.h> + +#include <qvbuttongroup.h> +#include <qbuttongroup.h> + + +extern "C"{ +#include <sane/saneopts.h> +} +#ifndef SANE_NAME_DOCUMENT_FEEDER +#define SANE_NAME_DOCUMENT_FEEDER "Automatic Document Feeder" +#endif + + +ScanSourceDialog::ScanSourceDialog( QWidget *parent, const QStrList list, ADF_BEHAVE adfBehave ) + : KDialogBase( parent, "SOURCE_DIALOG", true, i18n("Scan Source Selection"), + Ok|Cancel,Ok, true) +{ + QVBox *vbox = makeVBoxMainWidget(); + + (void) new QLabel( i18n("<B>Source selection</B><P>" + "Note that you may see more sources than actually exist"), vbox ); + + /* Combo Box for sources */ + const QStrList xx = list; + sources = new KScanCombo( vbox, + i18n("Select the Scanner document source:"), + xx); + connect( sources, SIGNAL( activated(int)), this, SLOT( slChangeSource(int))); + + + /* Button Group for ADF-Behaviour */ + bgroup = 0; + adf = ADF_OFF; + + if( sourceAdfEntry() > -1 ) + { + bgroup = new QVButtonGroup( i18n("Advanced ADF-Options"), vbox, "ADF_BGROUP" ); + + connect( bgroup, SIGNAL(clicked(int)), this, SLOT( slNotifyADF(int))); + + /* Two buttons inside */ + QRadioButton *rbADFTillEnd = new QRadioButton( i18n("Scan until ADF reports out of paper"), + bgroup ); + bgroup->insert( rbADFTillEnd, ADF_SCAN_ALONG ); + + QRadioButton *rbADFOnce = new QRadioButton( i18n("Scan only one sheet of ADF per click"), + bgroup ); + bgroup->insert( rbADFOnce, ADF_SCAN_ONCE ); + + switch ( adfBehave ) + { + case ADF_OFF: + bgroup->setButton( ADF_SCAN_ONCE ); + bgroup->setEnabled( false ); + adf = ADF_OFF; + break; + case ADF_SCAN_ONCE: + bgroup->setButton( ADF_SCAN_ONCE ); + adf = ADF_SCAN_ONCE; + break; + case ADF_SCAN_ALONG: + bgroup->setButton( ADF_SCAN_ALONG ); + adf = ADF_SCAN_ALONG; + break; + default: + kdDebug(29000) << "Undefined Source !" << endl; + // Hmmm. + break; + } + } +} + +QString ScanSourceDialog::getText( void ) const +{ + return( sources->currentText() ); +} + +void ScanSourceDialog::slNotifyADF( int ) +{ + // debug( "reported adf-select %d", adf_group ); + /* this seems to be broken, adf_text is a visible string? + * needs rework if SANE 2 comes up which supports i18n */ +#if 0 + QString adf_text = getText(); + + adf = ADF_OFF; + + if( adf_text == "Automatic Document Feeder" || + adf_text == "ADF" ) + { + if( adf_group == 0 ) + adf = ADF_SCAN_ALONG; + else + adf = ADF_SCAN_ONCE; + } +#endif +} + + +void ScanSourceDialog::slChangeSource( int i ) +{ + if( ! bgroup ) return; + + if( i == sourceAdfEntry()) + { + /* Adf was switched on */ + bgroup->setEnabled( true ); + bgroup->setButton( 0 ); + adf = ADF_SCAN_ALONG; + adf_enabled = true; + } + else + { + bgroup->setEnabled( false ); + // adf = ADF_OFF; + adf_enabled = false; + } +} + + + +int ScanSourceDialog::sourceAdfEntry( void ) const +{ + if( ! sources ) return( -1 ); + + int cou = sources->count(); + + for( int i = 0; i < cou; i++ ) + { + QString q = sources->text( i ); + +#if 0 + if( q == "ADF" || q == SANE_NAME_DOCUMENT_FEEDER ) + return( i ); +#endif + + } + return( -1 ); +} + + + +void ScanSourceDialog::slSetSource( const QString source ) +{ + if( !sources ) return; + kdDebug(29000) << "Setting <" << source << "> as source" << endl; + + if( bgroup ) + bgroup->setEnabled( false ); + adf_enabled = false ; + + + for( int i = 0; i < sources->count(); i++ ) + { + if( sources->text( i ) == source ) + { + sources->setCurrentItem( i ); + if( source == QString::number(sourceAdfEntry()) ) + { + if( bgroup ) + bgroup->setEnabled( true ); + adf_enabled = true; + } + break; + } + } + +} + + +ScanSourceDialog::~ScanSourceDialog() +{ + +} + +/* EOF */ +#include "scansourcedialog.moc" diff --git a/libkscan/scansourcedialog.h b/libkscan/scansourcedialog.h new file mode 100644 index 00000000..3379843f --- /dev/null +++ b/libkscan/scansourcedialog.h @@ -0,0 +1,69 @@ +/* This file is part of the KDE Project + Copyright (C) 2000 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef SCANSOURCEDIALOG_H +#define SCANSOURCEDIALOG_H +#include <qwidget.h> +#include <kdialogbase.h> +#include <qstrlist.h> +#include <qstring.h> + +/** + *@author Klaas Freitag + */ + +typedef enum { ADF_OFF, ADF_SCAN_ALONG, ADF_SCAN_ONCE } ADF_BEHAVE; + +class KScanCombo; +class QRadioButton; +class QButtonGroup; + +class ScanSourceDialog : public KDialogBase +{ + Q_OBJECT +public: + ScanSourceDialog( QWidget *parent, const QStrList, ADF_BEHAVE ); + ~ScanSourceDialog(); + + // void fillWithSources( QStrList *list ); + QString getText( void ) const; + + ADF_BEHAVE getAdfBehave( void ) const + { return( adf ); } + + +public slots: + void slNotifyADF( int ); + void slChangeSource( int ); + int sourceAdfEntry( void ) const; + void slSetSource( const QString source ); + +private: + + KScanCombo *sources; + QButtonGroup *bgroup; + QRadioButton *rb0, *rb1; + ADF_BEHAVE adf; + bool adf_enabled; + + class ScanSourceDialogPrivate; + ScanSourceDialogPrivate *d; +}; + +#endif diff --git a/libkscan/sizeindicator.cpp b/libkscan/sizeindicator.cpp new file mode 100644 index 00000000..4ad1986d --- /dev/null +++ b/libkscan/sizeindicator.cpp @@ -0,0 +1,113 @@ +/* This file is part of the KDE Project + Copyright (C) 2000 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "sizeindicator.h" + +#include <qpalette.h> +#include <qimage.h> + +#include <kimageeffect.h> +#include <klocale.h> +#include <kdebug.h> +#include <qpainter.h> + + + +SizeIndicator::SizeIndicator( QWidget *parent, long thres, long crit ) + :QLabel( parent ) +{ + sizeInByte = -1; + setFrameStyle( QFrame::Box | QFrame::Sunken ); + setMinimumWidth( fontMetrics().width( QString::fromLatin1("MMM.MM MB") )); + setCritical( crit ); + threshold = thres; + +} + + +void SizeIndicator::setCritical( long crit ) +{ + critical = crit; + devider = 255.0 / double( critical ); +} + + +void SizeIndicator::setThreshold( long thres ) +{ + threshold = thres; +} + + +SizeIndicator::~SizeIndicator() +{ + +} + +void SizeIndicator::setSizeInByte( long newSize ) +{ + sizeInByte = newSize; + kdDebug(29000) << "New size in byte: " << newSize << endl ; + + QString t; + + QString unit = i18n( "%1 kB" ); + double sizer = double(sizeInByte)/1024.0; // produces kiloBytes + int precision = 1; + int fwidth = 3; + + if( sizer > 999.9999999 ) + { + unit = i18n( "%1 MB" ); + sizer = sizer / 1024.0; + precision = 2; + fwidth = 2; + } + + t = unit.arg( sizer, fwidth, 'f', precision); + setText(t); + +} + + + +void SizeIndicator::drawContents( QPainter *p ) +{ + QSize s = size(); + + QColor warnColor; + + if( sizeInByte >= threshold ) + { + int c = int( double(sizeInByte) * devider ); + if( c > 255 ) c = 255; + + warnColor.setHsv( 0, c, c ); + + p->drawImage( 0,0, + KImageEffect::unbalancedGradient( s, colorGroup().background(), + warnColor, KImageEffect::CrossDiagonalGradient, 200,200 )); + } + /* Displaying the text */ + QString t = text(); + p->drawText( 0, 0, s.width(), s.height(), + AlignHCenter | AlignVCenter, t); + +} + +#include "sizeindicator.moc" diff --git a/libkscan/sizeindicator.h b/libkscan/sizeindicator.h new file mode 100644 index 00000000..213fe931 --- /dev/null +++ b/libkscan/sizeindicator.h @@ -0,0 +1,98 @@ +/* This file is part of the KDE Project + Copyright (C) 2000 Klaas Freitag <freitag@suse.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef SIZEINDICATOR_H +#define SIZEINDICATOR_H + +#define DEFAULT_CRITICAL (3*1024*1024) +#define DEFAULT_THRESHOLD (1*1024*1024) + +class QPainter; +#include <qlabel.h> + +/** + * @short small size indication widget for file sizes + * @author Klaas Freitag + * + * the size indicator is a small widget that displays a file size in a small + * frame. The unit (currently kB and MB) is selected automagically. + * If the file size grows bigger than the threshold set in the constructor, + * the widget starts to change its background color to visualise to the + * user that he is doing something obvious. + */ + +class SizeIndicator: public QLabel +{ + Q_OBJECT + // Q_PROPERTY( KGammaTable *gt READ getGt WRITE setGt ) + +public: + /** + * Creates a size indicator widget. + * @param thres: Threshold, value on that the widget starts to become red. + * @param crit: Critical value, not yet used. + + */ + SizeIndicator( QWidget *parent, long thres = DEFAULT_THRESHOLD, + long crit = DEFAULT_CRITICAL ); + /** + * destructor does not really do much yet. + */ + ~SizeIndicator(); + +public slots: + + /** + * is the slot that sets the file size to display. The widget gets + * updated. + * @param sizeInByte: the size to set. + */ + void setSizeInByte( long ); + + /** + * sets the critical size. + * @param crit: the critical value + */ + void setCritical( long ); + + /** + * sets the threshold value. + * @param thres: the threshold bytesize + */ + void setThreshold( long ); + + +protected: + /** + * reimplemented to display the color + */ + virtual void drawContents( QPainter* ); + +private: + + long sizeInByte; + long critical, threshold; + + double devider; + + class sizeIndicatorPrivate; + sizeIndicatorPrivate *d; +}; + +#endif |