diff options
Diffstat (limited to 'klinkstatus')
104 files changed, 14477 insertions, 0 deletions
diff --git a/klinkstatus/AUTHORS b/klinkstatus/AUTHORS new file mode 100644 index 00000000..9fc838ae --- /dev/null +++ b/klinkstatus/AUTHORS @@ -0,0 +1 @@ +Paulo Moura Guedes <moura@kdewebdev.org> diff --git a/klinkstatus/COPYING b/klinkstatus/COPYING new file mode 100644 index 00000000..c13faf0d --- /dev/null +++ b/klinkstatus/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/klinkstatus/ChangeLog b/klinkstatus/ChangeLog new file mode 100644 index 00000000..c278c40e --- /dev/null +++ b/klinkstatus/ChangeLog @@ -0,0 +1,122 @@ +Changes for 0.3.1 +----------------------------------------------------- + +- Add the possibility to configure the User-Agent sent to the HTTP server. +- Fix a problem with redirections different than 301 (moved permanently) +- Don't require unsermake no more, in order to build the application +- Added the possibility to set the document root for protocols different than HTTP + (to resolve URLs started with '/') +- Improved behaviour in the layout of results columns +- Context menu actions in results are more clear now +- Fixed linkage order to build in IRIX +- Added support for links in maps (area element) +- Don't crash when checking anchors (bug #118495) +- Properly disable close tab button +- Other bug fixes and improvements + + +Changes for 0.3.0 +----------------------------------------------------- + +- Results can be exported to HTML (BUG #85592). + Users are invited to improve the XSLT stylesheet and propose it to the author. + +- Improved interface + - ability to show/hide the search options + - option to control whether the list view should follow the last link checked + - option to reset the search options + - standard open/close tab widget tool buttons + - Replace the search buttons by toolbar actions + - ability to close the tabs on hover + - Revamped toolbars and usability in general + +- Search bar where one can filter links by text and status. +- Fix a bug where the title of a URL was being set in the wrong page, if it wasn't the current. +- Other bug fixes and improvements + + +Changes for 0.2.3 +----------------------------------------------------- + +- Display correctly the page title with HTML entities (BUG #113560). +- Find anchors identified with id attribute (BUG #112030). +- Check anchors of all documents even if not searched yet (no more "URL ... not checked yet"). +- Remember port when resolving relative links (BUG 112249). +- Display correctly link labels with HTML entities (BUG #111694). +- Remember layout (order of columns, etc) of the result tree view. +- Show the filename on the root link. +- Add a folder button in the search field so one can easily set the URL to search. +- Fixed the KIO problem in kdelibs that was causing random crashes. +- New KLinkStatus logo from Alberto Gamez. + + +Changes for 0.2.2 +----------------------------------------------------- + +- Compare domains in a case insensitive way (BUG #105415) +- Fix crash when using File -> Quit (BUG #82617) + + +Changes for 0.2.1 +----------------------------------------------------- + +- New feature: don't check URLs given a regular expression. +- Fix current search settings being reseted when check button is pressed. + + +Changes for 0.2.0 +----------------------------------------------------- + +- Added a tree view that reflects the structure of the website. +It is possible to switch choose a tree view or a flat view in settings. +- Other bugfixes and minor improvements. + +Changes for 0.1.3 +----------------------------------------------------- + +- Added some shortcuts and improve their configuration. +- Improved settings update. +- Use Quanta project preview prefix on the URL to check. +- Improved 'Edit referrer with Quanta' action. +- Other bugfixes and minor improvements. + + +Changes for 0.1.2 +----------------------------------------------------- + +- Dialog for configuring settings added. +- Tool button to clear the combo url. +- Pause and resume feature. +- View menu to display good, broken, etc, links. +- Use title page for tab labels. +- Validate URLs with reference (#) +- Edit referrers in Quanta (can open all at a time). + Quanta needs to be run with --unique or embed KLinkStatus (for now...) +- other minor improvements and bug fixes. + + +Changes for 0.1.1 +----------------------------------------------------- + +- KConfigXT (no GUI yet) +- File Dialog opens remote files now. +- Fixed error status when URL is a directory +- Combo url saves history and has autocompletion +- Combo url navigation improved (like Konqueror) +- Improved display of relative URLs +- Show horizontal scroll when URL is too long +- other improvements and bug fixes. + + +Changes for 0.1.0 +----------------------------------------------------- +- compilation fixes +- fixed external links and parent directories judgment +- different defaults for search +- several other fixes and improvements that I can't remember + + +Changes for 0.1-b1 +----------------------------------------------------- + Initial beta release + diff --git a/klinkstatus/FEATURES b/klinkstatus/FEATURES new file mode 100644 index 00000000..33a328e4 --- /dev/null +++ b/klinkstatus/FEATURES @@ -0,0 +1,18 @@ +- Support several protocols (allowing fast checking of local documents): http, ftp, ssh (fish or sftp) and file. +- Proxy support +- Allows authentication when checking restricted documents +- Supports the latest Web standards-- HTML 4.0, HTTP 1.1 +- Server-Side Includes (SSI, aka SHTML) are supported and checked +- Regular expressions to restrict which URLs are searched +- Show link results as they are checked +- Tree like view (that reflects the file structure of the documents) or flat view +- Limit the search depth +- Fragment identifiers ("#" anchor links that point to a specific section in a document) are supported and checked +- Find and respect the charset of the documents +- Pause/Resume of checking session +- History of checked URLs +- Tabbed checking (allow multiple sessions at the same time) +- Filter checked links (good, broken, malformed and undetermined) +- Configurable number of simultaneous connections (performance tunning) +- Other configurable options like "check external links", "check parent folders", "timeout" +- Good integration with Quanta+
\ No newline at end of file diff --git a/klinkstatus/INSTALL b/klinkstatus/INSTALL new file mode 100644 index 00000000..02a4a074 --- /dev/null +++ b/klinkstatus/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/klinkstatus/Makefile.am b/klinkstatus/Makefile.am new file mode 100644 index 00000000..3da2d71a --- /dev/null +++ b/klinkstatus/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = data src diff --git a/klinkstatus/NEWS b/klinkstatus/NEWS new file mode 100644 index 00000000..0519ecba --- /dev/null +++ b/klinkstatus/NEWS @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/klinkstatus/README b/klinkstatus/README new file mode 100644 index 00000000..eaf5b045 --- /dev/null +++ b/klinkstatus/README @@ -0,0 +1,5 @@ +KLinkStatus is a link checker for KDE, based in LinkStatus. + +You can report bugs at http://linkstatus.paradigma.co.pt/bugs/ + +Contact: pmg@netcabo.pt diff --git a/klinkstatus/TODO b/klinkstatus/TODO new file mode 100644 index 00000000..36f28fb0 --- /dev/null +++ b/klinkstatus/TODO @@ -0,0 +1,24 @@ +Do list: + +- Option to skip already timed-out hosts + +- Ability to do edit actions after a search is finished, + like findind links, etc + +- GUI for search stats + +- GUI for info about a page/URL + +- Send email with broken links + +- Check the size for complete request on homepage + +- Use proper charset for the pages + +- Advanced search features like: + - check only files modified recently + - check only files matching given regular expression + - check only files in given path + +- Write better documentation + diff --git a/klinkstatus/configure.in.in b/klinkstatus/configure.in.in new file mode 100644 index 00000000..3a2e3182 --- /dev/null +++ b/klinkstatus/configure.in.in @@ -0,0 +1,6 @@ +#MIN_CONFIG(3.2) + +AM_INIT_AUTOMAKE(klinkstatus, 0.1.0) +AC_C_BIGENDIAN +AC_CHECK_KDEMAXPATHLEN + diff --git a/klinkstatus/data/Makefile.am b/klinkstatus/data/Makefile.am new file mode 100644 index 00000000..f8219dbf --- /dev/null +++ b/klinkstatus/data/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = icons styles + +klinkstatusicondir = $(kde_datadir)/klinkstatus/icons diff --git a/klinkstatus/data/icons/16x16/304.png b/klinkstatus/data/icons/16x16/304.png Binary files differnew file mode 100644 index 00000000..036bff9b --- /dev/null +++ b/klinkstatus/data/icons/16x16/304.png diff --git a/klinkstatus/data/icons/16x16/Makefile.am b/klinkstatus/data/icons/16x16/Makefile.am new file mode 100644 index 00000000..1d19c3a3 --- /dev/null +++ b/klinkstatus/data/icons/16x16/Makefile.am @@ -0,0 +1,4 @@ +klinkstatusicondir = $(kde_datadir)/klinkstatuspart/pics + +klinkstatusicon_DATA = 304.png +KDE_ICON = bug diff --git a/klinkstatus/data/icons/16x16/cr16-action-bug.png b/klinkstatus/data/icons/16x16/cr16-action-bug.png Binary files differnew file mode 100644 index 00000000..827d5f8e --- /dev/null +++ b/klinkstatus/data/icons/16x16/cr16-action-bug.png diff --git a/klinkstatus/data/icons/22x22/Makefile.am b/klinkstatus/data/icons/22x22/Makefile.am new file mode 100644 index 00000000..bdcbfad2 --- /dev/null +++ b/klinkstatus/data/icons/22x22/Makefile.am @@ -0,0 +1 @@ +klinkstatusicondir = $(kde_datadir)/klinkstatuspart/pics diff --git a/klinkstatus/data/icons/32x32/Makefile.am b/klinkstatus/data/icons/32x32/Makefile.am new file mode 100644 index 00000000..5d234f12 --- /dev/null +++ b/klinkstatus/data/icons/32x32/Makefile.am @@ -0,0 +1,2 @@ +klinkstatusicondir = $(kde_datadir)/klinkstatuspart/pics + diff --git a/klinkstatus/data/icons/Makefile.am b/klinkstatus/data/icons/Makefile.am new file mode 100644 index 00000000..14f1949a --- /dev/null +++ b/klinkstatus/data/icons/Makefile.am @@ -0,0 +1,2 @@ +SUBDIRS = 32x32 22x22 16x16 +KDE_ICON = klinkstatus diff --git a/klinkstatus/data/icons/hi128-app-klinkstatus.png b/klinkstatus/data/icons/hi128-app-klinkstatus.png Binary files differnew file mode 100644 index 00000000..2aec7026 --- /dev/null +++ b/klinkstatus/data/icons/hi128-app-klinkstatus.png diff --git a/klinkstatus/data/icons/hi16-app-klinkstatus.png b/klinkstatus/data/icons/hi16-app-klinkstatus.png Binary files differnew file mode 100644 index 00000000..ea405926 --- /dev/null +++ b/klinkstatus/data/icons/hi16-app-klinkstatus.png diff --git a/klinkstatus/data/icons/hi22-app-klinkstatus.png b/klinkstatus/data/icons/hi22-app-klinkstatus.png Binary files differnew file mode 100644 index 00000000..6d359296 --- /dev/null +++ b/klinkstatus/data/icons/hi22-app-klinkstatus.png diff --git a/klinkstatus/data/icons/hi32-app-klinkstatus.png b/klinkstatus/data/icons/hi32-app-klinkstatus.png Binary files differnew file mode 100644 index 00000000..fc4b9eff --- /dev/null +++ b/klinkstatus/data/icons/hi32-app-klinkstatus.png diff --git a/klinkstatus/data/icons/hi48-app-klinkstatus.png b/klinkstatus/data/icons/hi48-app-klinkstatus.png Binary files differnew file mode 100644 index 00000000..68e67f4a --- /dev/null +++ b/klinkstatus/data/icons/hi48-app-klinkstatus.png diff --git a/klinkstatus/data/icons/hi64-app-klinkstatus.png b/klinkstatus/data/icons/hi64-app-klinkstatus.png Binary files differnew file mode 100644 index 00000000..47b4668f --- /dev/null +++ b/klinkstatus/data/icons/hi64-app-klinkstatus.png diff --git a/klinkstatus/data/styles/Makefile.am b/klinkstatus/data/styles/Makefile.am new file mode 100644 index 00000000..7f17fbd9 --- /dev/null +++ b/klinkstatus/data/styles/Makefile.am @@ -0,0 +1,6 @@ +styles_DATA = \ + results_stylesheet.xsl + +stylesdir = $(kde_datadir)/klinkstatus/styles +EXTRA_DIST = $(styles_DATA) + diff --git a/klinkstatus/data/styles/results_stylesheet.xsl b/klinkstatus/data/styles/results_stylesheet.xsl new file mode 100644 index 00000000..d0b0c10d --- /dev/null +++ b/klinkstatus/data/styles/results_stylesheet.xsl @@ -0,0 +1,99 @@ +<?xml version='1.0' encoding='utf-8' ?> +<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> + + <xsl:output method="html" /> + + <xsl:template match="/klinkstatus"> + <html> + <body> + + <strong>URL: </strong> + <xsl:value-of select="/klinkstatus/url"/> + <br/> + <strong>Depth: </strong> + <xsl:value-of select="/klinkstatus/depth"/> + <br/> + <strong>Check Parent Folders: </strong> + <xsl:value-of select="/klinkstatus/check_parent_folders"/> + <br/> + <strong>Check external links: </strong> + <xsl:value-of select="/klinkstatus/check_external_links"/> + <br/> + <xsl:if test="/klinkstatus/check_regular_expression[@check = 'true']"> + <strong>Do not check links matching regular expression: </strong> + <xsl:value-of select="/klinkstatus/check_regular_expression"/> + <br/> + </xsl:if> + <br/> + + <table border="1"> + <thead> + <tr> + <th>URL</th> + <th>Status</th> + <th>Label</th> + <th>Referrers</th> + </tr> + </thead> + <tbody> + <xsl:for-each select="/klinkstatus/link_list/link"> + <tr> + <td> + <xsl:apply-templates select="url"/> + </td> + <td> + <xsl:apply-templates select="status"/> + </td> + <td> + <xsl:apply-templates select="label"/> + </td> + <td> + <xsl:apply-templates select="referrers"/> + </td> + </tr> + </xsl:for-each> + </tbody> + </table> + </body> + </html> + </xsl:template> + + <xsl:template match="url"> + <xsl:variable name="isBroken"> + <xsl:value-of select="../status/@broken"/> + </xsl:variable> + <xsl:choose> + <xsl:when test="$isBroken='true'"> + <span style="color: red;"> + <xsl:value-of select="."/> + </span> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="."/> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <xsl:template match="status"> + <xsl:value-of select="."/> + <br/> + </xsl:template> + + <xsl:template match="label"> + <xsl:value-of select="."/> + <br/> + </xsl:template> + + <xsl:template match="referrers"> + <xsl:for-each select="url"> + <xsl:apply-templates select="."/> + </xsl:for-each> + </xsl:template> + + + <xsl:template match="/klinkstatus/link_list/link/referrers/url"> + <xsl:value-of select="."/> + <br/> + </xsl:template> + +</xsl:stylesheet> diff --git a/klinkstatus/klinkstatus.kdevelop b/klinkstatus/klinkstatus.kdevelop new file mode 100644 index 00000000..5abf4488 --- /dev/null +++ b/klinkstatus/klinkstatus.kdevelop @@ -0,0 +1,267 @@ +<?xml version = '1.0'?> +<kdevelop> + <general> + <author>Paulo Moura Guedes</author> + <email>moura@kdewebdev.org</email> + <version>0.2.2</version> + <projectmanagement>KDevKDEAutoProject</projectmanagement> + <primarylanguage>C++</primarylanguage> + <keywords> + <keyword>C++</keyword> + <keyword>Code</keyword> + <keyword>Qt</keyword> + <keyword>KDE</keyword> + </keywords> + <projectdirectory>.</projectdirectory> + <absoluteprojectpath>false</absoluteprojectpath> + <description></description> + <ignoreparts/> + <secondaryLanguages/> + <versioncontrol/> + </general> + <kdevautoproject> + <general> + <activetarget>src/klinkstatus</activetarget> + <useconfiguration>default</useconfiguration> + </general> + <run> + <mainprogram>klinkstatus/src/klinkstatus</mainprogram> + <directoryradio>build</directoryradio> + <customdirectory>/</customdirectory> + <programargs/> + <terminal>false</terminal> + <autocompile>true</autocompile> + <envvars/> + </run> + <configurations> + <optimized> + <builddir>optimized</builddir> + <ccompiler>kdevgccoptions</ccompiler> + <cxxcompiler>kdevgppoptions</cxxcompiler> + <f77compiler>kdevg77options</f77compiler> + <cxxflags>-O2 -g0</cxxflags> + </optimized> + <debug> + <configargs>--enable-debug=full</configargs> + <builddir>debug</builddir> + <ccompiler>kdevgccoptions</ccompiler> + <cxxcompiler>kdevgppoptions</cxxcompiler> + <f77compiler>kdevg77options</f77compiler> + <cxxflags>-DKDE_NO_COMPAT -DQT_NO_COMPAT -DQT_FATAL_ASSERT</cxxflags> + <topsourcedir>..</topsourcedir> + <cppflags/> + <ldflags/> + <ccompilerbinary/> + <cxxcompilerbinary/> + <f77compilerbinary/> + <cflags/> + <f77flags/> + <envvars> + <envvar value="kfilereplace kommander kimagemapeditor kxsldbg quanta lib" name="DO_NOT_COMPILE" /> + </envvars> + </debug> + <default> + <envvars> + <envvar value="kfilereplace kommander kimagemapeditor kxsldbg quanta lib" name="DO_NOT_COMPILE" /> + </envvars> + <configargs>--enable-debug=full</configargs> + <builddir></builddir> + <topsourcedir>..</topsourcedir> + <cppflags></cppflags> + <ldflags></ldflags> + <ccompiler>kdevgccoptions</ccompiler> + <cxxcompiler>kdevgppoptions</cxxcompiler> + <f77compiler>kdevg77options</f77compiler> + <ccompilerbinary></ccompilerbinary> + <cxxcompilerbinary></cxxcompilerbinary> + <f77compilerbinary></f77compilerbinary> + <cflags></cflags> + <cxxflags>-DQT_FATAL_ASSERT</cxxflags> + <f77flags></f77flags> + </default> + </configurations> + <make> + <envvars> + <envvar value="1" name="WANT_AUTOCONF_2_5" /> + <envvar value="1" name="WANT_AUTOMAKE_1_6" /> + </envvars> + <abortonerror>false</abortonerror> + <numberofjobs>1</numberofjobs> + <dontact>false</dontact> + <makebin/> + <prio>0</prio> + <runmultiplejobs>false</runmultiplejobs> + </make> + </kdevautoproject> + <kdevfileview> + <groups> + <group pattern="*.cpp;*.cxx;*.h;*.H" name="Sources" /> + <group pattern="*.ui" name="User Interface" /> + <group pattern="*.png" name="Icons" /> + <group pattern="*.po;*.ts" name="Translations" /> + <group pattern="*" name="Others" /> + <hidenonprojectfiles>false</hidenonprojectfiles> + <hidenonlocation>false</hidenonlocation> + </groups> + <tree> + <hidepatterns>*.o,*.lo,CVS</hidepatterns> + <hidenonprojectfiles>true</hidenonprojectfiles> + <showvcsfields>false</showvcsfields> + </tree> + </kdevfileview> + <kdevdoctreeview> + <ignoretocs> + <toc>ada</toc> + <toc>ada_bugs_gcc</toc> + <toc>bash</toc> + <toc>bash_bugs</toc> + <toc>clanlib</toc> + <toc>w3c-dom-level2-html</toc> + <toc>fortran_bugs_gcc</toc> + <toc>gnome1</toc> + <toc>gnustep</toc> + <toc>gtk</toc> + <toc>gtk_bugs</toc> + <toc>haskell</toc> + <toc>haskell_bugs_ghc</toc> + <toc>java_bugs_gcc</toc> + <toc>java_bugs_sun</toc> + <toc>pascal_bugs_fp</toc> + <toc>php</toc> + <toc>php_bugs</toc> + <toc>perl</toc> + <toc>perl_bugs</toc> + <toc>python</toc> + <toc>python_bugs</toc> + <toc>ruby</toc> + <toc>ruby_bugs</toc> + <toc>sdl</toc> + <toc>w3c-svg</toc> + <toc>sw</toc> + <toc>w3c-uaag10</toc> + <toc>wxwidgets_bugs</toc> + </ignoretocs> + <ignoreqt_xml> + <toc>qmake User Guide</toc> + </ignoreqt_xml> + <projectdoc> + <userdocDir>html/</userdocDir> + <apidocDir>html/</apidocDir> + </projectdoc> + <ignoredoxygen/> + <ignorekdocs/> + <ignoredevhelp/> + </kdevdoctreeview> + <kdevdebugger> + <general> + <dbgshell>libtool</dbgshell> + <programargs></programargs> + <gdbpath></gdbpath> + <configGdbScript></configGdbScript> + <runShellScript></runShellScript> + <runGdbScript></runGdbScript> + <breakonloadinglibs>true</breakonloadinglibs> + <separatetty>false</separatetty> + <floatingtoolbar>false</floatingtoolbar> + </general> + <display> + <staticmembers>false</staticmembers> + <demanglenames>true</demanglenames> + <outputradix>10</outputradix> + </display> + </kdevdebugger> + <kdevfilecreate> + <filetypes/> + <useglobaltypes> + <type ext="ui" /> + <type ext="cpp" /> + <type ext="h" /> + </useglobaltypes> + </kdevfilecreate> + <cppsupportpart> + <filetemplates> + <interfacesuffix>.h</interfacesuffix> + <implementationsuffix>.cpp</implementationsuffix> + </filetemplates> + </cppsupportpart> + <kdevcppsupport> + <codecompletion> + <includeGlobalFunctions>true</includeGlobalFunctions> + <includeTypes>true</includeTypes> + <includeEnums>true</includeEnums> + <includeTypedefs>true</includeTypedefs> + <automaticCodeCompletion>false</automaticCodeCompletion> + <automaticArgumentsHint>false</automaticArgumentsHint> + <automaticHeaderCompletion>true</automaticHeaderCompletion> + <codeCompletionDelay>250</codeCompletionDelay> + <argumentsHintDelay>400</argumentsHintDelay> + <headerCompletionDelay>250</headerCompletionDelay> + </codecompletion> + <references> + <pcs>Qt</pcs> + <pcs>KDElibs</pcs> + </references> + <qt> + <used>true</used> + <version>3</version> + <root>/usr/lib/qt-3.3</root> + </qt> + <creategettersetter> + <prefixGet></prefixGet> + <prefixSet>set</prefixSet> + <prefixVariable>m_,_</prefixVariable> + <parameterName>theValue</parameterName> + <inlineGet>true</inlineGet> + <inlineSet>true</inlineSet> + </creategettersetter> + </kdevcppsupport> + <dist> + <custom>false</custom> + <bzip>false</bzip> + <archname>%n-%v</archname> + <appname/> + <version>0.1.0</version> + <release/> + <vendor/> + <licence/> + <summary/> + <group/> + <packager/> + <description/> + <changelog/> + <devpackage>false</devpackage> + <docspackage>false</docspackage> + <appicon>false</appicon> + <arch>0</arch> + <genHTML>false</genHTML> + <useRPM>false</useRPM> + <ftpkde>false</ftpkde> + <appskde>false</appskde> + <url/> + </dist> + <kdevcvsservice> + <recursivewhenupdate>true</recursivewhenupdate> + <prunedirswhenupdate>true</prunedirswhenupdate> + <createdirswhenupdate>true</createdirswhenupdate> + <recursivewhencommitremove>true</recursivewhencommitremove> + <revertoptions>-C</revertoptions> + </kdevcvsservice> + <kdevcvs> + <cvsoptions>-f</cvsoptions> + <commitoptions/> + <addoptions/> + <logoptions/> + <updateoptions>-dP</updateoptions> + <removeoptions>-f</removeoptions> + <revertoptions>-C -d -P</revertoptions> + <diffoptions>-u3 -p</diffoptions> + <rshoptions/> + </kdevcvs> + <kdevdocumentation> + <projectdoc> + <docsystem/> + <docurl/> + <usermanualurl/> + </projectdoc> + </kdevdocumentation> +</kdevelop> diff --git a/klinkstatus/src/Makefile.am b/klinkstatus/src/Makefile.am new file mode 100644 index 00000000..590957a2 --- /dev/null +++ b/klinkstatus/src/Makefile.am @@ -0,0 +1,62 @@ +# set the include path for X, qt and KDE +INCLUDES = -I$(top_srcdir)/src/cfg -I./ui -I./ui/settings $(all_includes) + +# these are the headers for your project + + +# let automoc handle all of the meta source files (moc) +METASOURCES = AUTO + +# this Makefile creates both a KPart application and a KPart +######################################################################### +# APPLICATION SECTION +######################################################################### +# this is the program that gets installed. it's name is used for all +# of the other Makefile.am variables +bin_PROGRAMS = klinkstatus + +# the application source, library search path, and link libraries + +klinkstatus_LDFLAGS = $(KDE_RPATH) $(all_libraries) +klinkstatus_LDADD = $(LIB_KPARTS) + +xdg_apps_DATA = klinkstatus.desktop + +# this is where the shell's XML-GUI resource file goes +shellrcdir = $(kde_datadir)/klinkstatus +shellrc_DATA = klinkstatus_shell.rc + + +######################################################################### +# KPART SECTION +######################################################################### +kde_module_LTLIBRARIES = libklinkstatuspart.la + +# the Part's source, library search path, and link libraries + +libklinkstatuspart_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries) +libklinkstatuspart_la_LIBADD = ui/settings/libsettings.la \ + ui/libui.la engine/libengine.la \ + parser/libparser.la utils/libutils.la $(LIB_KPARTS) $(LIB_KFILE) + +# this is where the desktop file will go +partdesktopdir = $(kde_servicesdir) +partdesktop_DATA = klinkstatus_part.desktop + +# this is where the part's XML-GUI resource file goes +partrcdir = $(kde_datadir)/klinkstatuspart +partrc_DATA = klinkstatus_part.rc +noinst_HEADERS = klinkstatus.h klinkstatus_part.h global.h actionmanager.h +klinkstatus_SOURCES = klinkstatus.cpp main.cpp +libklinkstatuspart_la_SOURCES = klinkstatus_part.cpp global.cpp \ + actionmanager.cpp +SUBDIRS = cfg utils parser engine ui + +messages: rc.cpp + LIST=`find . -name \*.ui -o -name \*.kcfg -o -name \*.rc` ;\ + if test -n "$$LIST"; then $(EXTRACTRC) $$LIST >> rc.cpp; fi + LIST=`find . -name \*.h -o -name \*.hh -o -name \*.H -o -name \*.hxx -o -name \*.hpp -o -name \*.cpp -o -name \*.cc -o -name \*.cxx -o -name \*.ecpp -o -name \*.C`; \ + if test -n "$$LIST"; then \ + $(XGETTEXT) $$LIST -o $(podir)/klinkstatus.pot; \ + fi + diff --git a/klinkstatus/src/actionmanager.cpp b/klinkstatus/src/actionmanager.cpp new file mode 100644 index 00000000..9f60e7bf --- /dev/null +++ b/klinkstatus/src/actionmanager.cpp @@ -0,0 +1,273 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#include "actionmanager.h" + +#include <kactioncollection.h> +#include <kxmlguifactory.h> +#include <klocale.h> +#include <kaction.h> +#include <kguiitem.h> + +#include <qbuttongroup.h> + +#include "klinkstatus_part.h" +#include "ui/sessionwidget.h" +#include "ui/tabwidgetsession.h" +#include "cfg/klsconfig.h" + + +ActionManager* ActionManager::m_self = 0; + +ActionManager* ActionManager::getInstance() +{ + Q_ASSERT(m_self); + + return m_self; +} + +void ActionManager::setInstance(ActionManager* manager) +{ + Q_ASSERT(manager); + + m_self = manager; +} + +class ActionManager::ActionManagerPrivate +{ +public: + ActionManagerPrivate() + : part(0), tabWidgetSession(0), sessionWidget(0) + {} + + KActionCollection* actionCollection; + + KLinkStatusPart* part; + TabWidgetSession* tabWidgetSession; + SessionWidget* sessionWidget; +}; + +ActionManager::ActionManager(QObject *parent, const char *name) + : QObject(parent, name), d(new ActionManagerPrivate) +{} + +ActionManager::~ActionManager() +{ + delete d; + d = 0; +} + +void ActionManager::initPart(KLinkStatusPart* part) +{ + Q_ASSERT(part); + + if(d->part) + return; + + d->part = part; + d->actionCollection = part->actionCollection(); + + KAction* action = 0; + + // *************** File menu ********************* + + new KAction(i18n("New Link Check"), "filenew", + 0, + d->part, SLOT(slotNewLinkCheck()), + d->actionCollection, "new_link_check"); + + new KAction(i18n("Open URL..."), "fileopen", + 0, + d->part, SLOT(slotOpenLink()), + d->actionCollection, "open_link"); + + action = new KAction(i18n("Close Tab"), "fileclose", + 0, + d->part, SLOT(slotClose()), + d->actionCollection, "close_tab"); + action->setEnabled(false); + + // *************** Settings menu ********************* + + (void) new KAction(i18n("Configure KLinkStatus..."), "configure", + 0, d->part, SLOT(slotConfigureKLinkStatus()), + d->actionCollection, "configure_klinkstatus"); + + // *************** Help menu ********************* + + (void) new KAction(i18n("About KLinkStatus"), "klinkstatus", + 0, d->part, SLOT(slotAbout()), + d->actionCollection, "about_klinkstatus"); + + (void) new KAction(i18n("&Report Bug..."), 0, 0, d->part, + SLOT(slotReportBug()), d->actionCollection, "report_bug"); + + // *************** View menu ********************* +} + +void ActionManager::initTabWidget(TabWidgetSession* tabWidgetSession) +{ + Q_ASSERT(tabWidgetSession); + + if (d->tabWidgetSession) + return; + + d->tabWidgetSession = tabWidgetSession; + + // *************** File menu ********************* + + KAction* action = new KAction(i18n("E&xport Results as HTML..."), "filesave", 0, + d->tabWidgetSession, SLOT(slotExportAsHTML()), + d->actionCollection, "file_export_html"); + action->setEnabled(false); + + // *************** View menu ********************* + + // this action must be in the tabwidget because the slot can't be connected to a particular sessionWidget + KToggleAction* toggle_action = new KToggleAction(i18n("&Follow last Link checked"), + "make_kdevelop", "Ctrl+f", + d->tabWidgetSession, SLOT(slotFollowLastLinkChecked()), + d->actionCollection, "follow_last_link_checked"); + toggle_action->setChecked(KLSConfig::followLastLinkChecked()); + + // this action must be in the tabwidget because the slot can't be connected to a particular sessionWidget + toggle_action = new KToggleAction(i18n("&Hide Search Panel"), "bottom", "Ctrl+h", + d->tabWidgetSession, SLOT(slotHideSearchPanel()), + d->actionCollection, "hide_search_bar"); + KGuiItem item(i18n("&Show Search Panel"), "top", "Show Search Panel"); + toggle_action->setCheckedState(item); + + new KAction(i18n("&Reset Search Options"), "reload", "F5", + d->tabWidgetSession, SLOT(slotResetSearchOptions()), + d->actionCollection, "reset_search_bar"); + + // *************** Search menu ********************* + + toggle_action = new KToggleAction(i18n("&Start Search"), + "player_play", "Ctrl+s", + d->tabWidgetSession, SLOT(slotStartSearch()), + d->actionCollection, "start_search"); + toggle_action->setEnabled(false); + + toggle_action = new KToggleAction(i18n("&Pause Search"), + "player_pause", "Ctrl+p", + d->tabWidgetSession, SLOT(slotPauseSearch()), + d->actionCollection, "pause_search"); + toggle_action->setEnabled(false); + + action = new KAction(i18n("St&op Search"), + "player_stop", "Ctrl+c", + d->tabWidgetSession, SLOT(slotStopSearch()), + d->actionCollection, "stop_search"); + action->setEnabled(false); +} + +void ActionManager::initSessionWidget(SessionWidget* sessionWidget) +{ + Q_ASSERT(sessionWidget); + + if (d->sessionWidget) + return; + + d->sessionWidget = sessionWidget; + +} + +QWidget* ActionManager::container(const char* name) +{ + return d->part->factory()->container(name, d->part); +} + +KActionCollection* ActionManager::actionCollection() +{ + return d->actionCollection; +} + +KAction* ActionManager::action(const char* name, const char* classname) +{ + return d->actionCollection != 0 ? d->actionCollection->action(name, classname) : 0; +} + +void ActionManager::slotUpdateSessionWidgetActions(SessionWidget* page) +{ + KToggleAction* start_search_action_ = static_cast<KToggleAction*> (action("start_search")); + KToggleAction* pause_search_action_ = static_cast<KToggleAction*> (action("pause_search")); + KAction* stop_search_action_ = action("stop_search"); + + if(page->inProgress()) + { + Q_ASSERT(!page->stopped()); + + start_search_action_->setEnabled(true); + start_search_action_->setChecked(true); + + pause_search_action_->setEnabled(true); + + stop_search_action_->setEnabled(true); + } + if(page->paused()) + { + Q_ASSERT(page->inProgress()); + Q_ASSERT(!page->stopped()); + + start_search_action_->setEnabled(true); + start_search_action_->setChecked(true); + + pause_search_action_->setEnabled(true); + pause_search_action_->setChecked(true); + + stop_search_action_->setEnabled(true); + } + if(page->stopped()) + { + Q_ASSERT(!page->inProgress()); + Q_ASSERT(!page->paused()); + + start_search_action_->setEnabled(true); + start_search_action_->setChecked(false); + + pause_search_action_->setEnabled(false); + pause_search_action_->setChecked(false); + + stop_search_action_->setEnabled(false); + } + +// ____________________________________________________________________ + + KToggleAction* toggleAction = static_cast<KToggleAction*> (action("follow_last_link_checked")); + + if(!toggleAction) // the first sessionWidget is created before initSessionWidget is called + { + initSessionWidget(page); + toggleAction = static_cast<KToggleAction*> (action("follow_last_link_checked")); + } + Q_ASSERT(toggleAction); + toggleAction->setChecked(page->followLastLinkChecked()); + + toggleAction = static_cast<KToggleAction*> (action("hide_search_bar")); + Q_ASSERT(toggleAction); + toggleAction->setChecked(page->buttongroup_search->isHidden()); + + // ____________________________________________________________________ + + action("file_export_html")->setEnabled(!page->isEmpty()); +} + + +#include "actionmanager.moc" diff --git a/klinkstatus/src/actionmanager.h b/klinkstatus/src/actionmanager.h new file mode 100644 index 00000000..a33c37f4 --- /dev/null +++ b/klinkstatus/src/actionmanager.h @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#ifndef ACTIONMANAGER_H +#define ACTIONMANAGER_H + +#include <qobject.h> + +class KAction; +class KActionCollection; + +class SessionWidget; +class KLinkStatusPart; +class TabWidgetSession; + +/** + @author Paulo Moura Guedes <moura@kdewebdev.org> + + interface for accessing actions, popup menus etc. from widgets. +*/ +class ActionManager : public QObject +{ + Q_OBJECT +public: + ActionManager(QObject* parent=0, const char* name=0); + virtual ~ActionManager(); + + static ActionManager* getInstance(); + static void setInstance(ActionManager* manager); + + virtual KAction* action(const char* name, const char* classname=0); + virtual QWidget* container(const char* name); + + void initPart(KLinkStatusPart* part); + void initSessionWidget(SessionWidget* sessionWidget); + void initTabWidget(TabWidgetSession* tabWidgetSession); + +public slots: + void slotUpdateSessionWidgetActions(SessionWidget*); + +protected: + + KActionCollection* actionCollection(); + +private: + + static ActionManager* m_self; + + class ActionManagerPrivate; + ActionManagerPrivate* d; +}; + +#endif diff --git a/klinkstatus/src/cfg/Makefile.am b/klinkstatus/src/cfg/Makefile.am new file mode 100644 index 00000000..2e74b2ad --- /dev/null +++ b/klinkstatus/src/cfg/Makefile.am @@ -0,0 +1,6 @@ +kde_kcfg_DATA = klinkstatus.kcfg +METASOURCES = AUTO +libcfg_la_LDFLAGS = $(all_libraries) +noinst_LTLIBRARIES = libcfg.la +libcfg_la_SOURCES = dummy.cpp klsconfig.kcfgc +AM_CPPFLAGS=$(all_includes)
\ No newline at end of file diff --git a/klinkstatus/src/cfg/dummy.cpp b/klinkstatus/src/cfg/dummy.cpp new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/klinkstatus/src/cfg/dummy.cpp diff --git a/klinkstatus/src/cfg/klinkstatus.kcfg b/klinkstatus/src/cfg/klinkstatus.kcfg new file mode 100644 index 00000000..ae43c6b1 --- /dev/null +++ b/klinkstatus/src/cfg/klinkstatus.kcfg @@ -0,0 +1,91 @@ +<!DOCTYPE kcfg SYSTEM "http://www.kde.org/standards/kcfg/1.0/kcfg.dtd"> +<kcfg> + <kcfgfile name="klinkstatus_shell.rc" /> + + <group name="klinkstatus" > + + <entry key="MaxCountComboUrl" type="Int" > + <label>Maximum number of entries in the combo url.</label> + <default>50</default> + </entry> + + <entry key="MaxConnectionsNumber" type="Int" > + <label>Maximum number of simultaneous connections.</label> + <default>5</default> + </entry> + + <entry key="TimeOut" type="Int" > + <label>Timeout on getting an URL.</label> + <default>35</default> + </entry> + + <entry key="ComboUrlHistory" type="StringList" > + <label>History of combo url.</label> + </entry> + + <entry key="RecursiveCheck" type="Bool" > + <label>Whether to do a recursive check.</label> + <default>true</default> + </entry> + + <entry key="Depth" type="Int" > + <label>Maximum depth to check.</label> + <default>0</default> + </entry> + + <entry key="CheckParentFolders" type="Bool" > + <label>Whether to check parent folders.</label> + <default>true</default> + </entry> + + <entry key="CheckExternalLinks" type="Bool" > + <label>Whether to check external links.</label> + <default>true</default> + </entry> + + <entry key="RememberCheckSettings" type="Bool" > + <label>Whether to remeber the check settings like depth, and so on, on exit.</label> + <default>false</default> + </entry> + + <entry key="UseQuantaUrlPreviewPrefix" type="Bool" > + <label>Whether preview prefix in Quanta project is used to set the URL to check.</label> + <default>true</default> + </entry> + + <entry key="DisplayTreeView" type="Bool" > + <label>Whether to display a tree view or a flat view in the results view.</label> + <default>true</default> + </entry> + + <entry key="DisplayFlatView" type="Bool" > + <label>Whether to display a tree view or a flat view in the results view.</label> + <default>false</default> + </entry> + + <entry key="AutoAdjustColumnWidth" type="Bool" > + <label>Whether to automatically adjust the width of the result columns (Not used).</label> + <default>false</default> + </entry> + + <entry key="FollowLastLinkChecked" type="Bool" > + <label>Whether the viewport of the result view should follow the last link checked.</label> + <default>true</default> + </entry> + + <entry key="ShowMarkupStatus" type="Bool" > + <label>Whether the user can see if the markup is valid by showing a column with an icon indicator.</label> + <default>false</default> + </entry> + + <entry key="SendIdentification" type="Bool" > + <label>Whether to send an User-Agent in HTTP requests.</label> + <default>true</default> + </entry> + + <entry name="UserAgent" type="String"> + <label>Defines the HTTP User-Agent to send.</label> + </entry> + + </group> +</kcfg> diff --git a/klinkstatus/src/cfg/klsconfig.kcfgc b/klinkstatus/src/cfg/klsconfig.kcfgc new file mode 100644 index 00000000..089abab7 --- /dev/null +++ b/klinkstatus/src/cfg/klsconfig.kcfgc @@ -0,0 +1,7 @@ +# Code generation options for kconfig_compiler +File=klinkstatus.kcfg +#IncludeFiles=defines.h +ClassName=KLSConfig +Singleton=true +#CustomAdditions=true +Mutators=true diff --git a/klinkstatus/src/engine/Makefile.am b/klinkstatus/src/engine/Makefile.am new file mode 100644 index 00000000..1bd3ba88 --- /dev/null +++ b/klinkstatus/src/engine/Makefile.am @@ -0,0 +1,9 @@ +INCLUDES = -I$(top_srcdir)/src/ui $(all_includes) +METASOURCES = AUTO +noinst_HEADERS = linkchecker.h linkstatus.h linkstatus_impl.h searchmanager.h \ + searchmanager_impl.h linkfilter.h +libengine_la_LDFLAGS = $(all_libraries) +noinst_LTLIBRARIES = libengine.la +libengine_la_SOURCES = linkchecker.cpp linkstatus.cpp searchmanager.cpp \ + linkfilter.cpp +libengine_la_LIBADD = $(LIB_KHTML) diff --git a/klinkstatus/src/engine/linkchecker.cpp b/klinkstatus/src/engine/linkchecker.cpp new file mode 100644 index 00000000..bcc503ad --- /dev/null +++ b/klinkstatus/src/engine/linkchecker.cpp @@ -0,0 +1,703 @@ +/*************************************************************************** + * Copyright (C) 2004 by Puto Moura * + * mojo@localhost.localdomain * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#include "linkchecker.h" +#include "searchmanager.h" +#include "../utils/utils.h" +#include "../parser/htmlparser.h" + +#include <qstring.h> +#include <qtimer.h> +#include <qtextcodec.h> +#include <qcstring.h> + +#include <kio/netaccess.h> +#include <kio/global.h> +#include <kio/job.h> +#include <kio/scheduler.h> +#include <kio/slave.h> +#include <kmimetype.h> +#include <kapplication.h> +#include <klocale.h> +#include <khtml_part.h> +#include <dom/html_misc.h> +#include <dom/dom_node.h> +#include <dom/dom_string.h> + + +int LinkChecker::count_ = 0; + +LinkChecker::LinkChecker(LinkStatus* linkstatus, int time_out, + QObject *parent, const char *name) + : QObject(parent, name), search_manager_(0), + linkstatus_(linkstatus), t_job_(0), time_out_(time_out), checker_(0), document_charset_(), + redirection_(false), header_checked_(false), finnished_(false), + parsing_(false), is_charset_checked_(false), has_defined_charset_(false) +{ + Q_ASSERT(linkstatus_); + Q_ASSERT(!linkstatus_->checked()); + + kdDebug(23100) << endl << ++count_ << ": " << "Checking " << linkstatus_->absoluteUrl().url() << endl; +} + +LinkChecker::~LinkChecker() +{} + +void LinkChecker::setSearchManager(SearchManager* search_manager) +{ + Q_ASSERT(search_manager); + search_manager_ = search_manager; +} + +void LinkChecker::check() +{ + Q_ASSERT(!finnished_); + + KURL url(linkStatus()->absoluteUrl()); + Q_ASSERT(url.isValid()); + + if(url.hasRef()) { + KMimeType::Ptr mimeType = KMimeType::findByURL(url); + if(mimeType->is("text/html") || mimeType->is("application/xml")) { + checkRef(); + return; + } + } + + t_job_ = KIO::get(url, false, false); + + t_job_->addMetaData("PropagateHttpHeader", "true"); // to have the http header + + if (linkstatus_->parent()) { + t_job_->addMetaData("referrer", linkstatus_->parent()->absoluteUrl().prettyURL()); + } + + if(search_manager_->sendIdentification()) + { + t_job_->addMetaData("SendUserAgent", "true"); + t_job_->addMetaData("UserAgent", search_manager_->userAgent()); + } + else + t_job_->addMetaData("SendUserAgent", "false"); + + + QObject::connect(t_job_, SIGNAL(data(KIO::Job *, const QByteArray &)), + this, SLOT(slotData(KIO::Job *, const QByteArray &))); + QObject::connect(t_job_, SIGNAL(mimetype(KIO::Job *, const QString &)), + this, SLOT(slotMimetype(KIO::Job *, const QString &))); + QObject::connect(t_job_, SIGNAL(result(KIO::Job *)), + this, SLOT(slotResult(KIO::Job *))); + QObject::connect(t_job_, SIGNAL(redirection(KIO::Job *, const KURL &)), + this, SLOT(slotRedirection(KIO::Job *, const KURL &))); + + QTimer::singleShot( time_out_ * 1000, this, SLOT(slotTimeOut()) ); + + t_job_->setInteractive(false); +} + +void LinkChecker::slotTimeOut() +{ + if(!finnished_ && !parsing_) + { + kdDebug(23100) << "timeout: " << linkstatus_->absoluteUrl().url() << endl; + if(t_job_ && t_job_->slave()) + kdDebug(23100) << " - " << t_job_->slave() << "/" << t_job_->slave()->slave_pid() << endl; + else + kdDebug(23100) << endl; + + +// Q_ASSERT(t_job_); // can happen: e.g. bad result signal + if(t_job_->error() != KIO::ERR_USER_CANCELED) + { + linkstatus_->setErrorOccurred(true); + linkstatus_->setChecked(true); + linkstatus_->setError(i18n("Timeout")); + linkstatus_->setStatus(LinkStatus::TIMEOUT); + + killJob(); + finnish(); + } + } +} + +void LinkChecker::slotMimetype (KIO::Job* /*job*/, const QString &type) +{ + if(finnished_) + return; + +// kdDebug(23100) << "LinkChecker::slotMimetype:" << type << "-> " << linkstatus_->absoluteUrl().url() +// << " - " << t_job_->slave() << "/" << t_job_->slave()->slave_pid() << endl; + + Q_ASSERT(t_job_); + + LinkStatus* ls = 0; +/* if(redirection_) + ls = linkStatus()->redirection(); + else*/ + ls = linkstatus_; + Q_ASSERT(ls); + + ls->setMimeType(type); + KURL url = ls->absoluteUrl(); + + // we doesn't do nothing if file is http or https because we need the header + // which is only available in the data response + if(!t_job_->error()) // if a error happened let result() handle that + { + if(ls->onlyCheckHeader()) + { + //kdDebug(23100) << "only check header: " << ls->absoluteUrl().prettyURL() << endl; + + // file is OK (http can have an error page though job->error() is false) + if(!url.protocol().startsWith("http")) + { + ls->setStatusText("OK"); + ls->setStatus(LinkStatus::SUCCESSFULL); + + killJob(); + finnish(); + } + } + else // !ls->onlyCheckHeader() + { + //kdDebug(23100) << "NOT only check header: " << ls->absoluteUrl().prettyURL() << endl; + + // file is OK (http can have an error page though job->error() is false) + if(!url.protocol().startsWith("http")) // if not, it have to go trough slotData to get the http header + { + // it's not an html page, so we don't want the file content + if(type != "text/html"/* && type != "text/plain"*/) + { + //kdDebug(23100) << "mimetype: " << type << endl; + ls->setStatusText("OK"); + ls->setStatus(LinkStatus::SUCCESSFULL); + + killJob(); + finnish(); + } + } + } + } +} + +void LinkChecker::slotData(KIO::Job* /*job*/, const QByteArray& data) +{ + if(finnished_) + return; + + kdDebug(23100) << "LinkChecker::slotData -> " << linkstatus_->absoluteUrl().url() + << " - " << t_job_->slave() << "/" << t_job_->slave()->slave_pid() << endl; + + Q_ASSERT(t_job_); + + LinkStatus* ls = 0; +/* if(redirection_) + ls = linkStatus()->redirection(); + else*/ + ls = linkstatus_; + Q_ASSERT(ls); + + KURL url = ls->absoluteUrl(); + + if(!t_job_->error()) + { + if(ls->onlyCheckHeader()) + { + Q_ASSERT(header_checked_ == false); + // the job should have been killed in slotMimetype + Q_ASSERT(url.protocol() == "http" || url.protocol() == "https"); + + // get the header and quit + if(url.protocol().startsWith("http")) + { + // get the header + ls->setHttpHeader(getHttpHeader(t_job_)); + + if(t_job_->isErrorPage()) + ls->setIsErrorPage(true); + + if(header_checked_) + { + killJob(); + linkstatus_->setStatus(getHttpStatus()); + linkstatus_->setChecked(true); + finnish(); + return; + } + } + } + else + { + if(url.protocol().startsWith("http")) + { + if(!header_checked_) + { + ls->setHttpHeader(getHttpHeader(t_job_)); + } + if(ls->mimeType() != "text/html" && header_checked_) + { + //kdDebug(23100) << "mimetype of " << ls->absoluteUrl().prettyURL() << ": " << ls->mimeType() << endl; + ls->setStatus(getHttpStatus()); + killJob(); + finnish(); // if finnish is called before kill what you get is a segfault, don't know why + return; + } + else if(t_job_->isErrorPage() && header_checked_) + { + //kdDebug(23100) << "ERROR PAGE" << endl; + ls->setIsErrorPage(true); + ls->setStatus(getHttpStatus()); + killJob(); + finnish(); + return; + } + } + else + { + Q_ASSERT(ls->mimeType() == "text/html"); + } + if(!is_charset_checked_) + findDocumentCharset(data); + + QTextCodec* codec = 0; + if(has_defined_charset_) + codec = QTextCodec::codecForName(document_charset_); + if(!codec) + codec = QTextCodec::codecForName("iso8859-1"); // default + + doc_html_ += codec->toUnicode(data); + } + } +} + +void LinkChecker::findDocumentCharset(QString const& doc) +{ + Q_ASSERT(!is_charset_checked_); + + is_charset_checked_ = true; // only check the first stream of data + + if(header_checked_) + document_charset_ = linkstatus_->httpHeader().charset(); + + // try to look in the meta elements + if(document_charset_.isNull() || document_charset_.isEmpty()) + document_charset_ = HtmlParser::findCharsetInMetaElement(doc); + + if(!document_charset_.isNull() && !document_charset_.isEmpty()) + has_defined_charset_ = true; +} + +// only comes here if an error happened or in case of a clean html page +// if onlyCheckHeader is false +void LinkChecker::slotResult(KIO::Job* /*job*/) +{ + if(finnished_) + return; + + kdDebug(23100) << "LinkChecker::slotResult -> " << linkstatus_->absoluteUrl().url() << endl; + + Q_ASSERT(t_job_); + if(!t_job_) + return; + + if(redirection_) { + if(!processRedirection(redirection_url_)) { + t_job_ = 0; + linkstatus_->setChecked(true); + finnish(); + return; + } + } + + KIO::TransferJob* job = t_job_; + t_job_ = 0; + + emit jobFinnished(this); + + if(job->error() == KIO::ERR_USER_CANCELED) + { + // FIXME This can happen! If the job is non interactive... + kdWarning(23100) << endl << "Job killed quietly, yet signal result was emited..." << endl; + kdDebug(23100) << linkstatus_->toString() << endl; + finnish(); + return; + } + + LinkStatus* ls = 0; + if(redirection_) + ls = linkStatus()->redirection(); + else + ls = linkstatus_; + Q_ASSERT(ls); + + if(!(!ls->onlyCheckHeader() || + job->error() || + !header_checked_)) + kdWarning(23100) << ls->toString() << endl; + + Q_ASSERT(!ls->onlyCheckHeader() || job->error() || !header_checked_); + + if(ls->isErrorPage()) + kdWarning(23100) << "\n\n" << ls->toString() << endl << endl; + + Q_ASSERT(!job->isErrorPage()); + + if(job->error()) + { + kdDebug(23100) << "Job error: " << job->errorString() << endl; + kdDebug(23100) << "Job error code: " << job->error() << endl; + + if(job->error() == KIO::ERR_IS_DIRECTORY) + { + ls->setStatusText("OK"); + ls->setStatus(LinkStatus::SUCCESSFULL); + } + else + { + ls->setErrorOccurred(true); + if(job->error() == KIO::ERR_SERVER_TIMEOUT) + ls->setStatus(LinkStatus::TIMEOUT); + else + ls->setStatus(LinkStatus::BROKEN); + + if(job->errorString().isEmpty()) + kdWarning(23100) << "\n\nError string is empty, error = " << job->error() << "\n\n\n"; + if(job->error() != KIO::ERR_NO_CONTENT) + ls->setError(job->errorString()); + else + ls->setError(i18n("No Content")); + } + } + + else + { + if(!ls->absoluteUrl().protocol().startsWith("http")) { + ls->setStatusText("OK"); + ls->setStatus(LinkStatus::SUCCESSFULL); + } + else + { + if(!header_checked_) + { + kdDebug(23100) << "\n\nheader not received... checking again...\n\n\n"; + //check again + check(); + return; + } + Q_ASSERT(header_checked_); + + ls->setStatus(getHttpStatus()); + } + + if(!doc_html_.isNull() && !doc_html_.isEmpty()) + { + ls->setDocHtml(doc_html_); + + parsing_ = true; + HtmlParser parser(doc_html_); + + if(parser.hasBaseUrl()) + ls->setBaseURI(KURL(parser.baseUrl().url())); + if(parser.hasTitle()) + ls->setHtmlDocTitle(parser.title().attributeTITLE()); + + ls->setChildrenNodes(parser.nodes()); + parsing_ = false; + } + } + finnish(); +} + + +void LinkChecker::slotRedirection (KIO::Job* /*job*/, const KURL &url) +{ + kdDebug(23100) << "LinkChecker::slotRedirection -> " << + linkstatus_->absoluteUrl().url() << " -> " << url.url() << endl; +// << " - " << t_job_->slave() << "/" << t_job_->slave()->slave_pid() << endl; + + redirection_ = true; + redirection_url_ = url; +} + +bool LinkChecker::processRedirection(KURL const& toUrl) +{ + if(finnished_) + return true; + + kdDebug(23100) << "LinkChecker::processRedirection -> " << linkstatus_->absoluteUrl().url() << " -> " << toUrl.url() << endl; + + Q_ASSERT(t_job_); + Q_ASSERT(linkstatus_->absoluteUrl().protocol().startsWith("http")); + Q_ASSERT(redirection_); + + linkstatus_->setHttpHeader(getHttpHeader(t_job_, false)); + linkstatus_->setIsRedirection(true); + linkstatus_->setStatusText("redirection"); + linkstatus_->setStatus(LinkStatus::HTTP_REDIRECTION); + linkstatus_->setChecked(true); + + LinkStatus* ls_red = new LinkStatus(*linkstatus_); + ls_red->setAbsoluteUrl(toUrl); + ls_red->setRootUrl(linkstatus_->rootUrl()); + + if(!linkstatus_->onlyCheckHeader()) + ls_red->setOnlyCheckHeader(false); + + linkstatus_->setRedirection(ls_red); + ls_red->setParent(linkstatus_); + ls_red->setOriginalUrl(toUrl.url()); + + Q_ASSERT(search_manager_); + + if(search_manager_->localDomain(ls_red->absoluteUrl())) + ls_red->setExternalDomainDepth(-1); + else + { + if(search_manager_->localDomain(linkstatus_->absoluteUrl())) + ls_red->setExternalDomainDepth(linkstatus_->externalDomainDepth() + 1); + else + ls_red->setExternalDomainDepth(linkstatus_->externalDomainDepth()); + } + + if(!toUrl.isValid() || search_manager_->existUrl(toUrl, linkstatus_->absoluteUrl())) + { + ls_red->setChecked(false); + return false; + } + else + { + ls_red->setChecked(true); + return true; + } +} + +void LinkChecker::finnish() +{ + Q_ASSERT(!t_job_); + + if(!finnished_) + { + kdDebug(23100) << "LinkChecker::finnish -> " << linkstatus_->absoluteUrl().url() << endl; + + finnished_ = true; + + if(redirection_) + Q_ASSERT(linkstatus_->checked()); + else + linkstatus_->setChecked(true); + + emit transactionFinished(linkstatus_, this); + } +} + +HttpResponseHeader LinkChecker::getHttpHeader(KIO::Job* /*job*/, bool remember_check) +{ + //kdDebug(23100) << "LinkChecker::getHttpHeader -> " << linkstatus_->absoluteUrl().url() << endl; + + Q_ASSERT(!finnished_); + Q_ASSERT(t_job_); + + QString header_string = t_job_->queryMetaData("HTTP-Headers"); + // Q_ASSERT(!header_string.isNull() && !header_string.isEmpty()); +// kdDebug(23100) << "HTTP header: " << endl << header_string << endl; +// kdDebug(23100) << "Keys: " << HttpResponseHeader(header_string).keys() << endl; +// kdDebug(23100) << "Content-type: " << HttpResponseHeader(header_string).contentType() << endl; +// kdDebug(23100) << "Content-type: " << HttpResponseHeader(header_string).value("content-type") << endl; + + if(header_string.isNull() || header_string.isEmpty()) + { + header_checked_ = false; + kdWarning(23100) << "header_string.isNull() || header_string.isEmpty(): " + << linkstatus_->toString() << endl; + } + else if(remember_check) + header_checked_ = true; + + return HttpResponseHeader(header_string); +} + +void LinkChecker::checkRef() +{ + KURL url(linkStatus()->absoluteUrl()); + Q_ASSERT(url.hasRef()); + + QString ref = url.ref(); + if(ref == "" || ref == "top") { + linkstatus_->setStatusText("OK"); + linkstatus_->setStatus(LinkStatus::SUCCESSFULL); + finnish(); + return; + } + + QString url_base; + LinkStatus const* ls_parent = 0; + int i_ref = -1; + + if(linkStatus()->originalUrl().startsWith("#")) + ls_parent = linkStatus()->parent(); + + else + { + i_ref = url.url().find("#"); + url_base = url.url().left(i_ref); + //kdDebug(23100) << "url_base: " << url_base << endl; + + Q_ASSERT(search_manager_); + + ls_parent = search_manager_->linkStatus(url_base); + } + + if(ls_parent) + checkRef(ls_parent); + else + { + url = KURL::fromPathOrURL(url.url().left(i_ref)); + checkRef(url); + } +} + +void LinkChecker::checkRef(KURL const& url) +{ + Q_ASSERT(search_manager_); + + QString url_string = url.url(); + KHTMLPart* html_part = search_manager_->htmlPart(url_string); + if(!html_part) + { + kdDebug() << "new KHTMLPart: " + url_string << endl; + + html_part = new KHTMLPart(); + html_part->setOnlyLocalReferences(true); + + QString tmpFile; + if(KIO::NetAccess::download(url, tmpFile, 0)) + { + QString doc_html = FileManager::read(tmpFile); + html_part->begin(); + html_part->write(doc_html); + html_part->end(); + + KIO::NetAccess::removeTempFile(tmpFile); + } + else + { + kdDebug(23100) << KIO::NetAccess::lastErrorString() << endl; + } + + search_manager_->addHtmlPart(url_string, html_part); + } + + if(hasAnchor(html_part, linkStatus()->absoluteUrl().ref())) + { + linkstatus_->setStatusText("OK"); + linkstatus_->setStatus(LinkStatus::SUCCESSFULL); + } + else + { + linkstatus_->setErrorOccurred(true); + linkstatus_->setError(i18n( "Link destination not found." )); + linkstatus_->setStatus(LinkStatus::BROKEN); + } + + finnish(); +} + +void LinkChecker::checkRef(LinkStatus const* linkstatus_parent) +{ + Q_ASSERT(search_manager_); + + QString url_string = linkstatus_parent->absoluteUrl().url(); + KHTMLPart* html_part = search_manager_->htmlPart(url_string); + if(!html_part) + { + kdDebug() << "new KHTMLPart: " + url_string << endl; + + html_part = new KHTMLPart(); + html_part->setOnlyLocalReferences(true); + + html_part->begin(); + html_part->write(linkstatus_parent->docHtml()); + html_part->end(); + + search_manager_->addHtmlPart(url_string, html_part); + } + + if(hasAnchor(html_part, linkStatus()->absoluteUrl().ref())) + { + linkstatus_->setStatusText("OK"); + linkstatus_->setStatus(LinkStatus::SUCCESSFULL); + } + else + { + linkstatus_->setErrorOccurred(true); + linkstatus_->setError(i18n( "Link destination not found." )); + linkstatus_->setStatus(LinkStatus::BROKEN); + } + + finnish(); +} + +bool LinkChecker::hasAnchor(KHTMLPart* html_part, QString const& anchor) +{ + DOM::HTMLDocument htmlDocument = html_part->htmlDocument(); + DOM::HTMLCollection anchors = htmlDocument.anchors(); + + DOM::DOMString name_ref(anchor); + Q_ASSERT(!name_ref.isNull()); + + DOM::Node node = anchors.namedItem(name_ref); + if(node.isNull()) + { + node = htmlDocument.getElementById(name_ref); + } + + if(!node.isNull()) + return true; + else + return false; +} + +void LinkChecker::killJob() +{ + if(!t_job_) + return; + + KIO::TransferJob* aux = t_job_; + t_job_ = 0; + aux->disconnect(this); + aux->kill(true); // quietly +} + +LinkStatus::Status LinkChecker::getHttpStatus() const +{ + QString status_code = QString::number(linkstatus_->httpHeader().statusCode()); + + if(status_code[0] == '2') + return LinkStatus::SUCCESSFULL; + else if(status_code[0] == '3') + return LinkStatus::HTTP_REDIRECTION; + else if(status_code[0] == '4') + return LinkStatus::HTTP_CLIENT_ERROR; + else if(status_code[0] == '5') + return LinkStatus::HTTP_SERVER_ERROR; + else + return LinkStatus::UNDETERMINED; +} + +#include "linkchecker.moc" diff --git a/klinkstatus/src/engine/linkchecker.h b/klinkstatus/src/engine/linkchecker.h new file mode 100644 index 00000000..a992e5fd --- /dev/null +++ b/klinkstatus/src/engine/linkchecker.h @@ -0,0 +1,128 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#ifndef LINKCHECKER_H +#define LINKCHECKER_H + +#include <qobject.h> +#include <qthread.h> +#include <qstring.h> + +#include <kio/jobclasses.h> +class KHTMLPart; + +#include "../parser/http.h" +#include "linkstatus.h" +class SearchManager; + +#include <iostream> +using namespace std; + +/** +@author Paulo Moura Guedes +*/ +class LinkChecker : public QObject +{ + Q_OBJECT +public: + LinkChecker(LinkStatus* linkstatus, int time_out = 50, + QObject *parent = 0, const char *name = 0); + ~LinkChecker(); + + //virtual void run(); + void check(); + void setSearchManager(SearchManager* search_manager); + + LinkStatus const* linkStatus() const; + + static bool hasAnchor(KHTMLPart* html_part, QString const& anchor); + +signals: + + void transactionFinished(const LinkStatus * linkstatus, + LinkChecker * checker); + void jobFinnished(LinkChecker * checker); + +protected slots: + + void slotData(KIO::Job *, const QByteArray &data); + void slotRedirection (KIO::Job *, const KURL &url); + void slotMimetype(KIO::Job *, const QString &type); + void slotResult(KIO::Job* job); + void slotTimeOut(); + +protected: + + void finnish(); + HttpResponseHeader getHttpHeader(KIO::Job* job, bool remember_check = true); + void checkRef(); // #... + +private: + + LinkStatus::Status getHttpStatus() const; + void checkRef(LinkStatus const* linkstatus_parent); + void checkRef(KURL const& url); + void killJob(); + /** + * @param url + * @return false if the redirection was already checked by the search manager + */ + bool processRedirection(KURL const& url); + + void findDocumentCharset(QString const& data); + +private: + + SearchManager* search_manager_; + LinkStatus* const linkstatus_; + KIO::TransferJob* t_job_; + int time_out_; + LinkChecker* checker_; + QString document_charset_; +/* A redirection has appened, with the current URL. Several redirections + can happen until the final URL is reached.*/ + bool redirection_; + KURL redirection_url_; + QString doc_html_; + bool header_checked_; + bool finnished_; + bool parsing_; + + /** + * Whether the charset of the document is already checked. + * (e.g. <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>) + */ + bool is_charset_checked_; + /** + * Wheter the page define the enconding (latin1, utf8, etc). + * According to the spec (http://www.w3.org/TR/html4/charset.html), + * it first check the server response and then the info in the html meta element. + */ + bool has_defined_charset_; + + static int count_; // debug attribute that counts how many links were checked +}; + +inline LinkStatus const* LinkChecker::linkStatus() const +{ + return linkstatus_; +} + + +#endif diff --git a/klinkstatus/src/engine/linkfilter.cpp b/klinkstatus/src/engine/linkfilter.cpp new file mode 100644 index 00000000..4d15f2e6 --- /dev/null +++ b/klinkstatus/src/engine/linkfilter.cpp @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#include "linkfilter.h" + +#include "linkstatus.h" + + +LinkMatcher::LinkMatcher(QString const& text, ResultView::Status status) + : m_text(text), m_status(status) +{ +} + +LinkMatcher::~LinkMatcher() +{ +} + +bool LinkMatcher::matches(LinkStatus const& link ) const +{ +/* kdDebug() << link.absoluteUrl().url() << endl; + kdDebug() << link.label() << endl; + kdDebug() << link.absoluteUrl().url().contains(m_text) << endl; + kdDebug() << link.label().contains(m_text) << endl; + */ + return (link.absoluteUrl().url().contains(m_text, false) || link.label().contains(m_text, false)) && + ResultView::displayableWithStatus(&link, m_status); +} + + + diff --git a/klinkstatus/src/engine/linkfilter.h b/klinkstatus/src/engine/linkfilter.h new file mode 100644 index 00000000..84da16cb --- /dev/null +++ b/klinkstatus/src/engine/linkfilter.h @@ -0,0 +1,49 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#ifndef LINKFILTER_H +#define LINKFILTER_H + +#include "../ui/resultview.h" + +/** + @author Paulo Moura Guedes <moura@kdewebdev.org> +*/ +class LinkMatcher +{ +public: + LinkMatcher(QString const& text, ResultView::Status status); + ~LinkMatcher(); + + bool matches(LinkStatus const& link) const; + + void setText(const QString& text) { m_text = text; } + QString text() const { return m_text; } + + void setStatus(ResultView::Status status) { m_status = status; } + ResultView::Status status() const { return m_status; } + + bool nullFilter() const { return m_text.isEmpty() && m_status == ResultView::none; } + +private: + QString m_text; + ResultView::Status m_status; +}; + +#endif diff --git a/klinkstatus/src/engine/linkstatus.cpp b/klinkstatus/src/engine/linkstatus.cpp new file mode 100644 index 00000000..c8b359ed --- /dev/null +++ b/klinkstatus/src/engine/linkstatus.cpp @@ -0,0 +1,214 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "linkstatus.h" +#include "../parser/node.h" +#include "../ui/treeview.h" + +#include <klocale.h> +#include <kcharsets.h> + +#include <qdom.h> + + +LinkStatus::~LinkStatus() +{ + //kdDebug(23100) << "|"; + + for(uint i = 0; i != children_nodes_.size(); ++i) + { + if(children_nodes_[i]) + { + delete children_nodes_[i]; + children_nodes_[i] = 0; + } + } + + children_nodes_.clear(); + + if(isRedirection()) + { + if(redirection_) + { + delete redirection_; + redirection_ = 0; + } + } +} + +void LinkStatus::reset() +{ + depth_ = -1; + external_domain_depth_ = -1; + is_root_ = false; + error_occurred_ = false; + is_redirection_ = false; + checked_ = false; + only_check_header_ = true; + malformed_ = false; + Q_ASSERT(!node_); + has_base_URI_ = false; + label_ = ""; + absolute_url_ = ""; + doc_html_ = ""; + http_header_ = HttpResponseHeader(); + error_ = ""; + + for(uint i = 0; i != children_nodes_.size(); ++i) + { + if(children_nodes_[i]) + { + delete children_nodes_[i]; + children_nodes_[i] = 0; + } + } + + children_nodes_.clear(); + + if(isRedirection()) + { + if(redirection_) + { + delete redirection_; + redirection_ = 0; + } + } + Q_ASSERT(!parent_); + base_URI_ = ""; +} + +QString const LinkStatus::toString() const +{ + QString aux; + + if(!is_root_) + { + Q_ASSERT(parent_); + aux += i18n( "Parent: %1" ).arg( parent()->absoluteUrl().prettyURL() ) + "\n"; + } + Q_ASSERT(!original_url_.isNull()); + + aux += i18n( "URL: %1" ).arg( absoluteUrl().prettyURL() ) + "\n"; + aux += i18n( "Original URL: %1" ).arg( originalUrl() ) + "\n"; + if(node()) + aux += i18n( "Node: %1" ).arg( node()->content() ) + "\n"; + + return aux; +} + + +LinkStatus* LinkStatus::lastRedirection(LinkStatus* ls) +{ + if(ls->isRedirection()) + if(ls->redirection()) + return lastRedirection(ls->redirection()); + else + return ls; + else + return ls; +} + +void LinkStatus::loadNode() +{ + Q_ASSERT(node_); + + setOriginalUrl(node_->url()); + setLabel(node_->linkLabel()); + + if(malformed()) + { + setErrorOccurred(true); + setError(i18n( "Malformed" )); + setStatus(LinkStatus::MALFORMED); + kdDebug(23100) << "Malformed:" << endl; + kdDebug(23100) << "Node: " << node()->content() << endl; + //kdDebug(23100) << toString() << endl; // probable segfault + } +} + +bool LinkStatus::malformed() const // don't inline please (#include "node.h") +{ + return (malformed_ || node_->malformed()); +} + +void LinkStatus::setChildrenNodes(vector<Node*> const& nodes) // don't inline please (#include "node.h") +{ + children_nodes_.reserve(nodes.size()); + children_nodes_ = nodes; +} + +void LinkStatus::setMalformed(bool flag) +{ + malformed_ = flag; + if(flag) + { + setErrorOccurred(true); + setError(i18n( "Malformed" )); + setStatus(LinkStatus::MALFORMED); + kdDebug(23100) << "Malformed!" << endl; + kdDebug(23100) << node()->content() << endl; + //kdDebug(23100) << toString() << endl; // probable segfault + } + else if(error() == i18n( "Malformed" )) + { + setErrorOccurred(false); + setError(""); + setStatus(LinkStatus::UNDETERMINED); + } +} + +void LinkStatus::save(QDomElement& element) const +{ + QDomElement child_element = element.ownerDocument().createElement("link"); + + // <url> + QDomElement tmp_1 = element.ownerDocument().createElement("url"); + tmp_1.appendChild(element.ownerDocument().createTextNode(absoluteUrl().prettyURL())); + child_element.appendChild(tmp_1); + + // <status> + tmp_1 = element.ownerDocument().createElement("status"); + tmp_1.setAttribute("broken", + ResultView::displayableWithStatus(this, ResultView::bad) ? + "true" : "false"); + tmp_1.appendChild(element.ownerDocument().createTextNode(statusText())); + child_element.appendChild(tmp_1); + + // <label> + tmp_1 = element.ownerDocument().createElement("label"); + tmp_1.appendChild(element.ownerDocument().createTextNode(KCharsets::resolveEntities(label()))); + child_element.appendChild(tmp_1); + + // <referers> + tmp_1 = element.ownerDocument().createElement("referrers"); + + for(QValueVector<KURL>::const_iterator it = referrers_.begin(); it != referrers_.end(); ++it) + { + QDomElement tmp_2 = element.ownerDocument().createElement("url"); + tmp_2.appendChild(element.ownerDocument().createTextNode(it->prettyURL())); + + tmp_1.appendChild(tmp_2); + } + Q_ASSERT(!referrers_.isEmpty()); + child_element.appendChild(tmp_1); + + element.appendChild(child_element); +} + diff --git a/klinkstatus/src/engine/linkstatus.h b/klinkstatus/src/engine/linkstatus.h new file mode 100644 index 00000000..e7567460 --- /dev/null +++ b/klinkstatus/src/engine/linkstatus.h @@ -0,0 +1,187 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef LINKSTATUS_H +#define LINKSTATUS_H + +#include "../parser/http.h" +#include "../utils/mvector.h" + +#include <kurl.h> +#include <klocale.h> +#include <kdebug.h> +class TreeView; +class TreeViewItem; + +#include <qstring.h> +#include <qobject.h> +#include <qvaluevector.h> +class QDomElement; + +#include <vector> +#include <iostream> + +using namespace std; + + +class Node; + +class LinkStatus +{ +public: + + enum Status { + UNDETERMINED, + SUCCESSFULL, + BROKEN, + HTTP_REDIRECTION, + HTTP_CLIENT_ERROR, + HTTP_SERVER_ERROR, + TIMEOUT, + NOT_SUPPORTED, + MALFORMED + }; + + LinkStatus(); + LinkStatus(KURL const& absolute_url); + LinkStatus(Node* node, LinkStatus* parent); + ~LinkStatus(); + + void save(QDomElement& element) const; + + void reset(); + void setRootUrl(KURL const& url); + void setStatus(Status status); + void setDepth(uint depth); + void setParent(LinkStatus* parent); + void setOriginalUrl(QString const& url_original); + void setLabel(QString const& label); + void setAbsoluteUrl(KURL const& url_absoluto); + void setDocHtml(QString const& doc_html); + void setHttpHeader(HttpResponseHeader const& cabecalho_http); + void setStatusText(QString const& statusText); // FIXME Legacy. This should be eliminated in favor of LinkStatus::Status + void setError(QString const& error); + void setIsRoot(bool flag); + void setErrorOccurred(bool houve_error); + void setIsRedirection(bool e_redirection); + void setRedirection(LinkStatus* redirection); + void setNode(Node* node); + void setChildrenNodes(vector<Node*> const& nodes); + void addChildNode(Node* node); + void reserveMemoryForChildrenNodes(int n); + void setChecked(bool flag); + void setExternalDomainDepth(int p); + void setOnlyCheckHeader(bool flag); + void setMalformed(bool flag = true); + void setHasBaseURI(bool flag = true); + void setHasHtmlDocTitle(bool flag = true); + void setBaseURI(KURL const& base_url); + void setHtmlDocTitle(QString const& title); + void setIgnored(bool flag = true); + void setMimeType(QString const& mimetype); + void setIsErrorPage(bool flag); + void setIsLocalRestrict(bool flag); + void setTreeViewItem(TreeViewItem* tree_view_item); + void addReferrer(KURL const& url); + + KURL const& rootUrl() const; + Status const& status() const; + uint depth() const; + bool local() const; // linkstatus.paradigma.co.pt == paradigma.co.pt + bool isLocalRestrict() const; // linkstatus.paradigma.co.pt != paradigma.co.pt + LinkStatus const* parent() const; + QString const& originalUrl() const; + QString const& label() const; + KURL const& absoluteUrl() const; + QString const& docHtml() const; + HttpResponseHeader const& httpHeader() const; + HttpResponseHeader& httpHeader(); + QString statusText() const; // FIXME Legacy. This should be eliminated in favor of LinkStatus::Status + QString const& error() const; + bool isRoot() const; + bool errorOccurred() const; + bool isRedirection() const; + LinkStatus* redirection() const; + Node* node() const; + vector<Node*> const& childrenNodes() const; + QString const toString() const; + bool checked() const; + int externalDomainDepth() const; + bool onlyCheckHeader() const; + bool malformed() const; + bool hasBaseURI() const; + bool hasHtmlDocTitle() const; + KURL const& baseURI() const; + QString const& htmlDocTitle() const; + bool ignored() const; + bool redirectionExists(KURL const& url) const; // to avoid cyclic links + QString mimeType() const; + bool isErrorPage() const; + TreeViewItem* treeViewItem() const; + QValueVector<KURL> const& referrers() const; + + static LinkStatus* lastRedirection(LinkStatus* ls); + +private: + + /** + Load some atributes in function of his parent node. + */ + void loadNode(); + +private: + + KURL root_url_; // The URL which made the search start + Status status_; + int depth_; + int external_domain_depth_; // Para se poder escolher explorar domains diferentes ate n depth + QString original_url_; + QString label_; + KURL absolute_url_; + QString doc_html_; + HttpResponseHeader http_header_; + QString status_text_; // FIXME Legacy. This should be eliminated in favor of LinkStatus::Status + QString error_; + bool is_root_; + bool error_occurred_; + bool is_redirection_; + vector<Node*> children_nodes_; + LinkStatus* parent_; + LinkStatus* redirection_; + bool checked_; + bool only_check_header_; + bool malformed_; + Node* node_; + bool has_base_URI_; + bool has_html_doc_title_; + KURL base_URI_; + QString html_doc_title_; + bool ignored_; + QString mimetype_; + bool is_error_page_; + bool is_local_restrict_; + TreeViewItem* tree_view_item_; + QValueVector<KURL> referrers_; +}; + +#include "../parser/url.h" +#include "linkstatus_impl.h" + +#endif diff --git a/klinkstatus/src/engine/linkstatus_impl.h b/klinkstatus/src/engine/linkstatus_impl.h new file mode 100644 index 00000000..3359664c --- /dev/null +++ b/klinkstatus/src/engine/linkstatus_impl.h @@ -0,0 +1,417 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +inline LinkStatus::LinkStatus() + : status_(LinkStatus::UNDETERMINED), depth_(-1), external_domain_depth_(-1), is_root_(false), + error_occurred_(false), is_redirection_(false), parent_(0), redirection_(0), checked_(false), + only_check_header_(true), malformed_(false), + node_(0), has_base_URI_(false), has_html_doc_title_(false), ignored_(false), + mimetype_(""), is_error_page_(false), tree_view_item_(0) +{} + +inline LinkStatus::LinkStatus(KURL const& absolute_url) + : status_(LinkStatus::UNDETERMINED), depth_(-1), external_domain_depth_(-1), is_root_(false), + error_occurred_(false), is_redirection_(false), parent_(0), redirection_(0), checked_(false), + only_check_header_(true), malformed_(false), + node_(0), has_base_URI_(false), has_html_doc_title_(false), ignored_(false), + mimetype_(""), is_error_page_(false), tree_view_item_(0) +{ + setAbsoluteUrl(absolute_url); +} + +inline LinkStatus::LinkStatus(Node* node, LinkStatus* parent) + : status_(LinkStatus::UNDETERMINED), depth_(-1), external_domain_depth_(-1), is_root_(false), + error_occurred_(false), is_redirection_(false), parent_(0), redirection_(0), checked_(false), + only_check_header_(true), malformed_(false), + node_(node), has_base_URI_(false), has_html_doc_title_(false), ignored_(false), + mimetype_(""), is_error_page_(false), tree_view_item_(0) +{ + loadNode(); + + setDepth(parent->depth() + 1); + setParent(parent); + setRootUrl(parent->rootUrl()); +} + +inline void LinkStatus::setRootUrl(KURL const& url) +{ + root_url_ = url; +} + +inline void LinkStatus::setStatus(Status status) +{ + status_ = status; +} + +inline void LinkStatus::setDepth(uint depth) +{ + depth_ = depth; +} + +inline void LinkStatus::setParent(LinkStatus* parent) +{ + Q_ASSERT(parent); + + parent_ = parent; + addReferrer(parent->absoluteUrl()); +} + +inline void LinkStatus::setAbsoluteUrl(KURL const& url_absoluto) +{ + absolute_url_ = url_absoluto; +} + +inline void LinkStatus::setOriginalUrl(QString const& url_original) +{ + original_url_ = url_original; +} + +inline void LinkStatus::setLabel(QString const& label) +{ + label_ = label; +} + +inline void LinkStatus::setDocHtml(QString const& doc_html) +{ + Q_ASSERT(!doc_html.isEmpty()); + doc_html_ = doc_html; +} + +inline void LinkStatus::setHttpHeader(HttpResponseHeader const& cabecalho_http) +{ + http_header_ = cabecalho_http; +} + +inline void LinkStatus::setStatusText(QString const& status) +{ + Q_ASSERT(!status.isEmpty()); + status_text_ = status; +} + +inline void LinkStatus::setError(QString const& error) +{ + Q_ASSERT(!error.isEmpty()); + error_ = error; +} + +inline void LinkStatus::setErrorOccurred(bool houve_error) +{ + error_occurred_ = houve_error; +} + +inline void LinkStatus::setIsRoot(bool flag) +{ + is_root_ = flag; + label_ = i18n("ROOT"); +} + +inline void LinkStatus::setRedirection(LinkStatus* redirection) +{ + Q_ASSERT(redirection != NULL); + Q_ASSERT(isRedirection()); + redirection_ = redirection; +} + +inline void LinkStatus::setIsRedirection(bool e_redirection) +{ + is_redirection_ = e_redirection; +} + +inline void LinkStatus::addChildNode(Node* node) +{ + children_nodes_.push_back(node); +} + +inline void LinkStatus::reserveMemoryForChildrenNodes(int n) +{ + Q_ASSERT(n > 0); + children_nodes_.reserve(n); +} + +inline void LinkStatus::setChecked(bool flag) +{ + checked_ = flag; +} + +inline void LinkStatus::setExternalDomainDepth(int p) +{ + Q_ASSERT(p >= -1); + external_domain_depth_ = p; +} + +inline void LinkStatus::setOnlyCheckHeader(bool flag) +{ + only_check_header_= flag; +} + +inline void LinkStatus::setHasBaseURI(bool flag) +{ + has_base_URI_ = flag; +} + +inline void LinkStatus::setHasHtmlDocTitle(bool flag) +{ + has_html_doc_title_ = flag; +} + +inline void LinkStatus::setBaseURI(KURL const& base_url) +{ + if(!base_url.isValid()) + { + kdWarning(23100) << "base url not valid: " << endl + << "parent: " << parent()->absoluteUrl().prettyURL() << endl + << "url: " << absoluteUrl().prettyURL() << endl + << "base url resolved: " << base_url.prettyURL() << endl; + } + + Q_ASSERT(base_url.isValid()); + has_base_URI_ = true; + base_URI_ = base_url; +} + +inline void LinkStatus::setHtmlDocTitle(QString const& title) +{ + if(title.isNull() || title.isEmpty()) + { + kdError(23100) << "HTML doc title is null or empty!" << endl + << toString() << endl; + } + Q_ASSERT(!title.isNull() && !title.isEmpty()); + + has_html_doc_title_ = true; + html_doc_title_ = title; +} + +inline void LinkStatus::setIgnored(bool flag) +{ + ignored_ = flag; +} + +inline void LinkStatus::setMimeType(QString const& mimetype) +{ + Q_ASSERT(!mimetype.isNull() && !mimetype.isEmpty()); + mimetype_ = mimetype; +} + +inline void LinkStatus::setIsErrorPage(bool flag) +{ + is_error_page_ = flag; +} + +inline void LinkStatus::setIsLocalRestrict(bool flag) +{ + is_local_restrict_ = flag; +} + +inline void LinkStatus::setTreeViewItem(TreeViewItem* tree_view_item) +{ + Q_ASSERT(tree_view_item); + tree_view_item_ = tree_view_item; +} + +inline void LinkStatus::addReferrer(KURL const& url) +{ + Q_ASSERT(url.isValid()); + + referrers_.push_back(url); +} + + + + +inline KURL const& LinkStatus::rootUrl() const +{ + return root_url_; +} + +inline LinkStatus::Status const& LinkStatus::status() const +{ + return status_; +} + +inline uint LinkStatus::depth() const +{ + return depth_; +} + +inline bool LinkStatus::local() const +{ + return external_domain_depth_ == -1; +} + +inline bool LinkStatus::isLocalRestrict() const +{ + return is_local_restrict_; +} + +inline LinkStatus const* LinkStatus::parent() const +{ + return parent_; +} + +inline QString const& LinkStatus::originalUrl() const +{ + return original_url_; +} + +inline QString const& LinkStatus::label() const +{ + return label_; +} + +inline KURL const& LinkStatus::absoluteUrl() const +{ + return absolute_url_; +} + +inline QString const& LinkStatus::docHtml() const +{ + return doc_html_; +} + +inline HttpResponseHeader const& LinkStatus::httpHeader() const +{ + return http_header_; +} + +inline HttpResponseHeader& LinkStatus::httpHeader() +{ + return http_header_; +} + +inline QString LinkStatus::statusText() const +{ + if(errorOccurred()) + return error(); + else if(!absoluteUrl().protocol().startsWith("http")) + return status_text_; + else + { + QString string_code = QString::number(httpHeader().statusCode()); + if(absoluteUrl().hasRef()) // ref URL + return status_text_; + else if(string_code == "200"/* or string_code == "304"*/) + return "OK"; + else + return string_code; + } +} + +inline QString const& LinkStatus::error() const +{ + return error_; +} + +inline bool LinkStatus::isRoot() const +{ + return is_root_; +} + +inline bool LinkStatus::errorOccurred() const +{ + return error_occurred_; +} + +inline bool LinkStatus::isRedirection() const +{ + return is_redirection_; +} + +inline LinkStatus* LinkStatus::redirection() const +{ + Q_ASSERT(isRedirection()); + + return redirection_; +} + +inline Node* LinkStatus::node() const +{ + //Q_ASSERT(node_); + return node_; +} + +inline vector<Node*> const& LinkStatus::childrenNodes() const +{ + return children_nodes_; +} + +inline bool LinkStatus::checked() const +{ + return checked_; +} + +inline int LinkStatus::externalDomainDepth() const +{ + return external_domain_depth_; +} + +inline bool LinkStatus::onlyCheckHeader() const +{ + return only_check_header_; +} + +inline bool LinkStatus::hasBaseURI() const +{ + return has_base_URI_; +} + +inline bool LinkStatus::hasHtmlDocTitle() const +{ + return has_html_doc_title_; +} + +inline KURL const& LinkStatus::baseURI() const +{ + Q_ASSERT(hasBaseURI()); + return base_URI_; +} + +inline QString const& LinkStatus::htmlDocTitle() const +{ + Q_ASSERT(has_html_doc_title_); + return html_doc_title_; +} + +inline bool LinkStatus::ignored() const +{ + return ignored_; +} + +inline QString LinkStatus::mimeType() const +{ + Q_ASSERT(!mimetype_.isNull()); + return mimetype_; +} + +inline bool LinkStatus::isErrorPage() const +{ + return is_error_page_; +} + +inline TreeViewItem* LinkStatus::treeViewItem() const +{ + return tree_view_item_; +} + +inline QValueVector<KURL> const& LinkStatus::referrers() const +{ + return referrers_; +} + diff --git a/klinkstatus/src/engine/searchmanager.cpp b/klinkstatus/src/engine/searchmanager.cpp new file mode 100644 index 00000000..81562a7a --- /dev/null +++ b/klinkstatus/src/engine/searchmanager.cpp @@ -0,0 +1,916 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include <kapplication.h> +#include <kdebug.h> +#include <klocale.h> +#include <khtml_part.h> +#include <kprotocolmanager.h> + +#include <qstring.h> +#include <qvaluevector.h> +#include <qdom.h> + +#include <iostream> +#include <unistd.h> + +#include "searchmanager.h" +#include "../parser/mstring.h" +#include "../cfg/klsconfig.h" + + +SearchManager::SearchManager(int max_simultaneous_connections, int time_out, + QObject *parent, const char *name) + : QObject(parent, name), + max_simultaneous_connections_(max_simultaneous_connections), has_document_root_(false), + depth_(-1), current_depth_(0), external_domain_depth_(0), + current_node_(0), current_index_(0), links_being_checked_(0), + finished_connections_(max_simultaneous_connections_), + maximum_current_connections_(-1), general_domain_(false), + checked_general_domain_(false), time_out_(time_out), current_connections_(0), + send_identification_(true), canceled_(false), searching_(false), checked_links_(0), ignored_links_(0), + check_parent_dirs_(true), check_external_links_(true), check_regular_expressions_(false), + number_of_level_links_(0), number_of_links_to_check_(0) +{ + root_.setIsRoot(true); + + if (KLSConfig::userAgent().isEmpty()) { + KLSConfig::setUserAgent(KProtocolManager::defaultUserAgent()); + } + user_agent_ = KLSConfig::userAgent(); +} + +void SearchManager::reset() +{ + kdDebug(23100) << "SearchManager::reset()" << endl; + + //Q_ASSERT(not links_being_checked_); + + root_.reset(); + cleanItems(); + depth_ = -1; + current_depth_ = 0; + current_node_ = 0; + current_index_ = 0; + finished_connections_ = max_simultaneous_connections_; + domain_ = ""; + maximum_current_connections_ = -1; + general_domain_ = false; + checked_general_domain_ = false; + check_regular_expressions_ = false; + current_connections_ = 0; + canceled_ = false; + searching_ = false; + checked_links_ = 0; + if(KLSConfig::userAgent().isEmpty()) { + KLSConfig::setUserAgent(KProtocolManager::defaultUserAgent()); + } + user_agent_ = KLSConfig::userAgent(); + + removeHtmlParts(); +} + +SearchManager::~SearchManager() +{ + reset(); +} + +void SearchManager::cleanItems() +{ + for(uint i = 0; i != search_results_.size(); ++i) + { + for(uint j = 0; j != search_results_[i].size() ; ++j) + { + for(uint l = 0; l != (search_results_[i])[j].size(); ++l) + { + if(((search_results_[i])[j])[l] != 0) + { + delete ((search_results_[i])[j])[l]; + ((search_results_[i])[j])[l] = 0; + } + else + kdDebug(23100) << "LinkStatus NULL!!" << endl; + } + search_results_[i][j].clear(); + } + search_results_[i].clear(); + } + search_results_.clear(); + kdDebug(23100) << endl; +} + +void SearchManager::startSearch(KURL const& root, SearchMode const& modo) +{ + canceled_ = false; + + //time_.restart(); + time_.start(); + + Q_ASSERT(root.isValid()); + //Q_ASSERT(root.protocol() == "http" || root.protocol() == "https"); + + if(root.hasHost() && (domain_.isNull() || domain_.isEmpty())) + { + setDomain(root.host() + root.directory()); + kdDebug(23100) << "Domain: " << domain_ << endl; + } + root_.setIsRoot(true); + root_.setDepth(0); + root_.setOriginalUrl(root.prettyURL()); + root_.setAbsoluteUrl(root); + root_.setOnlyCheckHeader(false); + root_.setRootUrl(root); + + search_mode_ = modo; + if(modo == depth) + Q_ASSERT(depth_ != -1); + else if(modo == domain) + Q_ASSERT(depth_ == -1); + else + Q_ASSERT(depth_ != -1); + + searching_ = true; + + //Q_ASSERT(domain_ != QString::null); + checkRoot(); +} + +void SearchManager::resume() +{ + searching_ = true; + canceled_ = false; + continueSearch(); +} + +void SearchManager::finnish() +{ + searching_ = false; + while(links_being_checked_) + { + kdDebug(23100) << "links_being_checked_: " << links_being_checked_ << endl; + sleep(1); + } + emit signalSearchFinished(); +} + +void SearchManager::pause() +{ + searching_ = false; + while(links_being_checked_) + { + kdDebug(23100) << "links_being_checked_: " << links_being_checked_ << endl; + sleep(1); + } + emit signalSearchPaused(); +} + +void SearchManager::cancelSearch() +{ + canceled_ = true; +} + +void SearchManager::checkRoot() +{ + LinkChecker* checker = new LinkChecker(&root_, time_out_, this, "link_checker"); + checker->setSearchManager(this); + + connect(checker, SIGNAL(transactionFinished(const LinkStatus *, LinkChecker *)), + this, SLOT(slotRootChecked(const LinkStatus *, LinkChecker *))); + /* + connect(checker, SIGNAL(jobFinnished(LinkChecker *)), + this, SLOT(slotLinkCheckerFinnished(LinkChecker *))); + */ + checker->check(); +} + +void SearchManager::slotRootChecked(const LinkStatus * link, LinkChecker * checker) +{ + kdDebug(23100) << "SearchManager::slotRootChecked:" << endl; + kdDebug(23100) << link->absoluteUrl().url() << " -> " << + LinkStatus::lastRedirection(&root_)->absoluteUrl().url() << endl; + + Q_ASSERT(checked_links_ == 0); + Q_ASSERT(search_results_.size() == 0); + + ++checked_links_; + //kdDebug(23100) << "++checked_links_: SearchManager::slotRootChecked" << endl; + emit signalRootChecked(link, checker); + + if(search_mode_ != depth || depth_ > 0) + { + current_depth_ = 1; + + vector<LinkStatus*> no = children(LinkStatus::lastRedirection(&root_)); + + emit signalLinksToCheckTotalSteps(no.size()); + + vector< vector<LinkStatus*> > nivel; + nivel.push_back(no); + + search_results_.push_back(nivel); + + if(search_results_.size() != 1) + { + kdDebug(23100) << "search_results_.size() != 1:" << endl; + kdDebug(23100) << "size: " << search_results_.size() << endl; + } + Q_ASSERT(search_results_.size() == 1); + + if(no.size() > 0) + { + startSearch(); + } + else + { + kdDebug(23100) << "SearchManager::slotRootChecked#1" << endl; + finnish(); + } + } + + else + { + Q_ASSERT(search_results_.size() == 0); + kdDebug(23100) << "SearchManager::slotRootChecked#2" << endl; + finnish(); + } + + delete checker; + checker = 0; +} + +vector<LinkStatus*> SearchManager::children(LinkStatus* link) +{ + vector<LinkStatus*> children; + + if(!link || link->absoluteUrl().hasRef()) + return children; + + vector<Node*> const& nodes = link->childrenNodes(); + + int count = 0; + for(uint i = 0; i != nodes.size(); ++i) + { + ++count; + + Node* node = nodes[i]; + KURL url; + if(node->url().isEmpty()) + url = ""; + else + url = Url::normalizeUrl(node->url(), *link, documentRoot().path()); + + if( (node->isLink() && + checkable(url, *link) && + !Url::existUrl(url, children) && + !node->url().isEmpty()) + || + node->malformed() ) + { + LinkStatus* ls = new LinkStatus(node, link); + ls->setAbsoluteUrl(url); + + if(localDomain(ls->absoluteUrl())) + ls->setExternalDomainDepth(-1); + else + ls->setExternalDomainDepth(link->externalDomainDepth() + 1); + + //ls->setIsLocalRestrict(localDomain(url)); + ls->setIsLocalRestrict(ls->local()); // @todo clean this nonsense + + if(!validUrl(url)) { + ls->setMalformed(true); + ls->setErrorOccurred(true); + } + + ls->setOnlyCheckHeader(onlyCheckHeader(ls)); + + if(link->externalDomainDepth() > external_domain_depth_) + { + kdDebug(23100) << "link->externalDomainDepth() > external_domain_depth_: " + << link->externalDomainDepth() << endl; + kdDebug(23100) << "link: " << endl << link->toString() << endl; + kdDebug(23100) << "child: " << endl << ls->toString() << endl; + } + Q_ASSERT(link->externalDomainDepth() <= external_domain_depth_); + + children.push_back(ls); + } + if(count == 50) + { + kapp->processEvents(); + count = 0; + } + } + + return children; +} + +bool SearchManager::existUrl(KURL const& url, KURL const& url_parent) const +{ + if(url.prettyURL().isEmpty() || root_.originalUrl() == url.prettyURL()) + return true; + + for(uint i = 0; i != search_results_.size(); ++i) + for(uint j = 0; j != search_results_[i].size(); ++j) + for(uint l = 0; l != (search_results_[i])[j].size(); ++l) + { + LinkStatus* tmp = search_results_[i][j][l]; + Q_ASSERT(tmp); + if(tmp->absoluteUrl() == url) + { // URL exists + QValueVector<KURL> referrers(tmp->referrers()); + + // Add new referrer + for(uint i = 0; i != referrers.size(); ++i) + { + if(referrers[i] == url_parent) + return true; + } + tmp->addReferrer(url_parent); + + return true; + } + } + + return false; +} + +LinkStatus const* SearchManager::linkStatus(QString const& s_url) const +{ + Q_ASSERT(!s_url.isEmpty()); + + if(root_.absoluteUrl().url() == s_url) + return &root_; + + int count = 0; + for(uint i = 0; i != search_results_.size(); ++i) + for(uint j = 0; j != search_results_[i].size(); ++j) + for(uint l = 0; l != (search_results_[i])[j].size(); ++l) + { + ++count; + + LinkStatus* ls = search_results_[i][j][l]; + Q_ASSERT(ls); + if(ls->absoluteUrl().url() == s_url && ls->checked()) + return ls; + + if(count == 50) + { + count = 0; + kapp->processEvents(); + } + + } + + return 0; +} + + +void SearchManager::startSearch() +{ + Q_ASSERT(current_depth_ == 1); + Q_ASSERT(search_results_[current_depth_ - 1].size() == 1); + Q_ASSERT(current_node_ == 0); + + if( (int)current_depth_ <= depth_ || search_mode_ != depth ) + checkVectorLinks(nodeToAnalize()); + else + { + kdDebug(23100) << "Search Finished! (SearchManager::comecaPesquisa)" << endl; + finnish(); + } +} + +void SearchManager::continueSearch() +{ + Q_ASSERT(!links_being_checked_); + + vector<LinkStatus*> const& no = nodeToAnalize(); + + if((uint)current_index_ < no.size()) + checkVectorLinks(no); + + else + { + current_index_ = 0; + kdDebug(23100) << "Next node_____________________\n\n"; + ++current_node_; + if( (uint)current_node_ < (search_results_[current_depth_ - 1]).size() ) + checkVectorLinks(nodeToAnalize()); + else + { + kdDebug(23100) << "Next Level_____________________________________________________________________________________\n\n\n"; + if(search_mode_ == SearchManager::domain || + current_depth_ < depth_) + { + current_node_ = 0; + ++current_depth_; + + addLevel(); + + if( (uint)current_depth_ == search_results_.size() ) + checkVectorLinks(nodeToAnalize()); + else + { + kdDebug(23100) << "Search Finished! (SearchManager::continueSearch#1)" << endl; + finnish(); + } + } + else + { + kdDebug(23100) << "Search Finished! (SearchManager::continueSearch#2)" << endl; + finnish(); + } + } + } +} + +vector<LinkStatus*> const& SearchManager::nodeToAnalize() const +{ + Q_ASSERT( (uint)current_depth_ == search_results_.size() ); + Q_ASSERT( (uint)current_node_ < (search_results_[current_depth_ - 1]).size() ); + + return (search_results_[current_depth_ - 1])[current_node_]; +} + +void SearchManager::checkVectorLinks(vector<LinkStatus*> const& links) +{ + checkLinksSimultaneously(chooseLinks(links)); +} + +vector<LinkStatus*> SearchManager::chooseLinks(vector<LinkStatus*> const& links) +{ + vector<LinkStatus*> escolha; + for(int i = 0; i != max_simultaneous_connections_; ++i) + { + if((uint)current_index_ < links.size()) + escolha.push_back(links[current_index_++]); + } + return escolha; +} + +void SearchManager::checkLinksSimultaneously(vector<LinkStatus*> const& links) +{ + Q_ASSERT(finished_connections_ <= max_simultaneous_connections_); + finished_connections_ = 0; + links_being_checked_ = 0; + maximum_current_connections_ = -1; + + if(links.size() < (uint)max_simultaneous_connections_) + maximum_current_connections_ = links.size(); + else + maximum_current_connections_ = max_simultaneous_connections_; + + for(uint i = 0; i != links.size(); ++i) + { + LinkStatus* ls(links[i]); + Q_ASSERT(ls); + + QString protocol = ls->absoluteUrl().protocol(); + + ++links_being_checked_; + Q_ASSERT(links_being_checked_ <= max_simultaneous_connections_); + + if(ls->malformed()) + { + Q_ASSERT(ls->errorOccurred()); + Q_ASSERT(ls->status() == LinkStatus::MALFORMED); + + ls->setChecked(true); + slotLinkChecked(ls, 0); + } + + else if(ls->absoluteUrl().prettyURL().contains("javascript:", false)) + { + ++ignored_links_; + ls->setIgnored(true); + ls->setErrorOccurred(true); + ls->setError(i18n( "Javascript not supported" )); + ls->setStatus(LinkStatus::NOT_SUPPORTED); + ls->setChecked(true); + slotLinkChecked(ls, 0); + } + /* + else if(!(protocol == "http" || protocol == "https")) + { + ++ignored_links_; + ls->setIgnored(true); + ls->setErrorOccurred(true); + ls->setError(i18n("Protocol %1 not supported").arg(protocol)); + ls->setStatus(LinkStatus::MALFORMED); + ls->setChecked(true); + slotLinkChecked(ls, 0); + } + */ + else + { + LinkChecker* checker = new LinkChecker(ls, time_out_, this, "link_checker"); + checker->setSearchManager(this); + + connect(checker, SIGNAL(transactionFinished(const LinkStatus *, LinkChecker *)), + this, SLOT(slotLinkChecked(const LinkStatus *, LinkChecker *))); + /* + connect(checker, SIGNAL(jobFinnished(LinkChecker *)), + this, SLOT(slotLinkCheckerFinnished(LinkChecker *))); + */ + checker->check(); + } + } +} + +void SearchManager::slotLinkChecked(const LinkStatus * link, LinkChecker * checker) +{ + kdDebug(23100) << "SearchManager::slotLinkChecked:" << endl; +// kdDebug(23100) << link->absoluteUrl().url() << " -> " << +// LinkStatus::lastRedirection((const_cast<LinkStatus*> (link)))->absoluteUrl().url() << endl; + + Q_ASSERT(link); + emit signalLinkChecked(link, checker); + ++checked_links_; + ++finished_connections_; + --links_being_checked_; + + if(links_being_checked_ < 0) + kdDebug(23100) << link->toString() << endl; + Q_ASSERT(links_being_checked_ >= 0); + + if(canceled_ && searching_ && !links_being_checked_) + { + pause(); + } + + else if(!canceled_ && finished_connections_ == maximumCurrentConnections() ) + { + continueSearch(); + return; + } + /* + delete checker; + checker = 0; + */ +} + +void SearchManager::addLevel() +{ + search_results_.push_back(vector< vector <LinkStatus*> >()); + vector< vector <LinkStatus*> >& ultimo_nivel(search_results_[search_results_.size() - 2]); + + number_of_level_links_ = 0; + number_of_links_to_check_ = 0; + uint end = ultimo_nivel.size(); + + for(uint i = 0; i != end; ++i) // nodes + { + uint end_sub1 = ultimo_nivel[i].size(); + for(uint j = 0; j != end_sub1; ++j) // links + ++number_of_level_links_; + } + + if(number_of_level_links_) + emit signalAddingLevelTotalSteps(number_of_level_links_); + + for(uint i = 0; i != end; ++i) // nodes + { + uint end_sub1 = ultimo_nivel[i].size(); + for(uint j = 0; j != end_sub1; ++j) // links + { + vector <LinkStatus*> f(children( LinkStatus::lastRedirection(((ultimo_nivel[i])[j])) )); + if(f.size() != 0) + { + search_results_[search_results_.size() - 1].push_back(f); + number_of_links_to_check_ += f.size(); + } + + emit signalAddingLevelProgress(); +// kapp->processEvents(); + } + } + if( (search_results_[search_results_.size() - 1]).size() == 0 ) + search_results_.pop_back(); + else + emit signalLinksToCheckTotalSteps(number_of_links_to_check_); +} + +bool SearchManager::checkable(KURL const& url, LinkStatus const& link_parent) const +{ + if(existUrl(url, link_parent.absoluteUrl())) + return false; + + if(!checkableByDomain(url, link_parent)) + return false; + + if(!check_parent_dirs_) + { + if(Url::parentDir(root_.absoluteUrl(), url)) + return false; + } + if(!check_external_links_) + { + if(Url::externalLink(root_.absoluteUrl(), url)) + return false; + } + if(check_regular_expressions_) + { + Q_ASSERT(!reg_exp_.isEmpty()); + + if(reg_exp_.search(url.url()) != -1) + return false; + } + + //kdDebug(23100) << "url " << url.url() << " is checkable!" << endl; + return true; +} + +bool SearchManager::checkableByDomain(KURL const& url, LinkStatus const& link_parent) const +{ + bool result = false; + if(localDomain(url)) + result = true; + else if( (link_parent.externalDomainDepth() + 1) < external_domain_depth_ ) + result = true; + else + result = false; + /* + if(!result) + kdDebug(23100) << "\n\nURL " << url.url() << " is not checkable by domain\n\n" << endl; + */ + return result; +} +/* +bool SearchManager::localDomain(KURL const& url) const + { + KURL url_root = root_.absoluteUrl(); + + if(url_root.protocol() != url.protocol()) + return false; + + if(url_root.hasHost()) + { + if(generalDomain()) + { + return equalHost(domain_, url.host()); + } + else + { + vector<QString> referencia = tokenizeWordsSeparatedBy(domain_, QChar('/')); + vector<QString> a_comparar = tokenizeWordsSeparatedBy(url.host() + url.directory(), QChar('/')); + + if(a_comparar.size() < referencia.size()) + return false; + else + { + for(uint i = 0; i != referencia.size(); ++i) + { + if(i == 0) + { // host, deal with specific function + if(!equalHost(referencia[i], a_comparar[i], !check_parent_dirs_)) + return false; + } + else if(referencia[i] != a_comparar[i]) + return false; + } + } + return true; + } + } + else if(checkParentDirs()) + return true; + else + return url_root.isParentOf(url); + } +*/ + +/** + The same as SearchManager::localDomain(), but only for http or https. + http://linkstatus.paradigma.co.pt != http://paradigma.co.pt +*/ +/* +bool SearchManager::isLocalRestrict(KURL const& url) const + { + Q_ASSERT(url.protocol() == "http" || url.protocol() == "https"); + + KURL url_root = root_.absoluteUrl(); + + if(url_root.protocol() != url.protocol()) + return false; + + if(url_root.hasHost()) + { + vector<QString> referencia = tokenizeWordsSeparatedBy(domain_, QChar('/')); + vector<QString> a_comparar = tokenizeWordsSeparatedBy(url.host() + url.directory(), QChar('/')); + + if(a_comparar.size() < referencia.size()) + return false; + else + { + for(uint i = 0; i != referencia.size(); ++i) + { + if(i == 0) + { // host, deal with specific function + if(!equalHost(referencia[i], a_comparar[i], true)) + return false; + } + else if(referencia[i] != a_comparar[i]) + return false; + } + } + return true; + } + else + return false; + } +*/ +bool SearchManager::generalDomain() const +{ + if(checked_general_domain_) + return general_domain_; + + else + { + Q_ASSERT(!domain_.isEmpty()); + + if(!check_parent_dirs_) + return false; + + int barra = domain_.find('/'); + if(barra != -1 && (uint)barra != domain_.length() - 1) + { + kdDebug(23100) << "Domain nao vago" << endl; + return false; + } + else + { + vector<QString> palavras = tokenizeWordsSeparatedByDots(domain_); + Q_ASSERT(palavras.size() >= 1); // host might be localhost + + QString primeira_palavra = palavras[0]; + if(primeira_palavra == "www") + { + Q_ASSERT(palavras.size() >= 3); + kdDebug(23100) << "Domain vago" << endl; + return true; + } + else if(palavras.size() == 2) + { + kdDebug(23100) << "Domain vago" << endl; + return true; + } + else + { + kdDebug(23100) << "Domain nao vago" << endl; + return false; + } + } + } +} + +bool SearchManager::onlyCheckHeader(LinkStatus* ls) const +{ + if(search_mode_ == depth) + return current_depth_ == depth_; + + else if(search_mode_ == domain) + return !ls->local() && + ls->externalDomainDepth() == external_domain_depth_ - 1; + + else + return + current_depth_ == depth_ || + (!ls->local() && + ls->externalDomainDepth() == external_domain_depth_ - 1); +} + +void SearchManager::slotSearchFinished() +{} + +void SearchManager::slotLinkCheckerFinnished(LinkChecker * checker) +{ + kdDebug(23100) << "deleting linkchecker" << endl; + + Q_ASSERT(checker); + //Q_ASSERT(checker->linkStatus()->checked()); + + delete checker; + checker = 0; +} + +KHTMLPart* SearchManager::htmlPart(QString const& key_url) const +{ + if(!html_parts_.contains(key_url)) + return 0; + + return html_parts_[key_url]; +} + +void SearchManager::addHtmlPart(QString const& key_url, KHTMLPart* html_part) +{ + Q_ASSERT(!key_url.isEmpty()); + Q_ASSERT(html_part); + + // FIXME configurable + if(html_parts_.count() > 150) + removeHtmlParts(); + + html_parts_.insert(key_url, html_part); +} + +void SearchManager::removeHtmlParts() +{ + KHTMLPartMap::Iterator it; + for(it = html_parts_.begin(); it != html_parts_.end(); ++it) + { + delete it.data(); + it.data() = 0; + } + + html_parts_.clear(); +} + +void SearchManager::save(QDomElement& element) const +{ + // <url> + QDomElement child_element = element.ownerDocument().createElement("url"); + child_element.appendChild(element.ownerDocument().createTextNode(root_.absoluteUrl().prettyURL())); + element.appendChild(child_element); + + // <recursively> + bool recursively = searchMode() == domain || depth_ > 0; + child_element = element.ownerDocument().createElement("recursively"); + child_element.appendChild(element.ownerDocument().createTextNode(recursively ? "true" : "false")); + element.appendChild(child_element); + + // <depth> + child_element = element.ownerDocument().createElement("depth"); + child_element.appendChild(element.ownerDocument(). + createTextNode(searchMode() == domain ? QString("Unlimited") : QString::number(depth_))); + element.appendChild(child_element); + + // <check_parent_folders> + child_element = element.ownerDocument().createElement("check_parent_folders"); + child_element.appendChild(element.ownerDocument(). + createTextNode(checkParentDirs() ? "true" : "false")); + element.appendChild(child_element); + + // <check_external_links> + child_element = element.ownerDocument().createElement("check_external_links"); + child_element.appendChild(element.ownerDocument(). + createTextNode(checkExternalLinks() ? "true" : "false")); + element.appendChild(child_element); + + // <check_regular_expression> + child_element = element.ownerDocument().createElement("check_regular_expression"); + child_element.setAttribute("check", checkRegularExpressions() ? "true" : "false"); + if(checkRegularExpressions()) + child_element.appendChild(element.ownerDocument(). + createTextNode(reg_exp_.pattern())); + element.appendChild(child_element); + + child_element = element.ownerDocument().createElement("link_list"); + element.appendChild(child_element); + + for(uint i = 0; i != search_results_.size(); ++i) + { + for(uint j = 0; j != search_results_[i].size() ; ++j) + { + for(uint l = 0; l != (search_results_[i])[j].size(); ++l) + { + LinkStatus* ls = ((search_results_[i])[j])[l]; + if(ls->checked()) + ls->save(child_element); + } + } + } +} + +QString SearchManager::toXML() const +{ + QDomDocument doc; + doc.appendChild(doc.createProcessingInstruction( "xml", + "version=\"1.0\" encoding=\"UTF-8\"")); + + QDomElement root = doc.createElement("klinkstatus"); + doc.appendChild(root); + + save(root); + + return doc.toString(4); +} + +#include "searchmanager.moc" diff --git a/klinkstatus/src/engine/searchmanager.h b/klinkstatus/src/engine/searchmanager.h new file mode 100644 index 00000000..135d267a --- /dev/null +++ b/klinkstatus/src/engine/searchmanager.h @@ -0,0 +1,193 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef GESTOR_PESQUISA_H +#define GESTOR_PESQUISA_H + +#include <kurl.h> + +#include <qobject.h> +#include <qstring.h> +#include <qdatetime.h> +#include <qregexp.h> +#include <qmap.h> +class QDomElement; + +#include <vector> + +#include "linkstatus.h" +#include "linkchecker.h" +#include "../parser/node.h" +#include "../parser/url.h" + +using namespace std; + +typedef QMap<QString, KHTMLPart*> KHTMLPartMap; + +class SearchManager: public QObject +{ + Q_OBJECT + +public: + + enum SearchMode { + depth, + domain, + depth_and_domain + }; + + SearchManager(int max_simultaneous_connections = 3, int time_out = 50, + QObject *parent = 0, const char *name = 0); + ~SearchManager(); + + QString toXML() const; + void save(QDomElement& element) const; + + KHTMLPartMap const& htmlParts() const { return html_parts_; } + + KHTMLPart* htmlPart(QString const& key_url) const; + void addHtmlPart(QString const& key_url, KHTMLPart* html_part); + void removeHtmlParts(); + + void startSearch(KURL const& root); + void startSearch(KURL const& root, SearchMode const& modo); + void resume(); + void cancelSearch(); + + bool hasDocumentRoot() const; + KURL const& documentRoot() const; + void setDocumentRoot(KURL const& url); + + void setSearchMode(SearchMode modo); + void setDepth(int depth); + void setExternalDomainDepth(int depth); + void setDomain(QString const& domain); + void setCheckParentDirs(bool flag); + void setCheckExternalLinks(bool flag); + void setCheckRegularExpressions(bool flag); + void setRegularExpression(QString const& reg_exp, bool case_sensitive); + void setTimeOut(int time_out); + + void cleanItems(); + void reset(); + + bool searching() const; + bool localDomain(KURL const& url, bool restrict = true) const; + //bool isLocalRestrict(KURL const& url) const; + SearchMode const& searchMode() const; + bool checkRegularExpressions() const { return check_regular_expressions_; } + bool existUrl(KURL const& url, KURL const& url_parent) const; + LinkStatus const* linkStatus(QString const& s_url) const; + int checkedLinks() const; + QTime timeElapsed() const; + bool checkParentDirs() const; + bool checkExternalLinks() const; + LinkStatus const* linkStatusRoot() const; + int maxSimultaneousConnections() const; + int timeOut() const; + + bool sendIdentification() const { return send_identification_; } + QString const& userAgent() const { return user_agent_; } + +private: + + void checkRoot(); + void checkVectorLinks(vector<LinkStatus*> const& links); // corresponde a um no de um nivel de depth + vector<LinkStatus*> children(LinkStatus* link); + void startSearch(); + void continueSearch(); + void finnish(); + void pause(); + vector<LinkStatus*> const& nodeToAnalize() const; + vector<LinkStatus*> chooseLinks(vector<LinkStatus*> const& links); + void checkLinksSimultaneously(vector<LinkStatus*> const& links); + void addLevel(); + bool checkableByDomain(KURL const& url, LinkStatus const& link_parent) const; + bool checkable(KURL const& url, LinkStatus const& link_parent) const; + int maximumCurrentConnections() const; + bool onlyCheckHeader(LinkStatus* ls) const; + + /* + Entende-se por domain vago um domain do tipo www.google.pt ou google.pt, pelo que, + por exemplo, imagens.google.pt, e considerado estar no mesmo domain. + pwp.netcabo.pt ou www.google.pt/imagens nao sao considerados domains vagos. + */ + bool generalDomain() const; + bool generalDomainChecked() const; // Para garantir que o procedimento generalDomain() so e chamado uma vez + +private slots: + + void slotRootChecked(const LinkStatus * link, LinkChecker * checker); + void slotLinkChecked(const LinkStatus * link, LinkChecker * checker); + void slotSearchFinished(); + void slotLinkCheckerFinnished(LinkChecker * checker); + +signals: + + void signalRootChecked(const LinkStatus * link, LinkChecker * checker); + void signalLinkChecked(const LinkStatus * link, LinkChecker * checker); + void signalSearchFinished(); + void signalSearchPaused(); + void signalAddingLevelTotalSteps(uint number_of_links); + void signalAddingLevelProgress(); + void signalLinksToCheckTotalSteps(uint links_to_check); + //void signalLinksToCheckProgress(); + +private: + + int max_simultaneous_connections_; + SearchMode search_mode_; + LinkStatus root_; + bool has_document_root_; + KURL document_root_url_; // in case of non http protocols the document root must be explicitly given + int depth_; + int current_depth_; + int external_domain_depth_; + int current_node_; + int current_index_; + int links_being_checked_; + int finished_connections_; + int maximum_current_connections_; + QRegExp reg_exp_; + QString domain_; + bool general_domain_; + bool checked_general_domain_; + int time_out_; + int current_connections_; + bool send_identification_; // user-agent + QString user_agent_; + + bool canceled_; + bool searching_; + int checked_links_; + QTime time_; + int ignored_links_; + bool check_parent_dirs_; + bool check_external_links_; + bool check_regular_expressions_; + uint number_of_level_links_; + uint number_of_links_to_check_; + vector< vector< vector <LinkStatus*> > > search_results_; + KHTMLPartMap html_parts_; +}; + +#include "searchmanager_impl.h" + +#endif diff --git a/klinkstatus/src/engine/searchmanager_impl.h b/klinkstatus/src/engine/searchmanager_impl.h new file mode 100644 index 00000000..eaa5e572 --- /dev/null +++ b/klinkstatus/src/engine/searchmanager_impl.h @@ -0,0 +1,158 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + + + + +inline int SearchManager::maximumCurrentConnections() const +{ + Q_ASSERT(maximum_current_connections_ != -1); + return maximum_current_connections_; +} + +inline SearchManager::SearchMode const& SearchManager::searchMode() const +{ + return search_mode_; +} + +inline int SearchManager::checkedLinks() const +{ + Q_ASSERT(checked_links_ > 0); + return checked_links_; +} + +inline QTime SearchManager::timeElapsed() const +{ + int ms = time_.elapsed(); + //kdDebug(23100) << "Time elapsed (ms): " << ms << endl; + return QTime(0, 0).addMSecs(ms); +} + +inline void SearchManager::startSearch(KURL const& root) +{ + startSearch(root, search_mode_); +} + +inline void SearchManager::setSearchMode(SearchMode modo) +{ + search_mode_ = modo; +} + +inline void SearchManager::setDepth(int depth) +{ + depth_ = depth; +} + +inline void SearchManager::setExternalDomainDepth(int depth) +{ + external_domain_depth_ = depth; +} + +inline void SearchManager::setDomain(QString const& domain) +{ + Q_ASSERT(domain.find("http://") == -1); + domain_ = domain; + general_domain_ = generalDomain(); + checked_general_domain_ = true; +} + +inline void SearchManager::setCheckParentDirs(bool flag) +{ + check_parent_dirs_ = flag; +} + +inline void SearchManager::setCheckExternalLinks(bool flag) +{ + check_external_links_ = flag; +} + +inline void SearchManager::setCheckRegularExpressions(bool flag) +{ + check_regular_expressions_ = flag; +} + +inline void SearchManager::setRegularExpression(QString const& reg_exp, bool case_sensitive) +{ + reg_exp_ = QRegExp(reg_exp, case_sensitive); +} + +inline void SearchManager::setTimeOut(int time_out) +{ + Q_ASSERT(time_out > 0); + time_out_ = time_out; +} + + + +inline bool SearchManager::checkParentDirs() const +{ + return check_parent_dirs_; +} + +inline bool SearchManager::checkExternalLinks() const +{ + return check_external_links_; +} + +inline LinkStatus const* SearchManager::linkStatusRoot() const +{ + return &root_; +} + +inline bool SearchManager::searching() const +{ + return searching_; +} + +inline bool SearchManager::localDomain(KURL const& url, bool restrict) const +{ + return Url::localDomain(root_.absoluteUrl(), url, restrict); +} + +inline int SearchManager::maxSimultaneousConnections() const +{ + return max_simultaneous_connections_; +} + +inline int SearchManager::timeOut() const +{ + return time_out_; +} + +inline bool SearchManager::hasDocumentRoot() const +{ + return has_document_root_; +} + +inline KURL const& SearchManager::documentRoot() const +{ + return document_root_url_; +} + +inline void SearchManager::setDocumentRoot(KURL const& url) +{ + Q_ASSERT(url.isValid()); // includes empty URLs + Q_ASSERT(!url.protocol().startsWith("http")); + + document_root_url_ = url; + has_document_root_ = true; +} + + diff --git a/klinkstatus/src/global.cpp b/klinkstatus/src/global.cpp new file mode 100644 index 00000000..10395f7b --- /dev/null +++ b/klinkstatus/src/global.cpp @@ -0,0 +1,205 @@ +// +// C++ Implementation: global +// +// Description: +// +// +// Author: Paulo Moura Guedes <moura@kdewebdev.org>, (C) 2004 +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "global.h" + +#include <qstring.h> +#include <qtimer.h> + +#include <dcopclient.h> +#include <dcopref.h> +#include <kdebug.h> +#include <kapplication.h> +#include <kstaticdeleter.h> +#include <kurl.h> +#include <kprocess.h> + +#include <sys/types.h> +#include <unistd.h> + + +Global* Global::m_self_ = 0; +static KStaticDeleter<Global> staticDeleter; + + +Global* Global::self() +{ + if (!m_self_) + { + staticDeleter.setObject(m_self_, new Global()); + } + + return m_self_; +} + +Global::Global(QObject *parent, const char *name) + : QObject(parent, name), loop_started_(false) +{ + m_self_ = this; + dcop_client_ = kapp->dcopClient(); +} + +Global::~Global() +{ + if(m_self_ == this) + staticDeleter.setObject(m_self_, 0, false); +} + +bool Global::isKLinkStatusEmbeddedInQuanta() +{ + QCString app_id = "quanta-" + QCString().setNum(getpid()); + return self()->dcop_client_->isApplicationRegistered(app_id); +} + +bool Global::isQuantaRunningAsUnique() +{ + return self()->dcop_client_->isApplicationRegistered("quanta"); +} + +bool Global::isQuantaAvailableViaDCOP() +{ + if(isQuantaRunningAsUnique() || isKLinkStatusEmbeddedInQuanta()) + return true; + + else + { + self()->execCommand("ps h -o pid -C quanta -C quanta_be"); + QStringList ps_list = QStringList::split("\n", self()->script_output_); + + for(uint i = 0; i != ps_list.size(); ++i) + { + ps_list[i] = ps_list[i].stripWhiteSpace (); + if(self()->dcop_client_->isApplicationRegistered("quanta-" + ps_list[i].local8Bit())) + { + //kdDebug(23100) << "Application registered!" << endl; + return true; + } + } + return false; + } +} + +QCString Global::quantaDCOPAppId() +{ + DCOPClient* client = kapp->dcopClient(); + QCString app_id; + + if(client->isApplicationRegistered("quanta")) // quanta is unnique application + app_id = "quanta"; + + else if(self()->isKLinkStatusEmbeddedInQuanta()) // klinkstatus is running as a part inside quanta + { + QCString app = "quanta-"; + QCString pid = QCString().setNum(getpid()); + app_id = app + pid; + } + + else + { + self()->execCommand("ps h -o pid -C quanta -C quanta_be"); + QStringList ps_list = QStringList::split("\n", self()->script_output_); + + for(uint i = 0; i != ps_list.size(); ++i) + { + ps_list[i] = ps_list[i].stripWhiteSpace (); + if(self()->dcop_client_->isApplicationRegistered("quanta-" + ps_list[i].local8Bit())) + app_id = "quanta-" + ps_list[i]; + } + } + + if(self()->dcop_client_->isApplicationRegistered(app_id)) + return app_id; + else + { + kdError(23100) << "You didn't check if Global::isQuantaAvailableViaDCOP!" << endl; + return ""; + } +} + +KURL Global::urlWithQuantaPreviewPrefix(KURL const& url) +{ + Q_ASSERT(isKLinkStatusEmbeddedInQuanta()); + + DCOPRef quanta(Global::quantaDCOPAppId(),"WindowManagerIf"); + QString string_url_with_prefix = quanta.call("urlWithPreviewPrefix", url.url()); + //kdDebug(23100) << "string_url_with_prefix: " << string_url_with_prefix << endl; + + return KURL(string_url_with_prefix); +} + +void Global::openQuanta(QStringList const& args) +{ + QString command(args.join(" ")); + Global::execCommand("quanta " + command); +} + +void Global::execCommand(QString const& command) +{ + + //We create a KProcess that executes the "ps" *nix command to get the PIDs of the + //other instances of quanta actually running + self()->process_PS_ = new KProcess(); + *(self()->process_PS_) << QStringList::split(" ",command); + + connect( self()->process_PS_, SIGNAL(receivedStdout(KProcess*,char*,int)), + self(), SLOT(slotGetScriptOutput(KProcess*,char*,int))); + connect( self()->process_PS_, SIGNAL(receivedStderr(KProcess*,char*,int)), + self(), SLOT(slotGetScriptError(KProcess*,char*,int))); + connect( self()->process_PS_, SIGNAL(processExited(KProcess*)), + self(), SLOT(slotProcessExited(KProcess*))); + + //if KProcess fails I think a message box is needed... I will fix it + if (!self()->process_PS_->start(KProcess::NotifyOnExit,KProcess::All)) + kdError() << "Failed to query for running KLinkStatus instances!" << endl; + //TODO: Replace the above error with a real messagebox after the message freeze is over + else + { + //To avoid lock-ups, start a timer. + QTimer* timer = new QTimer(self()); + connect(timer, SIGNAL(timeout()), + self(), SLOT(slotProcessTimeout())); + timer->start(120*1000, true); + self()->loop_started_ = true; + kapp->enter_loop(); + delete timer; + } +} + +void Global::slotGetScriptOutput(KProcess* /*process*/, char* buf, int buflen) +{ + QCString tmp( buf, buflen + 1 ); + script_output_ = QString::null; + script_output_ = QString::fromLocal8Bit(tmp).remove(" "); +} + +void Global::slotGetScriptError(KProcess*, char* buf, int buflen) +{ + //TODO: Implement some error handling? + Q_UNUSED(buf); + Q_UNUSED(buflen); +} + +void Global::slotProcessExited(KProcess*) +{ + slotProcessTimeout(); +} + +void Global::slotProcessTimeout() +{ + if (loop_started_) + { + kapp->exit_loop(); + loop_started_ = false; + } +} + + +#include "global.moc" diff --git a/klinkstatus/src/global.h b/klinkstatus/src/global.h new file mode 100644 index 00000000..2ee2f0c0 --- /dev/null +++ b/klinkstatus/src/global.h @@ -0,0 +1,60 @@ +// +// C++ Interface: global +// +// Description: +// +// +// Author: Paulo Moura Guedes <moura@kdewebdev.org>, (C) 2004 +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef GLOBAL_H +#define GLOBAL_H + +#include <qobject.h> +class QCString; + +class DCOPClient; +class KURL; +class KProcess; + +/** +@author Paulo Moura Guedes +*/ +class Global : public QObject +{ + Q_OBJECT +public: + static Global* self(); + ~Global(); + + static bool isKLinkStatusEmbeddedInQuanta(); + static bool isQuantaRunningAsUnique(); + static bool isQuantaAvailableViaDCOP(); + static QCString quantaDCOPAppId(); + static KURL urlWithQuantaPreviewPrefix(KURL const& url); + + //static void setLoopStarted(bool flag); + static void openQuanta(QStringList const& args); + +private: + Global(QObject *parent = 0, const char *name = 0); + static void execCommand(QString const& command); + +private slots: + void slotGetScriptOutput(KProcess* process, char* buffer, int buflen); + void slotGetScriptError(KProcess* process, char* buffer, int buflen); + void slotProcessExited(KProcess* process); + void slotProcessTimeout(); + +private: + static Global* m_self_; + + DCOPClient* dcop_client_; + bool loop_started_; + QString script_output_; + KProcess* process_PS_; +}; + +#endif diff --git a/klinkstatus/src/klinkstatus.cpp b/klinkstatus/src/klinkstatus.cpp new file mode 100644 index 00000000..7d984f0f --- /dev/null +++ b/klinkstatus/src/klinkstatus.cpp @@ -0,0 +1,213 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "klinkstatus.h" + +#include <kkeydialog.h> +#include <kfiledialog.h> +#include <kconfig.h> +#include <kurl.h> +#include <kedittoolbar.h> +#include <kaction.h> +#include <kstdaction.h> +#include <klibloader.h> +#include <kmessagebox.h> +#include <kstatusbar.h> +#include <klocale.h> +#include <kdebug.h> +#include <kaccel.h> + + +KLinkStatus::KLinkStatus() + : KParts::MainWindow( 0L, "KLinkStatus" ) +{ + // set the shell's ui resource file + setXMLFile("klinkstatus_shell.rc"); + + setupActions(); + + // and a status bar + //statusBar()->show(); + + // this routine will find and load our Part. it finds the Part by + // name which is a bad idea usually.. but it's alright in this + // case since our Part is made for this Shell + KLibFactory *factory = KLibLoader::self()->factory("libklinkstatuspart"); + if (factory) + { + // now that the Part is loaded, we cast it to a Part to get + // our hands on it + m_part = static_cast<KParts::ReadOnlyPart *>(factory->create(this, + "klinkstatus_part", "KParts::ReadOnlyPart" )); + + if (m_part) + { + // tell the KParts::MainWindow that this is indeed the main widget + setCentralWidget(m_part->widget()); + setStandardToolBarMenuEnabled(true); + + // and integrate the part's GUI with the shell's + createGUI(m_part); + removeDuplicatedActions(); + } + } + else + { + // if we couldn't find our Part, we exit since the Shell by + // itself can't do anything useful + KMessageBox::error(this, i18n("Could not find the KLinkStatus part; did you configure with '--prefix=/$KDEDIR' and perform 'make install'?")); + kapp->quit(); + // we return here, cause kapp->quit() only means "exit the + // next time we enter the event loop... + return; + } + + // apply the saved mainwindow settings, if any, and ask the mainwindow + // to automatically save settings if changed: window size, toolbar + // position, icon size, etc. + setAutoSaveSettings(); + + setupPartActions(); +} + +KLinkStatus::~KLinkStatus() +{} + +void KLinkStatus::load(const KURL& url) +{ + m_part->openURL( url ); +} + +void KLinkStatus::setupActions() +{ + // KStdAction::quit(kapp, SLOT(quit()), actionCollection()); + // The above causes a segfault when using File->Quit. + // Here's Waldo's explanation: +/* I had a look. The problem is due to the SessionWidget destructor calling + KLSConfig. If you use the window button, the window and the SessionWidget is + destructed first and then later the application is destructed. + If you use File->Quit it calls kapp->quit which destructs the application + without destructing the window first. The application first destructs all + static deleters and its administration, and then goes on to kill the + remaining windows that it owns. Therein lies the problem because by then the + static deleters aren't usable any longer, and calling KLSConfig from the + SessionWidget destructor crashes when it tries to recreate KLSConfig and + register it with staticKLSConfigDeleter due to the lack of static deleter + administration. + The easiest solution is to call close() on the mainwindow instead of + KApplication::quit()*/ + KStdAction::quit(this, SLOT(close()), actionCollection()); + + //m_toolbarAction = KStdAction::showToolbar(this, SLOT(optionsShowToolbar()), actionCollection()); + //m_statusbarAction = KStdAction::showStatusbar(this, SLOT(optionsShowStatusbar()), actionCollection()); + + KStdAction::keyBindings(this, SLOT(optionsConfigureKeys()), actionCollection()); + KStdAction::configureToolbars(this, SLOT(optionsConfigureToolbars()), actionCollection()); +} + +void KLinkStatus::setupPartActions() +{ + Q_ASSERT(m_part); + KActionCollection* part_action_collection = m_part->actionCollection(); + part_action_collection->action("new_link_check")->setShortcut(KStdAccel::shortcut(KStdAccel::New)); + part_action_collection->action("open_link")->setShortcut(KStdAccel::shortcut(KStdAccel::Open)); + part_action_collection->action("close_tab")->setShortcut(KStdAccel::shortcut(KStdAccel::Close)); +} + +void KLinkStatus::removeDuplicatedActions() +{ + KActionCollection* part_action_collection = m_part->actionCollection(); + KAction* part_about_action = part_action_collection->action("about_klinkstatus"); + KAction* part_report_action = part_action_collection->action("report_bug"); + + QWidget* container = part_about_action->container(0); // call this only once or segfault + part_about_action->unplug(container); + part_report_action->unplug(container); + part_action_collection->remove(part_about_action); + part_action_collection->remove(part_report_action); +} + +void KLinkStatus::saveProperties(KConfig* /*config*/) +{ + // the 'config' object points to the session managed + // config file. anything you write here will be available + // later when this app is restored +} + +void KLinkStatus::readProperties(KConfig* /*config*/) +{ + // the 'config' object points to the session managed + // config file. this function is automatically called whenever + // the app is being restored. read in here whatever you wrote + // in 'saveProperties' +} + +void KLinkStatus::optionsShowToolbar() +{ + // this is all very cut and paste code for showing/hiding the + // toolbar + if (m_toolbarAction->isChecked()) + toolBar()->show(); + else + toolBar()->hide(); +} + +void KLinkStatus::optionsShowStatusbar() +{ + // this is all very cut and paste code for showing/hiding the + // statusbar + if (m_statusbarAction->isChecked()) + statusBar()->show(); + else + statusBar()->hide(); +} + +void KLinkStatus::optionsConfigureKeys() +{ + //KKeyDialog::configure(actionCollection()); + + KKeyDialog dlg( false, this ); + QPtrList<KXMLGUIClient> clients = guiFactory()->clients(); + for( QPtrListIterator<KXMLGUIClient> it( clients ); + it.current(); ++it ) + { + dlg.insert( (*it)->actionCollection() ); + } + dlg.configure(); +} + +void KLinkStatus::optionsConfigureToolbars() +{ + saveMainWindowSettings(KGlobal::config(), autoSaveGroup()); + + // use the standard toolbar editor + KEditToolbar dlg(factory()); + connect(&dlg, SIGNAL(newToolbarConfig()), + this, SLOT(applyNewToolbarConfig())); + dlg.exec(); +} + +void KLinkStatus::applyNewToolbarConfig() +{ + applyMainWindowSettings(KGlobal::config(), autoSaveGroup()); +} + + +#include "klinkstatus.moc" diff --git a/klinkstatus/src/klinkstatus.desktop b/klinkstatus/src/klinkstatus.desktop new file mode 100644 index 00000000..bb1cdbd0 --- /dev/null +++ b/klinkstatus/src/klinkstatus.desktop @@ -0,0 +1,54 @@ +[Desktop Entry] +Name=KLinkStatus +Name[ne]=केडीई लिङ्क स्थिति +Name[sk]=KLink status +Name[sv]=Klinkstatus +Name[ta]=Kதொகுதி நிலைமை +Name[tr]=K Köprü Durumu +Exec=klinkstatus %i %m -caption "%c" +Icon=klinkstatus +Type=Application +DocPath=klinkstatus/index.html +Terminal=false +GenericName=Link Checker +GenericName[bg]=Проверка на препратки +GenericName[ca]=Comprovador d'enllaços +GenericName[cs]=Kontrola odkazů +GenericName[da]=Link-tjekker +GenericName[de]=Überprüfungswerkzeug für Verknüpfungen +GenericName[el]=Ελεγκτής σύνδεσης +GenericName[es]=Comprobador de enlaces +GenericName[et]=Viidakontrollija +GenericName[eu]=Esteka egiaztatzailea +GenericName[fa]=بررسیکنندۀ پیوند +GenericName[fi]=Linkkien tarkistaja +GenericName[fr]=Vérificateur de liens +GenericName[gl]=Verificador de ligazóns +GenericName[hu]=Linkellenőrző +GenericName[is]=Yfirfer tengla +GenericName[it]=Controllo dei collegamenti +GenericName[ja]=リンクチェッカー +GenericName[ka]=ბმულების შემმოწმებელი +GenericName[lt]=Nuorody tikrintuvė +GenericName[ms]=Pemeriksa Pautan +GenericName[nds]=Linkprööv +GenericName[ne]=लिङ्क परीक्षक +GenericName[nl]=Linkchecker +GenericName[pl]=Program sprawdzający odnośniki +GenericName[pt]=Verificação de Ligações +GenericName[pt_BR]=Verificador de Links +GenericName[ru]=Проверка ссылок +GenericName[sk]=Kontrole spojenia +GenericName[sl]=Preverjalnik povezav +GenericName[sr]=Провера везе +GenericName[sr@Latn]=Provera veze +GenericName[sv]=Länkkontroll +GenericName[ta]=இணைப்பு சரிபார்ப்பான் +GenericName[tg]=Тафтиши истинод +GenericName[tr]=Bağlantı Kontrolcüsü +GenericName[uk]=Перевірка посилань +GenericName[zh_CN]=链接检查器 +GenericName[zh_HK]=連結檢查程式 +GenericName[zh_TW]=連結檢查程式 +Categories=Qt;KDE;Development;WebDevelopment; + diff --git a/klinkstatus/src/klinkstatus.h b/klinkstatus/src/klinkstatus.h new file mode 100644 index 00000000..14ba2934 --- /dev/null +++ b/klinkstatus/src/klinkstatus.h @@ -0,0 +1,86 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef _KLINKSTATUS_H_ +#define _KLINKSTATUS_H_ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <kapplication.h> +#include <kparts/mainwindow.h> + +class KToggleAction; + +/** + * This is the application "Shell". It has a menubar, toolbar, and + * statusbar but relies on the "Part" to do all the real work. + * + * @short Application Shell + * @author Paulo Moura Guedes <moura@kdewebdev.org> + * @version 0.1.3 + */ +class KLinkStatus : public KParts::MainWindow +{ + Q_OBJECT +public: + KLinkStatus(); + virtual ~KLinkStatus(); + + /** Use this method to load whatever file/URL you have */ + void load(const KURL& url); + +protected: + /** + * This method is called when it is time for the app to save its + * properties for session management purposes. + */ + void saveProperties(KConfig *); + + /** + * This method is called when this app is restored. The KConfig + * object points to the session management config file that was saved + * with @ref saveProperties + */ + void readProperties(KConfig *); + +private slots: + void optionsShowToolbar(); + void optionsShowStatusbar(); + void optionsConfigureKeys(); + void optionsConfigureToolbars(); + + void applyNewToolbarConfig(); + +private: + void setupAccel(); + void setupActions(); + void setupPartActions(); + void removeDuplicatedActions(); + +private: + KParts::ReadOnlyPart *m_part; + + KToggleAction *m_toolbarAction; + KToggleAction *m_statusbarAction; +}; + +#endif // _KLINKSTATUS_H_ diff --git a/klinkstatus/src/klinkstatus.lsm b/klinkstatus/src/klinkstatus.lsm new file mode 100644 index 00000000..94221faf --- /dev/null +++ b/klinkstatus/src/klinkstatus.lsm @@ -0,0 +1,16 @@ +Begin3 +Title: KLinkStatus -- Some description +Version: 0.2.1 +Entered-date: +Description: +Keywords: KDE Qt +Author: Paulo Moura Guedes <pmg@netcabo.pt> +Maintained-by: Paulo Moura Guedes <pmg@netcabo.pt> +Home-page: +Alternate-site: +Primary-site: ftp://ftp.kde.org/pub/kde/unstable/apps/utils + xxxxxx klinkstatus-0.1.0.tar.gz + xxx klinkstatus-0.1.0.lsm +Platform: Linux. Needs KDE +Copying-policy: GPL +End diff --git a/klinkstatus/src/klinkstatus_part.cpp b/klinkstatus/src/klinkstatus_part.cpp new file mode 100644 index 00000000..cf04df7a --- /dev/null +++ b/klinkstatus/src/klinkstatus_part.cpp @@ -0,0 +1,207 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include <kaboutdata.h> +#include <klocale.h> +#include <kinstance.h> +#include <kaction.h> +#include <kstdaction.h> +#include <kfiledialog.h> +#include <kparts/genericfactory.h> +#include <kparts/factory.h> +#include <kstandarddirs.h> +#include <kaboutapplication.h> +#include <kbugreport.h> +#include <kconfigdialog.h> +#include <kglobalsettings.h> +#include <kshortcut.h> +#include <kaccel.h> +#include <kkeydialog.h> + +#include <qbuttongroup.h> + +#include "global.h" +#include "cfg/klsconfig.h" +#include "klinkstatus_part.h" +#include "ui/tabwidgetsession.h" +#include "ui/sessionwidget.h" +#include "ui/settings/configsearchdialog.h" +#include "ui/settings/configresultsdialog.h" +#include "ui/settings/configidentificationdialog.h" +#include "actionmanager.h" + + +const char KLinkStatusPart::description_[] = I18N_NOOP( "A Link Checker" ); +const char KLinkStatusPart::version_[] = "0.3.2"; + +// Factory code for KDE 3 +typedef KParts::GenericFactory<KLinkStatusPart> KLinkStatusFactory; +K_EXPORT_COMPONENT_FACTORY( libklinkstatuspart, KLinkStatusFactory ) + +KLinkStatusPart::KLinkStatusPart(QWidget *parentWidget, const char *widgetName, + QObject *parent, const char *name, + const QStringList & /*string_list*/) + : KParts::ReadOnlyPart(parent, name), m_dlgAbout(0) +{ + setInstance(KLinkStatusFactory::instance()); + + action_manager_ = new ActionManager(this); + ActionManager::setInstance(action_manager_); + initGUI(); + + tabwidget_ = new TabWidgetSession(parentWidget, widgetName); + setWidget(tabwidget_); + action_manager_->initTabWidget(tabwidget_); + + // we are not modified since we haven't done anything yet + setModified(false); + + openURL(""); +} + +KLinkStatusPart::~KLinkStatusPart() +{} + +void KLinkStatusPart::initGUI() +{ + setXMLFile("klinkstatus_part.rc", true); + + // initialize the part actions + action_manager_->initPart(this); +} + +void KLinkStatusPart::setModified(bool modified) +{ + // get a handle on our Save action and make sure it is valid + KAction *save = actionCollection()->action(KStdAction::stdName(KStdAction::Save)); + if (!save) + return; + + // if so, we either enable or disable it based on the current + // state + if (modified) + save->setEnabled(true); + else + save->setEnabled(false); +} + +bool KLinkStatusPart::openURL(KURL const& url) +{ + KURL url_aux = url; + + if(KLSConfig::useQuantaUrlPreviewPrefix() && Global::isKLinkStatusEmbeddedInQuanta()) + { + url_aux = Global::urlWithQuantaPreviewPrefix(url); + if(!url_aux.isValid() || url_aux.isEmpty()) + url_aux = url; + } + else + url_aux = url; + + tabwidget_->slotNewSession(url_aux); + + return true; +} + +bool KLinkStatusPart::openFile() +{ + return false; +} + +void KLinkStatusPart::slotNewLinkCheck() +{ + openURL(""); +} + +void KLinkStatusPart::slotOpenLink() +{ + QString file_name = KFileDialog::getOpenURL().url(); + + if (file_name.isEmpty() == false) + { + openURL(file_name); + } +} + +void KLinkStatusPart::slotClose() +{ + tabwidget_->closeSession(); +} + +void KLinkStatusPart::slotConfigureKLinkStatus() +{ + KConfigDialog *dialog = new KConfigDialog(tabwidget_, "klsconfig", KLSConfig::self()); + dialog->addPage(new ConfigSearchDialog(0, "config_search_dialog"), i18n("Check"), "viewmag"); + dialog->addPage(new ConfigResultsDialog(0, "config_results_dialog"), i18n("Results"), "player_playlist"); + dialog->addPage(new ConfigIdentificationDialog(0), i18n("Identification"), + "agent", i18n("Configure the way KLinkstatus reports itself")); + dialog->show(); + connect(dialog, SIGNAL(settingsChanged()), tabwidget_, SLOT(slotLoadSettings())); +} + +void KLinkStatusPart::slotAbout() +{ + if(m_dlgAbout == 0) + { + m_dlgAbout = new KAboutApplication(createAboutData(), tabwidget_, "about_app"); + if(m_dlgAbout == 0) + return; + } + + if(!m_dlgAbout->isVisible()) + { + m_dlgAbout->show(); + } + else + { + m_dlgAbout->raise(); + } +} + +void KLinkStatusPart::slotReportBug() +{ + KAboutData aboutData("klinkstatus", I18N_NOOP("KLinkStatus"), version_); + KBugReport bugReportDlg(0, true, &aboutData); + bugReportDlg.exec(); +} + +KAboutData* KLinkStatusPart::createAboutData() +{ + KAboutData * about = new KAboutData("klinkstatuspart", I18N_NOOP("KLinkStatus Part"), version_, + description_, KAboutData::License_GPL_V2, + "(C) 2004 Paulo Moura Guedes", 0, 0, "moura@kdewebdev.org"); + + about->addAuthor("Paulo Moura Guedes", 0, "moura@kdewebdev.org"); + + about->addCredit("Manuel Menezes de Sequeira", 0, 0, "http://home.iscte.pt/~mms/"); + about->addCredit("Gonçalo Silva", 0, "gngs@paradigma.co.pt"); + about->addCredit("Nuno Monteiro", 0, 0, "http://www.itsari.org"); + about->addCredit("Eric Laffoon", 0, "sequitur@kde.org"); + about->addCredit("Andras Mantia", 0, "amantia@kde.org"); + about->addCredit("Michal Rudolf", 0, "mrudolf@kdewebdev.org"); + about->addCredit("Mathieu Kooiman", 0, " quanta@map-is.nl"); + about->addCredit("Jens Herden", 0, "jens@kdewebdev.org"); + + KGlobal::dirs()->addResourceType("appicon", KStandardDirs::kde_default("data") + "klinkstatuspart/pics/"); + + return about; +} + +#include "klinkstatus_part.moc" diff --git a/klinkstatus/src/klinkstatus_part.desktop b/klinkstatus/src/klinkstatus_part.desktop new file mode 100644 index 00000000..9515bbfa --- /dev/null +++ b/klinkstatus/src/klinkstatus_part.desktop @@ -0,0 +1,17 @@ +[Desktop Entry] +Name=KLinkStatusPart +Name[de]=KLinkStatus-Komponente +Name[fr]=Composant de KLinkStatus +Name[nds]=KLinkStatus-Komponent +Name[ne]=केडीई लिङ्क वस्तुस्थिति भाग +Name[pt_BR]=Componente do KLinkStatus +Name[ru]=Компонент KLinkStatus +Name[sk]=KLink status Part +Name[sv]=Klinkstatus-delprogram +Name[ta]=Kதொகுதி நிலைமை பகுதி +Name[tg]=Қисми KLinkStatus +Icon=klinkstatus +MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++; +ServiceTypes=KParts/ReadOnlyPart,KParts/ReadWritePart +X-KDE-Library=libklinkstatuspart +Type=Service diff --git a/klinkstatus/src/klinkstatus_part.h b/klinkstatus/src/klinkstatus_part.h new file mode 100644 index 00000000..b32c8675 --- /dev/null +++ b/klinkstatus/src/klinkstatus_part.h @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef _KLINKSTATUSPART_H_ +#define _KLINKSTATUSPART_H_ + +#include <kparts/part.h> + +class TabWidgetSession; +class ActionManager; + +class QWidget; +class QPainter; + +class KURL; +class KAboutData; +class KAboutApplication; +class KAction; + +class KLinkStatusPart: public KParts::ReadOnlyPart +{ + Q_OBJECT +public: + KLinkStatusPart(QWidget *parentWidget, const char *widgetName, + QObject *parent, const char *name, + const QStringList& args); + virtual ~KLinkStatusPart(); + + /** Reimplemented to disable and enable Save action */ + virtual void setModified(bool modified); + + static KAboutData* createAboutData(); + +protected: + /** This must be implemented by each part */ + virtual bool openFile(); + virtual bool openURL (const KURL &url); + // virtual bool saveFile(){}; + +protected slots: + void slotNewLinkCheck(); + void slotOpenLink(); + void slotClose(); + void slotConfigureKLinkStatus(); +/* void slotDisplayAllLinks(); + void slotDisplayGoodLinks(); + void slotDisplayBadLinks(); + void slotDisplayMalformedLinks(); + void slotDisplayUndeterminedLinks();*/ + void slotAbout(); + void slotReportBug(); + +// private slots: +// void slotEnableDisplayLinksActions(); +// void slotDisableDisplayLinksActions(); + +private: + void initGUI(); + +private: + static const char description_[]; + static const char version_[]; + + ActionManager* action_manager_; + + TabWidgetSession* tabwidget_; + KAboutApplication* m_dlgAbout; +}; + +#endif // _KLINKSTATUSPART_H_ diff --git a/klinkstatus/src/klinkstatus_part.rc b/klinkstatus/src/klinkstatus_part.rc new file mode 100644 index 00000000..f2d580de --- /dev/null +++ b/klinkstatus/src/klinkstatus_part.rc @@ -0,0 +1,58 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="klinkstatus_part" version="2"> + <MenuBar> + + <Menu name="file"><text>File</text> + <Action name="new_link_check"/> + <Action name="open_link"/> + <Action name="close_tab"/> + <Separator/> + <Action name="file_export_html"/> + <Separator/> + </Menu> + + <Menu name="settings"><text>&Settings</text> + <Separator/> + <Action name="configure_klinkstatus"/> + </Menu> + + <Menu name="view"><text>&View</text> + <Action name="hide_search_bar"/> + <Action name="reset_search_bar"/> + <Separator/> + <Action name="follow_last_link_checked"/> +<!-- <Separator/> + <Action name="display_all_links"/> + <Action name="display_good_links"/> + <Action name="display_bad_links"/> + <Action name="display_malformed_links"/> + <Action name="display_undetermined_links"/>--> + </Menu> + + <Menu name="search"><text>S&earch</text> + <Action name="start_search"/> + <Action name="pause_search"/> + <Action name="stop_search"/> + <Separator/> + </Menu> + + <Menu name="help"><text>&Help</text> + <Separator/> + <Action name="about_klinkstatus"/> + <Action name="report_bug"/> + </Menu> + </MenuBar> + + <ToolBar name="linksToolBar"> + <Action name="start_search"/> + <Action name="pause_search"/> + <Action name="stop_search"/> + <Separator/> + <Action name="file_export_html"/> + <Separator/> + <Action name="hide_search_bar"/> + <Action name="reset_search_bar"/> + <Separator/> + <Action name="follow_last_link_checked"/> + </ToolBar> +</kpartgui> diff --git a/klinkstatus/src/klinkstatus_shell.rc b/klinkstatus/src/klinkstatus_shell.rc new file mode 100644 index 00000000..04cbc329 --- /dev/null +++ b/klinkstatus/src/klinkstatus_shell.rc @@ -0,0 +1,28 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="klinkstatus_shell" version="1"> +<MenuBar> + + <Menu noMerge="1" name="file"><text>&File</text> + <Merge/> + <Separator/> + <Action name="file_quit"/> + </Menu> + + <Menu noMerge="1" name="view"><text>&View</text> + <Separator/> + </Menu> + + <Menu noMerge="1" name="search"><text>S&earch</text> + <Separator/> + </Menu> + + <Menu noMerge="1" name="settings"><text>&Settings</text> + <Action name="options_configure_keybinding"/> + <Action name="options_configure_toolbars"/> + <Merge name="configure_merge"/> + <Separator/> + <Merge/> + </Menu> +</MenuBar> + +</kpartgui> diff --git a/klinkstatus/src/main.cpp b/klinkstatus/src/main.cpp new file mode 100644 index 00000000..8f5f1f95 --- /dev/null +++ b/klinkstatus/src/main.cpp @@ -0,0 +1,98 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "klinkstatus.h" + +#include <kapplication.h> +#include <kaboutdata.h> +#include <kcmdlineargs.h> +#include <klocale.h> +#include <kdebug.h> + +#include <iostream> +using namespace std; + + +static const char description[] = + I18N_NOOP("A Link Checker.\n\nKLinkStatus belongs to the kdewebdev module from KDE."); + +static const char version[] = "0.3.2"; + +static KCmdLineOptions options[] = + { + { "+[URL]", I18N_NOOP( "Document to open" ), 0 }, + KCmdLineLastOption + }; + +int main(int argc, char *argv[]) +{ + //____________________________________________________ + + KAboutData about("klinkstatus", I18N_NOOP("KLinkStatus"), version, description, + KAboutData::License_GPL_V2, "(C) 2004 Paulo Moura Guedes", 0, + "http://klinkstatus.kdewebdev.org"); + + about.addAuthor("Paulo Moura Guedes", 0, "moura@kdewebdev.org"); + + about.addCredit("Manuel Menezes de Sequeira", 0, 0, "http://home.iscte.pt/~mms/"); + about.addCredit("Gonçalo Silva", 0, "gngs@paradigma.co.pt"); + about.addCredit("Nuno Monteiro", 0, 0, "http://www.itsari.org"); + about.addCredit("Eric Laffoon", 0, "sequitur@kde.org"); + about.addCredit("Andras Mantia", 0, "amantia@kde.org"); + about.addCredit("Michal Rudolf", 0, "mrudolf@kdewebdev.org"); + about.addCredit("Mathieu Kooiman", 0, " quanta@map-is.nl"); + about.addCredit("Jens Herden", 0, "jens@kdewebdev.org"); + about.addCredit("Helge Hielscher", 0, "hhielscher@unternehmen.com"); + + KCmdLineArgs::init(argc, argv, &about); + KCmdLineArgs::addCmdLineOptions( options ); + + KApplication app; + + // see if we are starting with session management + if (app.isRestored()) + { + RESTORE(KLinkStatus); + } + else + { + // no session.. just start up normally + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + KLinkStatus *widget = new KLinkStatus; + widget->show(); + + if ( args->count() == 0 ) + { + widget->load(KURL()); + } + else + { + int i = 0; + for (; i < args->count(); i++ ) + { + widget->load( args->url( i ) ); + } + } + args->clear(); + } + + return app.exec(); +} diff --git a/klinkstatus/src/parser/Makefile.am b/klinkstatus/src/parser/Makefile.am new file mode 100644 index 00000000..b99146c1 --- /dev/null +++ b/klinkstatus/src/parser/Makefile.am @@ -0,0 +1,6 @@ +INCLUDES = $(all_includes) +METASOURCES = AUTO +noinst_HEADERS = htmlparser.h http.h mstring.h node.h node_impl.h url.h +libparser_la_LDFLAGS = $(all_libraries) +noinst_LTLIBRARIES = libparser.la +libparser_la_SOURCES = htmlparser.cpp http.cpp mstring.cpp node.cpp url.cpp diff --git a/klinkstatus/src/parser/htmlparser.cpp b/klinkstatus/src/parser/htmlparser.cpp new file mode 100644 index 00000000..6bc93761 --- /dev/null +++ b/klinkstatus/src/parser/htmlparser.cpp @@ -0,0 +1,455 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "htmlparser.h" + +#include <kapplication.h> +#include <kdebug.h> + + +HtmlParser::HtmlParser(QString const& documento) + : is_content_type_set_(false), document_(documento) +{ + Q_ASSERT(!documento.isEmpty()); + + stripScriptContent(); + stripComments(); // after removing the script because comments in scripts have diferent sintaxe + + nodes_.reserve(estimativaLinks(documento.length() * 2)); // à confiança ;) + + parseNodesOfTypeA(); + parseNodesOfTypeAREA(); + parseNodesOfTypeLINK(); + parseNodesOfTypeMETA(); + parseNodesOfTypeIMG(); + parseNodesOfTypeFRAME(); + parseNodesOfTypeIFRAME(); + parseNodesOfTypeBASE(); + parseNodesOfTypeTITLE(); +} + +bool HtmlParser::hasBaseUrl() const +{ + return (node_BASE_.element() == Node::BASE && + !node_BASE_.url().isEmpty()); +} + +NodeBASE const& HtmlParser::baseUrl() const +{ + Q_ASSERT(hasBaseUrl()); + return node_BASE_; +} + +NodeMETA const& HtmlParser::contentTypeMetaNode() const +{ + Q_ASSERT(hasContentType()); + return node_META_content_type_; +} + +bool HtmlParser::hasTitle() const +{ + return (node_TITLE_.element() == Node::TITLE && + !node_TITLE_.attributeTITLE().isEmpty()); +} + +NodeTITLE const& HtmlParser::title() const +{ + Q_ASSERT(hasTitle()); + return node_TITLE_; +} + +vector<QString> const& HtmlParser::parseNodesOfType(QString const& element) +{ + HtmlParser::parseNodesOfType(element, document_, aux_); + return aux_; +} + +void HtmlParser::parseNodesOfType(QString const& tipo, QString const& document, vector<QString>& nodes) +{ + QString node; + QString doc(document); + int inicio = 0, fim = 0; + + nodes.clear(); + if(upperCase(tipo) == "A") + nodes.reserve(estimativaLinks(doc.length() * 2)); + + while(true) + { + inicio = findSeparableWord(doc, "<" + tipo); + if(inicio == -1) + return; + + //if( (doc[inicio] != ' ' && doc[inicio] != '\n' && doc[inicio] != '\r') ) + if(!::isSpace(doc[inicio])) + { + doc.remove(0, QString("<" + tipo).length()); + continue; + } + + if(upperCase(tipo) == "A") + fim = findWord(doc, "</A>", inicio); + else + { + //fim = findChar(doc, '>', inicio + 1); + fim = endOfTag(doc, inicio, '>'); + } + + if(fim == -1) + { + doc.remove(0, 1); + continue; + } + + int tag_begining_go_back = (tipo.length() + QString("<").length()); + node = doc.mid(inicio - tag_begining_go_back, + fim - inicio + tag_begining_go_back); + nodes.push_back(node); + doc.remove(0, fim); + } +} + +int HtmlParser::endOfTag(QString const& s, int index, QChar end_of_tag) +{ + if( (uint)index >= s.length() ) + return -1; + + int _end_of_tag = s.find(end_of_tag, index); + if(_end_of_tag == -1) + return _end_of_tag; + + int open_aspas = s.find('"', index); + if(open_aspas == -1) + return _end_of_tag + 1; + + else if(_end_of_tag < open_aspas) + return _end_of_tag + 1; + + else if( ((uint)open_aspas + 1) >= s.length() - 1 ) + return -1; + + else + { + int close_aspas = s.find('"', open_aspas + 1); + if(close_aspas != -1) + return endOfTag(s, close_aspas + 1, end_of_tag); + else + { + kdDebug(23100) << "Mismatched quotes (\"): " << s.mid(index, _end_of_tag - index) << endl; + //return -1; + return _end_of_tag + 1; + } + } +} + +vector<Node*> const& HtmlParser::nodes() const +{ + return nodes_; +} + + +void HtmlParser::parseNodesOfTypeA() +{ + vector<QString> const& aux = parseNodesOfType("A"); + + for(vector<QString>::size_type i = 0; i != aux.size(); ++i) + { + nodes_.push_back( new NodeA(aux[i]) ); + } +} + +void HtmlParser::parseNodesOfTypeAREA() +{ + vector<QString> const& aux = parseNodesOfType("AREA"); + + for(vector<QString>::size_type i = 0; i != aux.size(); ++i) + { + nodes_.push_back( new NodeAREA(aux[i]) ); + } +} + +void HtmlParser::parseNodesOfTypeLINK() +{ + vector<QString> const& aux = parseNodesOfType("LINK"); + + for(vector<QString>::size_type i = 0; i != aux.size(); ++i) + nodes_.push_back( new NodeLINK(aux[i]) ); +} + +void HtmlParser::parseNodesOfTypeMETA() +{ + vector<QString> const& aux = parseNodesOfType("META"); + + for(vector<QString>::size_type i = 0; i != aux.size(); ++i) + { + NodeMETA* node = new NodeMETA(aux[i]); + nodes_.push_back(node); + + if(!is_content_type_set_ && node->atributoHTTP_EQUIV().lower() == QString("Content-Type").lower()) { + is_content_type_set_ = true; + node_META_content_type_.setNode(aux[i]); + } + } +} + +QString HtmlParser::findCharsetInMetaElement(QString const& html) +{ + vector<QString> metaTags; + parseNodesOfType("META", html, metaTags); + + for(vector<QString>::size_type i = 0; i != metaTags.size(); ++i) + { + NodeMETA node(metaTags[i]); + + if(node.atributoHTTP_EQUIV().lower() == QString("Content-Type").lower()) { + return node.charset(); + } + } + return QString(); +} + +void HtmlParser::parseNodesOfTypeIMG() +{ + vector<QString> const& aux = parseNodesOfType("IMG"); + + for(vector<QString>::size_type i = 0; i != aux.size(); ++i) + nodes_.push_back( new NodeIMG(aux[i]) ); +} + +void HtmlParser::parseNodesOfTypeFRAME() +{ + vector<QString> const& aux = parseNodesOfType("FRAME"); + + for(vector<QString>::size_type i = 0; i != aux.size(); ++i) + nodes_.push_back( new NodeFRAME(aux[i]) ); +} + +void HtmlParser::parseNodesOfTypeIFRAME() +{ + vector<QString> const& aux = parseNodesOfType("IFRAME"); + + for(vector<QString>::size_type i = 0; i != aux.size(); ++i) + nodes_.push_back( new NodeFRAME(aux[i]) ); +} + +void HtmlParser::parseNodesOfTypeBASE() +{ + QString node; + QString doc = document_; + int inicio = 0, fim = 0; + + inicio = findSeparableWord(doc, "<BASE"); + if(inicio == -1 || !doc[inicio].isSpace()) + return; + + fim = doc.find(">", inicio); + if(fim == -1) + return; + + node = doc.mid(inicio, fim-inicio); + node_BASE_.setNode(node); +} + +void HtmlParser::parseNodesOfTypeTITLE() +{ + QString node; + QString doc = document_; + int inicio = 0, fim = 0; + + inicio = findSeparableWord(doc, "<TITLE>"); + if(inicio == -1) + return; + + fim = findSeparableWord(doc, "</TITLE>", inicio); + if(fim == -1) + return; + + node = doc.mid(inicio, fim-inicio); + + node_TITLE_.setNode(node); +} + + +void HtmlParser::stripComments() +{ + QString begin_comment = "<!--"; + QString end_comment = "-->"; + uint const begin_comment_length = begin_comment.length(); + + int inicio = -1; + do + { + inicio = findWord(document_, begin_comment); + if(inicio != -1) + { + int fim = findWord(document_, end_comment, inicio); + if(fim == -1) + { + kdDebug(23100) << "End of comment is missing!" << endl; + document_.remove(inicio - begin_comment_length, begin_comment_length); + } + else + { + comments_ += "\n" + document_.mid(inicio - begin_comment_length, + fim - inicio + begin_comment_length); + document_.remove(inicio - begin_comment_length, fim - inicio + begin_comment_length); + } + } + } + while(inicio != -1); +} + +void HtmlParser::stripScriptContent() +{ + int inicio = -1; + QString const begin_script = "<script"; + QString const end_script = "</script>"; + uint const begin_script_length = begin_script.length(); + + do + { + inicio = findWord(document_, begin_script); + if(inicio != -1) + { + int fim = findWord(document_, end_script, inicio); + + if(fim == -1) + { + kdDebug(23100) << "Malformed script tag!" << endl; + document_.remove(inicio - begin_script_length, begin_script_length); + } + else + { + script_ += "\n" + document_.mid(inicio - begin_script_length, + fim - inicio + begin_script_length); + + document_.remove(inicio - begin_script_length, + fim - inicio + begin_script_length); + } + } + } + while(inicio != -1); +} + + + + +#include <iostream> +void HtmlParser::mostra() const +{ + kdDebug(23100) << "\nA:\n\n"; + for(unsigned int i = 0; i != nodes_.size(); ++i) + { + if(nodes_[i]->element() == Node::A) + kdDebug(23100) << nodes_[i]->url() << "\t" << nodes_[i]->linkLabel() << endl; + } + kdDebug(23100) << "____________________________________________________________________" << endl; + + kdDebug(23100) << "\nLINK:\n\n"; + for(unsigned int i = 0; i != nodes_.size(); ++i) + { + if(nodes_[i]->element() == Node::LINK) + kdDebug(23100) << nodes_[i]->url() << "\t" << nodes_[i]->linkLabel() << endl; + } + kdDebug(23100) << "____________________________________________________________________" << endl; + + kdDebug(23100) << "\nMETA:\n"; + for(unsigned int i = 0; i != nodes_.size(); ++i) + { + if(nodes_[i]->element() == Node::META) + { +#if defined Q_WS_WIN + NodeMETA* nm = (NodeMETA*)nodes_[i]; +#else + + NodeMETA* nm = dynamic_cast<NodeMETA*>(nodes_[i]); +#endif + + kdDebug(23100) << nm->url() << endl + << nm->atributoHTTP_EQUIV() << endl + << nm->atributoNAME() << endl + << nm->atributoCONTENT() << endl; + } + } + kdDebug(23100) << "____________________________________________________________________" << endl; + + kdDebug(23100) << "\nIMG:\n\n"; + for(unsigned int i = 0; i != nodes_.size(); ++i) + { + if(nodes_[i]->element() == Node::IMG) + kdDebug(23100) << nodes_[i]->url() << "\t" + << nodes_[i]->linkLabel() << endl; + } + kdDebug(23100) << "____________________________________________________________________" << endl; + + kdDebug(23100) << "\nFRAME:\n\n"; + for(unsigned int i = 0; i != nodes_.size(); ++i) + { + if(nodes_[i]->element() == Node::FRAME) + kdDebug(23100) << nodes_[i]->url() << endl; + } + kdDebug(23100) << "____________________________________________________________________" << endl; + + kdDebug(23100) << "\nBASE:\n\n"; + kdDebug(23100) << node_BASE_.url() << endl; + + kdDebug(23100) << "____________________________________________________________________" << endl; + +} + +#ifdef HTMLPARSER + +#include <fstream> + +int main() +{ + //ifstream stream("aterraprometida.html"); + //ifstream stream("/var/www/html/STL/standard_library.html"); + //ifstream stream("/var/www/html/qt-doc/functions.html"); + ifstream stream("/var/www/html/index.html"); + + QString content; + while(stream) + { + char c; + stream.get(c); + content += c; + } + // kdDebug(23100) << content << endl; + kdDebug(23100) << "__________________________________________________________" << endl; + HtmlParser parser(content); + parser.mostra(); + kdDebug(23100) << "__________________________________________________________\n\n\n" << endl; + vector<Node*> nods = parser.nodes(); + for(int i = 0; i != nods.size(); ++i) + { + if(nods[i]->element() == Node::META) + { + NodeMETA* nod_meta = (NodeMETA*)(nods[i]); + //Node* nod_meta = nods[i]; + + kdDebug(23100) << nod_meta->atributoCONTENT() << endl; + } + + } +} + + +#endif diff --git a/klinkstatus/src/parser/htmlparser.h b/klinkstatus/src/parser/htmlparser.h new file mode 100644 index 00000000..cf487ebf --- /dev/null +++ b/klinkstatus/src/parser/htmlparser.h @@ -0,0 +1,124 @@ + /*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef HTML_PARSER_H +#define HTML_PARSER_H + +#include <qstring.h> + +#include <vector> + + +#include "mstring.h" +#include "node.h" + +#include <iostream> + +using namespace std; + +typedef unsigned int uint; + + + +class HtmlParser +{ +public: + + HtmlParser(); + HtmlParser(QString const& documento); + ~HtmlParser(); + + vector<Node*> const& nodes() const; + bool hasBaseUrl() const; + bool hasTitle() const; + bool hasContentType() const; + NodeBASE const& baseUrl() const; + NodeTITLE const& title() const; + NodeMETA const& contentTypeMetaNode() const; + + static uint estimativaLinks(uint doc_size); + /** + * Convenience function for performance as it only parse in order + * to get the charset. + */ + static QString findCharsetInMetaElement(QString const& html); + + // test: + void mostra() const; + +private: + + vector<QString> const& parseNodesOfType(QString const& element); + /** + * Vector nodes passed for performance. + */ + static void parseNodesOfType(QString const& element, QString const& doc, vector<QString>& nodes); + + void parseNodesOfTypeA(); + void parseNodesOfTypeAREA(); + void parseNodesOfTypeLINK(); + void parseNodesOfTypeMETA(); + void parseNodesOfTypeIMG(); + void parseNodesOfTypeFRAME(); + void parseNodesOfTypeIFRAME(); + void parseNodesOfTypeBASE(); + void parseNodesOfTypeTITLE(); + + void stripComments(); + void stripScriptContent(); + + /** + Return the index of the next character of the end of tag. + e.g. + endOfTag("<img src=\"bad > luck\">") => 22 (not 15) + */ + static int endOfTag(QString const& s, int index = 0, QChar end_of_tag = '>'); + +private: + + vector<QString> aux_; // for what the hell is this? looks ugly... maybe I was drunk, can't remember + vector<Node*> nodes_; + NodeBASE node_BASE_; + NodeTITLE node_TITLE_; + NodeMETA node_META_content_type_; + bool is_content_type_set_; + + QString document_; + QString script_; // Fica aqui guardado (JavaScript, etc) + QString comments_; +}; + + +inline HtmlParser::~HtmlParser() +{ + //kdDebug(23100) << "*"; +} + +inline uint HtmlParser::estimativaLinks(uint doc_size) +{ + return doc_size / 100; // valor estimado... +} + +inline bool HtmlParser::hasContentType() const +{ + return is_content_type_set_; +} + +#endif diff --git a/klinkstatus/src/parser/http.cpp b/klinkstatus/src/parser/http.cpp new file mode 100644 index 00000000..1133c937 --- /dev/null +++ b/klinkstatus/src/parser/http.cpp @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "http.h" +#include "mstring.h" + +#include <kdebug.h> + +#include <iostream> + + + +void HttpResponseHeader::parseLocation() +{ + QString cabecalho(toString()); + + int location = findWord(cabecalho, "Location: "); + Q_ASSERT(location != -1); + + int fim_de_linha_1 = cabecalho.find('\n', location); + int fim_de_linha_2 = cabecalho.find('\r', location); + + Q_ASSERT(fim_de_linha_1 != -1 || fim_de_linha_2 != -1); + + int fim_de_linha; + + if(fim_de_linha_1 == -1 && fim_de_linha_2 != -1) + fim_de_linha = fim_de_linha_2; + + else if(fim_de_linha_2 == -1 && fim_de_linha_1 != -1) + fim_de_linha = fim_de_linha_1; + + else if(fim_de_linha_1 < fim_de_linha_2) + fim_de_linha = fim_de_linha_1; + + else fim_de_linha = fim_de_linha_2; + + location_ = cabecalho.mid(location, fim_de_linha - location); +} + +QString HttpResponseHeader::charset() const +{ + return HttpResponseHeader::charset(value("content-type")); +} + +QString HttpResponseHeader::charset(QString const& contentTypeHttpHeaderLine) +{ + QString _charset; + + if(contentTypeHttpHeaderLine.isEmpty()) + return _charset; + + int index = contentTypeHttpHeaderLine.find("charset="); + if(index != -1) + index += QString("charset=").length(); + else { + index = contentTypeHttpHeaderLine.find("charset:"); + if(index != -1) + index += QString("charset:").length(); + } + + if(index != -1) { + _charset = contentTypeHttpHeaderLine.mid(index); + _charset = _charset.stripWhiteSpace(); + } + +// kdDebug(23100) << "Charset: |" << _charset << "|" << endl; + return _charset; + +} diff --git a/klinkstatus/src/parser/http.h b/klinkstatus/src/parser/http.h new file mode 100644 index 00000000..5878cfd1 --- /dev/null +++ b/klinkstatus/src/parser/http.h @@ -0,0 +1,79 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef HTTP_H +#define HTTP_H + +#include <qhttp.h> +#include <qstring.h> + + +class HttpResponseHeader: public QHttpResponseHeader +{ +public: + + HttpResponseHeader(); + HttpResponseHeader(const QHttpResponseHeader & header); + HttpResponseHeader(QString const& str); + virtual ~HttpResponseHeader(); + + void parseLocation(); + QString const& location() const; + QString charset() const; + + /** + * Parses the charset from this kind of server response: + * Content-Type: text/html; charset=EUC-JP + * Return an empty string in case it doesn't find nothing. + */ + static QString charset(QString const& contentTypeHttpHeaderLine); + +private: + + QString location_; +}; + + +inline HttpResponseHeader::HttpResponseHeader() + : QHttpResponseHeader() +{ +} + +inline HttpResponseHeader::HttpResponseHeader(const QHttpResponseHeader & /*header*/) + : QHttpResponseHeader() +{ +} + +inline HttpResponseHeader::HttpResponseHeader(QString const& str) + : QHttpResponseHeader() +{ + parse(str); +} + +inline HttpResponseHeader::~HttpResponseHeader() +{ +} + +inline QString const& HttpResponseHeader::location() const +{ + return location_; +} + +#endif diff --git a/klinkstatus/src/parser/mstring.cpp b/klinkstatus/src/parser/mstring.cpp new file mode 100644 index 00000000..114d6dc6 --- /dev/null +++ b/klinkstatus/src/parser/mstring.cpp @@ -0,0 +1,278 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "mstring.h" + +#include <iostream> + + +using namespace std; + + +int findWord(QString const& s, QString const& palavra, uint a_partir_do_indice) +{ + int indice = s.find(palavra, a_partir_do_indice, false); + + if(indice == -1) + return indice; + else + return (indice + palavra.length()); +} + +int findChar(QString const& s, QChar letra, uint a_partir_do_indice) +{ + int index = s.find(letra, a_partir_do_indice, false); + if(index == -1) + return index; + else + return index + 1; +} + +/** + The string palavra, must not have any spaces. +*/ +int findSeparableWord(QString const& s_, QString const& palavra, uint a_partir_do_indice) +{ + bool encontrou = true; + QString s(s_); + uint indice_palavra = 0; + int indice = a_partir_do_indice; + + do + { + encontrou = true; + indice_palavra = 0; + + indice = findChar(s, (palavra[indice_palavra++]), indice ); + + if(indice == -1) + { + return indice; + } + --indice; + + while(encontrou && indice_palavra != palavra.length() && indice < (int)s.length()) + { + indice = nextNonSpaceChar(s, indice); + + if(indice == -1) + return indice; + + // Nao se incrementa o indice porque isso j��feito com a fun�o nextNonSpaceChar + encontrou = encontrou && !(notEqual(s[indice], palavra[indice_palavra++]) ); + + } + } + while(!encontrou && indice < (int)s.length()); + + if(encontrou && indice < (int)s.length()) + return ++indice; + else + return -1; +} + +int nextNonSpaceChar(QString const& s, uint i) +{ + ++i; + // while( (s[i] == ' ' || s[i] == '\t' || s[i] == '\r' || s[i] == '\n') + while(isSpace(s[i]) + && i < s.length() ) + ++i; + + if(i < s.length()) + return i; + else + return -1; +} + + +/** + e.g. + nextSpaceChar("o biltre") => 1 +*/ +int nextSpaceChar(QString const& s, uint i) +{ + //while( (s[i] != ' ' && s[i] != '\r' && s[i] != '\n' && s[i] != '\t') && + //i < s.size() ) + while(!isSpace(s[i]) && + i < s.length() ) + ++i; + + if(i < s.length()) + return i; + else + return -1; +} + +int nextCharDifferentThan(QChar c, QString const& s, uint i) +{ + while(i < s.length() && s[i] == c) + ++i; + + if(i != s.length()) + return i; + else + return -1; +} + +vector<QString> tokenize(QString s) +{ + Q_ASSERT(!s.isEmpty()); + vector<QString> v; + + while(true) + { + int inicio = 0; + //if(s[0] == ' ' || s[0] == '\t' || s[0] == '\r' || s[0] == '\n') + if(isSpace(s[0])) + inicio = nextNonSpaceChar(s, 0); + if(inicio == -1) + return v; + + int fim = nextSpaceChar(s, inicio); + if(fim == -1) + { + v.push_back(s.mid(inicio)); + return v; + } + else + { + QString palavra = s.mid(inicio, fim - inicio); + v.push_back(palavra); + s.remove(0, fim); + } + } +} + +vector<QString> tokenizeWordsSeparatedByDots(QString s) +{ + vector<QString> v; + + while(true) + { + int inicio = 0; + if(s[0] == '.') + inicio = nextCharDifferentThan(QChar('.'), s, 0); + if(inicio == -1) + return v; + + int fim = s.find('.', inicio); + if(fim == -1) + { + v.push_back(s.mid(inicio)); + return v; + } + else + { + QString palavra = s.mid(inicio, fim - inicio); + v.push_back(palavra); + s.remove(0, fim); + } + } +} + +vector<QString> tokenizeWordsSeparatedBy(QString s, QChar criteria) +{ + vector<QString> v; + + while(true) + { + int inicio = 0; + if(s[0] == criteria) + inicio = nextCharDifferentThan(criteria, s, 0); + if(inicio == -1) + return v; + + int fim = s.find(criteria, inicio); + if(fim == -1) + { + v.push_back(s.mid(inicio)); + return v; + } + else + { + QString palavra = s.mid(inicio, fim - inicio); + v.push_back(palavra); + s.remove(0, fim); + } + } +} + + + +#ifdef STRING +//c++ -g -o teste_string mstring.cpp -DSTRING +#include <fstream> + +int main(int argc, char* argv[]) +{ + string s; + s = "S"; + s = "Afazer"; + s = "O MeU S sdadsadd "; + s = "www.trolltech.com/search/qt-interest/bla bla%20Bla"; + s = "...http://w.ww..go.o.gle.p.t......."; + + /* + ifstream stream("testeparser.html"); + string content; + while(stream) { + char c; + stream.get(c); + content += c; + } + */ + // kdDebug(23100) << simplifyWhiteSpace(content) << endl; + kdDebug(23100) << simplifyWhiteSpace(s) << endl; + + /* + vector<string> v(tokenize(s)); + for(int i = 0; i != v.size(); ++i) + kdDebug(23100) << v[i] << endl; + */ + + /* + int i = nextSpaceChar(s, 0); + i = nextNonSpaceChar(s, i); + kdDebug(23100) << s.substr(i) << endl; + */ + + + vector<string> v(tokenizeWordsSeparatedByDots(s)); + for(int i = 0; i != v.size(); ++i) + kdDebug(23100) << v[i] << endl; + + removeLastCharIfExists(s, '/'); + kdDebug(23100) << s << endl; + + /* + kdDebug(23100) << findChar(s, 'T') << endl; + kdDebug(23100) << findWord(s, "trolltech") << endl; + kdDebug(23100) << findWord(s, "TROLLTECH") << endl; + kdDebug(23100) << findWord(s, "TROLLTECH", 2) << endl; + */ + /* + stripWhiteSpace(s); + kdDebug(23100) << s << endl; + */ +} + + +#endif diff --git a/klinkstatus/src/parser/mstring.h b/klinkstatus/src/parser/mstring.h new file mode 100644 index 00000000..cd359c7d --- /dev/null +++ b/klinkstatus/src/parser/mstring.h @@ -0,0 +1,174 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef STRING_H +#define STRING_H + +#include <qstring.h> + +#include <vector> +#include <cctype> + +class QString; + +typedef unsigned int uint; + + +/* Similar to std::string::find but return the next index of the last char + of the first word it finds. + Case insensitive. + e.g. + findWord("Biltre larvado", "biltre") => 6 +*/ +int findWord(QString const& s, QString const& palavra, uint a_partir_do_indice = 0); + +/** + Similar to std::string::find but return the next index of the first char + it finds. + Case insensitive. +*/ +int findChar(QString const& s, QChar letra, uint a_partir_do_indice = 0); + +/** + Same as findWord but non space chars are eliminated. + e.g. + findWord("<a href=""></a>", "<a") => 2 + findSeparableWord("<a href=""></a>", "<a") => 2 + + findWord("<\na href=""></a>", "<a") => -1 + findSeparableWord("<\na href=""></a>", "<a") => 3 +*/ +int findSeparableWord(QString const& s, QString const& palavra, uint a_partir_do_indice = 0); + +/** + Space means Unicode characters with decimal values + 9 (TAB), 10 (LF), 11 (VT), 12 (FF), 13 (CR), and 32 (Space). +*/ +bool isSpace(QChar c); + +/** + Return -1 if unsuccessful. +*/ +int nextNonSpaceChar(QString const& s, uint i); +int nextNonSpaceCharReverse(QString const& s, uint i); +int nextSpaceChar(QString const& s, uint i); + +int nextCharDifferentThan(QChar c, QString const& s, uint i); + +/** Return a vector with the words */ +std::vector<QString> tokenize(QString s); +std::vector<QString> tokenizeWordsSeparatedByDots(QString s); +std::vector<QString> tokenizeWordsSeparatedBy(QString s, QChar criteria); + +/** + Returns a string that has whitespace removed from the start and the end, + and which has each sequence of internal whitespace replaced with a single space. +*/ +QString simplifyWhiteSpace(QString const& s); + +/** + If char 'caractere' is the last in the string 's' it is removed +*/ +void removeLastCharIfExists(QString& s, QChar caractere); + +QString upperCase(QString const& s); +QString lowerCase(QString const& s); + +/** + Remove whitespaces from the end of the string +*/ +void stripWhiteSpaceFromTheEnd(QString& s); + +/** + Returns a string that has whitespace removed from the start and the end. +*/ +void stripWhiteSpace(QString& s); + +/** + Case insensitive comparisons +*/ +bool equal(QString const& s1, QString const& s2); +bool notEqual(QString const& s1, QString const& s2); + +bool equal(QChar c1, QChar c2); +bool notEqual(QChar c1, QChar c2); + + +//_________________________________________________________________________ + +inline bool isSpace(QChar c) +{ + return c.isSpace(); +} + +inline bool equal(QString const& s1, QString const& s2) +{ + if(s1 == s2) + return true; + else + return s1.lower() == s2.lower(); +} + +inline bool notEqual(QString const& s1, QString const& s2) +{ + return !(equal(s1, s2)); +} + +inline bool equal(QChar c1, QChar c2) +{ + return c1.lower() == c2.lower(); +} + +inline bool notEqual(QChar c1, QChar c2) +{ + return !(equal(c1, c2)); +} + +inline QString upperCase(QString const& s) +{ + return s.upper(); +} + +inline QString lowerCase(QString const& s) +{ + return s.lower(); +} + +inline QString simplifyWhiteSpace(QString const& s) +{ + return s.simplifyWhiteSpace(); +} + +inline void removeLastCharIfExists(QString& s, QChar caractere) +{ + int index = s.length() - 1; + if(s[index] == caractere) + s.remove(index); +} + +inline void stripWhiteSpace(QString& s) +{ + s = s.stripWhiteSpace(); +} + + + + +#endif diff --git a/klinkstatus/src/parser/node.cpp b/klinkstatus/src/parser/node.cpp new file mode 100644 index 00000000..068184ae --- /dev/null +++ b/klinkstatus/src/parser/node.cpp @@ -0,0 +1,255 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "node.h" +#include "mstring.h" +#include "url.h" +#include "../utils/utils.h" + + +/* + Node________________________________________________________________________ +*/ + +QString Node::getAttribute(QString const& atributo) +{ + QString attribute_; + int fim = - 1; + bool tem_aspas_ou_plicas = false; + + int inicio = findWord(content_, atributo); + if(inicio != -1) + { + if(content_[inicio] == '"') + { + fim = content_.find("\"", inicio + 1); + tem_aspas_ou_plicas = true; + } + else if(content_[inicio] == '\'') + { + fim = content_.find("'", inicio + 1); + tem_aspas_ou_plicas = true; + } + else + { + int fim_bloco = nextSpaceChar(content_, inicio + 1); + int fim_tag = content_.find(">", inicio + 1); + int fim_aspas = content_.find("\"", inicio + 1); + + if(fim_bloco == -1 && fim_tag == -1 && fim_aspas == -1) + { + attribute_ = content_; + malformed_ = true; + return attribute_; + } + + if(smallerUnsigned(fim_bloco, fim_tag) == -1 && + smallerUnsigned(fim_bloco, fim_aspas) == -1) + fim = fim_bloco; + + else if(smallerUnsigned(fim_tag, fim_aspas) == -1) + fim = fim_tag; + + else + fim = fim_aspas; + } + + if(fim == -1) + { + attribute_ = content_; + malformed_ = true; + return attribute_; + } + + attribute_ = content_.mid(inicio, fim-inicio); + + if(tem_aspas_ou_plicas) + { + attribute_ = attribute_.mid(1, attribute_.length() - 1); + } + else + { + ::stripWhiteSpace(attribute_); + } + } + + else + { + attribute_ = ""; + } + ::decode(attribute_); + + return attribute_; +} + + +/* + NodeLink________________________________________________________________________ +*/ + +void NodeLink::parseAttributeHREF() +{ + if(findWord(content(), "HREF") == -1 && + findWord(content(), "NAME") == -1 && + findWord(content(), "TARGET") == -1) + { + kdDebug(23100) << "MALFORMED: " << endl + << "NodeLink::parseAttributeHREF: " << content() << endl; + setMalformed(true); + return; + } + + else if(findWord(content(), "HREF") != -1) + { + attribute_href_ = getAttribute("HREF="); + + if( !(malformed() || attribute_href_.isEmpty()) ) + { + // Definnishr o tipo de link + linktype_ = Url::resolveLinkType(attribute_href_); + + parseLinkLabel(); + } + } +} + +void NodeLink::parseLinkLabel() +{ + int fim_tag = 0; + char proximo_caractere = ' '; + + do + { + fim_tag = content_.find(">", fim_tag); + + if(fim_tag != -1) + proximo_caractere = QChar(content_[++fim_tag]); + + } + while(fim_tag != -1 && proximo_caractere == '<'/*If the label starts by <*/); + + if(fim_tag != -1) + { + int fim_label = content_.find("<", fim_tag); + + if(fim_label != -1) + { + link_label_ = + ::simplifyWhiteSpace(content_.mid(fim_tag, + fim_label - fim_tag)); + } + } +} + + +/* + NodeMETA________________________________________________________________________ +*/ + +void NodeMETA::parseAttributeURL() +{ + if(attribute_http_equiv_.isEmpty()) + parseAttributeHTTP_EQUIV(); + + if(upperCase(attribute_http_equiv_) == "REFRESH") + { + is_redirection_ = true; + + if(findWord(content(), "URL") == -1) + { + //setMalformed(true); + return; + } + + attribute_url_ = getAttribute("URL="); + + int aspas = -1; + do + { + aspas = attribute_url_.find("\""); + if(aspas != -1) + attribute_url_.remove(aspas, 1); + } + while(aspas != -1); + + if(attribute_url_.isEmpty()) + kdDebug(23100) << "void NodeMeta::parseAttributeURL(): Assertion `!attribute_url_.isEmpty()' failed.\n" + << content_ << endl << attribute_http_equiv_ << endl << attribute_url_ << endl; + Q_ASSERT(!attribute_url_.isEmpty()); + + linktype_ = Url::resolveLinkType(attribute_url_); + } +} + +QString NodeMETA::charset() const +{ + QString charset; + QString content(atributoCONTENT()); + + if(content.isEmpty()) + return charset; + + int index = content.find("charset="); + if(index != -1) + { + index += QString("charset=").length(); + charset = content.mid(index, content.length() - index); + charset = charset.stripWhiteSpace(); + } + +// kdDebug(23100) << "Charset: |" << charset << "|" << endl; + return charset; +} + +/* + NodeIMG________________________________________________________________________ +*/ + +void NodeIMG::parseAttributeSRC() +{ + if(findWord(content(), "SRC") == -1) + { + kdDebug(23100) << "MALFORMED_____________________________________________________________" << endl; + kdDebug(23100) << "Conteudo: " << content() << endl; + setMalformed(true); + return; + } + + attribute_src_ = getAttribute("SRC="); + linktype_ = Url::resolveLinkType(attribute_src_); +} + + +/* + NodeFRAME________________________________________________________________________ +*/ + +void NodeFRAME::parseAttributeSRC() +{ + if(findWord(content(), "SRC") == -1) + { + //setMalformed(true); + return; + } + + attribute_src_ = getAttribute("SRC="); + linktype_ = Url::resolveLinkType(attribute_src_); +} + diff --git a/klinkstatus/src/parser/node.h b/klinkstatus/src/parser/node.h new file mode 100644 index 00000000..1d0b1fc3 --- /dev/null +++ b/klinkstatus/src/parser/node.h @@ -0,0 +1,279 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef NODULO_H +#define NODULO_H + +#include "mstring.h" + +#include <qstring.h> + +#include <kdebug.h> +#include <kcharsets.h> + +using namespace std; + +typedef unsigned int uint; + + +class Node +{ +public: + + enum Element { + A, + AREA, + LINK, + META, + IMG, + FRAME, + BASE, + TITLE + }; + enum LinkType { + href, + file_href, + mailto, + relative + }; + + Node(); + Node(QString const& content); + virtual ~Node(); + + QString getAttribute(QString const& atributo); + virtual QString const& url() const = 0; + virtual QString const& linkLabel() const = 0; // URL label + virtual void setNode(QString const& content); + virtual void parse() = 0; + void setMalformed(bool flag = true); + virtual void setLinkType(LinkType const& lt); + + QString const& content() const; + bool malformed() const; + LinkType linkType() const; + Element element() const; + virtual bool isLink() const = 0; + + bool isRedirection() const; + +protected: + + Element element_; + LinkType linktype_; + QString link_label_; + QString content_; + bool is_redirection_; + bool malformed_; +}; + + +class NodeLink: public Node +{ +public: + NodeLink(); + NodeLink(QString const& content); + ~NodeLink() + {} + ; + + virtual void parse(); + + virtual QString const& url() const; + virtual QString const& linkLabel() const; // URL label + virtual QString mailto() const; + virtual bool isLink() const; + +private: + virtual void parseAttributeHREF(); + void parseLinkLabel(); + +private: + QString attribute_href_; +}; + +class NodeA: public NodeLink +{ +public: + NodeA(QString const& content); + ~NodeA() + {} + ; + QString const& attributeNAME() const; + + virtual void parse(); + +private: + void parseAttributeNAME(); + +private: + QString attribute_name_; +}; + +class NodeAREA: public NodeLink +{ +public: + NodeAREA(QString const& content); + ~NodeAREA() {}; + + QString const& attributeTITLE() const; + + virtual void parse(); + +private: + void parseAttributeTITLE(); + +private: + QString attribute_title_; +}; + + +class NodeLINK: public NodeLink +{ +public: + NodeLINK(QString const& content); + ~NodeLINK() + {} + ; +}; + +class NodeMETA: public Node +{ +public: + NodeMETA(); + NodeMETA(QString const& content); + ~NodeMETA() + {} + ; + + virtual QString const& url() const; + virtual const QString& linkLabel() const; + virtual bool isLink() const; + QString const& atributoHTTP_EQUIV() const; + QString const& atributoNAME() const; + QString const& atributoCONTENT() const; + QString charset() const; + bool isRedirection() const; + + virtual void parse(); + +private: + /** + Procura se existem os atributos HTTP-EQUIV=Refresh e URL=... + Se existir considera o content do atributo URL como um link. + ex: <META HTTP-EQUIV=Refresh CONTENT="10; URL=http://www.htmlhelp.com/"> + */ + void parseAttributeURL(); + + void parseAttributeHTTP_EQUIV(); + void parseAttributeNAME(); + void parseAttributeCONTENT(); + +private: + QString attribute_http_equiv_; + QString attribute_url_; + QString attribute_name_; + QString attribute_content_; +}; + +class NodeIMG: public Node +{ +public: + NodeIMG(QString const& content); + ~NodeIMG() + {} + ; + + virtual void parse(); + + virtual QString const& url() const; + virtual QString const& linkLabel() const; // Image label + virtual bool isLink() const; + +private: + void parseAttributeSRC(); + void parseAttributeTITLE(); + void parseAttributeALT(); + +private: + QString attribute_src_; + QString attribute_title_; + QString attribute_alt_; +}; + +class NodeFRAME: public Node +{ +public: + NodeFRAME(QString const& content); + ~NodeFRAME() + {} + ; + + virtual void parse(); + virtual QString const& url() const; + virtual QString const& linkLabel() const; + virtual bool isLink() const; + +private: + void parseAttributeSRC(); + +private: + QString attribute_src_; +}; + +class NodeBASE: public NodeLink +{ +public: + NodeBASE(); + NodeBASE(QString const& content); + ~NodeBASE() + {} + ; + + virtual bool isLink() const; +}; + +class NodeTITLE: public Node +{ +public: + NodeTITLE(); + NodeTITLE(QString const& content); + ~NodeTITLE() + {} + ; + + virtual QString const& url() const; + virtual QString const& linkLabel() const; + virtual void parse(); + virtual bool isLink() const; + + QString const& attributeTITLE() const; + +private: + void parseAttributeTITLE(); + +private: + QString attribute_title_; +}; + + +#include "node_impl.h" + +#endif diff --git a/klinkstatus/src/parser/node_impl.h b/klinkstatus/src/parser/node_impl.h new file mode 100644 index 00000000..51249075 --- /dev/null +++ b/klinkstatus/src/parser/node_impl.h @@ -0,0 +1,412 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +inline Node::Node() + : is_redirection_(false), malformed_(false) +{} + +inline Node::~Node() +{ + //kdDebug(23100) << "/"; +} + +inline Node::Node(QString const& content) + : content_(content), is_redirection_(false), malformed_(false) +{} + +inline void Node::setNode(QString const& content) +{ + content_ = content; + parse(); +} + +inline QString const& Node::content() const +{ + return content_; +} + +inline bool Node::malformed() const +{ + return malformed_; +} + +inline void Node::setMalformed(bool flag) +{ + malformed_ = flag; +} + +inline Node::LinkType Node::linkType() const +{ + return linktype_; +} + +inline Node::Element Node::element() const +{ + return element_; +} + +inline void Node::setLinkType(Node::LinkType const& lt) +{ + linktype_ = lt; +} + +inline bool Node::isRedirection() const +{ + return is_redirection_; +} + +// class NodeLink_______________________________________________________ + +inline NodeLink::NodeLink() + : Node() +{} + +inline NodeLink::NodeLink(QString const& content) + : Node(content) +{ + parse(); +} + +inline void NodeLink::parse() +{ + parseAttributeHREF(); +} + +inline QString const& NodeLink::url() const +{ + return attribute_href_; +} + +inline QString const& NodeLink::linkLabel() const +{ + return link_label_; +} + +inline QString NodeLink::mailto() const +{ + Q_ASSERT(linktype_ == Node::mailto); + + QString href = KCharsets::resolveEntities(attribute_href_); + + int inicio = findWord(href, "MAILTO:"); + Q_ASSERT(inicio != -1); + + return href.mid(inicio); +} + +inline bool NodeLink::isLink() const +{ + if(Node::linkType() != Node::mailto && !url().isEmpty()) + return true; + else + return false; +} + +// class NodeA_______________________________________________________ + +inline NodeA::NodeA(QString const& content) + : NodeLink(content) +{ + element_ = A; + parse(); +} + +inline QString const& NodeA::attributeNAME() const +{ + return attribute_name_; +} + +inline void NodeA::parse() +{ + parseAttributeNAME(); +} + +inline void NodeA::parseAttributeNAME() +{ + attribute_name_ = getAttribute("NAME="); + //kdDebug(23100) << "NodeA::parseAttributeNAME: " << attribute_name_ << endl; +} + +// class NodeAREA_______________________________________________________ + +inline NodeAREA::NodeAREA(QString const& content) + : NodeLink(content) +{ + element_ = AREA; + parse(); +} + +inline QString const& NodeAREA::attributeTITLE() const +{ + return attribute_title_; +} + +inline void NodeAREA::parse() +{ + parseAttributeTITLE(); +} + +inline void NodeAREA::parseAttributeTITLE() +{ + attribute_title_ = getAttribute("TITLE="); +//kdDebug(23100) << "NodeAREA::parseAttributeTITLE: " << attribute_title_ << endl; +} + +// class NodeLINK________________________________________ + +inline NodeLINK::NodeLINK(QString const& content) + : NodeLink(content) +{ + element_ = LINK; +} + +// class NodeMeta________________________________________ + +inline NodeMETA::NodeMETA() + : Node() +{ + element_ = META; +} + +inline NodeMETA::NodeMETA(QString const& content) + : Node(content) +{ + element_ = META; + parse(); +} + +inline QString const& NodeMETA::url() const +{ + return attribute_url_; +} + +inline const QString& NodeMETA::linkLabel() const +{ + return link_label_; +} + +inline bool NodeMETA::isLink() const +{ + if(upperCase(attribute_http_equiv_) == "REFRESH" && + findWord(content(), "URL") != -1) + { + // Q_ASSERT(findWord(content(), "URL") != -1); // not necessarily + return true; + } + else + return false; +} + +inline QString const& NodeMETA::atributoHTTP_EQUIV() const +{ + return attribute_http_equiv_; +} + +inline QString const& NodeMETA::atributoNAME() const +{ + return attribute_name_; +} + +inline QString const& NodeMETA::atributoCONTENT() const +{ + return attribute_content_; +} + +inline bool NodeMETA::isRedirection() const +{ + return + upperCase(attribute_http_equiv_) == "REFRESH"; +} + +inline void NodeMETA::parse() +{ + parseAttributeHTTP_EQUIV(); + parseAttributeNAME(); + parseAttributeCONTENT(); + + parseAttributeURL(); +} + +inline void NodeMETA::parseAttributeHTTP_EQUIV() +{ + attribute_http_equiv_ = getAttribute("HTTP-EQUIV="); +} + +inline void NodeMETA::parseAttributeNAME() +{ + attribute_name_ = getAttribute("NAME="); +} + +inline void NodeMETA::parseAttributeCONTENT() +{ + attribute_content_ = getAttribute("CONTENT="); +// kdDebug(23100) << "CONTENT: " << attribute_content_ << endl; +} + + +// class NodeIMG________________________________________ + +inline NodeIMG::NodeIMG(QString const& content) + : Node(content) +{ + element_ = IMG; + parse(); +} + +inline void NodeIMG::parse() +{ + parseAttributeSRC(); + parseAttributeTITLE(); + parseAttributeALT(); +} + +inline QString const& NodeIMG::url() const +{ + return attribute_src_; +} + +inline QString const& NodeIMG::linkLabel() const +{ + if(!attribute_title_.isEmpty()) + return attribute_title_; + else + return attribute_alt_; +} + +inline bool NodeIMG::isLink() const +{ + if(!url().isEmpty()) + return true; + else + return false; +} + +inline void NodeIMG::parseAttributeTITLE() +{ + attribute_title_ = getAttribute("TITLE="); +} + +inline void NodeIMG::parseAttributeALT() +{ + attribute_alt_ = getAttribute("ALT="); +} + + +// class NodeFRAME________________________________________ + +inline NodeFRAME::NodeFRAME(QString const& content) + : Node(content) +{ + element_ = FRAME; + parse(); +} + +inline void NodeFRAME::parse() +{ + parseAttributeSRC(); +} + +inline QString const& NodeFRAME::url() const +{ + return attribute_src_; +} + +inline QString const& NodeFRAME::linkLabel() const +{ + return link_label_; +} + +inline bool NodeFRAME::isLink() const +{ + if(!url().isEmpty()) + return true; + else + return false; +} + +// class NodeBASE________________________________________ + +inline NodeBASE::NodeBASE() + : NodeLink() +{ + element_ = BASE; +} + +inline NodeBASE::NodeBASE(QString const& content) + : NodeLink(content) +{ + element_ = BASE; +} + +inline bool NodeBASE::isLink() const +{ + return false; +} + +// class NodeTITLE________________________________________ + +inline NodeTITLE::NodeTITLE() + : Node() +{ + element_ = TITLE; + parse(); +} + +inline NodeTITLE::NodeTITLE(QString const& content) + : Node(content) +{ + element_ = TITLE; + parse(); +} + +inline QString const& NodeTITLE::url() const +{ + return QString::null; +} + +inline QString const& NodeTITLE::linkLabel() const +{ + return QString::null; +} + +inline void NodeTITLE::parse() +{ + parseAttributeTITLE(); +} + +inline bool NodeTITLE::isLink() const +{ + return false; +} + +inline QString const& NodeTITLE::attributeTITLE() const +{ + return attribute_title_; +} + +inline void NodeTITLE::parseAttributeTITLE() +{ + attribute_title_ = content_; + attribute_title_.replace("<TITLE>", "", false); + attribute_title_.replace("</TITLE>", "", false); + attribute_title_.stripWhiteSpace(); + + //kdDebug(23100) << "TITLE: " << attribute_title_ << endl; +} diff --git a/klinkstatus/src/parser/url.cpp b/klinkstatus/src/parser/url.cpp new file mode 100644 index 00000000..f7f1f6f8 --- /dev/null +++ b/klinkstatus/src/parser/url.cpp @@ -0,0 +1,350 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include <kresolver.h> + +#include "url.h" +#include "mstring.h" +#include "../utils/utils.h" + +#include <kcharsets.h> + + +Node::LinkType Url::resolveLinkType(QString const& url) +{ + QString aux(url); + aux = KURL::decode_string(aux); + + if(aux.isNull()) + return Node::relative; + + if(findWord(url, "FILE:") != -1) + return Node::file_href; + else if(findWord(KCharsets::resolveEntities(url), "MAILTO:") != -1) + return Node::mailto; + else if( (int)url.find(":/") != -1) + return Node::href; + else + return Node::relative; +} + +KURL Url::normalizeUrl(QString const& string_url, LinkStatus const& link_parent, QString const& document_root) +{ + QString _string_url = string_url.stripWhiteSpace(); + + QString s_url; + KURL base_url; + + // resolve base url + if(link_parent.hasBaseURI()) + base_url = link_parent.baseURI(); + else + base_url = link_parent.absoluteUrl(); + + // resolve relative url + if(_string_url.isEmpty()) + return base_url; + else if(Url::hasProtocol(_string_url)) + return KURL(_string_url); + else + { + s_url.prepend(base_url.protocol() + "://" + base_url.host()); + + if(_string_url[0] == '/') { + if(!base_url.protocol().startsWith("http")) { + s_url.append(document_root); + } + } + else { + s_url.append(base_url.directory(true, false) + "/"); + } + + if( (_string_url[0] == ';' || // parameters + _string_url[0] == '?' || // query + _string_url[0] == '#') ) // fragment or reference + { + s_url.append(base_url.fileName(false)); + } + + s_url.append(_string_url); + KURL url(s_url); + if(base_url.hasUser()) + url.setUser(base_url.user()); + if(base_url.hasPass()) + url.setPass(base_url.pass()); + + url.setPort(base_url.port()); + + url.cleanPath(); + +// kdDebug(23100) << "Normalized URL: " +// << KCharsets::resolveEntities(KURL::decode_string(url.url())) << endl; + + return KURL(KCharsets::resolveEntities(KURL::decode_string(url.url()))); + } +} + +KURL Url::normalizeUrl(QString const& string_url) +{ + QString qs_url(KCharsets::resolveEntities(string_url.stripWhiteSpace())); + + if(qs_url[0] == '/') + { + KURL url; + url.setPath(qs_url); + url.cleanPath(); + return url; + } + + else + { + if(!Url::hasProtocol(qs_url)) + qs_url.prepend("http://"); + + KURL url(qs_url); + url.cleanPath(); + return url; + } +} + +bool Url::existUrl(KURL const& url, vector<LinkStatus*> const& v) +{ + if(url.prettyURL().isEmpty()) + return true; + + for(uint i = 0; i != v.size(); ++i) + if(v[i]->absoluteUrl() == url) + return true; + + return false; +} + +/** + www.iscte.pt, iscte.pt => true; + iscte.pt, www.iscte.pt => true; + www.iscte.pt, alunos.iscte.pt => true; (if restrict = false) + www.iscte.pt, alunos.iscte.pt => false; (if restrict = true) + alunos.iscte.pt, www.iscte.pt => false; + alunos.iscte.pt, iscte.pt => false. +*/ +// FIXME - Rename this function to sameDomain +bool Url::equalHost(QString const& host1, QString const& host2, bool restrict) +{ + //Q_ASSERT(!host1.isEmpty()); + //Q_ASSERT(!host2.isEmpty()); // this fails if href="javascript:......." + //if(host2.isEmpty()) + //return false; + + if(host1 == host2) + return true; + + QString host1_(KNetwork::KResolver::normalizeDomain(host1)); + QString host2_(KNetwork::KResolver::normalizeDomain(host2)); + removeLastCharIfExists(host1_, '/'); + removeLastCharIfExists(host2_, '/'); + + vector<QString> v1 = tokenizeWordsSeparatedByDots(host1_); + vector<QString> v2 = tokenizeWordsSeparatedByDots(host2_); + uint const size1 = v1.size(); + uint const size2 = v2.size(); + + if( !(size1 >= 1 && size2 >= 1) && // localhost would have size = 1 + !(host1_[0].isNumber() || host2_[0].isNumber()) ) // not (host == IP) + { + kdDebug(23100) << "Invalid host: " << host2 << endl; + return false; + } + + vector<QString>::size_type aux = 0; + vector<QString>::size_type aux2 = 0; + if(v1[0] == "www") + aux = 1; + if(v2[0] == "www") + aux2 = 1; + + if((size2 - aux2 < size1 - aux) && restrict) // e.g. paradigma.co.pt < linkstatus.paradigma.co.pt + return false; + + if(restrict && (size2 - aux2 > size1 - aux)) // e.g. linkstatus.paradigma.co.pt > paradigma.co.pt + return false; + + int i = 1; + while( ((int)(size1 - i) >= (int)aux) && ((int)(size2 - i) >= (int)aux) ) + { + if( !(v1[size1 - i] == v2[size2 - i]) ) + return false; + + ++i; + } + + return true; +} + +/* This should be done by parsing but I wan't to know when some new scheme comes along :) */ +bool Url::hasProtocol(QString const& url) +{ + QString s_url(url); + s_url.stripWhiteSpace(); + + if(s_url[0] == '/') + return false; + + else + { + KURL url = KURL::fromPathOrURL(s_url); + if(!url.protocol().isEmpty()) + return true; + /* + if(s_url.startsWith("http:") || + s_url.startsWith("https:") || + s_url.startsWith("ftp:") || + s_url.startsWith("sftp:") || + s_url.startsWith("webdav:") || + s_url.startsWith("webdavs:") || + s_url.startsWith("finger:") || + s_url.startsWith("fish:") || + s_url.startsWith("imap:") || + s_url.startsWith("imaps:") || + s_url.startsWith("lan:") || + s_url.startsWith("ldap:") || + s_url.startsWith("pop3:") || + s_url.startsWith("pop3s:") || + s_url.startsWith("smtp:") || + s_url.startsWith("smtps:") || + s_url.startsWith("file:") || + s_url.startsWith("news:") || + s_url.startsWith("gopher:") || + s_url.startsWith("mailto:") || + s_url.startsWith("telnet:") || + s_url.startsWith("prospero:") || + s_url.startsWith("wais:") || + s_url.startsWith("nntp:") ) + { + return true; + } + */ + else + return false; + } +} + +/** + http://linkstatus.paradigma.co.pt/en/index.html&bix=bix -> /en/index.html&bix=bix +*/ +QString Url::convertToLocal(LinkStatus const* ls) +{ + KURL url = ls->absoluteUrl(); + KURL base_url = ls->rootUrl(); + + if(base_url == url) + return "./" + url.fileName(); + else + return KURL::relativeURL(base_url, url); +} + +/** + If url2 has the same domain has url1 returns true. + If restrict, sourceforge.net != quanta.sourceforge.net. + Else is equal. +*/ +bool Url::localDomain(KURL const& url1, KURL const& url2, bool restrict) +{ + if(url1.protocol() != url2.protocol()) + { + //kdDebug(23100) << "NOT localDomain" << endl; + return false; + } + else if(!url1.hasHost()) + { + //kdDebug(23100) << "localDomain" << endl; + return true; + } + else + { + //return ::equalHost(url1.host(), url2.host(), restrict); + if(Url::equalHost(url1.host(), url2.host(), restrict)) + { + //kdDebug(23100) << "localDomain" << endl; + return true; + } + else + { + //kdDebug(23100) << "NOT localDomain" << endl; + return false; + } + + } +} + +/** + Returns true if url2 is a parent of url1. +*/ +bool Url::parentDir(KURL const& url1, KURL const& url2) +{ + if(url1.protocol() != url2.protocol()) + return false; + + else if(!url1.hasHost()) + return url2.isParentOf(url1); + + else + { + if(!equalHost(url1.host(), url2.host())) + return false; + + vector<QString> tokens_1 = tokenizeWordsSeparatedBy(url1.directory(true, false), QChar('/')); + vector<QString> tokens_2 = tokenizeWordsSeparatedBy(url2.directory(true, false), QChar('/')); + + if(tokens_1.size() == 0) + return false; + + //if(tokens_2.size() > tokens_1.size() or tokens_2.size() == 0) + //return true; + vector<QString>::size_type size = 0; + if(tokens_1.size() < tokens_2.size()) + size = tokens_1.size(); + else + size = tokens_2.size(); + + for(vector<QString>::size_type i = 0; i != size; ++i) + { + if(tokens_2[i] != tokens_1[i]) + return true; + } + } + + return false; +} + +bool Url::externalLink(KURL const& url1, KURL const& url2, bool restrict) +{ + if(url1.protocol() != url2.protocol()) + { + kdDebug(23100) << "externalLink" << endl; + return true; + } + else if(!url1.hasHost() && !url2.hasHost()) + { + kdDebug(23100) << "NOT externalLink" << endl; + return false; + } + else + return !Url::equalHost(url1.host(), url2.host(), restrict); +} diff --git a/klinkstatus/src/parser/url.h b/klinkstatus/src/parser/url.h new file mode 100644 index 00000000..6f22743d --- /dev/null +++ b/klinkstatus/src/parser/url.h @@ -0,0 +1,57 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef URL_H +#define URL_H + +#include "../engine/linkstatus.h" +#include "node.h" + +#include <kurl.h> +#include <qstring.h> + +#include <vector> + +using namespace std; + + +class LinkStatus; + +namespace Url +{ +Node::LinkType resolveLinkType(QString const& url); +KURL normalizeUrl(QString const& string_url, LinkStatus const& link_parent, QString const& document_root); +KURL normalizeUrl(QString const& string_url); +bool validUrl(KURL const& url); +bool existUrl(KURL const& url, vector<LinkStatus*> const& v); +bool equalHost(QString const& host1, QString const& host2, bool restrict = false); +bool hasProtocol(QString const& url); +QString convertToLocal(LinkStatus const* ls); +bool localDomain(KURL const& url1, KURL const& url2, bool restrict = true); +bool parentDir(KURL const& url1, KURL const& url2); +bool externalLink(KURL const& url1, KURL const& url2, bool restrict = true); +} + +inline bool validUrl(KURL const& url) +{ + return (url.isValid() /*&& url.hasHost()*/); +} + +#endif diff --git a/klinkstatus/src/tests/data/entities/bull&bladder.jpg b/klinkstatus/src/tests/data/entities/bull&bladder.jpg Binary files differnew file mode 100644 index 00000000..f83f6c72 --- /dev/null +++ b/klinkstatus/src/tests/data/entities/bull&bladder.jpg diff --git a/klinkstatus/src/tests/data/entities/bull_bladder.jpg b/klinkstatus/src/tests/data/entities/bull_bladder.jpg Binary files differnew file mode 100644 index 00000000..f83f6c72 --- /dev/null +++ b/klinkstatus/src/tests/data/entities/bull_bladder.jpg diff --git a/klinkstatus/src/tests/data/entities/link_with_html_entities.html b/klinkstatus/src/tests/data/entities/link_with_html_entities.html new file mode 100644 index 00000000..e4d919c2 --- /dev/null +++ b/klinkstatus/src/tests/data/entities/link_with_html_entities.html @@ -0,0 +1,7 @@ +<!-- + - Both links should not be broken. + - The visible text should be bull&bladder.jpg. +--> +<img src="bull_bladder.jpg"> +<br/><br/> +<img src="bull&bladder.jpg"/> diff --git a/klinkstatus/src/ui/Makefile.am b/klinkstatus/src/ui/Makefile.am new file mode 100644 index 00000000..7f687d39 --- /dev/null +++ b/klinkstatus/src/ui/Makefile.am @@ -0,0 +1,11 @@ +INCLUDES = -I$(top_srcdir)/src -I$(top_builddir)/klinkstatus/src/cfg \ + -I$(top_builddir)/klinkstatus/src $(all_includes) +METASOURCES = AUTO +noinst_HEADERS = sessionwidget.h tabwidgetsession.h klshistorycombo.h \ + resultview.h resultssearchbar.h +libui_la_LDFLAGS = $(all_libraries) +libui_la_LIBADD = $(top_builddir)/klinkstatus/src/cfg/libcfg.la +noinst_LTLIBRARIES = libui.la +libui_la_SOURCES = sessionwidgetbase.ui sessionwidget.cpp tabwidgetsession.cpp \ + klshistorycombo.cpp resultview.cpp treeview.cpp resultssearchbar.cpp documentrootdialog.cpp +SUBDIRS = settings diff --git a/klinkstatus/src/ui/celltooltip.cpp b/klinkstatus/src/ui/celltooltip.cpp new file mode 100644 index 00000000..b449cbce --- /dev/null +++ b/klinkstatus/src/ui/celltooltip.cpp @@ -0,0 +1,54 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "celltooltip.h" +#include "tablelinkstatus.h" + +#include <qscrollview.h> + +#include <iostream> +using namespace std; + + +CellToolTip::CellToolTip ( TableLinkstatus * table, QToolTipGroup * group) + : QToolTip(table->viewport(), group), table_(table) +{} + +void CellToolTip::maybeTip ( const QPoint & p ) +{ + QPoint cp = table_->viewportToContents(p); + + int row = table_->rowAt(cp.y()); + int col = table_->columnAt(cp.x()); + + if( row != -1 && col != -1) + { + if(col == 0 || !table_->textFitsInCell(row, col)) + { + TableItem* item = table_->myItem(row, col); + QString tip_string = item->toolTip(); + + QRect cr = table_->cellGeometry( row, col ); + cr.moveTopLeft( table_->contentsToViewport( cr.topLeft() ) ); + + tip(cr, tip_string); + } + } +} diff --git a/klinkstatus/src/ui/celltooltip.h b/klinkstatus/src/ui/celltooltip.h new file mode 100644 index 00000000..33136ceb --- /dev/null +++ b/klinkstatus/src/ui/celltooltip.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef CELLTOOLTIP_H +#define CELLTOOLTIP_H + +#include <qtooltip.h> + + +class TableLinkstatus; + +class CellToolTip: public QToolTip +{ +public: + + CellToolTip ( TableLinkstatus * table, QToolTipGroup * group = 0 ); + +protected: + + virtual void maybeTip ( const QPoint & p ); + +private: + + TableLinkstatus * table_; +}; + + +#endif diff --git a/klinkstatus/src/ui/documentrootdialog.cpp b/klinkstatus/src/ui/documentrootdialog.cpp new file mode 100644 index 00000000..c2ed3e7d --- /dev/null +++ b/klinkstatus/src/ui/documentrootdialog.cpp @@ -0,0 +1,89 @@ +/*************************************************************************** + * Copyright (C) 2006 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#include "documentrootdialog.h" + +#include <kurlrequester.h> +#include <klocale.h> +#include <kurl.h> + +#include <qstring.h> +#include <qlayout.h> +#include <qlabel.h> + + +DocumentRootDialog::DocumentRootDialog(QWidget *parent, QString const& url) + : KDialogBase(parent, "DocumentRootDialog", true, "Choose a Document Root", + KDialogBase::Ok, KDialogBase::Ok, true), + m_url(url) +{ + QWidget* page = new QWidget(this); + setMainWidget(page); + QVBoxLayout* topLayout = new QVBoxLayout(page, 0, spacingHint()); + + QLabel* label = new QLabel(i18n("As you are using a protocol different than HTTP, \nthere is no way to guess where the document root is, \nin order to resolve relative URLs like the ones started with \"/\".\n\nPlease specify one:"), page); + topLayout->addWidget(label); + + m_urlRequester = new KURLRequester(page); + m_urlRequester->setURL(url); + m_urlRequester->setMinimumWidth(fontMetrics().maxWidth()*20); + m_urlRequester->setFocus(); + topLayout->addWidget(m_urlRequester); + + topLayout->addStretch(10); + + // setInitialSize(configDialogSize("klinkstatus")); + + m_urlRequester->setMode(KFile::Directory); +// enableButtonOK(false); + + connect(m_urlRequester, SIGNAL(textChanged (const QString &)), + this, SLOT(slotTextChanged (const QString &))); + connect(m_urlRequester, SIGNAL(returnPressed (const QString &)), + this, SLOT(slotReturnPressed (const QString &))); + connect(m_urlRequester, SIGNAL(urlSelected (const QString &)), + this, SLOT(slotTextChanged (const QString &))); +} + +DocumentRootDialog::~DocumentRootDialog() +{ + saveDialogSize("klinkstatus", true); +} + +void DocumentRootDialog::slotReturnPressed( const QString & ) +{ + slotOk(); +} + +void DocumentRootDialog::slotTextChanged( const QString & s) +{ + KURL url(s); + enableButtonOK(!s.isEmpty() && url.isValid()); +} + +void DocumentRootDialog::slotOk( ) +{ + m_url = m_urlRequester->url(); + + KDialogBase::slotOk(); +} + + + +#include "documentrootdialog.moc" diff --git a/klinkstatus/src/ui/documentrootdialog.h b/klinkstatus/src/ui/documentrootdialog.h new file mode 100644 index 00000000..52696727 --- /dev/null +++ b/klinkstatus/src/ui/documentrootdialog.h @@ -0,0 +1,57 @@ +/*************************************************************************** + * Copyright (C) 2006 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#ifndef DOCUMENTROOTDIALOG_H +#define DOCUMENTROOTDIALOG_H + +#include <kdialogbase.h> + +class KURLRequester; + +/** + @author Paulo Moura Guedes <moura@kdewebdev.org> +*/ +class DocumentRootDialog : public KDialogBase +{ +Q_OBJECT +public: + DocumentRootDialog(QWidget *parent, QString const& url); + ~DocumentRootDialog(); + + void setUrl(const QString& theValue) { m_url = theValue; } + QString url() const { return m_url; } + + +protected: + virtual void closeEvent (QCloseEvent*) {} + +protected slots: + virtual void reject() {} + virtual void slotOk(); + +private slots: + void slotTextChanged(const QString &); + void slotReturnPressed(const QString &); + +private: + KURLRequester* m_urlRequester; + QString m_url; +}; + +#endif diff --git a/klinkstatus/src/ui/klshistorycombo.cpp b/klinkstatus/src/ui/klshistorycombo.cpp new file mode 100644 index 00000000..36deb385 --- /dev/null +++ b/klinkstatus/src/ui/klshistorycombo.cpp @@ -0,0 +1,198 @@ +// +// C++ Implementation: klshistorycombo +// +// Description: +// +// +// Author: Paulo Moura Guedes <moura@kdewebdev.org>, (C) 2004 +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "klshistorycombo.h" +#include "klsconfig.h" + +#include <kapplication.h> +#include <kconfig.h> +#include <kcompletionbox.h> +#include <kdebug.h> +#include <kstdaccel.h> +#include <kurldrag.h> +#include <kglobalsettings.h> + + +bool KLSHistoryCombo::items_saved_ = false; + + +KLSHistoryCombo::KLSHistoryCombo(QWidget *parent, const char *name) + : KHistoryCombo(parent, name) +{ + setMaxCount(KLSConfig::maxCountComboUrl()); + + setDuplicatesEnabled(false); + setAutoCompletion(false); + + connect(this, SIGNAL(activated(const QString& )), + this, SLOT(addToHistory(const QString& ))); +} + +KLSHistoryCombo::~KLSHistoryCombo() +{} + +void KLSHistoryCombo::init() +{ + loadItems(); +} + +void KLSHistoryCombo::saveItems() +{ + if(items_saved_) + return; + + QStringList items = historyItems(); + + KLSConfig::setComboUrlHistory(items); + KLSConfig::writeConfig(); + + items_saved_ = true; +} + +void KLSHistoryCombo::loadItems() +{ + clear(); + + QStringList items = KLSConfig::comboUrlHistory(); + + bool block = signalsBlocked(); + blockSignals( true ); + + setHistoryItems(items); + blockSignals(block); + + completionObject()->setItems(items); + + setCompletionMode(KGlobalSettings::completionMode()); +} + +bool KLSHistoryCombo::eventFilter( QObject *o, QEvent *ev ) +{ + // Handle Ctrl+Del/Backspace etc better than the Qt widget, which always + // jumps to the next whitespace. + QLineEdit *edit = lineEdit(); + if ( o == edit ) + { + int type = ev->type(); + if ( type == QEvent::KeyPress ) + { + QKeyEvent *e = static_cast<QKeyEvent *>( ev ); + + if ( e->key() == Key_Return || e->key() == Key_Enter ) + { + //m_modifier = e->state(); + return false; + } + + int delete_word_back = KStdAccel::deleteWordBack().keyCodeQt(); + int delete_word_forward = KStdAccel::deleteWordForward().keyCodeQt(); + + if ( KKey( e ) == KKey(delete_word_back) || + KKey( e ) == KKey(delete_word_forward) || + ((e->state() & ControlButton) && + (e->key() == Key_Left || e->key() == Key_Right) ) ) + { + selectWord(e); + e->accept(); + return true; + } + } + + else if ( type == QEvent::MouseButtonDblClick ) + { + edit->selectAll(); + return true; + } + } + return KComboBox::eventFilter( o, ev ); +} + +/* + Handle Ctrl+Cursor etc better than the Qt widget, which always + jumps to the next whitespace. This code additionally jumps to + the next [/#?:], which makes more sense for URLs. The list of + chars that will stop the cursor are '/', '.', '?', '#', ':'. +*/ +void KLSHistoryCombo::selectWord(QKeyEvent *e) +{ + QLineEdit* edit = lineEdit(); + QString text = edit->text(); + int pos = edit->cursorPosition(); + int pos_old = pos; + int count = 0; + + // TODO: make these a parameter when in kdelibs/kdeui... + QValueList<QChar> chars; + chars << QChar('/') << QChar('.') << QChar('?') << QChar('#') << QChar(':'); + bool allow_space_break = true; + + if( e->key() == Key_Left || e->key() == Key_Backspace ) + { + do + { + pos--; + count++; + if( allow_space_break && text[pos].isSpace() && count > 1 ) + break; + } + while( pos >= 0 && (chars.findIndex(text[pos]) == -1 || count <= 1) ); + + if( e->state() & ShiftButton ) + { + edit->cursorForward(true, 1-count); + } + else if( e->key() == Key_Backspace ) + { + edit->cursorForward(false, 1-count); + QString text = edit->text(); + int pos_to_right = edit->text().length() - pos_old; + QString cut = text.left(edit->cursorPosition()) + text.right(pos_to_right); + edit->setText(cut); + edit->setCursorPosition(pos_old-count+1); + } + else + { + edit->cursorForward(false, 1-count); + } + } + else if( e->key() == Key_Right || e->key() == Key_Delete ) + { + do + { + pos++; + count++; + if( allow_space_break && text[pos].isSpace() ) + break; + } + while( pos < (int) text.length() && chars.findIndex(text[pos]) == -1 ); + + if( e->state() & ShiftButton ) + { + edit->cursorForward(true, count+1); + } + else if( e->key() == Key_Delete ) + { + edit->cursorForward(false, -count-1); + QString text = edit->text(); + int pos_to_right = text.length() - pos - 1; + QString cut = text.left(pos_old) + + (pos_to_right > 0 ? text.right(pos_to_right) : QString() ); + edit->setText(cut); + edit->setCursorPosition(pos_old); + } + else + { + edit->cursorForward(false, count+1); + } + } +} + +#include "klshistorycombo.moc" diff --git a/klinkstatus/src/ui/klshistorycombo.h b/klinkstatus/src/ui/klshistorycombo.h new file mode 100644 index 00000000..ab990a1e --- /dev/null +++ b/klinkstatus/src/ui/klshistorycombo.h @@ -0,0 +1,42 @@ +// +// C++ Interface: klshistorycombo +// +// Description: +// +// +// Author: Paulo Moura Guedes <moura@kdewebdev.org>, (C) 2004 +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef KLSHISTORYCOMBO_H +#define KLSHISTORYCOMBO_H + +#include <kcombobox.h> +class KConfig; + +/** +@author Paulo Moura Guedes +Based on KonqCombo +*/ +class KLSHistoryCombo : public KHistoryCombo +{ + Q_OBJECT + +public: + KLSHistoryCombo(QWidget* parent, const char* name); + ~KLSHistoryCombo(); + + void init(); + void loadItems(); + void saveItems(); + +protected: + virtual bool eventFilter(QObject* o, QEvent* ev); + void selectWord(QKeyEvent* e); + +private: + static bool items_saved_; +}; + +#endif diff --git a/klinkstatus/src/ui/resultssearchbar.cpp b/klinkstatus/src/ui/resultssearchbar.cpp new file mode 100644 index 00000000..7f772b54 --- /dev/null +++ b/klinkstatus/src/ui/resultssearchbar.cpp @@ -0,0 +1,236 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "resultssearchbar.h" + +#include <kcombobox.h> +#include <kiconloader.h> +#include <klineedit.h> +#include <klocale.h> +#include <kstandarddirs.h> +#include <kdebug.h> + +#include <qapplication.h> +#include <qhbox.h> +#include <qlabel.h> +#include <qpixmap.h> +#include <qstring.h> +#include <qtimer.h> +#include <qtoolbutton.h> +#include <qtooltip.h> +#include <qlayout.h> + + +class ResultsSearchBar::ResultsSearchBarPrivate +{ +public: + ResultsSearchBarPrivate() + : layout(0), searchLine(0), searchCombo(0), delay(400), m_lastComboIndex(0) + {} + + QString searchText; + QTimer timer; + QHBoxLayout* layout; + KLineEdit* searchLine; + KComboBox* searchCombo; + int delay; + int m_lastComboIndex; +}; + +ResultsSearchBar::ResultsSearchBar(QWidget* parent, const char* name) + : QWidget(parent, name), d(new ResultsSearchBar::ResultsSearchBarPrivate) +{ + setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); + + d->layout = new QHBoxLayout(this); + d->layout->setMargin(2); + d->layout->setSpacing(5); + + QToolButton* clearButton = new QToolButton(this); + clearButton->setIconSet(SmallIconSet(QApplication::reverseLayout() ? "clear_left" : "locationbar_erase")); + clearButton->setAutoRaise(true); + d->layout->addWidget(clearButton); + + QLabel* searchLabel = new QLabel(this); + searchLabel->setText(i18n("S&earch:")); + d->layout->addWidget(searchLabel); + + d->searchLine = new KLineEdit(this, "searchline"); + connect(d->searchLine, SIGNAL(textChanged(const QString &)), + this, SLOT(slotSearchStringChanged(const QString &))); + + searchLabel->setBuddy(d->searchLine); + d->layout->addWidget(d->searchLine); + + QLabel* statusLabel = new QLabel(this); + statusLabel->setText( i18n("Status:") ); + d->layout->addWidget(statusLabel); + + d->searchCombo = new KComboBox(this, "searchcombo"); + QPixmap iconAll = KGlobal::iconLoader()->loadIcon("exec", KIcon::Small); + QPixmap iconGood = KGlobal::iconLoader()->loadIcon("ok", KIcon::Small); + QPixmap iconBroken = KGlobal::iconLoader()->loadIcon("no", KIcon::Small); + QPixmap iconMalformed = KGlobal::iconLoader()->loadIcon("bug", KIcon::Small); + QPixmap iconUndetermined = KGlobal::iconLoader()->loadIcon("help", KIcon::Small); + + d->searchCombo->insertItem(iconAll, i18n("All Links")); + d->searchCombo->insertItem(iconGood, i18n("Good Links")); + d->searchCombo->insertItem(iconBroken, i18n("Broken Links")); + d->searchCombo->insertItem(iconMalformed, i18n("Malformed Links")); + d->searchCombo->insertItem(iconUndetermined, i18n("Undetermined Links")); + d->layout->addWidget(d->searchCombo); + + QToolTip::add(clearButton, i18n("Clear filter")); + QToolTip::add( d->searchLine, i18n("Enter the terms to filter the result link list")); + QToolTip::add( d->searchCombo, i18n("Choose what kind of link status to show in result list")); + + connect(clearButton, SIGNAL( clicked() ), + this, SLOT(slotClearSearch()) ); + + connect(d->searchCombo, SIGNAL(activated(int)), + this, SLOT(slotSearchComboChanged(int))); + + connect(&(d->timer), SIGNAL(timeout()), this, SLOT(slotActivateSearch())); +} + +ResultsSearchBar::~ResultsSearchBar() +{ + delete d; + d = 0; +} + +QString const& ResultsSearchBar::text() const +{ + return d->searchText; +} + +int ResultsSearchBar::status() const +{ + return d->searchCombo->currentItem(); +} + +void ResultsSearchBar::setDelay(int ms) +{ + d->delay = ms; +} + +int ResultsSearchBar::delay() const +{ + return d->delay; +} + +void ResultsSearchBar::slotClearSearch() +{ + if(status() != 0 || !d->searchLine->text().isEmpty()) + { + d->searchLine->clear(); + d->searchCombo->setCurrentItem(0); + d->timer.stop(); + slotActivateSearch(); + } +} + +void ResultsSearchBar::slotSetStatus(int status) +{ + d->searchCombo->setCurrentItem(status); +} + +void ResultsSearchBar::slotSetText(const QString& text) +{ + d->searchLine->setText(text); +} + +void ResultsSearchBar::slotSearchComboChanged(int index) +{ + if(d->timer.isActive()) + d->timer.stop(); + + if(d->m_lastComboIndex == index) + return; + + d->m_lastComboIndex = index; + + d->timer.start(200, true); +} + +void ResultsSearchBar::slotSearchStringChanged(const QString& search) +{ + if(d->timer.isActive()) + d->timer.stop(); + + if(d->searchText == search) + return; + + d->searchText = search; + + d->timer.start(200, true); +} + +void ResultsSearchBar::slotActivateSearch() +{ + kdDebug(23100) << "ResultsSearchBar::slotActivateSearch" << endl; + + ResultView::Status status = selectedStatus(); + + emit signalSearch(LinkMatcher(d->searchLine->text(), status)); +} + +LinkMatcher ResultsSearchBar::currentLinkMatcher() const +{ + return LinkMatcher(d->searchLine->text(), selectedStatus()); +} + +ResultView::Status ResultsSearchBar::selectedStatus() const +{ + ResultView::Status status = ResultView::none; + + if(d->searchCombo->currentItem()) + { + switch(d->searchCombo->currentItem()) + { + case 1: // Good + { + status = ResultView::good; + break; + } + case 2: // Broken + { + status = ResultView::bad; + break; + } + case 3: // Malformed + { + status = ResultView::malformed; + break; + } + case 4: // Undetermined + { + status = ResultView::undetermined; + break; + } + default: + break; + } + } + return status; +} + + +#include "resultssearchbar.moc" diff --git a/klinkstatus/src/ui/resultssearchbar.h b/klinkstatus/src/ui/resultssearchbar.h new file mode 100644 index 00000000..67d30a99 --- /dev/null +++ b/klinkstatus/src/ui/resultssearchbar.h @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef RESULTSSEARCHBAR_H +#define RESULTSSEARCHBAR_H + +#include <qstring.h> + +#include "resultview.h" +#include "../engine/linkfilter.h" + +/** + @author Paulo Moura Guedes <moura@kdewebdev.org> + Based on Akregator code. Kudos ;) +*/ +class ResultsSearchBar : public QWidget +{ + Q_OBJECT +public: + ResultsSearchBar(QWidget *parent = 0, const char *name = 0); + ~ResultsSearchBar(); + + QString const& text() const; + int status() const; + + void setDelay(int ms); + int delay() const; + + LinkMatcher currentLinkMatcher() const; + +signals: + /** emitted when the text and status filters were updated. Params are textfilter, statusfilter */ + void signalSearch(LinkMatcher); + +public slots: + void slotClearSearch(); + void slotSetStatus(int status); + void slotSetText(const QString& text); + +private slots: + + void slotSearchStringChanged(const QString& search); + void slotSearchComboChanged(int index); + void slotActivateSearch(); + +private: + + ResultView::Status selectedStatus() const; + +private: + + class ResultsSearchBarPrivate; + ResultsSearchBarPrivate* d; +}; + +#endif diff --git a/klinkstatus/src/ui/resultview.cpp b/klinkstatus/src/ui/resultview.cpp new file mode 100644 index 00000000..4078bfca --- /dev/null +++ b/klinkstatus/src/ui/resultview.cpp @@ -0,0 +1,184 @@ +// +// C++ Implementation: resultlinkview +// +// Description: +// +// +// Author: Paulo Moura Guedes <moura@kdewebdev.org>, (C) 2004 +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "resultview.h" +#include "../engine/linkstatus.h" + +#include <qpainter.h> +#include <qcolor.h> + +#include <klocale.h> +#include <kurl.h> +#include <kiconloader.h> + +const QString ResultView::URL_LABEL = "URL"; +const QString ResultView::STATUS_LABEL = "Status"; +const QString ResultView::MARKUP_LABEL = "Markup"; +const QString ResultView::LINK_LABEL_LABEL = "Label"; + + +// ******************************* ResultView ******************************** + +ResultView::ResultView() + : col_status_(-1), + col_label_(-1), + col_url_(-1), + col_markup_(-1), + sub_menu_(0), + cell_tip_(0) +{} + + +ResultView::~ResultView() +{} + +void ResultView::setColumns(QStringList const& columns) +{ + Q_ASSERT(columns.size() != 0); + + columns_.clear(); + for(uint i = 0; i != columns.size(); ++i) + { + if(columns[i] == ResultView::URL_LABEL) + { + col_url_ = i + 1; + } + else if(columns[i] == ResultView::STATUS_LABEL) + { + col_status_ = i + 1; + } + else if(columns[i] == ResultView::MARKUP_LABEL) + { + col_markup_ = i + 1; + } + else if(columns[i] == ResultView::LINK_LABEL_LABEL) + { + col_label_ = i + 1; + } + + columns_.push_back(columns[i]); + } + number_of_columns_ = columns.size(); +} + +bool ResultView::displayableWithStatus(LinkStatus const* ls, Status const& status) +{ + if(status == ResultView::good) + { + return + ls->status() == LinkStatus::SUCCESSFULL || + ls->status() == LinkStatus::HTTP_REDIRECTION; + } + else if(status == ResultView::bad) + { + return + ls->status() == LinkStatus::BROKEN || + ls->status() == LinkStatus::HTTP_CLIENT_ERROR || + ls->status() == LinkStatus::HTTP_SERVER_ERROR; + } + else if(status == ResultView::malformed) + { + return ls->status() == LinkStatus::MALFORMED; + } + else if(status == ResultView::undetermined) + { + return + ls->status() == LinkStatus::UNDETERMINED || + ls->status() == LinkStatus::TIMEOUT || + ls->status() == LinkStatus::NOT_SUPPORTED; + } + else + return true; +} + + +// ******************************* ResultViewItem ***************************** + +ResultViewItem::ResultViewItem(LinkStatus const* linkstatus, int column_index) + : ls_((LinkStatus*)linkstatus), column_index_(column_index) +{ + Q_ASSERT(ls_); + Q_ASSERT(column_index_ > 0); +} + +ResultViewItem::~ResultViewItem() +{} + +void ResultViewItem::setColumnIndex(int i) +{ + Q_ASSERT(i > 0); + column_index_ = i; +} + +int ResultViewItem::columnIndex() const +{ + return column_index_; +} + +LinkStatus const* ResultViewItem::linkStatus() const +{ + Q_ASSERT(ls_); + return ls_; +} + +QColor const& ResultViewItem::textStatusColor() const +{ + if(linkStatus()->errorOccurred()) + { + //kdDebug(23100) << "ERROR: " << linkStatus()->error() << ": " << linkStatus()->absoluteUrl().prettyURL() << endl; + if(linkStatus()->error() == i18n( "Javascript not supported" )) + return Qt::lightGray; + else + return Qt::red; + } + + else if(linkStatus()->absoluteUrl().hasRef()) + return Qt::blue; + + else if(!linkStatus()->absoluteUrl().protocol().startsWith("http")) + return Qt::darkGreen; + + else + { + QString status_code(QString::number(linkStatus()->httpHeader().statusCode())); + + if(status_code[0] == '0') + { + kdWarning(23100) << "status code == 0: " << endl; + kdWarning(23100) << linkStatus()->toString() << endl; + kdWarning(23100) << linkStatus()->httpHeader().toString() << endl; + } + //Q_ASSERT(status_code[0] != '0'); + + if(status_code[0] == '5') + return Qt::darkMagenta; + + else if(status_code[0] == '4') + return Qt::red; + + else if(status_code[0] == '3') + return Qt::blue; + + else if(status_code[0] == '2') + return Qt::darkGreen; + + else + return Qt::red; + } +} + + + + + + + + diff --git a/klinkstatus/src/ui/resultview.h b/klinkstatus/src/ui/resultview.h new file mode 100644 index 00000000..e6d3e789 --- /dev/null +++ b/klinkstatus/src/ui/resultview.h @@ -0,0 +1,134 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef RESULTVIEW_H +#define RESULTVIEW_H + +#include <qvaluevector.h> +#include <qpopupmenu.h> +#include <qstringlist.h> + +class KURL; + +class LinkStatus; +class CellToolTip; + + +/** +@author Paulo Moura Guedes +*/ +class ResultView +{ +public: + + static const QString URL_LABEL; + static const QString STATUS_LABEL; + static const QString MARKUP_LABEL; + static const QString LINK_LABEL_LABEL; + + enum Status { + none = 0, + good, + bad, + malformed, + undetermined // timeouts and refs + }; + + ResultView(); + virtual ~ResultView(); + + //virtual void insertResult(LinkStatus const* linkstatus) = 0; + virtual void clear() = 0; + virtual void show(Status const& status) = 0; + virtual void showAll() = 0; + //virtual void ensureCellVisible(int row, int col) = 0; + + virtual void setColumns(QStringList const& columns); + static bool displayableWithStatus(LinkStatus const* ls, Status const& status); + + int numberOfColumns() const { return number_of_columns_; } + + int urlColumnIndex() const {return col_url_; } + int statusColumnIndex() const {return col_status_; } + int markupColumnIndex() const {return col_markup_; } + int labelColumnIndex() const {return col_label_; } + +protected: + //virtual bool textFitsInCell(int row, int col) const = 0; + virtual bool isEmpty() const = 0; + virtual void loadContextTableMenu(QValueVector<KURL> const& referrers, bool is_root = false) = 0; + +protected slots: + + //virtual void slotPopupContextMenu(int row, int col, const QPoint& pos) = 0; + virtual void slotCopyUrlToClipboard() const = 0; + virtual void slotCopyParentUrlToClipboard() const = 0; + virtual void slotCopyCellTextToClipboard() const = 0; + virtual void slotEditReferrersWithQuanta() = 0; + virtual void slotEditReferrerWithQuanta(int id) = 0; + virtual void slotEditReferrerWithQuanta(KURL const& url) = 0; + virtual void slotViewUrlInBrowser() = 0; + virtual void slotViewParentUrlInBrowser() = 0; + +protected: + QStringList columns_; + int col_status_; + int col_label_; + int col_url_; + int col_markup_; // optional + QPopupMenu context_table_menu_; + QPopupMenu* sub_menu_; + CellToolTip* cell_tip_; + +private: + int number_of_columns_; +}; + + +class ResultViewItem +{ +public: + ResultViewItem(LinkStatus const* linkstatus, + int column_index); + virtual ~ResultViewItem(); + + virtual void setColumnIndex(int i); + virtual int columnIndex() const; + + virtual QString toolTip() const = 0; + LinkStatus const* linkStatus() const; + +protected: + + QColor const& textStatusColor() const; + virtual void paint( QPainter *p, const QColorGroup &cg, + const QRect &cr, bool selected ) = 0; + virtual void setText() = 0; + virtual void setPixmap() = 0; + +protected: + + LinkStatus* ls_; + int column_index_; + int alignment_; +}; + + +#endif diff --git a/klinkstatus/src/ui/sessionwidget.cpp b/klinkstatus/src/ui/sessionwidget.cpp new file mode 100644 index 00000000..da128070 --- /dev/null +++ b/klinkstatus/src/ui/sessionwidget.cpp @@ -0,0 +1,723 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include <kapplication.h> +#include <kurl.h> +#include <kcombobox.h> +#include <ksqueezedtextlabel.h> +#include <kprogress.h> +#include <kmessagebox.h> +#include <kconfig.h> +#include <kiconloader.h> +#include <kglobal.h> +#include <kpushbutton.h> +#include <kfiledialog.h> +#include <kactionclasses.h> +#include <ktempfile.h> +#include <ksavefile.h> +#include <kstandarddirs.h> +#include <kio/netaccess.h> + +#include <qevent.h> +#include <qlineedit.h> +#include <qspinbox.h> +#include <qcheckbox.h> +#include <qpushbutton.h> +#include <qlayout.h> +#include <qlabel.h> +#include <qlistbox.h> +#include <qstringlist.h> +#include <qbuttongroup.h> +#include <qtoolbutton.h> +#include <qregexp.h> + +#include "sessionwidget.h" +#include "tablelinkstatus.h" +#include "treeview.h" +#include "documentrootdialog.h" +#include "klshistorycombo.h" +#include "klsconfig.h" +#include "resultview.h" +#include "../global.h" +#include "../engine/linkstatus.h" +#include "../engine/linkchecker.h" +#include "../engine/searchmanager.h" +#include "resultssearchbar.h" +#include "../actionmanager.h" +#include "../utils/utils.h" +#include "../utils/xsl.h" + + +SessionWidget::SessionWidget(int max_simultaneous_connections, int time_out, + QWidget* parent, const char* name, WFlags f) + : SessionWidgetBase(parent, name, f), search_manager_(0), + action_manager_(ActionManager::getInstance()), + ready_(true), to_start_(false), to_pause_(false), to_stop_(false), + in_progress_(false), paused_(false), stopped_(true), + bottom_status_timer_(this, "bottom_status_timer"), + max_simultaneous_connections_(max_simultaneous_connections), + time_out_(time_out), tree_display_(false), follow_last_link_checked_(KLSConfig::followLastLinkChecked()), + start_search_action_(0) +{ + newSearchManager(); + + init(); + slotLoadSettings(); + + connect(combobox_url, SIGNAL( textChanged ( const QString & ) ), + this, SLOT( slotEnableCheckButton( const QString & ) ) ); + + connect(tree_view, SIGNAL( clicked ( QListViewItem * ) ), + this, SLOT( showBottomStatusLabel( QListViewItem * ) ) ); + + connect(&bottom_status_timer_, SIGNAL(timeout()), this, SLOT(clearBottomStatusLabel()) ); +} + +SessionWidget::~SessionWidget() +{ + //combobox_url->saveItems(); This is done every time a URL is checked + + if(KLSConfig::rememberCheckSettings()) + saveCurrentCheckSettings(); +} + +void SessionWidget::init() +{ + combobox_url->init(); + + toolButton_clear_combo->setIconSet(SmallIconSet("locationbar_erase")); + + pushbutton_url->setIconSet(KGlobal::iconLoader()->loadIconSet("fileopen", KIcon::Small)); + QPixmap pixMap = KGlobal::iconLoader()->loadIcon("fileopen", KIcon::Small); + pushbutton_url->setFixedSize(pixMap.width() + 8, pixMap.height() + 8); + connect(pushbutton_url, SIGNAL(clicked()), this, SLOT(slotChooseUrlDialog())); + + resultsSearchBar->hide(); + + start_search_action_ = static_cast<KToggleAction*> (action_manager_->action("start_search")); + + connect(resultsSearchBar, SIGNAL(signalSearch(LinkMatcher)), + this, SLOT(slotApplyFilter(LinkMatcher))); +} + +void SessionWidget::slotLoadSettings(bool modify_current_widget_settings) +{ + if(modify_current_widget_settings) + { + checkbox_recursively->setChecked(KLSConfig::recursiveCheck()); + spinbox_depth->setValue(KLSConfig::depth()); + checkbox_subdirs_only->setChecked(!KLSConfig::checkParentFolders()); + checkbox_external_links->setChecked(KLSConfig::checkExternalLinks()); + tree_display_ = KLSConfig::displayTreeView(); + tree_view->setTreeDisplay(tree_display_); + } + + search_manager_->setTimeOut(KLSConfig::timeOut()); + + //kdDebug(23100) << "tree_display_: " << tree_display_ << endl; +} + +void SessionWidget::saveCurrentCheckSettings() +{ + KLSConfig::setRecursiveCheck(checkbox_recursively->isChecked()); + KLSConfig::setDepth(spinbox_depth->value()); + KLSConfig::setCheckParentFolders(!checkbox_subdirs_only->isChecked()); + KLSConfig::setCheckExternalLinks(checkbox_external_links->isChecked()); + + KLSConfig::writeConfig(); +} + +void SessionWidget::newSearchManager() +{ + if(search_manager_) + delete search_manager_; + + search_manager_ = new SearchManager(KLSConfig::maxConnectionsNumber(), + KLSConfig::timeOut(), + this, "search_manager"); + Q_ASSERT(search_manager_); + + connect(search_manager_, SIGNAL(signalRootChecked(const LinkStatus *, LinkChecker *)), + this, SLOT(slotRootChecked(const LinkStatus *, LinkChecker *))); + connect(search_manager_, SIGNAL(signalLinkChecked(const LinkStatus *, LinkChecker *)), + this, SLOT(slotLinkChecked(const LinkStatus *, LinkChecker *))); + connect(search_manager_, SIGNAL(signalSearchFinished()), + this, SLOT(slotSearchFinished())); + connect(search_manager_, SIGNAL(signalSearchPaused()), + this, SLOT(slotSearchPaused())); + connect(search_manager_, SIGNAL(signalAddingLevelTotalSteps(uint)), + this, SLOT(slotAddingLevelTotalSteps(uint))); + connect(search_manager_, SIGNAL(signalAddingLevelProgress()), + this, SLOT(slotAddingLevelProgress())); + connect(search_manager_, SIGNAL(signalLinksToCheckTotalSteps(uint)), + this, SLOT(slotLinksToCheckTotalSteps(uint))); +} + +void SessionWidget::setColumns(QStringList const& colunas) +{ + tree_view->setColumns(colunas); +} + +void SessionWidget::setUrl(KURL const& url) +{ + combobox_url->setCurrentText(url.prettyURL()); + combobox_url->setFocus(); +} + +bool SessionWidget::isEmpty() const +{ + Q_ASSERT(tree_view); + return tree_view->isEmpty(); +} + +SearchManager const* SessionWidget::getSearchManager() const +{ + return search_manager_; +} + +void SessionWidget::slotEnableCheckButton(const QString & s) +{ + if(!(stopped_ && !pendingActions())) + return; + + if(!s.isEmpty() && !search_manager_->searching()) + { + start_search_action_->setEnabled(true); + } + else + { + start_search_action_->setEnabled(false); + } +} + +void SessionWidget::slotCheck() +{ + Q_ASSERT(to_start_); + Q_ASSERT(!in_progress_); + Q_ASSERT(!paused_); + Q_ASSERT(stopped_); + + ready_ = false; + if(!validFields()) + { + ready_ = true; + KApplication::beep(); + return; + } + + emit signalSearchStarted(); + + in_progress_ = true; + paused_ = false; + stopped_ = false; + + slotLoadSettings(false); // it seems that KConfigDialogManager is not trigering this slot + + newSearchManager(); + + insertUrlAtCombobox(combobox_url->currentText()); + combobox_url->saveItems(); + progressbar_checker->reset(); + progressbar_checker->setPercentageVisible(true); + progressbar_checker->setTotalSteps(1); // check root page + progressbar_checker->setProgress(0); + textlabel_progressbar->setText(i18n( "Checking..." )); + + textlabel_elapsed_time->setEnabled(true); + //textlabel_elapsed_time_value->setText(""); + textlabel_elapsed_time_value->setEnabled(true); + + //table_linkstatus->clear(); + tree_view->clear(); + + KURL url = Url::normalizeUrl(combobox_url->currentText()); + + if(!url.protocol().startsWith("http")) + { + QString documentRootHint = url.directory().isEmpty() ? "/" : url.directory(); + DocumentRootDialog dialog(this, documentRootHint); + dialog.exec(); + search_manager_->setDocumentRoot(KURL::fromPathOrURL(dialog.url())); + } + + if(KLSConfig::useQuantaUrlPreviewPrefix() && Global::isKLinkStatusEmbeddedInQuanta()) + { + KURL url_aux = Global::urlWithQuantaPreviewPrefix(url); + if(url_aux.isValid() && !url_aux.isEmpty()) + url = url_aux; + } + + if(!checkbox_recursively->isChecked()) + { + search_manager_->setSearchMode(SearchManager::depth); + search_manager_->setDepth(0); + } + + else if(checkbox_recursively->isChecked()) + { + if(spinbox_depth->value() == 0) + { + search_manager_->setSearchMode(SearchManager::domain); + } + else + { + search_manager_->setSearchMode(SearchManager::depth_and_domain); + search_manager_->setDepth(spinbox_depth->value()); + } + + if(checkbox_subdirs_only->isChecked()) + { + search_manager_->setCheckParentDirs(false); + + if(url.hasHost()) + search_manager_->setDomain(url.host() + url.directory(true, false)); + } + else + { + search_manager_->setCheckParentDirs(true); + + if(url.hasHost()) + search_manager_->setDomain(url.host()); + } + if(checkbox_external_links->isChecked()) + { + search_manager_->setCheckExternalLinks(true); + search_manager_->setExternalDomainDepth(1); + } + else + { + search_manager_->setCheckExternalLinks(false); + search_manager_->setExternalDomainDepth(0); + } + } + if(!lineedit_reg_exp->text().isEmpty()) + { + search_manager_->setCheckRegularExpressions(true); + search_manager_->setRegularExpression(lineedit_reg_exp->text(), false); + } + + kdDebug(23100) << "URI: " << url.prettyURL() << endl; + combobox_url->setCurrentText(url.prettyURL()); + search_manager_->startSearch(url); + slotSetTimeElapsed(); +} + +void SessionWidget::keyPressEvent ( QKeyEvent* e ) +{ + if( e->key() == Qt::Key_Return && + ( combobox_url->hasFocus() || + //lineedit_domain->hasFocus() || + //checkbox_depth->hasFocus() || + spinbox_depth->hasFocus() || + //checkbox_domain->hasFocus() || + //spinbox_external_domain->hasFocus() + checkbox_recursively->hasFocus() || + checkbox_external_links->hasFocus() || + checkbox_subdirs_only->hasFocus() ) ) + { + if(validFields()) + { + slotStartSearch(); + } + } + + else if(e->key() == Qt::Key_F6) + { + combobox_url->lineEdit()->selectAll(); + } +} + +bool SessionWidget::validFields() +{ + if(combobox_url->currentText().isEmpty()) + { + KMessageBox::sorry(this, i18n("Cowardly refusing to check an empty URL.")); + return false; + } + + else + return true; +} + +void SessionWidget::slotRootChecked(LinkStatus const* linkstatus, LinkChecker * anal) +{ + slotSetTimeElapsed(); + emit signalUpdateTabLabel(search_manager_->linkStatusRoot(), this); + + Q_ASSERT(textlabel_progressbar->text() == i18n("Checking...") || + textlabel_progressbar->text() == i18n("Stopped")); + + progressbar_checker->setProgress(1); + + //table_linkstatus->insertResult(linkstatus); + TreeViewItem* tree_view_item = new TreeViewItem(tree_view, tree_view->lastItem(), linkstatus); + LinkStatus* ls = const_cast<LinkStatus*> (linkstatus); + ls->setTreeViewItem(tree_view_item); + + if(linkstatus->isRedirection() && linkstatus->redirection()) + slotLinkChecked(linkstatus->redirection(), anal); + + resultsSearchBar->show(); + ActionManager::getInstance()->action("file_export_html")->setEnabled(!isEmpty()); +} + +void SessionWidget::slotLinkChecked(LinkStatus const* linkstatus, LinkChecker * anal) +{ + slotSetTimeElapsed(); + + kdDebug(23100) << textlabel_progressbar->text() << endl; + + Q_ASSERT(textlabel_progressbar->text() == i18n("Checking...") || + textlabel_progressbar->text() == i18n("Stopped")); + + progressbar_checker->setProgress(progressbar_checker->progress() + 1); + + if(linkstatus->checked()) + { + TreeViewItem* tree_view_item = 0; + TreeViewItem* parent_item = linkstatus->parent()->treeViewItem(); + bool match = resultsSearchBar->currentLinkMatcher().matches(*linkstatus); + + if(tree_display_) + { + //kdDebug(23100) << "TREE!!!!!" << endl; + tree_view_item = new TreeViewItem(tree_view, parent_item, parent_item->lastChild(), linkstatus); + + parent_item->setLastChild(tree_view_item); + if(follow_last_link_checked_) + tree_view->ensureRowVisible(tree_view_item, tree_display_); + + tree_view_item->setEnabled(match); + } + else + { + //kdDebug(23100) << "FLAT!!!!!" << endl; + tree_view_item = new TreeViewItem(tree_view, tree_view->lastItem(), linkstatus); + if(follow_last_link_checked_) + tree_view->ensureRowVisible(tree_view_item, tree_display_); + + tree_view_item->setVisible(match); + } + + LinkStatus* ls = const_cast<LinkStatus*> (linkstatus); + ls->setTreeViewItem(tree_view_item); + + if(linkstatus->isRedirection() && linkstatus->redirection()) + slotLinkChecked(linkstatus->redirection(), anal); + } +} + +void SessionWidget::slotSearchFinished() +{ + Q_ASSERT(in_progress_); + Q_ASSERT(!paused_); + Q_ASSERT(!stopped_); + + KApplication::beep (); + + textlabel_progressbar->setText(i18n( "Ready" )); + progressbar_checker->reset(); + progressbar_checker->setPercentageVisible(false); + progressbar_checker->setTotalSteps(1); + progressbar_checker->setProgress(0); + + ready_ = true; + + textlabel_elapsed_time->setEnabled(true); + textlabel_elapsed_time_value->setEnabled(true); + textlabel_elapsed_time_value->setText(search_manager_->timeElapsed().toString("hh:mm:ss")); + + in_progress_ = false; + paused_ = false; + stopped_ = true; + resetPendingActions(); + action_manager_->slotUpdateSessionWidgetActions(this); + + emit signalSearchFinnished(); +} + +void SessionWidget::slotSearchPaused() +{ + Q_ASSERT(pendingActions()); + Q_ASSERT(in_progress_); + + KApplication::beep(); + + textlabel_progressbar->setText(i18n("Stopped")); + + ready_ = true; + + if(to_stop_) + { + in_progress_ = false; + paused_ = false; + stopped_ = true; + } + else + { + Q_ASSERT(to_pause_); + Q_ASSERT(!stopped_); + + paused_ = true; + } + + textlabel_elapsed_time->setEnabled(true); + textlabel_elapsed_time_value->setEnabled(true); + textlabel_elapsed_time_value->setText(search_manager_->timeElapsed().toString("hh:mm:ss")); + + resetPendingActions(); + action_manager_->slotUpdateSessionWidgetActions(this); + + emit signalSearchPaused(); +} + +void SessionWidget::insertUrlAtCombobox(QString const& url) +{ + combobox_url->addToHistory(url); +} + +void SessionWidget::showBottomStatusLabel(QListViewItem* item) +{ + kdDebug(23100) << "SessionWidget::showBottomStatusLabel" << endl; + + if(!item) + return; + + TreeViewItem* _item = tree_view->myItem(item); + if(_item) + { + QString status = _item->linkStatus()->statusText(); + textlabel_status->setText(status); + + if(textlabel_status->sizeHint().width() > textlabel_status->maximumWidth()) + QToolTip::add(textlabel_status, status); + else + QToolTip::remove(textlabel_status); + + bottom_status_timer_.stop(); + bottom_status_timer_.start(5 * 1000, true); + } +} + +void SessionWidget::clearBottomStatusLabel() +{ + textlabel_status->clear(); +} + +void SessionWidget::slotSetTimeElapsed() +{ + textlabel_elapsed_time_value->setText(search_manager_->timeElapsed().toString("hh:mm:ss")); +} + +void SessionWidget::slotAddingLevelTotalSteps(uint steps) +{ + textlabel_progressbar->setText(i18n( "Adding level..." )); + progressbar_checker->reset(); + progressbar_checker->setTotalSteps(steps); + progressbar_checker->setProgress(0); +} + +void SessionWidget::slotAddingLevelProgress() +{ + Q_ASSERT(textlabel_progressbar->text() == i18n( "Adding level..." )); + progressbar_checker->setProgress(progressbar_checker->progress() + 1); +} + +void SessionWidget::slotLinksToCheckTotalSteps(uint steps) +{ + textlabel_progressbar->setText(i18n( "Checking..." )); + progressbar_checker->reset(); + progressbar_checker->setTotalSteps(steps); + progressbar_checker->setProgress(0); +} + +void SessionWidget::slotClearComboUrl() +{ + combobox_url->setCurrentText(""); +} + +void SessionWidget::slotChooseUrlDialog() +{ + setUrl(KFileDialog::getOpenURL()); +} + +void SessionWidget::slotHideSearchPanel() +{ + if(buttongroup_search->isHidden()) + buttongroup_search->show(); + else + buttongroup_search->hide(); +} + +void SessionWidget::setFollowLastLinkChecked(bool follow) +{ + kdDebug(23100) << "setFollowLastLinkChecked: " << follow << endl; + follow_last_link_checked_ = follow; +} + +void SessionWidget::slotFollowLastLinkChecked() +{ + follow_last_link_checked_ = !follow_last_link_checked_; +} + +void SessionWidget::slotResetSearchOptions() +{ + slotLoadSettings(true); + + combobox_url->clear(); + lineedit_reg_exp->clear(); +} + +void SessionWidget::slotStartSearch() +{ + if(in_progress_) + { + start_search_action_->setChecked(true); // do not toggle + Q_ASSERT(!stopped_); + KApplication::beep(); + return; + } + + to_start_ = true; + slotLoadSettings(false); + slotCheck(); + resetPendingActions(); + + action_manager_->slotUpdateSessionWidgetActions(this); +} + +void SessionWidget::slotPauseSearch() +{ + Q_ASSERT(in_progress_); + Q_ASSERT(!stopped_); + + if(pendingActions()) + return; + + to_pause_ = true; + + if(!paused_) + { + Q_ASSERT(!ready_); + Q_ASSERT(search_manager_->searching()); + + search_manager_->cancelSearch(); + } + else + { + Q_ASSERT(ready_); + + paused_ = false; + + textlabel_progressbar->setText(i18n("Checking...")); + ready_ = false; + search_manager_->resume(); + + emit signalSearchStarted(); + slotLoadSettings(isEmpty()); // it seems that KConfigDialogManager is not trigering this slot + + resetPendingActions(); + } +} + +void SessionWidget::slotStopSearch() +{ + Q_ASSERT(in_progress_); + Q_ASSERT(!stopped_); + + if(pendingActions()) + return; + + to_stop_ = true; + + if(!paused_) + { + Q_ASSERT(!ready_); + Q_ASSERT(search_manager_->searching()); + + search_manager_->cancelSearch(); + } + else + { + in_progress_ = false; + paused_ = false; + stopped_ = true; + + action_manager_->slotUpdateSessionWidgetActions(this); + } +} + +bool SessionWidget::pendingActions() const +{ + return (to_start_ || to_pause_ || to_stop_); +} + +void SessionWidget::resetPendingActions() +{ + to_start_ = false; + to_pause_ = false; + to_stop_ = false; +} + +void SessionWidget::slotApplyFilter(LinkMatcher link_matcher) +{ + tree_view->show(link_matcher); +} + +void SessionWidget::slotExportAsHTML( ) +{ + KURL url = KFileDialog::getSaveURL(QString::null,"text/html", 0, i18n("Export Results as HTML")); + + if(url.isEmpty()) + return; + + QString filename; + KTempFile tmp; // ### only used for network export + + if(url.isLocalFile()) + filename = url.path(); + else + filename = tmp.name(); + + KSaveFile *savefile = new KSaveFile(filename); + if(savefile->status() == 0) // ok + { + QTextStream *outputStream = savefile->textStream(); + outputStream->setEncoding(QTextStream::UnicodeUTF8); + + QString xslt_doc = FileManager::read(locate("appdata", "styles/results_stylesheet.xsl")); + XSLT xslt(xslt_doc); +// kdDebug(23100) << search_manager_->toXML() << endl; + QString html_ouptut = xslt.transform(search_manager_->toXML()); + (*outputStream) << html_ouptut << endl; + + savefile->close(); + } + + delete savefile; + + if (url.isLocalFile()) + return; + + KIO::NetAccess::upload(filename, url, 0); +} + + +#include "sessionwidget.moc" diff --git a/klinkstatus/src/ui/sessionwidget.h b/klinkstatus/src/ui/sessionwidget.h new file mode 100644 index 00000000..af525a08 --- /dev/null +++ b/klinkstatus/src/ui/sessionwidget.h @@ -0,0 +1,149 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef SESSION_WIDGET_H +#define SESSION_WIDGET_H + +#include "sessionwidgetbase.h" +#include "../engine/linkchecker.h" +#include "../engine/linkstatus.h" +class SearchManager; +class TableItem; +class ActionManager; +class LinkMatcher; + +#include <qtimer.h> +#include <qstring.h> +class QStringList; +class QListViewItem; + +class KURL; +class KConfig; +class KToggleAction; + +#include <vector> + +using namespace std; + +class SessionWidget: public SessionWidgetBase +{ + Q_OBJECT + +public: + + SessionWidget(int max_simultaneous_connections = 3, int time_out = 50, + QWidget* parent = 0, const char* name = 0, WFlags f = 0); + + ~SessionWidget(); + + void setColumns(QStringList const& colunas); + void setUrl(KURL const& url); + + bool treeDisplay() const { return tree_display_; } + + bool followLastLinkChecked() const { return follow_last_link_checked_; } + void setFollowLastLinkChecked(bool follow); + + bool isEmpty() const; + SearchManager const* getSearchManager() const; + + bool inProgress() const { return in_progress_; } + bool paused() const { return paused_; } + bool stopped() const { return stopped_; } + +signals: + void signalUpdateTabLabel(const LinkStatus *, SessionWidget*); + void signalSearchStarted(); + void signalSearchPaused(); + void signalSearchFinnished(); + +public slots: + + virtual void slotClearComboUrl(); + void slotLoadSettings(bool modify_current_widget_settings = true); + + void slotStartSearch(); + void slotPauseSearch(); + void slotStopSearch(); + + void slotHideSearchPanel(); + void slotResetSearchOptions(); + void slotFollowLastLinkChecked(); + + void slotExportAsHTML(); + +private slots: + + virtual void slotCheck(); + virtual void slotCancel() {} // FIXME hack + //virtual void slotSuggestDomain(bool toogle); + + void slotEnableCheckButton(const QString &); + void slotRootChecked(LinkStatus const* linkstatus, LinkChecker * anal); + void slotLinkChecked(LinkStatus const* linkstatus, LinkChecker * anal); + void slotSearchFinished(); + void slotSearchPaused(); + /** Shows the status of the clicked URL (row) for 5 seconds */ + void showBottomStatusLabel(QListViewItem* item); + void clearBottomStatusLabel(); + void slotSetTimeElapsed(); + void newSearchManager(); + + void slotAddingLevelTotalSteps(uint steps); + void slotAddingLevelProgress(); + void slotLinksToCheckTotalSteps(uint steps); + + void slotChooseUrlDialog(); + + void slotApplyFilter(LinkMatcher); + +private: + + virtual void keyPressEvent ( QKeyEvent* e ); + bool validFields(); + void insertUrlAtCombobox(QString const& url); + void init(); + void saveCurrentCheckSettings(); + bool pendingActions() const; + void resetPendingActions(); + +private: + SearchManager* search_manager_; + ActionManager* action_manager_; + + bool ready_; + bool to_start_; + bool to_pause_; + bool to_stop_; + bool in_progress_; + bool paused_; + bool stopped_; + + QTimer bottom_status_timer_; + int max_simultaneous_connections_; + int time_out_; + bool tree_display_; // tree/flat result display + bool follow_last_link_checked_; + KToggleAction* start_search_action_; +}; + + + +#endif diff --git a/klinkstatus/src/ui/sessionwidgetbase.ui b/klinkstatus/src/ui/sessionwidgetbase.ui new file mode 100644 index 00000000..0d194143 --- /dev/null +++ b/klinkstatus/src/ui/sessionwidgetbase.ui @@ -0,0 +1,602 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>SessionWidgetBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>SessionWidgetBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>689</width> + <height>683</height> + </rect> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>300</height> + </size> + </property> + <property name="baseSize"> + <size> + <width>1000</width> + <height>500</height> + </size> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>buttongroup_search</cstring> + </property> + <property name="title"> + <string>Search</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout15</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout14</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout13</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QToolButton"> + <property name="name"> + <cstring>toolButton_clear_combo</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>25</width> + <height>25</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>25</width> + <height>25</height> + </size> + </property> + <property name="text"> + <string></string> + </property> + <property name="autoRaise"> + <bool>true</bool> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textlabel_url</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>4</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>URL: </string> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignLeft</set> + </property> + </widget> + <widget class="KLSHistoryCombo"> + <property name="name"> + <cstring>combobox_url</cstring> + </property> + <property name="focusPolicy"> + <enum>StrongFocus</enum> + </property> + <property name="acceptDrops"> + <bool>false</bool> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>pushbutton_url</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>4</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>Spacer11</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Maximum</enum> + </property> + <property name="sizeHint"> + <size> + <width>16</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout10</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>checkbox_recursively</cstring> + </property> + <property name="text"> + <string>Recursivel&y:</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Check pages recursively</string> + </property> + </widget> + <widget class="QSpinBox"> + <property name="name"> + <cstring>spinbox_depth</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="specialValueText"> + <string>Unlimited</string> + </property> + <property name="maxValue"> + <number>15</number> + </property> + <property name="minValue"> + <number>0</number> + </property> + <property name="value"> + <number>0</number> + </property> + <property name="toolTip" stdset="0"> + <string></string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>checkbox_subdirs_only</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>Do &not check parent folders</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>160</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout11</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>checkbox_external_links</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>Chec&k external links</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer5</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>MinimumExpanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>174</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout11</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Do not check regular expression:</string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>lineedit_reg_exp</cstring> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer5_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Maximum</enum> + </property> + <property name="sizeHint"> + <size> + <width>16</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + </vbox> + </widget> + </hbox> + </widget> + </vbox> + </widget> + <widget class="ResultsSearchBar"> + <property name="name"> + <cstring>resultsSearchBar</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="TreeView"> + <property name="name"> + <cstring>tree_view</cstring> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>300</height> + </size> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout10</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textlabel_progressbar</cstring> + </property> + <property name="minimumSize"> + <size> + <width>94</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>Ready</string> + </property> + </widget> + <widget class="KProgress"> + <property name="name"> + <cstring>progressbar_checker</cstring> + </property> + <property name="totalSteps"> + <number>1</number> + </property> + <property name="progress"> + <number>0</number> + </property> + <property name="percentageVisible"> + <bool>false</bool> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout11</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KSqueezedTextLabel"> + <property name="name"> + <cstring>textlabel_status</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>470</width> + <height>32767</height> + </size> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer5_2_3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Preferred</enum> + </property> + <property name="sizeHint"> + <size> + <width>30</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout12_2</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textlabel_elapsed_time</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>4</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Elapsed time:</string> + </property> + <property name="toolTip" stdset="0"> + <string>hh:mm:ss.zzz</string> + </property> + </widget> + <widget class="QLabel" row="1" column="1"> + <property name="name"> + <cstring>textlabel_elapsed_time_value</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>4</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>80</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string></string> + </property> + <property name="toolTip" stdset="0"> + <string>hh:mm:ss.zzz</string> + </property> + </widget> + </grid> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<customwidgets> + <customwidget> + <class>KLSHistoryCombo</class> + <header location="global">klshistorycombo.h</header> + <sizehint> + <width>-1</width> + <height>-1</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>5</hordata> + <verdata>5</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + </customwidget> + <customwidget> + <class>TreeView</class> + <header location="local">treeview.h</header> + <sizehint> + <width>-1</width> + <height>-1</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>7</hordata> + <verdata>7</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + </customwidget> + <customwidget> + <class>ResultsSearchBar</class> + <header location="local">resultssearchbar.h</header> + <sizehint> + <width>-1</width> + <height>-1</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>5</hordata> + <verdata>5</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image1</pixmap> + </customwidget> +</customwidgets> +<images> + <image name="image0"> + <data format="XBM.GZ" length="79">789c534e494dcbcc4b554829cdcdad8c2fcf4c29c95030e0524611cd48cd4ccf28010a1797249664262b2467241641a592324b8aa363156c15aab914146aadb90067111b1f</data> + </image> + <image name="image1"> + <data format="PNG" length="824">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b000002ff49444154388db59531681c4714863f992dde820cb370815b50600f54e8ca0ba43970712a8fb838438a3895634813d238a5ab80e314ae4d0a812060a4226017c27221c8a9da6b8c4fe0e00d28b0571cec82043b85611f78c12966efa4bb8bc085f29a6567df7cef9f7fdeccaec571cc2cbaddee47ae21e2385e5b9b815f1ebcfcd8de6a63ad25cb338af7c52741acb5a4a729a3d723a82ec6bd99d267bf3f23fc1c4cab2442d14a915986e792fdfa59569766573049417784f1b12e8267954dab24b78714450a28beaf941f847c2a14e70a0841035a2d45d641eb027213c210c69756320767794684d6508bef0befde1a860796e4c402333542b4256c0f0cdd1e50b97191458be6e0e27d81563a87c643d8fb2d7793d685d696413cc8a6cae46f65f7d79c7c62b87b4f2e15fd0fb0d302be0fefde4a0d557a5f35e90f84e0334014d590f855c9de4ecee17e4eb319d1ff3a00ec02f8c67299f283307c61e7d06fbf1782d082588a33e1cf1705fd81cf773f3601e1f9bec59e2f4b5c7ef5209f0ac95f16630cfd818067c103b586dd274a726229cee0fe8380d191cb4d1267d3d58aa1de7d258ceae5d7d0a78fdd269a86f0c52d414c49bbe3762c9b686de41560d7a72e41c4795a6486a78f95e4c4151481d686efbe7b3398ac58b1a23868b8c474aaa8068c8e714a8dd06c1af2a9e5d1c38c641c909dba6e08237f19b358a7ac5cf3479bc2e41f257e55d2ffc6a73833746f09e186cfa387904f2cbffc90a2aa9886d0e99464d3c5965b512cebd01f1800f67672e2a392fb0f023a3d883a053ffddcc2340dd65ab452b6074dc2cd15c1cbceb863daed413e353cdfcfd97d92333a12da6d0181ec3443cf753ef3cdd092de0e116ff1a02cdc157338ca9d7b8269461cfee1ba2139b9286e1a427f10110f2d561555b076d18a39383d4d99a4c0cd0b787f20747b214962c8266e3cdcf0e97c59126ec2f6edd089f40a92f115e0d1eb11ba238461dd6a15f32b53666de841965bb203575a3cc15a48c64a965fe57105e3635db8fa96dcffc431172b5d715d7103dc3fea7f015f373c8ee3b57f0135105a0fae7717960000000049454e44ae426082</data> + </image> +</images> +<connections> + <connection> + <sender>checkbox_recursively</sender> + <signal>toggled(bool)</signal> + <receiver>spinbox_depth</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>checkbox_recursively</sender> + <signal>toggled(bool)</signal> + <receiver>checkbox_external_links</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>checkbox_recursively</sender> + <signal>toggled(bool)</signal> + <receiver>checkbox_subdirs_only</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>toolButton_clear_combo</sender> + <signal>clicked()</signal> + <receiver>SessionWidgetBase</receiver> + <slot>slotClearComboUrl()</slot> + </connection> +</connections> +<tabstops> + <tabstop>combobox_url</tabstop> + <tabstop>checkbox_recursively</tabstop> + <tabstop>spinbox_depth</tabstop> + <tabstop>checkbox_subdirs_only</tabstop> + <tabstop>checkbox_external_links</tabstop> +</tabstops> +<slots> + <slot specifier="pure virtual">slotCheck()</slot> + <slot specifier="pure virtual">slotCancel()</slot> + <slot specifier="pure virtual">slotClearComboUrl()</slot> +</slots> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>klshistorycombo.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>resultssearchbar.h</includehint> + <includehint>treeview.h</includehint> + <includehint>kprogress.h</includehint> + <includehint>ksqueezedtextlabel.h</includehint> +</includehints> +</UI> diff --git a/klinkstatus/src/ui/settings/Makefile.am b/klinkstatus/src/ui/settings/Makefile.am new file mode 100644 index 00000000..777e9e5c --- /dev/null +++ b/klinkstatus/src/ui/settings/Makefile.am @@ -0,0 +1,7 @@ +INCLUDES = -I$(top_builddir)/klinkstatus/src/cfg -I$(top_builddir)/klinkstatus/src $(all_includes) +METASOURCES = AUTO +libsettings_la_LDFLAGS = $(all_libraries) +noinst_LTLIBRARIES = libsettings.la +libsettings_la_SOURCES = configsearchdialog.ui dummy.cpp configresultsdialog.ui \ + configidentificationdialog.cpp configidentificationdialogui.ui +noinst_HEADERS = configidentificationdialog.h diff --git a/klinkstatus/src/ui/settings/configidentificationdialog.cpp b/klinkstatus/src/ui/settings/configidentificationdialog.cpp new file mode 100644 index 00000000..3dcd1239 --- /dev/null +++ b/klinkstatus/src/ui/settings/configidentificationdialog.cpp @@ -0,0 +1,55 @@ +/*************************************************************************** + * Copyright (C) 2006 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "configidentificationdialog.h" + +#include <kprotocolmanager.h> +#include <kpushbutton.h> +#include <klineedit.h> + +#include <qstring.h> + +#include "../cfg/klsconfig.h" + + +ConfigIdentificationDialog::ConfigIdentificationDialog(QWidget *parent, const char *name) + : ConfigIdentificationDialogUi(parent, name) +{ + if(KLSConfig::userAgent().isEmpty()) + { + slotDefaultUA(); + } + + connect(buttonDefault, SIGNAL(clicked()), this, SLOT(slotDefaultUA())); +} + + +ConfigIdentificationDialog::~ConfigIdentificationDialog() +{ +} + +void ConfigIdentificationDialog::slotDefaultUA() +{ + KLSConfig::setUserAgent(KProtocolManager::defaultUserAgent()); + kcfg_UserAgent->setText(KLSConfig::userAgent()); +} + + +#include "configidentificationdialog.moc" diff --git a/klinkstatus/src/ui/settings/configidentificationdialog.h b/klinkstatus/src/ui/settings/configidentificationdialog.h new file mode 100644 index 00000000..e75a65a6 --- /dev/null +++ b/klinkstatus/src/ui/settings/configidentificationdialog.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2006 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef CONFIGIDENTIFICATIONDIALOG_H +#define CONFIGIDENTIFICATIONDIALOG_H + +#include "configidentificationdialogui.h" + +/** + @author Paulo Moura Guedes <moura@kdewebdev.org> +*/ +class ConfigIdentificationDialog : public ConfigIdentificationDialogUi +{ +Q_OBJECT +public: + ConfigIdentificationDialog(QWidget *parent = 0, const char *name = 0); + ~ConfigIdentificationDialog(); + +private slots: + void slotDefaultUA(); +}; + +#endif diff --git a/klinkstatus/src/ui/settings/configidentificationdialogui.ui b/klinkstatus/src/ui/settings/configidentificationdialogui.ui new file mode 100644 index 00000000..29723358 --- /dev/null +++ b/klinkstatus/src/ui/settings/configidentificationdialogui.ui @@ -0,0 +1,134 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>ConfigIdentificationDialogUi</class> +<widget class="QWidget"> + <property name="name"> + <cstring>ConfigIdentificationDialogUi</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>570</width> + <height>113</height> + </rect> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>buttonGroup4</cstring> + </property> + <property name="font"> + <font> + <bold>0</bold> + </font> + </property> + <property name="title"> + <string>Identification</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + </font> + </property> + <property name="text"> + <string>User-Agent</string> + </property> + </widget> + <widget class="KLineEdit" row="1" column="1"> + <property name="name"> + <cstring>kcfg_UserAgent</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>300</width> + <height>0</height> + </size> + </property> + </widget> + <widget class="KPushButton" row="1" column="2"> + <property name="name"> + <cstring>buttonDefault</cstring> + </property> + <property name="text"> + <string>Default</string> + </property> + </widget> + <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>kcfg_SendIdentification</cstring> + </property> + <property name="text"> + <string>Send Identification</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </grid> + </widget> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<connections> + <connection> + <sender>kcfg_SendIdentification</sender> + <signal>toggled(bool)</signal> + <receiver>textLabel1</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>kcfg_SendIdentification</sender> + <signal>toggled(bool)</signal> + <receiver>kcfg_UserAgent</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>kcfg_SendIdentification</sender> + <signal>toggled(bool)</signal> + <receiver>buttonDefault</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<tabstops> + <tabstop>kcfg_SendIdentification</tabstop> + <tabstop>kcfg_UserAgent</tabstop> + <tabstop>buttonDefault</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>klineedit.h</includehint> + <includehint>kpushbutton.h</includehint> +</includehints> +</UI> diff --git a/klinkstatus/src/ui/settings/configresultsdialog.ui b/klinkstatus/src/ui/settings/configresultsdialog.ui new file mode 100644 index 00000000..544f3273 --- /dev/null +++ b/klinkstatus/src/ui/settings/configresultsdialog.ui @@ -0,0 +1,72 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>ConfigResultsDialog</class> +<widget class="QWidget"> + <property name="name"> + <cstring>ConfigResultsDialog</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>233</width> + <height>183</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>buttonGroup13</cstring> + </property> + <property name="title"> + <string>View</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>kcfg_DisplayTreeView</cstring> + </property> + <property name="text"> + <string>Tree</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>kcfg_DisplayFlatView</cstring> + </property> + <property name="text"> + <string>Flat</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>buttonGroup13_2</cstring> + </property> + <property name="title"> + <string>Misc</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>kcfg_FollowLastLinkChecked</cstring> + </property> + <property name="text"> + <string>Follow Last Link Checked</string> + </property> + </widget> + </vbox> + </widget> + </vbox> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/klinkstatus/src/ui/settings/configsearchdialog.ui b/klinkstatus/src/ui/settings/configsearchdialog.ui new file mode 100644 index 00000000..604a431c --- /dev/null +++ b/klinkstatus/src/ui/settings/configsearchdialog.ui @@ -0,0 +1,353 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>ConfigSearchDialog</class> +<widget class="QWidget"> + <property name="name"> + <cstring>ConfigSearchDialog</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>459</width> + <height>365</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>buttonGroup13</cstring> + </property> + <property name="title"> + <string>Network</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KIntSpinBox" row="0" column="1"> + <property name="name"> + <cstring>kcfg_MaxConnectionsNumber</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="maxValue"> + <number>1000</number> + </property> + <property name="minValue"> + <number>1</number> + </property> + <property name="value"> + <number>5</number> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1_2_2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>4</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Timeout in seconds:</string> + </property> + </widget> + <widget class="KIntSpinBox" row="1" column="1"> + <property name="name"> + <cstring>kcfg_TimeOut</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maxValue"> + <number>3600</number> + </property> + <property name="minValue"> + <number>1</number> + </property> + <property name="value"> + <number>40</number> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>4</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Number of simultaneous connections:</string> + </property> + </widget> + </grid> + </widget> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>buttonGroup8</cstring> + </property> + <property name="title"> + <string>Input</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KIntSpinBox" row="0" column="1"> + <property name="name"> + <cstring>kcfg_MaxCountComboUrl</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maxValue"> + <number>1000</number> + </property> + <property name="minValue"> + <number>5</number> + </property> + <property name="value"> + <number>50</number> + </property> + </widget> + <widget class="QCheckBox" row="2" column="0"> + <property name="name"> + <cstring>kcfg_CheckParentFolders</cstring> + </property> + <property name="text"> + <string>Check parent folders</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>4</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Number of items in URL history:</string> + </property> + </widget> + <widget class="QCheckBox" row="3" column="0"> + <property name="name"> + <cstring>kcfg_CheckExternalLinks</cstring> + </property> + <property name="text"> + <string>Check external links</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="QLayoutWidget" row="1" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>layout21</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_RecursiveCheck</cstring> + </property> + <property name="text"> + <string>Recursive</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer25</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Maximum</enum> + </property> + <property name="sizeHint"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout20</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_2_2_2</cstring> + </property> + <property name="text"> + <string>Depth:</string> + </property> + </widget> + <widget class="KIntSpinBox"> + <property name="name"> + <cstring>kcfg_Depth</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="specialValueText"> + <string>Unlimited</string> + </property> + <property name="maxValue"> + <number>15</number> + </property> + <property name="minValue"> + <number>0</number> + </property> + <property name="value"> + <number>1</number> + </property> + </widget> + </hbox> + </widget> + </hbox> + </widget> + </grid> + </widget> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>buttonGroup3</cstring> + </property> + <property name="title"> + <string>Quanta</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox" row="0" column="0"> + <property name="name"> + <cstring>kcfg_UseQuantaUrlPreviewPrefix</cstring> + </property> + <property name="text"> + <string>Use preview prefix</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Check this one if you want to use Quanta's project preview prefix in the URL to check</string> + </property> + </widget> + </grid> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_RememberCheckSettings</cstring> + </property> + <property name="text"> + <string>Remember settings when exit</string> + </property> + </widget> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<connections> + <connection> + <sender>kcfg_RecursiveCheck</sender> + <signal>toggled(bool)</signal> + <receiver>kcfg_Depth</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>kcfg_RecursiveCheck</sender> + <signal>toggled(bool)</signal> + <receiver>textLabel1_2_2_2</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>kcfg_RecursiveCheck</sender> + <signal>toggled(bool)</signal> + <receiver>kcfg_CheckParentFolders</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>kcfg_RecursiveCheck</sender> + <signal>toggled(bool)</signal> + <receiver>kcfg_CheckExternalLinks</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<tabstops> + <tabstop>kcfg_MaxConnectionsNumber</tabstop> + <tabstop>kcfg_TimeOut</tabstop> + <tabstop>kcfg_MaxCountComboUrl</tabstop> + <tabstop>kcfg_RecursiveCheck</tabstop> + <tabstop>kcfg_Depth</tabstop> + <tabstop>kcfg_CheckParentFolders</tabstop> + <tabstop>kcfg_CheckExternalLinks</tabstop> + <tabstop>kcfg_RememberCheckSettings</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> +</includehints> +</UI> diff --git a/klinkstatus/src/ui/settings/dummy.cpp b/klinkstatus/src/ui/settings/dummy.cpp new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/klinkstatus/src/ui/settings/dummy.cpp diff --git a/klinkstatus/src/ui/tablelinkstatus.cpp b/klinkstatus/src/ui/tablelinkstatus.cpp new file mode 100644 index 00000000..695365a9 --- /dev/null +++ b/klinkstatus/src/ui/tablelinkstatus.cpp @@ -0,0 +1,750 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "tablelinkstatus.h" +#include "../utils/utils.h" +#include "../parser/url.h" +#include "../global.h" + +#include <qmemarray.h> +#include <qtooltip.h> +#include <qpixmap.h> +#include <qclipboard.h> +#include <qpainter.h> +#include <qprocess.h> + +#include <kapplication.h> +#include <kstandarddirs.h> +#include <krun.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <dcopclient.h> +#include <dcopref.h> + + +/* + +********************* TableLinkstatus *************************** + +*/ + +TableLinkstatus::TableLinkstatus(QWidget * parent, const char * name, + int column_index_status, + int column_index_label, + int column_index_URL) + : QTable(parent, name), + ResultView(column_index_status, column_index_label, column_index_URL) + //context_table_menu_(this, "context_table_menu") +{ + setShowGrid(false); + setSorting(false); + setSelectionMode(QTable::NoSelection); + setFocusStyle(QTable::FollowStyle); + setReadOnly(true); + + verticalHeader()->hide(); + setLeftMargin(0); + + cell_tip_ = new CellToolTip(this); + + sub_menu_ = new QPopupMenu(this, "sub_menu_referrers"); + + connect(this, SIGNAL( contextMenuRequested ( int, int, const QPoint& )), + this, SLOT( slotPopupContextMenu( int, int, const QPoint&)) ); +} + +TableLinkstatus::~TableLinkstatus() +{ + delete cell_tip_; +} + +void TableLinkstatus::setColumns(QStringList const& columns) +{ + ResultView::setColumns(columns); + + removeColunas(); + setNumCols(columns.size()); + + QHeader* horizontal_header = horizontalHeader(); + for(uint i = 0; i != columns.size(); ++i) + { + if(i == 0) + { + Q_ASSERT(columns[i] == i18n("Status") && col_status_ == 1); + setColumnWidth(i, STATUS_COLUMN_WIDTH); + } + else if(i == 1) + { + Q_ASSERT(columns[i] == i18n("Label") && col_label_ == 2); + setColumnWidth(i, width() / 3); + } + else if(i == 2) + Q_ASSERT(columns[i] == i18n("URL") && col_url_ == 3); + + horizontal_header->setLabel(i, i18n(columns[i])); + } + + setColumnStretchable(col_url_ - 1, true); + horizontal_header->adjustHeaderSize(); +} + +void TableLinkstatus::insertResult(LinkStatus const* linkstatus) +{ + insereLinha(generateRowOfTableItems(linkstatus)); +} + +vector<TableItem*> TableLinkstatus::generateRowOfTableItems(LinkStatus const* linkstatus) +{ + vector<TableItem*> items; + int column = 1; + + TableItem* item1 = new TableItemStatus(this, QTableItem::Never, + linkstatus, column++); + TableItem* item2 = new TableItemNome(this, QTableItem::Never, + linkstatus, column++); + TableItem* item3 = new TableItemURL(this, QTableItem::Never, + linkstatus, column++); + items.push_back(item1); + items.push_back(item2); + items.push_back(item3); + + // If more columns are choosed in the settings, create and add the items here + // ... + + return items; +} + +void TableLinkstatus::insereLinha(vector<TableItem*> items) +{ + Q_ASSERT(items.size() == (uint)numCols()); + + setNumRows(numRows() + 1); + int row = numRows() - 1; + + for(vector<TableItem*>::size_type i = 0; i != items.size(); ++i) + { + Q_ASSERT(items[i]); + + int col = items[i]->columnIndex() - 1; + setItem(row, col, items[i]); + } + + if(items[col_url_ - 1]->sizeHint().width() > columnWidth(col_url_ - 1)) + { + setColumnStretchable(col_url_ - 1, false); + setColumnWidth(col_url_ - 1, items[col_url_ - 1]->sizeHint().width()); + } + + ensureCellVisible(row, 0); +} + +void TableLinkstatus::clear() +{ + QMemArray<int> linhas(numRows()); + for(uint i = 0; i != linhas.size(); ++i) + linhas[i] = i + 1; + + removeRows(linhas); + + Q_ASSERT(numRows() == 0); +} + +void TableLinkstatus::removeColunas() +{ + QMemArray<int> columns(numCols()); + for(uint i = 0; i != columns.size(); ++i) + columns[i] = i + 1; + + removeColumns(columns); + + Q_ASSERT(numCols() == 0); +} + +void TableLinkstatus::show(ResultView::Status const& status) +{ + for(int i = 0; i != numRows(); ++i) + { + int row = i; + TableItem* _item = myItem(row, col_status_); + + if(!ResultView::displayableWithStatus(_item->linkStatus(), status)) + hideRow(row); + else + showRow(row); + } +} + +void TableLinkstatus::showAll() +{ + for(int i = 0; i != numRows(); ++i) + showRow(i); +} + +/* +void TableLinkstatus::mostraPorStatusCode(int status_code) +{ + for(int i = 0; i != numRows(); ++i) + { + int row = i + 1; + QTableItem* _item = myItem(row, col_status_); + + if(status_code != _item->text().toInt()) + hideRow(row); + } +} +*/ +/** + Use this procedure when you insert a row at the bottom of the table, + and you only want the to scroll down if you were already at the bottom, + before inserting the row. + This allows you to see what's going on on other cells without having + the table always scrolling down when every row is inserted. +*/ +void TableLinkstatus::ensureCellVisible(int row, int col) +{ + // table viewport is at the bottom + if(rowPos(row - 1) <= (contentsY() + visibleHeight())) + QTable::ensureCellVisible(row, col); +} + +bool TableLinkstatus::textFitsInCell(int row, int col) const +{ + QTableItem* itm(myItem(row, col)); + Q_ASSERT(itm); + + QSize size_hint(itm->sizeHint()); + int visible_width = 0; + + if(col == numCols() - 1) + visible_width = contentsX() + visibleWidth(); + else + visible_width = columnPos(col) + columnWidth(col); + + if(columnPos(col) + size_hint.width() > visible_width) + return false; + else + return true; +} + +bool TableLinkstatus::isEmpty() const +{ + return numRows() == 0; +} + +TableItem* TableLinkstatus::myItem(int row, int col) const +{ + TableItem* _item = dynamic_cast<TableItem*> (QTable::item(row, col)); + Q_ASSERT(_item); + return _item; +} + +void TableLinkstatus::slotPopupContextMenu(int r, int w, const QPoint& pos) +{ + TableItem* table_item = myItem(r, w); + if(table_item) + { + QValueVector<KURL> referrers = table_item->linkStatus()->referrers(); + loadContextTableMenu(referrers, table_item->linkStatus()->isRoot()); + context_table_menu_.popup(pos); + } +} + +void TableLinkstatus::loadContextTableMenu(QValueVector<KURL> const& referrers, bool is_root) +{ + context_table_menu_.clear(); + sub_menu_->clear(); + + if(!is_root) + { + sub_menu_->insertItem(i18n("All"), this, SLOT(slotEditReferrersWithQuanta())); + sub_menu_->insertSeparator(); + + for(uint i = 0; i != referrers.size(); ++i) + { + sub_menu_->insertItem(referrers[i].prettyURL()); + } + connect(sub_menu_, SIGNAL(activated(int)), this, SLOT(slotEditReferrerWithQuanta(int))); + + context_table_menu_.insertItem(SmallIconSet("fileopen"), i18n("Edit Referrer with Quanta"), + sub_menu_); + } + else + { + int id = context_table_menu_.insertItem(SmallIconSet("fileopen"), i18n("Edit Referrer with Quanta")); + context_table_menu_.setItemEnabled(id, false); + } + + context_table_menu_.insertItem(SmallIconSet("fileopen"), i18n("Open URL"), + this, SLOT(slotViewUrlInBrowser())); + + context_table_menu_.insertItem(SmallIconSet("fileopen"), i18n("Open Referrer URL"), + this, SLOT(slotViewParentUrlInBrowser())); + + context_table_menu_.insertSeparator(); + + context_table_menu_.insertItem(SmallIconSet("editcopy"), i18n("Copy URL"), + this, SLOT(slotCopyUrlToClipboard())); + + context_table_menu_.insertItem(SmallIconSet("editcopy"), i18n("Copy Referrer URL"), + this, SLOT(slotCopyParentUrlToClipboard())); + + context_table_menu_.insertItem(SmallIconSet("editcopy"), i18n("Copy Cell Text"), + this, SLOT(slotCopyCellTextToClipboard())); +} + +void TableLinkstatus::slotCopyUrlToClipboard() const +{ + TableItem* _item = myItem(currentRow(), currentColumn()); + QString content(_item->linkStatus()->absoluteUrl().prettyURL()); + QClipboard* cb = kapp->clipboard(); + cb->setText(content); +} + +void TableLinkstatus::slotCopyParentUrlToClipboard() const +{ + TableItem* _item = myItem(currentRow(), currentColumn()); + QString content(_item->linkStatus()->parent()->absoluteUrl().prettyURL()); + QClipboard* cb = kapp->clipboard(); + cb->setText(content); +} + +void TableLinkstatus::slotCopyCellTextToClipboard() const +{ + QString cell_text(text(currentRow(), currentColumn())); + QClipboard* cb = kapp->clipboard(); + cb->setText(cell_text); +} + +void TableLinkstatus::slotEditReferrersWithQuanta() +{ + TableItem* _item = myItem(currentRow(), currentColumn()); + QValueVector<KURL> referrers = _item->linkStatus()->referrers(); + + if(Global::isQuantaAvailableViaDCOP()) + { + for(uint i = 0; i != referrers.size(); ++i) + slotEditReferrerWithQuanta(referrers[i]); + } + else + { + QStringList list_urls; + + for(uint i = 0; i != referrers.size(); ++i) + list_urls.append(referrers[i].url()); + + Global::openQuanta(list_urls); + } +} + +void TableLinkstatus::slotEditReferrerWithQuanta(int id) +{ + int index = sub_menu_->indexOf(id); + + if(index == 0) + return; + Q_ASSERT(index != -1); + Q_ASSERT(index != 1); // separator + + //kdDebug(23100) << "id: " << id << endl; + //kdDebug(23100) << "index: " << index << endl; + + index -= 2; // The list of referrers starts on index = 2 + + TableItem* _item = myItem(currentRow(), currentColumn()); + QValueVector<KURL> referrers = _item->linkStatus()->referrers(); + Q_ASSERT(index >= 0 && (uint)index < referrers.size()); + + slotEditReferrerWithQuanta(referrers[index]); +} + +void TableLinkstatus::slotEditReferrerWithQuanta(KURL const& url) +{ + QString filePath = url.url(); + + if(Global::isQuantaAvailableViaDCOP()) + { + DCOPRef quanta(Global::quantaDCOPAppId(),"WindowManagerIf"); + bool success = quanta.send("openFile", filePath, 0, 0); + + if(!success) + { + QString message = i18n("<qt>File <b>%1</b> cannot be opened. Might be a DCOP problem.</qt>").arg(filePath); + KMessageBox::error(parentWidget(), message); + } + } + else + { + QStringList args(url.url()); + Global::openQuanta(args); + } +} + +void TableLinkstatus::slotViewUrlInBrowser() +{ + TableItem* _item = myItem(currentRow(), currentColumn()); + KURL url = _item->linkStatus()->absoluteUrl(); + + if(url.isValid()) + { + (void) new KRun (url, 0, url.isLocalFile(), true); + } + else + KMessageBox::sorry(this, i18n("Invalid URL.")); +} + +void TableLinkstatus::slotViewParentUrlInBrowser() +{ + TableItem* _item = myItem(currentRow(), currentColumn()); + + if(_item->linkStatus()->isRoot()) + { + KMessageBox::sorry(this, i18n("ROOT URL.")); + } + else + { + LinkStatus const* ls_parent = _item->linkStatus()->parent(); + Q_ASSERT(ls_parent); + + KURL url = ls_parent->absoluteUrl(); + + if(url.isValid()) + (void) new KRun (url, 0, url.isLocalFile(), true); + else + KMessageBox::sorry(this, i18n("Invalid URL.")); + } +} + +/* + +********************* TableItem *************************** + +*/ + +TableItem::TableItem(QTable* table, EditType et, + LinkStatus const* linkstatus, + int column_index, int alignment) + : QTableItem(table, et, ""), ResultViewItem(linkstatus, column_index), + /*ls_((LinkStatus*)linkstatus), + column_index_(column_index),*/ alignment_(alignment) +{ + //Q_ASSERT(ls_); + //Q_ASSERT(column_index_ > 0); +} + +TableItem::~TableItem() +{} + +void TableItem::setColumnIndex(int i) +{ + Q_ASSERT(i > 0 && i <= table()->numCols()); + + //column_index_ = i; + ResultViewItem::setColumnIndex(i); +} + +int TableItem::columnIndex() const +{ + Q_ASSERT(column_index_ <= table()->numCols()); + + return ResultViewItem::columnIndex(); +} + +void TableItem::setAlignment(int aFlags) +{ + alignment_ = aFlags; +} + +int TableItem::alignment() const +{ + return alignment_; +} +/* +LinkStatus const* const TableItem::linkStatus() const +{ + Q_ASSERT(ls_); + return ls_; +} + +QColor const& TableItem::textStatusColor() const +{ + if(linkStatus()->errorOccurred()) + { + //kdDebug(23100) << "ERROR: " << linkStatus()->error() << ": " << linkStatus()->absoluteUrl().prettyURL() << endl; + if(linkStatus()->error() == i18n( "Javascript not supported" )) + return Qt::lightGray; + else + return Qt::red; + } + + else if(linkStatus()->absoluteUrl().hasRef()) + return Qt::blue; + + else if(linkStatus()->absoluteUrl().protocol() != "http" && + linkStatus()->absoluteUrl().protocol() != "https") + return Qt::darkGreen; + + else + { + QString status_code(QString::number(linkStatus()->httpHeader().statusCode())); + + if(status_code[0] == '0') + { + kdWarning(23100) << "status code == 0: " << endl; + kdWarning(23100) << linkStatus()->toString() << endl; + kdWarning(23100) << linkStatus()->httpHeader().toString() << endl; + } + //Q_ASSERT(status_code[0] != '0'); + + if(status_code[0] == '5') + return Qt::darkMagenta; + + else if(status_code[0] == '4') + return Qt::red; + + else if(status_code[0] == '3') + return Qt::blue; + + else if(status_code[0] == '2') + return Qt::darkGreen; + + else + return Qt::red; + } +} +*/ + +/* + +********************* TableItemURL *************************** + +*/ + +TableItemURL::TableItemURL(QTable* table, EditType et, + LinkStatus const* linkstatus, int column_index) + : TableItem(table, et, linkstatus, column_index) +{ + setText(); +} + +void TableItemURL::setText() +{ + if(linkStatus()->node() && linkStatus()->malformed()) + { + if(linkStatus()->node()->url().isEmpty()) + QTableItem::setText( linkStatus()->node()->content().simplifyWhiteSpace() ); + else + QTableItem::setText( linkStatus()->node()->url() ); + } + else + { + KURL url = linkStatus()->absoluteUrl(); + QTableItem::setText(::convertToLocal(linkStatus())); + } +} + +void TableItemURL::setPixmap() +{} + +QString TableItemURL::toolTip() const +{ + return text(); // Pode parecer repeticao mas nao eh... Ver construtor +} + +void TableItemURL::paint( QPainter *p, const QColorGroup &cg, + const QRect &cr, bool selected ) +{ + // Get a color to draw the text + QColorGroup m_cg(cg); + QColor color(textStatusColor()); + m_cg.setColor(QColorGroup::Text, color); + + QTableItem::paint(p, m_cg, cr, selected); +} + +QColor const& TableItemURL::textStatusColor() const +{ + // TODO clean this code + + QString status_code(QString::number(linkStatus()->httpHeader().statusCode())); + + if(linkStatus()->errorOccurred()) + { + if(linkStatus()->error().contains(i18n( "Timeout" ))) + return darkMagenta; + else if(linkStatus()->error().contains(i18n( "not supported" ))) + return lightGray; + else + return red; + } + else if(linkStatus()->absoluteUrl().protocol() != "http" && + linkStatus()->absoluteUrl().protocol() != "https") + return black; + + else if(status_code[0] == '5') + return darkMagenta; + + else if(status_code[0] == '4') + return red; + + else + return black; +} + +/* + +********************* TableItemStatus *************************** + +*/ + +TableItemStatus::TableItemStatus(QTable* table, EditType et, + LinkStatus const* linkstatus, int column_index) + : TableItem(table, et, linkstatus, column_index) +{ + setAlignment(Qt::AlignHCenter /*| Qt :: AlignVCenter*/); + setText(); + setPixmap(); +} + +void TableItemStatus::setText() +{ + if(linkStatus()->errorOccurred() || + linkStatus()->status() == "OK" || + linkStatus()->status() == "304") + { + QTableItem::setText(""); + } + else + { + /* + if(linkStatus()->httpHeader().statusCode() == 0) + { + kdDebug(23100) << "TableItemStatus::setText : statusCode() == 0" << endl; + kdDebug(23100) << linkStatus()->toString() << endl; + kdDebug(23100) << linkStatus()->docHtml() << endl; + } + */ + //Q_ASSERT(linkStatus()->httpHeader().statusCode() != 0); //<------------------------------------------------------------ + //QTableItem::setText( QString::number(linkStatus()->httpHeader().statusCode()) ); + QTableItem::setText( linkStatus()->status() ); + } +} + +void TableItemStatus::setPixmap() +{ + if(linkStatus()->errorOccurred()) + { + + if(linkStatus()->error().contains(i18n( "Timeout" ))) + { + QTableItem::setPixmap(SmallIcon("kalarm")); + } + else if(linkStatus()->error() == i18n( "Malformed" )) + { + QTableItem::setPixmap(SmallIcon("bug")); + } + else + { + QTableItem::setPixmap(SmallIcon("no")); + } + } + else if(linkStatus()->status() == "304") + QTableItem::setPixmap(UserIcon("304")); + + else if(linkStatus()->status() == "OK") + QTableItem::setPixmap(SmallIcon("ok")); +} + +QString TableItemStatus::toolTip() const +{ + if(linkStatus()->errorOccurred() || + linkStatus()->absoluteUrl().hasRef() || + (linkStatus()->absoluteUrl().protocol() != "http" && + linkStatus()->absoluteUrl().protocol() != "https")) + { + return i18n("%1").arg(linkStatus()->status()); + } + else + return i18n("%1").arg(linkStatus()->httpHeader().reasonPhrase()); +} + +void TableItemStatus::paint( QPainter *p, const QColorGroup &cg, + const QRect &cr, bool selected ) +{ + p->fillRect( 0, 0, cr.width(), cr.height(), + selected ? cg.brush( QColorGroup::Highlight ) + : cg.brush( QColorGroup::Base ) ); + + int w = cr.width(); + int h = cr.height(); + + int x = 0; + if ( !pixmap().isNull() ) + { + p->drawPixmap( ( w - pixmap().width() ) / 2, + ( h - pixmap().height() ) / 2, + pixmap() ); + x = pixmap().width() + 2; + } + + // Get a color to draw the text + QColorGroup m_cg(cg); + QColor color(textStatusColor()); + m_cg.setColor(QColorGroup::Text, color); + + //QTableItem::paint(p, m_cg, cr, selected); + + if ( selected ) + p->setPen( m_cg.highlightedText() ); + else + p->setPen( m_cg.text() ); + p->drawText( x + 2, 0, w - x - 4, h, + wordWrap() ? (alignment() | WordBreak) : alignment(), text() ); +} + +/* + +********************* TableItemNome *************************** + +*/ + +TableItemNome::TableItemNome(QTable* table, EditType et, + LinkStatus const* linkstatus, int column_index) + : TableItem(table, et, linkstatus, column_index) +{ + setText(); +} + +void TableItemNome::setText() +{ + QString label(linkStatus()->label()); + if(!label.isNull()) + QTableItem::setText(label.simplifyWhiteSpace()); +} + +void TableItemNome::setPixmap() +{} + +QString TableItemNome::toolTip() const +{ + return text(); // Pode parecer repeticao mas nao eh... Ver construtor +} + +#include "tablelinkstatus.moc" diff --git a/klinkstatus/src/ui/tablelinkstatus.h b/klinkstatus/src/ui/tablelinkstatus.h new file mode 100644 index 00000000..0b3f2cf2 --- /dev/null +++ b/klinkstatus/src/ui/tablelinkstatus.h @@ -0,0 +1,202 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef TABLE_LINKSTATUS_H +#define TABLE_LINKSTATUS_H + +#include <qtable.h> +#include <qstring.h> +#include <qcolor.h> +#include <qpopupmenu.h> +#include <qvaluevector.h> +class QStringList; + +class KURL; + +#include <vector> + +#include "../engine/linkstatus.h" +#include "celltooltip.h" +#include "resultview.h" + +using namespace std; + + +int const STATUS_COLUMN_WIDTH = 50; + +class TableItem; + +class TableLinkstatus: public QTable, public ResultView +{ + Q_OBJECT +public: + + TableLinkstatus(QWidget * parent = 0, const char * name = 0, + int column_index_status = 1, + int column_index_label = 2, + int column_index_URL = 3); + ~TableLinkstatus(); + + virtual void setColumns(QStringList const& columns); + + /* Insere uma nova entrada no fim da tabela */ + virtual void insertResult(LinkStatus const* linkstatus); + + + virtual void clear(); + void removeColunas(); + virtual void show(ResultView::Status const& status); + virtual void showAll(); + + + /* Specialization of QTable::ensureCellVisible */ + virtual void ensureCellVisible(int row, int col); + + virtual bool textFitsInCell(int row, int col) const; + virtual bool isEmpty() const; + + TableItem* myItem(int row, int col) const; + +private slots: + + virtual void slotPopupContextMenu(int row, int col, const QPoint& pos); + virtual void slotCopyUrlToClipboard() const; + virtual void slotCopyParentUrlToClipboard() const; + virtual void slotCopyCellTextToClipboard() const; + virtual void slotEditReferrersWithQuanta(); + virtual void slotEditReferrerWithQuanta(int id); + virtual void slotEditReferrerWithQuanta(KURL const& url); + virtual void slotViewUrlInBrowser(); + virtual void slotViewParentUrlInBrowser(); + virtual void loadContextTableMenu(QValueVector<KURL> const& referrers, bool is_root = false); + +private: + + vector<TableItem*> generateRowOfTableItems(LinkStatus const* linkstatus); + void insereLinha(vector<TableItem*> items); + +private: +/* + int col_status_; + int col_label_; + int col_url_; + CellToolTip* cell_tip_; + QPopupMenu context_table_menu_; + QPopupMenu* sub_menu_; +*/ +}; + + +class TableItem: public QTableItem, public ResultViewItem +{ +public: + + TableItem(QTable* table, EditType et, + LinkStatus const* linkstatus, + int column_index, int alignment = Qt::AlignLeft); + virtual ~TableItem(); + + virtual void setColumnIndex(int i); + virtual int columnIndex() const; + + void setAlignment(int aFlags); + virtual int alignment() const; + + virtual QString toolTip() const = 0; + //LinkStatus const* const linkStatus() const; + +protected: + + //QColor const& textStatusColor() const; + virtual void paint( QPainter *p, const QColorGroup &cg, + const QRect &cr, bool selected ); + virtual void setText() = 0; + virtual void setPixmap() = 0; + +private: + + //LinkStatus* ls_; + //int column_index_; + int alignment_; +}; + + +class TableItemURL: public TableItem +{ +public: + + TableItemURL(QTable* table, EditType et, + LinkStatus const* linkstatus, int column_index = 3); + //virtual ~TableItemURL(){}; + + virtual QString toolTip() const; + +protected: + + virtual void setText(); + virtual void setPixmap(); + virtual void paint( QPainter *p, const QColorGroup &cg, const QRect &cr, bool selected ); + QColor const& textStatusColor() const; +}; + + +class TableItemStatus: public TableItem +{ +public: + + TableItemStatus(QTable* table, EditType et, + LinkStatus const* linkstatus, int column_index = 1); + //virtual ~TableItemStatus(){}; + + virtual QString toolTip() const; + +protected: + + virtual void setText(); + virtual void setPixmap(); + virtual void paint( QPainter *p, const QColorGroup &cg, const QRect &cr, bool selected ); +}; + + +class TableItemNome: public TableItem +{ +public: + + TableItemNome(QTable* table, EditType et, + LinkStatus const* linkstatus, int column_index = 2); + //virtual ~TableItemNome(){}; + + virtual QString toolTip() const; + +protected: + + virtual void setText(); + virtual void setPixmap(); + //virtual void paint( QPainter *p, const QColorGroup &cg, const QRect &cr, bool selected ); +}; + + +inline void TableItem::paint( QPainter *p, const QColorGroup &cg, + const QRect &cr, bool selected ) +{ + QTableItem::paint(p, cg, cr, selected); +} + +#endif diff --git a/klinkstatus/src/ui/tabwidgetsession.cpp b/klinkstatus/src/ui/tabwidgetsession.cpp new file mode 100644 index 00000000..9d9033a7 --- /dev/null +++ b/klinkstatus/src/ui/tabwidgetsession.cpp @@ -0,0 +1,274 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#include "tabwidgetsession.h" +#include "sessionwidget.h" +#include "klsconfig.h" +#include "treeview.h" +#include "../engine/searchmanager.h" +#include "../actionmanager.h" + +#include <qtoolbutton.h> +#include <qcursor.h> +#include <qtooltip.h> +#include <qpushbutton.h> +#include <qpixmap.h> +#include <qiconset.h> +#include <qstringlist.h> + +#include <kapplication.h> +#include <kstandarddirs.h> +#include <klocale.h> +#include <kstringhandler.h> +#include <kcharsets.h> +#include <kmimetype.h> +#include <kaction.h> +#include <kiconloader.h> + + +TabWidgetSession::TabWidgetSession(QWidget* parent, const char* name, WFlags f) + : KTabWidget(parent, name, f) // tabs_ is initialized with size 17 +{ + setFocusPolicy(QTabWidget::NoFocus); + setMargin(0); + setTabReorderingEnabled(true); + setHoverCloseButton(true); + setHoverCloseButtonDelayed(true); + + tabs_.setAutoDelete(false); + + QToolButton* tabs_new = new QToolButton(this); + tabs_new->setAccel(QKeySequence("Ctrl+N")); + connect(tabs_new, SIGNAL(clicked()), this, SLOT(slotNewSession())); + tabs_new->setIconSet(SmallIconSet("tab_new_raised")); + tabs_new->adjustSize(); + QToolTip::add(tabs_new, i18n("Open new tab")); + setCornerWidget(tabs_new, TopLeft); + + tabs_close_ = new QToolButton(this); + tabs_close_->setAccel(QKeySequence("Ctrl+W")); + connect(tabs_close_, SIGNAL(clicked()), this, SLOT(closeSession())); + tabs_close_->setIconSet(SmallIconSet("tab_remove")); + tabs_close_->adjustSize(); + QToolTip::add(tabs_close_, i18n("Close the current tab")); + setCornerWidget(tabs_close_, TopRight); + + connect(this, SIGNAL(currentChanged(QWidget*)), this, SLOT(slotCurrentChanged(QWidget*))); +} + +TabWidgetSession::~TabWidgetSession() +{} + +SessionWidget* TabWidgetSession::currentSession() const +{ + return tabs_[currentPageIndex()]; +} + +bool TabWidgetSession::emptySessionsExist() const +{ + if(count() == 0) + return true; + + for(int i = 0; i != count(); ++i) + { + Q_ASSERT(tabs_[i]); + if(tabs_[i]->isEmpty() && !tabs_[i]->getSearchManager()->searching()) + return true; + } + return false; +} + +SessionWidget* TabWidgetSession::getEmptySession() const +{ + Q_ASSERT(emptySessionsExist()); + Q_ASSERT(count() != 0); + + for(uint i = 0; i != tabs_.count(); ++i) + { + if(tabs_[i]->isEmpty()) + return tabs_[i]; + } + return 0; +} + +// Remember to use count() and not size() +QIntDict<SessionWidget> const& TabWidgetSession::sessions() const +{ + return tabs_; +} + +SessionWidget* TabWidgetSession::newSession() +{ + // TODO: settings: number of connections, timeout + SessionWidget* session_widget = newSessionWidget(); + connect(session_widget, SIGNAL(signalUpdateTabLabel(const LinkStatus *, SessionWidget*)), + this, SLOT(updateTabLabel(const LinkStatus *, SessionWidget*))); + + insertTab(session_widget, i18n("Session") + i18n(QString::number(count() + 1).ascii())); + + tabs_.insert(count() - 1, session_widget); + Q_ASSERT(tabs_[count() - 1]); + setCurrentPage(count() - 1); + + return session_widget; +} + +SessionWidget* TabWidgetSession::newSession(KURL const& url) +{ + SessionWidget* sessionwidget = newSession(); + currentSession()->setUrl(url); + + return sessionwidget; +} + +void TabWidgetSession::closeSession() +{ + if(count() > 1) + removePage(currentPage()); + + tabs_close_->setEnabled(count() > 1); + ActionManager::getInstance()->action("close_tab")->setEnabled(count() > 1); +} + +SessionWidget* TabWidgetSession::newSessionWidget() +{ + SessionWidget* session_widget = new SessionWidget(KLSConfig::maxConnectionsNumber(), + KLSConfig::timeOut(), this, QString("session_widget-" + count())); + + QStringList columns; + + columns.push_back(TreeView::URL_LABEL); + columns.push_back(TreeView::STATUS_LABEL); + if(KLSConfig::showMarkupStatus()) + columns.push_back(TreeView::MARKUP_LABEL); + columns.push_back(TreeView::LINK_LABEL_LABEL); + + session_widget->setColumns(columns); + + session_widget->tree_view->restoreLayout(KLSConfig::self()->config(), "klinkstatus"); + + return session_widget; +} + +void TabWidgetSession::updateTabLabel(LinkStatus const* linkstatus, SessionWidget* page) +{ + QString label; + KURL url = linkstatus->absoluteUrl(); + + if(linkstatus->hasHtmlDocTitle()) + { + label = linkstatus->htmlDocTitle(); + label = KStringHandler::csqueeze(label, 30); + } + else + { + if(url.fileName(false).isEmpty()) + label = url.prettyURL(); + else + label = url.fileName(false); + + label = KStringHandler::lsqueeze(label, 30); + } + + changeTab(page, KCharsets::resolveEntities(label)); + setTabIconSet(page, QIconSet(KMimeType::pixmapForURL(url))); +} + +void TabWidgetSession::slotLoadSettings() +{ + for(uint i = 0; i != tabs_.count(); ++i) + { + if(tabs_[i]->isEmpty()) + { + SessionWidget* session_widget = tabs_[i]; + if(session_widget->isEmpty()) + session_widget->slotLoadSettings(true); + else + session_widget->slotLoadSettings(false); + } + } +} + +void TabWidgetSession::setUrl(KURL const& url) +{ + currentSession()->setUrl(url); +} + +void TabWidgetSession::slotCurrentChanged(QWidget* /*page*/) +{ + tabs_close_->setEnabled(count() > 1); + + SessionWidget* session_widget = currentSession(); + ActionManager::getInstance()->slotUpdateSessionWidgetActions(session_widget); +} + +void TabWidgetSession::slotHideSearchPanel() +{ + currentSession()->slotHideSearchPanel(); +} + +void TabWidgetSession::slotFollowLastLinkChecked() +{ + currentSession()->slotFollowLastLinkChecked(); +} + +void TabWidgetSession::slotResetSearchOptions() +{ + currentSession()->slotResetSearchOptions(); +} + +void TabWidgetSession::slotNewSession(KURL const& url) +{ + if(count() == 0 || !emptySessionsExist()) + { + SessionWidget* sessionwidget = newSession(url); + ActionManager::getInstance()->initSessionWidget(sessionwidget); + } + else + { + SessionWidget* sessionwidget = getEmptySession(); + sessionwidget->setUrl(url); + showPage(sessionwidget); + } + + ActionManager::getInstance()->action("close_tab")->setEnabled(count() > 1); +} + +void TabWidgetSession::slotStartSearch() +{ + currentSession()->slotStartSearch(); +} + +void TabWidgetSession::slotPauseSearch() +{ + currentSession()->slotPauseSearch(); +} + +void TabWidgetSession::slotStopSearch() +{ + currentSession()->slotStopSearch(); +} + +void TabWidgetSession::slotExportAsHTML() +{ + currentSession()->slotExportAsHTML(); +} + + +#include "tabwidgetsession.moc" diff --git a/klinkstatus/src/ui/tabwidgetsession.h b/klinkstatus/src/ui/tabwidgetsession.h new file mode 100644 index 00000000..616c8173 --- /dev/null +++ b/klinkstatus/src/ui/tabwidgetsession.h @@ -0,0 +1,85 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#ifndef TABWIDGETSESSION_H +#define TABWIDGETSESSION_H + +#include <ktabwidget.h> +#include <kurl.h> + +#include <qintdict.h> +class QToolButton; + +class SessionWidget; +class LinkStatus; + + +/** +This class handles the creation and destruction of sessions, i.e, severals instances of searching tabs. + +@author Paulo Moura Guedes +*/ +class TabWidgetSession : public KTabWidget +{ + Q_OBJECT + +public: + TabWidgetSession(QWidget * parent = 0, const char * name = 0, WFlags f = 0); + ~TabWidgetSession(); + + /** Set the URL in the current session widget */ + void setUrl(KURL const& url); + + SessionWidget* currentSession() const; + bool emptySessionsExist() const; + /** Returns the first empty session it finds */ + SessionWidget* getEmptySession() const; + QIntDict<SessionWidget> const& sessions() const; + + +public slots: + void slotNewSession(KURL const& url = KURL()); + SessionWidget* newSession(); + SessionWidget* newSession(KURL const& url); + void closeSession(); + void updateTabLabel(LinkStatus const* linkstatus, SessionWidget*); + void slotLoadSettings(); + + void slotHideSearchPanel(); + void slotResetSearchOptions(); + void slotFollowLastLinkChecked(); + + void slotStartSearch(); + void slotPauseSearch(); + void slotStopSearch(); + + void slotExportAsHTML(); + +private slots: + void slotCurrentChanged(QWidget* page); + +private: + SessionWidget* newSessionWidget(); + +private: + QIntDict<SessionWidget> tabs_; + QToolButton* tabs_close_; +}; + +#endif diff --git a/klinkstatus/src/ui/treeview.cpp b/klinkstatus/src/ui/treeview.cpp new file mode 100644 index 00000000..7ad92d8e --- /dev/null +++ b/klinkstatus/src/ui/treeview.cpp @@ -0,0 +1,609 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include <klocale.h> +#include <kiconloader.h> +#include <kapplication.h> +#include <kurl.h> +#include <krun.h> +#include <dcopref.h> +#include <kmessagebox.h> +#include <dcopclient.h> +#include <kcharsets.h> + +#include <qvaluevector.h> +#include <qheader.h> +#include <qclipboard.h> + +#include "treeview.h" +#include "../global.h" +#include "../engine/linkstatus.h" +#include "../engine/linkfilter.h" +#include "../cfg/klsconfig.h" + + +TreeView::TreeView(QWidget *parent, const char *name) + : KListView(parent, name), + ResultView(), + current_column_(0) +{ + setShowToolTips(true); + //setAllColumnsShowFocus(true); + setSorting(1000); // don't start sorting any column + setShowSortIndicator(true); + //setFocusPolicy( WheelFocus ); + setRootIsDecorated(KLSConfig::displayTreeView()); +// setResizeMode(QListView::LastColumn); + + sub_menu_ = new QPopupMenu(this, "sub_menu_referrers"); + + connect(this, SIGNAL( rightButtonClicked ( QListViewItem *, const QPoint &, int )), + this, SLOT( slotPopupContextMenu( QListViewItem *, const QPoint &, int )) ); +} + + +TreeView::~TreeView() +{ + saveLayout(KLSConfig::self()->config(), "klinkstatus"); +} + +void TreeView::setColumns(QStringList const& columns) +{ + ResultView::setColumns(columns); + removeColunas(); + +// resetColumns is called automatically + for(uint i = 0; i != columns.size(); ++i) + { + addColumn(i18n(columns[i])); + setColumnWidthMode(i, QListView::Manual); + } + + setColumnAlignment(col_status_ - 1, Qt::AlignCenter); + if(KLSConfig::showMarkupStatus()) + setColumnAlignment(col_markup_ - 1, Qt::AlignCenter); +} + +void TreeView::resetColumns() +{ + setColumnWidth(col_url_ - 1, (int)(0.45 * width())); + + setResizeMode(QListView::LastColumn); // fit to the window + // resize again + setColumnWidthMode(col_label_ - 1, QListView::Manual); + setResizeMode(QListView::NoColumn); +} + +double TreeView::columnsWidth() const +{ + kdDebug(23100) << "columns: " << columns() << endl; + + double width = 0.0; + for(int i = 0; i != columns(); ++i) + { + kdDebug(23100) << "column width: " << columnWidth(i) << endl; + width += columnWidth(i); + } + return width; +} + +void TreeView::clear() +{ + KListView::clear(); +} + +void TreeView::removeColunas() +{ + clear(); +} + +void TreeView::show(ResultView::Status const& status) +{ + QListViewItemIterator it(static_cast<KListView*> (this)); + while(it.current()) + { + TreeViewItem* item = myItem(it.current()); + if(!ResultView::displayableWithStatus(item->linkStatus(), status)) + { + item->setVisible(false); + //kdDebug(23100) << "Hide: " << item->linkStatus()->absoluteUrl().url() << endl; + } + else + { + item->setVisible(true); + //item->setEnabled(true); + /* + if(KLSConfig::displayTreeView() && status != ResultView::good && item->parent()) + { + TreeViewItem* parent = myItem(item->parent()); + while(parent) + { + kdDebug(23100) << "Show: " << parent->linkStatus()->absoluteUrl().url() << endl; + + parent->setVisible(true); + //parent->setEnabled(false); + + if(parent->parent()) + parent = myItem(parent->parent()); + else + parent = 0; + } + } + */ + } +// + ++it; + } +} + +void TreeView::show(LinkMatcher link_matcher) +{ + QListViewItemIterator it(this); + while(it.current()) + { + TreeViewItem* item = myItem(it.current()); + bool match = link_matcher.matches(*(item->linkStatus())); + + if(tree_display_) + item->setEnabled(match); + else + item->setVisible(match); + + ++it; + } +} + +void TreeView::showAll() +{ + QListViewItemIterator it(this); + while(it.current()) + { + it.current()->setVisible(true); + //it.current()->setEnabled(true); + ++it; + } +} + +void TreeView::ensureRowVisible(const QListViewItem * i, bool tree_display) +{ + QScrollBar* vertical_scroll_bar = verticalScrollBar(); + + if(tree_display || + vertical_scroll_bar->value() > (vertical_scroll_bar->maxValue() - vertical_scroll_bar->lineStep())) + ensureItemVisible(i); +} + +bool TreeView::isEmpty() const +{ + return !childCount(); +} + +void TreeView::resizeEvent(QResizeEvent *e) +{ + KListView::resizeEvent(e); + resetColumns(); + clipper()->repaint(); +} + +void TreeView::slotPopupContextMenu(QListViewItem* item, const QPoint& pos, int col) +{ + current_column_ = col; + + TreeViewItem* tree_item = myItem(item); + if(tree_item) + { + QValueVector<KURL> referrers = tree_item->linkStatus()->referrers(); + loadContextTableMenu(referrers, tree_item->linkStatus()->isRoot()); + context_table_menu_.popup(pos); + } +} + +void TreeView::slotCopyUrlToClipboard() const +{ + TreeViewItem* _item = myItem(currentItem()); + QString content(_item->linkStatus()->absoluteUrl().prettyURL()); + QClipboard* cb = kapp->clipboard(); + cb->setText(content); +} + +void TreeView::slotCopyParentUrlToClipboard() const +{ + TreeViewItem* _item = myItem(currentItem()); + QString content(_item->linkStatus()->parent()->absoluteUrl().prettyURL()); + QClipboard* cb = kapp->clipboard(); + cb->setText(content); +} + +void TreeView::slotCopyCellTextToClipboard() const +{ + TreeViewItem* _item = myItem(currentItem()); + QString cell_text(_item->text(current_column_)); + QClipboard* cb = kapp->clipboard(); + cb->setText(cell_text); +} + +void TreeView::slotEditReferrersWithQuanta() +{ + TreeViewItem* _item = myItem(currentItem()); + QValueVector<KURL> referrers = _item->linkStatus()->referrers(); + + if(Global::isQuantaAvailableViaDCOP()) + { + for(uint i = 0; i != referrers.size(); ++i) + slotEditReferrerWithQuanta(referrers[i]); + } + else + { + QStringList list_urls; + + for(uint i = 0; i != referrers.size(); ++i) + list_urls.append(referrers[i].url()); + + Global::openQuanta(list_urls); + } +} + +void TreeView::slotEditReferrerWithQuanta(int id) +{ + int index = sub_menu_->indexOf(id); + + if(index == 0) + return; + Q_ASSERT(index != -1); + Q_ASSERT(index != 1); // separator + + //kdDebug(23100) << "id: " << id << endl; + //kdDebug(23100) << "index: " << index << endl; + + index -= 2; // The list of referrers starts on index = 2 + + TreeViewItem* _item = myItem(currentItem()); + QValueVector<KURL> referrers = _item->linkStatus()->referrers(); + Q_ASSERT(index >= 0 && (uint)index < referrers.size()); + + slotEditReferrerWithQuanta(referrers[index]); +} + +void TreeView::slotEditReferrerWithQuanta(KURL const& url) +{ + QString filePath = url.url(); + + if(Global::isQuantaAvailableViaDCOP()) + { + DCOPRef quanta(Global::quantaDCOPAppId(),"WindowManagerIf"); + bool success = quanta.send("openFile", filePath, 0, 0); + + if(!success) + { + QString message = i18n("<qt>File <b>%1</b> cannot be opened. Might be a DCOP problem.</qt>").arg(filePath); + KMessageBox::error(parentWidget(), message); + } + } + else + { + QStringList args(url.url()); + Global::openQuanta(args); + } +} + +void TreeView::slotViewUrlInBrowser() +{ + TreeViewItem* _item = myItem(currentItem()); + KURL url = _item->linkStatus()->absoluteUrl(); + + if(url.isValid()) + { + (void) new KRun (url, 0, url.isLocalFile(), true); + } + else + KMessageBox::sorry(this, i18n("Invalid URL.")); +} + +void TreeView::slotViewParentUrlInBrowser() +{ + TreeViewItem* _item = myItem(currentItem()); + + if(_item->linkStatus()->isRoot()) + { + KMessageBox::sorry(this, i18n("ROOT URL.")); + } + else + { + LinkStatus const* ls_parent = _item->linkStatus()->parent(); + Q_ASSERT(ls_parent); + + KURL url = ls_parent->absoluteUrl(); + + if(url.isValid()) + (void) new KRun (url, 0, url.isLocalFile(), true); + else + KMessageBox::sorry(this, i18n("Invalid URL.")); + } +} + +void TreeView::loadContextTableMenu(QValueVector<KURL> const& referrers, bool is_root) +{ + context_table_menu_.clear(); + sub_menu_->clear(); + + if(!is_root) + { + sub_menu_->insertItem(i18n("All"), this, SLOT(slotEditReferrersWithQuanta())); + sub_menu_->insertSeparator(); + + for(uint i = 0; i != referrers.size(); ++i) + { + sub_menu_->insertItem(referrers[i].prettyURL()); + } + connect(sub_menu_, SIGNAL(activated(int)), this, SLOT(slotEditReferrerWithQuanta(int))); + + context_table_menu_.insertItem(SmallIconSet("edit"), i18n("Edit Referrer with Quanta"), + sub_menu_); + context_table_menu_.insertSeparator(); + } + else + { + int id = context_table_menu_.insertItem(SmallIconSet("fileopen"), i18n("Edit Referrer with Quanta")); + context_table_menu_.setItemEnabled(id, false); + } + + context_table_menu_.insertItem(SmallIconSet("fileopen"), i18n("Open URL"), + this, SLOT(slotViewUrlInBrowser())); + + context_table_menu_.insertItem(/*SmallIconSet("fileopen"), */i18n("Open Referrer URL"), + this, SLOT(slotViewParentUrlInBrowser())); + + context_table_menu_.insertSeparator(); + + context_table_menu_.insertItem(SmallIconSet("editcopy"), i18n("Copy URL"), + this, SLOT(slotCopyUrlToClipboard())); + + context_table_menu_.insertItem(/*SmallIconSet("editcopy"), */i18n("Copy Referrer URL"), + this, SLOT(slotCopyParentUrlToClipboard())); + + context_table_menu_.insertItem(/*SmallIconSet("editcopy"), */i18n("Copy Cell Text"), + this, SLOT(slotCopyCellTextToClipboard())); +} + +TreeViewItem* TreeView::myItem(QListViewItem* item) const +{ + TreeViewItem* _item = dynamic_cast<TreeViewItem*> (item); + Q_ASSERT(_item); + return _item; +} + + +/* ******************************* TreeViewItem ******************************* */ + +TreeViewItem::TreeViewItem(TreeView* parent, QListViewItem* after, + LinkStatus const* linkstatus) + : KListViewItem(parent, after), + last_child_(0), root_(parent) +{ + init(linkstatus); +} + +TreeViewItem::TreeViewItem(TreeView* root, QListViewItem* listview_item, QListViewItem* after, + LinkStatus const* linkstatus) + : KListViewItem(listview_item, after), + last_child_(0), root_(root) + +{ + init(linkstatus); +} + +TreeViewItem::~TreeViewItem() +{} + +void TreeViewItem::init(LinkStatus const* linkstatus) +{ + setOpen(true); + + for(int i = 0; i != root_->numberOfColumns(); ++i) + { + TreeColumnViewItem item(root_, linkstatus, i + 1); + column_items_.push_back(item); + + if(i + 1 == root_->urlColumnIndex()) { + setText(item.columnIndex() - 1, KURL::decode_string( + KCharsets::resolveEntities(item.text(i + 1)))); + } + else { + setText(item.columnIndex() - 1, KCharsets::resolveEntities(item.text(i + 1))); + } + + setPixmap(item.columnIndex() - 1, item.pixmap(i + 1)); + } +} + +void TreeViewItem::setLastChild(QListViewItem* last_child) +{ + Q_ASSERT(last_child); + last_child_ = last_child; +} + +QListViewItem* TreeViewItem::lastChild() const +{ + return last_child_; +} + +QString TreeViewItem::key(int column, bool) const +{ + // FIXME magic numbers + switch(column) + { + case 1: // status column + return linkStatus()->statusText(); + } + + return text(column); +} + +LinkStatus const* TreeViewItem::linkStatus() const +{ + return column_items_[0].linkStatus(); +} + +void TreeViewItem::paintCell(QPainter * p, const QColorGroup & cg, int column, int width, int align) +{ + TreeColumnViewItem item = column_items_[column]; + + // Get a color to draw the text + QColorGroup m_cg(cg); + QColor color(item.textStatusColor()); + m_cg.setColor(QColorGroup::Text, color); + + KListViewItem::paintCell(p, m_cg, column, width, align); + + setHeight(22); +} + + +/* ******************************* TreeColumnViewItem ******************************* */ + +TreeColumnViewItem::TreeColumnViewItem(TreeView* root, LinkStatus const* linkstatus, int column_index) + : root_(root), ls_(linkstatus), column_index_(column_index) +{ + Q_ASSERT(ls_); +// Q_ASSERT(column_index_ > 0); +} + +TreeColumnViewItem::~TreeColumnViewItem() +{} + +/* +void TreeColumnViewItem::setColumnIndex(int i) +{ + Q_ASSERT(i > 0); + column_index_ = i; +} +*/ + +int TreeColumnViewItem::columnIndex() const +{ + return column_index_; +} + +LinkStatus const* TreeColumnViewItem::linkStatus() const +{ + Q_ASSERT(ls_); + return ls_; +} + +QColor const& TreeColumnViewItem::textStatusColor() const +{ + if(columnIndex() == root_->urlColumnIndex() || columnIndex() == root_->statusColumnIndex()) + { + if(linkStatus()->status() == LinkStatus::BROKEN) + return Qt::red; + else if(linkStatus()->status() == LinkStatus::HTTP_CLIENT_ERROR) + return Qt::red; + else if(linkStatus()->status() == LinkStatus::HTTP_REDIRECTION) + return Qt::black; + else if(linkStatus()->status() == LinkStatus::HTTP_SERVER_ERROR) + return Qt::darkMagenta; + else if(linkStatus()->status() == LinkStatus::MALFORMED) + return Qt::red; + else if(linkStatus()->status() == LinkStatus::NOT_SUPPORTED) + return Qt::lightGray; + else if(linkStatus()->status() == LinkStatus::SUCCESSFULL) + return Qt::black; + else if(linkStatus()->status() == LinkStatus::TIMEOUT) + return Qt::darkMagenta; + else if(linkStatus()->status() == LinkStatus::UNDETERMINED) + return Qt::blue; + + return Qt::red; + } + else + return Qt::black; +} + + +QString TreeColumnViewItem::text(int column) const +{ + Q_ASSERT(column > 0); + + + if(column == root_->urlColumnIndex()) + { + if(linkStatus()->node() && linkStatus()->malformed()) + { + if(linkStatus()->node()->url().isEmpty()) + return linkStatus()->node()->content().simplifyWhiteSpace(); + else + return linkStatus()->node()->url(); + } + else + { + KURL url = linkStatus()->absoluteUrl(); + return Url::convertToLocal(linkStatus()); + } + } + else if(column == root_->statusColumnIndex()) + { + return QString(); + } + else if(column == root_->labelColumnIndex()) + { + QString label(linkStatus()->label()); + if(!label.isNull()) + return label.simplifyWhiteSpace(); + } + + return QString(); +} + +QPixmap TreeColumnViewItem::pixmap(int column) const +{ + Q_ASSERT(column > 0); + + if(column == root_->statusColumnIndex()) + { + if(linkStatus()->status() == LinkStatus::BROKEN) + return SmallIcon("no"); + else if(linkStatus()->status() == LinkStatus::HTTP_CLIENT_ERROR) + return SmallIcon("no"); + else if(linkStatus()->status() == LinkStatus::HTTP_REDIRECTION) + { + if(linkStatus()->statusText() == "304") + return UserIcon("304"); + else + return SmallIcon("redo"); + } + else if(linkStatus()->status() == LinkStatus::HTTP_SERVER_ERROR) + return SmallIcon("no"); + else if(linkStatus()->status() == LinkStatus::MALFORMED) + return SmallIcon("editdelete"); + else if(linkStatus()->status() == LinkStatus::NOT_SUPPORTED) + return SmallIcon("help"); + else if(linkStatus()->status() == LinkStatus::SUCCESSFULL) + return SmallIcon("ok"); + else if(linkStatus()->status() == LinkStatus::TIMEOUT) + return SmallIcon("history_clear"); + else if(linkStatus()->status() == LinkStatus::UNDETERMINED) + return SmallIcon("help"); + } + + return QPixmap(); +} + + +#include "treeview.moc" diff --git a/klinkstatus/src/ui/treeview.h b/klinkstatus/src/ui/treeview.h new file mode 100644 index 00000000..eef34ff8 --- /dev/null +++ b/klinkstatus/src/ui/treeview.h @@ -0,0 +1,142 @@ +// +// C++ Interface: treeview +// +// Description: +// +// +// Author: Paulo Moura Guedes <moura@kdewebdev.org>, (C) 2004 +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TREEVIEW_H +#define TREEVIEW_H + +#include <klistview.h> + +#include "resultview.h" +class TreeViewItem; +class TreeColumnViewItem; +class LinkMatcher; + +/** +@author Paulo Moura Guedes +TreeView and TreeViewItem and currently a little messes up in its API +because of ResultView. ResultView class was to be the base interface to +a QTable and a QListView, but the APIs are a little diferent... then I realize +that a QTable view isn't needed at all so some day I will clean this up. +*/ +class TreeView : public KListView, public ResultView +{ + Q_OBJECT +public: + + TreeView(QWidget *parent = 0, const char *name = 0); + ~TreeView(); + + virtual void setColumns(QStringList const& columns); + virtual void clear(); + void removeColunas(); + virtual void show(ResultView::Status const& status); + void show(LinkMatcher link_matcher); + virtual void showAll(); + + void setTreeDisplay(bool tree_display); + + /** + If tree_display is false the view scrolls to follow the last link inserted, + except if the user scrolls the view up (like Konsole). + If tree_view, it follows always the last link inserted. + */ + void ensureRowVisible(const QListViewItem * i, bool tree_display); + virtual bool isEmpty() const; + + TreeViewItem* myItem(QListViewItem* item) const; + +protected: + virtual void resizeEvent(QResizeEvent *e); + +private slots: + + void slotPopupContextMenu(QListViewItem *, const QPoint &, int); + virtual void slotCopyUrlToClipboard() const; + virtual void slotCopyParentUrlToClipboard() const; + virtual void slotCopyCellTextToClipboard() const; + virtual void slotEditReferrersWithQuanta(); + virtual void slotEditReferrerWithQuanta(int id); + virtual void slotEditReferrerWithQuanta(KURL const& url); + virtual void slotViewUrlInBrowser(); + virtual void slotViewParentUrlInBrowser(); + virtual void loadContextTableMenu(QValueVector<KURL> const& referrers, bool is_root = false); + +private: + void resetColumns(); + double columnsWidth() const; + +private: + int current_column_; // apparently it's impossible to know what is the current column + bool tree_display_; +}; + +inline void TreeView::setTreeDisplay(bool tree_display) { + tree_display_ = tree_display; + setRootIsDecorated(tree_display_); +} + + +/* ******************************* TreeViewItem ******************************* */ + +class TreeViewItem: public KListViewItem +{ +public: + + TreeViewItem(TreeView* parent, QListViewItem* after, + LinkStatus const* linkstatus); + TreeViewItem(TreeView* root, QListViewItem* parent_item, QListViewItem* after, + LinkStatus const* linkstatus); + virtual ~TreeViewItem(); + + void setLastChild(QListViewItem* last_child); + QListViewItem* lastChild() const; + + QString key(int column, bool) const; + LinkStatus const* linkStatus() const; + +protected: + virtual void paintCell(QPainter * p, const QColorGroup & cg, int column, int width, int align); + +private: + void init(LinkStatus const* linkstatus); + +private: + QValueVector<TreeColumnViewItem> column_items_; + QListViewItem* last_child_; + TreeView* root_; +}; + + +/* ******************************* TreeColumnViewItem ******************************* */ + +class TreeColumnViewItem +{ +public: + TreeColumnViewItem() + {} + ; + TreeColumnViewItem(TreeView* root, LinkStatus const* linkstatus, int column_index); + ~TreeColumnViewItem(); + + //void setColumnIndex(int i); + int columnIndex() const; + LinkStatus const* linkStatus() const; + QColor const& textStatusColor() const; + QString text(int column) const; + QPixmap pixmap(int column) const; + +private: + TreeView* root_; + LinkStatus const* ls_; + int column_index_; +}; + +#endif diff --git a/klinkstatus/src/utils/Makefile.am b/klinkstatus/src/utils/Makefile.am new file mode 100644 index 00000000..0d2ba5ba --- /dev/null +++ b/klinkstatus/src/utils/Makefile.am @@ -0,0 +1,7 @@ +INCLUDES = $(LIBXSLT_CFLAGS) $(all_includes) +METASOURCES = AUTO +libutils_la_LDFLAGS = $(all_libraries) +noinst_LTLIBRARIES = libutils.la +noinst_HEADERS = mvector.h utils.h xsl.h +libutils_la_SOURCES = utils.cpp xsl.cpp +libutils_la_LIBADD = $(LIBXSLT_LIBS) $(LIBXML_LIBS) diff --git a/klinkstatus/src/utils/mvector.h b/klinkstatus/src/utils/mvector.h new file mode 100644 index 00000000..e48cfda3 --- /dev/null +++ b/klinkstatus/src/utils/mvector.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef VECTOR_H +#define VECTOR_H + +#include <vector> + +using namespace std; + +typedef unsigned int uint; + + +template<class T> +void append(vector<T>* const v1, vector<T> const* const v2); + + +template<class T> +void append(vector<T>* const v1, vector<T> const* const v2) +{ + v1->reserve(v1->size() + v2->size()); + + for(uint i = 0; i != v2->size(); ++i) + v1->push_back( (*v2)[i]); +} + + +#endif diff --git a/klinkstatus/src/utils/utils.cpp b/klinkstatus/src/utils/utils.cpp new file mode 100644 index 00000000..6259f8d0 --- /dev/null +++ b/klinkstatus/src/utils/utils.cpp @@ -0,0 +1,204 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "utils.h" + +#include <qprocess.h> +#include <qwidget.h> + +#include <kapplication.h> +#include <kmessagebox.h> +#include <kdebug.h> + + +QString htmlDocCharset[NUMBER_OF_HTML_CODES][2] = { + + { "€", "@" }, + { "	", "\t" }, + { " ", "\n" }, + { " ", "\r" }, + { " ", " " }, + { "!", "!" }, + { """, "\"" }, + { "#", "#" }, + { "$", "$" }, + { "%", "%" }, + { "&", "&" }, + { "'", "'" }, + { "(", "(" }, + { ")", ")" }, + { "*", "*" }, + { "+", "+" }, + { ",", "," }, + { "-", "-" }, + { ".", "." }, + { "/", "/" }, + // numbers.... + { ":", ":" }, + { ";", ";" }, + { "<", "<" }, + { "=", "=" }, + { ">", ">" }, + { "?", "?" }, + { "@", "@" }, + // letters... + { "[", "[" }, + { "\", "\\" }, + { "]", "]" }, + { "^", "^" }, + { "_", "_" }, + { "`", "`" }, + //letters... + { "{", "{" }, + { "|", "|" }, + { "}", "}" }, + { "~", "~" }, + { "€", "?" }, + { "‚", "," }, + { "ƒ", "?" }, + { "„", "\"" }, + { "…", "?" }, + { "†", "?" }, + { "‡", "?" }, + { "‰", "?" }, + { "Š", "?" }, + { "‹", "<" }, + { "Œ", "?" }, + { "Ž", "?" }, + { "‘", "'" }, + { "’", "'" }, + { "“", "\"" }, + { "”", "\"" }, + { "•", "*" }, + { "–", "-" }, + { "—", "-" }, + { "˜", "~" }, + { "™", "?" }, + { "š", "?" }, + { "›", ">" }, + { "œ", "?" }, + { "ž", "?" }, + { "Ÿ", "?" }, + { "¡", "?" }, + { "¢", "?" }, + { "£", "?" }, + { "¤", "?" }, + { "¥", "?" }, + { "¦", "?" }, + { "§", "?" }, + { "¨", "?" }, + { "©", "" }, + { "ª", "?" }, + { "«", "?" }, + { "¬", "?" }, + { "®", "?" }, + { "¯", "?" }, + { "°", "" }, + { "±", "?" }, + { "²", "" }, + { "³", "?" }, + { "´", "?" }, + { "µ", "?" }, + { "¶", "?" }, + { "·", "" }, + { "¸", "?" }, + { "¹", "?" }, + { "º", "?" }, + { "»", "?" }, + { "¼", "?" }, + { "½", "?" }, + { "¾", "?" } + +}; + + +void decode(QString& url) +{ + if( (int)url.find('&') != -1) + { + for(int i = 0; i != NUMBER_OF_HTML_CODES; ++i) + { + int index = url.find(htmlDocCharset[i][0]); + if(index != - 1) + { + url.replace(htmlDocCharset[i][0], htmlDocCharset[i][1]); + } + } + } +} +/* +void decode(string& url) +{ + if( (int)url.find('&') != -1) + { + for(int i = 0; i != NUMBER_OF_HTML_CODES; ++i) + { + int index = url.find(htmlDocCharset[i][0].latin1()); + if(index != - 1) + { + int length = htmlDocCharset[i][0].length(); + url.replace(index, length, htmlDocCharset[i][1].latin1()); + } + } + } +} +*/ +int smallerUnsigned(int a, int b) +{ + if(a >= 0 && b >= 0) + { + if(a < b) + return -1; + else if(a > b) + return 1; + else + return 0; + } + + else if(a < 0 && b < 0) + return 0; + + else if(a < 0) + return 1; + + else + return -1; +} + +namespace FileManager +{ +QString read(QString const& path) +{ + QFile file(path); + + if(!file.open(IO_ReadOnly)) + { + kdDebug() << "File " << path << " not found." << endl; + return QString(); + } + + QTextStream stream(&file); + QString fileString = stream.read(); + + file.close(); + + return fileString; +} +} diff --git a/klinkstatus/src/utils/utils.h b/klinkstatus/src/utils/utils.h new file mode 100644 index 00000000..97d81ea0 --- /dev/null +++ b/klinkstatus/src/utils/utils.h @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef UTILS_H +#define UTILS_H + +#include <kurl.h> +#include <qstring.h> + +//#include <string> + + +using namespace std; + + +int const NUMBER_OF_HTML_CODES = 92; +extern QString htmlDocCharset[NUMBER_OF_HTML_CODES][2]; + +/** + Decode the html charset. + e.g. + decode("mail@server.org") => "mail@server.org" +*/ +void decode(QString& url); +//void decode(string& url); + +/** + Compares to integers and returns -1 if a is smaller than b, + 1 if b is smaller than a, and 0 if a and b are equal or both negative. + If one of the integers is negative and the other isn't, it is considered + that the positive is smaller. + e.g.: + a = 0, b = +1 => -1 + a = +1, b = 0 => +1 + a = -1, b = -1 => 0 + a = +3, b = +3 => 0 + a = +1, b = -1 => -1 +*/ +int smallerUnsigned(int a, int b); + +namespace FileManager +{ +QString read(QString const& path); +} + + +#endif diff --git a/klinkstatus/src/utils/xsl.cpp b/klinkstatus/src/utils/xsl.cpp new file mode 100644 index 00000000..c3b13412 --- /dev/null +++ b/klinkstatus/src/utils/xsl.cpp @@ -0,0 +1,437 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#include "xsl.h" + +#include <libxml/globals.h> +#include <libxml/parser.h> + +// Don't try to sort the libxslt includes alphabetically! +// transform.h _HAS_ to be after xsltInternals.h and xsltconfig.h _HAS_ to be +// the first libxslt include or it will break the compilation on some +// libxslt versions +#include <libxslt/xsltconfig.h> +#include <libxslt/xsltInternals.h> +#include <libxslt/transform.h> + +// stdlib.h is required to build on Solaris +#include <stdlib.h> + +#include <qregexp.h> +#include <qsignal.h> +#include <qstylesheet.h> +#include <qthread.h> +#include <qevent.h> +#include <qmutex.h> + +#include <kapplication.h> +#include <kdebug.h> +#include <klocale.h> +#include <kstandarddirs.h> + +/** + * @author Jason Keirstead <jason@keirstead.org> + * + * The thread class that actually performs the XSL processing. + * Using a thread allows async operation. + */ +class KopeteXSLThread : public QObject, public QThread +{ +public: + /** + * Thread constructor + * + * @param xmlString The XML to be transformed + * @param xslString The XSL stylesheet we will use to transform + * @param target Target object to connect to for async operation + * @param slotCompleted Slot to fire on completion in asnc operation + */ + KopeteXSLThread( const QString &xmlString, xsltStylesheetPtr xslDoc, QObject *target = 0L, const char *slotCompleted = 0L ); + + /** + * Reimplemented from QThread. Does the processing. + */ + virtual void run(); + + /** + * A user event is used to get back to the UI thread to emit the completed signal + */ + bool event( QEvent *event ); + + static QString xsltTransform( const QString &xmlString, xsltStylesheetPtr xslDoc ); + + /** + * Returns the result string + */ + const QString &result() + { return m_resultString; }; + +private: + QString m_xml; + xsltStylesheetPtr m_xsl; + QString m_resultString; + QObject *m_target; + const char *m_slotCompleted; + QMutex dataMutex; +}; + +KopeteXSLThread::KopeteXSLThread( const QString &xmlString, xsltStylesheetPtr xslDoc, QObject *target, const char *slotCompleted ) +{ + m_xml = xmlString; + m_xsl = xslDoc; + + m_target = target; + m_slotCompleted = slotCompleted; +} + +void KopeteXSLThread::run() +{ + dataMutex.lock(); + m_resultString = xsltTransform( m_xml, m_xsl ); + dataMutex.unlock(); + // get back to the main thread + qApp->postEvent( this, new QEvent( QEvent::User ) ); +} + +bool KopeteXSLThread::event( QEvent *event ) +{ + if ( event->type() == QEvent::User ) + { + dataMutex.lock(); + if( m_target && m_slotCompleted ) + { + QSignal completeSignal( m_target ); + completeSignal.connect( m_target, m_slotCompleted ); + completeSignal.setValue( m_resultString ); + completeSignal.activate(); + } + dataMutex.unlock(); + delete this; + return true; + } + return QObject::event( event ); +} + +QString KopeteXSLThread::xsltTransform( const QString &xmlString, xsltStylesheetPtr styleSheet ) +{ + // Convert QString into a C string + QCString xmlCString = xmlString.utf8(); + + QString resultString; + QString errorMsg; + + xmlDocPtr xmlDoc = xmlParseMemory( xmlCString, xmlCString.length() ); + if ( xmlDoc ) + { + if ( styleSheet ) + { + static QCString appPath( QString::fromLatin1("\"%1\"").arg( KApplication::kApplication()->dirs()->findDirs("appdata", QString::fromLatin1("styles/data") ).front() ).utf8() ); + + static const char* params[3] = { + "appdata", + appPath, + NULL + }; + + xmlDocPtr resultDoc = xsltApplyStylesheet( styleSheet, xmlDoc, params ); + if ( resultDoc ) + { + // Save the result into the QString + xmlChar *mem; + int size; + xmlDocDumpMemory( resultDoc, &mem, &size ); + resultString = QString::fromUtf8( QCString( ( char * )( mem ), size + 1 ) ); + xmlFree( mem ); + xmlFreeDoc( resultDoc ); + } + else + { + errorMsg = i18n( "Message is null." ); + } + } + else + { + errorMsg = i18n( "The selected stylesheet is invalid." ); + } + + xmlFreeDoc( xmlDoc ); + } + else + { + errorMsg = i18n( "Message could not be parsed. This is likely due to an encoding problem." ); + } + + if ( resultString.isEmpty() ) + { + resultString = i18n( "<div><b>KLinkStatus encountered the following error while parsing a message:</b><br />%1</div>" ).arg( errorMsg ); + } + + #ifdef RAWXSL + kdDebug(23100) << k_funcinfo << resultString << endl; + #endif + return resultString; +} + +class XSLTPrivate +{ +public: + xmlDocPtr xslDoc; + xsltStylesheetPtr styleSheet; + unsigned int flags; +}; + +XSLT::XSLT( const QString &document, QObject *parent ) + : QObject( parent ), d(new XSLTPrivate) +{ + d->flags = 0; + d->xslDoc = 0; + d->styleSheet = 0; + + // Init Stuff + xmlLoadExtDtdDefaultValue = 0; + xmlSubstituteEntitiesDefault( 1 ); + + setXSLT( document ); +} + +XSLT::~XSLT() +{ + xsltFreeStylesheet( d->styleSheet ); + + delete d; +} + +void XSLT::setXSLT( const QString &_document ) +{ + // Search for '<kopete-i18n>' elements and feed them through i18n(). + // After that replace the %VAR% variables with their proper XSLT counterpart. + // + // FIXME: Preprocessing the document using the QString API is fast and simple, + // but also error-sensitive. + // In fact, there are a couple of known issues with this algorithm that + // depend on the strings in the current styles. If these strings change + // they may break the parsing here. + // + // The reason I'm doing it like this is because of issues with QDOM and + // namespaces in earlier Qt versions. When we drop Qt 3.1.x support we + // should probably convert this to more accurate DOM code. - Martijn + // + // Actually, since we need to parse into a libxml2 document anyway, this whole + // nonsense could be replaced with some simple XPath expressions - JK + // + QRegExp elementMatch( QString::fromLatin1( "<kopete-i18n>(.*)</kopete-i18n>" ) ); + elementMatch.setMinimal( true ); + QString document = _document; + int pos; + while ( ( pos = elementMatch.search( document ) ) != -1 ) + { + QString orig = elementMatch.cap( 1 ); + //kdDebug( 14010 ) << k_funcinfo << "Original text: " << orig << endl; + + // Split on % and go over all parts + // WARNING: If you change the translator comment, also change it in the + // styles/extracti18n Perl script, because the strings have to be + // identical! + QStringList parts = QStringList::split( '%', i18n( + "Translators: The %FOO% placeholders are variables that are substituted " + "in the code, please leave them untranslated", orig.utf8() ), true ); + + // The first part is always text, as our variables are written like %FOO% + QStringList::Iterator it = parts.begin(); + QString trans = *it; + bool prependPercent = true; + it = parts.remove( it ); + for ( it = parts.begin(); it != parts.end(); ++it ) + { + prependPercent = false; + + if ( *it == QString::fromLatin1( "TIME" ) ) + { + trans += QString::fromLatin1( "<xsl:value-of select=\"@time\"/>" ); + } + else if ( *it == QString::fromLatin1( "TIMESTAMP" ) ) + { + trans += QString::fromLatin1( "<xsl:value-of select=\"@timestamp\"/>" ); + } + else if ( *it == QString::fromLatin1( "FORMATTEDTIMESTAMP" ) ) + { + trans += QString::fromLatin1( "<xsl:value-of select=\"@formattedTimestamp\"/>" ); + } + else if ( *it == QString::fromLatin1( "FROM_CONTACT_DISPLAYNAME" ) ) + { + trans += QString::fromLatin1( "<span><xsl:attribute name=\"title\">" + "<xsl:choose>" + "<xsl:when test='from/contact/@contactId=from/contact/contactDisplayName/@text'>" + "<xsl:value-of disable-output-escaping=\"yes\" select=\"from/contact/metaContactDisplayName/@text\"/>" + "</xsl:when>" + "<xsl:otherwise>" + "<xsl:value-of disable-output-escaping=\"yes\" select=\"from/contact/metaContactDisplayName/@text\"/> " + "(<xsl:value-of disable-output-escaping=\"yes\" select=\"from/contact/@contactId\"/>)" + "</xsl:otherwise>" + "</xsl:choose></xsl:attribute>" + "<xsl:attribute name=\"dir\">" + "<xsl:value-of select=\"from/contact/contactDisplayName/@dir\"/>" + "</xsl:attribute>" + "<xsl:value-of disable-output-escaping=\"yes\" select=\"from/contact/contactDisplayName/@text\"/></span>" ); + } + else if ( *it == QString::fromLatin1( "TO_CONTACT_DISPLAYNAME" ) ) + { + trans += QString::fromLatin1( "<span><xsl:attribute name=\"title\">" + "<xsl:choose>" + "<xsl:when test='to/contact/@contactId=from/contact/contactDisplayName/@text'>" + "<xsl:value-of disable-output-escaping=\"yes\" select=\"to/contact/metaContactDisplayName/@text\"/>" + "</xsl:when>" + "<xsl:otherwise>" + "<xsl:value-of disable-output-escaping=\"yes\" select=\"to/contact/metaContactDisplayName/@text\"/> " + "(<xsl:value-of disable-output-escaping=\"yes\" select=\"to/contact/@contactId\"/>)" + "</xsl:otherwise>" + "</xsl:choose></xsl:attribute>" + "<xsl:attribute name=\"dir\">" + "<xsl:value-of select=\"to/contact/contactDisplayName/@dir\"/>" + "</xsl:attribute>" + "<xsl:value-of disable-output-escaping=\"yes\" select=\"to/contact/contactDisplayName/@text\"/></span>" ); + } + else if ( *it == QString::fromLatin1( "FROM_METACONTACT_DISPLAYNAME" ) ) + { + trans += QString::fromLatin1( "<span>" + "<xsl:attribute name=\"dir\">" + "<xsl:value-of select=\"from/contact/metaContactDisplayName/@dir\"/>" + "</xsl:attribute>" + "<xsl:value-of disable-output-escaping=\"yes\" select=\"from/contact/metaContactDisplayName/@text\"/></span>" ); + } + else if ( *it == QString::fromLatin1( "TO_METACONTACT_DISPLAYNAME" ) ) + { + trans += QString::fromLatin1( "<span>" + "<xsl:attribute name=\"dir\">" + "<xsl:value-of select=\"to/contact/metaContactDisplayName/@dir\"/>" + "</xsl:attribute>" + "<xsl:value-of disable-output-escaping=\"yes\" select=\"to/contact/metaContactDisplayName/@text\"/></span>" ); + } + else if ( *it == QString::fromLatin1( "FROM_CONTACT_ID" ) ) + { + trans += QString::fromLatin1( "<span><xsl:attribute name=\"title\">" + "<xsl:value-of disable-output-escaping=\"yes\" select=\"from/contact/contactDisplayName/@text\"/></xsl:attribute>" + "<xsl:value-of disable-output-escaping=\"yes\" select=\"from/contact/@contactId\"/></span>" ); + } + else if ( *it == QString::fromLatin1( "TO_CONTACT_ID" ) ) + { + trans += QString::fromLatin1( "<span><xsl:attribute name=\"title\">" + "<xsl:value-of disable-output-escaping=\"yes\" select=\"to/contact/contactDisplayName/@text\"/></xsl:attribute>" + "<xsl:value-of disable-output-escaping=\"yes\" select=\"to/contact/@contactId\"/></span>" ); + } + else if ( *it == QString::fromLatin1( "BODY" ) ) + { + trans += QString::fromLatin1( "<xsl:value-of disable-output-escaping=\"yes\" select=\"body\"/>" ); + } + else + { + if ( prependPercent ) + trans += '%'; + trans += *it; + prependPercent = true; + } + } + //kdDebug( 14010 ) << k_funcinfo << "Translated text: " << trans << endl; + // Add "<kopete-i18n>" and "</kopete-i18n>" to length, hence the '+ 27' + document.replace( uint( pos ), orig.length() + 27, trans ); + } + + #ifdef RAWXSL + kdDebug(14000) << k_funcinfo << document.utf8() << endl; + #endif + + //Freeing the stylesheet also frees the doc pointer; + xsltFreeStylesheet( d->styleSheet ); + d->styleSheet = 0; + d->xslDoc = 0; + d->flags = 0; + + QCString rawDocument = document.utf8(); + d->xslDoc = xmlParseMemory( rawDocument, rawDocument.length() ); + + if( d->xslDoc ) + { + d->styleSheet = xsltParseStylesheetDoc( d->xslDoc ); + if( d->styleSheet ) + { + // Check for flags + QStringList flags; + for( xmlNodePtr child = d->xslDoc->children; child != d->xslDoc->last; child = child->next ) + { + if( child->type == XML_PI_NODE ) + { + //We have a flag. Enable it; + QCString flagData( (const char*)child->content ); + + if( flagData.contains( "Flag:" ) ) + { + flags += flagData.mid(5); + } + } + } + + if( !flags.isEmpty() ) + setProperty("flags", flags.join( QString::fromLatin1("|") ) ); + } + else + { + kdWarning(14000) << "Invalid stylesheet provided" << endl; + + //We don't have a stylesheet, so free the doc pointer + xmlFreeDoc( d->xslDoc ); + d->styleSheet = 0; + d->xslDoc = 0; + } + } + else + { + kdWarning(14000) << "Invalid stylesheet provided" << endl; + d->xslDoc = 0; + } +} + +QString XSLT::transform( const QString &xmlString ) +{ + return KopeteXSLThread::xsltTransform( xmlString, d->styleSheet ); +} + +void XSLT::transformAsync( const QString &xmlString, QObject *target, const char *slotCompleted ) +{ + ( new KopeteXSLThread( xmlString, d->styleSheet, target, slotCompleted ) )->start(); +} + +bool XSLT::isValid() const +{ + return d->styleSheet != NULL; +} + +void XSLT::setFlags( unsigned int flags ) +{ + d->flags = flags; +} + +unsigned int XSLT::flags() const +{ + return d->flags; +} + +#include "xsl.moc" + +// vim: set noet ts=4 sts=4 sw=4: + diff --git a/klinkstatus/src/utils/xsl.h b/klinkstatus/src/utils/xsl.h new file mode 100644 index 00000000..9d12c1c1 --- /dev/null +++ b/klinkstatus/src/utils/xsl.h @@ -0,0 +1,111 @@ +/*************************************************************************** + * Copyright (C) 2004 by Paulo Moura Guedes * + * moura@kdewebdev.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#ifndef XSL_H +#define XSL_H + +#include <qobject.h> + +class XSLTPrivate; + +/** + @author Paulo Moura Guedes <moura@kdewebdev.org> + + Taken from kopetexsl. Kudos to the Kopete team. + * + * This class provides an easy to use interface to basic + * libxslt transformations. +*/ +class XSLT : public QObject +{ + Q_OBJECT + + Q_PROPERTY( Flags flags READ flags WRITE setFlags ) + Q_PROPERTY( bool isValid READ isValid ) + + Q_SETS( Flags ) + +public: + /** + * Special flags to be used during the transformation process. Passed + * into the engine as processing instructions. + */ + enum Flags + { + TransformAllMessages = 1 + }; + + /** + * Constructor. + * + * Constructs a new XSLT parser using the provided XSLT document + */ + XSLT( const QString &xsltDocument, QObject *parent = 0L ); + + virtual ~XSLT(); + + /** + * Set the XSLT document + * + * @return an ORed set of @ref Flags, or 0 if none + */ + void setXSLT( const QString &document ); + + /** + * Transforms the XML string using the XSLT document, synchronously + * + * @param xmlString The source XML + * @return The result of the transformation + */ + QString transform( const QString &xmlString ); + + /** + * Transforms the XML string using the XSLT document, asynchronously + * + * @param xmlString The source XML + * @param target The QObject that contains the slot to be executed when processing is complete + * @param slotCompleted A slot that accepts a QVariant & paramater, that is the result + * of the transformation + */ + void transformAsync( const QString &xmlString, QObject *target, const char *slotCompleted ); + + /** + * Check whether the XSLT document is valid + * + * @return Whether the document represents a valid XSLT stylesheet + */ + bool isValid() const; + + /** + * @return An ORed list of Flags that the current stylesheet provides via processing instructions + */ + unsigned int flags() const; + + /** + * Sets flags to be used for the transformation. + * + * @param flags An ORed list of flags + */ + void setFlags( unsigned int flags ); + +private: + XSLTPrivate *d; +}; + +#endif |