diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | bd9e6617827818fd043452c08c606f07b78014a0 (patch) | |
tree | 425bb4c3168f9c02f10150f235d2cb998dcc6108 /kbugbuster | |
download | tdesdk-bd9e6617827818fd043452c08c606f07b78014a0.tar.gz tdesdk-bd9e6617827818fd043452c08c606f07b78014a0.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdesdk@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kbugbuster')
119 files changed, 12603 insertions, 0 deletions
diff --git a/kbugbuster/AUTHORS b/kbugbuster/AUTHORS new file mode 100644 index 00000000..08de29fa --- /dev/null +++ b/kbugbuster/AUTHORS @@ -0,0 +1,4 @@ +Martijn Klingens <klingens@kde.org> +Simon Hausmann <hausmann@kde.org> +Cornelius Schumacher <schumacher@kde.org> +David Faure <faure@kde.org> diff --git a/kbugbuster/COPYING b/kbugbuster/COPYING new file mode 100644 index 00000000..8832c184 --- /dev/null +++ b/kbugbuster/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 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) 19yy <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/kbugbuster/ChangeLog b/kbugbuster/ChangeLog new file mode 100644 index 00000000..b17cae8f --- /dev/null +++ b/kbugbuster/ChangeLog @@ -0,0 +1,4 @@ +CHANGELOG FOR KBUGBUSTER +------------------------ +30 Jul 2001 - Started development + diff --git a/kbugbuster/INSTALL b/kbugbuster/INSTALL new file mode 100644 index 00000000..02a4a074 --- /dev/null +++ b/kbugbuster/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/kbugbuster/Makefile.am b/kbugbuster/Makefile.am new file mode 100644 index 00000000..44b31733 --- /dev/null +++ b/kbugbuster/Makefile.am @@ -0,0 +1,28 @@ +INCLUDES= -I$(top_srcdir)/kbugbuster/backend $(all_includes) + +if include_kcalresource +KRESOURCES_SUBDIR = kresources +endif + +SUBDIRS = backend gui pics $(KRESOURCES_SUBDIR) + +bin_PROGRAMS = kbugbuster + +kbugbuster_SOURCES = main.cpp +kbugbuster_LDADD = -lkutils gui/libkbbmainwindow.la \ + backend/libkbbbackend.la $(LIB_KHTML) $(LIB_KIO) +kbugbuster_LDFLAGS = $(all_libraries) $(KDE_RPATH) + +xdg_apps_DATA = kbugbuster.desktop + +KDE_ICON = kbugbuster + +METASOURCES = AUTO + +EXTRA_DIST = AUTHORS COPYING ChangeLog INSTALL README TODO kbugbuster.lsm + +messages: rc.cpp + $(EXTRACTRC) */*.kcfg >> rc.cpp + $(EXTRACTRC) gui/*.rc >> rc.cpp + $(EXTRACTRC) gui/*.ui >> rc.cpp + $(XGETTEXT) *.cpp */*.h */*.cpp -o $(podir)/kbugbuster.pot diff --git a/kbugbuster/README b/kbugbuster/README new file mode 100644 index 00000000..aee7659d --- /dev/null +++ b/kbugbuster/README @@ -0,0 +1,2 @@ +This is still heavily under development, use at your own risk ;-) + diff --git a/kbugbuster/TODO b/kbugbuster/TODO new file mode 100644 index 00000000..e4b4622b --- /dev/null +++ b/kbugbuster/TODO @@ -0,0 +1,4 @@ +TODO +---- + +- Consistently use KBB namespace (at least in backend) diff --git a/kbugbuster/backend/Makefile.am b/kbugbuster/backend/Makefile.am new file mode 100644 index 00000000..e1bfd266 --- /dev/null +++ b/kbugbuster/backend/Makefile.am @@ -0,0 +1,16 @@ +INCLUDES= -I$(srcdir)/.. $(all_includes) + +noinst_LTLIBRARIES = libkbbbackend.la + +libkbbbackend_la_SOURCES = packagelistjob.cpp bugjob.cpp \ + package.cpp bugsystem.cpp bug.cpp bugdetails.cpp \ + bugcommand.cpp buglistjob.cpp bugmybugsjob.cpp \ + mailsender.cpp bugcache.cpp bugdetailsjob.cpp \ + person.cpp smtp.cpp bugserver.cpp \ + bugserverconfig.cpp \ + processor.cpp \ + domprocessor.cpp rdfprocessor.cpp htmlparser.cpp \ + kbbprefs.cpp + +METASOURCES = AUTO + diff --git a/kbugbuster/backend/bug.cpp b/kbugbuster/backend/bug.cpp new file mode 100644 index 00000000..4523652e --- /dev/null +++ b/kbugbuster/backend/bug.cpp @@ -0,0 +1,240 @@ + +#include "bug.h" + +#include "bugimpl.h" + +#include <assert.h> +#include <kdebug.h> + +Bug::Bug() +: m_impl( NULL ) +{ +} + +Bug::Bug( BugImpl *impl ) +: m_impl( impl ) +{ +} + +Bug::Bug( const Bug &other ) +{ + (*this) = other; +} + +Bug Bug::fromNumber( const QString &bugNumber ) +{ + return new BugImpl( QString::null, Person(), bugNumber, 0xFFFFFFFF, Normal, Person(), + Unconfirmed, Bug::BugMergeList() ); +} + +Bug &Bug::operator=( const Bug &rhs ) +{ + m_impl = rhs.m_impl; + return *this; +} + +Bug::~Bug() +{ +} + +QString Bug::severityLabel( Bug::Severity s ) +{ + switch ( s ) + { + case Critical: return i18n("Critical"); + case Grave: return i18n("Grave"); + case Major: return i18n("Major"); + case Crash: return i18n("Crash"); + case Normal: return i18n("Normal"); + case Minor: return i18n("Minor"); + case Wishlist: return i18n("Wishlist"); + default: + case SeverityUndefined: return i18n("Undefined"); + } +} + +QString Bug::severityToString( Bug::Severity s ) +{ + switch ( s ) { + case Critical: return QString::fromLatin1( "critical" ); + case Grave: return QString::fromLatin1( "grave" ); + case Major: return QString::fromLatin1( "major" ); + case Crash: return QString::fromLatin1( "crash" ); + case Normal: return QString::fromLatin1( "normal" ); + case Minor: return QString::fromLatin1( "minor" ); + case Wishlist: return QString::fromLatin1( "wishlist" ); + default: + kdWarning() << "Bug::severityToString invalid severity " << s << endl; + return QString::fromLatin1( "<invalid>" ); + } +} + +Bug::Severity Bug::stringToSeverity( const QString &s, bool *ok ) +{ + if ( ok ) + *ok = true; + + if ( s == "critical" ) return Critical; + else if ( s == "grave" ) return Grave; + else if ( s == "major" ) return Major; + else if ( s == "crash" || s == "drkonqi" ) return Crash; + else if ( s == "normal" ) return Normal; + else if ( s == "minor" ) return Minor; + else if ( s == "wishlist" ) return Wishlist; + + kdWarning() << "Bug::stringToSeverity: invalid severity: " << s << endl; + if ( ok ) + *ok = false; + return SeverityUndefined; +} + +QValueList<Bug::Severity> Bug::severities() +{ + QValueList<Severity> s; + s << Critical << Grave << Major << Crash << Normal << Minor << Wishlist; + return s; +} + +QString Bug::statusLabel( Bug::Status s ) +{ + switch ( s ) + { + case Unconfirmed: return i18n("Unconfirmed"); + case New: return i18n("New"); + case Assigned: return i18n("Assigned"); + case Reopened: return i18n("Reopened"); + case Closed: return i18n("Closed"); + default: + case StatusUndefined: return i18n("Undefined"); + } +} + +QString Bug::statusToString( Bug::Status s ) +{ + switch ( s ) { + case Unconfirmed: return QString::fromLatin1( "unconfirmed" ); + case New: return QString::fromLatin1( "new" ); + case Assigned: return QString::fromLatin1( "assigned" ); + case Reopened: return QString::fromLatin1( "reopened" ); + case Closed: return QString::fromLatin1( "closed" ); + default: + kdWarning() << "Bug::statusToString invalid status " << s << endl; + return QString::fromLatin1( "<invalid>" ); + } +} + +Bug::Status Bug::stringToStatus( const QString &s, bool *ok ) +{ + if ( ok ) + *ok = true; + + if ( s == "unconfirmed" ) return Unconfirmed; + else if ( s == "new" ) return New; + else if ( s == "assigned" ) return Assigned; + else if ( s == "reopened" ) return Reopened; + else if ( s == "closed" ) return Closed; + + kdWarning() << "Bug::stringToStatus: invalid status: " << s << endl; + if ( ok ) + *ok = false; + return StatusUndefined; +} + +QString Bug::title() const +{ + if ( !m_impl ) + return QString::null; + + return m_impl->title; +} + +void Bug::setTitle( QString title) +{ + if ( m_impl ) + m_impl->title = title; +} + +uint Bug::age() const +{ + if ( !m_impl ) + return 0; + + return m_impl->age; +} + +void Bug::setAge( uint age ) +{ + if ( m_impl ) + m_impl->age = age; +} + +struct Person Bug::submitter() const +{ + if ( !m_impl ) + return Person( QString::null, QString::null ); + + return m_impl->submitter; +} + +QString Bug::number() const +{ + if ( !m_impl ) + return QString::null; + + return m_impl->number; +} + +Bug::Severity Bug::severity() const +{ + if ( !m_impl ) + return Normal; + + return m_impl->severity; +} + +void Bug::setSeverity( Bug::Severity severity ) +{ + if ( m_impl ) + m_impl->severity = severity; +} + +Bug::BugMergeList Bug::mergedWith() const +{ + if ( !m_impl ) + return Bug::BugMergeList(); + + return m_impl->mergedWith; +} + + +Bug::Status Bug::status() const +{ + if ( !m_impl ) + return StatusUndefined; + + return m_impl->status; +} + +QString Bug::severityAsString() const +{ + return severityToString( severity() ); +} + +Person Bug::developerTODO() const +{ + return (m_impl == NULL) ? Person( QString::null, QString::null ) : + m_impl->developerTODO; +} + +bool Bug::operator==( const Bug &rhs ) +{ + return m_impl == rhs.m_impl; +} + +bool Bug::operator<( const Bug &rhs ) const +{ + return m_impl < rhs.m_impl; +} + +/* vim: set ts=4 sw=4 et softtabstop=4: */ + diff --git a/kbugbuster/backend/bug.h b/kbugbuster/backend/bug.h new file mode 100644 index 00000000..9a5ae8b6 --- /dev/null +++ b/kbugbuster/backend/bug.h @@ -0,0 +1,92 @@ +#ifndef __bug_h__ +#define __bug_h__ + +#include "person.h" + +#include <qvaluelist.h> + +#include <ksharedptr.h> + +class BugImpl; + +class Bug +{ +public: + typedef QValueList<Bug> List; + typedef QValueList<int> BugMergeList; + + enum Severity { SeverityUndefined, Critical, Grave, Major, Crash, Normal, + Minor, Wishlist }; + enum Status { StatusUndefined, Unconfirmed, New, Assigned, Reopened, + Closed }; + + Bug(); + Bug( BugImpl *impl ); + Bug( const Bug &other ); + Bug &operator=( const Bug &rhs ); + ~Bug(); + + static QString severityLabel( Severity s ); + /** + Return string representation of severity. This function is symmetric to + stringToSeverity(). + */ + static QString severityToString( Severity s ); + /** + Return severity code of string representation. This function is symmetric + to severityToString(). + */ + static Severity stringToSeverity( const QString &, bool *ok = 0 ); + + static QValueList<Severity> severities(); + + uint age() const; + void setAge( uint days ); + + QString title() const; + void setTitle( QString title ); + Person submitter() const; + QString number() const; + Severity severity() const; + void setSeverity( Severity severity ); + QString severityAsString() const; + Person developerTODO() const; + + BugMergeList mergedWith() const; + + /** + * Status of a bug. Currently open or closed. + * TODO: Should we add a status 'deleted' here ? + */ + Status status() const; + void setStatus( Status newStatus ); + + static QString statusLabel( Status s ); + /** + Return string representation of status. This function is symmetric to + stringToStatus(). + */ + static QString statusToString( Status s ); + /** + Return status code of string representation. This function is symmetric + to statusToString(). + */ + static Status stringToStatus( const QString &, bool *ok = 0 ); + + bool operator==( const Bug &rhs ); + bool operator<( const Bug &rhs ) const; + + bool isNull() const { return m_impl == 0; } + + static Bug fromNumber( const QString &bugNumber ); + +private: + BugImpl *impl() const { return m_impl; } + + KSharedPtr<BugImpl> m_impl; +}; + +#endif + +/* vim: set sw=4 ts=4 et softtabstop=4: */ + diff --git a/kbugbuster/backend/bugcache.cpp b/kbugbuster/backend/bugcache.cpp new file mode 100644 index 00000000..0eb44c8c --- /dev/null +++ b/kbugbuster/backend/bugcache.cpp @@ -0,0 +1,289 @@ +// (C) 2001, Cornelius Schumacher + +#include <qstringlist.h> +#include <qfile.h> + +#include <ksimpleconfig.h> +#include <kstandarddirs.h> +#include <kurl.h> +#include <kdebug.h> + +#include "packageimpl.h" +#include "bugimpl.h" +#include "bugdetailsimpl.h" + +#include "bugcache.h" + +BugCache::BugCache( const QString &id ) +{ + mId = id; + + init(); +} + +BugCache::~BugCache() +{ + m_cachePackages->sync(); + m_cacheBugs->sync(); + + delete m_cachePackages; + delete m_cacheBugs; +} + +void BugCache::init() +{ + mCachePackagesFileName = locateLocal( "appdata", mId + "-packages.cache" ); + mCacheBugsFileName = locateLocal( "appdata", mId + "-bugs.cache" ); + + m_cachePackages = new KSimpleConfig( mCachePackagesFileName ); + m_cacheBugs = new KSimpleConfig( mCacheBugsFileName ); +} + +void BugCache::savePackageList( const Package::List &pkgs ) +{ + Package::List::ConstIterator it; + for (it = pkgs.begin(); it != pkgs.end(); ++it) { + m_cachePackages->setGroup((*it).name()); + m_cachePackages->writeEntry("description",(*it).description()); + m_cachePackages->writeEntry("numberOfBugs",(*it).numberOfBugs()); + m_cachePackages->writeEntry("components",(*it).components()); + writePerson(m_cachePackages,"Maintainer",(*it).maintainer()); + } +} + +Package::List BugCache::loadPackageList() +{ + Package::List pkgs; + + QStringList packages = m_cachePackages->groupList(); + QStringList::ConstIterator it; + for( it = packages.begin(); it != packages.end(); ++it ) { + if ((*it) == "<default>") continue; + if ((*it).contains("/")) continue; + + m_cachePackages->setGroup(*it); + + QString description = m_cachePackages->readEntry("description"); + int numberOfBugs = m_cachePackages->readNumEntry("numberOfBugs"); + Person maintainer = readPerson( m_cachePackages, "Maintainer"); + QStringList components = m_cachePackages->readListEntry("components"); + + pkgs.append( Package( new PackageImpl( (*it), description, numberOfBugs, + maintainer, components ) ) ); + } + + return pkgs; +} + +void BugCache::invalidatePackageList() +{ + // Completely wipe out packages.cache + QStringList packages = m_cachePackages->groupList(); + QStringList::ConstIterator it; + for( it = packages.begin(); it != packages.end(); ++it ) { + if ((*it) == "<default>") continue; + m_cachePackages->deleteGroup(*it, true); + } +} + +void BugCache::saveBugList( const Package &pkg, const QString &component, const Bug::List &bugs ) +{ + QStringList bugList; + + Bug::List::ConstIterator it; + for( it = bugs.begin(); it != bugs.end(); ++it ) { + QString number = (*it).number(); + bugList.append( number ); + m_cacheBugs->setGroup( number ); + m_cacheBugs->writeEntry( "Title", (*it).title() ); + m_cacheBugs->writeEntry( "Severity", Bug::severityToString((*it).severity()) ); + m_cacheBugs->writeEntry( "Status", Bug::statusToString((*it).status()) ); + m_cacheBugs->writeEntry( "MergedWith" , (*it).mergedWith() ); + m_cacheBugs->writeEntry( "Age", ( *it ).age() ); + writePerson( m_cacheBugs, "Submitter", (*it).submitter() ); + writePerson( m_cacheBugs, "TODO", (*it).developerTODO() ); + } + + if ( component.isEmpty() ) + m_cachePackages->setGroup( pkg.name() ); + else { + m_cachePackages->setGroup( pkg.name() + "/" + component ); + } + + m_cachePackages->writeEntry( "bugList", bugList ); +} + +Bug::List BugCache::loadBugList( const Package &pkg, const QString &component, bool disconnected ) +{ +// kdDebug() << "Loading bug list for " << pkg.name() << endl; + + Bug::List bugList; + + if ( component.isEmpty() ) + m_cachePackages->setGroup( pkg.name() ); + else + m_cachePackages->setGroup( pkg.name() + "/" + component ); + + QStringList bugs = m_cachePackages->readListEntry( "bugList" ); + +// kdDebug() << " Bugs: " << (bugs.join(",")) << endl; + + QStringList::ConstIterator it; + for( it = bugs.begin(); it != bugs.end(); ++it ) { + if ( m_cacheBugs->hasGroup(*it) ) + { + m_cacheBugs->setGroup(*it); + QString title = m_cacheBugs->readEntry("Title"); + if ( !title.isEmpty() ) // dunno how I ended up with an all empty bug in the cache + { + Person submitter = readPerson( m_cacheBugs, "Submitter" ); + Bug::Status status = Bug::stringToStatus( m_cacheBugs->readEntry("Status") ); + Bug::Severity severity = Bug::stringToSeverity( m_cacheBugs->readEntry("Severity") ); + Person developerTODO = readPerson( m_cacheBugs, "TODO" ); + Bug::BugMergeList mergedWith = m_cacheBugs->readIntListEntry( "MergedWith" ); + uint age = m_cacheBugs->readUnsignedNumEntry( "Age", 0xFFFFFFFF ); + bugList.append( Bug( new BugImpl( title, submitter, ( *it ), age, + severity, developerTODO, + status, mergedWith ) ) ); + } + } else { + // This bug is in the package cache's buglist but not in the bugs cache + // Probably a new bug, we need to fetch it - if we're not in disconnected mode + kdWarning() << "Bug " << *it << " not in bug cache" << endl; + if ( !disconnected ) + return Bug::List(); // returning an empty list will trigger a reload of the buglist + } + } + + return bugList; +} + +void BugCache::invalidateBugList( const Package& pkg, const QString &component ) +{ + kdDebug() << "BugCache::invalidateBugList " << pkg.name() + << " (" << component << ")" << endl; + + // Erase bug list for this package + if ( component.isEmpty() ) { + m_cachePackages->setGroup( pkg.name() ); + } else { + QString key = pkg.name() + "/" + component; + m_cachePackages->setGroup( key ); + m_cachePackages->setGroup( pkg.name() + "/" + component ); + } + + m_cachePackages->writeEntry("bugList",QString::null); +} + +void BugCache::saveBugDetails( const Bug &bug, const BugDetails &details ) +{ + m_cacheBugs->setGroup( bug.number() ); + + m_cacheBugs->writeEntry( "Version", details.version() ); + m_cacheBugs->writeEntry( "Source", details.source() ); + m_cacheBugs->writeEntry( "Compiler", details.compiler() ); + m_cacheBugs->writeEntry( "OS", details.os() ); + + QStringList senders; + QStringList texts; + QStringList dates; + + BugDetailsPart::List parts = details.parts(); + BugDetailsPart::List::ConstIterator it; + for ( it = parts.begin(); it != parts.end(); ++it ) { + senders.append( (*it).sender.fullName() ); + texts.append( (*it).text ); + dates.append( (*it).date.toString( Qt::ISODate ) ); + } + + m_cacheBugs->writeEntry( "Details", texts ); + m_cacheBugs->writeEntry( "Senders", senders ); + m_cacheBugs->writeEntry( "Dates", dates ); +} + +bool BugCache::hasBugDetails( const Bug& bug ) const +{ + if ( !m_cacheBugs->hasGroup( bug.number() ) ) + return false; + + m_cacheBugs->setGroup( bug.number() ); + return m_cacheBugs->hasKey( "Details" ); +} + +BugDetails BugCache::loadBugDetails( const Bug &bug ) +{ + if ( !m_cacheBugs->hasGroup( bug.number() ) ) { + return BugDetails(); + } + + m_cacheBugs->setGroup( bug.number() ); + + BugDetailsPart::List parts; + + QStringList texts = m_cacheBugs->readListEntry( "Details" ); + QStringList senders = m_cacheBugs->readListEntry( "Senders" ); + QStringList dates = m_cacheBugs->readListEntry( "Dates" ); + + QStringList::ConstIterator itTexts = texts.begin(); + QStringList::ConstIterator itSenders = senders.begin(); + QStringList::ConstIterator itDates = dates.begin(); + while( itTexts != texts.end() ) { + QDateTime date = QDateTime::fromString( *itDates, Qt::ISODate ); + parts.append( BugDetailsPart( Person(*itSenders), date, *itTexts ) ); + + ++itTexts; + ++itSenders; + ++itDates; + } + + if ( parts.count() == 0 ) { + return BugDetails(); + } + + QString version = m_cacheBugs->readEntry( "Version" ); + QString source = m_cacheBugs->readEntry( "Source" ); + QString compiler = m_cacheBugs->readEntry( "Compiler" ); + QString os = m_cacheBugs->readEntry( "OS" ); + + return BugDetails( new BugDetailsImpl( version, source, compiler, os, + parts ) ); +} + +void BugCache::invalidateBugDetails( const Bug& bug ) +{ + m_cacheBugs->deleteGroup( bug.number(), true ); +} + +void BugCache::clear() +{ + delete m_cachePackages; + delete m_cacheBugs; + + QFile f1( mCachePackagesFileName ); + f1.remove(); + + QFile f2( mCacheBugsFileName ); + f2.remove(); + + init(); +} + +void BugCache::writePerson( KSimpleConfig *file, const QString &key, + const Person &p ) +{ + QStringList values; + values.append(p.name); + values.append(p.email); + file->writeEntry( key, values ); +} + +struct Person BugCache::readPerson( KSimpleConfig *file, const QString &key ) +{ + struct Person p; + QStringList values = file->readListEntry(key); + if ( values.count() > 0 ) + p.name = values[0]; + if ( values.count() > 1 ) + p.email = values[1]; + return p; +} diff --git a/kbugbuster/backend/bugcache.h b/kbugbuster/backend/bugcache.h new file mode 100644 index 00000000..af1aed11 --- /dev/null +++ b/kbugbuster/backend/bugcache.h @@ -0,0 +1,47 @@ +#ifndef BUGCACHE_H +#define BUGCACHE_H + +class KSimpleConfig; + +#include "package.h" +#include "bug.h" +#include "bugdetails.h" + +class BugCache +{ + public: + BugCache( const QString &id ); + ~BugCache(); + + void savePackageList( const Package::List &pkgs ); + Package::List loadPackageList(); + void invalidatePackageList(); + + void saveBugList( const Package &pkg, const QString &component, const Bug::List & ); + Bug::List loadBugList( const Package &pkg, const QString &component, bool disconnected ); + void invalidateBugList( const Package &pkg, const QString &component ); + + void saveBugDetails( const Bug &bug, const BugDetails & ); + BugDetails loadBugDetails( const Bug &bug ); + void invalidateBugDetails( const Bug &bug ); + bool hasBugDetails( const Bug& bug ) const; + + void clear(); + + private: + void init(); + + void writePerson( KSimpleConfig *file, const QString &key, + const Person &p ); + struct Person readPerson (KSimpleConfig *file, const QString &key ); + + QString mId; + + KSimpleConfig *m_cachePackages; + KSimpleConfig *m_cacheBugs; + + QString mCachePackagesFileName; + QString mCacheBugsFileName; +}; + +#endif diff --git a/kbugbuster/backend/bugcommand.cpp b/kbugbuster/backend/bugcommand.cpp new file mode 100644 index 00000000..332c937d --- /dev/null +++ b/kbugbuster/backend/bugcommand.cpp @@ -0,0 +1,317 @@ +#include <kdebug.h> +#include <kconfig.h> +#include <klocale.h> + +#include "bugcommand.h" + +QString BugCommand::name() +{ + return i18n("Unknown"); +} + +QString BugCommand::details() +{ + return QString::null; +} + +BugCommand *BugCommand::load( KConfig *config, const QString &type ) +{ + QString bugNumber = config->group(); + // ### this sucks. we better let Bug implement proper persistance, + // because this way of instantiating a bug object doesn't bring back + // properties like title, package, etc. (Simon) + Bug bug = Bug::fromNumber( bugNumber ); + Package pkg = Package(); // hack + + if ( type == "Close" ) { + return new BugCommandClose( bug, config->readEntry( type ), pkg ); + } else if ( type == "Reopen" ) { + return new BugCommandReopen( bug, pkg ); + } else if ( type == "Merge" ) { + return new BugCommandMerge( config->readListEntry( type ), pkg ); + } else if ( type == "Unmerge" ) { + return new BugCommandUnmerge( bug, pkg ); + } else if ( type == "Reassign" ) { + return new BugCommandReassign( bug, config->readEntry( type ), pkg ); + } else if ( type == "Retitle" ) { + return new BugCommandRetitle( bug, config->readEntry( type ), pkg ); + } else if ( type == "Severity" ) { + return new BugCommandSeverity( bug, config->readEntry( type ), pkg ); + } else if ( type == "Reply" ) { + return new BugCommandReply( bug, config->readEntry( type ), config->readNumEntry("Recipient",Normal) ); + } else if ( type == "ReplyPrivate" ) { + QStringList args = config->readListEntry( type ); + if ( args.count() != 2 ) return 0; + return new BugCommandReplyPrivate( bug, *(args.at(0)), *(args.at(1)) ); + } else { + kdDebug() << "Warning! Unknown bug command '" << type << "'" << endl; + return 0; + } +} + +///////////////////// Close ///////////////////// + +QString BugCommandClose::controlString() const +{ + if (m_message.isEmpty()) { + return "close " + m_bug.number(); + } else { + return QString::null; + } +} + +QString BugCommandClose::mailAddress() const +{ + kdDebug() << "BugCommandClose::mailAddress(): number: " << m_bug.number() << endl; + + if (m_message.isEmpty()) { + return QString::null; + } else { + return m_bug.number() + "-done@bugs.kde.org"; + } +} + +QString BugCommandClose::mailText() const +{ + if (m_message.isEmpty()) { + return QString::null; + } else { + return m_message; + } +} + +QString BugCommandClose::name() +{ + return i18n("Close"); +} + +QString BugCommandClose::details() const +{ + return m_message; +} + +void BugCommandClose::save( KConfig *config ) +{ + config->writeEntry( "Close",m_message ); +} + +///////////////////// Close Silently ///////////////////// + +QString BugCommandCloseSilently::controlString() const +{ + return "done " + m_bug.number(); +} + +QString BugCommandCloseSilently::name() +{ + return i18n("Close Silently"); +} + +void BugCommandCloseSilently::save( KConfig *config ) +{ + config->writeEntry( "CloseSilently", true ); +} + +///////////////////// Reopen ///////////////////// + +QString BugCommandReopen::controlString() const +{ + return "reopen " + m_bug.number(); +} + +QString BugCommandReopen::name() +{ + return i18n("Reopen"); +} + +void BugCommandReopen::save( KConfig *config ) +{ + config->writeEntry( "Reopen", true ); +} + +///////////////////// Retitle ///////////////////// + +QString BugCommandRetitle::controlString() const +{ + return "retitle " + m_bug.number() + " " + m_title; +} + +QString BugCommandRetitle::name() +{ + return i18n("Retitle"); +} + +QString BugCommandRetitle::details() const +{ + return m_title; +} + +void BugCommandRetitle::save( KConfig *config ) +{ + config->writeEntry( "Retitle", m_title ); +} + +///////////////////// Merge ///////////////////// + +QString BugCommandMerge::controlString() const +{ + return "merge " + m_bugNumbers.join(" "); +} + +QString BugCommandMerge::name() +{ + return i18n("Merge"); +} + +QString BugCommandMerge::details() const +{ + return m_bugNumbers.join(", "); +} + +void BugCommandMerge::save( KConfig *config ) +{ + config->writeEntry( "Merge", m_bugNumbers ); +} + +///////////////////// Unmerge ///////////////////// + +QString BugCommandUnmerge::controlString() const +{ + return "unmerge " + m_bug.number(); +} + +QString BugCommandUnmerge::name() +{ + return i18n("Unmerge"); +} + +void BugCommandUnmerge::save( KConfig *config ) +{ + config->writeEntry( "Unmerge", true ); +} + +///////////////////// Reply ///////////////////// + +QString BugCommandReply::mailAddress() const +{ + return m_bug.number() + "@bugs.kde.org"; +#if 0 + switch ( m_recipient ) { + case Normal: + return m_bug.number() + "@bugs.kde.org"; + case Maintonly: + return m_bug.number() + "-maintonly@bugs.kde.org"; + case Quiet: + return m_bug.number() + "-quiet@bugs.kde.org"; + } + return QString::null; +#endif +} + +QString BugCommandReply::mailText() const +{ + return m_message; +} + +QString BugCommandReply::name() +{ + return i18n("Reply"); +#if 0 + switch ( m_recipient ) { + case Normal: + return i18n("Reply"); + case Maintonly: + return i18n("Reply (Maintonly)"); + case Quiet: + return i18n("Reply (Quiet)"); + } + return QString::null; +#endif +} + +QString BugCommandReply::details() const +{ + return m_message; +} + +void BugCommandReply::save( KConfig *config ) +{ + config->writeEntry( "Reply", m_message ); +#if 0 + config->writeEntry( "Recipient", m_recipient ); +#endif +} + +///////////////////// Reply Private ///////////////////// + +QString BugCommandReplyPrivate::mailAddress() const +{ + return m_address; +} + +QString BugCommandReplyPrivate::mailText() const +{ + return m_message; +} + +QString BugCommandReplyPrivate::name() +{ + return i18n("Private Reply"); +} + +QString BugCommandReplyPrivate::details() const +{ + return m_message; +} + +void BugCommandReplyPrivate::save( KConfig *config ) +{ + QStringList args; + args << m_address; + args << m_message; + config->writeEntry( "ReplyPrivate", args ); +} + +///////////////////// Severity ///////////////////// + +QString BugCommandSeverity::controlString() const +{ + return "severity " + m_bug.number() + " " + m_severity.lower(); +} + +QString BugCommandSeverity::name() +{ + return i18n("Severity"); +} + +QString BugCommandSeverity::details() const +{ + return m_severity; +} + +void BugCommandSeverity::save( KConfig *config ) +{ + config->writeEntry( "Severity", m_severity ); +} + +///////////////////// Reassign ///////////////////// + +QString BugCommandReassign::controlString() const +{ + return "reassign " + m_bug.number() + " " + m_package; +} + +QString BugCommandReassign::name() +{ + return i18n("Reassign"); +} + +QString BugCommandReassign::details() const +{ + return m_package; +} + +void BugCommandReassign::save( KConfig *config ) +{ + config->writeEntry( "Reassign", m_package ); +} diff --git a/kbugbuster/backend/bugcommand.h b/kbugbuster/backend/bugcommand.h new file mode 100644 index 00000000..96b9c85c --- /dev/null +++ b/kbugbuster/backend/bugcommand.h @@ -0,0 +1,217 @@ +#ifndef BUGCOMMAND_H +#define BUGCOMMAND_H + +#include <qstring.h> +#include <qstringlist.h> + +#include "bug.h" +#include "package.h" + +class KConfig; + +class BugCommand { + public: + enum Mode { Normal, Maintonly, Quiet }; + enum State { None, Queued, Sent }; + + BugCommand( const Bug &bug ) : m_bug( bug ) {} + BugCommand( const Bug &bug, const Package &pkg ) : m_bug( bug ), m_package( pkg ) {} + virtual ~BugCommand() {} + + virtual QString controlString() const { return QString::null; } + + virtual QString mailAddress() const { return QString::null; } + virtual QString mailText() const { return QString::null; } + + Bug bug() const { return m_bug; } + Package package() const { return m_package; } + + virtual QString name(); + virtual QString details(); + + virtual QString type() const { return QString::null; } + + virtual void save( KConfig * ) = 0; + static BugCommand *load( KConfig *, const QString &type ); + + protected: + Bug m_bug; + Package m_package; +}; + +class BugCommandClose : public BugCommand { + public: + BugCommandClose( const Bug &bug, const QString &message, const Package &pkg ) : + BugCommand( bug, pkg ), m_message( message ) {} + + QString controlString() const; + QString mailAddress() const; + QString mailText() const; + + QString name(); + QString details() const; + + QString type() const { return QString::fromLatin1("Close"); } + + void save( KConfig * ); + + private: + QString m_message; +}; + +class BugCommandCloseSilently : public BugCommand { + public: + BugCommandCloseSilently( const Bug &bug, const Package &pkg ) : + BugCommand( bug, pkg ) {} + + QString controlString() const; + + QString name(); + + QString type() const { return QString::fromLatin1("CloseSilently"); } + + void save( KConfig * ); +}; + +class BugCommandReopen : public BugCommand { + public: + BugCommandReopen( const Bug &bug, const Package &pkg ) : + BugCommand( bug, pkg ) {} + + QString controlString() const; + + QString name(); + + QString type() const { return QString::fromLatin1("Reopen"); } + + void save( KConfig * ); +}; + +class BugCommandRetitle : public BugCommand { + public: + BugCommandRetitle( const Bug &bug, const QString &title, const Package &pkg ) : + BugCommand( bug, pkg ), m_title( title ) {} + + QString controlString() const; + + QString name(); + QString details() const; + + QString type() const { return QString::fromLatin1("Retitle"); } + + void save( KConfig * ); + + private: + QString m_title; +}; + +class BugCommandMerge : public BugCommand { + public: + BugCommandMerge( const QStringList &bugNumbers, const Package &pkg ) : + BugCommand( Bug(), pkg ), m_bugNumbers( bugNumbers ) {} + + QString controlString() const; + + QString name(); + QString details() const; + + QString type() const { return QString::fromLatin1("Merge"); } + + void save( KConfig * ); + + private: + QStringList m_bugNumbers; +}; + +class BugCommandUnmerge : public BugCommand { + public: + BugCommandUnmerge( const Bug &bug, const Package &pkg ) : + BugCommand( bug, pkg ) {} + + QString name(); + + QString type() const { return QString::fromLatin1("Unmerge"); } + + void save( KConfig * ); + + QString controlString() const; +}; + +class BugCommandReply : public BugCommand { + public: + BugCommandReply( const Bug &bug, const QString &message, const int &recipient) : + BugCommand( bug ), m_message( message ), m_recipient( recipient ) {} + + QString mailAddress() const; + QString mailText() const; + + QString name(); + QString details() const; + + QString type() const { return QString::fromLatin1("Reply"); } + + void save( KConfig * ); + + private: + QString m_message; + int m_recipient; +}; + +class BugCommandReplyPrivate : public BugCommand { + public: + BugCommandReplyPrivate( const Bug &bug, const QString &address, + const QString &message ) : + BugCommand( bug ), m_address( address ), m_message( message ) {} + + QString mailAddress() const; + QString mailText() const; + + QString name(); + QString details() const; + + QString type() const { return QString::fromLatin1("ReplyPrivate"); } + + void save( KConfig * ); + + private: + QString m_address; + QString m_message; +}; + +class BugCommandSeverity : public BugCommand { + public: + BugCommandSeverity( const Bug &bug, const QString &severity, const Package &pkg ) : + BugCommand( bug, pkg ), m_severity( severity ) {} + + QString name(); + QString details() const; + + QString type() const { return QString::fromLatin1("Severity"); } + + QString controlString() const; + + void save( KConfig * ); + + private: + QString m_severity; +}; + +class BugCommandReassign : public BugCommand { + public: + BugCommandReassign( const Bug &bug, const QString &package, const Package &pkg ) : + BugCommand( bug, pkg ), m_package( package ) {} + + QString name(); + QString details() const; + + QString type() const { return QString::fromLatin1("Reassign"); } + + QString controlString() const; + + void save( KConfig * ); + + private: + QString m_package; +}; + +#endif diff --git a/kbugbuster/backend/bugdetails.cpp b/kbugbuster/backend/bugdetails.cpp new file mode 100644 index 00000000..ec512d79 --- /dev/null +++ b/kbugbuster/backend/bugdetails.cpp @@ -0,0 +1,268 @@ + +#include "bugdetails.h" + +#include "bugdetailsimpl.h" +#include <qstringlist.h> +#include <kdebug.h> +#include <kmdcodec.h> +#include <kmessagebox.h> +#include <qregexp.h> + +BugDetails::BugDetails() +{ +} + +BugDetails::BugDetails( BugDetailsImpl *impl ) : + m_impl( impl ) +{ +} + +BugDetails::BugDetails( const BugDetails &other ) +{ + (*this) = other; +} + +BugDetails &BugDetails::operator=( const BugDetails &rhs ) +{ + m_impl = rhs.m_impl; + return *this; +} + +BugDetails::~BugDetails() +{ +} + +QString BugDetails::version() const +{ + if ( !m_impl ) + return QString::null; + + return m_impl->version; +} + +QString BugDetails::source() const +{ + if ( !m_impl ) + return QString::null; + + return m_impl->source; +} + +QString BugDetails::compiler() const +{ + if ( !m_impl ) + return QString::null; + + return m_impl->compiler; +} + +QString BugDetails::os() const +{ + if ( !m_impl ) + return QString::null; + + return m_impl->os; +} + +QDateTime BugDetails::submissionDate() const +{ + if ( !m_impl ) return QDateTime(); + + if ( m_impl->parts.count() > 0 ) { + return m_impl->parts.last().date; + } + return QDateTime(); +} + +int BugDetails::age() const +{ + if ( !m_impl ) + return 0; + + return submissionDate().daysTo( QDateTime::currentDateTime() ); +} + +BugDetailsPart::List BugDetails::parts() const +{ + if ( !m_impl ) + return BugDetailsPart::List(); + + return m_impl->parts; +} + +QValueList<BugDetailsImpl::AttachmentDetails> BugDetails::attachmentDetails() const +{ + if ( m_impl ) + return m_impl->attachments; + else + return QValueList<BugDetailsImpl::AttachmentDetails>(); +} + +void BugDetails::addAttachmentDetails( const QValueList<BugDetailsImpl::AttachmentDetails>& attch ) +{ + if ( m_impl ) + m_impl->attachments = attch; +} + +QValueList<BugDetails::Attachment> BugDetails::extractAttachments() const +{ + QValueList<BugDetails::Attachment> lst; + if ( !m_impl ) + return lst; + BugDetailsPart::List parts = m_impl->parts; + for ( BugDetailsPart::List::ConstIterator it = parts.begin(); it != parts.end(); ++it ) { + lst += extractAttachments( (*it).text ); + } + return lst; +} + +//#define DEBUG_EXTRACT + +QValueList<BugDetails::Attachment> BugDetails::extractAttachments( const QString& text ) +{ + QValueList<BugDetails::Attachment> lst; + QStringList lines = QStringList::split( '\n', text ); +#ifdef DEBUG_EXTRACT + kdDebug() << k_funcinfo << lines.count() << " lines." << endl; +#endif + QString boundary; + for ( QStringList::Iterator it = lines.begin() ; it != lines.end() ; ++it ) + { +#ifdef DEBUG_EXTRACT + kdDebug() << "Line: " << *it << endl; +#endif + if ( (*it).startsWith( " Content-Type" ) ) // ## leading space comes from khtml + { +#ifdef DEBUG_EXTRACT + //kdDebug() << "BugDetails::extractAttachments going back, looking for empty or boundary=" << boundary << endl; +#endif + // Rewind until last empty line + QStringList::Iterator rit = it; + for ( ; rit != lines.begin() ; --rit ) { + QString line = *rit; + if ( line.endsWith( "<br />" ) ) + line = line.left( line.length() - 6 ); + while ( !line.isEmpty() && line[0] == ' ' ) + line.remove( 0, 1 ); +#ifdef DEBUG_EXTRACT + kdDebug() << "Back: '" << line << "'" << endl; +#endif + if ( line.isEmpty() ) { + ++rit; // boundary is next line + boundary = *rit; + if ( boundary.endsWith( "<br />" ) ) + boundary = boundary.left( boundary.length() - 6 ); + if ( boundary[0] == ' ' ) + boundary.remove( 0, 1 ); + kdDebug() << "BugDetails::extractAttachments boundary=" << boundary << endl; + break; + } + if ( line == boundary ) + break; + } + // Forward until next empty line (end of headers) - and parse filename + QString filename; + QString encoding; + rit = it; + for ( ; rit != lines.end() ; ++rit ) { + QString header = *rit; + if ( header.endsWith( "<br />" ) ) + header = header.left( header.length() - 6 ); + if ( header[0] == ' ' ) + header.remove( 0, 1 ); +#ifdef DEBUG_EXTRACT + kdDebug() << "Header: '" << header << "'" << endl; +#endif + if ( header.isEmpty() ) + break; + if ( header.startsWith( "Content-Disposition:" ) ) + { +#ifdef DEBUG_EXTRACT + kdDebug() << "BugDetails::extractAttachments found header " << *rit << endl; +#endif + // Taken from libkdenetwork/kmime_headers.cpp + int pos=header.find("filename=", 0, false); + QString fn; + if(pos>-1) { + pos+=9; + fn=header.mid(pos, header.length()-pos); + if ( fn.startsWith( "\"" ) && fn.endsWith( "\"" ) ) + fn = fn.mid( 1, fn.length() - 2 ); + //filename=decodeRFC2047String(fn, &e_ncCS, defaultCS(), forceCS()); + filename = fn; // hack + kdDebug() << "BugDetails::extractAttachments filename=" << filename << endl; + } + + } + else if ( header.startsWith( "Content-Transfer-Encoding:" ) ) + { + encoding = header.mid( 26 ).lower(); + while ( encoding[0] == ' ' ) + encoding.remove( 0, 1 ); + kdDebug() << "BugDetails::extractAttachments encoding=" << encoding << endl; + } + } //for + if ( rit == lines.end() ) + break; + + it = rit; + if ( rit != lines.end() && !filename.isEmpty() ) + { + ++it; + if ( it == lines.end() ) + break; + // Read encoded contents + QString contents; + for ( ; it != lines.end() ; ++it ) + { + QString line = *it; + if ( line.endsWith( "</tt>" ) ) + line = line.left( line.length() - 5 ); + if ( line.endsWith( "<br />" ) ) // necessary for the boundary check + line = line.left( line.length() - 6 ); + while ( !line.isEmpty() && line[0] == ' ' ) + line.remove( 0, 1 ); + if ( line.isEmpty() || line == boundary ) // end of attachment + break; + if ( line == boundary+"--" ) // end of last attachment + { + boundary = QString::null; + break; + } + contents += line; // no newline, because of linebreaking between <br and /> + } + contents = contents.replace( QRegExp("<br */>"), QString::null ); +#ifdef DEBUG_EXTRACT + kdDebug() << "BugDetails::extractAttachments contents=***\n" << contents << "\n***" << endl; +#endif + kdDebug() << "BugDetails::extractAttachments creating attachment " << filename << endl; + Attachment a; + if ( encoding == "base64" ) + KCodecs::base64Decode( contents.local8Bit(), a.contents /*out*/ ); + else + //KCodecs::uudecode( contents.local8Bit(), a.contents /*out*/ ); + KMessageBox::information( 0, i18n("Attachment %1 could not be decoded.\nEncoding: %2").arg(filename).arg(encoding) ); +#ifdef DEBUG_EXTRACT + kdDebug() << "Result: ***\n" << QCString( a.contents.data(), a.contents.size()+1 ) << "\n*+*" << endl; +#endif + a.filename = filename; + lst.append(a); + if ( it == lines.end() ) + break; + } + } + } +#ifdef DEBUG_EXTRACT + kdDebug() << "BugDetails::extractAttachments returning " << lst.count() << " attachments for this part." << endl; +#endif + return lst; +} + +bool BugDetails::operator==( const BugDetails &rhs ) +{ + return m_impl == rhs.m_impl; +} + +/** + * vim:ts=4:sw=4:et + */ diff --git a/kbugbuster/backend/bugdetails.h b/kbugbuster/backend/bugdetails.h new file mode 100644 index 00000000..08d20777 --- /dev/null +++ b/kbugbuster/backend/bugdetails.h @@ -0,0 +1,55 @@ +#ifndef __bugdetails_h__ +#define __bugdetails_h__ + +#include "person.h" +#include "bugdetailspart.h" +#include "bugdetailsimpl.h" + +#include <qvaluelist.h> + +#include <ksharedptr.h> + +class BugDetailsImpl; + +class BugDetails +{ +public: + typedef QValueList<BugDetails> List; + struct Attachment { + QByteArray contents; + QString filename; + }; + + BugDetails(); + BugDetails( BugDetailsImpl *impl ); + BugDetails( const BugDetails &other ); + BugDetails &operator=( const BugDetails &rhs ); + ~BugDetails(); + + QString version() const; + QString source() const; + QString compiler() const; + QString os() const; + BugDetailsPart::List parts() const; + void addAttachmentDetails( const QValueList<BugDetailsImpl::AttachmentDetails>& attch ); + QValueList<BugDetailsImpl::AttachmentDetails> attachmentDetails() const; + QValueList<BugDetails::Attachment> extractAttachments() const; + static QValueList<BugDetails::Attachment> extractAttachments( const QString& text ); + + QDateTime submissionDate() const; + int age() const; + + bool operator==( const BugDetails &rhs ); + + bool isNull() const { return m_impl == 0; } + +private: + BugDetailsImpl *impl() const { return m_impl; } + + KSharedPtr<BugDetailsImpl> m_impl; +}; + +#endif + +/* vim: set sw=4 ts=4 et softtabstop=4: */ + diff --git a/kbugbuster/backend/bugdetailsimpl.h b/kbugbuster/backend/bugdetailsimpl.h new file mode 100644 index 00000000..df3e5d6d --- /dev/null +++ b/kbugbuster/backend/bugdetailsimpl.h @@ -0,0 +1,40 @@ +#ifndef __bugdetails_impl_h__ +#define __bugdetails_impl_h__ + +#include <ksharedptr.h> + +#include "bugdetailspart.h" + +struct BugDetailsImpl : public KShared +{ +public: + BugDetailsImpl( const QString &_version, const QString &_source, + const QString &_compiler, const QString &_os, + const BugDetailsPart::List &_parts ) + : version( _version ), source( _source ), compiler( _compiler ), + os( _os ), parts( _parts ) {} + + struct AttachmentDetails { + AttachmentDetails() { } + AttachmentDetails( const QString& descr, const QString& dt, + const QString& idf ) : description( descr ), + date( dt ), + id( idf ) { } + QString description; + QString date; + QString id; + }; + + QString version; + QString source; + QString compiler; + QString os; + BugDetailsPart::List parts; + QValueList<BugDetailsImpl::AttachmentDetails> attachments; +}; + +#endif + +/* + * vim:sw=4:ts=4:et + */ diff --git a/kbugbuster/backend/bugdetailsjob.cpp b/kbugbuster/backend/bugdetailsjob.cpp new file mode 100644 index 00000000..83599c1d --- /dev/null +++ b/kbugbuster/backend/bugdetailsjob.cpp @@ -0,0 +1,50 @@ + +#include "bugdetailsjob.h" +#include "bug.h" +#include "bugdetails.h" +#include "bugdetailsimpl.h" +#include "packageimpl.h" +#include "bugserver.h" +#include "processor.h" + +#include <kdebug.h> +#include <assert.h> + +BugDetailsJob::BugDetailsJob( BugServer *server ) + : BugJob( server ) +{ +} + +BugDetailsJob::~BugDetailsJob() +{ +} + +void BugDetailsJob::start( const Bug &bug ) +{ + m_bug = bug; + + KURL bugUrl = server()->bugDetailsUrl( bug ); + + kdDebug() << "BugDetailsJob::start(): " << bugUrl.url() << endl; + BugJob::start( bugUrl ); +} + +void BugDetailsJob::process( const QByteArray &data ) +{ + BugDetails bugDetails; + + KBB::Error err = server()->processor()->parseBugDetails( data, bugDetails ); + + if ( err ) { + emit error( i18n("Bug %1: %2").arg( m_bug.number() ) + .arg( err.message() ) ); + } else { + emit bugDetailsAvailable( m_bug, bugDetails ); + } +} + +#include "bugdetailsjob.moc" + +/* + * vim:sw=4:ts=4:et + */ diff --git a/kbugbuster/backend/bugdetailsjob.h b/kbugbuster/backend/bugdetailsjob.h new file mode 100644 index 00000000..e07ad3a2 --- /dev/null +++ b/kbugbuster/backend/bugdetailsjob.h @@ -0,0 +1,32 @@ +#ifndef __bugdetailsjob_h__ +#define __bugdetailsjob_h__ + +#include "bugjob.h" +#include "bug.h" +#include "bugdetails.h" +#include "bugdetailspart.h" + +class BugDetailsJob : public BugJob +{ + Q_OBJECT + public: + BugDetailsJob( BugServer * ); + virtual ~BugDetailsJob(); + + void start( const Bug &bug ); + + signals: + void bugDetailsAvailable( const Bug &bug, const BugDetails &details ); + + protected: + virtual void process( const QByteArray &data ); + + private: + Bug m_bug; +}; + +#endif + +/* + * vim:ts=4:sw=4:et + */ diff --git a/kbugbuster/backend/bugdetailspart.h b/kbugbuster/backend/bugdetailspart.h new file mode 100644 index 00000000..483057c8 --- /dev/null +++ b/kbugbuster/backend/bugdetailspart.h @@ -0,0 +1,21 @@ +#ifndef BUGDETAILSPART_H +#define BUGDETAILSPART_H + +#include <qvaluelist.h> +#include <qdatetime.h> + +struct BugDetailsPart +{ + typedef QValueList<BugDetailsPart> List; + + BugDetailsPart () {} + BugDetailsPart( const Person &_sender, const QDateTime &_date, + const QString &_text ) + : sender( _sender ), date( _date ), text( _text ) {} + + Person sender; + QDateTime date; + QString text; +}; + +#endif diff --git a/kbugbuster/backend/bugimpl.h b/kbugbuster/backend/bugimpl.h new file mode 100644 index 00000000..630105a1 --- /dev/null +++ b/kbugbuster/backend/bugimpl.h @@ -0,0 +1,36 @@ +#ifndef __bug_impl_h__ +#define __bug_impl_h__ + +#include "person.h" +#include "bug.h" + +#include <kurl.h> +#include <ksharedptr.h> + +struct BugImpl : public KShared +{ +public: + BugImpl( const QString &_title, const Person &_submitter, QString _number, + uint _age, Bug::Severity _severity, Person _developerTODO, + Bug::Status _status, const Bug::BugMergeList& _mergedWith ) + : age( _age ), title( _title ), submitter( _submitter ), number( _number ), + severity( _severity ), developerTODO( _developerTODO ), + status( _status ), mergedWith( _mergedWith ) + { + } + + uint age; + QString title; + Person submitter; + QString number; + Bug::Severity severity; + Person developerTODO; + Bug::Status status; + + Bug::BugMergeList mergedWith; +}; + +#endif + +// vim: set sw=4 ts=4 sts=4 et: + diff --git a/kbugbuster/backend/bugjob.cpp b/kbugbuster/backend/bugjob.cpp new file mode 100644 index 00000000..6f32fb97 --- /dev/null +++ b/kbugbuster/backend/bugjob.cpp @@ -0,0 +1,97 @@ + +#include "bugjob.h" + +#include "kbbprefs.h" + +#include <kio/job.h> + +#include <string.h> +#include <klocale.h> +#include <kdebug.h> + +BugJob::BugJob( BugServer *server ) + : Job( false ), mServer( server ) +{ +} + +BugJob::~BugJob() +{ +} + +void BugJob::start( const KURL &url ) +{ + kdDebug() << "BugJob::start(): " << url.url() << endl; + + if ( KBBPrefs::instance()->mDebugMode ) { + BugSystem::saveQuery( url ); + } + + // ### obey post, if necessary + + KIO::Job *job = KIO::get( url, true /*always 'reload=true', we have our own cache*/, false ); + + connect( job, SIGNAL( result( KIO::Job * ) ), + this, SLOT( ioResult( KIO::Job * ) ) ); + connect( job, SIGNAL( data( KIO::Job *, const QByteArray & ) ), + this, SLOT( ioData( KIO::Job *, const QByteArray & ) ) ); + connect( job, SIGNAL( infoMessage( KIO::Job *, const QString & ) ), + this, SLOT( ioInfoMessage( KIO::Job *, const QString & ) ) ); + connect( job, SIGNAL( percent( KIO::Job *, unsigned long ) ), + this, SLOT( ioInfoPercent( KIO::Job *, unsigned long ) ) ); +} + +void BugJob::ioResult( KIO::Job *job ) +{ + m_error = job->error(); + m_errorText = job->errorText(); + + if ( job->error() ) + { + emit error( m_errorText ); + BugSystem::self()->unregisterJob(this); + this->kill(); + return; + } + + infoMessage( i18n( "Parsing..." ) ); + +#if 0 + kdDebug() << "--START:" << m_data << ":END--" << endl; +#endif + + if ( KBBPrefs::instance()->mDebugMode ) { + BugSystem::saveResponse( m_data ); + } + + process( m_data ); + infoMessage( i18n( "Ready." ) ); + + emit jobEnded( this ); + + delete this; +} + +void BugJob::ioData( KIO::Job *, const QByteArray &data ) +{ + unsigned int start = m_data.size(); + + m_data.resize( m_data.size() + data.size() ); + memcpy( m_data.data() + start, data.data(), data.size() ); +} + +void BugJob::ioInfoMessage( KIO::Job *, const QString &_text ) +{ + QString text = _text; + emit infoMessage( text ); +} + +void BugJob::ioInfoPercent( KIO::Job *, unsigned long percent ) +{ + emit infoPercent( percent ); +} + +#include "bugjob.moc" + +/* + * vim:sw=4:ts=4:et + */ diff --git a/kbugbuster/backend/bugjob.h b/kbugbuster/backend/bugjob.h new file mode 100644 index 00000000..2c439810 --- /dev/null +++ b/kbugbuster/backend/bugjob.h @@ -0,0 +1,45 @@ +#ifndef KBB_BUGJOB_H +#define KBB_BUGJOB_H + +#include <kio/jobclasses.h> + +#include "bugserver.h" + +class BugJob : public KIO::Job +{ + Q_OBJECT + public: + BugJob( BugServer * ); + virtual ~BugJob(); + + BugServer *server() const { return mServer; } + + signals: + void infoMessage( const QString &text ); + void infoPercent( unsigned long percent ); + void error( const QString &text ); + void jobEnded( BugJob * ); + + protected: + void start( const KURL &url /*, const KParts::URLArgs &args = KParts::URLArgs()*/ ); + + virtual void process( const QByteArray &data ) = 0; + + private slots: + void ioResult( KIO::Job *job ); + + void ioData( KIO::Job *job, const QByteArray &data ); + + void ioInfoMessage( KIO::Job *job, const QString &text ); + + void ioInfoPercent( KIO::Job *job, unsigned long percent ); + + private: + QByteArray m_data; + BugServer *mServer; +}; + +#endif +/* + * vim:sw=4:ts=4:et + */ diff --git a/kbugbuster/backend/buglistjob.cpp b/kbugbuster/backend/buglistjob.cpp new file mode 100644 index 00000000..e1dea2f4 --- /dev/null +++ b/kbugbuster/backend/buglistjob.cpp @@ -0,0 +1,75 @@ +/* + This file is part of KBugBuster. + + Copyright (c) 2002,2003 Cornelius Schumacher <schumacher@kde.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. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "buglistjob.h" +#include "bug.h" +#include "bugimpl.h" +#include "packageimpl.h" +#include "bugserver.h" +#include "domprocessor.h" +#include "htmlparser.h" + +#include <kdebug.h> + +#include <qbuffer.h> +#include <qregexp.h> + +BugListJob::BugListJob( BugServer *server ) + : BugJob( server ) +{ +} + +BugListJob::~BugListJob() +{ +} + +void BugListJob::start( const Package &pkg, const QString &component ) +{ + m_package = pkg; + m_component = component; + + BugJob::start( server()->bugListUrl( pkg, component ) ); +} + + +void BugListJob::process( const QByteArray &data ) +{ + Bug::List bugs; + + KBB::Error err = server()->processor()->parseBugList( data, bugs ); + + if ( err ) { + emit error( i18n("Package %1: %2").arg( m_package.name() ) + .arg( err.message() ) ); + } else { + emit bugListAvailable( m_package, m_component, bugs ); + } +} + + +#include "buglistjob.moc" + +/* + * vim:sw=4:ts=4:et + */ diff --git a/kbugbuster/backend/buglistjob.h b/kbugbuster/backend/buglistjob.h new file mode 100644 index 00000000..0260d3a0 --- /dev/null +++ b/kbugbuster/backend/buglistjob.h @@ -0,0 +1,58 @@ +/* + This file is part of KBugBuster. + + Copyright (c) 2002,2003 Cornelius Schumacher <schumacher@kde.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. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ +#ifndef BUGLISTJOB_H +#define BUGLISTJOB_H + +#include "bugjob.h" +#include "package.h" +#include "bug.h" + +#include <qdom.h> + +class BugListJob : public BugJob +{ + Q_OBJECT + public: + BugListJob( BugServer * ); + virtual ~BugListJob(); + + void start( const Package &pkg, const QString &component ); + + protected: + void process( const QByteArray &data ); + + signals: + void bugListAvailable( const Package &pkg, const QString &component, const Bug::List &bugs ); + + protected: + Package m_package; + QString m_component; +}; + + +#endif + +/* + * vim:ts=4:sw=4:et + */ diff --git a/kbugbuster/backend/bugmybugsjob.cpp b/kbugbuster/backend/bugmybugsjob.cpp new file mode 100644 index 00000000..92aa4448 --- /dev/null +++ b/kbugbuster/backend/bugmybugsjob.cpp @@ -0,0 +1,78 @@ +/* + This file is part of KBugBuster. + + Copyright (c) 2002-2003 Cornelius Schumacher <schumacher@kde.org> + Copyright (c) 2004 Martijn Klingens <klingens@kde.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. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "bugmybugsjob.h" +#include "bug.h" +#include "bugimpl.h" +#include "packageimpl.h" +#include "bugserver.h" +#include "domprocessor.h" +#include "htmlparser.h" + +#include <kdebug.h> + +#include <qbuffer.h> +#include <qregexp.h> + +BugMyBugsJob::BugMyBugsJob( BugServer *server ) +: BugJob( server ) +{ +} + +BugMyBugsJob::~BugMyBugsJob() +{ +} + +void BugMyBugsJob::start() +{ + KURL url = server()->serverConfig().baseUrl(); + url.setFileName( "buglist.cgi" ); + url.setQuery( "bug_status=NEW&bug_status=ASSIGNED&bug_status=UNCONFIRMED&bug_status=REOPENED" ); + url.addQueryItem( "email1", server()->serverConfig().user() ); + url.addQueryItem( "emailtype1", "exact" ); + url.addQueryItem( "emailassigned_to1", "1" ); + url.addQueryItem( "emailreporter1", "1" ); + url.addQueryItem( "format", "rdf" ); + BugJob::start( url ); +} + +void BugMyBugsJob::process( const QByteArray &data ) +{ + Bug::List bugs; + + Processor *processor = new RdfProcessor( server() ); + KBB::Error err = processor->parseBugList( data, bugs ); + delete processor; + + if ( err ) + emit error( i18n( "My Bugs: %2" ).arg( err.message() ) ); + else + emit bugListAvailable( i18n( "My Bugs" ), bugs ); +} + +#include "bugmybugsjob.moc" + +// vim: set sw=4 ts=4 et: + diff --git a/kbugbuster/backend/bugmybugsjob.h b/kbugbuster/backend/bugmybugsjob.h new file mode 100644 index 00000000..f51ee229 --- /dev/null +++ b/kbugbuster/backend/bugmybugsjob.h @@ -0,0 +1,52 @@ +/* + This file is part of KBugBuster. + + Copyright (c) 2002-2003 Cornelius Schumacher <schumacher@kde.org> + Copyright (c) 2004 Martijn Klingens <klingens@kde.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. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef BUGMYBUGSJOB_H +#define BUGMYBUGSJOB_H + +#include "bugjob.h" +#include "bug.h" + +class BugMyBugsJob : public BugJob +{ + Q_OBJECT + +public: + BugMyBugsJob( BugServer * ); + virtual ~BugMyBugsJob(); + + void start(); + +protected: + void process( const QByteArray &data ); + +signals: + void bugListAvailable( const QString &label, const Bug::List &bugs ); +}; + +#endif + +// vim: set ts=4 sw=4 et: + diff --git a/kbugbuster/backend/bugserver.cpp b/kbugbuster/backend/bugserver.cpp new file mode 100644 index 00000000..0e6a70e6 --- /dev/null +++ b/kbugbuster/backend/bugserver.cpp @@ -0,0 +1,412 @@ +/* + This file is part of KBugBuster. + Copyright (c) 2002,2003 Cornelius Schumacher <schumacher@kde.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. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "bugserver.h" +#include "kbbprefs.h" +#include "rdfprocessor.h" +#include "bugcache.h" +#include "bugcommand.h" +#include "mailsender.h" +#include "bugserverconfig.h" +#include "htmlparser.h" + +#include <kstandarddirs.h> +#include <ksimpleconfig.h> +#include <kdebug.h> + +BugServer::BugServer() +{ + init(); +} + +BugServer::BugServer( const BugServerConfig &cfg ) + : mServerConfig( cfg ) +{ + init(); +} + +void BugServer::init() +{ + mCache = new BugCache( identifier() ); + + QString commandsFile = locateLocal( "appdata", identifier() + "commands" ); + mCommandsFile = new KSimpleConfig( commandsFile ); + + QString bugzilla = mServerConfig.bugzillaVersion(); + + if ( bugzilla == "KDE" ) mProcessor = new DomProcessor( this ); + else if ( bugzilla == "2.10" ) mProcessor = new HtmlParser_2_10( this ); + else if ( bugzilla == "2.14.2" ) mProcessor = new HtmlParser_2_14_2( this ); + else if ( bugzilla == "2.17.1" ) mProcessor = new HtmlParser_2_17_1( this ); + else mProcessor = new HtmlParser( this ); + + loadCommands(); +} + +BugServer::~BugServer() +{ + saveCommands(); + + delete mProcessor; + delete mCommandsFile; + delete mCache; +} + +void BugServer::setServerConfig( const BugServerConfig &cfg ) +{ + mServerConfig = cfg; +} + +BugServerConfig &BugServer::serverConfig() +{ + return mServerConfig; +} + +QString BugServer::identifier() +{ + QString id = mServerConfig.baseUrl().host(); + return id; +} + +Processor *BugServer::processor() const +{ + return mProcessor; +} + +KURL BugServer::packageListUrl() +{ + KURL url = mServerConfig.baseUrl(); + + mProcessor->setPackageListQuery( url ); + + return url; +} + +KURL BugServer::bugListUrl( const Package &product, const QString &component ) +{ + KURL url = mServerConfig.baseUrl(); + + mProcessor->setBugListQuery( url, product, component ); + + return url; +} + +KURL BugServer::bugDetailsUrl( const Bug &bug ) +{ + KURL url = mServerConfig.baseUrl(); + + mProcessor->setBugDetailsQuery( url, bug ); + + return url; +} + +KURL BugServer::bugLink( const Bug &bug ) +{ + KURL url = mServerConfig.baseUrl(); + + url.setFileName( "show_bug.cgi" ); + url.setQuery( "id=" + bug.number() ); + + kdDebug() << "URL: " << url.url() << endl; + + return url; +} + +KURL BugServer::attachmentViewLink( const QString &id ) +{ + KURL url = mServerConfig.baseUrl(); + + url.setFileName( "attachment.cgi" ); + url.setQuery( "id=" + id + "&action=view" ); + + return url; +} + +KURL BugServer::attachmentEditLink( const QString &id ) +{ + KURL url = mServerConfig.baseUrl(); + + url.setFileName( "attachment.cgi" ); + url.setQuery( "id=" + id + "&action=edit" ); + + return url; +} + +Bug::Status BugServer::bugStatus( const QString &str ) +{ + if ( str == "UNCONFIRMED" ) { + return Bug::Unconfirmed; + } else if ( str == "NEW" ) { + return Bug::New; + } else if ( str == "ASSIGNED" ) { + return Bug::Assigned; + } else if ( str == "REOPENED" ) { + return Bug::Reopened; + } else if ( str == "RESOLVED" ) { + return Bug::Closed; + } else if ( str == "VERIFIED" ) { + return Bug::Closed; + } else if ( str == "CLOSED" ) { + return Bug::Closed; + } else { + return Bug::StatusUndefined; + } +} + +Bug::Severity BugServer::bugSeverity( const QString &str ) +{ + if ( str == "critical" ) { + return Bug::Critical; + } else if ( str == "grave" ) { + return Bug::Grave; + } else if ( str == "major" ) { + return Bug::Major; + } else if ( str == "crash" ) { + return Bug::Crash; + } else if ( str == "normal" ) { + return Bug::Normal; + } else if ( str == "minor" ) { + return Bug::Minor; + } else if ( str == "wishlist" ) { + return Bug::Wishlist; + } else { + return Bug::SeverityUndefined; + } +} + +void BugServer::readConfig( KConfig * /*config*/ ) +{ +} + +void BugServer::writeConfig( KConfig * /*config*/ ) +{ +} + +bool BugServer::queueCommand( BugCommand *cmd ) +{ + // mCommands[bug] is a QPtrList. Get or create, set to autodelete, then append command. + mCommands[cmd->bug().number()].setAutoDelete( true ); + QPtrListIterator<BugCommand> cmdIt( mCommands[cmd->bug().number()] ); + for ( ; cmdIt.current(); ++cmdIt ) + if ( cmdIt.current()->type() == cmd->type() ) + return false; + mCommands[cmd->bug().number()].append( cmd ); + return true; +} + +QPtrList<BugCommand> BugServer::queryCommands( const Bug &bug ) const +{ + CommandsMap::ConstIterator it = mCommands.find( bug.number() ); + if (it == mCommands.end()) return QPtrList<BugCommand>(); + else return *it; +} + +bool BugServer::hasCommandsFor( const Bug &bug ) const +{ + CommandsMap::ConstIterator it = mCommands.find( bug.number() ); + return it != mCommands.end(); +} + +void BugServer::sendCommands( MailSender *mailer, const QString &senderName, + const QString &senderEmail, bool sendBCC, + const QString &recipient ) +{ + // Disable mail commands for non-KDE servers + if ( mServerConfig.baseUrl() != KURL( "http://bugs.kde.org" ) ) return; + + QString controlText; + + // For each bug that has commands..... + CommandsMap::ConstIterator it; + for(it = mCommands.begin(); it != mCommands.end(); ++it ) { + Bug bug; + Package pkg; + // And for each command.... + QPtrListIterator<BugCommand> cmdIt( *it ); + for ( ; cmdIt.current() ; ++cmdIt ) { + BugCommand* cmd = cmdIt.current(); + bug = cmd->bug(); + if (!cmd->package().isNull()) + pkg = cmd->package(); + if (!cmd->controlString().isNull()) { + kdDebug() << "control@bugs.kde.org: " << cmd->controlString() << endl; + controlText += cmd->controlString() + "\n"; + } else { + kdDebug() << cmd->mailAddress() << ": " << cmd->mailText() << endl; + // ### hm, should probably re-use the existing mailer instance and + // implement message queueing for smtp + MailSender *directMailer = mailer->clone(); +#if 0 + connect( directMailer, SIGNAL( status( const QString & ) ), + this, SIGNAL( infoMessage( const QString & ) ) ); +#endif + if (!directMailer->send( senderName, senderEmail, cmd->mailAddress(), + cmd->bug().title().prepend( "Re: " ), + cmd->mailText(), sendBCC, recipient )) { + delete mailer; + return; + } + } + } + if (!bug.isNull()) { + mCommandsFile->deleteGroup( bug.number(), true ); // done, remove command + mCache->invalidateBugDetails( bug ); + if ( !pkg.isNull() ) { + mCache->invalidateBugList( pkg, QString::null ); // the status of the bug comes from the buglist... + + QStringList::ConstIterator it2; + for (it2 = pkg.components().begin();it2 != pkg.components().end();++it2) { + mCache->invalidateBugList( pkg, (*it2) ); // the status of the bug comes from the buglist... + } + } + } + } + + if (!controlText.isEmpty()) { + kdDebug() << "control@bugs.kde.org doesn't work anymore" << endl; +#if 0 + if ( !mailer->send( senderName, senderEmail, "control@bugs.kde.org", + i18n("Mail generated by KBugBuster"), controlText, + sendBCC, recipient )) + return; +#endif + } else { + delete mailer; + } + + mCommands.clear(); +} + +void BugServer::clearCommands( const QString &bug ) +{ + mCommands.remove( bug ); + mCommandsFile->deleteGroup( bug, true ); +} + +bool BugServer::commandsPending() const +{ + if ( mCommands.count() > 0 ) return true; + else return false; +} + +QStringList BugServer::listCommands() const +{ + QStringList result; + CommandsMap::ConstIterator it; + for(it = mCommands.begin(); it != mCommands.end(); ++it ) { + QPtrListIterator<BugCommand> cmdIt( *it ); + for ( ; cmdIt.current() ; ++cmdIt ) { + BugCommand* cmd = cmdIt.current(); + if (!cmd->controlString().isNull()) + result.append( i18n("Control command: %1").arg(cmd->controlString()) ); + else + result.append( i18n("Mail to %1").arg(cmd->mailAddress()) ); + } + } + return result; +} + +QStringList BugServer::bugsWithCommands() const +{ + QStringList bugs; + + CommandsMap::ConstIterator it; + for(it = mCommands.begin(); it != mCommands.end(); ++it ) { + bugs.append( it.key() ); + } + + return bugs; +} + +void BugServer::saveCommands() const +{ + CommandsMap::ConstIterator it; + for(it = mCommands.begin(); it != mCommands.end(); ++it ) { + mCommandsFile->setGroup( it.key() ); + QPtrListIterator<BugCommand> cmdIt( *it ); + for ( ; cmdIt.current() ; ++cmdIt ) { + BugCommand* cmd = cmdIt.current(); + cmd->save( mCommandsFile ); + } + } + + mCommandsFile->sync(); +} + +void BugServer::loadCommands() +{ + mCommands.clear(); + + QStringList bugs = mCommandsFile->groupList(); + QStringList::ConstIterator it; + for( it = bugs.begin(); it != bugs.end(); ++it ) { + mCommandsFile->setGroup( *it ); + QMap<QString, QString> entries = mCommandsFile->entryMap ( *it ); + QMap<QString, QString>::ConstIterator it; + for( it = entries.begin(); it != entries.end(); ++it ) { + QString type = it.key(); + BugCommand *cmd = BugCommand::load( mCommandsFile, type ); + if ( cmd ) { + mCommands[cmd->bug().number()].setAutoDelete(true); + mCommands[cmd->bug().number()].append(cmd); + } + } + } +} + +void BugServer::setPackages( const Package::List &packages ) +{ + mPackages = packages; +} + +const Package::List &BugServer::packages() const +{ + return mPackages; +} + +void BugServer::setBugs( const Package &pkg, const QString &component, + const Bug::List &bugs ) +{ + QPair<Package, QString> pkg_key = QPair<Package, QString>(pkg, component); + mBugs[ pkg_key ] = bugs; +} + +const Bug::List &BugServer::bugs( const Package &pkg, const QString &component ) +{ + QPair<Package, QString> pkg_key = QPair<Package, QString>(pkg, component); + return mBugs[ pkg_key ]; +} + +void BugServer::setBugDetails( const Bug &bug, const BugDetails &details ) +{ + mBugDetails[ bug ] = details; +} + +const BugDetails &BugServer::bugDetails( const Bug &bug ) +{ + return mBugDetails[ bug ]; +} + +/* + * vim:sw=4:ts=4:et + */ diff --git a/kbugbuster/backend/bugserver.h b/kbugbuster/backend/bugserver.h new file mode 100644 index 00000000..3b534fa0 --- /dev/null +++ b/kbugbuster/backend/bugserver.h @@ -0,0 +1,152 @@ +/* + This file is part of KBugBuster. + Copyright (c) 2002,2003 Cornelius Schumacher <schumacher@kde.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. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ +#ifndef BUGSERVER_H +#define BUGSERVER_H + +#include <qstring.h> + +#include <kurl.h> + +#include "bug.h" +#include "package.h" +#include "bugsystem.h" +#include "bugserverconfig.h" + +class Processor; +class BugCache; +class MailSender; +class BugServerConfig; + +class BugServer +{ + public: + BugServer(); + BugServer( const BugServerConfig & ); + ~BugServer(); + + /** + BugServer takes ownership of the BugServerConfig object. + */ + void setServerConfig( const BugServerConfig & ); + BugServerConfig &serverConfig(); + + QString identifier(); + + BugCache *cache() const { return mCache; } + + KURL packageListUrl(); + + KURL bugListUrl( const Package &, const QString &component ); + + KURL bugDetailsUrl( const Bug & ); + + KURL bugLink( const Bug & ); + KURL attachmentViewLink( const QString &id ); + KURL attachmentEditLink( const QString &id ); + + Bug::Status bugStatus( const QString & ); + + Bug::Severity bugSeverity( const QString & ); + + Processor *processor() const; + + void readConfig( KConfig * ); + + void writeConfig( KConfig * ); + + /** + Queue a new command. + */ + bool queueCommand( BugCommand * ); + /** + Return all the commands for a given bug. + */ + QPtrList<BugCommand> queryCommands( const Bug & ) const; + /** + Return true if we have a least one command for this bug. + */ + bool hasCommandsFor( const Bug &bug ) const; + /** + Send all commands (generate the mails). + */ + void sendCommands( MailSender *, const QString &senderName, + const QString &senderEmail, bool sendBCC, + const QString &recipient ); + /** + Forget all commands for a given bug. + */ + void clearCommands( const QString &bug ); + /** + Return true if any command has been created. + */ + bool commandsPending() const; + /** + List all pending commands. + */ + QStringList listCommands() const; + /** + Return numbers of all bugs having at least one command queued. + */ + QStringList bugsWithCommands() const; + + void saveCommands() const; + void loadCommands(); + + void setPackages( const Package::List & ); + const Package::List &packages() const; + + void setBugs( const Package &, const QString &component, + const Bug::List & ); + const Bug::List &bugs( const Package &, const QString &component ); + + void setBugDetails( const Bug &, const BugDetails & ); + const BugDetails &bugDetails( const Bug & ); + + private: + void init(); + + BugServerConfig mServerConfig; + + Processor *mProcessor; + + BugCache *mCache; + + Package::List mPackages; + // Map package -> list of bugs + typedef QMap< QPair<Package, QString>, Bug::List > BugListMap; + BugListMap mBugs; + // Map bug -> bug details (i.e. contents of the report) + typedef QMap< Bug, BugDetails > BugDetailsMap; + BugDetailsMap mBugDetails; + // Map bug-number -> list of commands + typedef QMap< QString, QPtrList<BugCommand> > CommandsMap; + CommandsMap mCommands; + + KSimpleConfig *mCommandsFile; +}; + +#endif + +/* + * vim:sw=4:ts=4:et + */ diff --git a/kbugbuster/backend/bugserverconfig.cpp b/kbugbuster/backend/bugserverconfig.cpp new file mode 100644 index 00000000..0669c0ab --- /dev/null +++ b/kbugbuster/backend/bugserverconfig.cpp @@ -0,0 +1,150 @@ +/* + This file is part of KBugBuster. + + Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.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. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "bugserverconfig.h" + +#include "kbbprefs.h" + +#include <kdebug.h> +#include <kconfig.h> + +BugServerConfig::BugServerConfig() +{ + mName = "KDE"; + mBaseUrl = "http://bugs.kde.org"; + mUser = "bugzilla@kde.org"; + mBugzillaVersion = "KDE"; +} + +BugServerConfig::BugServerConfig( const QString &name, const KURL &baseUrl ) + : mName( name ), mBaseUrl( baseUrl ), mBugzillaVersion( "KDE" ) +{ +} + +BugServerConfig::~BugServerConfig() +{ +} + +void BugServerConfig::setName( const QString &name ) +{ + mName = name; +} + +QString BugServerConfig::name() const +{ + return mName; +} + +void BugServerConfig::setBaseUrl( const KURL &baseUrl ) +{ + mBaseUrl = baseUrl; +} + +KURL BugServerConfig::baseUrl() const +{ + return mBaseUrl; +} + +void BugServerConfig::setUser( const QString &user ) +{ + mUser = user; +} + +QString BugServerConfig::user() const +{ + return mUser; +} + +void BugServerConfig::setPassword( const QString &password ) +{ + mPassword = password; +} + +QString BugServerConfig::password() const +{ + return mPassword; +} + +void BugServerConfig::setBugzillaVersion( const QString &s ) +{ + mBugzillaVersion = s; +} + +QString BugServerConfig::bugzillaVersion() const +{ + return mBugzillaVersion; +} + +QStringList BugServerConfig::bugzillaVersions() +{ + QStringList v; + + v << "2.10"; + v << "2.14.2"; + v << "2.16.2"; + v << "2.17.1"; + v << "KDE"; + v << "Bugworld"; + + return v; +} + +void BugServerConfig::readConfig( KConfig *cfg, const QString &name ) +{ + mName = name; + + cfg->setGroup( "BugServer " + name ); + + mBaseUrl = cfg->readEntry( "BaseUrl" ); + mUser = cfg->readEntry( "User" ); + mPassword = cfg->readEntry( "Password" ); + + mBugzillaVersion = cfg->readEntry( "BugzillaVersion", "KDE" ); + + mRecentPackages = cfg->readListEntry( "RecentPackages" ); + mCurrentPackage = cfg->readEntry( "CurrentPackage" ); + mCurrentComponent = cfg->readEntry( "CurrentComponent" ); + mCurrentBug = cfg->readEntry( "CurrentBug" ); +} + +void BugServerConfig::writeConfig( KConfig *cfg ) +{ + cfg->setGroup( "BugServer " + mName ); + + cfg->writeEntry( "BaseUrl", mBaseUrl.url() ); + cfg->writeEntry( "User", mUser ); + cfg->writeEntry( "Password", mPassword ); + + cfg->writeEntry( "BugzillaVersion", mBugzillaVersion ); + + cfg->writeEntry( "RecentPackages", mRecentPackages ); + cfg->writeEntry( "CurrentPackage", mCurrentPackage ); + cfg->writeEntry( "CurrentComponent", mCurrentComponent ); + cfg->writeEntry( "CurrentBug", mCurrentBug ); +} + + +/* + * vim:sw=4:ts=4:et + */ diff --git a/kbugbuster/backend/bugserverconfig.h b/kbugbuster/backend/bugserverconfig.h new file mode 100644 index 00000000..2c9be828 --- /dev/null +++ b/kbugbuster/backend/bugserverconfig.h @@ -0,0 +1,91 @@ +/* + This file is part of KBugBuster. + + Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.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. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ +#ifndef BUGSERVERCONFIG_H +#define BUGSERVERCONFIG_H + +#include <qstring.h> +#include <qstringlist.h> + +#include <kurl.h> + +class KConfig; + +class BugServerConfig +{ + public: + BugServerConfig(); + BugServerConfig( const QString &name, const KURL &baseUrl ); + ~BugServerConfig(); + + void setName( const QString &name ); + QString name() const; + + void setBaseUrl( const KURL &url ); + KURL baseUrl() const; + + void setUser( const QString &user ); + QString user() const; + + void setPassword( const QString &password ); + QString password() const; + + void readConfig( KConfig *, const QString &name ); + void writeConfig( KConfig * ); + + static QStringList bugzillaVersions(); + + void setBugzillaVersion( const QString & ); + QString bugzillaVersion() const; + + void setRecentPackages( const QStringList &v ) { mRecentPackages = v; } + QStringList recentPackages() const { return mRecentPackages; } + + void setCurrentPackage( const QString &v ) { mCurrentPackage = v; } + QString currentPackage() const { return mCurrentPackage; } + + void setCurrentComponent( const QString &v ) { mCurrentComponent = v; } + QString currentComponent() const { return mCurrentComponent; } + + void setCurrentBug( const QString &v ) { mCurrentBug = v; } + QString currentBug() const { return mCurrentBug; } + + private: + QString mName; + KURL mBaseUrl; + QString mUser; + QString mPassword; + + QString mBugzillaVersion; + + QStringList mRecentPackages; + QString mCurrentPackage; + QString mCurrentComponent; + QString mCurrentBug; +}; + +#endif + +/* + * vim:sw=4:ts=4:et + */ diff --git a/kbugbuster/backend/bugsystem.cpp b/kbugbuster/backend/bugsystem.cpp new file mode 100644 index 00000000..26432e08 --- /dev/null +++ b/kbugbuster/backend/bugsystem.cpp @@ -0,0 +1,436 @@ + +#include "bugsystem.h" +#include "packagelistjob.h" +#include "buglistjob.h" +#include "bugmybugsjob.h" +#include "bugdetailsjob.h" +#include "bugcommand.h" + +#include <kstaticdeleter.h> +#include <kdebug.h> +#include <klocale.h> +#include <kemailsettings.h> +#include <kstandarddirs.h> +#include <ksimpleconfig.h> +#include <kconfig.h> + +#include "packageimpl.h" +#include "bugimpl.h" +#include "bugdetailsimpl.h" +#include "mailsender.h" +#include "kbbprefs.h" +#include "bugserver.h" +#include "bugserverconfig.h" +#include "bugcache.h" + +KStaticDeleter<BugSystem> bssd; + +BugSystem *BugSystem::s_self = 0; + +QString BugSystem::mLastResponse; + +BugSystem *BugSystem::self() +{ + if ( !s_self ) + s_self = bssd.setObject( s_self, new BugSystem ); + + return s_self; +} + +BugSystem::BugSystem() + : m_disconnected( false ) +{ + mServer = 0; +} + +BugSystem::~BugSystem() +{ + QValueList<BugServer *>::ConstIterator it; + for( it = mServerList.begin(); it != mServerList.end(); ++it ) { + delete *it; + } +} + +BugCache *BugSystem::cache()const +{ + return mServer->cache(); +} + +void BugSystem::setDisconnected( bool disconnected ) +{ + m_disconnected = disconnected; +} + +bool BugSystem::disconnected() const +{ + return m_disconnected; +} + +void BugSystem::retrievePackageList() +{ + mServer->setPackages( mServer->cache()->loadPackageList() ); + + if( !mServer->packages().isEmpty() ) { + emit packageListAvailable( mServer->packages() ); + } else { + emit packageListCacheMiss(); + + if ( !m_disconnected ) + { + emit packageListLoading(); + + PackageListJob *job = new PackageListJob( mServer ); + connect( job, SIGNAL( packageListAvailable( const Package::List & ) ), + this, SIGNAL( packageListAvailable( const Package::List & ) ) ); + connect( job, SIGNAL( packageListAvailable( const Package::List & ) ), + this, SLOT( setPackageList( const Package::List & ) ) ); + connect( job, SIGNAL( error( const QString & ) ), + this, SIGNAL( loadingError( const QString & ) ) ); + connectJob( job ); + + registerJob( job ); + + job->start(); + } + } +} + +void BugSystem::retrieveBugList( const Package &pkg, const QString &component ) +{ + kdDebug() << "BugSystem::retrieveBugList(): " << pkg.name() << endl; + + if ( pkg.isNull() ) + return; + + mServer->setBugs( pkg, component, + mServer->cache()->loadBugList( pkg, component, + m_disconnected ) ); + + // Since the GUI stops showing the splash widget after this signal, + // we should not emit anything on a cache miss... + if( !mServer->bugs( pkg, component ).isEmpty() ) + emit bugListAvailable( pkg, component, mServer->bugs( pkg, component ) ); + else + { + emit bugListCacheMiss( pkg ); + + if ( !m_disconnected ) + { + kdDebug() << "BugSystem::retrieveBugList() starting job" << endl; + emit bugListLoading( pkg, component ); + + BugListJob *job = new BugListJob( mServer ); + connect( job, SIGNAL( bugListAvailable( const Package &, const QString &, const Bug::List & ) ), + this, SIGNAL( bugListAvailable( const Package &, const QString &, const Bug::List & ) ) ); + connect( job, SIGNAL( bugListAvailable( const Package &, const QString &, const Bug::List & ) ), + this, SLOT( setBugList( const Package &, const QString &, const Bug::List & ) ) ); + connect( job, SIGNAL( error( const QString & ) ), + this, SIGNAL( loadingError( const QString & ) ) ); + connectJob( job ); + + registerJob( job ); + + job->start( pkg, component ); + } + } +} + +void BugSystem::retrieveMyBugsList() +{ + kdDebug() << k_funcinfo << endl; + + if ( m_disconnected ) + { + // This function is not cached for now + emit bugListCacheMiss( i18n( "My Bugs" ) ); + } + else + { + kdDebug() << k_funcinfo << "Starting job" << endl; + + emit bugListLoading( i18n( "Retrieving My Bugs list..." ) ); + + BugMyBugsJob *job = new BugMyBugsJob( mServer ); + + connect( job, SIGNAL( bugListAvailable( const QString &, const Bug::List & ) ), + this, SIGNAL( bugListAvailable( const QString &, const Bug::List & ) ) ); + connect( job, SIGNAL( error( const QString & ) ), + this, SIGNAL( loadingError( const QString & ) ) ); + connectJob( job ); + + registerJob( job ); + + job->start(); + } +} + +void BugSystem::retrieveBugDetails( const Bug &bug ) +{ + if ( bug.isNull() ) + return; + + kdDebug() << "BugSystem::retrieveBugDetails(): " << bug.number() << endl; + + mServer->setBugDetails( bug, mServer->cache()->loadBugDetails( bug ) ); + + if ( !mServer->bugDetails( bug ).isNull() ) { +// kdDebug() << "Found in cache." << endl; + emit bugDetailsAvailable( bug, mServer->bugDetails( bug ) ); + } else { +// kdDebug() << "Not found in cache." << endl; + emit bugDetailsCacheMiss( bug ); + + if ( !m_disconnected ) { + emit bugDetailsLoading( bug ); + + BugDetailsJob *job = new BugDetailsJob( mServer ); + connect( job, SIGNAL( bugDetailsAvailable( const Bug &, const BugDetails & ) ), + this, SIGNAL( bugDetailsAvailable( const Bug &, const BugDetails & ) ) ); + connect( job, SIGNAL( bugDetailsAvailable( const Bug &, const BugDetails & ) ), + this, SLOT( setBugDetails( const Bug &, const BugDetails & ) ) ); + connect( job, SIGNAL( error( const QString & ) ), + this, SIGNAL( bugDetailsLoadingError() ) ); + connectJob( job ); + + registerJob( job ); + + job->start( bug ); + } + } +} + +void BugSystem::connectJob( BugJob *job ) +{ + connect( job, SIGNAL( infoMessage( const QString & ) ), + this, SIGNAL( infoMessage( const QString & ) ) ); + connect( job, SIGNAL( infoPercent( unsigned long ) ), + this, SIGNAL( infoPercent( unsigned long ) ) ); + connect( job, SIGNAL( jobEnded( BugJob * ) ), + SLOT( unregisterJob( BugJob * ) ) ); +} + +void BugSystem::setPackageList( const Package::List &pkgs ) +{ + mServer->setPackages( pkgs ); + + mServer->cache()->savePackageList( pkgs ); +} + +void BugSystem::setBugList( const Package &pkg, const QString &component, const Bug::List &bugs ) +{ + mServer->setBugs( pkg, component, bugs ); + mServer->cache()->saveBugList( pkg, component, bugs ); +} + +void BugSystem::setBugDetails( const Bug &bug, const BugDetails &details ) +{ + mServer->setBugDetails( bug , details ); + + mServer->cache()->saveBugDetails( bug, details ); +} + +Package::List BugSystem::packageList() const +{ + return mServer->packages(); +} + +Package BugSystem::package( const QString &pkgname ) const +{ + Package::List::ConstIterator it; + for( it = mServer->packages().begin(); it != mServer->packages().end(); ++it ) { + if( pkgname == (*it).name() ) return (*it); + } + return Package(); +} + +Bug BugSystem::bug( const Package &pkg, const QString &component, const QString &number ) const +{ + Bug::List bugs = mServer->bugs( pkg, component ); + + Bug::List::ConstIterator it; + for( it = bugs.begin(); it != bugs.end(); ++it ) { + if( number == (*it).number() ) return (*it); + } + return Bug(); +} + +void BugSystem::queueCommand( BugCommand *cmd ) +{ + if ( mServer->queueCommand( cmd ) ) emit commandQueued( cmd ); +} + +void BugSystem::clearCommands( const QString &bug ) +{ + mServer->clearCommands( bug ); + + emit commandCanceled( bug ); +} + +void BugSystem::clearCommands() +{ + QStringList bugs = mServer->bugsWithCommands(); + + QStringList::ConstIterator it; + for( it = bugs.begin(); it != bugs.end(); ++it ) { + clearCommands( *it ); + } +} + +void BugSystem::sendCommands() +{ + QString recipient = KBBPrefs::instance()->mOverrideRecipient; + bool sendBCC = KBBPrefs::instance()->mSendBCC; + + KEMailSettings emailSettings; + QString senderName = emailSettings.getSetting( KEMailSettings::RealName ); + QString senderEmail = emailSettings.getSetting( KEMailSettings::EmailAddress ); + QString smtpServer = emailSettings.getSetting( KEMailSettings::OutServer ); + + MailSender::MailClient client = (MailSender::MailClient)KBBPrefs::instance()->mMailClient; + + // ### connect to signals + MailSender *mailer = new MailSender( client, smtpServer ); + connect( mailer, SIGNAL( status( const QString & ) ), + SIGNAL( infoMessage( const QString & ) ) ); + + mServer->sendCommands( mailer, senderName, senderEmail, sendBCC, recipient ); +} + +void BugSystem::setServerList( const QValueList<BugServerConfig> &servers ) +{ + if ( servers.isEmpty() ) return; + + QString currentServer; + if ( mServer ) currentServer = mServer->serverConfig().name(); + else currentServer = KBBPrefs::instance()->mCurrentServer; + + killAllJobs(); + + QValueList<BugServer *>::ConstIterator serverIt; + for( serverIt = mServerList.begin(); serverIt != mServerList.end(); + ++serverIt ) { + delete *serverIt; + } + mServerList.clear(); + + QValueList<BugServerConfig>::ConstIterator cfgIt; + for( cfgIt = servers.begin(); cfgIt != servers.end(); ++cfgIt ) { + mServerList.append( new BugServer( *cfgIt ) ); + } + + setCurrentServer( currentServer ); +} + +QValueList<BugServer *> BugSystem::serverList() +{ + return mServerList; +} + +void BugSystem::setCurrentServer( const QString &name ) +{ + killAllJobs(); + + BugServer *server = findServer( name ); + if ( server ) { + mServer = server; + } else { + kdError() << "Server '" << name << "' not known." << endl; + if ( mServerList.isEmpty() ) { + kdError() << "Fatal error: server list empty." << endl; + } else { + mServer = mServerList.first(); + } + } + + if ( mServer ) { + KBBPrefs::instance()->mCurrentServer = mServer->serverConfig().name(); + } +} + +BugServer *BugSystem::findServer( const QString &name ) +{ + QValueList<BugServer *>::ConstIterator serverIt; + for( serverIt = mServerList.begin(); serverIt != mServerList.end(); + ++serverIt ) { + if ( (*serverIt)->serverConfig().name() == name ) return *serverIt; + } + return 0; +} + +void BugSystem::saveQuery( const KURL &url ) +{ + mLastResponse = "Query: " + url.url(); + mLastResponse += "\n\n"; +} + +void BugSystem::saveResponse( const QByteArray &response ) +{ + mLastResponse += response; +} + +QString BugSystem::lastResponse() +{ + return mLastResponse; +} + +void BugSystem::readConfig( KConfig *config ) +{ + config->setGroup("Servers"); + QStringList servers = config->readListEntry( "Servers" ); + + QValueList<BugServerConfig> serverList; + + if ( servers.isEmpty() ) { + serverList.append( BugServerConfig() ); + } else { + QStringList::ConstIterator it; + for( it = servers.begin(); it != servers.end(); ++it ) { + BugServerConfig cfg; + cfg.readConfig( config, *it ); + serverList.append( cfg ); + } + } + + setServerList( serverList ); +} + +void BugSystem::writeConfig( KConfig *config ) +{ + QValueList<BugServer *>::ConstIterator itServer; + QStringList servers; + QValueList<BugServer *> serverList = BugSystem::self()->serverList(); + for( itServer = serverList.begin(); itServer != serverList.end(); + ++itServer ) { + BugServerConfig serverConfig = (*itServer)->serverConfig(); + servers.append( serverConfig.name() ); + serverConfig.writeConfig( config ); + } + + config->setGroup("Servers"); + config->writeEntry( "Servers", servers ); +} + +void BugSystem::registerJob( BugJob *job ) +{ + mJobs.append( job ); +} + +void BugSystem::unregisterJob( BugJob *job ) +{ + mJobs.removeRef( job ); +} + +void BugSystem::killAllJobs() +{ + BugJob *job; + for( job = mJobs.first(); job; job = mJobs.next() ) { + job->kill(); + unregisterJob( job ); + } +} + +#include "bugsystem.moc" + +/* + * vim:sw=4:ts=4:et + */ diff --git a/kbugbuster/backend/bugsystem.h b/kbugbuster/backend/bugsystem.h new file mode 100644 index 00000000..c573698b --- /dev/null +++ b/kbugbuster/backend/bugsystem.h @@ -0,0 +1,146 @@ +#ifndef __bugsystem_h__ +#define __bugsystem_h__ + +#include "package.h" +#include "bug.h" +#include "bugdetails.h" +#include "bugcache.h" + +#include <kurl.h> + +#include <qobject.h> +#include <qptrlist.h> +#include <qmap.h> +#include <qpair.h> + +class KConfig; + +class BugCommand; +class BugServer; +class BugServerConfig; +class BugJob; + +class BugSystem : public QObject +{ + Q_OBJECT + friend class BugJob; + public: + BugSystem(); + virtual ~BugSystem(); + + static BugSystem *self(); + + BugCache *cache()const; + BugServer *server() const { return mServer; } + + /** + BugSystem takes ownership of the BugServerConfig objects. + */ + void setServerList( const QValueList<BugServerConfig> &servers ); + QValueList<BugServer *> serverList(); + + void setCurrentServer( const QString & ); + + void retrievePackageList(); + void retrieveBugList( const Package &, const QString &component ); + void retrieveBugDetails( const Bug & ); + + /** + * Load the bugs the user reported himself, or for which he is the assigned to person + */ + void retrieveMyBugsList(); + + /** + Queue a new command. + */ + void queueCommand( BugCommand * ); + /** + Forget all commands for a given bug. + */ + void clearCommands( const QString &bug ); + /** + Forget all commands for all bugs. + */ + void clearCommands(); + /** + Send all commands (generate the mails). + */ + void sendCommands(); + + void setDisconnected( bool ); + bool disconnected() const; + + Package::List packageList() const; + + Package package( const QString &pkgname ) const; + Bug bug( const Package &pkg, const QString &component, const QString &number ) const; + + static void saveQuery( const KURL &url ); + static void saveResponse( const QByteArray &d ); + static QString lastResponse(); + + void readConfig( KConfig * ); + void writeConfig( KConfig * ); + + signals: + void packageListAvailable( const Package::List &pkgs ); + void bugListAvailable( const Package &pkg, const QString &component, const Bug::List & ); + void bugListAvailable( const QString &label, const Bug::List & ); + void bugDetailsAvailable( const Bug &, const BugDetails & ); + + void packageListLoading(); + void bugListLoading( const Package &, const QString &component ); + void bugListLoading( const QString &label ); + void bugDetailsLoading( const Bug & ); + + void packageListCacheMiss(); + void bugListCacheMiss( const Package &package ); + void bugListCacheMiss( const QString &label ); + void bugDetailsCacheMiss( const Bug & ); + + void bugDetailsLoadingError(); + + void infoMessage( const QString &message ); + void infoPercent( unsigned long percent ); + + void commandQueued( BugCommand * ); + void commandCanceled( const QString & ); + + void loadingError( const QString &text ); + + protected: + BugServer *findServer( const QString &name ); + + void registerJob( BugJob * ); + + void connectJob( BugJob * ); + + void killAllJobs(); + + protected slots: + void unregisterJob( BugJob * ); + + private slots: + void setPackageList( const Package::List &pkgs ); + void setBugList( const Package &pkg, const QString &component, const Bug::List &bugs ); + void setBugDetails( const Bug &bug, const BugDetails &details ); + + private: + bool m_disconnected; + + BugServer *mServer; + + QValueList<BugServer *> mServerList; + + QPtrList<BugJob> mJobs; + + static BugSystem *s_self; + + static QString mLastResponse; +}; + +#endif + +/* + * vim:sw=4:ts=4:et + */ diff --git a/kbugbuster/backend/domprocessor.cpp b/kbugbuster/backend/domprocessor.cpp new file mode 100644 index 00000000..7643ca11 --- /dev/null +++ b/kbugbuster/backend/domprocessor.cpp @@ -0,0 +1,407 @@ +/* + This file is part of KBugBuster. + Copyright (c) 2002 Cornelius Schumacher <schumacher@kde.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. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "domprocessor.h" + +#include <qregexp.h> +#include <qstylesheet.h> + +#include <kdebug.h> +#include <kmdcodec.h> + +#include "bugserver.h" +#include "packageimpl.h" +#include "bugimpl.h" +#include "bugdetailsimpl.h" +#include "kbbprefs.h" + +DomProcessor::DomProcessor( BugServer *server ) + : Processor( server ) +{ +} + +DomProcessor::~DomProcessor() +{ +} + +KBB::Error DomProcessor::parsePackageList( const QByteArray &data, + Package::List &packages ) +{ + QDomDocument doc; + if ( !doc.setContent( data ) ) { + return KBB::Error( "Error parsing xml response for package list request." ); + } + + QDomElement bugzilla = doc.documentElement(); + + if ( bugzilla.isNull() ) { + return KBB::Error( "No document in xml response." ); + } + + KBB::Error err = parseDomPackageList( bugzilla, packages ); + + return err; +} + +KBB::Error DomProcessor::parseBugList( const QByteArray &data, Bug::List &bugs ) +{ + QDomDocument doc; + if ( !doc.setContent( data ) ) { + return KBB::Error( "Error parsing xml response for bug list request" ); + } + + QDomElement bugzilla = doc.documentElement(); + + if ( bugzilla.isNull() ) { + return KBB::Error( "No document in xml response." ); + } + + KBB::Error err = parseDomBugList( bugzilla, bugs ); + + return err; +} + +KBB::Error DomProcessor::parseBugDetails( const QByteArray &data, + BugDetails &bugDetails ) +{ + QDomDocument doc; + if ( !doc.setContent( data ) ) { + return KBB::Error( "Error parsing xml response for bug details request." ); + } + + QDomElement bugzilla = doc.documentElement(); + + if ( bugzilla.isNull() ) { + return KBB::Error( "No document in xml response." ); + } + + QDomNode p; + for ( p = bugzilla.firstChild(); !p.isNull(); p = p.nextSibling() ) { + QDomElement bug = p.toElement(); + if ( bug.tagName() != "bug" ) continue; + + KBB::Error err = parseDomBugDetails( bug, bugDetails ); + + if ( err ) return err; + } + + return KBB::Error(); +} + + +KBB::Error DomProcessor::parseDomPackageList( const QDomElement &element, + Package::List &packages ) +{ + QDomNode p; + for ( p = element.firstChild(); !p.isNull(); p = p.nextSibling() ) { + QDomElement bug = p.toElement(); + + if ( bug.tagName() != "product" ) continue; + + QString pkgName = bug.attribute( "name" ); + uint bugCount = 999; + Person maintainer; + QString description; + QStringList components; + + QDomNode n; + for( n = bug.firstChild(); !n.isNull(); n = n.nextSibling() ) { + QDomElement e = n.toElement(); + if ( e.tagName() == "descr" ) description= e.text().stripWhiteSpace(); + if ( e.tagName() == "component" ) components += e.text().stripWhiteSpace(); + } + + Package pkg( new PackageImpl( pkgName, description, bugCount, maintainer, components ) ); + + if ( !pkg.isNull() ) { + packages.append( pkg ); + } + } + + return KBB::Error(); +} + +KBB::Error DomProcessor::parseDomBugList( const QDomElement &topElement, + Bug::List &bugs ) +{ + QDomElement element; + + if ( topElement.tagName() != "querybugids" ) { + QDomNode buglist = topElement.namedItem( "querybugids" ); + element = buglist.toElement(); + if ( element.isNull() ) { + return KBB::Error( "No querybugids element found." ); + } + } else { + element = topElement; + } + + QDomNode p; + for ( p = element.firstChild(); !p.isNull(); p = p.nextSibling() ) { + QDomElement hit = p.toElement(); + + kdDebug() << "DomProcessor::parseDomBugList(): tag: " << hit.tagName() << endl; + + if ( hit.tagName() == "error" ) { + return KBB::Error( "Error: " + hit.text() ); + } else if ( hit.tagName() != "hit" ) continue; + + QString title; + QString submitterName; + QString submitterEmail; + QString bugNr; + Bug::Status status = Bug::StatusUndefined; + Bug::Severity severity = Bug::SeverityUndefined; + Person developerTodo; + Bug::BugMergeList mergedList; + uint age = 0xFFFFFFFF; + + QDomNode n; + for ( n = hit.firstChild(); !n.isNull(); n = n.nextSibling() ) + { + QDomElement e = n.toElement(); + + if ( e.tagName() == "bugid" ) + bugNr = e.text(); + else if ( e.tagName() == "status" ) + status = server()->bugStatus( e.text() ); + else if ( e.tagName() == "descr" ) + title = e.text(); + else if ( e.tagName() == "reporter" ) + submitterEmail = e.text(); + else if ( e.tagName() == "reporterName" ) + submitterName = e.text(); + else if ( e.tagName() == "severity" ) + severity = Bug::stringToSeverity( e.text() ); + else if ( e.tagName() == "creationdate" ) + age = ( QDateTime::fromString( e.text(), Qt::ISODate ) ).daysTo( QDateTime::currentDateTime() ); + } + + Person submitter( submitterName, submitterEmail ); + + Bug bug( new BugImpl( title, submitter, bugNr, age, severity, + developerTodo, status, mergedList ) ); + + if ( !bug.isNull() ) { + bugs.append( bug ); + } + } + + return KBB::Error(); +} + +KBB::Error DomProcessor::parseDomBugDetails( const QDomElement &element, + BugDetails &bugDetails ) +{ + if ( element.tagName() != "bug" ) return KBB::Error( "No <bug> tag found" ); + + BugDetailsPart::List parts; + QValueList<BugDetailsImpl::AttachmentDetails> attachments; + + QString versionXml; + QString osXml; + + QString version; + QString source; + QString compiler; + QString os; + + QDomNode n; + for( n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) { + QDomElement e = n.toElement(); + if ( e.tagName() == "version" ) versionXml = e.text().stripWhiteSpace(); + if ( e.tagName() == "op_sys" ) osXml = e.text().stripWhiteSpace(); + + if ( e.tagName() == "long_desc" ) { + + QString encoding = e.attribute( "encoding" ); + + Person sender; + QDateTime date; + QString text; + + QDomNode n2; + for( n2 = e.firstChild(); !n2.isNull(); n2 = n2.nextSibling() ) { + QDomElement e2 = n2.toElement(); + if ( e2.tagName() == "who" ) { + sender = Person::parseFromString( e2.text() ); + } else if ( e2.tagName() == "bug_when" ) { + date = parseDate( e2.text().stripWhiteSpace() ); + } else if ( e2.tagName() == "thetext" ) { + QString in; + if ( encoding == "base64" ) { + in = KCodecs::base64Decode( e2.text().latin1() ); + } else { + in = e2.text(); + } + + QString raw = QStyleSheet::escape( in ); + + if ( parts.isEmpty() ) + { + QTextStream ts( &raw, IO_ReadOnly ); + QString line; + while( !( line = ts.readLine() ).isNull() ) { + if ( parseAttributeLine( line, "Version", version ) ) continue; + if ( parseAttributeLine( line, "Installed from", source ) ) continue; + if ( parseAttributeLine( line, "Compiler", compiler ) ) continue; + if ( parseAttributeLine( line, "OS", os ) ) continue; + + text += line + "\n"; + } + } else { + text += raw; + } + QString bugBaseURL = server()->serverConfig().baseUrl().htmlURL(); + text = "<pre>" + wrapLines( text ).replace( QRegExp( "(Created an attachment \\(id=([0-9]+)\\))" ), + "<a href=\"" + bugBaseURL + "/attachment.cgi?id=\\2&action=view\">\\1</a>" ) + "\n</pre>"; + } + } + + parts.prepend( BugDetailsPart( sender, date, text ) ); + } + + if ( e.tagName() == "attachment" ) { + QString attachid, date, desc; + for( QDomNode node = e.firstChild(); !node.isNull(); node = node.nextSibling() ) { + QDomElement e2 = node.toElement(); + if ( e2.tagName() == "attachid" ) { + attachid = e2.text(); + } else if ( e2.tagName() == "date" ) { + date = e2.text().stripWhiteSpace(); + } else if ( e2.tagName() == "desc" ) { + desc = "<pre>" + wrapLines( QStyleSheet::escape(e2.text()) ) + "\n</pre>"; + } + } + attachments.append( BugDetailsImpl::AttachmentDetails( desc, date, attachid ) ); + } + } + + if ( version.isEmpty() ) version = versionXml; + if ( os.isEmpty() ) os = osXml; + + bugDetails = BugDetails( new BugDetailsImpl( version, source, compiler, os, + parts ) ); + bugDetails.addAttachmentDetails( attachments ); + + return KBB::Error(); +} + +void DomProcessor::setPackageListQuery( KURL &url ) +{ + url.setFileName( "xml.cgi" ); + url.setQuery( "?data=versiontable" ); +} + +void DomProcessor::setBugListQuery( KURL &url, const Package &product, const QString &component ) +{ + if ( server()->serverConfig().bugzillaVersion() == "Bugworld" ) { + url.setFileName( "bugworld.cgi" ); + } else { + url.setFileName( "xmlquery.cgi" ); + } + + QString user = server()->serverConfig().user(); + + if ( component.isEmpty() ) + url.setQuery( "?user=" + user + "&product=" + product.name() ); + else + url.setQuery( "?user=" + user + "&product=" + product.name() + "&component=" + component ); + + if ( KBBPrefs::instance()->mShowClosedBugs ) + url.addQueryItem( "addClosed", "1" ); +} + +void DomProcessor::setBugDetailsQuery( KURL &url, const Bug &bug ) +{ + url.setFileName( "xml.cgi" ); + url.setQuery( "?id=" + bug.number() ); +} + +QString DomProcessor::wrapLines( const QString &text ) +{ + int wrap = KBBPrefs::instance()->mWrapColumn; + + QStringList lines = QStringList::split( '\n', text, true ); + //kdDebug() << lines.count() << " lines." << endl; + + QString out; + bool removeBlankLines = true; + for ( QStringList::Iterator it = lines.begin() ; it != lines.end() ; ++it ) + { + QString line = *it; + + if ( removeBlankLines ) { + if ( line.isEmpty() ) continue; + else removeBlankLines = false; + } + + //kdDebug() << "BugDetailsJob::processNode IN line='" << line << "'" << endl; + + QString wrappedLine; + while ( line.length() > uint( wrap ) ) + { + int breakPoint = line.findRev( ' ', wrap ); + //kdDebug() << "Breaking at " << breakPoint << endl; + if( breakPoint == -1 ) { + wrappedLine += line.left( wrap ) + '\n'; + line = line.mid( wrap ); + } else { + wrappedLine += line.left( breakPoint ) + '\n'; + line = line.mid( breakPoint + 1 ); + } + } + wrappedLine += line; // the remainder + //kdDebug() << "BugDetailsJob::processNode OUT wrappedLine='" << wrappedLine << "'" << endl; + + out += wrappedLine + "\n"; + } + + return out; +} + +bool DomProcessor::parseAttributeLine( const QString &line, const QString &key, + QString &result ) +{ + if ( !result.isEmpty() ) return false; + + if ( !line.startsWith( key + ":" ) ) return false; + + QString value = line.mid( key.length() + 1 ); + value = value.stripWhiteSpace(); + + result = value; + + return true; +} + +QDateTime DomProcessor::parseDate( const QString &dateStr ) +{ + QDateTime date = QDateTime::fromString( dateStr, Qt::ISODate ); + + return date; +} + +/* + * vim:sw=4:ts=4:et + */ diff --git a/kbugbuster/backend/domprocessor.h b/kbugbuster/backend/domprocessor.h new file mode 100644 index 00000000..1553ae4a --- /dev/null +++ b/kbugbuster/backend/domprocessor.h @@ -0,0 +1,69 @@ +/* + This file is part of KBugBuster. + Copyright (c) 2002 Cornelius Schumacher <schumacher@kde.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. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ +#ifndef DOMPROCESSOR_H +#define DOMPROCESSOR_H + +#include "bug.h" +#include "bugdetails.h" +#include "package.h" +#include "error.h" +#include "processor.h" + +#include <kurl.h> + +#include <qdom.h> + +class BugServer; + +class DomProcessor : public Processor +{ + public: + DomProcessor( BugServer * ); + virtual ~DomProcessor(); + + KBB::Error parsePackageList( const QByteArray &data, + Package::List &packages ); + KBB::Error parseBugList( const QByteArray &data, Bug::List &bugs ); + KBB::Error parseBugDetails( const QByteArray &, BugDetails & ); + + void setPackageListQuery( KURL & ); + void setBugListQuery( KURL &, const Package &, const QString &component ); + void setBugDetailsQuery( KURL &, const Bug & ); + + protected: + virtual KBB::Error parseDomPackageList( const QDomElement &, + Package::List & ); + virtual KBB::Error parseDomBugList( const QDomElement &, Bug::List & ); + virtual KBB::Error parseDomBugDetails( const QDomElement &, BugDetails & ); + + QString wrapLines( const QString & ); + bool parseAttributeLine( const QString &line, const QString &key, + QString &result ); + QDateTime parseDate( const QString & ); +}; + +#endif + +/* + * vim:sw=4:ts=4:et + */ diff --git a/kbugbuster/backend/error.h b/kbugbuster/backend/error.h new file mode 100644 index 00000000..e12bcf61 --- /dev/null +++ b/kbugbuster/backend/error.h @@ -0,0 +1,43 @@ +/* + This file is part of KBugBuster. + Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.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. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ +#ifndef KBB_ERROR_H +#define KBB_ERROR_H + +namespace KBB { + +class Error +{ + public: + Error( const QString &msg = QString::null ) : mMsg( msg ) {} + + operator bool() { return !mMsg.isEmpty(); } + + QString message() const { return mMsg; } + + private: + QString mMsg; +}; + +} + +#endif diff --git a/kbugbuster/backend/htmlparser.cpp b/kbugbuster/backend/htmlparser.cpp new file mode 100644 index 00000000..7e53c1bd --- /dev/null +++ b/kbugbuster/backend/htmlparser.cpp @@ -0,0 +1,294 @@ +/* + This file is part of KBugBuster. + + Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.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. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "htmlparser.h" +#include "bugimpl.h" +#include "packageimpl.h" + +#include <kdebug.h> + +#include <qbuffer.h> +#include <qregexp.h> +#include <qtextstream.h> + +KBB::Error HtmlParser::parseBugList( const QByteArray &data, Bug::List &bugs ) +{ + QBuffer buffer( data ); + if ( !buffer.open( IO_ReadOnly ) ) { + return KBB::Error( "Can't open buffer" ); + } + + QTextStream ts( &buffer ); + + mState = Idle; + + QString line; + while ( !( line = ts.readLine() ).isNull() ) { + KBB::Error err = parseLine( line, bugs ); + if ( err ) return err; + } + + return KBB::Error(); +} + +KBB::Error HtmlParser::parsePackageList( const QByteArray &data, + Package::List &packages ) +{ + init(); + + QBuffer buffer( data ); + if ( !buffer.open( IO_ReadOnly ) ) { + return KBB::Error( "Can't open buffer" ); + } + + QTextStream ts( &buffer ); + + QString line; + while ( !( line = ts.readLine() ).isNull() ) { + KBB::Error err = parseLine( line, packages ); + if ( err ) return err; + } + + processResult( packages ); + + return KBB::Error(); +} + +void HtmlParser::init() +{ +} + +void HtmlParser::setPackageListQuery( KURL &url ) +{ + url.setFileName( "query.cgi" ); +} + +KBB::Error HtmlParser::parseLine( const QString &, Bug::List & ) +{ + return KBB::Error(); +} + +KBB::Error HtmlParser::parseLine( const QString &, Package::List & ) +{ + return KBB::Error(); +} + +void HtmlParser::processResult( Package::List & ) +{ +} + +QString HtmlParser::getAttribute( const QString &line, const QString &name ) +{ + int pos1 = line.find( name + "=\"" ); + if ( pos1 < 1 ) return QString::null; + pos1 += name.length() + 2; + int pos2 = line.find( "\"", pos1 ); + if ( pos2 < 1 ) return QString::null; + return line.mid( pos1, pos2 - pos1 ); +} + +bool HtmlParser::getCpts( const QString &line, QString &key, + QStringList &values ) +{ + if ( !line.contains( QRegExp( "\\s*cpts" ) ) ) return false; + +// kdDebug() << "LINE: " << line << endl; + int pos1 = line.find( "[" ); + if ( pos1 < 0 ) return false; + int pos2 = line.find( "]", ++pos1 ); + if ( pos2 < 0 ) return false; + + key = line.mid( pos1, pos2 - pos1 ); + int pos3 = key.find( "'" ); + if ( pos3 >= 0 ) { + int pos4 = key.find( "'", ++pos3 ); + if ( pos4 >= 0 ) key = key.mid( pos3, pos4 - pos3 ); + } +// kdDebug() << " KEY: " << key << endl; + + pos1 = line.find( "'", ++pos2 ); + if ( pos1 >= 0 ) pos2 = line.find( "'", ++pos1 ); + + while ( pos1 >= 0 && pos2 >= 0 ) { + QString value = line.mid( pos1, pos2 - pos1 ); +// kdDebug() << " VALUE: " << value << endl; + + values.append( value ); + + pos1 = line.find( "'", ++pos2 ); + if ( pos1 >= 0 ) pos2 = line.find( "'", ++pos1 ); + } + + return true; +} + +KBB::Error HtmlParser_2_10::parseLine( const QString &line, Bug::List &bugs ) +{ + if ( line.startsWith( "<TR VALIGN" ) ) { +// kdDebug() << "LINE: " << line << endl; + QRegExp re( "show_bug\\.cgi\\?id=(\\d+)" ); + re.search( line ); + QString number = re.cap( 1 ); +// kdDebug() << " NUMBER: " << number << endl; + + QString summary; + int pos = line.findRev( "summary>" ); + if ( pos >= 0 ) summary = line.mid( pos + 8 ); + + Bug bug( new BugImpl( summary, Person(), number, 0xFFFFFFFF, Bug::SeverityUndefined, + Person(), Bug::StatusUndefined, + Bug::BugMergeList() ) ); + + if ( !bug.isNull() ) { + bugs.append( bug ); + } + } + + return KBB::Error(); +} + +KBB::Error HtmlParser_2_10::parseLine( const QString &line, + Package::List &packages ) +{ + QString package; + QStringList components; + + if ( getCpts( line, package, components ) ) { + packages.append( Package( new PackageImpl( package, "", 0, Person(), + components ) ) ); + } + + return KBB::Error(); +} + + +void HtmlParser_2_14_2::init() +{ + mComponentsMap.clear(); + + mState = Idle; +} + +KBB::Error HtmlParser_2_14_2::parseLine( const QString &line, + Package::List & ) +{ + switch ( mState ) { + case Idle: + if ( line.startsWith( "tms[" ) ) mState = Components; + break; + case Components: { + if ( line.startsWith( "function" ) ) mState = Finished; + QString key; + QStringList values; + if ( getCpts( line, key, values ) ) { +// kdDebug() << "KEY: " << key << " VALUES: " << values.join(",") << endl; + if ( values.count() == 2 ) { + mComponentsMap[ values.last() ].append( key ); + } + } + } + default: + break; + } + + return KBB::Error(); +} + +void HtmlParser_2_14_2::processResult( Package::List &packages ) +{ + QMap<QString,QStringList>::ConstIterator it; + for ( it = mComponentsMap.begin(); it != mComponentsMap.end(); ++it ) { + packages.append( Package( new PackageImpl( it.key(), "", 0, Person(), + it.data() ) ) ); + } +} + + +void HtmlParser_2_17_1::init() +{ + mProducts.clear(); + mComponents.clear(); + + mState = Idle; +} + +KBB::Error HtmlParser_2_17_1::parseBugList( const QByteArray &data, Bug::List &bugs ) +{ + return RdfProcessor::parseBugList( data, bugs ); +} + +KBB::Error HtmlParser_2_17_1::parseLine( const QString & /*line*/, Bug::List &/*bugs*/ ) +{ + return KBB::Error( "Not implemented" ); +} + +KBB::Error HtmlParser_2_17_1::parseLine( const QString &line, Package::List & ) +{ + switch ( mState ) { + case Idle: + case SearchComponents: + if ( line.contains( "var cpts" ) ) mState = Components; + break; + case SearchProducts: + if ( line.contains( "onchange=\"selectProduct" ) ) mState = Products; + break; + case Components: { + if ( line.contains( QRegExp( "\\s*function" ) ) ) { + mState = SearchProducts; + } + QString key; + QStringList components; + if ( getCpts( line, key, components ) ) { + mComponents.append( components ); + } + } + case Products: { + if ( line.contains( "</select>" ) ) mState = Finished; + QString product = getAttribute( line, "value" ); + if ( !product.isEmpty() ) { + kdDebug() << "PRODUCT: " << product << endl; + mProducts.append( product ); + } + break; + } + case Finished: + default: + break; + } + + return KBB::Error(); +} + +void HtmlParser_2_17_1::processResult( Package::List &packages ) +{ + QStringList::ConstIterator itProduct = mProducts.begin(); + QValueList<QStringList>::ConstIterator itComponents = mComponents.begin(); + + while( itProduct != mProducts.end() && itComponents != mComponents.end() ) { + packages.append( Package( new PackageImpl( *itProduct, "", 0, Person(), + *itComponents ) ) ); + ++itProduct; + ++itComponents; + } +} diff --git a/kbugbuster/backend/htmlparser.h b/kbugbuster/backend/htmlparser.h new file mode 100644 index 00000000..ffb0a22a --- /dev/null +++ b/kbugbuster/backend/htmlparser.h @@ -0,0 +1,116 @@ +/* + This file is part of KBugBuster. + + Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.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. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ +#ifndef HTMLPARSER_H +#define HTMLPARSER_H + +#include "package.h" +#include "bug.h" +#include "error.h" +#include "rdfprocessor.h" + +#include <qstringlist.h> +#include <qvaluelist.h> +#include <qmap.h> + +class HtmlParser : public RdfProcessor +{ + protected: + enum State { Idle, SearchComponents, SearchProducts, Components, Products, + Finished }; + State mState; + + public: + HtmlParser( BugServer *s ) : RdfProcessor( s ), mState( Idle ) {} + virtual ~HtmlParser() {} + + KBB::Error parseBugList( const QByteArray &data, Bug::List &bugs ); + KBB::Error parsePackageList( const QByteArray &data, + Package::List &packages ); + + void setPackageListQuery( KURL & ); + + protected: + virtual void init(); + + virtual KBB::Error parseLine( const QString &line, Bug::List &bugs ); + virtual KBB::Error parseLine( const QString &line, + Package::List &packages ); + + virtual void processResult( Package::List &packages ); + + QString getAttribute( const QString &line, const QString &name ); + bool getCpts( const QString &line, QString &key, QStringList &values ); +}; + + +class HtmlParser_2_10 : public HtmlParser +{ + public: + HtmlParser_2_10( BugServer *s ) : HtmlParser( s ) {} + + protected: + KBB::Error parseLine( const QString &line, Bug::List &bugs ); + KBB::Error parseLine( const QString &line, Package::List &packages ); +}; + + +class HtmlParser_2_14_2 : public HtmlParser_2_10 +{ + public: + HtmlParser_2_14_2( BugServer *s ) : HtmlParser_2_10( s ) {} + + protected: + void init(); + + KBB::Error parseLine( const QString &line, Package::List &packages ); + + void processResult( Package::List &packages ); + + private: + QMap<QString, QStringList> mComponentsMap; +}; + + + +class HtmlParser_2_17_1 : public HtmlParser +{ + public: + HtmlParser_2_17_1( BugServer *s ) : HtmlParser( s ) {} + + KBB::Error parseBugList( const QByteArray &data, Bug::List &bugs ); + + protected: + void init(); + + KBB::Error parseLine( const QString &line, Bug::List &bugs ); + KBB::Error parseLine( const QString &line, Package::List &packages ); + + void processResult( Package::List &packages ); + + private: + QStringList mProducts; + QValueList<QStringList> mComponents; +}; + +#endif diff --git a/kbugbuster/backend/kbbprefs.cpp b/kbugbuster/backend/kbbprefs.cpp new file mode 100644 index 00000000..30f337ab --- /dev/null +++ b/kbugbuster/backend/kbbprefs.cpp @@ -0,0 +1,170 @@ +/* + This file is part of KBugBuster. + + Copyright (c) 2001,2003 Cornelius Schumacher <schumacher@kde.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. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include <qstring.h> +#include <qstringlist.h> + +#include <klocale.h> +#include <kdebug.h> +#include <kconfig.h> + +#include "bugsystem.h" +#include "bugserver.h" +#include "bugserverconfig.h" + +#include "kbbprefs.h" + +KBBPrefs *KBBPrefs::mInstance = 0; + +KBBPrefs::KBBPrefs() : KConfigSkeleton() +{ + setCurrentGroup("History"); + + addItemInt("RecentPackagesCount",mRecentPackagesCount,7); + addItemIntList("Splitter1",mSplitter1); + addItemIntList("Splitter2",mSplitter2); + + + setCurrentGroup("Personal Settings"); + + addItemInt("MailClient",mMailClient,MailSender::KMail,"Mail Client"); + addItemBool("ShowClosedBugs",mShowClosedBugs,false); + addItemBool("ShowWishes",mShowWishes,true); + addItemBool("ShowVotes", mShowVoted, false); + addItemInt("MinimumVotes", mMinVotes, 0); + addItemBool("SendBCC",mSendBCC,false); + addItemString("OverrideRecipient",mOverrideRecipient,QString::null); + addItemInt("WrapColumn",mWrapColumn,90); + + + setCurrentGroup("MsgInputDlg"); + + addItemInt("MsgDialogWidth",mMsgDlgWidth); + addItemInt("MsgDialogHeight",mMsgDlgHeight); + addItemIntList("MsgDialogSplitter",mMsgDlgSplitter); + + + setCurrentGroup( "Debug" ); + + addItemBool( "DebugMode", mDebugMode, false ); + + + setCurrentGroup( "Servers" ); + + addItemString("CurrentServer",mCurrentServer); +} + + +KBBPrefs::~KBBPrefs() +{ + delete mInstance; + mInstance = 0; +} + + +KBBPrefs *KBBPrefs::instance() +{ + if (!mInstance) { + mInstance = new KBBPrefs(); + mInstance->readConfig(); + } + + return mInstance; +} + +void KBBPrefs::usrSetDefaults() +{ + setMessageButtonsDefault(); +} + +void KBBPrefs::usrReadConfig() +{ + mMessageButtons.clear(); + + config()->setGroup("MessageButtons"); + QStringList buttonList = config()->readListEntry("ButtonList"); + if (buttonList.isEmpty()) { + setMessageButtonsDefault(); + } else { + QStringList::ConstIterator it; + for(it = buttonList.begin(); it != buttonList.end(); ++it) { + QString text = config()->readEntry(*it); + mMessageButtons.insert(*it,text); + } + } + + BugSystem::self()->readConfig( config() ); +} + +void KBBPrefs::usrWriteConfig() +{ + config()->setGroup("MessageButtons"); + QStringList buttonList; + QMap<QString,QString>::ConstIterator it; + for(it = mMessageButtons.begin();it != mMessageButtons.end();++it) { + buttonList.append(it.key()); + config()->writeEntry(it.key(),it.data()); + } + config()->writeEntry("ButtonList",buttonList); + + BugSystem::self()->writeConfig( config() ); +} + +void KBBPrefs::setMessageButtonsDefault() +{ + mMessageButtons.clear(); + mMessageButtons.insert(i18n("Bug Fixed in CVS"),"Thank you for your bug report.\n" + "The bug that you reported has been identified and has been fixed in the\n" + "latest development (CVS) version of KDE. The bug report will be closed.\n"); + mMessageButtons.insert(i18n("Duplicate Report"),"Thank you for your bug report.\n" + "This bug/feature request has already been reported and this report will\n" + "be marked as a duplicate.\n"); + mMessageButtons.insert(i18n("Packaging Bug"),"Thank you for your bug report.\n" + "The bug that you reported appears to be a packaging bug, due to a\n" + "problem in the way in which your distribution/vendor has packaged\n" + "KDE for distribution.\n" + "The bug report will be closed since it is not a KDE problem.\n" + "Please send the bug report to your distribution/vendor instead.\n"); + mMessageButtons.insert(i18n("Feature Implemented in CVS"),"Thank you for your bug report.\n" + "The feature that you requested has been implemented in the latest\n" + "development (CVS) version of KDE. The feature request will be closed.\n"); + mMessageButtons.insert(i18n("More Information Required"),"Thank you for your bug report.\n" + "You have not provided enough information for us to be able to reproduce\n" + "the bug. Please provide a detailed account of the steps required to\n" + "trigger and reproduce the bug. Without this information, we will not be\n" + "able to reproduce, identify and fix the bug.\n"); + mMessageButtons.insert(i18n("No Longer Applicable"),"Thank you for your bug report.\n" + "The bug that your reported no longer applies to the latest development\n" + "(CVS) version of KDE. This is most probably because it has been fixed,\n" + "the application has been substantially modified or the application no\n" + "longer exists. The bug report will be closed.\n"); + mMessageButtons.insert(i18n("Won't Fix Bug"),"Thank you for your bug report/feature request.\n" + "Unfortunately, this bug will never be fixed or the feature never\n" + "implemented. The bug report/feature request will be closed.\n"); + mMessageButtons.insert(i18n("Cannot Reproduce Bug"),"Thank you for your bug report.\n" + "This bug can not be reproduced using the current development (CVS)\n" + "version of KDE. This suggests that the bug has already been fixed.\n" + "The bug report will be closed.\n"); +} + diff --git a/kbugbuster/backend/kbbprefs.h b/kbugbuster/backend/kbbprefs.h new file mode 100644 index 00000000..64d7f20d --- /dev/null +++ b/kbugbuster/backend/kbbprefs.h @@ -0,0 +1,82 @@ +/* + This file is part of KBugBuster. + + Copyright (c) 2001,2003 Cornelius Schumacher <schumacher@kde.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. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ +#ifndef KBBPREFS_H +#define KBBPREFS_H + +#include <qmap.h> + +#include <kconfigskeleton.h> + +#include "mailsender.h" + +class QStringList; + +class KBBPrefs : public KConfigSkeleton +{ + public: + virtual ~KBBPrefs(); + + static KBBPrefs *instance(); + + protected: + void usrSetDefaults(); + void usrReadConfig(); + void usrWriteConfig(); + + void setMessageButtonsDefault(); + + private: + KBBPrefs(); + + static KBBPrefs *mInstance; + + public: + int mRecentPackagesCount; + + QValueList<int> mSplitter1; + QValueList<int> mSplitter2; + + int mMailClient; + bool mShowClosedBugs; + bool mShowWishes; + bool mSendBCC; + QString mOverrideRecipient; + + bool mShowVoted; + int mMinVotes; + + int mWrapColumn; + + QMap<QString,QString> mMessageButtons; + + int mMsgDlgWidth; + int mMsgDlgHeight; + QValueList<int> mMsgDlgSplitter; + + bool mDebugMode; + + QString mCurrentServer; +}; + +#endif diff --git a/kbugbuster/backend/mailsender.cpp b/kbugbuster/backend/mailsender.cpp new file mode 100644 index 00000000..ec32405d --- /dev/null +++ b/kbugbuster/backend/mailsender.cpp @@ -0,0 +1,212 @@ +#ifndef QT_NO_ASCII_CAST +#define QT_NO_ASCII_CAST +#endif + +#include <unistd.h> +#include <stdio.h> + +#include <qtimer.h> + +#include <klocale.h> +#include <kstandarddirs.h> +#include <kdebug.h> +#include <kmessagebox.h> +#include <kurl.h> +#include <kapplication.h> +#include <dcopclient.h> +#include <kprocess.h> + +#include "mailsender.h" +#include "smtp.h" + +MailSender::MailSender(MailClient client,const QString &smtpServer) : + m_client( client ), m_smtpServer( smtpServer ) +{ +} + +MailSender::~MailSender() +{ +} + +MailSender *MailSender::clone() const +{ + return new MailSender(m_client,m_smtpServer); +} + +bool MailSender::send(const QString &fromName,const QString &fromEmail,const QString &to, + const QString &subject,const QString &body,bool bcc, + const QString &recipient) +{ + QString from( fromName ); + if ( !fromEmail.isEmpty() ) + from += QString::fromLatin1( " <%2>" ).arg( fromEmail ); + kdDebug() << "MailSender::sendMail():\nFrom: " << from << "\nTo: " << to + << "\nbccflag:" << bcc + << "\nRecipient:" << recipient + << "\nSubject: " << subject << "\nBody: \n" << body << endl; + + // ### FIXME: bcc is not supported in direct mode and recipient is not + // supported in sendmail and kmail mode + + if (m_client == Sendmail) { + kdDebug() << "Sending per Sendmail" << endl; + + bool needHeaders = true; + + QString command = KStandardDirs::findExe(QString::fromLatin1("sendmail"), + QString::fromLatin1("/sbin:/usr/sbin:/usr/lib")); + if (!command.isNull()) command += QString::fromLatin1(" -oi -t"); + else { + command = KStandardDirs::findExe(QString::fromLatin1("mail")); + if (command.isNull()) return false; // give up + + command.append(QString::fromLatin1(" -s ")); + command.append(KProcess::quote(subject)); + + if (bcc) { + command.append(QString::fromLatin1(" -b ")); + command.append(KProcess::quote(from)); + } + + command.append(" "); + command.append(KProcess::quote(to)); + + needHeaders = false; + } + + FILE * fd = popen(command.local8Bit(),"w"); + if (!fd) + { + kdError() << "Unable to open a pipe to " << command << endl; + QTimer::singleShot( 0, this, SLOT( deleteLater() ) ); + return false; + } + + QString textComplete; + if (needHeaders) + { + textComplete += QString::fromLatin1("From: ") + from + '\n'; + textComplete += QString::fromLatin1("To: ") + to + '\n'; + if (bcc) textComplete += QString::fromLatin1("Bcc: ") + from + '\n'; + textComplete += QString::fromLatin1("Subject: ") + subject + '\n'; + textComplete += QString::fromLatin1("X-Mailer: KBugBuster") + '\n'; + } + textComplete += '\n'; // end of headers + textComplete += body; + + emit status( i18n( "Sending through sendmail..." ) ); + fwrite(textComplete.local8Bit(),textComplete.length(),1,fd); + + pclose(fd); + } else if ( m_client == KMail ) { + kdDebug() << "Sending per KMail" << endl; + + if (!kapp->dcopClient()->isApplicationRegistered("kmail")) { + KMessageBox::error(0,i18n("No running instance of KMail found.")); + QTimer::singleShot( 0, this, SLOT( deleteLater() ) ); + return false; + } + + emit status( i18n( "Passing mail to KDE email program..." ) ); + if (!kMailOpenComposer(to,"", (bcc ? from : ""), subject,body,0,KURL())) { + QTimer::singleShot( 0, this, SLOT( deleteLater() ) ); + return false; + } + } else if ( m_client == Direct ) { + kdDebug() << "Sending Direct" << endl; + + QStringList recipients; + if ( !recipient.isEmpty() ) + recipients << recipient; + else + recipients << to; + + QString message = QString::fromLatin1( "From: " ) + from + + QString::fromLatin1( "\nTo: " ) + to + + QString::fromLatin1( "\nSubject: " ) + subject + + QString::fromLatin1( "\nX-Mailer: KBugBuster" ) + + QString::fromLatin1( "\n\n" ) + body; + + Smtp *smtp = new Smtp( fromEmail, recipients, message, m_smtpServer ); + connect( smtp, SIGNAL( status( const QString & ) ), + this, SIGNAL( status( const QString & ) ) ); + connect( smtp, SIGNAL( success() ), + this, SLOT( smtpSuccess() ) ); + connect( smtp, SIGNAL( error( const QString &, const QString & ) ), + this, SLOT( smtpError( const QString &, const QString & ) ) ); + + smtp->insertChild( this ); // die when smtp dies + } else { + kdDebug() << "Invalid mail client setting." << endl; + } + + if (m_client != Direct) + { + emit finished(); + QTimer::singleShot( 0, this, SLOT( deleteLater() ) ); + } + + return true; +} + +void MailSender::smtpSuccess() +{ + if ( parent() != sender() || !parent()->inherits( "Smtp" ) ) + return; + + static_cast<Smtp *>( parent() )->quit(); + emit finished(); +} + +void MailSender::smtpError(const QString &_command, const QString &_response) +{ + if ( parent() != sender() || !parent()->inherits( "Smtp" ) ) + return; + + QString command = _command; + QString response = _response; + + Smtp *smtp = static_cast<Smtp *>( parent() ); + smtp->removeChild( this ); + delete smtp; + + KMessageBox::error( qApp->activeWindow(), + i18n( "Error during SMTP transfer.\n" + "command: %1\n" + "response: %2" ).arg( command ).arg( response ) ); + + emit finished(); + QTimer::singleShot( 0, this, SLOT( deleteLater() ) ); +} + +int MailSender::kMailOpenComposer(const QString& arg0,const QString& arg1, + const QString& arg2,const QString& arg3,const QString& arg4,int arg5, + const KURL& arg6) +{ + int result = 0; + + QByteArray data, replyData; + QCString replyType; + QDataStream arg( data, IO_WriteOnly ); + arg << arg0; + arg << arg1; + arg << arg2; + arg << arg3; + arg << arg4; + arg << arg5; + arg << arg6; + if (kapp->dcopClient()->call("kmail","KMailIface","openComposer(QString,QString,QString,QString,QString,int,KURL)", data, replyType, replyData ) ) { + if ( replyType == "int" ) { + QDataStream _reply_stream( replyData, IO_ReadOnly ); + _reply_stream >> result; + } else { + kdDebug() << "kMailOpenComposer() call failed." << endl; + } + } else { + kdDebug() << "kMailOpenComposer() call failed." << endl; + } + return result; +} + +#include "mailsender.moc" + diff --git a/kbugbuster/backend/mailsender.h b/kbugbuster/backend/mailsender.h new file mode 100644 index 00000000..06517f9c --- /dev/null +++ b/kbugbuster/backend/mailsender.h @@ -0,0 +1,50 @@ +#ifndef MAILSENDER_H +#define MAILSENDER_H + +#include <qstring.h> +#include <qobject.h> + +class KURL; +class Smtp; + +class MailSender : public QObject +{ + Q_OBJECT + public: + enum MailClient { Sendmail = 0, KMail = 1, Direct = 2 }; + + MailSender(MailClient,const QString &smtpServer=QString::null); + virtual ~MailSender(); + + MailSender *clone() const; + + /** + Send mail with specified from, to and subject field and body as text. If + bcc is set, send a blind carbon copy to the sender from. + If recipient is specified the mail is sent to the specified address + instead of 'to' . (this currently only works in for direct mail + sending through SMTP. + */ + bool send(const QString &fromName, const QString &fromEmail, + const QString &to,const QString &subject, + const QString &body,bool bcc=false, + const QString &recipient = QString::null); + + signals: + void status( const QString &message ); + void finished(); + + private slots: + void smtpSuccess(); + void smtpError(const QString &command, const QString &response); + + private: + int kMailOpenComposer(const QString& arg0,const QString& arg1, + const QString& arg2,const QString& arg3, + const QString& arg4,int arg5,const KURL& arg6); + + MailClient m_client; + QString m_smtpServer; +}; + +#endif diff --git a/kbugbuster/backend/package.cpp b/kbugbuster/backend/package.cpp new file mode 100644 index 00000000..ae009397 --- /dev/null +++ b/kbugbuster/backend/package.cpp @@ -0,0 +1,82 @@ + +#include "package.h" + +#include "packageimpl.h" + +Package::Package() +{ +} + +Package::Package( PackageImpl *impl ) + : m_impl( impl ) +{ +} + +Package::Package( const Package &other ) +{ + (*this) = other; +} + +Package &Package::operator=( const Package &rhs ) +{ + m_impl = rhs.m_impl; + return *this; +} + +Package::~Package() +{ +} + +QString Package::name() const +{ + if ( !m_impl ) + return QString::null; + + return m_impl->name; +} + +QString Package::description() const +{ + if ( !m_impl ) + return QString::null; + + return m_impl->description; +} + +uint Package::numberOfBugs() const +{ + if ( !m_impl ) + return 0; + + return m_impl->numberOfBugs; +} + +Person Package::maintainer() const +{ + if ( !m_impl ) + return Person(); + + return m_impl->maintainer; +} + +const QStringList Package::components() const +{ + if ( !m_impl ) + return QStringList(); + + return m_impl->components; +} + +bool Package::operator==( const Package &rhs ) +{ + return m_impl == rhs.m_impl; +} + +bool Package::operator<( const Package &rhs ) const +{ + return m_impl < rhs.m_impl; +} + +/** + * vim:ts=4:sw=4:et + */ diff --git a/kbugbuster/backend/package.h b/kbugbuster/backend/package.h new file mode 100644 index 00000000..7b5c69a1 --- /dev/null +++ b/kbugbuster/backend/package.h @@ -0,0 +1,43 @@ +#ifndef __package_h__ +#define __package_h__ + +#include "person.h" + +#include <qvaluelist.h> + +#include <ksharedptr.h> + +class PackageImpl; + +class Package +{ +public: + typedef QValueList<Package> List; + + Package(); + Package( PackageImpl *impl ); + Package( const Package &other ); + Package &operator=( const Package &rhs ); + ~Package(); + + QString name() const; + QString description() const; + uint numberOfBugs() const; + Person maintainer() const; + const QStringList components() const; + + bool isNull() const { return m_impl == 0; } + + PackageImpl *impl() const { return m_impl; } + + bool operator==( const Package &rhs ); + bool operator<( const Package &rhs ) const; + +private: + KSharedPtr<PackageImpl> m_impl; +}; + +#endif + +/* vim: set sw=4 ts=4 et softtabstop=4: */ + diff --git a/kbugbuster/backend/packageimpl.h b/kbugbuster/backend/packageimpl.h new file mode 100644 index 00000000..c60a1079 --- /dev/null +++ b/kbugbuster/backend/packageimpl.h @@ -0,0 +1,31 @@ +#ifndef __packageimpl_h__ +#define __packageimpl_h__ + +#include "person.h" + +#include <qstringlist.h> +#include <kurl.h> +#include <ksharedptr.h> + +struct PackageImpl : public KShared +{ +public: + PackageImpl( const QString &_name, const QString &_description, + uint _numberOfBugs, const Person &_maintainer, + const QStringList &_components ) + : name( _name ), description( _description ),numberOfBugs( _numberOfBugs ), + maintainer( _maintainer ), components(_components) + {} + + QString name; + QString description; + uint numberOfBugs; + Person maintainer; + QStringList components; +}; + +#endif + +/* + * vim:sw=4:ts=4:et + */ diff --git a/kbugbuster/backend/packagelistjob.cpp b/kbugbuster/backend/packagelistjob.cpp new file mode 100644 index 00000000..6b05badf --- /dev/null +++ b/kbugbuster/backend/packagelistjob.cpp @@ -0,0 +1,68 @@ +/* + This file is part of KBugBuster. + + Copyright (c) 2002,2003 Cornelius Schumacher <schumacher@kde.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. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "packagelistjob.h" +#include "package.h" +#include "packageimpl.h" +#include "bugserver.h" +#include "domprocessor.h" +#include "htmlparser.h" + +#include <kdebug.h> +#include <assert.h> + +#include <qdom.h> +#include <qbuffer.h> + +PackageListJob::PackageListJob( BugServer *server ) + : BugJob( server ) +{ +} + +PackageListJob::~PackageListJob() +{ +} + +void PackageListJob::start() +{ + BugJob::start( server()->packageListUrl() ); +} + +void PackageListJob::process( const QByteArray &data ) +{ + Package::List packages; + KBB::Error err = server()->processor()->parsePackageList( data, packages ); + if ( err ) { + emit error( err.message() ); + } else { + emit packageListAvailable( packages ); + } +} + + +#include "packagelistjob.moc" + +/* + * vim:sw=4:ts=4:et + */ diff --git a/kbugbuster/backend/packagelistjob.h b/kbugbuster/backend/packagelistjob.h new file mode 100644 index 00000000..0e1bca99 --- /dev/null +++ b/kbugbuster/backend/packagelistjob.h @@ -0,0 +1,55 @@ +/* + This file is part of KBugBuster. + + Copyright (c) 2002,2003 Cornelius Schumacher <schumacher@kde.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. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef __packagelistjob_h__ +#define __packagelistjob_h__ + +#include "bugjob.h" + +#include "package.h" + +#include <qdom.h> + +class PackageListJob : public BugJob +{ + Q_OBJECT + public: + PackageListJob( BugServer * ); + virtual ~PackageListJob(); + + void start(); + + protected: + void process( const QByteArray &data ); + + signals: + void packageListAvailable( const Package::List &pkgs ); +}; + + +#endif + +/* + * vim:ts=4:sw=4:et + */ diff --git a/kbugbuster/backend/person.cpp b/kbugbuster/backend/person.cpp new file mode 100644 index 00000000..a9f63be0 --- /dev/null +++ b/kbugbuster/backend/person.cpp @@ -0,0 +1,74 @@ +#include <kdebug.h> + +#include "person.h" + +Person::Person( const QString &fullName ) +{ + int emailPos = fullName.find( '<' ); + if ( emailPos < 0 ) { + email = fullName; + } else { + email = fullName.mid( emailPos + 1, fullName.length() - 1 ); + name = fullName.left( emailPos - 1 ); + } +} + +QString Person::fullName(bool html) const +{ + if( name.isEmpty() ) + { + if( email.isEmpty() ) + return i18n( "Unknown" ); + else + return email; + } + else + { + if( email.isEmpty() ) + return name; + else + if ( html ) { + return name + " <" + email + ">"; + } else { + return name + " <" + email + ">"; + } + } +} + +Person Person::parseFromString( const QString &_str ) +{ + Person res; + + QString str = _str; + + int ltPos = str.find( '<' ); + if ( ltPos != -1 ) + { + int gtPos = str.find( '>', ltPos ); + if ( gtPos != -1 ) + { + res.name = str.left( ltPos - 1 ); + str = str.mid( ltPos + 1, gtPos - ( ltPos + 1 ) ); + } + } + + int atPos = str.find( '@' ); + int spacedAtPos = str.find( QString::fromLatin1( " at " ) ); + if ( atPos == -1 && spacedAtPos != -1 ) + str.replace( spacedAtPos, 4, QString::fromLatin1( "@" ) ); + + int spacePos = str.find( ' ' ); + while ( spacePos != -1 ) + { + str[ spacePos ] = '.'; + spacePos = str.find( ' ', spacePos ); + } + + res.email = str; + + return res; +} + +/** + * vim:et:ts=4:sw=4 + */ diff --git a/kbugbuster/backend/person.h b/kbugbuster/backend/person.h new file mode 100644 index 00000000..f29074f9 --- /dev/null +++ b/kbugbuster/backend/person.h @@ -0,0 +1,26 @@ +#ifndef __person_h__ +#define __person_h__ + +#include <qstring.h> +#include <klocale.h> + +struct Person +{ + Person() {} + Person( const QString &fullName ); + Person( const QString &_name, const QString &_email ) + : name( _name ), email( _email ) {} + + QString name; + QString email; + + QString fullName( bool html = false ) const; + + static Person parseFromString( const QString &str ); +}; + +#endif + +/* + * vim:sw=4:ts=4:et + */ diff --git a/kbugbuster/backend/processor.cpp b/kbugbuster/backend/processor.cpp new file mode 100644 index 00000000..332b4418 --- /dev/null +++ b/kbugbuster/backend/processor.cpp @@ -0,0 +1,78 @@ +/* + This file is part of KBugBuster. + + Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.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. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include <qstylesheet.h> + +#include <kdebug.h> +#include <kmdcodec.h> + +#include "processor.h" + +#include "bugserver.h" +#include "packageimpl.h" +#include "bugimpl.h" +#include "bugdetailsimpl.h" +#include "kbbprefs.h" + +Processor::Processor( BugServer *server ) + : mServer( server ) +{ +} + +Processor::~Processor() +{ +} + +void Processor::setPackageListQuery( KURL &url ) +{ + url.setFileName( "xml.cgi" ); + url.setQuery( "?data=versiontable" ); +} + +void Processor::setBugListQuery( KURL &url, const Package &product, const QString &component ) +{ + if ( mServer->serverConfig().bugzillaVersion() == "Bugworld" ) { + url.setFileName( "bugworld.cgi" ); + } else { + url.setFileName( "xmlquery.cgi" ); + } + + QString user = mServer->serverConfig().user(); + + if ( component.isEmpty() ) + url.setQuery( "?user=" + user + "&product=" + product.name() ); + else + url.setQuery( "?user=" + user + "&product=" + product.name() + "&component=" + component ); +} + +void Processor::setBugDetailsQuery( KURL &url, const Bug &bug ) +{ + url.setFileName( "xml.cgi" ); + url.setQuery( "?id=" + bug.number() ); +} + + +/* + * vim:sw=4:ts=4:et + */ diff --git a/kbugbuster/backend/processor.h b/kbugbuster/backend/processor.h new file mode 100644 index 00000000..cadaa407 --- /dev/null +++ b/kbugbuster/backend/processor.h @@ -0,0 +1,63 @@ +/* + This file is part of KBugBuster. + + Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.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. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ +#ifndef KBB_PROCESSOR_H +#define KBB_PROCESSOR_H + +#include "bug.h" +#include "bugdetails.h" +#include "package.h" +#include "error.h" + +#include <kurl.h> + +class BugServer; + +class Processor +{ + public: + Processor( BugServer * ); + virtual ~Processor(); + + BugServer *server() const { return mServer; } + + virtual KBB::Error parseBugList( const QByteArray &data, + Bug::List &bugs ) = 0; + virtual KBB::Error parsePackageList( const QByteArray &data, + Package::List &packages ) = 0; + virtual KBB::Error parseBugDetails( const QByteArray &, BugDetails & ) = 0; + + virtual void setPackageListQuery( KURL & ) = 0; + virtual void setBugListQuery( KURL &, const Package &, + const QString &component ) = 0; + virtual void setBugDetailsQuery( KURL &, const Bug & ) = 0; + + private: + BugServer *mServer; +}; + +#endif + +/* + * vim:sw=4:ts=4:et + */ diff --git a/kbugbuster/backend/rdfprocessor.cpp b/kbugbuster/backend/rdfprocessor.cpp new file mode 100644 index 00000000..89bb3402 --- /dev/null +++ b/kbugbuster/backend/rdfprocessor.cpp @@ -0,0 +1,107 @@ +/* + This file is part of KBugBuster. + Copyright (c) 2002 Cornelius Schumacher <schumacher@kde.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. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "rdfprocessor.h" + +#include "bugserver.h" +#include "packageimpl.h" +#include "bugimpl.h" +#include "bugdetailsimpl.h" +#include "kbbprefs.h" + +#include "kdebug.h" + +RdfProcessor::RdfProcessor( BugServer *server ) + : DomProcessor( server ) +{ +} + +RdfProcessor::~RdfProcessor() +{ +} + +KBB::Error RdfProcessor::parseDomBugList( const QDomElement &element, + Bug::List &bugs ) +{ + if ( element.tagName() != "RDF" ) { + kdDebug() << "RdfProcessor::parseBugList(): no RDF element." << endl; + return KBB::Error( "No RDF element found" ); + } + + QDomNodeList bugNodes = element.elementsByTagName( "bz:bug" ); + + for( uint i = 0; i < bugNodes.count(); ++i ) { + QString title; + Person submitter; + QString bugNr; + Bug::Status status = Bug::StatusUndefined; + Bug::Severity severity = Bug::SeverityUndefined; + Person developerTodo; + Bug::BugMergeList mergedList; + + QDomNode hit = bugNodes.item( i ); + + QDomNode n; + for( n = hit.firstChild(); !n.isNull(); n = n.nextSibling() ) { + QDomElement e = n.toElement(); + + if ( e.tagName() == "bz:id" ) { + bugNr = e.text(); + } else if ( e.tagName() == "bz:status" ) { + status = server()->bugStatus( e.text() ); + } else if ( e.tagName() == "bz:severity" ) { + severity = server()->bugSeverity( e.text() ); + } else if ( e.tagName() == "bz:summary" ) { + title = e.text(); + } + } + + Bug bug( new BugImpl( title, submitter, bugNr, 0xFFFFFFFF, severity, + developerTodo, status, mergedList ) ); + + if ( !bug.isNull() ) { + bugs.append( bug ); + } + } + + return KBB::Error(); +} + +void RdfProcessor::setBugListQuery( KURL &url, const Package &product, const QString &component ) +{ + url.setFileName( "buglist.cgi" ); + if ( component.isEmpty() ) + url.setQuery( "?format=rdf&product=" + product.name() ); + else + url.setQuery( "?format=rdf&product=" + product.name() + "&component=" + component ); + if ( KBBPrefs::instance()->mShowVoted ) { + url.addQueryItem( "field0-0-0", "votes" ); + url.addQueryItem( "type0-0-0", "greaterthan" ); + QString num = QString::number( KBBPrefs::instance()->mMinVotes );; + url.addQueryItem( "value0-0-0", num ); + } +} + +/* + * vim:sw=4:ts=4:et + */ diff --git a/kbugbuster/backend/rdfprocessor.h b/kbugbuster/backend/rdfprocessor.h new file mode 100644 index 00000000..0e0bd780 --- /dev/null +++ b/kbugbuster/backend/rdfprocessor.h @@ -0,0 +1,43 @@ +/* + This file is part of KBugBuster. + Copyright (c) 2002 Cornelius Schumacher <schumacher@kde.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. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ +#ifndef RDFPROCESSOR_H +#define RDFPROCESSOR_H + +#include "domprocessor.h" + +class RdfProcessor : public DomProcessor +{ + public: + RdfProcessor( BugServer * ); + virtual ~RdfProcessor(); + + KBB::Error parseDomBugList( const QDomElement &, Bug::List & ); + + void setBugListQuery( KURL &, const Package &, const QString &component ); +}; + +#endif + +/* + * vim:sw=4:ts=4:et + */ diff --git a/kbugbuster/backend/smtp.cpp b/kbugbuster/backend/smtp.cpp new file mode 100644 index 00000000..d54cafab --- /dev/null +++ b/kbugbuster/backend/smtp.cpp @@ -0,0 +1,181 @@ +/**************************************************************************** +** +** This file is a modified version of part of an example program for Qt. +** This file may be used, distributed and modified without limitation. +** +** Don Sanders <sanders@kde.org> +** +*****************************************************************************/ + +#include "smtp.h" + +#include <qtextstream.h> +#include <qsocket.h> +#include <qtimer.h> +#include <kapplication.h> +#include <kmessagebox.h> +#include <klocale.h> + +Smtp::Smtp( const QString &from, const QStringList &to, + const QString &aMessage, + const QString &server, + unsigned short int port ) +{ + skipReadResponse = false; + mSocket = new QSocket( this ); + connect ( mSocket, SIGNAL( readyRead() ), + this, SLOT( readyRead() ) ); + connect ( mSocket, SIGNAL( connected() ), + this, SLOT( connected() ) ); + connect ( mSocket, SIGNAL( error(int) ), + this, SLOT( socketError(int) ) ); + + message = aMessage; + + this->from = from; + rcpt = to; + state = smtpInit; + command = ""; + + emit status( i18n( "Connecting to %1" ).arg( server ) ); + + mSocket->connectToHost( server, port ); + t = new QTextStream( mSocket ); + t->setEncoding(QTextStream::Latin1); +} + + +Smtp::~Smtp() +{ + if (t) + delete t; + if (mSocket) + delete mSocket; +} + + +void Smtp::send( const QString &from, const QStringList &to, + const QString &aMessage ) +{ + skipReadResponse = true; + message = aMessage; + this->from = from; + rcpt = to; + + state = smtpMail; + command = ""; + readyRead(); +} + + +void Smtp::quit() +{ + skipReadResponse = true; + state = smtpQuit; + command = ""; + readyRead(); +} + + +void Smtp::connected() +{ + emit status( i18n( "Connected to %1" ).arg( mSocket->peerName() ) ); +} + +void Smtp::socketError(int errorCode) +{ + command = "CONNECT"; + switch ( errorCode ) { + case QSocket::ErrConnectionRefused: + responseLine = i18n( "Connection refused." ); + break; + case QSocket::ErrHostNotFound: + responseLine = i18n( "Host Not Found." ); + break; + case QSocket::ErrSocketRead: + responseLine = i18n( "Error reading socket." ); + break; + default: + responseLine = i18n( "Internal error, unrecognized error." ); + } + QTimer::singleShot( 0, this, SLOT(emitError()) ); +} + +void Smtp::emitError() { + error( command, responseLine ); +} + +void Smtp::readyRead() +{ + if (!skipReadResponse) { + // SMTP is line-oriented + if ( !mSocket->canReadLine() ) + return; + + do { + responseLine = mSocket->readLine(); + response += responseLine; + } while( mSocket->canReadLine() && responseLine[3] != ' ' ); + } + skipReadResponse = false; + + if ( state == smtpInit && responseLine[0] == '2' ) { + // banner was okay, let's go on + command = "HELO there"; + *t << "HELO there\r\n"; + state = smtpMail; + } else if ( state == smtpMail && responseLine[0] == '2' ) { + // HELO response was okay (well, it has to be) + command = "MAIL"; + *t << "MAIL FROM: <" << from << ">\r\n"; + state = smtpRcpt; + } else if ( state == smtpRcpt && responseLine[0] == '2' && (rcpt.begin() != rcpt.end())) { + command = "RCPT"; + *t << "RCPT TO: <" << *(rcpt.begin()) << ">\r\n"; + rcpt.remove( rcpt.begin() ); + if (rcpt.begin() == rcpt.end()) + state = smtpData; + } else if ( state == smtpData && responseLine[0] == '2' ) { + command = "DATA"; + *t << "DATA\r\n"; + state = smtpBody; + } else if ( state == smtpBody && responseLine[0] == '3' ) { + command = "DATA"; + QString seperator = ""; + if (message[message.length() - 1] != '\n') + seperator = "\r\n"; + *t << message << seperator << ".\r\n"; + state = smtpSuccess; + } else if ( state == smtpSuccess && responseLine[0] == '2' ) { + QTimer::singleShot( 0, this, SIGNAL(success()) ); + } else if ( state == smtpQuit && responseLine[0] == '2' ) { + command = "QUIT"; + *t << "QUIT\r\n"; + // here, we just close. + state = smtpClose; + emit status( i18n( "Message sent" ) ); + } else if ( state == smtpClose ) { + // we ignore it + } else { // error occurred + QTimer::singleShot( 0, this, SLOT(emitError()) ); + state = smtpClose; + } + + response = ""; + + if ( state == smtpClose ) { + delete t; + t = 0; + delete mSocket; + mSocket = 0; + QTimer::singleShot( 0, this, SLOT(deleteMe()) ); + } +} + + +void Smtp::deleteMe() +{ + delete this; +} + +#include "smtp.moc" diff --git a/kbugbuster/backend/smtp.h b/kbugbuster/backend/smtp.h new file mode 100644 index 00000000..d800cb77 --- /dev/null +++ b/kbugbuster/backend/smtp.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** This file is a modified version of part of an example program for Qt. +** This file may be used, distributed and modified without limitation. +** +** Don Sanders <sanders@kde.org> +** +*****************************************************************************/ + +#ifndef SMTP_H +#define SMTP_H + +#include <qobject.h> +#include <qstring.h> +#include <qstringlist.h> + +class QSocket; +class QTextStream; + +class Smtp : public QObject +{ + Q_OBJECT + +public: + Smtp( const QString &from, const QStringList &to, const QString &message, + const QString &server, unsigned short int port = 25 ); + ~Smtp(); + void send( const QString &, const QStringList &, const QString & ); + void quit(); + + +signals: + void success(); + void status( const QString & ); + void error( const QString &command, const QString &response ); + +private slots: + void readyRead(); + void connected(); + void deleteMe(); + void socketError(int err); + void emitError(); + +private: + enum State { + smtpInit, + smtpMail, + smtpRcpt, + smtpData, + smtpBody, + smtpSuccess, + smtpQuit, + smtpClose + }; + + QString message; + QString from; + QStringList rcpt; + QSocket *mSocket; + QTextStream * t; + int state; + QString response, responseLine; + bool skipReadResponse; + QString command; +}; + +#endif diff --git a/kbugbuster/configure.in.in b/kbugbuster/configure.in.in new file mode 100644 index 00000000..92b239ee --- /dev/null +++ b/kbugbuster/configure.in.in @@ -0,0 +1,8 @@ +AC_DEFUN([KBUGBUSTER_CHECK_KCAL],[HAVE_KCAL=0 +KDE_CHECK_HEADER(libkcal/resourcecalendar.h,HAVE_KCAL=1, + AC_MSG_WARN([Unable to find libkcal. The Bugzilla todo list \ + resource for KOrganizer won't be compiled.])) +AM_CONDITIONAL(include_kcalresource, test "$HAVE_KCAL" = 1) +]) + +KBUGBUSTER_CHECK_KCAL diff --git a/kbugbuster/gui/Makefile.am b/kbugbuster/gui/Makefile.am new file mode 100644 index 00000000..f1a13327 --- /dev/null +++ b/kbugbuster/gui/Makefile.am @@ -0,0 +1,24 @@ +INCLUDES= -I$(top_srcdir)/kbugbuster/backend -I$(top_srcdir)/kbugbuster/ $(all_includes) + +noinst_LTLIBRARIES = libkbbmainwindow.la + +libkbbmainwindow_la_SOURCES = packagelvi.cpp buglvi.cpp cwloadingwidget.cpp \ + cwsearchwidget_base.ui cwsearchwidget.cpp \ + cwbugdetailscontainer_base.ui \ + cwbugdetailscontainer.cpp \ + cwbuglistcontainer.cpp \ + cwbugdetails.cpp \ + centralwidget_base.ui centralwidget.cpp \ + kbbmainwindow.cpp msginputdialog.cpp \ + packageselectdialog.cpp messageeditor.cpp \ + severityselectdialog.cpp \ + preferencesdialog.cpp loadallbugsdlg.cpp \ + serverconfigdialog.cpp + +METASOURCES = AUTO + +EXTRA_DIST = kbugbusterui.rc + +rcdir = $(kde_datadir)/kbugbuster +rc_DATA = kbugbusterui.rc + diff --git a/kbugbuster/gui/README b/kbugbuster/gui/README new file mode 100644 index 00000000..54e5fa67 --- /dev/null +++ b/kbugbuster/gui/README @@ -0,0 +1,21 @@ +Normal classes: +=============== +kbbmainwindow.* KBugBuster's main window +buglvi.* QListViewItem representing a bug +packagelvi.* QListViewItem representing a package +cwloadingwidget.* The temporary widget shown initially, while loading + +msginputdialog.* The dialog for entering a message for closing the bug +messageeditor.* The dialog for editing the custom messages, associated with buttons +packageselectdialog.* Dialog for selecting a package +severityselectdialog.* Dialog for selecting a severity + +Widgets, including a Qt designer .ui file: +========================================== +centralwidget* The main widget of kbbmainwindow +cwbuglistcontainer* The widget containing the bug list (top of central widget) +cwbugdetailscontainer* The widget containing the bug details + the buttons in its right (bottom of central widget). +cwbugdetails* The widget showing details of a bug report, using KHTMLPart +cwsearchwidget* A future search widget on the left of the bug details. + Same functionality is in the menus, this would take too much screen space IMHO (DF) +preferencesdialog* Preferences dialog diff --git a/kbugbuster/gui/buglvi.cpp b/kbugbuster/gui/buglvi.cpp new file mode 100644 index 00000000..be629510 --- /dev/null +++ b/kbugbuster/gui/buglvi.cpp @@ -0,0 +1,109 @@ +/* + buglvi.cpp - Custom QListViewItem that holds a Bug object + + copyright : (c) 2001 by Martijn Klingens + email : klingens@kde.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. * + * * + ************************************************************************* +*/ + +#include <qfont.h> +#include <qpainter.h> + +#include <kstringhandler.h> +#include <kdebug.h> + +#include "bugsystem.h" +#include "bugserver.h" + +#include "buglvi.h" + +using namespace KBugBusterMainWindow; + +BugLVI::BugLVI( KListView *parent , const Bug &bug ) +: KListViewItem( parent, bug.number() + " ", + i18n( "1 day", "%n days", bug.age() ), + bug.title(), //KStringHandler::csqueeze( bug.title(), 70 ), + Bug::statusLabel( bug.status() ), + Bug::severityLabel( bug.severity() ), + bug.submitter().fullName() ) +{ + m_bug = bug; + + bool hasCommands = BugSystem::self()->server()->hasCommandsFor( bug ); + mCommandState = hasCommands ? BugCommand::Queued : BugCommand::None; + + if ( bug.age() == 0xFFFFFFFF ) + setText( 1, i18n( "Unknown" ) ); + + Person developer = bug.developerTODO(); + if ( !developer.name.isEmpty() ) + setText( 3, i18n( "%1 (%2)" ).arg( Bug::statusLabel( bug.status() ), developer.name ) ); +} + +BugLVI::~BugLVI() +{ +} + +QString BugLVI::key( int column, bool /* ascending */ ) const +{ + QString key; + + if ( column == 0 ) + { + key = text( 0 ).rightJustify( 10, '0' ); + } + else if ( column == 1 ) + { + if ( m_bug.age() == 0xFFFFFFFF ) + key = "0"; + else + key = QString::number( m_bug.age() ).rightJustify( 10, '0' ); + } + else if ( column == 4 ) + { + key = QString::number( 10 - m_bug.severity() ); + key += m_bug.number().rightJustify( 10, '0' ); + } + else + { + key = text( column ); + } + + return key; +} + +void BugLVI::paintCell(QPainter* p, const QColorGroup& cg, + int column, int width, int align) +{ + QColorGroup newCg = cg; + if ( mCommandState == BugCommand::Queued ) { + QFont font = p->font(); + font.setBold( true ); + p->setFont( font ); + } else if ( mCommandState == BugCommand::Sent ) { + QFont font = p->font(); + font.setItalic( true ); + p->setFont( font ); + } else if ( m_bug.status() == Bug::Closed ) { + // Different color for closed bugs + newCg.setColor( QColorGroup::Text, cg.color( QColorGroup::Dark ) ); + } + + KListViewItem::paintCell( p, newCg, column, width, align ); +} + +void BugLVI::setCommandState( BugCommand::State state) +{ + mCommandState = state; +} + +// vim: set et ts=4 sw=4 sts=4: + diff --git a/kbugbuster/gui/buglvi.h b/kbugbuster/gui/buglvi.h new file mode 100644 index 00000000..ff8fa7f9 --- /dev/null +++ b/kbugbuster/gui/buglvi.h @@ -0,0 +1,57 @@ +/* + buglvi.h - Custom QListViewItem that holds a Bug object + + copyright : (c) 2001 by Martijn Klingens + email : klingens@kde.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. * + * * + ************************************************************************* +*/ + +#ifndef KBBMAINWINDOW_BUGLVI_H +#define KBBMAINWINDOW_BUGLVI_H + +#include <klistview.h> + +#include "bug.h" +#include "bugcommand.h" + +namespace KBugBusterMainWindow +{ + +/** + * @author Martijn Klingens + */ +class BugLVI : public KListViewItem +{ +public: + BugLVI( KListView *parent , const Bug &bug ); + ~BugLVI(); + + Bug& bug() { return m_bug; } + void setBug( Bug &bug ) { m_bug = bug; } + + QString key ( int column, bool ascending ) const; + + void setCommandState( BugCommand::State state ); + + void paintCell(QPainter* p, const QColorGroup& cg, + int column, int width, int align); + +private: + Bug m_bug; + BugCommand::State mCommandState; +}; + +} // namespace + +#endif // KBBMAINWINDOW_BUGLVI_H + +/* vim: set et ts=4 softtabstop=4 sw=4: */ + diff --git a/kbugbuster/gui/centralwidget.cpp b/kbugbuster/gui/centralwidget.cpp new file mode 100644 index 00000000..80bd0672 --- /dev/null +++ b/kbugbuster/gui/centralwidget.cpp @@ -0,0 +1,507 @@ +/* + centralwidget.cpp - Central widget for the KBB main window + + copyright : (c) 2001 by Martijn Klingens + email : klingens@kde.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. * + * * + ************************************************************************* +*/ + +#include <qsplitter.h> +#include <qpushbutton.h> +#include <qwidgetstack.h> +#include <qlayout.h> + +#include <kdialog.h> +#include <kdebug.h> +#include <kcombobox.h> +#include <klistview.h> +#include <kinputdialog.h> + +#include "kbbprefs.h" +#include "bugsystem.h" +#include "packagelvi.h" +#include "buglvi.h" +#include "msginputdialog.h" +#include "packageselectdialog.h" +#include "cwbugdetails.h" +#include "bugcommand.h" +#include "severityselectdialog.h" +#include "cwsearchwidget.h" +#include "cwbuglistcontainer.h" +#include "cwbugdetailscontainer.h" +#include "bugserver.h" + +#include "centralwidget.h" +#include <kfiledialog.h> +#include <kmessagebox.h> +#include "loadallbugsdlg.h" + +using namespace KBugBusterMainWindow; + +CentralWidget::CentralWidget( const QCString &initialPackage, + const QCString &initialComponent, + const QCString &initialBug, QWidget *parent, + const char * name ) + : QWidget( parent, name ) +{ + // Master layout + ( new QVBoxLayout( this, 0, + KDialog::spacingHint() ) )->setAutoAdd( true ); + + // Create QSplitter children + m_vertSplitter = new QSplitter( QSplitter::Vertical, this ); + m_listPane = new CWBugListContainer( m_vertSplitter ); + m_horSplitter = new QSplitter( QSplitter::Horizontal,m_vertSplitter ); +// The search pane isn't used. Should we remove the code? + m_searchPane = new CWSearchWidget( m_horSplitter ); + m_bugPane = new CWBugDetailsContainer( m_horSplitter ); + + m_searchPane->hide(); +// m_listPane->hide(); + + m_searchPane->setSizePolicy( QSizePolicy( QSizePolicy::Minimum, + QSizePolicy::Minimum ) ); + m_horSplitter->setResizeMode( m_searchPane, QSplitter::FollowSizeHint ); + + connect( m_listPane, SIGNAL( resetProgressBar() ), + SIGNAL( resetProgressBar() ) ); + connect( m_bugPane, SIGNAL( resetProgressBar() ), + SIGNAL( resetProgressBar() ) ); + + // Start the proper jobs for loading the package lists + connect( BugSystem::self(), + SIGNAL( packageListAvailable( const Package::List & ) ), + SLOT( updatePackageList( const Package::List & ) ) ); + connect( BugSystem::self(), + SIGNAL( bugListAvailable( const Package &, const QString &, const Bug::List & ) ), + SLOT( updateBugList( const Package &, const QString &, const Bug::List & ) ) ); + connect( BugSystem::self(), + SIGNAL( bugListAvailable( const QString &, const Bug::List & ) ), + SLOT( updateBugList( const QString &, const Bug::List & ) ) ); + connect( BugSystem::self(), + SIGNAL( bugDetailsAvailable( const Bug &, const BugDetails & ) ), + SLOT( updateBugDetails( const Bug &, const BugDetails & ) ) ); + + connect( BugSystem::self(), SIGNAL( loadingError( const QString & ) ), + SLOT( showLoadingError( const QString & ) ) ); + + connect( m_bugPane, SIGNAL( signalCloseBug() ), SLOT( closeBug() ) ); + connect( m_bugPane, SIGNAL( signalCloseBugSilently() ), SLOT( closeBugSilently() ) ); + connect( m_bugPane, SIGNAL( signalReopenBug() ), SLOT( reopenBug() ) ); + connect( m_bugPane, SIGNAL( signalReassignBug() ), SLOT( reassignBug() ) ); + connect( m_bugPane, SIGNAL( signalTitleBug() ), SLOT( titleBug() ) ); + connect( m_bugPane, SIGNAL( signalSeverityBug() ), SLOT( severityBug() ) ); + connect( m_bugPane, SIGNAL( signalReplyBug() ), SLOT( replyBug() ) ); + connect( m_bugPane, SIGNAL( signalReplyPrivateBug() ), SLOT( replyPrivateBug() ) ); + + connect( m_bugPane, SIGNAL( signalClearCommand() ), SLOT( clearCommand() ) ); + + // Add the selection slots for the listviews + connect( m_searchPane->m_searchPackages, + SIGNAL( activated( const QString & ) ), + SLOT( slotRetrieveBugList( const QString & ) ) ); + + connect( m_listPane, SIGNAL( executed( const Bug & ) ), + SLOT( slotRetrieveBugDetails( const Bug & ) ) ); + connect( m_listPane, SIGNAL( currentChanged( const Bug & ) ), + SLOT( slotSetActiveBug( const Bug & ) ) ); + + connect( m_listPane, SIGNAL( searchPackage() ), SIGNAL( searchPackage() ) ); + connect( m_bugPane, SIGNAL( searchBugNumber() ), SIGNAL( searchBugNumber() ) ); + + m_bLoadingAllBugs = false; + + initialize( initialPackage, initialComponent, initialBug ); +} + +CentralWidget::~CentralWidget() +{ +// kdDebug() << "CentralWidget::~CentralWidget()" << endl; +} + +void CentralWidget::initialize( const QString& p, const QString &c, const QString& b ) +{ +// kdDebug() << "CentralWidget::initialize(): package: '" << p +// << "' bug: '" << b << "'" << endl; + + BugServerConfig cfg = BugSystem::self()->server()->serverConfig(); + QString package = p.isEmpty() ? cfg.currentPackage() : p; + QString bug = b.isEmpty() ? cfg.currentBug() : b; + QString component = c.isEmpty() ? cfg.currentComponent() : c; + + m_listPane->setNoList(); + m_bugPane->setNoBug(); + + BugSystem::self()->retrievePackageList(); + if ( !package.isEmpty() ) { + m_currentPackage = BugSystem::self()->package( package ); + m_currentComponent = component; + BugSystem::self()->retrieveBugList( m_currentPackage, m_currentComponent ); + + if ( !bug.isEmpty() ) { + m_currentBug = BugSystem::self()->bug( m_currentPackage, + m_currentComponent, bug ); + BugSystem::self()->retrieveBugDetails( m_currentBug ); + } + } else { + if ( !bug.isEmpty() ) { + // ### bad way to instanciating a bug object! doesn't restore details! + m_currentBug = Bug::fromNumber( bug ); // bug number specified on cmdline. Is it a problem that we don't have details ? + BugSystem::self()->retrieveBugDetails( m_currentBug ); + } + } +} + +void CentralWidget::readConfig() +{ + m_horSplitter->setSizes( KBBPrefs::instance()->mSplitter2 ); + m_vertSplitter->setSizes( KBBPrefs::instance()->mSplitter1 ); +} + +void CentralWidget::writeConfig() +{ +#if 0 + kdDebug() << "m_vertSplitter" << endl; + QValueList<int> sizes = m_vertSplitter->sizes(); + QValueList<int>::ConstIterator it; + for( it = sizes.begin(); it != sizes.end(); ++it ) { + kdDebug() << " " << (*it) << endl; + } +#endif + + KBBPrefs::instance()->mSplitter1 = m_vertSplitter->sizes(); + KBBPrefs::instance()->mSplitter2 = m_horSplitter->sizes(); + + BugServer *server = BugSystem::self()->server(); + server->serverConfig().setCurrentPackage( m_currentPackage.name() ); + server->serverConfig().setCurrentComponent( m_currentComponent ); + server->serverConfig().setCurrentBug( m_currentBug.number() ); +} + +void CentralWidget::slotRetrieveBugList( const QString &package ) +{ + slotRetrieveBugList( package, QString::null ); +} + +void CentralWidget::slotRetrieveBugList( const QString &p, const QString &component ) +{ + if ( p.isEmpty() ) return; + + Package package = m_packageList[ p ]; + + if ( package.isNull() ) { + // Invalid package, return + return; + } + + if ( ( package == m_currentPackage ) && ( m_currentComponent == component ) ) { + return; // Nothing to do + } + + m_currentComponent = component; + m_currentPackage = package; + + BugSystem::self()->retrieveBugList( m_currentPackage, m_currentComponent ); +} + +QString CentralWidget::currentNumber() const +{ + if( m_currentBug.isNull() ) + return ""; + else + return m_currentBug.number(); +} + +QString CentralWidget::currentTitle() const +{ + if( m_currentBug.isNull() ) + return ""; + else + return m_currentBug.title(); +} + +void CentralWidget::slotRetrieveBugDetails( const Bug &bug ) +{ + if( m_currentBug == bug ) + return; // Nothing to do + + m_currentBug = bug; + BugSystem::self()->retrieveBugDetails( m_currentBug ); +} + +void CentralWidget::slotSetActiveBug( const Bug &bug ) +{ + if( bug.isNull() ) + { + return; + } + + if( m_activeBug == bug ) + return; // Nothing to do + + m_activeBug = bug; +} + +void CentralWidget::updatePackageList( const Package::List &pkgs ) +{ + // ### needs proper implementation ;-) + + m_searchPane->m_searchPackages->clear(); + m_searchPane->m_searchPackages->completionObject()->clear(); +// m_bugPane->m_bugDetails->m_bugPackage->clear(); + emit resetProgressBar(); + + Package::List::ConstIterator it = pkgs.begin(); + for ( ; it != pkgs.end(); ++it ) + { + m_packageList[ ( *it ).name() ] = *it; + m_searchPane->m_searchPackages->insertItem( ( *it ).name() ); + m_searchPane->m_searchPackages-> + completionObject()->addItem( ( *it ).name() ); +// m_bugPane->m_bugDetails->m_bugPackage->insertItem( ( *it ).name() ); + } + +/* + if( m_bugPane->m_bugStack->id( + m_bugPane->m_bugStack->visibleWidget() ) != 0 ) + { + m_bugPane->m_bugDetails->m_bugPackage->setCurrentItem( -1 ); + } +*/ +} + +void CentralWidget::updateBugList( const Package &pkg, const QString &component, const Bug::List &bugs ) +{ + m_listPane->setBugList( pkg, component, bugs ); +} + +void CentralWidget::updateBugList( const QString &label, const Bug::List &bugs ) +{ + m_listPane->setBugList( label, bugs ); +} + +void CentralWidget::updateBugDetails( const Bug &bug, const BugDetails &bd ) +{ + if ( !m_bLoadingAllBugs ) + m_bugPane->setBug( bug, bd ); +} + +void CentralWidget::slotReloadPackageList() +{ + BugSystem::self()->cache()->invalidatePackageList(); + BugSystem::self()->retrievePackageList(); +} + +void CentralWidget::slotReloadPackage() +{ + if (!m_currentPackage.isNull()) { + BugSystem::self()->cache()->invalidateBugList( m_currentPackage, m_currentComponent ); + BugSystem::self()->retrieveBugList( m_currentPackage, m_currentComponent ); + } +} + +void CentralWidget::slotLoadMyBugs() +{ + BugSystem::self()->retrieveMyBugsList(); +} + +void CentralWidget::slotReloadBug() +{ + if (!m_currentBug.isNull()) { + BugSystem::self()->cache()->invalidateBugDetails( m_currentBug ); + BugSystem::self()->retrieveBugDetails( m_currentBug ); + } +} + +void CentralWidget::updatePackage() +{ + if (!m_currentPackage.isNull()) { + BugSystem::self()->retrieveBugList( m_currentPackage, m_currentComponent ); + } +} + +void CentralWidget::slotExtractAttachments() +{ + if (!m_currentBug.isNull()) { + // Grab bug details (i.e. full-text) from cache, then extract attachments from it + BugDetails details = BugSystem::self()->cache()->loadBugDetails( m_currentBug ); + QValueList<BugDetails::Attachment> attachments = details.extractAttachments(); + if ( !attachments.isEmpty() ) + { + QStringList fileList; + for ( QValueList<BugDetails::Attachment>::Iterator it = attachments.begin() ; it != attachments.end() ; ++it ) + { + // Handle duplicates + if ( fileList.contains( (*it).filename ) ) + { + int n = 2; // looks stupid to have "blah" and "1-blah", start at 2 + QString fn = QString::number(n) + '-' + (*it).filename; + while ( fileList.contains( fn ) ) + { + ++n; + fn = QString::number(n) + '-' + (*it).filename; + } + (*it).filename = fn; + } + fileList += (*it).filename; + } + + int res = KMessageBox::questionYesNoList( this, + i18n("Found the following attachments. Save?"), + fileList, QString::null, KStdGuiItem::save(), KStdGuiItem::dontSave() ); + if ( res == KMessageBox::No ) + return; + QString dir = KFileDialog::getExistingDirectory( QString::null, this, i18n("Select Folder Where to Save Attachments") ); + if ( !dir.isEmpty() ) + { + if ( !dir.endsWith( "/" ) ) + dir += '/'; + for ( QValueList<BugDetails::Attachment>::Iterator it = attachments.begin() ; it != attachments.end() ; ++it ) + { + QString filename = m_currentBug.number() + '-' + (*it).filename; + QFile file( dir + filename ); + if ( file.open( IO_WriteOnly ) ) + file.writeBlock( (*it).contents ); + else + kdError() << "Couldn't save attachment to " << filename << endl; + file.close(); + } + } + } + } +} + +void CentralWidget::mergeBugs() +{ + QStringList bugNumbers = m_listPane->selectedBugs(); + if ( bugNumbers.count() >= 2 ) { + BugSystem::self()->queueCommand( + new BugCommandMerge( bugNumbers, m_currentPackage ) ); + } +} + +void CentralWidget::unmergeBugs() +{ + BugSystem::self()->queueCommand( + new BugCommandUnmerge( m_currentBug, m_currentPackage ) ); +} + +void CentralWidget::closeBug() +{ + MsgInputDialog *dlg = new MsgInputDialog( MsgInputDialog::Close, + m_currentBug, m_currentPackage, + m_bugPane->bugDetailsWidget()->selectedText(), this ); + dlg->show(); +} + +void CentralWidget::closeBugSilently() +{ + BugSystem::self()->queueCommand( + new BugCommandCloseSilently( m_currentBug, m_currentPackage ) ); +} + +void CentralWidget::reopenBug() +{ + BugSystem::self()->queueCommand( + new BugCommandReopen( m_currentBug, m_currentPackage ) ); +} + +void CentralWidget::reassignBug() +{ + PackageSelectDialog *dlg = new PackageSelectDialog( this ); + dlg->exec(); + + dlg->setPackages( BugSystem::self()->packageList() ); + BugServerConfig cfg = BugSystem::self()->server()->serverConfig(); + dlg->setRecentPackages( cfg.recentPackages() ); + + Package package = dlg->selectedPackage(); + + if ( package.isNull() ) { + return; + } + + BugSystem::self()->queueCommand( + new BugCommandReassign( m_currentBug, package.name(), m_currentPackage ) ); +} + +void CentralWidget::titleBug() +{ + bool ok = false; + QString title = KInputDialog::getText( i18n("Change Bug Title"), + i18n( "Please enter a new title:" ), + m_currentBug.title(), &ok, this ); + if ( ok && !title.isEmpty() ) { + BugSystem::self()->queueCommand( + new BugCommandRetitle( m_currentBug, title, m_currentPackage ) ); + } +} + +void CentralWidget::severityBug() +{ + SeveritySelectDialog *dlg = new SeveritySelectDialog( this ); + dlg->setSeverity( m_currentBug.severity() ); + int result = dlg->exec(); + if ( result == QDialog::Accepted ) { + BugSystem::self()->queueCommand( + new BugCommandSeverity( m_currentBug, + dlg->selectedSeverityAsString(), m_currentPackage ) ); + } +} + +void CentralWidget::replyBug() +{ + MsgInputDialog *dlg = new MsgInputDialog( MsgInputDialog::Reply, + m_currentBug, m_currentPackage, + m_bugPane->bugDetailsWidget()->selectedText(), this ); + dlg->show(); +} + +void CentralWidget::replyPrivateBug() +{ + MsgInputDialog *dlg = new MsgInputDialog( MsgInputDialog::ReplyPrivate, + m_currentBug, m_currentPackage, + m_bugPane->bugDetailsWidget()->selectedText(), this ); + dlg->show(); +} + +void CentralWidget::clearCommand() +{ + BugSystem::self()->clearCommands( m_currentBug.number() ); +} + +void CentralWidget::searchBugByTitle( int options, const QString& pattern ) +{ + m_listPane->searchBugByTitle( options, pattern ); +} + +void CentralWidget::slotRetrieveAllBugDetails() +{ + m_bLoadingAllBugs = true; + // Make a modal dialog to show the progress, and block usage of main window. + LoadAllBugsDlg dlg( m_currentPackage, m_currentComponent ); + dlg.exec(); + m_bLoadingAllBugs = false; +} + +void CentralWidget::showLoadingError( const QString &text ) +{ + KMessageBox::error( this, text ); +} + +CWBugDetails *CentralWidget::bugDetailsWidget() +{ + return m_bugPane->bugDetailsWidget(); +} + +#include "centralwidget.moc" + +/* vim: set et ts=4 sw=4 softtabstop=4: */ diff --git a/kbugbuster/gui/centralwidget.h b/kbugbuster/gui/centralwidget.h new file mode 100644 index 00000000..85b97eca --- /dev/null +++ b/kbugbuster/gui/centralwidget.h @@ -0,0 +1,142 @@ +/* + centralwidget.h - Central widget for the KBB main window + + copyright : (c) 2001 by Martijn Klingens + email : klingens@kde.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. * + * * + ************************************************************************* +*/ + +#ifndef KBBMAINWINDOW_CENTRALWIDGET_H +#define KBBMAINWINDOW_CENTRALWIDGET_H + +#include <qwidget.h> + +#include "package.h" +#include "bug.h" +#include "bugdetails.h" + +class QSplitter; +class QListViewItem; + +namespace KBugBusterMainWindow +{ + +class CWSearchWidget; +class CWBugListContainer; +class CWBugDetailsContainer; +class CWBugDetails; + +/** + * @author Martijn Klingens + */ +class CentralWidget : public QWidget +{ + Q_OBJECT + +public: + CentralWidget( const QCString &initialPackage, + const QCString &initalComponent,const QCString& initialBug, + QWidget* parent = 0, const char* name = 0 ); + ~CentralWidget(); + + void initialize( const QString &initialPackage = QString::null, + const QString &initalComponent = QString::null, + const QString &initialBug = QString::null ); + + void readConfig(); + void writeConfig(); + + void searchBugByTitle( int options, const QString& pattern ); + + virtual QString currentNumber() const; + virtual QString currentTitle() const; + + void updatePackage(); + + CWBugDetails *bugDetailsWidget(); + +public slots: + void slotRetrieveBugList( const QString &package, const QString &component ); + void slotRetrieveBugList( const QString &package ); + void slotRetrieveBugDetails( const Bug & ); + void slotSetActiveBug( const Bug & ); + void slotRetrieveAllBugDetails(); + + void updatePackageList( const Package::List &pkgs ); + void updateBugList( const Package &pkg, const QString &component, const Bug::List &bugs ); + void updateBugList( const QString &label, const Bug::List &bugs ); + void updateBugDetails( const Bug &, const BugDetails & ); + + void slotReloadPackageList(); + void slotReloadPackage(); + void slotReloadBug(); + void slotExtractAttachments(); + + /** + * Load the bugs the user reported himself, or for which he is the assigned to person + */ + void slotLoadMyBugs(); + + void mergeBugs(); + void unmergeBugs(); + + void closeBug(); + void closeBugSilently(); + void reopenBug(); + void titleBug(); + void severityBug(); + void replyBug(); + void replyPrivateBug(); + void reassignBug(); + + void clearCommand(); + +signals: + void resetProgressBar(); + void searchPackage(); // when clicking on the initial package widget + void searchBugNumber(); // when clicking on the initial bug-details widget + +protected slots: + void showLoadingError( const QString & ); + +private: + CWSearchWidget *m_searchPane; + CWBugListContainer *m_listPane; + CWBugDetailsContainer *m_bugPane; + + QSplitter *m_vertSplitter; + QSplitter *m_horSplitter; + + /** + * Other status info + */ + Package m_currentPackage; + QString m_currentComponent; + Bug m_currentBug; + + QMap<QString, Package> m_packageList; + + /** + * We do multi-select, but the close/reopen buttons are per-item and + * on highlight instead of on execute! Hence this different member + */ + Bug m_activeBug; + + // For "load all bugs" + bool m_bLoadingAllBugs; +}; + +} // namespace + +#endif // KBBMAINWINDOW_CENTRALWIGET_H + +/* vim: set et ts=4 softtabstop=4 sw=4: */ + diff --git a/kbugbuster/gui/centralwidget_base.ui b/kbugbuster/gui/centralwidget_base.ui new file mode 100644 index 00000000..d2f707de --- /dev/null +++ b/kbugbuster/gui/centralwidget_base.ui @@ -0,0 +1,236 @@ +<!DOCTYPE UI><UI version="3.0" stdsetdef="1"> +<class>CentralWidget_Base</class> +<widget class="QWidget"> + <property name="name"> + <cstring>CentralWidget_Base</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>630</width> + <height>518</height> + </rect> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>m_searchGroup</cstring> + </property> + <property name="title"> + <string>Search</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>11</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>m_searchContainer</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>7</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QWidgetStack"> + <property name="name"> + <cstring>m_searchStack</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>7</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>TextLabel9</cstring> + </property> + <property name="text"> + <string>Bug &number:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>m_searchBugNumber</cstring> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout17</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLineEdit"> + <property name="name"> + <cstring>m_searchBugNumber</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_searchBugNumberBtn</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string></string> + </property> + <property name="pixmap"> + <pixmap>image0</pixmap> + </property> + </widget> + </hbox> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>TextLabel7</cstring> + </property> + <property name="text"> + <string>&Description:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>m_searchDesc</cstring> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout15</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLineEdit"> + <property name="name"> + <cstring>m_searchDesc</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_searchDescBtn</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string></string> + </property> + <property name="pixmap"> + <pixmap>image0</pixmap> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + </vbox> + </widget> + <widget class="QSplitter"> + <property name="name"> + <cstring>m_splitter</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + </widget> + </hbox> +</widget> +<customwidgets> + <customwidget> + <class>QWidgetStack</class> + <header location="global">qwidgetstack.h</header> + <sizehint> + <width>-1</width> + <height>-1</height> + </sizehint> + <container>1</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="XPM.GZ" length="1725">789c75d4594fdb401405e0777e4584df5075b0e325b6aa3eb015ca1e76a8fa309ea518ca9eb055fdef9d39774815447311e4c3c73377c6cbfc5cef6477ab37373ff33052a34ef7f4b9baefcd99f1d5d5cbf71f5f7ecfcc6655cfff544daf3ffb69667638eae9def6cdb50d40e791a4fc04274aac7df1f840ecfc87bea3b350b4a5fba1787e420f42f1f8906e42d16b741b8a6e691b8a7e09e6f0323fc7eb0f42d179706e956a739afde62e146dde5cc8f93ab8487d49bf2774def65b4d1f0797b96e4c416f0757a9ceb4a1fb74ad2b23e73fd146175acecf68e7cdfe13ae87cb97fc0a9de94a8e23a52bdd9a92de129b8195f51cd1b5a9a3bfd28d56b1bf92567e3ed9bf535aeb26f67b41bbba5fcbfcb7c1755ae735af0f76e8526b23aee846d746f6eb9056a6b5d2df01ddfafee4f832ed8cb3355d0437a9298df4f318dd18b91e9b746933abe8bd60559b2caeff3adaca7c0968632a23f9fd6863a5df1bb12de2fc67b4f579997f8976b671d2ef46709b5a65a59f57baf2fdc97ed57463fc90f46a741bd7fb4c2babe3f963da5ae3647fcf8375660a53b17fde6f3a0fc5e3eb623fbfdc2f5774691b2bf335b4f2c7a59f5dda581dfb77b435b591e76731d8a42e73b27f0b74e9fa4eee8f6ff4c09696fde0e7c4b25fbf68edf3d2df88b6ae74f27c5d065b3e40f47d7411f30fb472ad53f23291420285f64df17f7a387acbc0c0c2e127ced1c14ce724e31317b8c42f5ce11a37fe7b4ce116b793cc1deef18011c678c4139ed1c5840e25ef0fbc62c12716b18465ace02b56b136c924f24ec137acfb3136b0892d6c6307bb18c655243217d6b0877d1ce0104738c6094e7126b34df5dcf9515264e8fb44eebf77ff59d718054a54feef853f67f03ec354871a4d82b03f18f8dfddfb0c73774982bbe12851e89236d16fa9c9387aea0a74d0ff5253e3e88f53b167eef9bbab3949c97b45f6fce319a732c9fb91980a73fdf93cf317e4bbac85</data> + </image> + <image name="image1"> + <data format="XPM.GZ" length="646">789c6dd2c10ac2300c00d07bbf2234b7229d1be245fc04c5a3201e4615f430059d0711ff5ddb2e6bb236ec90eed134cb5a19d8ef36602af5ecdbfeeac05dda0798d3abebde87e3faa374d3807fa0d633a52d38d8de6f679fe33fc776e196f53cd010188256a3600a292882096246517815ca99884606e18044a3a40d91824820924265a7923a2e8bcd05f33db1173e002913175f2a6be6d3294871a2d95fa00e8a94ee017b69d339d90df1e77c57ea072ede6758</data> + </image> +</images> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kbugbuster/gui/cwbugdetails.cpp b/kbugbuster/gui/cwbugdetails.cpp new file mode 100644 index 00000000..7aaf16a4 --- /dev/null +++ b/kbugbuster/gui/cwbugdetails.cpp @@ -0,0 +1,212 @@ +/* + cwbugdetails.cpp - Details of a bug report + + copyright : (c) 2001 by Martijn Klingens + email : klingens@kde.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. * + * * + ************************************************************************* +*/ + +#include <qtextview.h> +#include <qlineedit.h> +#include <qcombobox.h> +#include <qlabel.h> + +#include "cwbugdetails.h" +#include "kbbprefs.h" +#include "bugsystem.h" +#include "bugserver.h" + +#include <khtml_part.h> +#include <khtmlview.h> +#include <kdebug.h> +#include <kglobal.h> +#include <krun.h> + +#include <qlayout.h> +#include <qpalette.h> + +using namespace KBugBusterMainWindow; + +CWBugDetails::CWBugDetails( QWidget *parent , const char * name ) + : QWidget( parent, name ) +{ + QBoxLayout *topLayout = new QVBoxLayout( this ); + + m_bugDesc = new KHTMLPart( this, "m_bugDesc" ); + connect( m_bugDesc->browserExtension(), SIGNAL( openURLRequest( const KURL &, const KParts::URLArgs & ) ), + this, SLOT( handleOpenURLRequest( const KURL &, const KParts::URLArgs & ) ) ); + + topLayout->addWidget( m_bugDesc->view() ); +} + +CWBugDetails::~CWBugDetails() +{ +} + +void CWBugDetails::setBug( const Bug &bug, const BugDetails &details ) +{ + QColorGroup cg = m_bugDesc->view()->palette().active(); + QString text = + "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n" + "<html><head><title></title></head>\n" + "<style>"; + + text.append( + QString( "table.helpT { text-align: center; font-family: Verdana; font-weight: normal; font-size: 11px; color: #404040; width: 100%; background-color: #fafafa; border: 1px #6699CC solid; border-collapse: collapse; border-spacing: 0px; }\n" + "td.helpHed { border-bottom: 2px solid #000000; border-left: 1px solid #000000; background-color: %1; text-align: center; text-indent: 5px; font-family: Verdana; font-weight: bold; font-size: 11px; color: %2; }\n" + "td.helpBod { border-bottom: 1px solid #9CF; border-top: 0px; border-left: 1px solid #9CF; border-right: 0px; text-align: center; text-indent: 10px; font-family: Verdana, sans-serif, Arial; font-weight: normal; font-size: 11px; color: #404040; background-color: #000000; }\n" + "table.sofT { text-align: center; font-family: Verdana; font-weight: normal; font-size: 11px; color: #404040; width: 100%; background-color: #fafafa; border: 1px #000000 solid; border-collapse: collapse; border-spacing: 0px; }\n" + "</style>\n" ) + .arg( cg.highlight().name() ) + .arg( cg.highlightedText().name() ) ); + + text.append( "<body style=\"margin: 0px\">\n" ); + + QString highlightStyle = QString( "background: %1; color: %2; " ) + .arg( cg.highlight().name() ) + .arg( cg.highlightedText().name() ); + QString borderBottomStyle = QString( "border-bottom: solid %1 1px; " ) + .arg( cg.foreground().name() ); + QString borderTopStyle = QString( "border-top: solid %1 1px; " ) + .arg( cg.foreground().name() ); + + QString submitter = bug.submitter().fullName( true ); + int age = details.age(); + text.append( "<div style=\"" + highlightStyle + "padding: 8px; float: left\">" ); + text.append( "<a href=\"" + BugSystem::self()->server()->bugLink( bug ).url() + + "\">" + i18n("Bug Report</a> from <b>%1</b> " ) + .arg( submitter ) ); + int replies = details.parts().count() - 1; + if ( replies >= 1 ) text += i18n( "(1 reply)", "(%n replies)", replies ); + text += "</div>"; + text += "<div style=\"" + highlightStyle + borderBottomStyle + + " text-align: right; padding: 8px\">" + + i18n( "1 day old", "%n days old", age ) + "</div>\n"; + + text.append( + QString( "<div style=\"background: %1; color: %2; " + + borderBottomStyle + + "border-bottom: solid %3 1px; " + "padding: 4px\">" + "<table cellspacing=\"0\" cellpadding=\"4\" width=\"100%\">" ) + .arg( cg.background().name() ) + .arg( cg.foreground().name() ) ); + text.append( textBugDetailsAttribute( details.version(), i18n("Version") ) ); + text.append( textBugDetailsAttribute( details.source(), i18n("Source") ) ); + text.append( textBugDetailsAttribute( details.compiler(), i18n("Compiler") ) ); + text.append( textBugDetailsAttribute( details.os(), i18n("OS") ) ); + text.append( "</table></div>\n" ); + + BugDetailsPart::List bdp = details.parts(); + BugDetailsPart::List::ConstIterator it; + bool firstHeader = true; + + for ( it = bdp.begin(); it != bdp.end(); ++it ) { + if ( bdp.count() > 1 ) { + text.append( "<div style=\"" + highlightStyle + "padding: 8px; float: left; " ); + if ( !firstHeader ) text += borderTopStyle; + + text.append( borderBottomStyle + "\">" ); + QString sender = (*it).sender.fullName( true ); + QString date = KGlobal::locale()->formatDateTime( (*it).date, false ); + BugDetailsPart::List::ConstIterator it2 = it; + if ( ++it2 == bdp.end() ) + text.append( "<a href=\"" + BugSystem::self()->server()->bugLink( bug ).url() + + "\">" + i18n("Bug Report</a> from <b>%1</b>") + .arg( sender ) ); + else { + text.append( "<a href=\"" + BugSystem::self()->server()->bugLink( bug ).url() + QString("#c%1").arg( replies ) + + "\">" + i18n("Reply #%1</a> from <b>%2</b>") + .arg( replies ).arg( sender ) ); + replies--; + } + text.append( "</div>\n" ); + text += "<div style=\"" + highlightStyle + borderBottomStyle; + if ( !firstHeader ) text += borderTopStyle; + text += "text-align: right; padding: 8px\">" + date + "</div>\n"; + + firstHeader = false; + } + + // Adding of <pre> tags is now done by BugDetailsJob::processNode. This + // was breaking the display of attachments because they have <br/>\n + // after each line -> two newlines with <pre> + text.append( "<div style=\"padding: 8px\">" ); + text.append( (*it).text ); + text.append( "</div>\n" ); + } + + QValueList<BugDetailsImpl::AttachmentDetails> atts = details.attachmentDetails(); + if ( atts.count() > 0 ) { + text.append( "<table summary=\"Attachment data table\" class=\"sofT\" cellspacing=\"0\">" ); + text.append( QString( "<tr> <td colspan=\"4\" class=\"helpHed\">%1</td> </tr>") + .arg( i18n( "Attachment List") ) ); + text.append( QString("<tr> <td class=\"helpHed\">%1</td> <td class=\"helpHed\">%2</td> <td class=\"helpHed\">%3</td> <td class=\"helpHed\">%4</td> </tr>") + .arg( i18n("Description") ) + .arg( i18n("Date") ) + .arg( i18n("View") ) + .arg( i18n("Edit") ) ); + QValueList<BugDetailsImpl::AttachmentDetails>::iterator it; + for ( it = atts.begin() ; it != atts.end() ; ++it ) { + text.append( QString("<tr><td>%1</td>").arg( (*it).description ) ) ; + text.append( QString("<td>%1</td>").arg( (*it).date ) ); + text.append( "<td><a href=\"" + + BugSystem::self()->server()->attachmentViewLink( (*it).id ).url() + + "\">" + i18n("View") + "</a></td>" ); + text.append( "<td><a href=\"" + + BugSystem::self()->server()->attachmentEditLink( (*it).id ).url() + + "\">" + i18n("Edit") + "</a></td>" ); + text.append( "</tr>" ); + } + } + + text.append( "</body></html>" ); + + //kdDebug() << "BEGIN OUTPUT" << text << "END OUTPUT" << endl; + + m_bugDesc->view()->setContentsPos(0,0); + m_bugDesc->begin(); + m_bugDesc->write( text ); + m_bugDesc->end(); + + if ( KBBPrefs::instance()->mDebugMode ) mSource = text; +} + +void CWBugDetails::handleOpenURLRequest( const KURL &url, const KParts::URLArgs & ) +{ + new KRun( url ); +} + +QString CWBugDetails::textBugDetailsAttribute( const QString &value, + const QString &name ) +{ + QString text = ""; + if ( !value.isEmpty() ) { + text.append( "<tr><td style=\"width: 20%\"><b>" + name + "</b></td>" + "<td>" + value + "</td></tr>" ); + } + return text; +} + +QString CWBugDetails::source() const +{ + return mSource; +} + +QString CWBugDetails::selectedText() const +{ + return m_bugDesc->selectedText(); +} + +#include "cwbugdetails.moc" + +/* vim: set et ts=4 sw=4 softtabstop=4: */ + diff --git a/kbugbuster/gui/cwbugdetails.h b/kbugbuster/gui/cwbugdetails.h new file mode 100644 index 00000000..31a5339f --- /dev/null +++ b/kbugbuster/gui/cwbugdetails.h @@ -0,0 +1,65 @@ +/* + cwbugdetails.h - Details of a bug report + + copyright : (c) 2001 by Martijn Klingens + email : klingens@kde.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. * + * * + ************************************************************************* +*/ + +#ifndef KBBMAINWINDOW_CWBUGDETAILS_H +#define KBBMAINWINDOW_CWBUGDETAILS_H + +#include "bug.h" +#include "bugdetails.h" + +#include <qwidget.h> + +#include <kparts/browserextension.h> + +class KHTMLPart; + +namespace KBugBusterMainWindow +{ + +/** + * @author Martijn Klingens + */ +class CWBugDetails : public QWidget +{ + Q_OBJECT + + public: + CWBugDetails( QWidget* parent = 0, const char* name = 0 ); + ~CWBugDetails(); + + void setBug( const Bug &, const BugDetails & ); + + QString source() const; + QString selectedText() const; + + private slots: + void handleOpenURLRequest( const KURL &url, const KParts::URLArgs & ); + + private: + QString textBugDetailsAttribute( const QString &value, + const QString &name ); + + KHTMLPart *m_bugDesc; + + QString mSource; +}; + +} // namespace + +#endif + +/* vim: set et ts=4 softtabstop=4 sw=4: */ + diff --git a/kbugbuster/gui/cwbugdetailscontainer.cpp b/kbugbuster/gui/cwbugdetailscontainer.cpp new file mode 100644 index 00000000..b33ec3b1 --- /dev/null +++ b/kbugbuster/gui/cwbugdetailscontainer.cpp @@ -0,0 +1,269 @@ +/* + cwbugdetailscontainer.cpp - Container for bug details + + copyright : (c) 2001 by Martijn Klingens + email : klingens@kde.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. * + * * + ************************************************************************* +*/ + +#include <qpushbutton.h> +#include <qwidgetstack.h> +#include <qlayout.h> +#include <qtooltip.h> + +#include <kiconloader.h> +#include <klocale.h> +#include <kactivelabel.h> +#include <kdialog.h> + +#include "bugsystem.h" +#include "bugcommand.h" +#include "bugserver.h" + +#include "cwbugdetails.h" +#include "cwloadingwidget.h" + +#include "cwbugdetailscontainer.h" +#include <kstringhandler.h> + +using namespace KBugBusterMainWindow; + +CWBugDetailsContainer::CWBugDetailsContainer( QWidget *parent , const char * name ) +: CWBugDetailsContainer_Base( parent, name ) +{ + // Do some stuff Designer can't do: + m_bugCloseBtn->setIconSet( BarIconSet( "edittrash" ) ); + m_bugCloseSilentlyBtn->setIconSet( BarIconSet( "edittrash" ) ); + m_bugReopenBtn->setIconSet( SmallIconSet( "idea" ) ); + m_bugReassignBtn->setIconSet( BarIconSet( "folder_new" ) ); + m_bugTitleBtn->setIconSet( SmallIconSet( "text_under" ) ); + m_bugSeverityBtn->setIconSet( SmallIconSet( "edit" ) ); + m_bugReplyBtn->setIconSet( SmallIconSet( "mail_replyall" ) ); + m_bugReplyPrivBtn->setIconSet( SmallIconSet( "mail_reply" ) ); + + // The Bugzilla mail interface doesn't support all commands yet. + m_bugCloseSilentlyBtn->hide(); + m_bugReassignBtn->hide(); + m_bugTitleBtn->hide(); + m_bugSeverityBtn->hide(); + + // Create Bug Details pane + m_bugDetails = new CWBugDetails( m_bugStack ); + + // Fill WidgetStack in Bug Details pane + m_bugLoading = new CWLoadingWidget( CWLoadingWidget::BottomFrame, + m_bugStack ); + connect( m_bugLoading, SIGNAL( clicked() ), SIGNAL( searchBugNumber() ) ); + + m_bugStack->addWidget( m_bugDetails, 0 ); + m_bugStack->addWidget( m_bugLoading, 1 ); + + setNoBug(); + + QFont f = m_bugLabel->font(); + f.setBold( true ); + m_bugLabel->setFont( f ); + + // Set fonts and margins + CWBugDetailsContainer_BaseLayout->setSpacing( KDialog::spacingHint() ); + CWBugDetailsContainer_BaseLayout->setMargin( KDialog::marginHint() ); + + connect( m_bugCloseBtn, SIGNAL( clicked() ), SIGNAL( signalCloseBug() ) ); + connect( m_bugCloseSilentlyBtn, SIGNAL( clicked() ), SIGNAL( signalCloseBugSilently() ) ); + connect( m_bugReopenBtn, SIGNAL( clicked() ), SIGNAL( signalReopenBug() ) ); + connect( m_bugReassignBtn, SIGNAL( clicked() ), SIGNAL( signalReassignBug() ) ); + connect( m_bugTitleBtn, SIGNAL( clicked() ), SIGNAL( signalTitleBug() ) ); + connect( m_bugSeverityBtn, SIGNAL( clicked() ), SIGNAL( signalSeverityBug() ) ); + connect( m_bugReplyBtn, SIGNAL( clicked() ), SIGNAL( signalReplyBug() ) ); + connect( m_bugReplyPrivBtn, SIGNAL( clicked() ), SIGNAL( signalReplyPrivateBug() ) ); + + connect( m_cmdClearBtn, SIGNAL( clicked() ), SIGNAL( signalClearCommand() ) ); + + connect( BugSystem::self(), SIGNAL( bugDetailsLoading( const Bug & ) ), + SLOT( setLoading( const Bug & ) ) ); + connect( BugSystem::self(), SIGNAL( bugDetailsCacheMiss( const Bug & ) ), + SLOT( setCacheMiss( const Bug & ) ) ); + connect( BugSystem::self(), SIGNAL( commandQueued( BugCommand * ) ), + SLOT( commandQueued( BugCommand * ) ) ); + connect( BugSystem::self(), SIGNAL( commandCanceled( const QString & ) ), + SLOT( clearCommand( const QString & ) ) ); +} + +CWBugDetailsContainer::~CWBugDetailsContainer() +{ +} + +void CWBugDetailsContainer::setBug( const Bug &bug, const BugDetails &details ) +{ + m_bug = bug; + m_bugDetails->setBug( bug, details ); + + QString labelText; + + if ( bug.mergedWith().size() ) + { + //FIXME: What should the separator be for lists? Don't see anything in KLocale for that + QString list; + Bug::BugMergeList mergedWith = bug.mergedWith(); + for (Bug::BugMergeList::ConstIterator i = mergedWith.begin(); i != mergedWith.end(); ++i) + { + list += QString::number(*i)+", "; + } + + list.truncate( list.length()-2 ); //Strip off the last ", " + + labelText = i18n("bug #number [Merged with: a list of bugs] (severity): title","Bug #%1 [Merged with: %2] (%3): %4") + .arg( bug.number() ) + .arg( list ) + .arg( bug.severityAsString() ) + .arg( bug.title() ); + + } + else + { + labelText = i18n("bug #number (severity): title","Bug #%1 (%2): %3") + .arg( bug.number() ).arg( bug.severityAsString() ) + .arg( bug.title() ); + } + + m_bugLabel->setText( KStringHandler::tagURLs( labelText ) ); + + showCommands( bug ); + + enableButtons( bug ); + + m_bugStack->raiseWidget( 0 ); + emit resetProgressBar(); +} + +void CWBugDetailsContainer::showCommands( const Bug& bug ) +{ + QPtrList<BugCommand> commands = BugSystem::self()->server()->queryCommands( bug ); + if ( !commands.isEmpty() ) { + QString cmdDetails; + QString cmdText = i18n("Pending commands:")+" "; + bool first = true; + QPtrListIterator<BugCommand> cmdIt( commands ); + for( ; cmdIt.current(); ++cmdIt ) + { + BugCommand *cmd = cmdIt.current(); + if (!first) + cmdText += " | "; // separator in case of multiple commands + first = false; + cmdText += QString("<b>%1</b>").arg( cmd->name() ); + if (!cmdDetails.isEmpty()) + cmdDetails += " | "; // separator in case of multiple commands + cmdDetails += cmd->details(); + } + // Set summary as text label, details into tooltip + m_cmdLabel->setText( cmdText ); + if ( !cmdDetails.isEmpty() ) { + QToolTip::add( m_cmdLabel, cmdDetails ); + } else { + QToolTip::remove( m_cmdLabel ); + } + m_cmdLabel->show(); + } else { + hideCommands(); + } +} + +void CWBugDetailsContainer::hideCommands() +{ + m_cmdLabel->hide(); +} + +void CWBugDetailsContainer::clearCommand( const QString &bug ) +{ + if ( bug == m_bug.number() ) + showCommands( m_bug ); +} + +void CWBugDetailsContainer::commandQueued( BugCommand *cmd ) +{ + // ### use == operator instead? + // (might not work because impl is different) + if ( cmd && cmd->bug().number() == m_bug.number() ) + showCommands( m_bug ); +} + +void CWBugDetailsContainer::setNoBug() +{ + m_bugLabel->setText( i18n("Bug Title") ); + + m_bug = Bug(); + showCommands( m_bug ); + + m_bugLoading->setText( i18n( "Click here to select a bug by number" ) ); + m_bugStack->raiseWidget( 1 ); +} + +void CWBugDetailsContainer::setLoading( const Bug &bug ) +{ + m_bug = bug; + showCommands( bug ); + + m_bugLoading->setText(i18n( "Retrieving Details for Bug %1\n\n(%2)" ) + .arg( bug.number() ).arg( bug.title() ) ); + m_bugStack->raiseWidget( 1 ); +} + +void CWBugDetailsContainer::setCacheMiss( const Bug &bug ) +{ + m_bug = bug; + showCommands( bug ); + + QString msg; + if( BugSystem::self()->disconnected() ) + msg = i18n( "Bug #%1 (%2) is not available offline." ). + arg( bug.number() ).arg( bug.title() ); + else + msg = i18n( "Retrieving details for bug #%1\n" + "(%2)" ). + arg( bug.number() ).arg( bug.title() ); + m_bugLoading->setText( msg ); + m_bugStack->raiseWidget( 1 ); +} + + +void CWBugDetailsContainer::enableButtons( const Bug &bug ) +{ + if( bug.isNull() ) { + m_bugCloseBtn->setEnabled( false ); + m_bugCloseSilentlyBtn->setEnabled( false ); + m_bugReopenBtn->setEnabled( false ); + m_bugReassignBtn->setEnabled( false ); + m_bugTitleBtn->setEnabled( false ); + m_bugSeverityBtn->setEnabled( false ); + m_bugReplyBtn->setEnabled( false ); + m_bugReplyPrivBtn->setEnabled( false ); + } else { + if( bug.status() != Bug::Closed ) { + m_bugCloseBtn->setEnabled( true ); + m_bugCloseSilentlyBtn->setEnabled( true ); + m_bugReopenBtn->setEnabled( false ); + } else { + m_bugCloseBtn->setEnabled( false ); + m_bugCloseSilentlyBtn->setEnabled( false ); + m_bugReopenBtn->setEnabled( true ); + } + m_bugReassignBtn->setEnabled( true ); + m_bugTitleBtn->setEnabled( true ); + m_bugSeverityBtn->setEnabled( true ); + m_bugReplyBtn->setEnabled( true ); + m_bugReplyPrivBtn->setEnabled( true ); + } +} + +#include "cwbugdetailscontainer.moc" + +/* vim: set et ts=4 sw=4 softtabstop=4: */ + diff --git a/kbugbuster/gui/cwbugdetailscontainer.h b/kbugbuster/gui/cwbugdetailscontainer.h new file mode 100644 index 00000000..c183a8ea --- /dev/null +++ b/kbugbuster/gui/cwbugdetailscontainer.h @@ -0,0 +1,88 @@ +/* + cwbugdetailscontainer.h - Container for bug details + + copyright : (c) 2001 by Martijn Klingens + email : klingens@kde.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. * + * * + ************************************************************************* +*/ + +#ifndef KBBMAINWINDOW_CWBUGDETAILSCONTAINER_H +#define KBBMAINWINDOW_CWBUGDETAILSCONTAINER_H + +#include "bug.h" +#include "bugdetails.h" + +#include "cwbugdetailscontainer_base.h" + +class BugCommand; + +namespace KBugBusterMainWindow +{ + +class CWBugDetails; +class CWLoadingWidget; + +/** + * @author Martijn Klingens + */ +class CWBugDetailsContainer : public CWBugDetailsContainer_Base +{ + Q_OBJECT + +public: + CWBugDetailsContainer( QWidget* parent = 0, const char* name = 0 ); + ~CWBugDetailsContainer(); + + void setBug( const Bug &, const BugDetails & ); + + CWBugDetails *bugDetailsWidget() { return m_bugDetails; } + +public slots: + void setNoBug(); + void setLoading( const Bug &bug ); + void setCacheMiss( const Bug &bug ); + + void enableButtons( const Bug & ); + +signals: + void resetProgressBar(); + void searchBugNumber(); + + void signalCloseBug(); + void signalCloseBugSilently(); + void signalReopenBug(); + void signalReassignBug(); + void signalTitleBug(); + void signalSeverityBug(); + void signalReplyBug(); + void signalReplyPrivateBug(); + + void signalClearCommand(); + +private slots: + void showCommands( const Bug & ); + void clearCommand( const QString & ); + void commandQueued( BugCommand * ); + +private: + void hideCommands(); + + CWLoadingWidget *m_bugLoading; + CWBugDetails *m_bugDetails; + Bug m_bug; +}; + +} // namespace + +#endif + +/* vim: set et ts=4 softtabstop=4 sw=4: */ + diff --git a/kbugbuster/gui/cwbugdetailscontainer_base.ui b/kbugbuster/gui/cwbugdetailscontainer_base.ui new file mode 100644 index 00000000..c5ef87b7 --- /dev/null +++ b/kbugbuster/gui/cwbugdetailscontainer_base.ui @@ -0,0 +1,244 @@ +<!DOCTYPE UI><UI version="3.0" stdsetdef="1"> +<class>CWBugDetailsContainer_Base</class> +<widget class="QWidget"> + <property name="name"> + <cstring>CWBugDetailsContainer_Base</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>507</width> + <height>559</height> + </rect> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>11</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QWidgetStack" row="1" column="0"> + <property name="name"> + <cstring>m_bugStack</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>7</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="QLayoutWidget" row="0" column="0"> + <property name="name"> + <cstring>Layout5</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <widget class="KActiveLabel"> + <property name="name"> + <cstring>m_bugLabel</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>100</width> + <height>20</height> + </size> + </property> + <property name="text"> + <string>Bug Title</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>m_cmdLabel</cstring> + </property> + <property name="text"> + <string>Bug Commands</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QLayoutWidget" row="1" column="1"> + <property name="name"> + <cstring>Layout3</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_cmdClearBtn</cstring> + </property> + <property name="text"> + <string>Clear Co&mmands</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>Spacer1</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_bugCloseBtn</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>C&lose...</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_bugCloseSilentlyBtn</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Close Silentl&y</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_bugReopenBtn</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Re&open</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_bugReassignBtn</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Re&assign...</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_bugTitleBtn</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Change &Title...</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_bugSeverityBtn</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Chan&ge Severity...</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>Spacer1_2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_bugReplyBtn</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>&Reply...</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_bugReplyPrivBtn</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Reply &Privately...</string> + </property> + </widget> + </vbox> + </widget> + </grid> +</widget> +<customwidgets> + <customwidget> + <class>QWidgetStack</class> + <header location="global">qwidgetstack.h</header> + <sizehint> + <width>-1</width> + <height>-1</height> + </sizehint> + <container>1</container> + <sizepolicy> + <hordata>5</hordata> + <verdata>5</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + </customwidget> +</customwidgets> +<images> + <image name="image0"> + <data format="XPM.GZ" length="646">789c6dd2c10ac2300c00d07bbf2234b7229d1ddec44f503c0ae2a154410f53d0ed20e2bf6bdb656dd6861dd23d9a66591b0587fd1654235ebded6f0edcd53e419d87ae7b1f4f9b8f906d0bfe012317426a70b07bdc2f3ec77f8ed6b89559061a0343d06a124cc105596482585094bc0ae599b04646c9018926491b2205e140c485cace25755c175d0a967b622ff900b8cc9c7d29af594ea722d589167f813aa852ba07d94b9dce296e883fe7bb163f23896753</data> + </image> +</images> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kbugbuster/gui/cwbuglistcontainer.cpp b/kbugbuster/gui/cwbuglistcontainer.cpp new file mode 100644 index 00000000..0188a996 --- /dev/null +++ b/kbugbuster/gui/cwbuglistcontainer.cpp @@ -0,0 +1,328 @@ +/* + cwbuglistcontainer.cpp - Container for the bug list + + copyright : (c) 2001 by Martijn Klingens + email : klingens@kde.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. * + * * + ************************************************************************* +*/ + +#include <qpushbutton.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qwidgetstack.h> + +#include <kapplication.h> +#include <kiconloader.h> +#include <klistview.h> +#include <klocale.h> +#include <kdialog.h> +#if KDE_IS_VERSION( 3, 2, 90 ) +#include <klistviewsearchline.h> +#endif +#include <kdebug.h> + +#include "bugsystem.h" + +#include "cwloadingwidget.h" +#include "buglvi.h" +#include "kbbprefs.h" +#include "kfind.h" + +#include "cwbuglistcontainer.h" +#include <kfinddialog.h> + +using namespace KBugBusterMainWindow; + +CWBugListContainer::CWBugListContainer( QWidget *parent , const char * name ) + : QWidget( parent, name ), m_find(0), m_findItem(0) +{ + QBoxLayout *topLayout = new QVBoxLayout( this ); + topLayout->setSpacing( KDialog::spacingHint() ); + topLayout->setMargin( KDialog::marginHint() ); + + m_listLabel = new QLabel( this ); + topLayout->addWidget( m_listLabel ); + topLayout->setStretchFactor( m_listLabel, 0 ); + + QFont f = m_listLabel->font(); + f.setBold( true ); + m_listLabel->setFont( f ); + + m_listStack = new QWidgetStack( this ); + + // Create Outstanding Bugs listview + m_listBugs = new KListView( m_listStack ); + + topLayout->addWidget( new KListViewSearchLineWidget( m_listBugs, this ) ); + topLayout->addWidget( m_listStack ); + topLayout->setStretchFactor( m_listStack, 1 ); + + m_listBugs->addColumn( i18n( "Number" ) ); + m_listBugs->addColumn( i18n( "Age" ) ); + m_listBugs->addColumn( i18n( "Title" ), 500 ); // so that the widthmode isn't "Maximum" + m_listBugs->addColumn( i18n( "Status" ) ); + m_listBugs->addColumn( i18n( "Severity" ) ); + m_listBugs->addColumn( i18n( "Sender" ), 150 ); // idem. hardcoded widths suck a bit, but... + m_listBugs->setAllColumnsShowFocus( true ); + m_listBugs->setColumnAlignment( 0, AlignRight ); + m_listBugs->setSorting( 0, false ); + m_listBugs->setShowSortIndicator( true ); + m_listBugs->setSelectionMode( QListView::Extended ); // needed for merging bugs + + m_listBugs->restoreLayout( KBBPrefs::instance()->config(), "BugListLayout" ); + + connect( m_listBugs, SIGNAL( executed( QListViewItem * ) ), + SLOT( execute( QListViewItem * ) ) ); + connect( m_listBugs, SIGNAL( returnPressed( QListViewItem * ) ), + SLOT( execute( QListViewItem * ) ) ); + connect( m_listBugs, SIGNAL( currentChanged( QListViewItem * ) ), + SLOT( changeCurrent( QListViewItem * ) ) ); + + // Fill WidgetStack in Outstanding Bugs pane + m_listLoading = new CWLoadingWidget( CWLoadingWidget::TopFrame, + m_listStack ); + connect( m_listLoading, SIGNAL( clicked() ), SIGNAL( searchPackage() ) ); + + m_listStack->addWidget( m_listBugs, 0 ); + m_listStack->addWidget( m_listLoading, 1 ); + + setNoList(); + + connect( BugSystem::self(), SIGNAL( bugListLoading( const Package &, const QString & ) ), + SLOT( setLoading( const Package &, const QString & ) ) ); + connect( BugSystem::self(), SIGNAL( bugListLoading( const QString & ) ), + SLOT( setLoading( const QString & ) ) ); + connect( BugSystem::self(), SIGNAL( bugListCacheMiss( const Package & ) ), + SLOT( setCacheMiss( const Package & ) ) ); + connect( BugSystem::self(), SIGNAL( bugListCacheMiss( const QString & ) ), + SLOT( setCacheMiss( const QString & ) ) ); + connect( BugSystem::self(), SIGNAL( commandQueued( BugCommand * ) ), + SLOT( markBugCommand( BugCommand * ) ) ); + connect( BugSystem::self(), SIGNAL( commandCanceled( const QString & ) ), + SLOT( clearCommand( const QString & ) ) ); +} + +CWBugListContainer::~CWBugListContainer() +{ + m_listBugs->saveLayout( KBBPrefs::instance()->config(), "BugListLayout" ); + KBBPrefs::instance()->writeConfig(); + delete m_find; +} + +void CWBugListContainer::setBugList( const QString &label, const Bug::List &bugs ) +{ + // List pane is invisible by default, make visible + show(); + + m_listBugs->clear(); + emit resetProgressBar(); + bool showClosed = KBBPrefs::instance()->mShowClosedBugs; + bool showWishes = KBBPrefs::instance()->mShowWishes; + uint noBugs = 0; + uint noWishes = 0; + + for ( Bug::List::ConstIterator it = bugs.begin(); it != bugs.end(); ++it ) + { + if ( ( *it ).status() != Bug::Closed || showClosed ) + { + if ( ( *it ).severity() != Bug::Wishlist || showWishes ) + new BugLVI( m_listBugs, *it ); + + if ( ( *it ).severity() != Bug::Wishlist ) + noBugs++; + else + noWishes++; + } + } + + m_listLabel->setText( i18n( "%1 (%2 bugs, %3 wishes)" ).arg( label ).arg( noBugs ).arg( noWishes ) ); + m_listStack->raiseWidget( 0 ); +} + +void CWBugListContainer::setBugList( const Package &package, const QString &component, const Bug::List &bugs ) +{ + QString listLabel; + if ( component.isEmpty() ) + { + if ( package.components().count() > 1 ) + listLabel = i18n( "Product '%1', all components" ).arg( package.name() ); + else + listLabel = i18n( "Product '%1'" ).arg( package.name() ); + } + else + { + listLabel = i18n( "Product '%1', component '%2'" ).arg( package.name(), component ); + } + + setBugList( listLabel, bugs ); +} + +void CWBugListContainer::execute( QListViewItem *lvi ) +{ + BugLVI *item = dynamic_cast<BugLVI *>( lvi ); + if( !item ) + { + kdWarning() << "CWBugListContainer::execute() Selected bug " + << lvi->text( 0 ) + << " is not a BugLVI! Ignoring event." << endl; + return; + } + + emit executed( item->bug() ); +} + +void CWBugListContainer::changeCurrent( QListViewItem *lvi ) +{ + if( !lvi ) { + emit currentChanged( Bug() ); + return; + } + + BugLVI *item = dynamic_cast<BugLVI *>( lvi ); + if( !item ) + { + kdWarning() << "CWBugListContainer::changeCurrent() Selected bug " + << lvi->text( 0 ) + << " is not a BugLVI! Ignoring event." << endl; + return; + } + + emit currentChanged( item->bug() ); +} + +void CWBugListContainer::setNoList() +{ + m_listLabel->setText( i18n("Outstanding Bugs") ); + m_listLoading->setText( i18n( "Click here to select a product" ) ); + m_listStack->raiseWidget( 1 ); +} + +void CWBugListContainer::setLoading( const Package &package, const QString &component ) +{ + if ( component.isEmpty() ) + setLoading( i18n( "Retrieving List of Outstanding Bugs for Product '%1'..." ).arg( package.name() ) ); + else + setLoading( i18n( "Retrieving List of Outstanding Bugs for Product '%1' (Component %2)..." ).arg( package.name(), component ) ); +} + +void CWBugListContainer::setLoading( const QString &label ) +{ + m_listLoading->setText( label ); + m_listStack->raiseWidget( 1 ); +} + +void CWBugListContainer::setCacheMiss( const Package &package ) +{ + setCacheMiss( i18n( "Package '%1'" ).arg( package.name() ) ); +} + +void CWBugListContainer::setCacheMiss( const QString &label ) +{ + m_listLoading->setText( i18n( "%1 is not available offline." ).arg( label ) ); + m_listStack->raiseWidget( 1 ); +} + +void CWBugListContainer::markBugCommand( BugCommand *cmd ) +{ + BugLVI *item = (BugLVI *)m_listBugs->firstChild(); + while( item ) { + if ( item->bug().number() == cmd->bug().number() ) { + item->setCommandState( BugCommand::Queued ); + break; + } + item = (BugLVI *)item->nextSibling(); + } + m_listBugs->triggerUpdate(); +} + +void CWBugListContainer::clearCommand( const QString &bug ) +{ + BugLVI *item = (BugLVI *)m_listBugs->firstChild(); + while( item ) { + if ( item->bug().number() == bug ) { + item->setCommandState( BugCommand::None ); + break; + } + item = (BugLVI *)item->nextSibling(); + } + m_listBugs->triggerUpdate(); +} + +void CWBugListContainer::searchBugByTitle( int options, const QString& pattern ) +{ + m_find = new KFind( pattern, options, this ); + // Connect signals to code which handles highlighting + // of found text. + connect(m_find, SIGNAL( highlight( const QString &, int, int ) ), + this, SLOT( searchHighlight( const QString &, int, int ) ) ); + connect(m_find, SIGNAL( findNext() ), this, SLOT( slotFindNext() ) ); + + m_findItem = (BugLVI *)m_listBugs->firstChild(); + if ( options & KFindDialog::FromCursor && m_listBugs->currentItem() ) + m_findItem = (BugLVI *)m_listBugs->currentItem(); + + slotFindNext(); +} + +// Note: if a 'find next' action is added, then one should also ensure +// that m_findItem never becomes dangling (i.e. clear it when clearing the listview). +void CWBugListContainer::slotFindNext() +{ + KFind::Result res = KFind::NoMatch; + while( res == KFind::NoMatch && m_findItem ) { + + if ( m_find->needData() ) + m_find->setData( m_findItem->text(2) ); + + // Let KFind inspect the text fragment, and display a dialog if a match is found + res = m_find->find(); + + if ( res == KFind::NoMatch ) { + if ( m_find->options() & KFindDialog::FindBackwards ) + m_findItem = (BugLVI *)m_findItem->itemAbove(); + else + m_findItem = (BugLVI *)m_findItem->itemBelow(); + } + } + if ( res == KFind::NoMatch ) // i.e. at end + if ( m_find->shouldRestart() ) { + m_findItem = (BugLVI *)m_listBugs->firstChild(); + slotFindNext(); + } else { + delete m_find; + m_find = 0L; + } +} + +void CWBugListContainer::searchHighlight( const QString &, int, int ) +{ + if ( m_findItem ) { + m_listBugs->clearSelection(); + m_listBugs->setSelected( m_findItem, true ); + m_listBugs->ensureItemVisible( m_findItem ); + } +} + +QStringList CWBugListContainer::selectedBugs() const +{ + QStringList lst; + BugLVI *item = (BugLVI *)m_listBugs->firstChild(); + while( item ) { + if ( item->isSelected() ) + lst.append( item->bug().number() ); + item = (BugLVI *)item->nextSibling(); + } + return lst; +} + +#include "cwbuglistcontainer.moc" + +/* vim: set et ts=4 sw=4 softtabstop=4: */ diff --git a/kbugbuster/gui/cwbuglistcontainer.h b/kbugbuster/gui/cwbuglistcontainer.h new file mode 100644 index 00000000..bcda4c15 --- /dev/null +++ b/kbugbuster/gui/cwbuglistcontainer.h @@ -0,0 +1,99 @@ +/* + cwbuglistcontainer.h - Container for the bug list + + copyright : (c) 2001 by Martijn Klingens + email : klingens@kde.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. * + * * + ************************************************************************* +*/ + +#ifndef KBBMAINWINDOW_CWBUGLISTCONTAINER_H +#define KBBMAINWINDOW_CWBUGLISTCONTAINER_H + +#include "package.h" +#include "bug.h" + +#include <qwidget.h> + +class KListView; +class KFind; +class BugCommand; +class BugLVI; + +namespace KBugBusterMainWindow +{ + +class CWLoadingWidget; + +/** + * @author Martijn Klingens + */ +class CWBugListContainer : public QWidget +{ + Q_OBJECT + +public: + CWBugListContainer( QWidget* parent = 0, const char* name = 0 ); + ~CWBugListContainer(); + + void setBugList( const Package &package, const QString &component, const Bug::List &bugs ); + + /** + * Overloaded method that takes a QString for the label. To be used when the + * bug list doesn't belong to a package, liek search results + */ + void setBugList( const QString &label, const Bug::List &bugs ); + + void searchBugByTitle( int options, const QString& pattern ); + + /** Return list of selected bugs in the listview. Used for merging. */ + QStringList selectedBugs() const; + +public slots: + void setNoList(); + void setLoading( const Package &package, const QString &component ); + void setLoading( const QString &label ); + void setCacheMiss( const Package &package ); + void setCacheMiss( const QString &label ); + void slotFindNext(); + +signals: + void resetProgressBar(); + void searchPackage(); + + void executed( const Bug & ); + void currentChanged( const Bug & ); + +private slots: + void execute( QListViewItem * ); + void changeCurrent( QListViewItem * ); + + void markBugCommand( BugCommand * ); + void clearCommand( const QString & ); + + void searchHighlight( const QString &, int, int ); + +private: + QLabel *m_listLabel; + QWidgetStack *m_listStack; + + KListView *m_listBugs; + KFind *m_find; + BugLVI *m_findItem; + + CWLoadingWidget *m_listLoading; +}; + +} // namespace + +#endif + +/* vim: set et ts=4 softtabstop=4 sw=4: */ + diff --git a/kbugbuster/gui/cwloadingwidget.cpp b/kbugbuster/gui/cwloadingwidget.cpp new file mode 100644 index 00000000..ddd218a5 --- /dev/null +++ b/kbugbuster/gui/cwloadingwidget.cpp @@ -0,0 +1,256 @@ +/* + cwloadingwidget.cpp - Widget to be shown while loading data + + copyright : (c) 2001 by Martijn Klingens + email : klingens@kde.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. * + * * + ************************************************************************* +*/ + +#include "cwloadingwidget.h" + +#include <qpainter.h> +#include <qpixmap.h> +#include <kpixmap.h> +#include <kpixmapeffect.h> + +#include <kstandarddirs.h> +#include <klocale.h> +#include <kglobalsettings.h> + +using namespace KBugBusterMainWindow; + +CWLoadingWidget::CWLoadingWidget( WidgetMode mode, QWidget *parent, + const char * name ) +: QFrame( parent, name ) +{ + init( mode ); +} + +CWLoadingWidget::CWLoadingWidget( const QString &text, WidgetMode mode, + QWidget *parent, const char * name ) +: QFrame( parent, name ) +{ + init( mode ); + setText( text ); +} + +void CWLoadingWidget::init( WidgetMode mode ) +{ + m_mode = mode; + + QPalette pal = palette(); + pal.setColor( QPalette::Active, QColorGroup::Background, + QColor( 49, 121, 173 ) ); + pal.setColor( QPalette::Inactive, QColorGroup::Background, + QColor( 49, 121, 173 ) ); + pal.setColor( QPalette::Disabled, QColorGroup::Background, + QColor( 49, 121, 173 ) ); + setPalette( pal ); + + setFrameShape( StyledPanel ); + setFrameShadow( Sunken ); + setLineWidth( 2 ); + + setBackgroundMode( NoBackground ); // no flicker + + // Load images and prepare pixmap effect + if( m_mode == TopFrame ) + { + m_logoPixmap = + new QPixmap( locate( "data", "kbugbuster/pics/logo.png" ) ); + m_topRightPixmap = + new QPixmap( locate( "data", "kbugbuster/pics/top-right.png" ) ); + m_barsPixmap = + new QPixmap( locate( "data", "kbugbuster/pics/bars.png" ) ); + m_toolsPixmap = 0L; + m_toolsPixmapEffect = 0L; + } + else + { + m_toolsPixmap = + new QPixmap( locate( "data", "kbugbuster/pics/tools.png" ) ); + + m_toolsPixmapEffect = new KPixmap( m_toolsPixmap->size() ); + + QPainter pb; + pb.begin( m_toolsPixmapEffect ); + pb.fillRect( 0, 0, m_toolsPixmap->width(), m_toolsPixmap->height(), + QBrush( QColor( 49, 121, 172 ) ) ); + pb.drawPixmap( 0, 0, *m_toolsPixmap ); + pb.end(); + + KPixmapEffect::fade( *m_toolsPixmapEffect, 0.75, white ); + + m_logoPixmap = 0L; + m_topRightPixmap = 0L; + m_barsPixmap = 0L; + } + + // Create and fill the buffer + m_buffer = new QPixmap; +} + +void CWLoadingWidget::resizeEvent( QResizeEvent * ) +{ + updatePixmap(); +} + +void CWLoadingWidget::setText( const QString &text ) +{ + m_text = text; + updatePixmap(); + repaint(); +} + +void CWLoadingWidget::updatePixmap() +{ + QRect cr = contentsRect(); + cr.setWidth( cr.width() + 2 ); + cr.setHeight( cr.height() + 2 ); + m_buffer->resize( cr.width(), cr.height() ); + + QPainter p( m_buffer ); + + // fill background + p.fillRect( 0, 0, cr.width(), cr.height(), + QBrush( QColor( 49, 121, 173 ) ) ); + + if( m_mode == TopFrame ) + { + QFont bigFont = QFont( KGlobalSettings::generalFont().family(), + 28, QFont::Bold, true ); + + int xoffset = m_logoPixmap->width(); + + // Draw bars tiled + int xpos = xoffset; + if( width() > xpos ) + p.drawTiledPixmap( xpos, 0, cr.width() - xpos, + m_barsPixmap->height(), *m_barsPixmap ); + + // Draw logo + p.drawPixmap(width() - m_topRightPixmap->width(), 0, *m_topRightPixmap); + p.drawPixmap( 0, 0, *m_logoPixmap ); + + // Draw title text + p.setPen( black ); + p.drawText( 150, 84, cr.width() - 150, 108 - 84, + AlignAuto | AlignVCenter, m_text ); + + // Draw intro text + QString desc = i18n( "Welcome to KBugBuster, a tool to manage the " + "KDE Bug Report System. With KBugBuster you can " + "manage outstanding bug reports for KDE from a " + "convenient front end." ); + p.setPen( black ); + p.drawText( 28, 128, cr.width() - 28, 184 - 128, + AlignAuto | AlignVCenter | WordBreak, desc ); + + // Draw the caption text + QString caption = i18n( "KBugBuster" ); + p.setFont( bigFont ); + p.setPen( QColor(139, 183, 222) ); + p.drawText( 220, 60, caption ); + p.setPen( black ); + p.drawText( 217, 57, caption ); + } + else + { + // draw tools image + if( cr.height() <= 24 ) + return; + + int toolsEffectY = cr.height() - m_toolsPixmap->height(); + int toolsEffectX = cr.width() - m_toolsPixmap->width(); + if ( toolsEffectX < 0) + toolsEffectX = 0; + if ( height() < 24 + m_toolsPixmap->height() ) + toolsEffectY = 24; + + p.drawPixmap( toolsEffectX, toolsEffectY, *m_toolsPixmap ); + + // draw textbox + if( cr.height() <= 24 + 50 ) + return; + + int fheight = fontMetrics().height(); + + int boxX = 25; + int boxY = 24 + 50; + int boxW = cr.width() - 2 * boxX - 2 * 10; + if( boxW > 500 ) + boxW = 500; + + QRect br = fontMetrics().boundingRect( boxX, boxY, + boxW, cr.height() - boxY - 10 - 2 * fheight, + AlignAuto | AlignTop | WordBreak, m_text ); + + QRect box = br; + box.setHeight( box.height() + 2 * fheight ); + box.setWidth( box.width() + 2 * 10 ); + if( box.width() < cr.width() - 2 * boxX ) + box.setWidth( QMIN( cr.width() - 2 * boxX, 500 + 2 * 10 ) ); + if( box.height() < 100 ) + box.setHeight( QMIN( cr.height() - boxY - 2 * fheight - 10, 100 ) ); + + p.setClipRect( box ); + p.fillRect( box, QBrush( QColor( 204, 222, 234 ) ) ); + p.drawPixmap( toolsEffectX, toolsEffectY, *m_toolsPixmapEffect ); + + p.setViewport( box ); + p.setWindow( 0, 0, box.width(), box.height() ); + + p.drawText( 10, fheight, br.width(), + QMAX( br.height(), box.height() - 2 * fheight ), + AlignAuto | AlignVCenter | WordBreak, m_text ); + } +} + +CWLoadingWidget::~CWLoadingWidget() +{ + if( m_toolsPixmap ) + delete m_toolsPixmap; + if( m_logoPixmap ) + delete m_logoPixmap; + if( m_topRightPixmap ) + delete m_topRightPixmap; + if( m_barsPixmap ) + delete m_barsPixmap; + if( m_toolsPixmapEffect ) + delete m_toolsPixmapEffect; + + delete m_buffer; + + m_toolsPixmap = 0L; + m_logoPixmap = 0L; + m_topRightPixmap = 0L; + m_barsPixmap = 0L; + m_toolsPixmapEffect = 0L; + m_buffer = 0L; +} + +void CWLoadingWidget::mouseReleaseEvent( QMouseEvent * ) +{ + emit clicked(); +} + +void CWLoadingWidget::drawContents( QPainter *p ) +{ + if( !m_buffer || m_buffer->isNull() ) + p->fillRect( contentsRect(), QBrush( QColor( 255, 121, 172 ) ) ); + else + p->drawPixmap( QPoint( contentsRect().x(), contentsRect().y()), + *m_buffer, contentsRect() ); +} + +#include "cwloadingwidget.moc" + +/* vim: set et ts=4 sw=4 softtabstop=4: */ diff --git a/kbugbuster/gui/cwloadingwidget.h b/kbugbuster/gui/cwloadingwidget.h new file mode 100644 index 00000000..17c91f05 --- /dev/null +++ b/kbugbuster/gui/cwloadingwidget.h @@ -0,0 +1,87 @@ +/* + cwloadingwidget.h - Widget to be shown while loading data + + begin : sun march 18 17:12:24 CET 2001 + copyright : (c) 2001 by Martijn Klingens + email : klingens@kde.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. * + * * + ************************************************************************* +*/ + +#ifndef KBBMAINWINDOW_CWLOADINGWIDGET_H +#define KBBMAINWINDOW_CWLOADINGWIDGET_H + +#include <qlabel.h> +#include <qframe.h> + +class QPixmap; +class KPixmap; + +namespace KBugBusterMainWindow +{ + +/** + * @author Martijn Klingens + */ +class CWLoadingWidget : public QFrame +{ + Q_OBJECT + +public: + /** + * Use WidgetMode to specify the layout for the background images + * TopFrame loads and uses the logo and horizontal bars, + * BottomFrame loads the tools and the translucent block. + */ + enum WidgetMode { TopFrame = 0, BottomFrame }; + + CWLoadingWidget( WidgetMode mode = TopFrame, QWidget* parent = 0, + const char* name = 0 ); + CWLoadingWidget( const QString &text, WidgetMode mode = TopFrame, + QWidget* parent = 0, const char* name = 0 ); + ~CWLoadingWidget(); + + QString text() const { return m_text; } + void setText( const QString &text ); + +protected: + virtual void mouseReleaseEvent( QMouseEvent * ); + virtual void drawContents( QPainter *p ); + virtual void resizeEvent( QResizeEvent * ); + +signals: + void clicked(); + +private: + void init( WidgetMode mode ); + void updatePixmap(); + + QString m_text; + WidgetMode m_mode; + + // Pixmaps used + QPixmap *m_toolsPixmap; + QPixmap *m_logoPixmap; + QPixmap *m_topRightPixmap; + QPixmap *m_barsPixmap; + + // For performance reasons we apply the KPixmapEffect only once + KPixmap *m_toolsPixmapEffect; + + QPixmap *m_buffer; + +}; + +} // namespace + +#endif // KBBMAINWINDOW_CWLOADINGWIDGET_H + +/* vim: set et ts=4 softtabstop=4 sw=4: */ + diff --git a/kbugbuster/gui/cwsearchwidget.cpp b/kbugbuster/gui/cwsearchwidget.cpp new file mode 100644 index 00000000..8f7fcb26 --- /dev/null +++ b/kbugbuster/gui/cwsearchwidget.cpp @@ -0,0 +1,69 @@ +/* + cwsearchwidget.cpp - Search Pane + + copyright : (c) 2001 by Martijn Klingens + email : klingens@kde.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. * + * * + ************************************************************************* +*/ + +#include <qpushbutton.h> +#include <klocale.h> +#include <kdialog.h> +#include <qlineedit.h> +#include <qlayout.h> +#include <kcombobox.h> +#include <qlabel.h> + +#include "cwsearchwidget.h" + +using namespace KBugBusterMainWindow; + +CWSearchWidget::CWSearchWidget( QWidget *parent , const char * name ) +: CWSearchWidget_Base( parent, name ) +{ + // Set fonts and margins + CWSearchWidget_BaseLayout->setSpacing( KDialog::spacingHint() ); + CWSearchWidget_BaseLayout->setMargin( KDialog::marginHint() ); + + QFont f = m_searchLabel->font(); + f.setBold( true ); + m_searchLabel->setFont( f ); + + connect( m_searchDesc, SIGNAL( textChanged ( const QString & ) ), + this, SLOT( textDescriptionChanged ( const QString & ) ) ); + + connect( m_searchBugNumber, SIGNAL( textChanged ( const QString & ) ), + this, SLOT( textNumberChanged ( const QString & ) ) ); + + m_searchDescBtn->setEnabled( !m_searchDesc->text().isEmpty() ); + m_searchBugNumberBtn->setEnabled( !m_searchBugNumber->text().isEmpty() ); + +// m_searchPackages->setCompletionMode( KGlobalSettings::CompletionAuto ); +} + +CWSearchWidget::~CWSearchWidget() +{ +} + +void CWSearchWidget::textDescriptionChanged ( const QString &_text ) +{ + m_searchDescBtn->setEnabled( !_text.isEmpty() ); +} + +void CWSearchWidget::textNumberChanged ( const QString &_text ) +{ + m_searchBugNumberBtn->setEnabled( !_text.isEmpty() ); +} + +#include "cwsearchwidget.moc" + +/* vim: set et ts=4 sw=4 softtabstop=4: */ + diff --git a/kbugbuster/gui/cwsearchwidget.h b/kbugbuster/gui/cwsearchwidget.h new file mode 100644 index 00000000..4cf65720 --- /dev/null +++ b/kbugbuster/gui/cwsearchwidget.h @@ -0,0 +1,46 @@ +/* + cwsearchwidget.h - Search Pane + + copyright : (c) 2001 by Martijn Klingens + email : klingens@kde.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. * + * * + ************************************************************************* +*/ + +#ifndef KBBMAINWINDOW_CWSEARCHWIDGET_H +#define KBBMAINWINDOW_CWSEARCHWIDGET_H + +#include "cwsearchwidget_base.h" + +namespace KBugBusterMainWindow +{ + +/** + * @author Martijn Klingens + */ +class CWSearchWidget : public CWSearchWidget_Base +{ + Q_OBJECT + +public: + CWSearchWidget( QWidget* parent = 0, const char* name = 0 ); + ~CWSearchWidget(); + +public slots: + void textNumberChanged ( const QString & ); + void textDescriptionChanged ( const QString & ); +}; + +} // namespace + +#endif + +/* vim: set et ts=4 softtabstop=4 sw=4: */ + diff --git a/kbugbuster/gui/cwsearchwidget_base.ui b/kbugbuster/gui/cwsearchwidget_base.ui new file mode 100644 index 00000000..c5e9b860 --- /dev/null +++ b/kbugbuster/gui/cwsearchwidget_base.ui @@ -0,0 +1,198 @@ +<!DOCTYPE UI><UI version="3.0" stdsetdef="1"> +<class>CWSearchWidget_Base</class> +<widget class="QWidget"> + <property name="name"> + <cstring>CWSearchWidget_Base</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>165</width> + <height>266</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>m_searchLabel</cstring> + </property> + <property name="text"> + <string>Search</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>TextLabel2</cstring> + </property> + <property name="text"> + <string>&Package:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>m_searchPackages</cstring> + </property> + </widget> + <widget class="KComboBox"> + <property name="name"> + <cstring>m_searchPackages</cstring> + </property> + <property name="focusPolicy"> + <enum>StrongFocus</enum> + </property> + <property name="editable"> + <bool>true</bool> + </property> + <property name="insertionPolicy"> + <enum>NoInsertion</enum> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>TextLabel9</cstring> + </property> + <property name="text"> + <string>Bug &number:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>m_searchBugNumber</cstring> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout17</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLineEdit"> + <property name="name"> + <cstring>m_searchBugNumber</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_searchBugNumberBtn</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string></string> + </property> + <property name="pixmap"> + <pixmap>image0</pixmap> + </property> + </widget> + </hbox> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>TextLabel7</cstring> + </property> + <property name="text"> + <string>&Description:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>m_searchDesc</cstring> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>Layout15</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLineEdit"> + <property name="name"> + <cstring>m_searchDesc</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>m_searchDescBtn</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string></string> + </property> + <property name="pixmap"> + <pixmap>image0</pixmap> + </property> + </widget> + </hbox> + </widget> + <spacer> + <property name="name"> + <cstring>Spacer1</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + </spacer> + </vbox> +</widget> +<images> + <image name="image0"> + <data format="XPM.GZ" length="1725">789c75d4594fdb401405e0777e4584df5075b0e325b6aa3eb015ca1e76a8fa309ea518ca9eb055fdef9d39774815447311e4c3c73377c6cbfc5cef6477ab37373ff33052a34ef7f4b9baefcd99f1d5d5cbf71f5f7ecfcc6655cfff544daf3ffb69667638eae9def6cdb50d40e791a4fc04274aac7df1f840ecfc87bea3b350b4a5fba1787e420f42f1f8906e42d16b741b8a6e691b8a7e09e6f0323fc7eb0f42d179706e956a739afde62e146dde5cc8f93ab8487d49bf2774def65b4d1f0797b96e4c416f0757a9ceb4a1fb74ad2b23e73fd146175acecf68e7cdfe13ae87cb97fc0a9de94a8e23a52bdd9a92de129b8195f51cd1b5a9a3bfd28d56b1bf92567e3ed9bf535aeb26f67b41bbba5fcbfcb7c1755ae735af0f76e8526b23aee846d746f6eb9056a6b5d2df01ddfafee4f832ed8cb3355d0437a9298df4f318dd18b91e9b746933abe8bd60559b2caeff3adaca7c0968632a23f9fd6863a5df1bb12de2fc67b4f579997f8976b671d2ef46709b5a65a59f57baf2fdc97ed57463fc90f46a741bd7fb4c2babe3f963da5ae3647fcf8375660a53b17fde6f3a0fc5e3eb623fbfdc2f5774691b2bf335b4f2c7a59f5dda581dfb77b435b591e76731d8a42e73b27f0b74e9fa4eee8f6ff4c09696fde0e7c4b25fbf68edf3d2df88b6ae74f27c5d065b3e40f47d7411f30fb472ad53f23291420285f64df17f7a387acbc0c0c2e127ced1c14ce724e31317b8c42f5ce11a37fe7b4ce116b793cc1deef18011c678c4139ed1c5840e25ef0fbc62c12716b18465ace02b56b136c924f24ec137acfb3136b0892d6c6307bb18c655243217d6b0877d1ce0104738c6094e7126b34df5dcf9515264e8fb44eebf77ff59d718054a54feef853f67f03ec354871a4d82b03f18f8dfddfb0c73774982bbe12851e89236d16fa9c9387aea0a74d0ff5253e3e88f53b167eef9bbab3949c97b45f6fce319a732c9fb91980a73fdf93cf317e4bbac85</data> + </image> +</images> +<includes> + <include location="global" impldecl="in declaration">kcombobox.h</include> +</includes> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kbugbuster/gui/kbbbookmarkmanager.h b/kbugbuster/gui/kbbbookmarkmanager.h new file mode 100644 index 00000000..64edfc24 --- /dev/null +++ b/kbugbuster/gui/kbbbookmarkmanager.h @@ -0,0 +1,23 @@ +#ifndef KBBBOOKMARKMANAGER_H +#define KBBBOOKMARKMANAGER_H + +#include <kbookmarkmanager.h> +#include <kstandarddirs.h> + +class KBBBookmarkManager +{ +public: + static KBookmarkManager * self() { + if ( !s_bookmarkManager ) + { + QString bookmarksFile = locateLocal("data", QString::fromLatin1("kbugbuster/bookmarks.xml")); + s_bookmarkManager = KBookmarkManager::managerForFile( bookmarksFile ); + } + return s_bookmarkManager; + } + + + static KBookmarkManager *s_bookmarkManager; +}; + +#endif diff --git a/kbugbuster/gui/kbbmainwindow.cpp b/kbugbuster/gui/kbbmainwindow.cpp new file mode 100644 index 00000000..66a9b588 --- /dev/null +++ b/kbugbuster/gui/kbbmainwindow.cpp @@ -0,0 +1,504 @@ +/*************************************************************************** + kbbmainwindow.cpp - description + ------------------- + copyright : (C) 2001 by Martijn Klingens + email : klingens@kde.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. * + * * + ***************************************************************************/ + +#include "kbbmainwindow.h" + +#include <qlabel.h> +#include <qlayout.h> +#include <qmultilineedit.h> +#include <qprogressbar.h> +#include <qpushbutton.h> +#include <qtextview.h> +#include <qwidgetstack.h> + +#include <kaction.h> +#include <kbookmarkmenu.h> +#include <kcombobox.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kdialog.h> +#include <kinputdialog.h> +#include <klineedit.h> +#include <klistview.h> +#include <klocale.h> +#include <kmenubar.h> +#include <kmessagebox.h> +#include <kstatusbar.h> +#include <kstdaction.h> +#include <kstdguiitem.h> +#include <kedittoolbar.h> + + +#include "bugcommand.h" +#include "bugserver.h" +#include "bugserverconfig.h" +#include "bugsystem.h" +#include "centralwidget.h" +#include "cwbugdetails.h" +#include "kbbbookmarkmanager.h" +#include "kbbprefs.h" +#include "kfinddialog.h" +#include "packageselectdialog.h" +#include "preferencesdialog.h" + +#define ID_STATUS_MSG 1 + +using namespace KBugBusterMainWindow; + +class TextViewer : public KDialogBase +{ + public: + TextViewer( const QString &title, QWidget *parent = 0 ) + : KDialogBase( Plain, title, Ok, Ok, parent, 0, + false ) + { + QFrame *topFrame = plainPage(); + + QBoxLayout *topLayout = new QVBoxLayout( topFrame ); + + mTextView = new QTextEdit( topFrame ); + mTextView->setReadOnly( true ); + mTextView->setTextFormat( PlainText ); + topLayout->addWidget( mTextView ); + + resize( 600, 400 ); + } + + void setText( const QString &text ) + { + mTextView->setText( text ); + } + + private: + QTextEdit *mTextView; +}; + +KBookmarkManager* KBBBookmarkManager::s_bookmarkManager; + +KBBMainWindow::KBBMainWindow( const QCString &initialPackage, + const QCString &initialComponent, + const QCString &initialBug, + QWidget * , const char * name ) + : KMainWindow( 0, name ), mPreferencesDialog( 0 ), mResponseViewer( 0 ), + mBugSourceViewer( 0 ), mPackageSelectDialog( 0 ) +{ + BugSystem::self()->setCurrentServer( KBBPrefs::instance()->mCurrentServer ); + + m_statusLabel = new QLabel( i18n( "Welcome to <b>KBugBuster</b>." ), statusBar() ); + m_statusLabel->setMaximumHeight( statusBar()->fontMetrics().height() + 6 ); + m_statusLabel->setIndent( KDialog::marginHint() / 2 ); + statusBar()->addWidget( m_statusLabel, 1 ); + + m_mainWidget = new CentralWidget( initialPackage, initialComponent, + initialBug, this ); + setCentralWidget( m_mainWidget ); + + initActions(); + + m_progressBar = new QProgressBar( 100, statusBar() ); + m_progressBar->setCenterIndicator( true ); + m_progressBar->setMinimumWidth( 150 ); + m_progressBar->setMaximumHeight( statusBar()->fontMetrics().height() + 6 ); + statusBar()->addWidget( m_progressBar ); + connect( m_mainWidget, SIGNAL( resetProgressBar() ), + m_progressBar, SLOT( reset() ) ); + connect( m_mainWidget, SIGNAL( searchPackage() ), + this, SLOT( searchPackage() ) ); + connect( m_mainWidget, SIGNAL( searchBugNumber() ), + this, SLOT( searchBugNumber() ) ); + + connect( BugSystem::self(), SIGNAL( infoMessage( const QString & ) ), + SLOT( slotStatusMsg( const QString & ) ) ); + + connect( BugSystem::self(), SIGNAL( infoMessage( const QString & ) ), + SLOT( slotStatusMsg( const QString & ) ) ); + connect( BugSystem::self(), SIGNAL( infoPercent( unsigned long ) ), + SLOT( slotSetPercent( unsigned long ) ) ); + + m_mainWidget->readConfig(); +} + +KBBMainWindow::~KBBMainWindow() +{ +// kdDebug() << "KBBMainWindow::~KBBMainWindow()" << endl; + delete m_pBookmarkMenu; + + m_mainWidget->writeConfig(); + + KBBPrefs::instance()->writeConfig(); +} + +void KBBMainWindow::initActions() +{ + // Prepare and create XML GUI + fileQuit = KStdAction::quit( this, + SLOT( close() ), actionCollection() ); + fileQuit->setToolTip( i18n( "Quit KBugBuster" ) ); + + new KAction( i18n("See &Pending Changes"), "contents", 0, this, SLOT( slotListChanges() ), + actionCollection(), "file_seechanges" ); + new KAction( i18n("&Submit Changes"), "mail_send", 0, this, SLOT( slotSubmit() ), + actionCollection(), "file_submit" ); + + reloadpacklist = new KAction( i18n("Reload &Product List"), "reload", CTRL+Qt::Key_F5, m_mainWidget, SLOT( slotReloadPackageList() ), + actionCollection(), "reload_packagelist" ); + reloadpack= new KAction( i18n("Reload Bug &List (for current product)"), "reload", Qt::Key_F5, m_mainWidget, SLOT( slotReloadPackage() ), + actionCollection(), "reload_package" ); + reloadbug = new KAction( i18n("Reload Bug &Details (for current bug)"), "reload", SHIFT+Qt::Key_F5, m_mainWidget, SLOT( slotReloadBug() ), + actionCollection(), "reload_bug" ); + loadMyBugs = new KAction( i18n( "Load &My Bugs List" ), 0, m_mainWidget, SLOT( slotLoadMyBugs() ), + actionCollection(), "load_my_bugs" ); + reloadall = new KAction( i18n("Load All Bug Details (for current product)"), Qt::Key_F6, m_mainWidget, SLOT( slotRetrieveAllBugDetails() ), actionCollection(), "load_allbugs" ); + new KAction( i18n("Extract &Attachments"), "filesave", Qt::Key_F4, m_mainWidget, SLOT( slotExtractAttachments() ), + actionCollection(), "extract_attachments" ); + + new KAction( i18n("Clear Cache"), 0, this, SLOT( clearCache() ), + actionCollection(), "clear_cache" ); + + new KAction( i18n("&Search by Product..."), "goto", CTRL+Qt::Key_P, this, + SLOT( searchPackage() ), actionCollection(), "search_package" ); + new KAction( i18n("Search by Bug &Number..."), "filefind", CTRL+Qt::Key_N, this, + SLOT( searchBugNumber() ), actionCollection(), "search_bugnumber" ); + // For now "Description" searches by title. Maybe later we can have a + // full-text search interfacing bugs.kde.org and rename the current one to "By Title". + new KAction( i18n("Search by &Description...") ,"find", CTRL+Qt::Key_D, this, + SLOT( searchDescription() ), actionCollection(), "search_description" ); + +// new KAction( i18n("&Merge"), "view_remove", CTRL+Qt::Key_M, m_mainWidget, +// SLOT( mergeBugs() ), actionCollection(), "cmd_merge" ); +// new KAction( i18n("&Unmerge"), "view_top_bottom", CTRL+SHIFT+Qt::Key_M, m_mainWidget, +// SLOT( unmergeBugs() ), actionCollection(), "cmd_unmerge" ); + new KAction( i18n("C&lose..."), "edittrash", CTRL+Qt::Key_L, m_mainWidget, + SLOT( closeBug() ), actionCollection(), "cmd_close" ); +// new KAction( i18n("Clos&e Silently"), "edittrash", CTRL+Qt::Key_E, m_mainWidget, +// SLOT( closeBugSilently() ), actionCollection(), "cmd_close_silently" ); + new KAction( i18n("Re&open"), "idea", CTRL+Qt::Key_O, m_mainWidget, + SLOT( reopenBug() ), actionCollection(), "cmd_reopen" ); +// new KAction( i18n("Re&assign..."), "folder_new", CTRL+Qt::Key_A, m_mainWidget, +// SLOT( reassignBug() ), actionCollection(), "cmd_reassign" ); +// new KAction( i18n("Change &Title..."), "text_under", CTRL+Qt::Key_T, m_mainWidget, +// SLOT( titleBug() ), actionCollection(), "cmd_title" ); +// new KAction( i18n("Change &Severity..."), "edit", CTRL+Qt::Key_S, m_mainWidget, +// SLOT( severityBug() ), actionCollection(), "cmd_severity" ); + new KAction( i18n("&Reply..."), "mail_replyall",CTRL+Qt::Key_R , m_mainWidget, + SLOT( replyBug() ), actionCollection(), "cmd_reply" ); + new KAction( i18n("Reply &Privately..."), "mail_reply", CTRL+Qt::Key_I, m_mainWidget, + SLOT( replyPrivateBug() ), actionCollection(), "cmd_replyprivate" ); + + KStdAction::showMenubar(this, SLOT( slotToggleMenubar() ), actionCollection() ); +#if KDE_IS_VERSION( 3, 1, 90 ) + createStandardStatusBarAction(); + setStandardToolBarMenuEnabled(true); +#endif + + m_disconnectedAction = new KToggleAction( i18n("&Disconnected Mode"), 0, + this, + SLOT( slotDisconnectedAction() ), + actionCollection(), + "settings_disconnected" ); + m_disconnectedAction->setChecked( BugSystem::self()->disconnected() ); + + // Bookmarks menu + m_pamBookmarks = new KActionMenu( i18n( "&Bookmarks" ), "bookmark", actionCollection(), "bookmarks" ); + m_pBookmarkMenu = new KBookmarkMenu( KBBBookmarkManager::self(), this, m_pamBookmarks->popupMenu(), actionCollection(), true ); + + KStdAction::preferences( this, SLOT(preferences()), actionCollection() ); + + KToggleAction *toggleTmp = new KToggleAction( i18n("Show Closed Bugs"), "recycled", 0, this, SLOT( slotToggleDone() ), + actionCollection(), "cmd_toggle_done" ); +#if KDE_IS_VERSION( 3, 2, 90 ) + toggleTmp->setCheckedState(i18n("Hide Closed Bugs")); +#endif + toggleTmp->setChecked( KBBPrefs::instance()->mShowClosedBugs ); + + toggleTmp =new KToggleAction( i18n("Show Wishes"), "bookmark", 0, this, SLOT( slotToggleWishes() ), + actionCollection(), "cmd_toggle_wishes" ); +#if KDE_IS_VERSION( 3, 2, 90 ) + toggleTmp->setCheckedState(i18n("Hide Wishes")); +#endif + toggleTmp->setChecked(KBBPrefs::instance()->mShowWishes); + + mSelectServerAction = new KSelectAction( i18n( "Select Server" ), 0, 0, + this, + SLOT( slotSelectServer() ), + actionCollection(), + "select_server" ); + + setupSelectServerAction(); + + if ( KBBPrefs::instance()->mDebugMode ) { + new KAction( i18n("Show Last Server Response..."), 0 , this, + SLOT( showLastResponse() ), actionCollection(), + "debug_lastresponse" ); + new KAction( i18n("Show Bug HTML Source..."), 0 , this, + SLOT( showBugSource() ), actionCollection(), + "debug_showbugsource" ); + } + +#if KDE_IS_VERSION( 3, 2, 90 ) + setupGUI( (ToolBar | Keys | StatusBar | Save | Create ), "kbugbusterui.rc" ); +#else + createGUI(); +#endif +} + +void KBBMainWindow::slotToggleMenubar() +{ + if ( !hasMenuBar() ) + return; + + if ( menuBar()->isVisible() ) + menuBar()->hide(); + else + menuBar()->show(); +} + +void KBBMainWindow::setupSelectServerAction() +{ + QStringList servers; + int current = -1; + QValueList<BugServer *> serverList = BugSystem::self()->serverList(); + QValueList<BugServer *>::ConstIterator it; + for ( it = serverList.begin(); it != serverList.end(); ++it ) { + QString name = (*it)->serverConfig().name(); + servers.append( name ); + if ( name == KBBPrefs::instance()->mCurrentServer ) { + current = servers.count() - 1; + } + } + mSelectServerAction->setItems( servers ); + if ( current >= 0 ) { + mSelectServerAction->setCurrentItem( current ); + } +} + +QString KBBMainWindow::currentURL() const +{ + QString number=m_mainWidget->currentNumber(); + + if (number.isEmpty()) + return ""; + else + return "bug:"+number; +} + +QString KBBMainWindow::currentTitle() const +{ + return "#"+m_mainWidget->currentNumber()+": "+m_mainWidget->currentTitle(); +} + +void KBBMainWindow::openBookmarkURL( const QString & url ) +{ + if ( url.left(4)=="bug:" ) { + QString bugnumber = url.mid(4); + m_mainWidget->slotRetrieveBugDetails( Bug::fromNumber( bugnumber ) ); + } +} + +// --- SLOT IMPLEMENTATIONS ------------------------------------------------- + +void KBBMainWindow::slotDisconnectedAction() +{ + BugSystem::self()->setDisconnected( m_disconnectedAction->isChecked() ); + + bool enable = !m_disconnectedAction->isChecked(); + + reloadpacklist->setEnabled( enable ); + reloadpacklist->setEnabled( enable ); + reloadpack->setEnabled( enable ); + reloadbug->setEnabled( enable ); + reloadall->setEnabled( enable ); + loadMyBugs->setEnabled( enable ); +} + +void KBBMainWindow::slotStatusMsg( const QString &text ) +{ + // Change status message permanently + m_statusLabel->setText( text ); +} + +void KBBMainWindow::slotSubmit() +{ + BugSystem::self()->sendCommands(); +} + +void KBBMainWindow::slotListChanges() +{ + QStringList list = BugSystem::self()->server()->listCommands(); + + if (list.count() > 0) + { + int ret = KMessageBox::questionYesNoList( this, i18n("List of pending commands:"), + list, QString::null, KStdGuiItem::clear(), KStdGuiItem::close() ); + if ( ret == KMessageBox::Yes ) + { + // Ask for confirmation, it's too easy to click the wrong button in the above dlg box + if ( KMessageBox::warningContinueCancel( this, i18n("Do you really want to delete all commands?"), + i18n("Confirmation Required"), KGuiItem( i18n("&Delete"), "editdelete"), "clearcommands", true) + == KMessageBox::Continue ) + BugSystem::self()->clearCommands(); + } + } + else + { + KMessageBox::information( this, i18n("There are no pending commands.") ); + } +} + +void KBBMainWindow::slotSetPercent( unsigned long percent ) +{ + // KIO::Job::percent() shouldn't return an unsigned long. - Frerich + m_progressBar->setProgress( percent ); +} + +void KBBMainWindow::searchPackage() +{ + if ( !mPackageSelectDialog ) { + mPackageSelectDialog = new PackageSelectDialog( this ); + } + mPackageSelectDialog->setPackages( BugSystem::self()->packageList() ); + BugServerConfig cfg = BugSystem::self()->server()->serverConfig(); + QStringList recent = cfg.recentPackages(); + kdDebug() << "MainWindow RECENT: " << recent.join(",") << endl; + mPackageSelectDialog->setRecentPackages( recent ); + + mPackageSelectDialog->exec(); + Package package = mPackageSelectDialog->selectedPackage(); + + if ( package.isNull() ) { + return; + } + + QString component = mPackageSelectDialog->selectedComponent(); + m_mainWidget->slotRetrieveBugList( package.name(), component ); +} + +void KBBMainWindow::searchBugNumber() +{ + bool ok = false; + QString result = KInputDialog::getText( i18n("Search for Bug Number"), + i18n("Please enter a bug number:"), + QString::null, &ok, this ); + if ( ok ) { + //Strip whitespace and # if needed + result = result.stripWhiteSpace(); + if (result[0]=='#') + result = result.mid(1); + } + + if ( ok && !result.isEmpty()) { + // ### bad way to instantiate a bug! doesn't get us the details! + // Right - but do we need the details in this case ? There's no listview entry here... (DF) + m_mainWidget->slotRetrieveBugDetails( Bug::fromNumber( result ) ); + } +} + +void KBBMainWindow::searchDescription() +{ + kdDebug() << "KBBMainWindow::searchDescription()." << endl; + //KMessageBox::sorry(this,i18n("searchDescription(): to be implemented."),i18n("Not implemented")); + KFindDialog dlg( this ); + if ( dlg.exec() == KDialogBase::Accepted ) + m_mainWidget->searchBugByTitle( dlg.options(), dlg.pattern() ); +} + +bool KBBMainWindow::queryClose() +{ + if ( ! BugSystem::self()->server()->commandsPending() ) return true; + + int result = KMessageBox::warningYesNoCancel(this,i18n("There are unsent bug commands." + " Do you want to send them now?"), QString::null, i18n("Send"), i18n("Do Not Send")); + if ( result == KMessageBox::Cancel ) return false; + if ( result == KMessageBox::Yes ) { + BugSystem::self()->sendCommands(); + } + return true; +} + +void KBBMainWindow::preferences() +{ + if (!mPreferencesDialog) { + mPreferencesDialog = new PreferencesDialog(this); + connect( mPreferencesDialog, SIGNAL( configChanged() ), + SLOT( setupSelectServerAction() ) ); + connect( mPreferencesDialog, SIGNAL( configChanged() ), + m_mainWidget, SLOT( slotReloadPackage() ) ); + } + mPreferencesDialog->show(); + mPreferencesDialog->raise(); +} + +void KBBMainWindow::updatePackage() +{ + m_mainWidget->updatePackage(); +} + +void KBBMainWindow::slotToggleDone() +{ + KBBPrefs::instance()->mShowClosedBugs=!(KBBPrefs::instance()->mShowClosedBugs); + updatePackage(); +} + +void KBBMainWindow::slotToggleWishes() +{ + KBBPrefs::instance()->mShowWishes=!(KBBPrefs::instance()->mShowWishes); + updatePackage(); +} + +void KBBMainWindow::slotSelectServer() +{ + m_mainWidget->writeConfig(); + + QString currentServer = mSelectServerAction->currentText(); + + BugSystem::self()->setCurrentServer( currentServer ); + + m_mainWidget->initialize(); +} + +void KBBMainWindow::showLastResponse() +{ + if ( !mResponseViewer ) { + mResponseViewer = new TextViewer( i18n("Last Server Response"), this ); + } + + mResponseViewer->setText( BugSystem::lastResponse() ); + + mResponseViewer->show(); + mResponseViewer->raise(); +} + +void KBBMainWindow::showBugSource() +{ + if ( !mBugSourceViewer ) { + mBugSourceViewer = new TextViewer( i18n("Bug HTML Source"), this ); + } + + mBugSourceViewer->setText( m_mainWidget->bugDetailsWidget()->source() ); + + mBugSourceViewer->show(); + mBugSourceViewer->raise(); +} + +void KBBMainWindow::clearCache() +{ + BugSystem::self()->server()->cache()->clear(); +} + +#include "kbbmainwindow.moc" + +/* vim: set et ts=4 sw=4 softtabstop=4: */ + diff --git a/kbugbuster/gui/kbbmainwindow.h b/kbugbuster/gui/kbbmainwindow.h new file mode 100644 index 00000000..f139c733 --- /dev/null +++ b/kbugbuster/gui/kbbmainwindow.h @@ -0,0 +1,144 @@ +/* + kbbmainwindow.h - KBugBuster's main window + + Copyright (c) 2001-2004 by Martijn Klingens <klingens@kde.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. * + * * + ************************************************************************* +*/ + +#ifndef KBBMAINWINDOW_H +#define KBBMAINWINDOW_H + +#include <kapplication.h> +#include <kbookmarkmanager.h> +#include <kmainwindow.h> +#include <qmap.h> + +#include "package.h" +#include "bug.h" +#include "bugdetails.h" + +class KAction; +class KActionMenu; +class KBookmarkMenu; +class KToggleAction; +class KSelectAction; +class QLabel; +class QListViewItem; +class QProgressBar; +class PreferencesDialog; +class TextViewer; +class PackageSelectDialog; + +namespace KBugBusterMainWindow +{ + class CentralWidget; +} + +/** + * @author Martijn Klingens + */ +class KBBMainWindow : public KMainWindow, virtual public KBookmarkOwner +{ + Q_OBJECT + public: + /** + * construtor of KBugBusterApp, calls all init functions to create the application. + */ + KBBMainWindow( const QCString &initialPackage = "", + const QCString &initialCpomponent = "", + const QCString &initialBug = "", + QWidget* parent = 0, const char* name = 0 ); + ~KBBMainWindow(); + + /// Overloaded functions of KBookmarkOwner + virtual void openBookmarkURL( const QString & _url ); + virtual QString currentTitle() const; + virtual QString currentURL() const; + + public slots: + /** + * Event handlers for our KActions + */ + void slotStatusMsg( const QString &text ); + void slotDisconnectedAction(); + void slotSubmit(); + void slotListChanges(); + void slotSetPercent( unsigned long percent ); + void slotSelectServer(); + + void showLastResponse(); + void showBugSource(); + + void clearCache(); + + /** + * Other event handlers + */ + + void searchPackage(); + void searchBugNumber(); + void searchDescription(); + + void preferences(); + void updatePackage(); + void slotToggleDone(); + void slotToggleWishes(); + + protected: + virtual bool queryClose(); + + protected slots: + void setupSelectServerAction(); + void slotToggleMenubar(); + + private: + void initActions(); + + /** + * Our main widget + */ + KBugBusterMainWindow::CentralWidget *m_mainWidget; + + /** + * Used KActions + */ + KAction *fileQuit; + KAction *reloadpacklist; + KAction *reloadpack; + KAction *reloadbug; + KAction *reloadall; + KAction *loadMyBugs; + KToggleAction *m_disconnectedAction; + + /** + * Status bar label. We need this, because the default Qt version doesn't + * support rich text in the messages + */ + QLabel *m_statusLabel; + QProgressBar *m_progressBar; + + PreferencesDialog *mPreferencesDialog; + + KActionMenu *m_pamBookmarks; + KBookmarkMenu* m_pBookmarkMenu; + + KSelectAction *mSelectServerAction; + + TextViewer *mResponseViewer; + TextViewer *mBugSourceViewer; + + PackageSelectDialog *mPackageSelectDialog; +}; + +#endif + +/* vim: set et ts=4 softtabstop=4 sw=4: */ + diff --git a/kbugbuster/gui/kbugbusterui.rc b/kbugbuster/gui/kbugbusterui.rc new file mode 100644 index 00000000..f347855c --- /dev/null +++ b/kbugbuster/gui/kbugbusterui.rc @@ -0,0 +1,78 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="kbugbuster" version="11"> + +<MenuBar> + <Menu name="file" noMerge="1"><text>&File</text> + <Action name="file_new_window" /> + <Action name="file_seechanges" /> + <Action name="file_submit" /> + <Separator/> + <Action name="file_quit" /> + </Menu> + <Menu name="view"><text>&View</text> + <Action name="reload_packagelist" /> + <Action name="reload_package" /> + <Action name="reload_bug" /> + <Separator/> + <Action name="load_my_bugs" /> + <Action name="load_allbugs" /> + <Separator/> + <Action name="extract_attachments" /> + <Separator/> + <Action name="clear_cache" /> + <Separator/> + <Action name="debug_lastresponse" /> + <Action name="debug_showbugsource" /> + </Menu> + <Menu name="search"><text>S&earch</text> + <Action name="search_package" /> + <Action name="search_bugnumber" /> + <Action name="search_description" /> + </Menu> + <Action name="bookmarks"/> + <Menu name="commands"><text>&Commands</text> + <!-- <Action name="cmd_merge" /> + <Action name="cmd_unmerge" /> + <Separator/> --> + <Action name="cmd_close" /> + <Action name="cmd_close_silently" /> + <Action name="cmd_reopen" /> + <Action name="cmd_reassign" /> + <Action name="cmd_title" /> + <Action name="cmd_severity" /> + <Separator/> + <Action name="cmd_reply" /> + <Action name="cmd_replyprivate" /> + </Menu> + <Menu name="settings"><text>&Settings</text> + <Action name="settings_disconnected" append="save_merge"/> + <Action name="select_server" append="save_merge"/> + </Menu> +</MenuBar> + +<ToolBar name="searchToolBar"><text>Search Toolbar</text> + <Action name="search_package" /> + <Action name="search_bugnumber" /> + <Action name="search_description" /> +</ToolBar> + +<ToolBar name="commandToolBar"><text>Command Toolbar</text> + <Action name="cmd_merge" /> + <Action name="cmd_unmerge" /> + <Separator/> + <Action name="cmd_close" /> + <Action name="cmd_reopen" /> + <Action name="cmd_reassign" /> + <Action name="cmd_title" /> + <Action name="cmd_severity" /> + <Separator/> + <Action name="cmd_reply" /> + <Action name="cmd_replyprivate" /> +</ToolBar> + +<ToolBar name="settingToolBar"><text>Settings Toolbar</text> + <Action name="cmd_toggle_wishes" /> + <Action name="cmd_toggle_done" /> +</ToolBar> + +</kpartgui> diff --git a/kbugbuster/gui/loadallbugsdlg.cpp b/kbugbuster/gui/loadallbugsdlg.cpp new file mode 100644 index 00000000..40ecd6d8 --- /dev/null +++ b/kbugbuster/gui/loadallbugsdlg.cpp @@ -0,0 +1,92 @@ +/*************************************************************************** + loadallbugsdlg.cpp - progress dialog while loading all bugs for a package + ------------------- + copyright : (C) 2002 by David Faure + email : david@mandrakesoft.com + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 version 2. * + * * + ***************************************************************************/ + +#include "loadallbugsdlg.h" +#include "bugsystem.h" +#include "bugcache.h" +#include <kdebug.h> +#include <kio/defaultprogress.h> +#include <qtimer.h> + +LoadAllBugsDlg::LoadAllBugsDlg( const Package& pkg, const QString &component ) + : QDialog( 0L, "progressdlg", TRUE ) +{ + m_bugLoadingProgress = new KIO::DefaultProgress( this ); + connect( m_bugLoadingProgress, SIGNAL( stopped() ), + this, SLOT( slotStopped() ) ); + setCaption( i18n( "Loading All Bugs for Product %1" ).arg( pkg.name() ) ); + connect( BugSystem::self(), + SIGNAL( bugDetailsAvailable( const Bug &, const BugDetails & ) ), + SLOT( slotBugDetailsAvailable( const Bug &, const BugDetails & ) ) ); + connect( BugSystem::self(), + SIGNAL( bugDetailsLoadingError() ), + SLOT( slotBugDetailsLoadingError() ) ); + // The package (and its buglist) has to be in the cache already... + m_bugs = BugSystem::self()->cache()->loadBugList( pkg, component, true ); + m_count = m_bugs.count(); + m_bugLoadingProgress->slotTotalSize( 0, m_count ); + kdDebug() << "LoadAllBugsDlg: " << m_count << " bugs to load" << endl; + m_processed = 0; + QTimer::singleShot( 0, this, SLOT( loadNextBug() ) ); +} + +void LoadAllBugsDlg::slotBugDetailsAvailable( const Bug &bug, const BugDetails & ) +{ + kdDebug() << "LoadAllBugsDlg::slotBugDetailsAvailable " << bug.number() << endl; + m_bugLoadingProgress->slotInfoMessage( 0L, i18n( "Bug %1 loaded" ).arg(bug.number()) ); + loadNextBug(); +} + +void LoadAllBugsDlg::slotBugDetailsLoadingError() +{ + // Abort at the first error. Otherwise we get spammed with "no host bugs.kde.org" msg boxes.... + reject(); +} + +void LoadAllBugsDlg::loadNextBug() +{ + kdDebug() << "LoadAllBugsDlg::loadNextBug" << endl; + if ( m_bugs.isEmpty() ) + { + kdDebug() << "LoadAllBugsDlg::loadNextBug DONE!" << endl; + accept(); + } else { + BugCache* cache = BugSystem::self()->cache(); + Bug bug; + do { + bug = m_bugs.front(); + m_bugs.pop_front(); + m_processed++; + m_bugLoadingProgress->slotPercent( 0L, (100 * m_processed) / m_count ); + kdDebug() << "looking at bug " << bug.number() << " in cache:" << cache->hasBugDetails( bug ) << endl; + } while ( cache->hasBugDetails( bug ) && !m_bugs.isEmpty() ); + if ( !cache->hasBugDetails( bug ) ) { + kdDebug() << "LoadAllBugsDlg::loadNextBug loading bug " << bug.number() << endl; + BugSystem::self()->retrieveBugDetails( bug ); + } else { + kdDebug() << "LoadAllBugsDlg::loadNextBug DONE!" << endl; + accept(); + } + } +} + +void LoadAllBugsDlg::slotStopped() +{ + kdDebug() << "LoadAllBugsDlg::slotStopped" << endl; + // TODO abort job? + reject(); +} + +#include "loadallbugsdlg.moc" diff --git a/kbugbuster/gui/loadallbugsdlg.h b/kbugbuster/gui/loadallbugsdlg.h new file mode 100644 index 00000000..685abcb0 --- /dev/null +++ b/kbugbuster/gui/loadallbugsdlg.h @@ -0,0 +1,43 @@ +/*************************************************************************** + loadallbugsdlg.h - progress dialog while loading all bugs for a package + ------------------- + copyright : (C) 2002 by David Faure + email : david@mandrakesoft.com + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 version 2. * + * * + ***************************************************************************/ +#ifndef loadallbugsdlg_h +#define loadallbugsdlg_h + +#include <qdialog.h> +#include "bug.h" +class Package; +class BugDetails; + +namespace KIO { class DefaultProgress; } + +class LoadAllBugsDlg : public QDialog +{ + Q_OBJECT +public: + LoadAllBugsDlg( const Package& pkg, const QString &component ); + +protected slots: + void slotBugDetailsAvailable( const Bug &bug, const BugDetails &bd ); + void slotBugDetailsLoadingError(); + void slotStopped(); + void loadNextBug(); +private: + Bug::List m_bugs; + unsigned int m_processed; + unsigned int m_count; + KIO::DefaultProgress* m_bugLoadingProgress; +}; + +#endif diff --git a/kbugbuster/gui/messageeditor.cpp b/kbugbuster/gui/messageeditor.cpp new file mode 100644 index 00000000..517ef80b --- /dev/null +++ b/kbugbuster/gui/messageeditor.cpp @@ -0,0 +1,117 @@ +#include <qcombobox.h> +#include <ktextedit.h> +#include <kinputdialog.h> +#include <qlayout.h> +#include <qlabel.h> + +#include <klocale.h> +#include <kmessagebox.h> +#include <kdebug.h> + +#include "kbbprefs.h" + +#include "messageeditor.h" +#include <qpushbutton.h> +#include "messageeditor.moc" + +MessageEditor::MessageEditor( QWidget *parent ) + : KDialogBase(Plain,i18n("Edit Message Buttons"),Ok|Cancel,Ok,parent,0, + true,true) +{ + QFrame *topFrame = plainPage(); + QBoxLayout *topLayout = new QVBoxLayout(topFrame,0,spacingHint()); + + QBoxLayout *selectionLayout = new QHBoxLayout; + topLayout->addLayout(selectionLayout); + + QLabel *selectionLabel = new QLabel(i18n("Button:"),topFrame); + selectionLayout->addWidget(selectionLabel); + + mSelectionCombo = new QComboBox(topFrame); + selectionLayout->addWidget(mSelectionCombo); + connect(mSelectionCombo,SIGNAL(activated(int)),SLOT(changeMessage())); + + QPushButton *addButton = new QPushButton(i18n("Add Button..."),topFrame); + selectionLayout->addWidget(addButton); + connect(addButton,SIGNAL(clicked()),SLOT(addButton())); + + QPushButton *removeButton = new QPushButton(i18n("Remove Button"),topFrame); + selectionLayout->addWidget(removeButton); + connect(removeButton,SIGNAL(clicked()),SLOT(removeButton())); + + mMessageEdit = new KTextEdit(topFrame); + topLayout->addWidget(mMessageEdit,1); + + updateConfig(); +} + +void MessageEditor::updateConfig() +{ + mMessageButtons = KBBPrefs::instance()->mMessageButtons; + + mSelectionCombo->clear(); + + QMap<QString,QString>::ConstIterator it; + for(it = mMessageButtons.begin();it != mMessageButtons.end();++it) { + mSelectionCombo->insertItem(it.key()); + } + + updateMessage(); +} + +void MessageEditor::addButton() +{ + QString txt; + txt = KInputDialog::getText(i18n("Add Message Button"), + i18n("Enter button name:"), QString::null, + NULL, this ); + + if ( !txt.isNull() ) { + saveMessage(); + mSelectionCombo->insertItem(txt); + mMessageButtons.insert(txt,""); + mSelectionCombo->setCurrentItem(mSelectionCombo->count()-1); + updateMessage(); + } + +} + +void MessageEditor::removeButton() +{ + int result = KMessageBox::warningContinueCancel(this, + i18n("Remove the button %1?").arg(mSelectionCombo->currentText()), + i18n("Remove"), KGuiItem( i18n("Delete"), "editdelete") ); + + if (result == KMessageBox::Continue) { + mMessageButtons.remove(mSelectionCombo->currentText()); + mSelectionCombo->removeItem(mSelectionCombo->currentItem()); + mSelectionCombo->setCurrentItem(0); + updateMessage(); + } +} + +void MessageEditor::changeMessage() +{ + saveMessage(); + updateMessage(); +} + +void MessageEditor::updateMessage() +{ + mCurrentButton = mSelectionCombo->currentText(); + + mMessageEdit->setText(mMessageButtons[mCurrentButton]); +} + +void MessageEditor::saveMessage() +{ + mMessageButtons.replace(mCurrentButton,mMessageEdit->text()); +} + +void MessageEditor::slotOk() +{ + saveMessage(); + + KBBPrefs::instance()->mMessageButtons = mMessageButtons; + accept(); +} diff --git a/kbugbuster/gui/messageeditor.h b/kbugbuster/gui/messageeditor.h new file mode 100644 index 00000000..e32e8cec --- /dev/null +++ b/kbugbuster/gui/messageeditor.h @@ -0,0 +1,33 @@ +#ifndef MESSAGEEDITOR_H +#define MESSAGEEDITOR_H + +#include <kdialogbase.h> + +class QComboBox; +class KTextEdit; + +class MessageEditor : public KDialogBase { + Q_OBJECT + public: + MessageEditor( QWidget *parent ); + + protected slots: + void slotOk(); + + private slots: + void addButton(); + void removeButton(); + void changeMessage(); + void saveMessage(); + void updateMessage(); + void updateConfig(); + + private: + QComboBox *mSelectionCombo; + KTextEdit *mMessageEdit; + + QString mCurrentButton; + QMap <QString,QString> mMessageButtons; +}; + +#endif diff --git a/kbugbuster/gui/msginputdialog.cpp b/kbugbuster/gui/msginputdialog.cpp new file mode 100644 index 00000000..a3fc39c7 --- /dev/null +++ b/kbugbuster/gui/msginputdialog.cpp @@ -0,0 +1,226 @@ +// $Id$ +// (c) 2001, Cornelius Schumacher + +#include <ktextedit.h> +#include <qlayout.h> + +#include <klocale.h> +#include <kdebug.h> +#include <qcombobox.h> +#include <qsplitter.h> +#include <qlabel.h> + +#include "messageeditor.h" +#include "kbbprefs.h" +#include "bugsystem.h" +#include "bugcommand.h" + +#include "msginputdialog.h" +#include "msginputdialog.moc" + +MsgInputDialog::MsgInputDialog(MsgInputDialog::MessageType type, const Bug &bug, + const Package &package, const QString "edMsg, + QWidget *parent) + : KDialogBase(Plain,QString::null,User1|User2|Ok|Cancel,Ok,parent,0,false, + true,KStdGuiItem::clear(),i18n( "&Edit Presets..." )), + mBug( bug ), + mPackage( package ), + mType( type ) +{ + switch ( mType ) { + case Close: + setCaption( i18n("Close Bug %1").arg( mBug.number() ) ); + break; + case Reply: + setCaption( i18n("Reply to Bug") ); + break; + case ReplyPrivate: + setCaption( i18n("Reply Privately to Bug") ); + break; + default: + break; + } + + QFrame *topFrame = plainPage(); + ( new QHBoxLayout( topFrame ) )->setAutoAdd( true ); + + mSplitter = new QSplitter( QSplitter::Horizontal, topFrame ); + + QWidget *w = new QWidget( mSplitter ); + ( new QVBoxLayout( w, spacingHint(), -1 ) )->setAutoAdd( true ); + + if ( mType == Reply ) { + QWidget *r = new QWidget( w ); + QHBoxLayout* rlayout = new QHBoxLayout( r ); + + QLabel *rlabel = new QLabel( i18n("&Recipient:"),r ); + QFont f = r->font(); + f.setBold( true ); + r->setFont( f ); + rlayout->add( rlabel ); + + mRecipient = new QComboBox( r ); + mRecipient->insertItem( i18n("Normal (bugs.kde.org & Maintainer & kde-bugs-dist)"), BugCommand::Normal ); + mRecipient->insertItem( i18n("Maintonly (bugs.kde.org & Maintainer)"), BugCommand::Maintonly ); + mRecipient->insertItem( i18n("Quiet (bugs.kde.org only)"), BugCommand::Quiet ); + rlabel->setBuddy( mRecipient ); + rlayout->add( mRecipient ); + + QSpacerItem *rspacer= new QSpacerItem( 1,1,QSizePolicy::Expanding ); + rlayout->addItem( rspacer ); + + // Reply currently only replies to the bug tracking system + r->hide(); + } + + + QLabel *l = new QLabel( i18n( "&Message" ), w ); + QFont f = l->font(); + f.setBold( true ); + l->setFont( f ); + + mMessageEdit = new KTextEdit( w ); + mMessageEdit->setMinimumWidth( mMessageEdit->fontMetrics().width('x') * 72 ); + mMessageEdit->setWordWrap( QTextEdit::FixedColumnWidth ); + mMessageEdit->setWrapColumnOrWidth( 72 ); + l->setBuddy( mMessageEdit ); + + w = new QWidget( mSplitter ); + ( new QVBoxLayout( w, spacingHint(), -1 ) )->setAutoAdd( true ); + l = new QLabel( i18n( "&Preset Messages" ), w ); + l->setFont( f ); + + mPresets = new KListBox( w ); + updatePresets(); + l->setBuddy( mPresets ); + + connect( mPresets, SIGNAL( executed( QListBoxItem* ) ), + SLOT( slotPresetSelected( QListBoxItem * ) ) ); + connect( this, SIGNAL( user2Clicked() ), SLOT( editPresets() ) ); + connect( this, SIGNAL( user1Clicked() ), SLOT( clearMessage() ) ); + mMessageEdit->setFocus(); + + if ( !quotedMsg.isEmpty() ) + insertQuotedMessage( quotedMsg ); + + readConfig(); +} + +MsgInputDialog::~MsgInputDialog() +{ + kdDebug() << "MsgInputDialog::~MsgInputDialog()" << endl; + writeConfig(); +} + +void MsgInputDialog::readConfig() +{ + resize( KBBPrefs::instance()->mMsgDlgWidth, + KBBPrefs::instance()->mMsgDlgHeight ); + QValueList<int> sizes = KBBPrefs::instance()->mMsgDlgSplitter; + mSplitter->setSizes( sizes ); +} + +void MsgInputDialog::writeConfig() +{ + KBBPrefs::instance()->mMsgDlgWidth = width(); + KBBPrefs::instance()->mMsgDlgHeight = height(); + KBBPrefs::instance()->mMsgDlgSplitter = mSplitter->sizes(); +} + +void MsgInputDialog::updatePresets() +{ + mPresets->clear(); + + QMap<QString,QString> messageButtons = KBBPrefs::instance()->mMessageButtons; + + int id = 0; + QMap<QString,QString>::ConstIterator it; + for( it = messageButtons.begin(); it != messageButtons.end(); ++it ) + mPresets->insertItem( it.key(), id ); +} + +QString MsgInputDialog::message() const +{ + return mMessageEdit->text(); +} + +void MsgInputDialog::editPresets() +{ + MessageEditor *dlg = new MessageEditor(this); + dlg->exec(); + delete dlg; + + updatePresets(); +} + +void MsgInputDialog::slotPresetSelected( QListBoxItem *lbi ) +{ + mMessageEdit->setText( KBBPrefs::instance()->mMessageButtons[ lbi->text() ] ); +} + +void MsgInputDialog::clearMessage() +{ + mMessageEdit->setText(""); +} + +void MsgInputDialog::queueCommand() +{ + switch ( mType ) { + case Close: + BugSystem::self()->queueCommand( + new BugCommandClose( mBug, message(), mPackage ) ); + break; + case Reply: + BugSystem::self()->queueCommand( + new BugCommandReply( mBug, message(), mRecipient->currentItem() ) ); + break; + case ReplyPrivate: + BugSystem::self()->queueCommand( + new BugCommandReplyPrivate( mBug, mBug.submitter().email, + message() ) ); + break; + default: + break; + } +} + +void MsgInputDialog::slotOk() +{ + queueCommand(); + delete this; +} + +void MsgInputDialog::slotCancel() +{ + delete this; +} + +void MsgInputDialog::insertQuotedMessage( const QString &msg ) +{ + Q_ASSERT( mMessageEdit->wordWrap() == QTextEdit::FixedColumnWidth ); + + const QString quotationMarker = "> "; + const unsigned int wrapColumn = mMessageEdit->wrapColumnOrWidth(); + + // ### Needs something more sophisticated than simplifyWhiteSpace to + // handle quoting multiple paragraphs properly. + QString line = msg.simplifyWhiteSpace(); + + QString quotedMsg; + while ( line.length() + quotationMarker.length() + 1 > wrapColumn ) { + int pos = wrapColumn - quotationMarker.length() - 1; + while ( pos > 0 && !line[ pos ].isSpace() ) + --pos; + if ( pos == 0 ) + pos = wrapColumn; + quotedMsg += quotationMarker + line.left( pos ) + "\n"; + line = line.mid( pos + 1 ); + } + quotedMsg += quotationMarker + line + "\n\n"; + + mMessageEdit->setText( quotedMsg ); + + const int lastPara = mMessageEdit->paragraphs() - 1; + const int lastParaLen = mMessageEdit->paragraphLength( lastPara ) - 1; + mMessageEdit->setCursorPosition( lastPara, lastParaLen ); +} diff --git a/kbugbuster/gui/msginputdialog.h b/kbugbuster/gui/msginputdialog.h new file mode 100644 index 00000000..9de767e3 --- /dev/null +++ b/kbugbuster/gui/msginputdialog.h @@ -0,0 +1,55 @@ +#ifndef MSGINPUTDIALOG_H +#define MSGINPUTDIALOG_H + +#include <kdialogbase.h> + +#include "bug.h" +#include "package.h" + +class KTextEdit; +class QSplitter; +class KListBox; + +class MsgInputDialog : public KDialogBase +{ + Q_OBJECT + public: + enum MessageType{ Close, Reply, ReplyPrivate }; + + MsgInputDialog( MessageType, const Bug &, const Package &, + const QString &, QWidget *parent=0); + virtual ~MsgInputDialog(); + + QString message() const; + + protected slots: + void slotOk(); + void slotCancel(); + + private slots: + void editPresets(); + void updatePresets(); + void slotPresetSelected( QListBoxItem * ); + void clearMessage(); + void queueCommand(); + + private: + void createButtons(); + void createLayout(); + + void readConfig(); + void writeConfig(); + + void insertQuotedMessage( const QString "edMsg ); + + QComboBox *mRecipient; + KTextEdit *mMessageEdit; + QSplitter *mSplitter; + KListBox *mPresets; + + Bug mBug; + Package mPackage; + MessageType mType; +}; + +#endif diff --git a/kbugbuster/gui/packagelvi.cpp b/kbugbuster/gui/packagelvi.cpp new file mode 100644 index 00000000..7fe7cfe6 --- /dev/null +++ b/kbugbuster/gui/packagelvi.cpp @@ -0,0 +1,38 @@ +/* + packagelvi.cpp - Custom QListViewItem that holds a Package object + + copyright : (c) 2001 by Martijn Klingens + email : klingens@kde.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. * + * * + ************************************************************************* +*/ + +#include "packagelvi.h" + +PackageLVI::PackageLVI( QListView *parent , const Package &pkg, const QString &component ) +: QListViewItem( parent, pkg.name(), pkg.description() ) +{ + m_package = pkg; + m_component = component; +} + +PackageLVI::PackageLVI( QListViewItem *parent , const Package &pkg, const QString &component ) +: QListViewItem( parent, component ) +{ + m_package = pkg; + m_component = component; +} + +PackageLVI::~PackageLVI() +{ +} + +/* vim: set et ts=4 sw=4 softtabstop=4: */ + diff --git a/kbugbuster/gui/packagelvi.h b/kbugbuster/gui/packagelvi.h new file mode 100644 index 00000000..32f48642 --- /dev/null +++ b/kbugbuster/gui/packagelvi.h @@ -0,0 +1,51 @@ +/* + packagelvi.h - Custom QListViewItem that holds a Package object + + copyright : (c) 2001 by Martijn Klingens + email : klingens@kde.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. * + * * + ************************************************************************* +*/ + +#ifndef PACKAGELVI_H +#define PACKAGELVI_H + +#include <qlistview.h> + +#include "package.h" + +/** + * @author Martijn Klingens + */ +class PackageLVI : public QListViewItem +{ +public: + // Top-level package + PackageLVI( QListView *parent , const Package &pkg, const QString &component ); + // Child component + PackageLVI( QListViewItem *parent , const Package &pkg, const QString &component ); + + ~PackageLVI(); + + Package& package() { return m_package; } + void setPackage( const Package &pkg ) { m_package = pkg; } + + QString component() { return m_component; } + void setComponent( const QString &component ) { m_component = component; } + +private: + Package m_package; + QString m_component; +}; + +#endif // PACKAGELVI_H + +/* vim: set et ts=4 softtabstop=4 sw=4: */ + diff --git a/kbugbuster/gui/packageselectdialog.cpp b/kbugbuster/gui/packageselectdialog.cpp new file mode 100644 index 00000000..7b791e9d --- /dev/null +++ b/kbugbuster/gui/packageselectdialog.cpp @@ -0,0 +1,214 @@ +#include <qlistview.h> +#include <qlayout.h> +#include <qheader.h> + +#include <kdebug.h> +#include <kcompletion.h> +#include <klineedit.h> + +#include "bugsystem.h" +#include "kbbprefs.h" +#include "bugserver.h" + +#include "packagelvi.h" +#include "packageselectdialog.h" +#include "packageselectdialog.moc" + +PackageListView::PackageListView( QWidget *parent ) : + QListView( parent ) +{ + setFocusPolicy( QWidget::StrongFocus ); +} + +void PackageListView::resetTyped() +{ + mTyped = ""; +} + +void PackageListView::keyPressEvent( QKeyEvent *e ) +{ + // Disable listview text completion for now + QListView::keyPressEvent( e ); + return; + + int k = e->key(); + if ( k == Key_Return || k == Key_Escape ) e->ignore(); + + QString key = e->text(); + mTyped.append(key); + emit typed( mTyped ); +} + +PackageSelectDialog::PackageSelectDialog(QWidget *parent,const char *name) : + KDialogBase( parent, name, true, i18n("Select Product"), Ok|Cancel ) +{ + QWidget *topWidget = new QWidget( this ); + setMainWidget( topWidget ); + + QBoxLayout *topLayout = new QVBoxLayout( topWidget ); + QSplitter *topSplitter = new QSplitter( QSplitter::Vertical, topWidget ); + topSplitter->setOpaqueResize( true ); + + topLayout->addWidget( topSplitter ); + + mRecentList = new QListView( topSplitter ); + mRecentList->addColumn( i18n("Recent") ); + mRecentList->resize( mRecentList->width(), mRecentList->fontMetrics().height() * + KBBPrefs::instance()->mRecentPackagesCount ); + + connect( mRecentList, SIGNAL( mouseButtonPressed( int, QListViewItem *, const QPoint &, int) ), + SLOT( recentSelected( int, QListViewItem * ) ) ); + connect( mRecentList, SIGNAL( doubleClicked( QListViewItem * ) ), + SLOT( slotOk() ) ); + + mCompletion = new KCompletion; + mCompletion->setCompletionMode( KGlobalSettings::CompletionAuto ); + + mCompleteList = new PackageListView( topSplitter ); + mCompleteList->addColumn( i18n("Name") ); + mCompleteList->addColumn( i18n("Description") ); + mCompleteList->setRootIsDecorated(true); + mCompleteList->setAllColumnsShowFocus( true ); + connect( mCompleteList, SIGNAL( typed( const QString & ) ), + SLOT( completeTyped( const QString & ) ) ); + + + connect( mCompleteList, SIGNAL( mouseButtonPressed( int, QListViewItem *, const QPoint &, int) ), + SLOT( completeSelected( int, QListViewItem * ) ) ); + connect( mCompleteList, SIGNAL( doubleClicked( QListViewItem * ) ), + SLOT( slotOk() ) ); + + mPackageEdit = new KLineEdit( topWidget ); + mPackageEdit->setFocus(); + + topLayout->addWidget( mPackageEdit ); + connect( mPackageEdit, SIGNAL( textChanged( const QString & ) ), + SLOT( completeTyped( const QString & ) ) ); + enableButtonOK( !mPackageEdit->text().isEmpty() ); +} + +PackageSelectDialog::~PackageSelectDialog() +{ + delete mCompletion; +} + +void PackageSelectDialog::setRecentPackages( const QStringList &recent ) +{ + mRecentList->clear(); + QStringList::ConstIterator it; + for( it = recent.begin(); it != recent.end(); ++it ) { + new QListViewItem( mRecentList, *it ); + } +} + +void PackageSelectDialog::setPackages( const Package::List &pkgs ) +{ + mCompleteList->clear(); + mCompletion->clear(); + mCompletionDict.clear(); + Package::List::ConstIterator it; + for( it = pkgs.begin(); it != pkgs.end(); ++it ) { + PackageLVI *item = new PackageLVI( mCompleteList, (*it), QString::null ); + QStringList components = (*it).components(); + + if (components.count() > 1) { + for( QStringList::ConstIterator cit = components.begin(); cit != components.end(); ++cit ) { + PackageLVI *component = new PackageLVI( item, (*it), (*cit) ); + QString completionName = (*it).name() + "/" + (*cit); + + mCompletion->addItem( completionName ); + mCompletionDict.insert( completionName, component ); + } + } + + mCompletion->addItem( (*it).name() ); + mCompletionDict.insert((*it).name(),item); + } +} + +void PackageSelectDialog::recentSelected( int, QListViewItem *item ) +{ + kdDebug() << "PackageSelectDialog::recentSelected()" << endl; + if ( item ) { + mCompleteList->clearSelection(); + // Why does a QLineEdit->setText() call emit the textChanged() signal? + mPackageEdit->blockSignals( true ); + mPackageEdit->setText( item->text( 0 ) ); + enableButtonOK(true); + mPackageEdit->blockSignals( false ); + } +} + +void PackageSelectDialog::completeSelected( int, QListViewItem *item ) +{ + PackageLVI *lvi = dynamic_cast<PackageLVI*>(item); + + if ( lvi ) { + mRecentList->clearSelection(); + if ( lvi->component().isEmpty() ) { + mPackageEdit->setText( lvi->package().name() ); + } + else { + mPackageEdit->setText( lvi->package().name() + "/" + lvi->component() ); + } + } +} + +void PackageSelectDialog::slotOk() +{ + PackageLVI *item = (PackageLVI *)mCompleteList->selectedItem(); + if ( item ) { + mSelectedPackage = item->package(); + mSelectedComponent = item->component(); + + QString recent_key; + if ( item->component().isEmpty() ) + recent_key = item->package().name(); + else + recent_key = item->package().name() + "/" + item->component(); + + BugServer *server = BugSystem::self()->server(); + QStringList recent = server->serverConfig().recentPackages(); + if( !recent.contains( recent_key ) ) { + recent.prepend( recent_key ); + if ( int( recent.count() ) > KBBPrefs::instance()->mRecentPackagesCount ) { + recent.remove( recent.last() ); + } + kdDebug() << "RECENT: " << recent.join(",") << endl; + server->serverConfig().setRecentPackages( recent ); + } + } else { + QListViewItem *recentItem = mRecentList->selectedItem(); + if ( recentItem ) { + QStringList tokens = QStringList::split( '/', recentItem->text( 0 ) ); + mSelectedPackage = BugSystem::self()->package( tokens[0] ); + mSelectedComponent = tokens[1]; + } + } + mCompleteList->resetTyped(); + accept(); +} + +Package PackageSelectDialog::selectedPackage() +{ + return mSelectedPackage; +} + +QString PackageSelectDialog::selectedComponent() +{ + return mSelectedComponent; +} + +void PackageSelectDialog::completeTyped( const QString &typed ) +{ + kdDebug() << "completeTyped: " << typed << endl; + QString completed = mCompletion->makeCompletion( typed ); + kdDebug() << "completed: " << completed << endl; + if ( !completed.isEmpty() ) { + mCompleteList->setSelected( mCompletionDict[ completed ], true ); + mCompleteList->ensureItemVisible( mCompletionDict[ completed ] ); + } else { + mCompleteList->resetTyped(); + } + enableButtonOK( !typed.isEmpty() ); +} diff --git a/kbugbuster/gui/packageselectdialog.h b/kbugbuster/gui/packageselectdialog.h new file mode 100644 index 00000000..1fe596aa --- /dev/null +++ b/kbugbuster/gui/packageselectdialog.h @@ -0,0 +1,64 @@ +#ifndef PACKAGESELECTDIALOG_H +#define PACKAGESELECTDIALOG_H + +#include <qdict.h> + +#include <kdialogbase.h> + +#include "package.h" + +class KCompletion; +class KLineEdit; + +class PackageListView : public QListView +{ + Q_OBJECT + public: + PackageListView( QWidget *parent ); + + void resetTyped(); + + signals: + void typed( const QString & ); + + protected: + void keyPressEvent( QKeyEvent *e ); + + private: + QString mTyped; +}; + +class PackageSelectDialog : public KDialogBase +{ + Q_OBJECT + public: + PackageSelectDialog(QWidget *parent=0,const char *name=0); + ~PackageSelectDialog(); + + void setRecentPackages( const QStringList & ); + void setPackages( const Package::List &pkgs ); + + Package selectedPackage(); + QString selectedComponent(); + + protected slots: + void slotOk(); + + private slots: + void recentSelected( int, QListViewItem * ); + void completeSelected( int, QListViewItem * ); + void completeTyped( const QString & ); + + private: + Package::List mPackages; + Package mSelectedPackage; + QString mSelectedComponent; + + QListView *mRecentList; + PackageListView *mCompleteList; + KLineEdit *mPackageEdit; + KCompletion *mCompletion; + QDict<QListViewItem> mCompletionDict; +}; + +#endif diff --git a/kbugbuster/gui/preferencesdialog.cpp b/kbugbuster/gui/preferencesdialog.cpp new file mode 100644 index 00000000..9cafff28 --- /dev/null +++ b/kbugbuster/gui/preferencesdialog.cpp @@ -0,0 +1,306 @@ +#include <qradiobutton.h> +#include <qcheckbox.h> +#include <qlineedit.h> +#include <qpushbutton.h> +#include <qlayout.h> +#include <qgroupbox.h> +#include <qbuttongroup.h> +#include <qlistview.h> +#include <qhbox.h> + +#include <knuminput.h> +#include <kurl.h> +#include <kmessagebox.h> +#include <kiconloader.h> +#include <kdebug.h> + +#include "mailsender.h" +#include "kbbprefs.h" +#include "kbbmainwindow.h" +#include "serverconfigdialog.h" +#include "bugsystem.h" +#include "bugserver.h" +#include "bugserverconfig.h" + +#include "preferencesdialog.h" + +class ServerItem : public QListViewItem +{ + public: + ServerItem( QListView *listView, const BugServerConfig &cfg ) + : QListViewItem( listView ) + { + setServerConfig( cfg ); + } + + void setServerConfig( const BugServerConfig &cfg ) + { + mServerConfig = cfg; + setText( 0, cfg.name() ); + setText( 1, cfg.baseUrl().prettyURL() ); + setText( 2, cfg.user() ); + setText( 3, cfg.bugzillaVersion() ); + } + + const BugServerConfig &serverConfig() const { return mServerConfig; } + + private: + BugServerConfig mServerConfig; +}; + +class ServerListView : public QListView +{ + public: + ServerListView( QWidget *parent ) : QListView( parent ) + { + addColumn( i18n("Name") ); + addColumn( i18n("Base URL") ); + addColumn( i18n("User") ); + addColumn( i18n("Version") ); + } +}; + +PreferencesDialog::PreferencesDialog( QWidget* parent, const char* name ) + : KDialogBase ( IconList, i18n("Preferences"), Ok|Apply|Cancel, Ok, + parent, name, false, true ) +{ + setupServerPage(); + setupAdvancedPage(); + + readConfig(); +} + +PreferencesDialog::~PreferencesDialog() +{ +} + +void PreferencesDialog::setupServerPage() +{ + QFrame *topFrame = addPage( i18n("Servers"), 0, + DesktopIcon( "gohome", KIcon::SizeMedium ) ); + + QBoxLayout *layout = new QVBoxLayout( topFrame ); + layout->setSpacing( spacingHint() ); + + mServerList = new ServerListView( topFrame ); + layout->addWidget( mServerList ); + + QHBox *buttonBox = new QHBox( topFrame ); + buttonBox->setSpacing( spacingHint() ); + layout->addWidget( buttonBox ); + + QPushButton *addButton = new QPushButton( i18n("Add Server..."), buttonBox ); + connect( addButton, SIGNAL( clicked() ), SLOT( addServer() ) ); + + QPushButton *editButton = new QPushButton( i18n("Edit Server..."), buttonBox ); + connect( editButton, SIGNAL( clicked() ), SLOT( editServer() ) ); + + QPushButton *removeButton = new QPushButton( i18n("Delete Server"), buttonBox ); + connect( removeButton, SIGNAL( clicked() ), SLOT( removeServer() ) ); + + QPushButton *button = new QPushButton( i18n("Select Server From List..."), + topFrame ); + layout->addWidget( button ); + connect( button, SIGNAL( clicked() ), SLOT( selectServer() ) ); + connect( mServerList, SIGNAL( doubleClicked ( QListViewItem *)), this, SLOT( editServer())); +} + +void PreferencesDialog::setupAdvancedPage() +{ + QFrame *topFrame = addPage( i18n("Advanced"), 0, + DesktopIcon( "misc", KIcon::SizeMedium ) ); + + QBoxLayout *layout = new QVBoxLayout( topFrame ); + layout->setSpacing( spacingHint() ); + + QButtonGroup *mailGroup = new QButtonGroup( 1, Horizontal, + i18n( "Mail Client" ), topFrame ); + layout->addWidget( mailGroup ); + + mKMailButton = new QRadioButton( i18n( "&KMail" ), mailGroup ); + mDirectButton = new QRadioButton( i18n( "D&irect" ), mailGroup ); + mSendmailButton = new QRadioButton( i18n( "&Sendmail" ), mailGroup ); + + mShowClosedCheckBox = new QCheckBox( i18n( "Show closed bugs" ), topFrame ); + layout->addWidget( mShowClosedCheckBox ); + + mShowWishesCheckBox = new QCheckBox( i18n( "Show wishes" ), topFrame ); + layout->addWidget( mShowWishesCheckBox ); + + mShowVotedCheckBox = new QCheckBox( i18n( "Show bugs with number of votes greater than:" ), topFrame ); + layout->addWidget( mShowVotedCheckBox ); + + mMinVotesInput = new KIntNumInput( topFrame ); + mMinVotesInput->setMinValue( 0 ); + connect( mShowVotedCheckBox, SIGNAL(toggled(bool)), + mMinVotesInput, SLOT(setEnabled(bool)) ); + layout->addWidget( mMinVotesInput ); + + mSendBccCheckBox = new QCheckBox( i18n( "Send BCC to myself" ), topFrame ); + layout->addWidget( mSendBccCheckBox ); +} + +void PreferencesDialog::setDefaults() +{ + KBBPrefs::instance()->setDefaults(); + readConfig(); +} + +void PreferencesDialog::slotApply() +{ + writeConfig(); +} + +void PreferencesDialog::slotOk() +{ + writeConfig(); + accept(); +} + +void PreferencesDialog::slotCancel() +{ + hide(); +} + +void PreferencesDialog::addServer() +{ + ServerConfigDialog *dlg = new ServerConfigDialog( this ); + int result = dlg->exec(); + if ( result == QDialog::Accepted ) { + new ServerItem( mServerList, dlg->serverConfig() ); + } +} + +void PreferencesDialog::editServer() +{ + ServerItem *item = static_cast<ServerItem *>( mServerList->currentItem() ); + if ( !item ) return; + + ServerConfigDialog *dlg = new ServerConfigDialog( this ); + dlg->setServerConfig( item->serverConfig() ); + + int result = dlg->exec(); + if ( result == QDialog::Accepted ) { + item->setServerConfig( dlg->serverConfig() ); + } +} + +void PreferencesDialog::removeServer() +{ + QListViewItem *item = mServerList->currentItem(); + if ( !item ) return; + + delete item; +} + +void PreferencesDialog::selectServer() +{ + SelectServerDlg *dlg =new SelectServerDlg( this, "Select Server" ); + + int result = dlg->exec(); + if ( result == QDialog::Accepted ) { + ServerItem *item = dlg->serverSelected(); + if ( item ) { + new ServerItem( mServerList, item->serverConfig() ); + } + } + delete dlg; +} + +void PreferencesDialog::createServerItem( ServerListView *listView, + const QString &name, + const QString &url, + const QString &version ) +{ + BugServerConfig cfg( name, KURL( url ) ); + cfg.setBugzillaVersion( version ); + new ServerItem( listView, cfg ); +} + +void PreferencesDialog::readConfig() +{ + int client = KBBPrefs::instance()->mMailClient; + switch(client) { + default: + case MailSender::KMail: + mKMailButton->setChecked(true); + break; + case MailSender::Sendmail: + mSendmailButton->setChecked(true); + break; + case MailSender::Direct: + mDirectButton->setChecked(true); + break; + } + mShowClosedCheckBox->setChecked( KBBPrefs::instance()->mShowClosedBugs ); + mShowWishesCheckBox->setChecked( KBBPrefs::instance()->mShowWishes ); + mShowVotedCheckBox->setChecked( KBBPrefs::instance()->mShowVoted ); + mMinVotesInput->setValue( KBBPrefs::instance()->mMinVotes ); + mSendBccCheckBox->setChecked( KBBPrefs::instance()->mSendBCC ); + + mServerList->clear(); + QValueList<BugServer *> servers = BugSystem::self()->serverList(); + QValueList<BugServer *>::ConstIterator it; + for( it = servers.begin(); it != servers.end(); ++it ) { + new ServerItem( mServerList, (*it)->serverConfig() ); + } +} + +void PreferencesDialog::writeConfig() +{ + MailSender::MailClient client = MailSender::KMail; + + if (mKMailButton->isChecked()) client = MailSender::KMail; + if (mSendmailButton->isChecked()) client = MailSender::Sendmail; + if (mDirectButton->isChecked()) client = MailSender::Direct; + + KBBPrefs::instance()->mMailClient = client; + KBBPrefs::instance()->mShowClosedBugs = mShowClosedCheckBox->isChecked(); + KBBPrefs::instance()->mShowWishes = mShowWishesCheckBox->isChecked(); + KBBPrefs::instance()->mShowVoted = mShowVotedCheckBox->isChecked(); + KBBPrefs::instance()->mMinVotes = mMinVotesInput->value(); + KBBPrefs::instance()->mSendBCC = mSendBccCheckBox->isChecked(); + KBBPrefs::instance()->writeConfig(); + + QValueList<BugServerConfig> servers; + QListViewItem *item; + for ( item = mServerList->firstChild(); item; + item = item->nextSibling() ) { + servers.append( static_cast<ServerItem *>( item )->serverConfig() ); + } + + BugSystem::self()->setServerList( servers ); + + emit configChanged(); +} + +SelectServerDlg::SelectServerDlg(PreferencesDialog *parent, const char */*name*/ ) + :KDialogBase(parent, 0, true, i18n("Select Server"), + KDialogBase::Ok | KDialogBase::Cancel) +{ + list = new ServerListView(this ); + setMainWidget( list ); + + parent->createServerItem( list, "KDE", "http://bugs.kde.org", "KDE" ); + parent->createServerItem( list, "GNOME", "http://bugzilla.gnome.org", "2.10" ); + parent->createServerItem( list, "Mozilla", "http://bugzilla.mozilla.org", "2.17.1" ); + parent->createServerItem( list, "Apache", "http://nagoya.apache.org/bugzilla/", "2.14.2" ); + parent->createServerItem( list, "XFree86", "http://bugs.xfree86.org/cgi-bin/bugzilla/", "2.14.2" ); + parent->createServerItem( list, "Ximian", "http://bugzilla.ximian.com", "2.10" ); + parent->createServerItem( list, "RedHat", "http://bugzilla.redhat.com/bugzilla/", "2.17.1" ); + parent->createServerItem( list, "Mandriva", "http://qa.mandriva.com/", "2.17.4" ); + connect( list, SIGNAL( doubleClicked ( QListViewItem *)), this, SLOT( slotDoubleClicked( QListViewItem *))); +} + + +ServerItem *SelectServerDlg::serverSelected() +{ + return static_cast<ServerItem *>( list->currentItem() ); +} + +void SelectServerDlg::slotDoubleClicked( QListViewItem *) +{ + accept(); +} + +#include "preferencesdialog.moc" diff --git a/kbugbuster/gui/preferencesdialog.h b/kbugbuster/gui/preferencesdialog.h new file mode 100644 index 00000000..29c72eaf --- /dev/null +++ b/kbugbuster/gui/preferencesdialog.h @@ -0,0 +1,76 @@ +#ifndef PREFERENCESDIALOG_H +#define PREFERENCESDIALOG_H + +#include <kdialogbase.h> + +class QCheckBox; +class QRadioButton; +class QLineEdit; +class QListView; +class KIntNumInput; +class ServerListView; + +class PreferencesDialog : public KDialogBase +{ + Q_OBJECT + public: + PreferencesDialog( QWidget* parent = 0, const char* name = 0 ); + ~PreferencesDialog(); + + void createServerItem( ServerListView *listView, const QString &name, + const QString &url, const QString &version ); + + public: + void readConfig(); + void writeConfig(); + + signals: + void configChanged(); + + protected slots: + void setDefaults(); + void slotApply(); + void slotOk(); + void slotCancel(); + + void addServer(); + void editServer(); + void removeServer(); + + void selectServer(); + + protected: + void setupServerPage(); + void setupAdvancedPage(); + + + private: + QCheckBox *mShowClosedCheckBox; + QCheckBox *mShowWishesCheckBox; + QCheckBox *mShowVotedCheckBox; + QCheckBox *mSendBccCheckBox; + KIntNumInput *mMinVotesInput; + QRadioButton *mKMailButton; + QRadioButton *mDirectButton; + QRadioButton *mSendmailButton; + QListView *mServerList; +}; + +class ServerListView; +class ServerItem; + +class SelectServerDlg : public KDialogBase +{ + Q_OBJECT +public: + SelectServerDlg(PreferencesDialog *parent, const char */*name*/ ); + ServerItem *serverSelected(); +protected slots: + void slotDoubleClicked( QListViewItem *); + +protected: + ServerListView *list; +}; + + +#endif diff --git a/kbugbuster/gui/serverconfigdialog.cpp b/kbugbuster/gui/serverconfigdialog.cpp new file mode 100644 index 00000000..32c5e241 --- /dev/null +++ b/kbugbuster/gui/serverconfigdialog.cpp @@ -0,0 +1,82 @@ +#include "serverconfigdialog.h" + +#include "bugserverconfig.h" + +#include <kpassdlg.h> +#include <kdebug.h> +#include <klocale.h> + +#include <qlayout.h> +#include <qlineedit.h> +#include <qlabel.h> +#include <qvbox.h> +#include <qcombobox.h> + +ServerConfigDialog::ServerConfigDialog( QWidget *parent, const char *name ) : + KDialogBase( parent, name, true, i18n("Edit Bugzilla Server"), Ok|Cancel ) +{ + QWidget *topFrame = makeMainWidget(); + + QGridLayout *topLayout = new QGridLayout( topFrame ); + topLayout->setSpacing( spacingHint() ); + + QLabel *label; + + mServerName = new QLineEdit( topFrame ); + label = new QLabel( mServerName, i18n("Name:"), topFrame ); + topLayout->addWidget( label, 0, 0 ); + topLayout->addWidget( mServerName, 0, 1 ); + mServerName->setFocus(); + + mServerUrl = new QLineEdit( topFrame ); + label = new QLabel( mServerUrl, i18n("URL:"), topFrame ); + topLayout->addWidget( label, 1, 0 ); + topLayout->addWidget( mServerUrl, 1, 1 ); + + mUser = new QLineEdit( topFrame ); + label = new QLabel( mUser, i18n("User:"), topFrame ); + topLayout->addWidget( label, 2, 0 ); + topLayout->addWidget( mUser, 2, 1 ); + + mPassword = new KPasswordEdit( topFrame ); + label = new QLabel( mPassword, i18n("Password:"), topFrame ); + topLayout->addWidget( label, 3, 0 ); + topLayout->addWidget( mPassword, 3, 1 ); + + mVersion = new QComboBox( topFrame ); + label = new QLabel( mVersion, i18n("Bugzilla version:"), topFrame ); + topLayout->addWidget( label, 4, 0 ); + topLayout->addWidget( mVersion, 4, 1 ); + mVersion->insertStringList( BugServerConfig::bugzillaVersions() ); +} + +void ServerConfigDialog::setServerConfig( const BugServerConfig &cfg ) +{ + mServerName->setText( cfg.name() ); + mServerUrl->setText( cfg.baseUrl().url() ); + mUser->setText( cfg.user() ); + mPassword->setText( cfg.password() ); + + int i; + for( i = 0; i < mVersion->count(); ++i ) { + if ( mVersion->text( i ) == cfg.bugzillaVersion() ) { + mVersion->setCurrentItem( i ); + break; + } + } +} + +BugServerConfig ServerConfigDialog::serverConfig() +{ + BugServerConfig cfg; + + cfg.setName( mServerName->text() ); + cfg.setBaseUrl( KURL( mServerUrl->text() ) ); + cfg.setUser( mUser->text() ); + cfg.setPassword( mPassword->text() ); + cfg.setBugzillaVersion( mVersion->currentText() ); + + return cfg; +} + +#include "serverconfigdialog.moc" diff --git a/kbugbuster/gui/serverconfigdialog.h b/kbugbuster/gui/serverconfigdialog.h new file mode 100644 index 00000000..5764bfdf --- /dev/null +++ b/kbugbuster/gui/serverconfigdialog.h @@ -0,0 +1,28 @@ +#ifndef SERVERCONFIGDIALOG_H +#define SERVERCONFIGDIALOG_H + +#include <kdialogbase.h> + +class BugServerConfig; +class QLineEdit; +class KPasswordEdit; +class QComboBox; + +class ServerConfigDialog : public KDialogBase +{ + Q_OBJECT + public: + ServerConfigDialog( QWidget *parent = 0 , const char *name = 0 ); + + void setServerConfig( const BugServerConfig & ); + BugServerConfig serverConfig(); + + private: + QLineEdit *mServerName; + QLineEdit *mServerUrl; + QLineEdit *mUser; + KPasswordEdit *mPassword; + QComboBox *mVersion; +}; + +#endif diff --git a/kbugbuster/gui/severityselectdialog.cpp b/kbugbuster/gui/severityselectdialog.cpp new file mode 100644 index 00000000..714e6f3a --- /dev/null +++ b/kbugbuster/gui/severityselectdialog.cpp @@ -0,0 +1,40 @@ +#include <qlayout.h> +#include <qbuttongroup.h> +#include <qradiobutton.h> + +#include <kdebug.h> + +#include "bugsystem.h" +#include "kbbprefs.h" + +#include "severityselectdialog.h" +#include "severityselectdialog.moc" + +SeveritySelectDialog::SeveritySelectDialog(QWidget *parent,const char *name) : + KDialogBase( parent, name, true, i18n("Select Severity"), Ok|Cancel ) +{ + mButtonGroup = new QButtonGroup( 1, Horizontal, i18n("Severity"), this ); + setMainWidget( mButtonGroup ); + + QValueList<Bug::Severity> severities = Bug::severities(); + QValueList<Bug::Severity>::ConstIterator it; + for( it = severities.begin(); it != severities.end(); ++it ) { + mButtonGroup->insert( + new QRadioButton( Bug::severityToString( *it ), mButtonGroup ), int(*it) ); + } +} + +void SeveritySelectDialog::setSeverity( Bug::Severity s ) +{ + mButtonGroup->setButton( s ); +} + +Bug::Severity SeveritySelectDialog::selectedSeverity() +{ + return (Bug::Severity)mButtonGroup->id( mButtonGroup->selected() ); +} + +QString SeveritySelectDialog::selectedSeverityAsString() +{ + return Bug::severityToString( selectedSeverity() ); +} diff --git a/kbugbuster/gui/severityselectdialog.h b/kbugbuster/gui/severityselectdialog.h new file mode 100644 index 00000000..12f36fe5 --- /dev/null +++ b/kbugbuster/gui/severityselectdialog.h @@ -0,0 +1,23 @@ +#ifndef SEVERITYSELECTDIALOG_H +#define SEVERITYSELECTDIALOG_H + +#include <kdialogbase.h> + +#include "bug.h" + +class SeveritySelectDialog : public KDialogBase +{ + Q_OBJECT + public: + SeveritySelectDialog(QWidget *parent=0,const char *name=0); + + void setSeverity( Bug::Severity ); + + Bug::Severity selectedSeverity(); + QString selectedSeverityAsString(); + + private: + QButtonGroup *mButtonGroup; +}; + +#endif diff --git a/kbugbuster/hi128-app-kbugbuster.png b/kbugbuster/hi128-app-kbugbuster.png Binary files differnew file mode 100644 index 00000000..a39081d9 --- /dev/null +++ b/kbugbuster/hi128-app-kbugbuster.png diff --git a/kbugbuster/hi16-app-kbugbuster.png b/kbugbuster/hi16-app-kbugbuster.png Binary files differnew file mode 100644 index 00000000..827d5f8e --- /dev/null +++ b/kbugbuster/hi16-app-kbugbuster.png diff --git a/kbugbuster/hi22-app-kbugbuster.png b/kbugbuster/hi22-app-kbugbuster.png Binary files differnew file mode 100644 index 00000000..3a3d1ea2 --- /dev/null +++ b/kbugbuster/hi22-app-kbugbuster.png diff --git a/kbugbuster/hi32-app-kbugbuster.png b/kbugbuster/hi32-app-kbugbuster.png Binary files differnew file mode 100644 index 00000000..59773925 --- /dev/null +++ b/kbugbuster/hi32-app-kbugbuster.png diff --git a/kbugbuster/hi48-app-kbugbuster.png b/kbugbuster/hi48-app-kbugbuster.png Binary files differnew file mode 100644 index 00000000..2565d767 --- /dev/null +++ b/kbugbuster/hi48-app-kbugbuster.png diff --git a/kbugbuster/hi64-app-kbugbuster.png b/kbugbuster/hi64-app-kbugbuster.png Binary files differnew file mode 100644 index 00000000..f2f45269 --- /dev/null +++ b/kbugbuster/hi64-app-kbugbuster.png diff --git a/kbugbuster/kbugbuster.desktop b/kbugbuster/kbugbuster.desktop new file mode 100644 index 00000000..1fa713a9 --- /dev/null +++ b/kbugbuster/kbugbuster.desktop @@ -0,0 +1,78 @@ +# KDE Config File +[Desktop Entry] +Type=Application +Exec=kbugbuster -caption "%c" %i %m +Icon=kbugbuster +DocPath=kbugbuster/index.html +GenericName=KDE Bug Management +GenericName[af]=Kde Fout Bestuuring +GenericName[bg]=Управление на грешки - KDE +GenericName[bs]=KDE upravljanje bugovima +GenericName[ca]=Gestió d'errors a KDE +GenericName[cs]=Správa chyb KDE +GenericName[cy]=Rheolaeth Namau KDE +GenericName[da]=KDE Fejlretningshåndtering +GenericName[de]=KDE-Programmfehler-Verwaltung +GenericName[el]=Διαχείριση σφαλμάτων του KDE +GenericName[eo]=KDE-Eraroadministrado +GenericName[es]=Administración de errores de KDE +GenericName[et]=KDE veahaldusprogramm +GenericName[eu]=KDE programa-errore kudeaketa +GenericName[fa]=مدیریت اشکال KDE +GenericName[fi]=KDE:n vianhallinta +GenericName[fo]=KDE-villuhandfaring +GenericName[fr]=Outil de gestion de bogues pour KDE +GenericName[ga]=Bainisteoireacht Fabhtanna KDE +GenericName[gl]=Xestión de erros para KDE +GenericName[he]=ניהול של באגים ב-KDE +GenericName[hi]=केडीई बग प्रबंधक +GenericName[hr]=KDE upravljanje bugovima +GenericName[hu]=KDE hibakezelő +GenericName[is]=KDE villustjórnun +GenericName[it]=Gestione bug di KDE +GenericName[ja]=KDE バグマネージメント +GenericName[ka]=KDE ბზიკთა მართვა +GenericName[kk]=KDE қателерді басқару +GenericName[lt]=KDE ydų tvarkymas +GenericName[lv]=KDE Kļūdu Pārvalde +GenericName[ms]=Pengurusan Pepijat KDE +GenericName[nb]=KDE-feilhåndtering +GenericName[nds]=KDE-Programmfehler-Pleeg +GenericName[ne]=केडीई बग प्रबन्धक +GenericName[nl]=KDE-bugs beheren +GenericName[nn]=KDE-feilhandtering +GenericName[pa]=KDE ਬੱਗ ਪਰਬੰਧਨ +GenericName[pl]=Zarządzanie błędami w KDE +GenericName[pt]=Gestão de Erros do KDE +GenericName[pt_BR]=Gerenciamento de Erros do KDE +GenericName[ro]=Utilitar de administrarea a erorilor din KDE +GenericName[ru]=Отслеживание ошибок +GenericName[sk]=Správa chýb KDE +GenericName[sl]=Upravljanje s hrošči v KDE +GenericName[sr]=KDE-ово управљање грешкама +GenericName[sr@Latn]=KDE-ovo upravljanje greškama +GenericName[sv]=Verktyg för KDE-felhantering +GenericName[ta]= KDE பக் மேனேஜ்மென்ட் +GenericName[tg]=Утилитаи идоракунии хатогиҳо +GenericName[th]=เครื่องมือจัดการบักสำหรับ KDE +GenericName[tr]=KDE Hata Ayıklayıcı +GenericName[uk]=Керування вадами KDE +GenericName[ven]=Malangulele a Bug a KDE +GenericName[vi]=Trình quản lí bug KDE +GenericName[xh]=Umxholo Wokuxoxwa We KDE +GenericName[zh_CN]=KDE 除错管理 +GenericName[zh_TW]=KDE 臭蟲管理 +GenericName[zu]=KDE Ukuphathwa Kwegciwane +Terminal=false +Name=KBugBuster +Name[af]=K-fout-buster +Name[cy]=KNamWasgydd +Name[eo]=Eraroĉasilo +Name[hi]=के-बग-बस्टर +Name[lv]=KKļūduMednieks +Name[pl]=Przeglądarka bazy błędów +Name[sv]=Kbugbuster +Name[ta]= Kபக்பஸ்டர் +Name[th]=บักบัสเตอร์ +Name[ven]=Mupandeli wa Baga wa K +Categories=Qt;KDE;Development; diff --git a/kbugbuster/kresources/Makefile.am b/kbugbuster/kresources/Makefile.am new file mode 100644 index 00000000..ddfde0fa --- /dev/null +++ b/kbugbuster/kresources/Makefile.am @@ -0,0 +1,16 @@ +INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/kbugbuster/backend $(all_includes) + +kde_module_LTLIBRARIES = kcal_bugzilla.la + +kcal_bugzilla_la_SOURCES = kcalresource.cpp kcalresourceconfig.cpp \ + kcalresource_plugin.cpp resourceprefs.kcfgc +kcal_bugzilla_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) +kcal_bugzilla_la_LIBADD = ../backend/libkbbbackend.la -lkcal + +servicedir = $(kde_servicesdir)/kresources/kcal +service_DATA = bugzilla.desktop + +METASOURCES = AUTO + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kres_bugzilla.pot diff --git a/kbugbuster/kresources/bugzilla.desktop b/kbugbuster/kresources/bugzilla.desktop new file mode 100644 index 00000000..75cbb5a5 --- /dev/null +++ b/kbugbuster/kresources/bugzilla.desktop @@ -0,0 +1,48 @@ +[Desktop Entry] +Name=Bugzilla To-do List +Name[bg]=Задачи (Bugzilla) +Name[ca]=Llista de pendents de Bugzilla +Name[cs]=Seznam úkolů (Bugzilla) +Name[da]=Bugzilla gøremålsliste +Name[de]=Bugzilla Todo-Liste +Name[el]=Προς υλοποίηση λίστα του Bugzilla +Name[es]=Listado de tareas pendientes de BugZilla +Name[et]=Bugzilla ülesannete nimekiri +Name[eu]=Bugzilla-ren egiteke zerrenda +Name[fa]=فهرست کار انجامی Bugzilla +Name[fi]=Bugzilla-tehtäväluettelo +Name[fr]=Liste de tâches de Bugzilla +Name[ga]=Tascliosta Bugzilla +Name[gl]=Lista de itens por facer de Bugzilla +Name[he]=רשימת מטלות של Bugzilla +Name[hu]=Bugzilla feladatlista +Name[is]=Bugzilla verklisti +Name[it]=Lista delle cosa da fare di Bugzilla +Name[ja]=BugzillaToDo リスト +Name[ka]=Bugzilla-ს დავალებათა სია +Name[kk]=Bugzilla To-do тізімі +Name[lt]=Bugzilla darbų sąrašas +Name[nb]=Bugzilla-huskeliste +Name[nds]=Bugzilla-Opgavenlist +Name[ne]=बगजिला गर्नुपर्ने कार्य सूची +Name[nl]=Bugzilla Todo-lijst +Name[nn]=Bugzilla-hugseliste +Name[pa]=ਬੱਗਜੀਲਾ ਕਰਨ ਸੂਚੀ +Name[pl]=Lista rzeczy do zrobienia w Bugzilli +Name[pt]=Lista de Itens Por-Fazer do Bugzilla +Name[pt_BR]=Lista de Itens Por-Fazer do Bugzilla +Name[ru]=Список TODO Bugzilla +Name[sk]=Zoznam úloh v Bugzille +Name[sl]=Seznam »za-narediti« v Bugzilli +Name[sr]=Листа послова Bugzilla-е +Name[sr@Latn]=Lista poslova Bugzilla-e +Name[sv]=Bugzilla uppgiftslista +Name[tr]=Bugzilla To-do Listesi +Name[uk]=Список завдань Bugzilla +Name[zh_CN]=Bugzilla 待办列表 +Name[zh_TW]=Bugzilla 待辦清單 +X-KDE-Library=kcal_bugzilla +Type=Service +ServiceTypes=KResources/Plugin +X-KDE-ResourceFamily=calendar +X-KDE-ResourceType=bugzilla diff --git a/kbugbuster/kresources/kcalresource.cpp b/kbugbuster/kresources/kcalresource.cpp new file mode 100644 index 00000000..fbd91bc1 --- /dev/null +++ b/kbugbuster/kresources/kcalresource.cpp @@ -0,0 +1,316 @@ +/* + This file is part of KBugBuster. + + Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <typeinfo> +#include <stdlib.h> + +#include <qdatetime.h> +#include <qstring.h> +#include <qptrlist.h> + +#include <kdebug.h> +#include <kurl.h> +#include <kio/job.h> +#include <klocale.h> +#include <kstandarddirs.h> + +#include <libkcal/vcaldrag.h> +#include <libkcal/vcalformat.h> +#include <libkcal/icalformat.h> +#include <libkcal/exceptions.h> +#include <libkcal/incidence.h> +#include <libkcal/event.h> +#include <libkcal/todo.h> +#include <libkcal/journal.h> +#include <libkcal/filestorage.h> + +#include <kabc/locknull.h> + +#include <kresources/configwidget.h> + +#include "bugsystem.h" +#include "bugserver.h" + +#include "kcalresourceconfig.h" +#include "resourceprefs.h" + +#include "kcalresource.h" + +KCalResource::KCalResource( const KConfig* config ) + : ResourceCached( config ), mLock( 0 ) +{ + mPrefs = new KBB::ResourcePrefs; + + KConfigSkeletonItem::List items = mPrefs->items(); + KConfigSkeletonItem::List::Iterator it; + for( it = items.begin(); it != items.end(); ++it ) { + (*it)->setGroup( identifier() ); + } + + if ( config ) { + readConfig( config ); + } + + init(); +} + +KCalResource::~KCalResource() +{ + close(); + + if ( mDownloadJob ) mDownloadJob->kill(); + if ( mUploadJob ) mUploadJob->kill(); + + delete mLock; +} + +void KCalResource::init() +{ + mDownloadJob = 0; + mUploadJob = 0; + + setType( "remote" ); + + mOpen = false; + + mLock = new KABC::LockNull( true ); + + KConfig config( "kbugbusterrc" ); + + BugSystem::self()->readConfig( &config ); +} + +KBB::ResourcePrefs *KCalResource::prefs() +{ + return mPrefs; +} + +void KCalResource::readConfig( const KConfig * ) +{ + mPrefs->readConfig(); +} + +void KCalResource::writeConfig( KConfig *config ) +{ + kdDebug() << "KCalResource::writeConfig()" << endl; + + ResourceCalendar::writeConfig( config ); + + mPrefs->writeConfig(); +} + +QString KCalResource::cacheFile() +{ + QString file = locateLocal( "cache", "kcal/kresources/" + identifier() ); + kdDebug() << "KCalResource::cacheFile(): " << file << endl; + return file; +} + +bool KCalResource::doOpen() +{ + kdDebug(5800) << "KCalResource::doOpen()" << endl; + + mOpen = true; + + return true; +} + +bool KCalResource::doLoad() +{ + kdDebug() << "KCalResource::doLoad()" << endl; + + if ( !mOpen ) return true; + + if ( mDownloadJob ) { + kdWarning() << "KCalResource::doLoad(): download still in progress." + << endl; + return false; + } + if ( mUploadJob ) { + kdWarning() << "KCalResource::doLoad(): upload still in progress." + << endl; + return false; + } + + mCalendar.close(); + + mCalendar.load( cacheFile() ); + + BugSystem *kbb = BugSystem::self(); + + kdDebug() << "KNOWN SERVERS:" << endl; + QValueList<BugServer *> servers = kbb->serverList(); + QValueList<BugServer *>::ConstIterator it; + for( it = servers.begin(); it != servers.end(); ++it ) { + kdDebug() << " " << (*it)->identifier() << endl; + } + + kbb->setCurrentServer( mPrefs->server() ); + if ( !kbb->server() ) { + kdError() << "Server not found." << endl; + return false; + } else { + kdDebug() << "CURRENT SERVER: " << kbb->server()->identifier() << endl; + } + + kbb->retrievePackageList(); + + Package package = kbb->package( mPrefs->product() ); + + connect( kbb, SIGNAL( bugListAvailable( const Package &, const QString &, + const Bug::List & ) ), + SLOT( slotBugListAvailable( const Package &, const QString &, + const Bug::List & ) ) ); + + kbb->retrieveBugList( package, mPrefs->component() ); + + return true; +} + +void KCalResource::slotBugListAvailable( const Package &, const QString &, + const Bug::List &bugs ) +{ + kdDebug() << "KCalResource::slotBugListAvailable()" << endl; + + if ( bugs.isEmpty() ) return; + + QString masterUid = "kbb_" + BugSystem::self()->server()->identifier(); + KCal::Todo *masterTodo = mCalendar.todo( masterUid ); + if ( !masterTodo ) { + masterTodo = new KCal::Todo; + masterTodo->setUid( masterUid ); + masterTodo->setSummary( resourceName() ); + mCalendar.addTodo( masterTodo ); + } + + Bug::List::ConstIterator it; + for( it = bugs.begin(); it != bugs.end(); ++it ) { + Bug bug = *it; + kdDebug() << " Bug " << bug.number() << ": " << bug.title() << endl; + QString uid = "KBugBuster_" + bug.number(); + KCal::Todo *newTodo = 0; + KCal::Todo *todo = mCalendar.todo( uid ); + if ( !todo ) { + newTodo = new KCal::Todo; + newTodo->setUid( uid ); + QString uri = "http://bugs.kde.org/show_bug.cgi?id=%1"; + newTodo->addAttachment( new KCal::Attachment( uri.arg( bug.number() ) ) ); + todo = newTodo; + } + + todo->setSummary( bug.number() + ": " + bug.title() ); + todo->setRelatedTo( masterTodo ); + + if ( newTodo ) mCalendar.addTodo( newTodo ); + } + + emit resourceChanged( this ); +} + +void KCalResource::slotLoadJobResult( KIO::Job *job ) +{ + if ( job->error() ) { + job->showErrorDialog( 0 ); + } else { + kdDebug() << "KCalResource::slotLoadJobResult() success" << endl; + + mCalendar.close(); + mCalendar.load( cacheFile() ); + + emit resourceChanged( this ); + } + + mDownloadJob = 0; + + emit resourceLoaded( this ); +} + +bool KCalResource::doSave() +{ + kdDebug() << "KCalResource::doSave()" << endl; + + if ( !mOpen ) return true; + + if ( readOnly() ) { + emit resourceSaved( this ); + return true; + } + + if ( mDownloadJob ) { + kdWarning() << "KCalResource::save(): download still in progress." + << endl; + return false; + } + if ( mUploadJob ) { + kdWarning() << "KCalResource::save(): upload still in progress." + << endl; + return false; + } + + mCalendar.save( cacheFile() ); + + mUploadJob = KIO::file_copy( KURL( cacheFile() ), mUploadUrl, -1, true ); + connect( mUploadJob, SIGNAL( result( KIO::Job * ) ), + SLOT( slotSaveJobResult( KIO::Job * ) ) ); + + return true; +} + +bool KCalResource::isSaving() +{ + return mUploadJob; +} + +void KCalResource::slotSaveJobResult( KIO::Job *job ) +{ + if ( job->error() ) { + job->showErrorDialog( 0 ); + } else { + kdDebug() << "KCalResource::slotSaveJobResult() success" << endl; + } + + mUploadJob = 0; + + emit resourceSaved( this ); +} + +void KCalResource::doClose() +{ + if ( !mOpen ) return; + + mCalendar.close(); + mOpen = false; +} + +KABC::Lock *KCalResource::lock() +{ + return mLock; +} + +void KCalResource::dump() const +{ + ResourceCalendar::dump(); + kdDebug(5800) << " DownloadUrl: " << mDownloadUrl.url() << endl; + kdDebug(5800) << " UploadUrl: " << mUploadUrl.url() << endl; + kdDebug(5800) << " ReloadPolicy: " << mReloadPolicy << endl; +} + +#include "kcalresource.moc" diff --git a/kbugbuster/kresources/kcalresource.h b/kbugbuster/kresources/kcalresource.h new file mode 100644 index 00000000..9970f5e4 --- /dev/null +++ b/kbugbuster/kresources/kcalresource.h @@ -0,0 +1,123 @@ +/* + This file is part of KBugBuster. + + Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KCALRESOURCE_H +#define KCALRESOURCE_H + +#include <qptrlist.h> +#include <qstring.h> +#include <qdatetime.h> + +#include <kurl.h> +#include <kconfig.h> +#include <kdirwatch.h> + +#include <libkcal/incidence.h> +#include <libkcal/calendarlocal.h> +#include <libkcal/icalformat.h> +#include <libkcal/resourcecached.h> + +#include <bugsystem.h> + +namespace KIO { +class FileCopyJob; +class Job; +} + +namespace KBB { +class ResourcePrefs; +} + +/** + This class provides a calendar stored as a remote file. +*/ +class KCalResource : public KCal::ResourceCached +{ + Q_OBJECT + + friend class KCalResourceConfig; + + public: + /** + Reload policy. + + @see setReloadPolicy(), reloadPolicy() + */ + enum { ReloadNever, ReloadOnStartup, ReloadOnceADay, ReloadAlways }; + + /** + Create resource from configuration information stored in KConfig object. + */ + KCalResource( const KConfig * ); + ~KCalResource(); + + void readConfig( const KConfig *config ); + void writeConfig( KConfig *config ); + + KBB::ResourcePrefs *prefs(); + + /** + Return name of file used as cache for remote file. + */ + QString cacheFile(); + + KABC::Lock *lock(); + + bool isSaving(); + + void dump() const; + + protected slots: + void slotBugListAvailable( const Package &, const QString &, + const Bug::List &bugs ); + + void slotLoadJobResult( KIO::Job * ); + void slotSaveJobResult( KIO::Job * ); + + protected: + bool doOpen(); + void doClose(); + bool doLoad(); + bool doSave(); + + private: + void init(); + + KBB::ResourcePrefs *mPrefs; + + KURL mDownloadUrl; + KURL mUploadUrl; + + int mReloadPolicy; + + KCal::ICalFormat mFormat; + + bool mOpen; + + KIO::FileCopyJob *mDownloadJob; + KIO::FileCopyJob *mUploadJob; + + KABC::Lock *mLock; + + class Private; + Private *d; +}; + +#endif diff --git a/kbugbuster/kresources/kcalresource_plugin.cpp b/kbugbuster/kresources/kcalresource_plugin.cpp new file mode 100644 index 00000000..23b5f8ac --- /dev/null +++ b/kbugbuster/kresources/kcalresource_plugin.cpp @@ -0,0 +1,37 @@ +/* + This file is part of KBugBuster. + + Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "kcalresourceconfig.h" +#include "kcalresource.h" + +#include <kglobal.h> +#include <klocale.h> + +using namespace KCal; + +extern "C" +{ + KDE_EXPORT void *init_kcal_bugzilla() + { + KGlobal::locale()->insertCatalogue( "kres_bugzilla" ); + return new KRES::PluginFactory<KCalResource,KCalResourceConfig>(); + } +} diff --git a/kbugbuster/kresources/kcalresourceconfig.cpp b/kbugbuster/kresources/kcalresourceconfig.cpp new file mode 100644 index 00000000..bb404445 --- /dev/null +++ b/kbugbuster/kresources/kcalresourceconfig.cpp @@ -0,0 +1,92 @@ +/* + This file is part of KBugBuster. + + Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <typeinfo> + +#include <qlabel.h> +#include <qlayout.h> + +#include <klineedit.h> +#include <klocale.h> +#include <kdebug.h> +#include <kstandarddirs.h> +#include <kdialog.h> + +#include "kcalresource.h" +#include "resourceprefs.h" +#include "kcalresourceconfig.h" + +KCalResourceConfig::KCalResourceConfig( QWidget* parent, const char* name ) + : KRES::ConfigWidget( parent, name ) +{ + resize( 245, 115 ); + + QGridLayout *mainLayout = new QGridLayout( this, 2, 2 ); + mainLayout->setSpacing( KDialog::spacingHint() ); + + QLabel *label = new QLabel( i18n("Server:"), this ); + mainLayout->addWidget( label, 0, 0 ); + + mServerEdit = new KLineEdit( this ); + mainLayout->addWidget( mServerEdit, 0, 1 ); + + + label = new QLabel( i18n("Product:"), this ); + mainLayout->addWidget( label, 1, 0 ); + + mProductEdit = new KLineEdit( this ); + mainLayout->addWidget( mProductEdit, 1, 1 ); + + + label = new QLabel( i18n("Component:"), this ); + mainLayout->addWidget( label, 2, 0 ); + + mComponentEdit = new KLineEdit( this ); + mainLayout->addWidget( mComponentEdit, 2, 1 ); +} + +void KCalResourceConfig::loadSettings( KRES::Resource *resource ) +{ + KCalResource *res = static_cast<KCalResource *>( resource ); + if ( res ) { + KBB::ResourcePrefs *p = res->prefs(); + mServerEdit->setText( p->server() ); + mProductEdit->setText( p->product() ); + mComponentEdit->setText( p->component() ); + } else { + kdError(5700) << "KCalResourceConfig::loadSettings(): no KCalResource, cast failed" << endl; + } +} + +void KCalResourceConfig::saveSettings( KRES::Resource *resource ) +{ + KCalResource *res = static_cast<KCalResource*>( resource ); + if ( res ) { + KBB::ResourcePrefs *p = res->prefs(); + p->setServer( mServerEdit->text() ); + p->setProduct( mProductEdit->text() ); + p->setComponent( mComponentEdit->text() ); + } else { + kdError(5700) << "KCalResourceConfig::saveSettings(): no KCalResource, cast failed" << endl; + } +} + +#include "kcalresourceconfig.moc" diff --git a/kbugbuster/kresources/kcalresourceconfig.h b/kbugbuster/kresources/kcalresourceconfig.h new file mode 100644 index 00000000..43ab60a1 --- /dev/null +++ b/kbugbuster/kresources/kcalresourceconfig.h @@ -0,0 +1,53 @@ +/* + This file is part of KBugBuster. + + Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef KCALRESOURCECONFIG_H +#define KCALRESOURCECONFIG_H + +#include <kresources/resource.h> +#include <kresources/configwidget.h> + +class KLineEdit; + +/** + Configuration widget for remote resource. + + @see KCalResource +*/ +class KCalResourceConfig : public KRES::ConfigWidget +{ + Q_OBJECT + public: + KCalResourceConfig( QWidget *parent = 0, const char *name = 0 ); + + public slots: + virtual void loadSettings( KRES::Resource *resource ); + virtual void saveSettings( KRES::Resource *resource ); + + private: + KLineEdit *mServerEdit; + KLineEdit *mComponentEdit; + KLineEdit *mProductEdit; + + class Private; + Private *d; +}; + +#endif diff --git a/kbugbuster/kresources/kresources_kcal_bugzilla.kcfg b/kbugbuster/kresources/kresources_kcal_bugzilla.kcfg new file mode 100644 index 00000000..ce23c969 --- /dev/null +++ b/kbugbuster/kresources/kresources_kcal_bugzilla.kcfg @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 + http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" > + <kcfgfile name="kresources_kcal_bugzillarc"/> + + <group name="General"> + <entry type="String" name="Server"> + <label>Server</label> + </entry> + <entry type="String" name="Product"> + <label>Product</label> + </entry> + <entry type="String" name="Component"> + <label>Component</label> + </entry> + </group> + +</kcfg> diff --git a/kbugbuster/kresources/resourceprefs.kcfgc b/kbugbuster/kresources/resourceprefs.kcfgc new file mode 100644 index 00000000..0f41bdb1 --- /dev/null +++ b/kbugbuster/kresources/resourceprefs.kcfgc @@ -0,0 +1,9 @@ +# Code generation options for kconfig_compiler +File=kresources_kcal_bugzilla.kcfg +ClassName=ResourcePrefs +NameSpace=KBB +Singleton=false +Mutators=true +GlobalEnums=true +#ItemAccessors=true +#SetUserTexts=true diff --git a/kbugbuster/lo16-app-kbugbuster.png b/kbugbuster/lo16-app-kbugbuster.png Binary files differnew file mode 100644 index 00000000..eb693eb6 --- /dev/null +++ b/kbugbuster/lo16-app-kbugbuster.png diff --git a/kbugbuster/lo32-app-kbugbuster.png b/kbugbuster/lo32-app-kbugbuster.png Binary files differnew file mode 100644 index 00000000..3ffbba21 --- /dev/null +++ b/kbugbuster/lo32-app-kbugbuster.png diff --git a/kbugbuster/main.cpp b/kbugbuster/main.cpp new file mode 100644 index 00000000..456d9dc6 --- /dev/null +++ b/kbugbuster/main.cpp @@ -0,0 +1,83 @@ +/*************************************************************************** + main.cpp - description + ------------------- + begin : zo mrt 18 17:12:24 CET 2001 + copyright : (C) 2001 by Martijn Klingens + email : klingens@kde.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. * + * * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <kcmdlineargs.h> +#include <kaboutdata.h> +#include <klocale.h> +#include <dcopclient.h> + +#include "gui/kbbmainwindow.h" +#include "bugsystem.h" +#include "kbbprefs.h" + +static const char description[] = + I18N_NOOP("KBugBuster"); +// INSERT A DESCRIPTION FOR YOUR APPLICATION HERE + +static const KCmdLineOptions options[] = +{ + {"d", 0, 0}, + {"disconnected", I18N_NOOP("Start in disconnected mode"), 0}, + {"pkg", 0, 0}, + {"package <pkg>", I18N_NOOP("Start with the buglist for <package>"), 0}, + {"bug <br>", I18N_NOOP("Start with bug report <br>"), 0}, + KCmdLineLastOption +}; + +int main(int argc, char *argv[]) +{ + KAboutData aboutData( "kbugbuster", I18N_NOOP( "KBugBuster" ), + VERSION, description, KAboutData::License_GPL, + I18N_NOOP("(c) 2001,2002,2003 the KBugBuster authors") ); + aboutData.addAuthor( "Martijn Klingens", 0, "klingens@kde.org" ); + aboutData.addAuthor( "Cornelius Schumacher", 0, "schumacher@kde.org" ); + aboutData.addAuthor( "Simon Hausmann", 0, "hausmann@kde.org" ); + aboutData.addAuthor( "David Faure", 0, "faure@kde.org" ); + KCmdLineArgs::init( argc, argv, &aboutData ); + KCmdLineArgs::addCmdLineOptions( options ); + + KApplication app; + + app.dcopClient()->attach(); + + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + if (args->isSet("disconnected")) { + BugSystem::self()->setDisconnected( true ); + } + + if ( app.isRestored() ) + { + RESTORE( KBBMainWindow ); + } + else + { + KBBMainWindow *mainWin = new KBBMainWindow(args->getOption("package"), args->getOption("bug")); + + // Since all background jobs remaing running after closing the + // main window we force a quit here + QObject::connect( &app, SIGNAL( lastWindowClosed() ), + &app, SLOT( quit() ) ); + mainWin->show(); + return app.exec(); + } +} + +/* vim: set et ts=4 sw=4 softtabstop=4: */ diff --git a/kbugbuster/pics/Makefile.am b/kbugbuster/pics/Makefile.am new file mode 100644 index 00000000..6ce39e37 --- /dev/null +++ b/kbugbuster/pics/Makefile.am @@ -0,0 +1,4 @@ +logo_DATA = tools.png logo.png bars.png top-right.png +logodir = $(kde_datadir)/kbugbuster/pics + +EXTRA_DIST = $(logo_DATA) diff --git a/kbugbuster/pics/bars.png b/kbugbuster/pics/bars.png Binary files differnew file mode 100644 index 00000000..7d03c72c --- /dev/null +++ b/kbugbuster/pics/bars.png diff --git a/kbugbuster/pics/logo.png b/kbugbuster/pics/logo.png Binary files differnew file mode 100644 index 00000000..360d6435 --- /dev/null +++ b/kbugbuster/pics/logo.png diff --git a/kbugbuster/pics/tools.png b/kbugbuster/pics/tools.png Binary files differnew file mode 100644 index 00000000..7f9584e3 --- /dev/null +++ b/kbugbuster/pics/tools.png diff --git a/kbugbuster/pics/top-right.png b/kbugbuster/pics/top-right.png Binary files differnew file mode 100644 index 00000000..bad44f08 --- /dev/null +++ b/kbugbuster/pics/top-right.png |