summaryrefslogtreecommitdiffstats
path: root/kbugbuster
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commitbd9e6617827818fd043452c08c606f07b78014a0 (patch)
tree425bb4c3168f9c02f10150f235d2cb998dcc6108 /kbugbuster
downloadtdesdk-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')
-rw-r--r--kbugbuster/AUTHORS4
-rw-r--r--kbugbuster/COPYING340
-rw-r--r--kbugbuster/ChangeLog4
-rw-r--r--kbugbuster/INSTALL167
-rw-r--r--kbugbuster/Makefile.am28
-rw-r--r--kbugbuster/README2
-rw-r--r--kbugbuster/TODO4
-rw-r--r--kbugbuster/backend/Makefile.am16
-rw-r--r--kbugbuster/backend/bug.cpp240
-rw-r--r--kbugbuster/backend/bug.h92
-rw-r--r--kbugbuster/backend/bugcache.cpp289
-rw-r--r--kbugbuster/backend/bugcache.h47
-rw-r--r--kbugbuster/backend/bugcommand.cpp317
-rw-r--r--kbugbuster/backend/bugcommand.h217
-rw-r--r--kbugbuster/backend/bugdetails.cpp268
-rw-r--r--kbugbuster/backend/bugdetails.h55
-rw-r--r--kbugbuster/backend/bugdetailsimpl.h40
-rw-r--r--kbugbuster/backend/bugdetailsjob.cpp50
-rw-r--r--kbugbuster/backend/bugdetailsjob.h32
-rw-r--r--kbugbuster/backend/bugdetailspart.h21
-rw-r--r--kbugbuster/backend/bugimpl.h36
-rw-r--r--kbugbuster/backend/bugjob.cpp97
-rw-r--r--kbugbuster/backend/bugjob.h45
-rw-r--r--kbugbuster/backend/buglistjob.cpp75
-rw-r--r--kbugbuster/backend/buglistjob.h58
-rw-r--r--kbugbuster/backend/bugmybugsjob.cpp78
-rw-r--r--kbugbuster/backend/bugmybugsjob.h52
-rw-r--r--kbugbuster/backend/bugserver.cpp412
-rw-r--r--kbugbuster/backend/bugserver.h152
-rw-r--r--kbugbuster/backend/bugserverconfig.cpp150
-rw-r--r--kbugbuster/backend/bugserverconfig.h91
-rw-r--r--kbugbuster/backend/bugsystem.cpp436
-rw-r--r--kbugbuster/backend/bugsystem.h146
-rw-r--r--kbugbuster/backend/domprocessor.cpp407
-rw-r--r--kbugbuster/backend/domprocessor.h69
-rw-r--r--kbugbuster/backend/error.h43
-rw-r--r--kbugbuster/backend/htmlparser.cpp294
-rw-r--r--kbugbuster/backend/htmlparser.h116
-rw-r--r--kbugbuster/backend/kbbprefs.cpp170
-rw-r--r--kbugbuster/backend/kbbprefs.h82
-rw-r--r--kbugbuster/backend/mailsender.cpp212
-rw-r--r--kbugbuster/backend/mailsender.h50
-rw-r--r--kbugbuster/backend/package.cpp82
-rw-r--r--kbugbuster/backend/package.h43
-rw-r--r--kbugbuster/backend/packageimpl.h31
-rw-r--r--kbugbuster/backend/packagelistjob.cpp68
-rw-r--r--kbugbuster/backend/packagelistjob.h55
-rw-r--r--kbugbuster/backend/person.cpp74
-rw-r--r--kbugbuster/backend/person.h26
-rw-r--r--kbugbuster/backend/processor.cpp78
-rw-r--r--kbugbuster/backend/processor.h63
-rw-r--r--kbugbuster/backend/rdfprocessor.cpp107
-rw-r--r--kbugbuster/backend/rdfprocessor.h43
-rw-r--r--kbugbuster/backend/smtp.cpp181
-rw-r--r--kbugbuster/backend/smtp.h67
-rw-r--r--kbugbuster/configure.in.in8
-rw-r--r--kbugbuster/gui/Makefile.am24
-rw-r--r--kbugbuster/gui/README21
-rw-r--r--kbugbuster/gui/buglvi.cpp109
-rw-r--r--kbugbuster/gui/buglvi.h57
-rw-r--r--kbugbuster/gui/centralwidget.cpp507
-rw-r--r--kbugbuster/gui/centralwidget.h142
-rw-r--r--kbugbuster/gui/centralwidget_base.ui236
-rw-r--r--kbugbuster/gui/cwbugdetails.cpp212
-rw-r--r--kbugbuster/gui/cwbugdetails.h65
-rw-r--r--kbugbuster/gui/cwbugdetailscontainer.cpp269
-rw-r--r--kbugbuster/gui/cwbugdetailscontainer.h88
-rw-r--r--kbugbuster/gui/cwbugdetailscontainer_base.ui244
-rw-r--r--kbugbuster/gui/cwbuglistcontainer.cpp328
-rw-r--r--kbugbuster/gui/cwbuglistcontainer.h99
-rw-r--r--kbugbuster/gui/cwloadingwidget.cpp256
-rw-r--r--kbugbuster/gui/cwloadingwidget.h87
-rw-r--r--kbugbuster/gui/cwsearchwidget.cpp69
-rw-r--r--kbugbuster/gui/cwsearchwidget.h46
-rw-r--r--kbugbuster/gui/cwsearchwidget_base.ui198
-rw-r--r--kbugbuster/gui/kbbbookmarkmanager.h23
-rw-r--r--kbugbuster/gui/kbbmainwindow.cpp504
-rw-r--r--kbugbuster/gui/kbbmainwindow.h144
-rw-r--r--kbugbuster/gui/kbugbusterui.rc78
-rw-r--r--kbugbuster/gui/loadallbugsdlg.cpp92
-rw-r--r--kbugbuster/gui/loadallbugsdlg.h43
-rw-r--r--kbugbuster/gui/messageeditor.cpp117
-rw-r--r--kbugbuster/gui/messageeditor.h33
-rw-r--r--kbugbuster/gui/msginputdialog.cpp226
-rw-r--r--kbugbuster/gui/msginputdialog.h55
-rw-r--r--kbugbuster/gui/packagelvi.cpp38
-rw-r--r--kbugbuster/gui/packagelvi.h51
-rw-r--r--kbugbuster/gui/packageselectdialog.cpp214
-rw-r--r--kbugbuster/gui/packageselectdialog.h64
-rw-r--r--kbugbuster/gui/preferencesdialog.cpp306
-rw-r--r--kbugbuster/gui/preferencesdialog.h76
-rw-r--r--kbugbuster/gui/serverconfigdialog.cpp82
-rw-r--r--kbugbuster/gui/serverconfigdialog.h28
-rw-r--r--kbugbuster/gui/severityselectdialog.cpp40
-rw-r--r--kbugbuster/gui/severityselectdialog.h23
-rw-r--r--kbugbuster/hi128-app-kbugbuster.pngbin0 -> 13453 bytes
-rw-r--r--kbugbuster/hi16-app-kbugbuster.pngbin0 -> 1107 bytes
-rw-r--r--kbugbuster/hi22-app-kbugbuster.pngbin0 -> 1914 bytes
-rw-r--r--kbugbuster/hi32-app-kbugbuster.pngbin0 -> 2994 bytes
-rw-r--r--kbugbuster/hi48-app-kbugbuster.pngbin0 -> 5091 bytes
-rw-r--r--kbugbuster/hi64-app-kbugbuster.pngbin0 -> 7630 bytes
-rw-r--r--kbugbuster/kbugbuster.desktop78
-rw-r--r--kbugbuster/kresources/Makefile.am16
-rw-r--r--kbugbuster/kresources/bugzilla.desktop48
-rw-r--r--kbugbuster/kresources/kcalresource.cpp316
-rw-r--r--kbugbuster/kresources/kcalresource.h123
-rw-r--r--kbugbuster/kresources/kcalresource_plugin.cpp37
-rw-r--r--kbugbuster/kresources/kcalresourceconfig.cpp92
-rw-r--r--kbugbuster/kresources/kcalresourceconfig.h53
-rw-r--r--kbugbuster/kresources/kresources_kcal_bugzilla.kcfg20
-rw-r--r--kbugbuster/kresources/resourceprefs.kcfgc9
-rw-r--r--kbugbuster/lo16-app-kbugbuster.pngbin0 -> 305 bytes
-rw-r--r--kbugbuster/lo32-app-kbugbuster.pngbin0 -> 493 bytes
-rw-r--r--kbugbuster/main.cpp83
-rw-r--r--kbugbuster/pics/Makefile.am4
-rw-r--r--kbugbuster/pics/bars.pngbin0 -> 258 bytes
-rw-r--r--kbugbuster/pics/logo.pngbin0 -> 15734 bytes
-rw-r--r--kbugbuster/pics/tools.pngbin0 -> 76482 bytes
-rw-r--r--kbugbuster/pics/top-right.pngbin0 -> 13794 bytes
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 + " &lt;" + email + "&gt;";
+ } 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 &amp;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>&amp;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&amp;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&amp;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&amp;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&amp;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&amp;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 &amp;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&amp;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>&amp;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 &amp;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>&amp;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 &amp;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>&amp;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>&amp;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>&amp;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&amp;earch</text>
+ <Action name="search_package" />
+ <Action name="search_bugnumber" />
+ <Action name="search_description" />
+ </Menu>
+ <Action name="bookmarks"/>
+ <Menu name="commands"><text>&amp;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>&amp;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 &quotedMsg,
+ 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 &quotedMsg );
+
+ 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
new file mode 100644
index 00000000..a39081d9
--- /dev/null
+++ b/kbugbuster/hi128-app-kbugbuster.png
Binary files differ
diff --git a/kbugbuster/hi16-app-kbugbuster.png b/kbugbuster/hi16-app-kbugbuster.png
new file mode 100644
index 00000000..827d5f8e
--- /dev/null
+++ b/kbugbuster/hi16-app-kbugbuster.png
Binary files differ
diff --git a/kbugbuster/hi22-app-kbugbuster.png b/kbugbuster/hi22-app-kbugbuster.png
new file mode 100644
index 00000000..3a3d1ea2
--- /dev/null
+++ b/kbugbuster/hi22-app-kbugbuster.png
Binary files differ
diff --git a/kbugbuster/hi32-app-kbugbuster.png b/kbugbuster/hi32-app-kbugbuster.png
new file mode 100644
index 00000000..59773925
--- /dev/null
+++ b/kbugbuster/hi32-app-kbugbuster.png
Binary files differ
diff --git a/kbugbuster/hi48-app-kbugbuster.png b/kbugbuster/hi48-app-kbugbuster.png
new file mode 100644
index 00000000..2565d767
--- /dev/null
+++ b/kbugbuster/hi48-app-kbugbuster.png
Binary files differ
diff --git a/kbugbuster/hi64-app-kbugbuster.png b/kbugbuster/hi64-app-kbugbuster.png
new file mode 100644
index 00000000..f2f45269
--- /dev/null
+++ b/kbugbuster/hi64-app-kbugbuster.png
Binary files differ
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
new file mode 100644
index 00000000..eb693eb6
--- /dev/null
+++ b/kbugbuster/lo16-app-kbugbuster.png
Binary files differ
diff --git a/kbugbuster/lo32-app-kbugbuster.png b/kbugbuster/lo32-app-kbugbuster.png
new file mode 100644
index 00000000..3ffbba21
--- /dev/null
+++ b/kbugbuster/lo32-app-kbugbuster.png
Binary files differ
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
new file mode 100644
index 00000000..7d03c72c
--- /dev/null
+++ b/kbugbuster/pics/bars.png
Binary files differ
diff --git a/kbugbuster/pics/logo.png b/kbugbuster/pics/logo.png
new file mode 100644
index 00000000..360d6435
--- /dev/null
+++ b/kbugbuster/pics/logo.png
Binary files differ
diff --git a/kbugbuster/pics/tools.png b/kbugbuster/pics/tools.png
new file mode 100644
index 00000000..7f9584e3
--- /dev/null
+++ b/kbugbuster/pics/tools.png
Binary files differ
diff --git a/kbugbuster/pics/top-right.png b/kbugbuster/pics/top-right.png
new file mode 100644
index 00000000..bad44f08
--- /dev/null
+++ b/kbugbuster/pics/top-right.png
Binary files differ